diff --git a/NEWS b/NEWS index 762cbc6..07b3b68 100644 --- a/NEWS +++ b/NEWS @@ -4,9 +4,8 @@ CHANGES SINCE 1.4-UP3 New features: - Cinnamon 2D - - Window preview and contour outlining in ALT-Tab mode + - Window preview and window thumbnails in ALT-Tab view (optional) - Workspace OSD - - Window thumbnails in Alt-Tab view - Keyboard navigation in Scale (close windows with CTRL+W or mouse middle-click) - Keyboard navigation in Expo - Expo grid view @@ -69,9 +68,7 @@ THEME CHANGES SINCE 1.4 .window-caption#selected { } -.switcher-outline-background { -} -.switcher-outline-frame { +.switcher-preview-backdrop { } /* =================================================================== @@ -103,8 +100,11 @@ THEME CHANGES SINCE 1.4 } /* =================================================================== - * Menu Applet + * Configurable Panels * ===================================================================*/ -.menu-place-cat-button-label { +.panel-top { +} +.panel-bottom { } + diff --git a/cinnamon.pot b/cinnamon.pot index ba94fe7..260900f 100644 --- a/cinnamon.pot +++ b/cinnamon.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2012-07-24 18:47+0100\n" +"POT-Creation-Date: 2012-09-14 18:40+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -80,42 +80,6 @@ msgstr[1] "" msgid "System Sounds" msgstr "" -#: js/gdm/loginDialog.js:629 -msgid "Session..." -msgstr "" - -#: js/gdm/loginDialog.js:845 -msgid "(or swipe finger)" -msgstr "" - -#: js/gdm/loginDialog.js:863 -msgid "Not listed?" -msgstr "" - -#: js/gdm/loginDialog.js:1031 js/ui/endSessionDialog.js:428 -#: js/ui/extensionSystem.js:477 js/ui/networkAgent.js:144 -#: js/ui/polkitAuthenticationAgent.js:173 -#: files/usr/share/cinnamon/applets/bluetooth@cinnamon.org/applet.js:160 -#: files/usr/share/cinnamon/applets/panel-launchers@cinnamon.org/applet.js:381 -#: files/usr/share/cinnamon/applets/panel-launchers@cinnamon.org/applet.js:398 -#: files/usr/share/cinnamon/applets/trash@cinnamon.org/applet.js:118 -msgid "Cancel" -msgstr "" - -#: js/gdm/powerMenu.js:116 -msgid "Suspend" -msgstr "" - -#: js/gdm/powerMenu.js:121 js/ui/endSessionDialog.js:87 -#: js/ui/endSessionDialog.js:95 js/ui/endSessionDialog.js:104 -msgid "Restart" -msgstr "" - -#: js/gdm/powerMenu.js:126 js/ui/endSessionDialog.js:78 -#: js/ui/endSessionDialog.js:89 -msgid "Power Off" -msgstr "" - #: js/misc/util.js:89 msgid "Command not found" msgstr "" @@ -129,16 +93,8 @@ msgstr "" msgid "Execution of '%s' failed:" msgstr "" -#: js/ui/applet.js:184 -msgid "Remove from Panel" -msgstr "" - -#: js/ui/applet.js:186 -msgid "Panel settings" -msgstr "" - -#: js/ui/applet.js:188 -msgid "Add/remove applets" +#: js/ui/applet.js:237 +msgid "Remove this applet" msgstr "" #: js/ui/autorunManager.js:280 @@ -225,6 +181,10 @@ msgstr[1] "" msgid "Logging out of the system." msgstr "" +#: js/ui/endSessionDialog.js:78 js/ui/endSessionDialog.js:89 +msgid "Power Off" +msgstr "" + #: js/ui/endSessionDialog.js:79 msgid "Click Power Off to quit these applications and power off the system." msgstr "" @@ -240,6 +200,11 @@ msgstr[1] "" msgid "Powering off the system." msgstr "" +#: js/ui/endSessionDialog.js:87 js/ui/endSessionDialog.js:95 +#: js/ui/endSessionDialog.js:104 +msgid "Restart" +msgstr "" + #: js/ui/endSessionDialog.js:96 msgid "Click Restart to quit these applications and restart the system." msgstr "" @@ -255,8 +220,12 @@ msgstr[1] "" msgid "Restarting the system." msgstr "" -#: js/ui/expo.js:66 js/ui/overview.js:81 -msgid "Undo" +#: js/ui/endSessionDialog.js:428 js/ui/extensionSystem.js:477 +#: js/ui/networkAgent.js:144 js/ui/polkitAuthenticationAgent.js:173 +#: files/usr/share/cinnamon/applets/bluetooth@cinnamon.org/applet.js:160 +#: files/usr/share/cinnamon/applets/trash@cinnamon.org/applet.js:118 +#: files/usr/share/cinnamon/applets/panel-launchers@cinnamon.org/add-panel-launcher.py:141 +msgid "Cancel" msgstr "" #: js/ui/extensionSystem.js:481 @@ -305,20 +274,20 @@ msgstr "" msgid "Web Page" msgstr "" -#: js/ui/main.js:382 +#: js/ui/main.js:369 msgid "WORKSPACE" msgstr "" -#: js/ui/messageTray.js:1234 +#: js/ui/messageTray.js:1250 msgid "Open" msgstr "" -#: js/ui/messageTray.js:1241 -#: files/usr/share/cinnamon/applets/panel-launchers@cinnamon.org/applet.js:46 +#: js/ui/messageTray.js:1257 +#: files/usr/share/cinnamon/applets/panel-launchers@cinnamon.org/applet.js:53 msgid "Remove" msgstr "" -#: js/ui/messageTray.js:1828 +#: js/ui/messageTray.js:1844 msgid "System Information" msgstr "" @@ -396,11 +365,83 @@ msgstr "" msgid "A password is required to connect to '%s'." msgstr "" -#: js/ui/overview.js:194 -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1323 +#: js/ui/overview.js:80 +msgid "Undo" +msgstr "" + +#: js/ui/overview.js:193 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2083 msgid "Windows" msgstr "" +#: js/ui/panel.js:384 +msgid "Yes" +msgstr "" + +#: js/ui/panel.js:391 +msgid "No" +msgstr "" + +#: js/ui/panel.js:426 +#: files/usr/share/cinnamon/applets/brightness@cinnamon.org/applet.js:148 +#: files/usr/share/cinnamon/applets/settings@cinnamon.org/applet.js:28 +msgid "Settings" +msgstr "" + +#: js/ui/panel.js:428 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1950 +msgid "Themes" +msgstr "" + +#: js/ui/panel.js:431 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2049 +msgid "Applets" +msgstr "" + +#: js/ui/panel.js:434 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1866 +msgid "Panel" +msgstr "" + +#: js/ui/panel.js:437 +#: files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js:661 +#: files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js:700 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1857 +msgid "Menu" +msgstr "" + +#: js/ui/panel.js:440 +msgid "All settings" +msgstr "" + +#: js/ui/panel.js:445 +msgid "Troubleshoot" +msgstr "" + +#: js/ui/panel.js:446 +msgid "Restart Cinnamon" +msgstr "" + +#: js/ui/panel.js:450 +msgid "Looking Glass" +msgstr "" + +#: js/ui/panel.js:454 +msgid "Restore all settings to default" +msgstr "" + +#: js/ui/panel.js:464 +msgid "Panel Edit mode" +msgstr "" + +#: js/ui/panel.js:488 +msgid "Panel settings" +msgstr "" + +#: js/ui/panel.js:491 +msgid "Add applets to the panel" +msgstr "" + #: js/ui/placesManager.js:119 #, c-format msgid "Failed to unmount '%s'" @@ -443,6 +484,7 @@ msgid "Accessibility" msgstr "" #: files/usr/share/cinnamon/applets/a11y@cinnamon.org/applet.js:57 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:56 msgid "Zoom" msgstr "" @@ -486,8 +528,9 @@ msgstr "" #: files/usr/share/cinnamon/applets/bluetooth@cinnamon.org/applet.js:61 #: files/usr/share/cinnamon/applets/bluetooth@cinnamon.org/applet.js:101 #: files/usr/share/cinnamon/applets/bluetooth@cinnamon.org/applet.js:134 -#: files/usr/share/cinnamon/applets/bluetooth@cinnamon.org/applet.js:210 -#: files/usr/share/cinnamon/applets/bluetooth@cinnamon.org/applet.js:442 +#: files/usr/share/cinnamon/applets/bluetooth@cinnamon.org/applet.js:206 +#: files/usr/share/cinnamon/applets/bluetooth@cinnamon.org/applet.js:211 +#: files/usr/share/cinnamon/applets/bluetooth@cinnamon.org/applet.js:443 msgid "Bluetooth" msgstr "" @@ -550,67 +593,67 @@ msgstr "" msgid "OK" msgstr "" -#: files/usr/share/cinnamon/applets/bluetooth@cinnamon.org/applet.js:223 +#: files/usr/share/cinnamon/applets/bluetooth@cinnamon.org/applet.js:224 msgid "Visibility" msgstr "" -#: files/usr/share/cinnamon/applets/bluetooth@cinnamon.org/applet.js:237 +#: files/usr/share/cinnamon/applets/bluetooth@cinnamon.org/applet.js:238 msgid "Send Files to Device..." msgstr "" -#: files/usr/share/cinnamon/applets/bluetooth@cinnamon.org/applet.js:238 +#: files/usr/share/cinnamon/applets/bluetooth@cinnamon.org/applet.js:239 msgid "Set up a New Device..." msgstr "" -#: files/usr/share/cinnamon/applets/bluetooth@cinnamon.org/applet.js:262 +#: files/usr/share/cinnamon/applets/bluetooth@cinnamon.org/applet.js:263 msgid "Bluetooth Settings" msgstr "" -#: files/usr/share/cinnamon/applets/bluetooth@cinnamon.org/applet.js:292 +#: files/usr/share/cinnamon/applets/bluetooth@cinnamon.org/applet.js:293 msgid "hardware disabled" msgstr "" -#: files/usr/share/cinnamon/applets/bluetooth@cinnamon.org/applet.js:389 +#: files/usr/share/cinnamon/applets/bluetooth@cinnamon.org/applet.js:390 msgid "Connection" msgstr "" -#: files/usr/share/cinnamon/applets/bluetooth@cinnamon.org/applet.js:398 +#: files/usr/share/cinnamon/applets/bluetooth@cinnamon.org/applet.js:399 #: files/usr/share/cinnamon/applets/network@cinnamon.org/applet.js:505 msgid "disconnecting..." msgstr "" -#: files/usr/share/cinnamon/applets/bluetooth@cinnamon.org/applet.js:411 +#: files/usr/share/cinnamon/applets/bluetooth@cinnamon.org/applet.js:412 #: files/usr/share/cinnamon/applets/network@cinnamon.org/applet.js:511 msgid "connecting..." msgstr "" -#: files/usr/share/cinnamon/applets/bluetooth@cinnamon.org/applet.js:429 +#: files/usr/share/cinnamon/applets/bluetooth@cinnamon.org/applet.js:430 msgid "Send Files..." msgstr "" -#: files/usr/share/cinnamon/applets/bluetooth@cinnamon.org/applet.js:434 +#: files/usr/share/cinnamon/applets/bluetooth@cinnamon.org/applet.js:435 msgid "Browse Files..." msgstr "" -#: files/usr/share/cinnamon/applets/bluetooth@cinnamon.org/applet.js:443 +#: files/usr/share/cinnamon/applets/bluetooth@cinnamon.org/applet.js:444 msgid "Error browsing device" msgstr "" -#: files/usr/share/cinnamon/applets/bluetooth@cinnamon.org/applet.js:444 +#: files/usr/share/cinnamon/applets/bluetooth@cinnamon.org/applet.js:445 #, c-format msgid "The requested device cannot be browsed, error is '%s'" msgstr "" -#: files/usr/share/cinnamon/applets/bluetooth@cinnamon.org/applet.js:452 +#: files/usr/share/cinnamon/applets/bluetooth@cinnamon.org/applet.js:453 msgid "Keyboard Settings" msgstr "" -#: files/usr/share/cinnamon/applets/bluetooth@cinnamon.org/applet.js:455 +#: files/usr/share/cinnamon/applets/bluetooth@cinnamon.org/applet.js:456 msgid "Mouse Settings" msgstr "" -#: files/usr/share/cinnamon/applets/bluetooth@cinnamon.org/applet.js:460 -#: files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js:879 +#: files/usr/share/cinnamon/applets/bluetooth@cinnamon.org/applet.js:461 +#: files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js:911 msgid "Sound Settings" msgstr "" @@ -638,12 +681,6 @@ msgstr "" msgid "Brightness not available" msgstr "" -#: files/usr/share/cinnamon/applets/brightness@cinnamon.org/applet.js:148 -#: files/usr/share/cinnamon/applets/settings@cinnamon.org/applet.js:81 -#: files/usr/share/cinnamon/applets/settings@cinnamon.org/applet.js:104 -msgid "Settings" -msgstr "" - #: files/usr/share/cinnamon/applets/calendar@cinnamon.org/applet.js:65 msgid "Date and Time Settings" msgstr "" @@ -652,91 +689,84 @@ msgstr "" msgid "Expo" msgstr "" -#: files/usr/share/cinnamon/applets/keyboard@cinnamon.org/applet.js:67 +#: files/usr/share/cinnamon/applets/keyboard@cinnamon.org/applet.js:66 msgid "Show Keyboard Layout" msgstr "" -#: files/usr/share/cinnamon/applets/keyboard@cinnamon.org/applet.js:71 +#: files/usr/share/cinnamon/applets/keyboard@cinnamon.org/applet.js:70 msgid "Show Character Table" msgstr "" -#: files/usr/share/cinnamon/applets/keyboard@cinnamon.org/applet.js:76 +#: files/usr/share/cinnamon/applets/keyboard@cinnamon.org/applet.js:74 msgid "Region and Language Settings" msgstr "" -#: files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js:202 +#: files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js:204 msgid "Add to panel" msgstr "" -#: files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js:205 +#: files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js:207 msgid "Add to desktop" msgstr "" -#: files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js:209 +#: files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js:211 msgid "Remove from favorites" msgstr "" -#: files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js:212 +#: files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js:214 msgid "Add to favorites" msgstr "" -#: files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js:320 +#: files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js:344 #: files/usr/share/cinnamon/applets/recent@cinnamon.org/applet.js:70 msgid "Clear list" msgstr "" -#: files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js:353 +#: files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js:381 msgid "All Applications" msgstr "" -#: files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js:378 +#: files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js:404 msgid "Places" msgstr "" -#: files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js:396 +#: files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js:422 msgid "Recent Files" msgstr "" -#: files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js:639 -#: files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js:677 -#: files/usr/share/cinnamon/applets/settings@cinnamon.org/applet.js:115 -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1114 -msgid "Menu" -msgstr "" - -#: files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js:728 +#: files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js:750 msgid "Edit menu" msgstr "" -#: files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js:730 +#: files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js:752 msgid "Menu settings" msgstr "" -#: files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js:1149 +#: files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js:1219 msgid "Lock screen" msgstr "" -#: files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js:1150 +#: files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js:1220 msgid "Lock the screen" msgstr "" -#: files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js:1166 +#: files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js:1236 msgid "Logout" msgstr "" -#: files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js:1167 +#: files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js:1237 msgid "Leave the session" msgstr "" -#: files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js:1183 +#: files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js:1253 msgid "Quit" msgstr "" -#: files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js:1184 +#: files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js:1254 msgid "Shutdown the computer" msgstr "" -#: files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js:1281 +#: files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js:1364 msgid "Type to search..." msgstr "" @@ -905,79 +935,50 @@ msgstr "" msgid "Clear notifications" msgstr "" -#: files/usr/share/cinnamon/applets/notifications@cinnamon.org/applet.js:222 +#: files/usr/share/cinnamon/applets/notifications@cinnamon.org/applet.js:226 msgid "No notifications" msgstr "" -#: files/usr/share/cinnamon/applets/notifications@cinnamon.org/applet.js:225 +#: files/usr/share/cinnamon/applets/notifications@cinnamon.org/applet.js:229 msgid " notification" msgstr "" -#: files/usr/share/cinnamon/applets/notifications@cinnamon.org/applet.js:228 +#: files/usr/share/cinnamon/applets/notifications@cinnamon.org/applet.js:232 msgid " notifications" msgstr "" -#: files/usr/share/cinnamon/applets/notifications@cinnamon.org/applet.js:242 +#: files/usr/share/cinnamon/applets/notifications@cinnamon.org/applet.js:246 msgid " (Just now)" msgstr "" -#: files/usr/share/cinnamon/applets/notifications@cinnamon.org/applet.js:245 +#: files/usr/share/cinnamon/applets/notifications@cinnamon.org/applet.js:249 #, c-format msgid " (%s seconds ago)" msgstr "" -#: files/usr/share/cinnamon/applets/notifications@cinnamon.org/applet.js:248 +#: files/usr/share/cinnamon/applets/notifications@cinnamon.org/applet.js:252 #, c-format msgid " (%s minute ago)" msgstr "" -#: files/usr/share/cinnamon/applets/notifications@cinnamon.org/applet.js:251 +#: files/usr/share/cinnamon/applets/notifications@cinnamon.org/applet.js:255 #, c-format msgid " (%s minutes ago)" msgstr "" -#: files/usr/share/cinnamon/applets/panel-launchers@cinnamon.org/applet.js:34 +#: files/usr/share/cinnamon/applets/panel-launchers@cinnamon.org/applet.js:41 msgid "Launch" msgstr "" -#: files/usr/share/cinnamon/applets/panel-launchers@cinnamon.org/applet.js:38 -#: files/usr/share/cinnamon/applets/panel-launchers@cinnamon.org/applet.js:394 +#: files/usr/share/cinnamon/applets/panel-launchers@cinnamon.org/applet.js:45 +#: files/usr/share/cinnamon/applets/panel-launchers@cinnamon.org/add-panel-launcher.py:151 msgid "Add" msgstr "" -#: files/usr/share/cinnamon/applets/panel-launchers@cinnamon.org/applet.js:42 +#: files/usr/share/cinnamon/applets/panel-launchers@cinnamon.org/applet.js:49 msgid "Edit" msgstr "" -#: files/usr/share/cinnamon/applets/panel-launchers@cinnamon.org/applet.js:265 -msgid "Name" -msgstr "" - -#: files/usr/share/cinnamon/applets/panel-launchers@cinnamon.org/applet.js:271 -msgid "Command" -msgstr "" - -#: files/usr/share/cinnamon/applets/panel-launchers@cinnamon.org/applet.js:277 -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:371 -msgid "Icon" -msgstr "" - -#: files/usr/share/cinnamon/applets/panel-launchers@cinnamon.org/applet.js:315 -msgid "Name cannot be empty!" -msgstr "" - -#: files/usr/share/cinnamon/applets/panel-launchers@cinnamon.org/applet.js:320 -msgid "Command cannot be empty!" -msgstr "" - -#: files/usr/share/cinnamon/applets/panel-launchers@cinnamon.org/applet.js:326 -msgid "Custom Launcher" -msgstr "" - -#: files/usr/share/cinnamon/applets/panel-launchers@cinnamon.org/applet.js:377 -msgid "Save" -msgstr "" - #: files/usr/share/cinnamon/applets/power@cinnamon.org/applet.js:104 msgid "AC adapter" msgstr "" @@ -1091,110 +1092,96 @@ msgid "Open file manager" msgstr "" #: files/usr/share/cinnamon/applets/scale@cinnamon.org/applet.js:19 -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1235 -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1249 -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1263 -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1277 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1999 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2010 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2021 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2032 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2043 msgid "Scale" msgstr "" -#: files/usr/share/cinnamon/applets/settings@cinnamon.org/applet.js:27 -msgid "Yes" -msgstr "" - -#: files/usr/share/cinnamon/applets/settings@cinnamon.org/applet.js:34 -msgid "No" -msgstr "" - -#: files/usr/share/cinnamon/applets/settings@cinnamon.org/applet.js:106 -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1189 -msgid "Themes" -msgstr "" - -#: files/usr/share/cinnamon/applets/settings@cinnamon.org/applet.js:109 -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1300 -msgid "Applets" +#: files/usr/share/cinnamon/applets/show-desktop@cinnamon.org/applet.js:16 +msgid "Show desktop" msgstr "" -#: files/usr/share/cinnamon/applets/settings@cinnamon.org/applet.js:112 -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1123 -msgid "Panel" +#: files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js:105 +msgid "Playing" msgstr "" -#: files/usr/share/cinnamon/applets/settings@cinnamon.org/applet.js:118 -msgid "All settings" +#: files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js:106 +msgid "Paused" msgstr "" -#: files/usr/share/cinnamon/applets/settings@cinnamon.org/applet.js:123 -msgid "Troubleshoot" +#: files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js:107 +msgid "Stopped" msgstr "" -#: files/usr/share/cinnamon/applets/settings@cinnamon.org/applet.js:124 -msgid "Restart Cinnamon" +#: files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js:373 +#: files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js:489 +msgid "Unknown Artist" msgstr "" -#: files/usr/share/cinnamon/applets/settings@cinnamon.org/applet.js:129 -msgid "Looking Glass" +#: files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js:374 +#: files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js:493 +msgid "Unknown Album" msgstr "" -#: files/usr/share/cinnamon/applets/settings@cinnamon.org/applet.js:133 -msgid "Restore all settings to default" +#: files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js:375 +#: files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js:497 +msgid "Unknown Title" msgstr "" -#: files/usr/share/cinnamon/applets/settings@cinnamon.org/applet.js:143 -msgid "Panel Edit mode" +#: files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js:386 +msgid "Previous" msgstr "" -#: files/usr/share/cinnamon/applets/show-desktop@cinnamon.org/applet.js:16 -msgid "Show desktop" +#: files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js:389 +msgid "Play" msgstr "" -#: files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js:104 -msgid "Playing" +#: files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js:392 +msgid "Stop" msgstr "" -#: files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js:105 -msgid "Paused" +#: files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js:395 +msgid "Next" msgstr "" -#: files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js:106 -msgid "Stopped" +#: files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js:409 +msgid "Open Player" msgstr "" -#: files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js:372 -#: files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js:482 -msgid "Unknown Artist" +#: files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js:419 +msgid "Quit Player" msgstr "" -#: files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js:373 -#: files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js:486 -msgid "Unknown Album" +#: files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js:744 +msgid "Mute output" msgstr "" -#: files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js:374 -#: files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js:490 -msgid "Unknown Title" +#: files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js:745 +msgid "Mute input" msgstr "" -#: files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js:852 +#: files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js:884 msgid "Launch player..." msgstr "" -#: files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js:865 -#: files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js:928 -#: files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js:929 -#: files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js:933 -#: files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js:934 -#: files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js:946 -#: files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js:947 +#: files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js:897 +#: files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js:960 +#: files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js:961 +#: files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js:966 +#: files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js:967 +#: files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js:986 +#: files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js:987 msgid "Volume" msgstr "" -#: files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js:872 +#: files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js:904 msgid "Microphone" msgstr "" -#: files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js:881 +#: files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js:913 msgid "Output device..." msgstr "" @@ -1226,46 +1213,54 @@ msgstr "" msgid "Empty" msgstr "" -#: files/usr/share/cinnamon/applets/window-list@cinnamon.org/applet.js:38 -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1026 +#: files/usr/share/cinnamon/applets/window-list@cinnamon.org/applet.js:41 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1759 msgid "Close" msgstr "" -#: files/usr/share/cinnamon/applets/window-list@cinnamon.org/applet.js:42 -#: files/usr/share/cinnamon/applets/window-list@cinnamon.org/applet.js:797 +#: files/usr/share/cinnamon/applets/window-list@cinnamon.org/applet.js:44 +msgid "Close all" +msgstr "" + +#: files/usr/share/cinnamon/applets/window-list@cinnamon.org/applet.js:47 +msgid "Close others" +msgstr "" + +#: files/usr/share/cinnamon/applets/window-list@cinnamon.org/applet.js:51 +#: files/usr/share/cinnamon/applets/window-list@cinnamon.org/applet.js:839 msgid "Restore" msgstr "" -#: files/usr/share/cinnamon/applets/window-list@cinnamon.org/applet.js:44 -#: files/usr/share/cinnamon/applets/window-list@cinnamon.org/applet.js:802 -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1027 +#: files/usr/share/cinnamon/applets/window-list@cinnamon.org/applet.js:53 +#: files/usr/share/cinnamon/applets/window-list@cinnamon.org/applet.js:844 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1760 msgid "Minimize" msgstr "" -#: files/usr/share/cinnamon/applets/window-list@cinnamon.org/applet.js:47 -#: files/usr/share/cinnamon/applets/window-list@cinnamon.org/applet.js:102 -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1028 +#: files/usr/share/cinnamon/applets/window-list@cinnamon.org/applet.js:56 +#: files/usr/share/cinnamon/applets/window-list@cinnamon.org/applet.js:117 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1761 msgid "Maximize" msgstr "" -#: files/usr/share/cinnamon/applets/window-list@cinnamon.org/applet.js:50 +#: files/usr/share/cinnamon/applets/window-list@cinnamon.org/applet.js:59 msgid "Move to left workspace" msgstr "" -#: files/usr/share/cinnamon/applets/window-list@cinnamon.org/applet.js:53 +#: files/usr/share/cinnamon/applets/window-list@cinnamon.org/applet.js:62 msgid "Move to right workspace" msgstr "" -#: files/usr/share/cinnamon/applets/window-list@cinnamon.org/applet.js:56 -#: files/usr/share/cinnamon/applets/window-list@cinnamon.org/applet.js:88 +#: files/usr/share/cinnamon/applets/window-list@cinnamon.org/applet.js:65 +#: files/usr/share/cinnamon/applets/window-list@cinnamon.org/applet.js:103 msgid "Visible on all workspaces" msgstr "" -#: files/usr/share/cinnamon/applets/window-list@cinnamon.org/applet.js:84 +#: files/usr/share/cinnamon/applets/window-list@cinnamon.org/applet.js:99 msgid "Only on this workspace" msgstr "" -#: files/usr/share/cinnamon/applets/window-list@cinnamon.org/applet.js:100 +#: files/usr/share/cinnamon/applets/window-list@cinnamon.org/applet.js:115 msgid "Unmaximize" msgstr "" @@ -1281,469 +1276,615 @@ msgstr "" msgid "Configure display settings..." msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:28 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:34 msgid "Desktop Settings" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:29 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:35 msgid "Desktop Configuration Tool" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:30 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:36 msgid "Fine-tune desktop settings" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:155 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:39 +msgid "Wallpaper" +msgstr "" + +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:45 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1238 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1999 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2010 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2021 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2032 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2043 +msgid "None" +msgstr "" + +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:46 +msgid "Horizontal" +msgstr "" + +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:47 +msgid "Vertical" +msgstr "" + +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:51 +msgid "No picture" +msgstr "" + +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:52 +msgid "Mosaic" +msgstr "" + +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:53 +msgid "Centered" +msgstr "" + +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:54 +msgid "Scaled" +msgstr "" + +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:55 +msgid "Stretched" +msgstr "" + +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:57 +msgid "Spanned" +msgstr "" + +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:480 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:625 +msgid "Add wallpapers" +msgstr "" + +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:503 +msgid "Folder" +msgstr "" + +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:511 +msgid "Recursive listing" +msgstr "" + +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:516 +msgid "Delay" +msgstr "" + +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:612 +msgid "Mode" +msgstr "" + +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:620 +msgid "Remove wallpaper" +msgstr "" + +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:648 +msgid "Advanced options" +msgstr "" + +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:655 +msgid "Picture aspect" +msgstr "" + +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:661 +msgid "Gradient" +msgstr "" + +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:668 +msgid "Colors" +msgstr "" + +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:742 msgid "Get new themes" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:158 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:745 msgid "Cinnamon themes" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:165 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:752 msgid "Window theme" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:167 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:754 msgid "Menus Have Icons" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:169 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:756 msgid "Buttons Have Icons" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:171 -msgid "Always Use Location Entry" +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:759 +msgid "Always Use Location Entry In Nemo" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:173 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:761 msgid "Cursor theme" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:175 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:763 msgid "Keybinding theme" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:177 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:765 msgid "Icon theme" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:179 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:767 msgid "GTK+ theme" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:182 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:770 msgid "Other settings" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:276 -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:367 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:864 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:955 msgid "Enable" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:280 -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:374 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:868 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:962 msgid "Description" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:302 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:890 msgid "Get new extensions" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:397 -msgid "Restore to default" +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:959 +#: files/usr/share/cinnamon/applets/panel-launchers@cinnamon.org/add-panel-launcher.py:140 +msgid "Icon" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:401 -msgid "Get new applets" +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:985 +msgid "Restore to default" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:589 -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1235 -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1249 -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1263 -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1277 -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1291 -msgid "None" +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:989 +msgid "Get new applets" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:755 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1489 msgid "Region" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:777 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1511 msgid "City" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:884 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1617 msgid ":" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:891 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1624 msgid "Date : " msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:893 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1626 msgid "Time : " msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1006 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1739 msgid "Left side title bar buttons" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1012 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1745 msgid "Right side title bar buttons" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1090 -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1387 -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1401 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1833 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2168 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2182 msgid "Cinnamon Settings" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1107 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1850 msgid "All Settings" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1116 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1859 msgid "Menu text" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1117 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1860 msgid "Menu icon" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1118 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1861 msgid "Menu hover delay" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1118 -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1240 -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1254 -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1268 -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1282 -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1296 -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1346 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1861 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1871 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1875 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2002 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2013 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2024 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2035 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2046 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2112 msgid "milliseconds" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1119 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1862 msgid "Activate menu on hover" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1120 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1863 msgid "Show bookmarks and places" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1121 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1864 msgid "Show recent files" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1125 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1868 msgid "Auto-hide panel" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1126 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1871 +msgid "Show delay" +msgstr "" + +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1875 +msgid "Hide delay" +msgstr "" + +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1878 msgid "Traditional (panel at the bottom)" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1126 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1878 msgid "Flipped (panel at the top)" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1126 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1878 msgid "Classic (panels at the top and at the bottom)" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1127 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1879 msgid "Panel layout" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1130 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1882 msgid "Note: If you change the layout you will need to restart Cinnamon." msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1132 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1885 msgid "Use customized panel size (otherwise it's defined by the theme)" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1133 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1888 +msgid "" +"Allow Cinnamon to scale panel text and icons according to the panel heights" +msgstr "" + +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1892 msgid "Top panel height" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1133 -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1134 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1892 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1896 msgid "Pixels" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1134 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1896 msgid "Bottom panel height" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1135 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1899 msgid "Panel edit mode" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1137 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1900 msgid "Calendar" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1139 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1902 msgid "Show week dates in calendar" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1140 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1903 msgid "Date format for the panel" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1141 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1904 msgid "Date format inside the date applet" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1142 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1905 msgid "Generate your own date formats" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1148 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1911 msgid "Use network time" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1164 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1927 msgid "Hot corner" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1166 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1929 msgid "Hot corner icon visible" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1167 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1930 msgid "Hot corner enabled" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1170 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1933 msgid "Hot corner position:" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1172 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1935 msgid "Top left" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1172 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1935 msgid "Top right" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1172 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1935 msgid "Bottom left" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1172 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1935 msgid "Bottom right" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1179 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1941 msgid "Hot corner function:" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1181 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1943 msgid "Workspace selection (ala Compiz Expo)" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1181 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1943 msgid "Window selection (ala Compiz Scale)" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1186 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1947 msgid "Expo applet: activate on hover" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1187 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1948 msgid "Scale applet: activate on hover" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1192 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1953 msgid "Effects" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1194 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1955 msgid "Enable desktop effects" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1195 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1958 msgid "Enable desktop effects on dialog boxes" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1233 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1997 msgid "Closing windows:" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1235 -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1249 -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1263 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1999 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2010 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2021 msgid "Fade" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1247 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2008 msgid "Mapping windows:" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1261 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2019 msgid "Minimizing windows:" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1263 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2021 msgid "Traditional" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1275 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2030 msgid "Maximizing windows:" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1289 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2041 msgid "Unmaximizing windows:" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1303 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2052 msgid "Extensions" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1309 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2059 msgid "Desktop" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1311 -msgid "Have file manager handle the desktop" +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2061 +msgid "Have file manager (Nemo) handle the desktop" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1313 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2064 msgid "Computer icon visible on desktop" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1315 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2068 msgid "Home icon visible on desktop" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1317 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2072 msgid "Network Servers icon visible on desktop" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1319 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2076 msgid "Trash icon visible on desktop" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1321 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2080 msgid "Show mounted volumes on the desktop" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1325 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2085 msgid "Action on title bar double-click" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1328 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2088 msgid "Action on title bar middle-click" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1331 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2091 msgid "Action on title bar right-click" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1334 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2094 msgid "Window focus mode" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1339 -msgid "Enable ALT+Tab outline and window preview" -msgstr "" - -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1340 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2099 msgid "Enable Edge Tiling (\"Aero Snap\")" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1341 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2100 msgid "Enable Edge Flip" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1343 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2101 +msgid "Attach dialog windows to their parent window's titlebar" +msgstr "" + +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2102 +msgid "Icons only" +msgstr "" + +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2102 +msgid "Icons and thumbnails" +msgstr "" + +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2102 +msgid "Icons and window preview" +msgstr "" + +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2102 +msgid "Window preview (no icons)" +msgstr "" + +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2103 +msgid "ALT-tab switcher style" +msgstr "" + +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2105 +msgid "Enable mouse-wheel scrolling in Window List applet" +msgstr "" + +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2107 msgid "Workspaces" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1345 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2109 msgid "Enable workspace OSD" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1346 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2112 msgid "Workspace OSD duration" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1347 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2116 msgid "Workspace OSD horizontal position" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1347 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2116 msgid "percent of the monitor's width" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1348 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2120 msgid "Workspace OSD vertical position" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1348 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2120 msgid "percent of the monitor's height" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1349 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2123 msgid "Only use workspaces on primary monitor (requires Cinnamon restart)" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1350 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2124 msgid "Display Expo view as a grid" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1352 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2126 msgid "Fonts" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1354 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2128 msgid "Text scaling factor" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1355 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2129 msgid "Default font" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1356 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2130 msgid "Document font" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1357 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2131 msgid "Monospace font" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1358 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2132 msgid "Window title font" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1359 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2133 msgid "Hinting" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1360 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2134 msgid "Antialiasing" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1362 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2136 msgid "General" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1364 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2138 msgid "" "Log LookingGlass output to ~/.cinnamon/glass.log (Requires Cinnamon restart)" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1365 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2139 msgid "Emulate middle click by clicking both left and right buttons" msgstr "" -#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:1366 +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2140 msgid "Display notifications" msgstr "" + +#: files/usr/lib/cinnamon-settings/cinnamon-settings.py:2146 +msgid "Backgrounds" +msgstr "" + +#: files/usr/share/cinnamon/applets/panel-launchers@cinnamon.org/add-panel-launcher.py:59 +msgid "Custom Launcher" +msgstr "" + +#: files/usr/share/cinnamon/applets/panel-launchers@cinnamon.org/add-panel-launcher.py:138 +msgid "Name" +msgstr "" + +#: files/usr/share/cinnamon/applets/panel-launchers@cinnamon.org/add-panel-launcher.py:139 +msgid "Application" +msgstr "" + +#: files/usr/share/cinnamon/applets/panel-launchers@cinnamon.org/add-panel-launcher.py:142 +msgid "Add panel launcher..." +msgstr "" + +#: files/usr/share/cinnamon/applets/panel-launchers@cinnamon.org/add-panel-launcher.py:148 +msgid "Update" +msgstr "" diff --git a/configure.ac b/configure.ac index 40aa9dc..2342e33 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ AC_PREREQ(2.63) -AC_INIT([cinnamon],[1.5.2],[https://bugzilla.gnome.org/enter_bug.cgi?product=cinnamon],[cinnamon]) +AC_INIT([cinnamon],[1.6.0],[https://bugzilla.gnome.org/enter_bug.cgi?product=cinnamon],[cinnamon]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_SRCDIR([src/cinnamon-global.c]) diff --git a/data/cinnamon.desktop.in.in b/data/cinnamon.desktop.in.in index 7b2a7df..1ba3ce0 100644 --- a/data/cinnamon.desktop.in.in +++ b/data/cinnamon.desktop.in.in @@ -7,7 +7,7 @@ X-GNOME-Bugzilla-Bugzilla=GNOME X-GNOME-Bugzilla-Product=cinnamon X-GNOME-Bugzilla-Component=general X-GNOME-Bugzilla-Version=@VERSION@ -Categories=GNOME;GTK;Core; +Categories=GNOME;GTK;System;Core; OnlyShowIn=GNOME; NoDisplay=true X-GNOME-Autostart-Phase=WindowManager diff --git a/data/org.cinnamon.gschema.xml.in b/data/org.cinnamon.gschema.xml.in index 1f45cf2..3ec4882 100644 --- a/data/org.cinnamon.gschema.xml.in +++ b/data/org.cinnamon.gschema.xml.in @@ -74,6 +74,14 @@ + + false + <_summary>Scale panel applet text and icons + <_description> + Whether the panel icons and text from applets is resized according to the custom panel height + + + true <_summary>Enable desktop effects @@ -91,7 +99,7 @@ - "scale" + "none" <_summary>Effect used when closing windows <_description> An effect: scale, fade, none @@ -156,7 +164,7 @@ - 250 + 150 <_summary>Duration of the effect (in milliseconds) <_description> Duration of the effect (in milliseconds) @@ -336,7 +344,7 @@ - [ 'firefox.desktop', 'gnome-terminal.desktop', 'nautilus.desktop'] + [ 'firefox.desktop', 'gnome-terminal.desktop', 'nemo.desktop'] <_summary>Desktop files of the applications to put in the panel launchers applet <_description> Cinnamon allows to show applications launchers on the panel. @@ -356,7 +364,7 @@ - [ 'firefox.desktop', 'mintInstall.desktop', 'cinnamon-settings.desktop', 'xchat.desktop', 'gnome-terminal.desktop', 'nautilus.desktop' ] + [ 'firefox.desktop', 'mintInstall.desktop', 'cinnamon-settings.desktop', 'xchat.desktop', 'gnome-terminal.desktop', 'nemo.desktop' ] <_summary>List of desktop file IDs for favorite applications <_description> The applications corresponding to these identifiers @@ -364,11 +372,11 @@ - - [ 'WORKSPACE 1', 'WORKSPACE 2' ] - <_summary>List of workspace names + + [] + <_summary>List of non-default workspace names <_description> - The names of the workspaces. + The user-set names of the workspaces. @@ -456,9 +464,20 @@ <_summary>Whether panel launchers are draggable - + + "icons" + <_summary>ALT-tab switcher style + <_description> + Controls the style of the ALT-tab window switcher. Can be any combination of "icons", "preview" and "thumbnails", separated by "+". + + + + false - <_summary>Enable ALT+Tab outline and window preview + Enable mouse-scroll in window-list applet + + When enabled the mouse wheel can scroll through the items in the window list applet. + @@ -606,4 +625,36 @@ + + + "wallpaper" + Background mode + + This key defines the whether the desktop background shows one + single wallpaper, a slideshow or an online slideshow (Flickr). + + + + "" + Folder to use for the slideshow + + This key defines the folder to use for the slideshow. + + + + false + Whether to list files recursively for the slideshow + + This key defines whether to list files recursively for the slideshow. + + + + 15 + Delay for the slideshow + + This key defines the delay for the slideshow. + + + + diff --git a/data/theme/checkbox-focused.svg b/data/theme/checkbox-focused.svg new file mode 100644 index 0000000..ed77ad2 --- /dev/null +++ b/data/theme/checkbox-focused.svg @@ -0,0 +1,289 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/theme/checkbox-off-focused.svg b/data/theme/checkbox-off-focused.svg new file mode 100644 index 0000000..4f77320 --- /dev/null +++ b/data/theme/checkbox-off-focused.svg @@ -0,0 +1,198 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/data/theme/checkbox-off.svg b/data/theme/checkbox-off.svg new file mode 100644 index 0000000..fe4ba51 --- /dev/null +++ b/data/theme/checkbox-off.svg @@ -0,0 +1,218 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/data/theme/checkbox.svg b/data/theme/checkbox.svg new file mode 100644 index 0000000..119b600 --- /dev/null +++ b/data/theme/checkbox.svg @@ -0,0 +1,243 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + diff --git a/data/theme/cinnamon.css b/data/theme/cinnamon.css index 0536c2b..c0555fa 100644 --- a/data/theme/cinnamon.css +++ b/data/theme/cinnamon.css @@ -277,6 +277,7 @@ StScrollBar StButton#vhandle:hover { spacing: 0px; margin: 0px; } + .panel-corner { -panel-corner-radius: 0px; -panel-corner-background-color: black; @@ -877,13 +878,8 @@ StScrollBar StButton#vhandle:hover { border-color: rgba(0,0,0,0); color: white; } -.switcher-outline-background { - background-color: rgba(50, 50, 50, 0.0); -} -.switcher-outline-frame { - border-color: #aaaaaa; - border: 0px; - border-radius: 0px; +.switcher-preview-backdrop { + background-color: rgba(0,0,0,0.7); } /* =================================================================== @@ -1233,7 +1229,6 @@ StScrollBar StButton#vhandle:hover { /* Main menu title */ .menu-favorites-box { - width: 50px; margin: auto; padding: 10px; border: 1px solid #666; @@ -1257,22 +1252,7 @@ StScrollBar StButton#vhandle:hover { box-shadow: inset 0px 0px 1px 1px rgba(255,255,255,0.06); border-radius: 4px; } -.menu-help-button { - padding-top: 2px; - padding-left: 5px; - padding-right: 5px; - padding-bottom: 2px; -} -.menu-help-button:hover { - color: white; - background-gradient-direction: vertical; - background-gradient-start: rgba(255,255,255,0.2); - background-gradient-end: rgba(255,255,255,0.08); - box-shadow: inset 0px 0px 1px 1px rgba(255,255,255,0.06); - border-radius: 4px; -} .menu-places-box { - width: 50px; margin: auto; padding: 10px; border: 0px solid #666; @@ -1301,7 +1281,6 @@ StScrollBar StButton#vhandle:hover { padding-right: 7px; padding-bottom: 7px; } -.menu-application-button:hover, .menu-application-button-selected { padding-top: 7px; padding-left: 7px; @@ -1320,19 +1299,12 @@ StScrollBar StButton#vhandle:hover { .menu-application-button-label:rtl { padding-right: 5px; } -.menu-place-cat-button-label:ltr { - padding-top: 4px; - padding-left: 5px; -} -.menu-place-cat-button-label:rtl { - padding-top: 4px; - padding-right: 5px; -} .menu-category-button { padding-top: 7px; padding-left: 7px; padding-right: 7px; padding-bottom: 7px; + transition-duration: 75; } .menu-category-button-greyed { padding-top: 7px; @@ -1353,7 +1325,7 @@ StScrollBar StButton#vhandle:hover { background-gradient-end: rgba(255,255,255,0.08); box-shadow: inset 0px 0px 1px 1px rgba(255,255,255,0.06); border-radius: 4px; - transition-duration: 300; + transition-duration: 75; } .menu-category-button-label:ltr { padding-left: 5px; @@ -1451,7 +1423,7 @@ StScrollBar StButton#vhandle:hover { border-radius: 2px 2px 0px 0px; padding-left: 5px; padding-right: 5px; - transition-duration: 300; + transition-duration: 100; } .window-list-item-box:active, .window-list-item-box:checked, @@ -1624,12 +1596,10 @@ StScrollBar StButton#vhandle:hover { * ===================================================================*/ .applet-box { padding-left: 3px; - padding-right: 3px; + padding-right: 3px; color: #ccc; text-shadow: black 0px 0px 2px; transition-duration: 300; - min-width: 1em; - } .applet-box:hover { color: #fff; @@ -1645,6 +1615,7 @@ StScrollBar StButton#vhandle:hover { } .applet-icon { color: #ccc; + icon-size: 22px; } .applet-icon:hover, .applet-box:hover > .applet-icon { @@ -1703,3 +1674,63 @@ StScrollBar StButton#vhandle:hover { .notification-applet-container { max-height: 100px; } + +/* Check Boxes */ +.check-box CinnamonGenericContainer { + spacing: .2em; + min-height: 30px; + padding-top: 2px; +} + +.check-box StBin { + width: 24px; + height: 18px; + background-image: url("checkbox-off.svg"); +} + +.check-box:focus StBin { + background-image: url("checkbox-off-focused.svg"); +} + +.check-box:checked StBin { + background-image: url("checkbox.svg"); +} + +.check-box:focus:checked StBin { + background-image: url("checkbox-focused.svg"); +} + +.check-box StLabel { + font-weight: normal; +} + +.radiobutton CinnamonGenericContainer { + spacing: .2em; + height: 26px; + padding-top: 2px; +} + +.radiobutton StBin { + width: 22px; + height: 22px; + background-image: url("radiobutton-off.svg"); + border-radius: 15px; +} + +.radiobutton:focus StBin { + background-image: url("radiobutton-off.svg"); +} + +.radiobutton:checked StBin { + background-image: url("radiobutton.svg"); +} + +.radiobutton:focus:checked StBin { + background-image: url("radiobutton.svg"); +} + +.radiobutton StLabel { + padding-top: 4px; + font-size: 0.9em; + box-shadow: none; +} diff --git a/data/theme/menu.png b/data/theme/menu.png index 131ae51..0830861 100644 Binary files a/data/theme/menu.png and b/data/theme/menu.png differ diff --git a/data/theme/radiobutton-off.svg b/data/theme/radiobutton-off.svg new file mode 100644 index 0000000..8c03625 --- /dev/null +++ b/data/theme/radiobutton-off.svg @@ -0,0 +1,120 @@ + + + + selected button + + + + + + + + + + + image/svg+xml + + image/svg+xml + + + + + + Layer 1 + + + + + + + + diff --git a/data/theme/radiobutton.svg b/data/theme/radiobutton.svg new file mode 100644 index 0000000..ed61c3f --- /dev/null +++ b/data/theme/radiobutton.svg @@ -0,0 +1,99 @@ + + + + + + + + + image/svg+xml + + image/svg+xml + + + + + + Layer 1 + + + + + + + + + diff --git a/debian/changelog b/debian/changelog index f7bc346..f307e79 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,45 @@ +cinnamon (1.6.0) maya; urgency=low + + * 1.6.0 + + -- Clement Lefebvre Mon, 17 Sep 2012 16:42:54 +0100 + +cinnamon (1.5.8) maya; urgency=low + + * 1.5.8 + + -- Clement Lefebvre Sun, 09 Sep 2012 13:36:26 +0100 + +cinnamon (1.5.7) maya; urgency=low + + * 1.5.7 + + -- Clement Lefebvre Wed, 05 Sep 2012 18:18:09 +0100 + +cinnamon (1.5.6) maya; urgency=low + + * 1.5.6 + + -- Clement Lefebvre Wed, 05 Sep 2012 16:25:33 +0100 + +cinnamon (1.5.5) maya; urgency=low + + * 1.5.5 + + -- Clement Lefebvre Wed, 05 Sep 2012 16:22:08 +0100 + +cinnamon (1.5.4) maya; urgency=low + + * 1.5.4 + + -- Clement Lefebvre Wed, 05 Sep 2012 12:31:55 +0100 + +cinnamon (1.5.3) maya; urgency=low + + * 1.5.3 + + -- Clement Lefebvre Wed, 29 Aug 2012 11:36:31 +0100 + cinnamon (1.5.2) maya; urgency=low * 1.5.2 diff --git a/debian/cinnamon.gsettings-override b/debian/cinnamon.gsettings-override index f66580e..3549455 100644 --- a/debian/cinnamon.gsettings-override +++ b/debian/cinnamon.gsettings-override @@ -1,3 +1,3 @@ [org.cinnamon] -favorite-apps=[ 'firefox.desktop', 'mintInstall.desktop', 'cinnamon-settings.desktop', 'xchat.desktop', 'gnome-terminal.desktop', 'nautilus.desktop' ] +favorite-apps=[ 'firefox.desktop', 'mintInstall.desktop', 'cinnamon-settings.desktop', 'xchat.desktop', 'gnome-terminal.desktop', 'nemo.desktop' ] diff --git a/debian/cinnamon.mk b/debian/cinnamon.mk index 79247cf..70638b6 100644 --- a/debian/cinnamon.mk +++ b/debian/cinnamon.mk @@ -44,4 +44,12 @@ $(patsubst %,binary-install/%,$(DEB_PACKAGES)) :: binary-install/%: $(if $(wildcard /usr/bin/dh_gconf),dh_gconf -p$(cdbs_curpkg) $(DEB_DH_GCONF_ARGS)) $(if $(wildcard /usr/bin/dh_icons),dh_icons -p$(cdbs_curpkg) $(DEB_DH_ICONS_ARGS)) +configure: + ./autogen.sh $(DEB_CONFIGURE_EXTRA_FLAGS) + +build: configure + dh_auto_build + +binary-arch: build + endif diff --git a/debian/control b/debian/control index 7dbca8e..7393761 100644 --- a/debian/control +++ b/debian/control @@ -70,9 +70,11 @@ Depends: ${gir:Depends}, gir1.2-upowerglib-1.0, python, python-dbus, + python-gconf, + python-imaging, pkg-config, mesa-utils -Recommends: gnome-control-center, gnome-user-guide, gnome-themes-standard, gnome-session-fallback +Recommends: gnome-control-center, gnome-user-guide, gnome-themes-standard, gnome-session-fallback, gnome-terminal, nemo Breaks: gnome-control-center (<< 1:3.0) Conflicts: cinnamon-session, cinnamon-settings Provides: cinnamon-session, cinnamon-settings diff --git a/debian/control.in b/debian/control.in index 4fe6c05..07c6659 100644 --- a/debian/control.in +++ b/debian/control.in @@ -12,6 +12,7 @@ Build-Depends: cdbs, gobject-introspection (>= 1.29.15), gir1.2-json-1.0, gnome-bluetooth (>= 3.1.0), + gnome-common, gsettings-desktop-schemas-dev (>= 0.1.7), libcaribou-dev, libcroco3-dev (>= 0.6.2), @@ -65,9 +66,12 @@ Depends: ${gir:Depends}, gir1.2-upowerglib-1.0, python, python-dbus, + python-gconf, + python-imaging, pkg-config, - mesa-utils -Recommends: gnome-control-center, gnome-user-guide, gnome-themes-standard, gnome-session-fallback + mesa-utils, + python-lxml +Recommends: gnome-control-center, gnome-user-guide, gnome-themes-standard, gnome-session-fallback, gnome-terminal, nemo Breaks: gnome-control-center (<< 1:3.0) Conflicts: cinnamon-session, cinnamon-settings Provides: cinnamon-session, cinnamon-settings diff --git a/files/Makefile.in b/files/Makefile.in index 7950186..0c21f15 100644 --- a/files/Makefile.in +++ b/files/Makefile.in @@ -2,6 +2,8 @@ all: clean: +distclean: + install: find -mindepth 1 -maxdepth 1 -type d -exec cp -R {} $(DESTDIR)/ \; diff --git a/files/usr/lib/cinnamon-settings/cinnamon-settings.py b/files/usr/lib/cinnamon-settings/cinnamon-settings.py index 5c88cfb..4ad0578 100755 --- a/files/usr/lib/cinnamon-settings/cinnamon-settings.py +++ b/files/usr/lib/cinnamon-settings/cinnamon-settings.py @@ -6,16 +6,22 @@ import sys import string import gettext - from gi.repository import Gio, Gtk, GObject + from gi.repository import Gio, Gtk, GObject, Gdk from gi.repository import GdkPixbuf import gconf import json import dbus - import tz + import tz import time from datetime import datetime from user import home import thread + import urllib + import lxml.etree + import locale + import imtools + import Image + import tempfile except Exception, detail: print detail sys.exit(1) @@ -28,7 +34,100 @@ menuName = _("Desktop Settings") menuGenericName = _("Desktop Configuration Tool") menuComment = _("Fine-tune desktop settings") - + +BACKGROUND_MODES = [ + ("wallpaper", _("Wallpaper")), + #("slideshow", _("Slideshow")), + #("flickr", _("Flickr")) +] + +BACKGROUND_COLOR_SHADING_TYPES = [ + ("solid", _("None")), + ("horizontal", _("Horizontal")), + ("vertical", _("Vertical")) +] + +BACKGROUND_PICTURE_OPTIONS = [ + ("none", _("No picture")), + ("wallpaper", _("Mosaic")), + ("centered", _("Centered")), + ("scaled", _("Scaled")), + ("stretched", _("Stretched")), + ("zoom", _("Zoom")), + ("spanned", _("Spanned")) +] + +BACKGROUND_ICONS_SIZE = 115 + +class PixCache(object): + def __init__(self): + self._data = {} + def get_pix(self, filename, size = None): + if not filename in self._data: + self._data[filename] = {} + if size in self._data[filename]: + pix = self._data[filename][size] + else: + if size: + try: + pix = GdkPixbuf.Pixbuf.new_from_file_at_size(filename, size, size) + except: + pix = None + else: + try: + pix = GdkPixbuf.Pixbuf.new_from_file(filename) + except: + pix = None + if pix: + self._data[filename][size] = pix + return pix + +PIX_CACHE = PixCache() + +# wrapper for timedated or gnome-settings-daemons DateTimeMechanism +class DateTimeWrapper: + def __init__(self): + try: + proxy = dbus.SystemBus().get_object("org.freedesktop.timedate1", "/org/freedesktop/timedate1") + self.dbus_iface = dbus.Interface(proxy, dbus_interface="org.freedesktop.timedate1") + self.properties_iface = dbus.Interface(proxy, dbus_interface=dbus.PROPERTIES_IFACE) + self.timedated = True + except dbus.exceptions.DBusException: + proxy = dbus.SystemBus().get_object("org.gnome.SettingsDaemon.DateTimeMechanism", "/") + self.dbus_iface = dbus.Interface(proxy, dbus_interface="org.gnome.SettingsDaemon.DateTimeMechanism") + self.timedated = False + + def set_time(self, seconds_since_epoch): + if self.timedated: + # timedated expects microseconds + return self.dbus_iface.SetTime(seconds_since_epoch * 1000000, False, True) + else: + return self.dbus_iface.SetTime(seconds_since_epoch) + + def get_timezone(self): + if self.timedated: + return self.properties_iface.Get("org.freedesktop.timedate1", "Timezone") + else: + return self.dbus_iface.GetTimezone() + + def set_timezone(self, tz): + if self.timedated: + return self.dbus_iface.SetTimezone(tz, True) + else: + return self.dbus_iface.SetTimezone(tz) + + def get_using_ntp(self): + if self.timedated: + return self.properties_iface.Get("org.freedesktop.timedate1", "NTP") + else: + return self.dbus_iface.GetUsingNtp() + + def set_using_ntp(self, usingNtp): + if self.timedated: + return self.dbus_iface.SetNTP(usingNtp, True) + else: + return self.dbus_iface.SetUsingNtp(usingNtp) + class SidePage: def __init__(self, name, icon, content_box): self.name = name @@ -107,6 +206,498 @@ def walk_directories(dirs, filter_func): #logging.critical("Error parsing directories", exc_info=True) return valid +def rec_mkdir(path): + if os.path.exists(path): + return + + rec_mkdir(os.path.split(path)[0]) + os.mkdir(path) + +class GSettingsColorChooser(Gtk.ColorButton): + def __init__(self, schema, key, dep_key): + Gtk.ColorButton.__init__(self) + self._schema = Gio.Settings(schema) + self._key = key + self.dep_key = dep_key + self.set_value(self._schema[self._key]) + self.connect("color-set", self._on_color_set) + self._schema.connect("changed::"+key, self._on_settings_value_changed) + self.dependency_invert = False + if self.dep_key is not None: + if self.dep_key[0] == '!': + self.dependency_invert = True + self.dep_key = self.dep_key[1:] + split = self.dep_key.split('/') + self.dep_settings = Gio.Settings.new(split[0]) + self.dep_key = split[1] + self.dep_settings.connect("changed::"+self.dep_key, self.on_dependency_setting_changed) + self.on_dependency_setting_changed(self, None) + + def _on_settings_value_changed(self, schema, key): + self.set_value(schema[key]) + def _on_color_set(self, *args): + self._schema.set_string(self._key, self.get_value()) + def get_value(self): + return self.get_color().to_string() + def set_value(self, value): + self.set_color(Gdk.color_parse(value)) + + def on_dependency_setting_changed(self, settings, dep_key): + if not self.dependency_invert: + self.set_sensitive(self.dep_settings.get_boolean(self.dep_key)) + else: + self.set_sensitive(not self.dep_settings.get_boolean(self.dep_key)) + +class ThreadedIconView(Gtk.IconView): + def __init__(self): + Gtk.IconView.__init__(self) + self.set_item_width(BACKGROUND_ICONS_SIZE * 1.1) + self._model = Gtk.ListStore(object, GdkPixbuf.Pixbuf, str) + self.set_model(self._model) + self.set_pixbuf_column(1) + self.set_markup_column(2) + + self._loading_queue = [] + self._loading_queue_lock = thread.allocate_lock() + + self._loading_lock = thread.allocate_lock() + self._loading = False + + self._loaded_data = [] + self._loaded_data_lock = thread.allocate_lock() + + def set_pictures_list(self, pictures_list): + self.clear() + for i in pictures_list: + self.add_picture(i) + + def clear(self): + self._loading_queue_lock.acquire() + self._loading_queue = [] + self._loading_queue_lock.release() + + self._loading_lock.acquire() + is_loading = self._loading + self._loading_lock.release() + while is_loading: + time.sleep(0.1) + self._loading_lock.acquire() + is_loading = self._loading + self._loading_lock.release() + + self._model.clear() + + def add_picture(self, picture): + self._loading_queue_lock.acquire() + self._loading_queue.append(picture) + self._loading_queue_lock.release() + + start_loading = False + self._loading_lock.acquire() + if not self._loading: + self._loading = True + start_loading = True + self._loading_lock.release() + + if start_loading: + GObject.timeout_add(100, self._check_loading_progress) + thread.start_new_thread(self._do_load, ()) + + def _check_loading_progress(self): + self._loading_lock.acquire() + self._loaded_data_lock.acquire() + res = self._loading + to_load = [] + while len(self._loaded_data) > 0: + to_load.append(self._loaded_data[0]) + self._loaded_data = self._loaded_data[1:] + self._loading_lock.release() + self._loaded_data_lock.release() + + for i in to_load: + self._model.append(i) + + return res + + def _do_load(self): + finished = False + while not finished: + self._loading_queue_lock.acquire() + if len(self._loading_queue) == 0: + finished = True + else: + to_load = self._loading_queue[0] + self._loading_queue = self._loading_queue[1:] + self._loading_queue_lock.release() + if not finished: + pix = PIX_CACHE.get_pix(to_load["filename"], BACKGROUND_ICONS_SIZE) + if pix != None: + try: + img = Image.open(to_load["filename"]) + if img.mode != 'RGB': + img = img.convert('RGB') + img.thumbnail((115, 115), Image.ANTIALIAS) + img = imtools.round_image(img, {}, False, None, 5, 255) + img = imtools.drop_shadow(img, 5, 5, background_color=(255, 255, 255, 0), shadow_color=0x444444, border=8, shadow_blur=3, force_background_color=False, cache=None) + # Convert Image -> Pixbuf (save to file, GTK3 is not reliable for that) + f = tempfile.NamedTemporaryFile(delete=False) + filename = f.name + f.close() + img.save(filename, "png") + pix = PIX_CACHE.get_pix(filename, BACKGROUND_ICONS_SIZE) + except Exception, detail: + print "Failed to convert %s: %s" % (to_load["filename"], detail) + pass + if "name" in to_load: + label = to_load["name"] + else: + label = os.path.split(to_load["filename"])[1] + self._loaded_data_lock.acquire() + self._loaded_data.append((to_load, pix, "%s" % label)) + self._loaded_data_lock.release() + + self._loading_lock.acquire() + self._loading = False + self._loading_lock.release() + +class BackgroundWallpaperPane (Gtk.VBox): + def __init__(self, sidepage, gnome_background_schema): + Gtk.VBox.__init__(self) + self.set_spacing(5) + + self._gnome_background_schema = gnome_background_schema + self._sidepage = sidepage + + scw = Gtk.ScrolledWindow() + scw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) + self.pack_start(scw, True, True, 0) + + self.icon_view = ThreadedIconView() + scw.add(self.icon_view) + self.icon_view.connect("selection-changed", self._on_selection_changed) + + self.update_icon_view() + + def get_selected_wallpaper(self): + selected_items = self.icon_view.get_selected_items() + if len(selected_items) == 1: + path = selected_items[0] + iter = self.icon_view.get_model().get_iter(path) + return self.icon_view.get_model().get(iter, 0)[0] + return None + + def _on_selection_changed(self, iconview): + self._sidepage.remove_wallpaper_button.set_sensitive(False) + wallpaper = self.get_selected_wallpaper() + if wallpaper: + for key in wallpaper: + if key == "filename": + self._gnome_background_schema.set_string("picture-uri", "file://" + wallpaper[key]) + elif key == "pcolor": + self._gnome_background_schema.set_string("primary-color", wallpaper[key]) + elif key == "scolor": + self._gnome_background_schema.set_string("secondary-color", wallpaper[key]) + elif key == "shade_type": + self._gnome_background_schema.set_string("color-shading-type", wallpaper[key]) + elif key == "options": + self._gnome_background_schema.set_string("picture-options", wallpaper[key]) + if (not "metadataFile" in wallpaper) or (wallpaper["metadataFile"] == ""): + self._sidepage.remove_wallpaper_button.set_sensitive(True) + + def splitLocaleCode(self, localeCode): + loc = localeCode.partition("_") + loc = (loc[0], loc[2]) + return loc + + def getLocalWallpaperName(self, names, loc): + result = "" + mainLocFound = False + for wp in names: + wpLoc = wp[0] + wpName = wp[1] + if wpLoc == ("", ""): + if not mainLocFound: + result = wpName + elif wpLoc[0] == loc[0]: + if wpLoc[1] == loc[1]: + return wpName + elif wpLoc[1] == "": + result = wpName + mainLocFound = True + return result + + + + def parse_xml_backgrounds_list(self, filename): + try: + locAttrName = "{http://www.w3.org/XML/1998/namespace}lang" + loc = self.splitLocaleCode(locale.getdefaultlocale()[0]) + res = [] + subLocaleFound = False + f = open(filename) + rootNode = lxml.etree.fromstring(f.read()) + f.close() + if rootNode.tag == "wallpapers": + for wallpaperNode in rootNode: + if wallpaperNode.tag == "wallpaper" and wallpaperNode.get("deleted") != "true": + wallpaperData = {"metadataFile": filename} + names = [] + for prop in wallpaperNode: + if type(prop.tag) == str: + if prop.tag != "name": + wallpaperData[prop.tag] = prop.text + else: + propAttr = prop.attrib + wpName = prop.text + locName = self.splitLocaleCode(propAttr.get(locAttrName)) if propAttr.has_key(locAttrName) else ("", "") + names.append((locName, wpName)) + wallpaperData["name"] = self.getLocalWallpaperName(names, loc) + + if "filename" in wallpaperData and wallpaperData["filename"] != "" and os.path.exists(wallpaperData["filename"]) and os.access(wallpaperData["filename"], os.R_OK): + if wallpaperData["name"] == "": + wallpaperData["name"] = os.path.basename(wallpaperData["filename"]) + res.append(wallpaperData) + return res + except: + return [] + + def update_icon_view(self): + pictures_list = [] + for i in os.listdir("/usr/share/gnome-background-properties"): + if i.endswith(".xml"): + pictures_list += self.parse_xml_backgrounds_list(os.path.join("/usr/share/gnome-background-properties", i)) + + path = os.path.join(os.getenv("HOME"), ".cinnamon", "backgrounds") + if os.path.exists(path): + for i in os.listdir(path): + filename = os.path.join(path, i) + if commands.getoutput("file -bi \"%s\"" % filename).startswith("image/"): + pictures_list.append({"filename": filename}) + self.icon_view.set_pictures_list(pictures_list) + +class AddWallpapersDialog(Gtk.FileChooserDialog): + def __init__(self, parent = None): + Gtk.FileChooserDialog.__init__(self, _("Add wallpapers"), parent, Gtk.FileChooserAction.OPEN) + self.add_button(Gtk.STOCK_OPEN, Gtk.ResponseType.OK) + self.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL) + self.set_select_multiple(True) + + def run(self): + self.show_all() + resp = Gtk.FileChooserDialog.run(self) + self.hide() + if resp == Gtk.ResponseType.OK: + res = self.get_filenames() + else: + res = [] + return res + +class BackgroundSlideshowPane(Gtk.Table): + def __init__(self, sidepage, gnome_background_schema, cinnamon_background_schema): + Gtk.Table.__init__(self) + self.set_col_spacings(5) + self.set_row_spacings(5) + + self._cinnamon_background_schema = cinnamon_background_schema + + l = Gtk.Label(_("Folder")) + l.set_alignment(0, 0.5) + self.attach(l, 0, 1, 0, 1, xoptions = Gtk.AttachOptions.FILL, yoptions = 0) + self.folder_selector = Gtk.FileChooserButton() + self.folder_selector.set_action(Gtk.FileChooserAction.SELECT_FOLDER) + self.folder_selector.connect("file-set", self._on_folder_selected) + self.folder_selector.set_filename(self._cinnamon_background_schema["slideshow-folder"]) + self.attach(self.folder_selector, 1, 2, 0, 1, xoptions = Gtk.AttachOptions.FILL | Gtk.AttachOptions.EXPAND, yoptions = 0) + self.recursive_cb = Gtk.CheckButton(_("Recursive listing")) + self.recursive_cb.set_active(self._cinnamon_background_schema.get_boolean("slideshow-recursive")) + self.recursive_cb.connect("toggled", self._on_recursive_toggled) + self.attach(self.recursive_cb, 2, 3, 0, 1, xoptions = Gtk.AttachOptions.FILL, yoptions = 0) + + l = Gtk.Label(_("Delay")) + l.set_alignment(0, 0.5) + self.attach(l, 0, 1, 1, 2, xoptions = Gtk.AttachOptions.FILL, yoptions = 0) + self.delay_button = Gtk.SpinButton() + self.attach(self.delay_button, 1, 3, 1, 2, xoptions = Gtk.AttachOptions.FILL | Gtk.AttachOptions.EXPAND, yoptions = 0) + self.delay_button.set_increments(1, 10) + self.delay_button.set_range(1, 120) + self.delay_button.set_value(self._cinnamon_background_schema.get_int("slideshow-delay")) + self.delay_button.connect("value-changed", self._on_delay_changed) + + def _on_recursive_toggled(self, button): + self._cinnamon_background_schema.set_boolean("slideshow-recursive", button.get_active()) + self.update_list() + + def _on_delay_changed(self, button): + self._cinnamon_background_schema.set_int("slideshow-delay", int(button.get_value())) + self.update_list() + + def _on_folder_selected(self, button): + self._cinnamon_background_schema.set_string("slideshow-folder", button.get_filename()) + self.update_list() + + def update_list(self): + thread.start_new_thread(self._do_update_list, (self.folder_selector.get_filename(), self.recursive_cb.get_active(), int(self.delay_button.get_value()))) + + def _list_pictures(self, res, path, files): + for i in files: + filename = os.path.join(path, i) + if commands.getoutput("file -bi \"%s\"" % filename).startswith("image/"): + res.append(filename) + + def _do_update_list(self, folder, recursive, delay, transition_duration = 0): + if os.path.exists(folder) and os.path.isdir(folder): + files = [] + if recursive: + os.path.walk(folder, self._list_pictures, files) + else: + for i in os.listdir(folder): + filename = os.path.join(folder, i) + if commands.getoutput("file -bi \"%s\"" % filename).startswith("image/"): + files.append(filename) + xml_data = "\n" + prev_file = None + first_file = None + for filename in files: + if prev_file: + xml_data += "\n%.1f\n%s\n%s\n\n" % (transition_duration, prev_file, filename) + else: + first_file = filename + xml_data += "\n%.1f\n%s\n\n" % (60 * delay, filename) + prev_file = filename + if first_file and prev_file and first_file != prev_file: + xml_data += "\n%.1f\n%s\n%s\n\n" % (transition_duration, prev_file, first_file) + xml_data += "" + + if not os.path.exists(os.path.join(os.getenv("HOME"), ".cinnamon", "backgrounds")): + rec_mkdir(os.path.join(os.getenv("HOME"), ".cinnamon", "backgrounds")) + filename = os.path.join(os.getenv("HOME"), ".cinnamon", "backgrounds", "slideshow.xml") + f = open(filename, "w") + f.write(xml_data) + f.close() + Gio.Settings("org.gnome.desktop.background").set_string("picture-uri", "file://" + filename) + +class BackgroundSidePage (SidePage): + def __init__(self, name, icon, content_box): + SidePage.__init__(self, name, icon, content_box) + self._gnome_background_schema = Gio.Settings("org.gnome.desktop.background") + self._cinnamon_background_schema = Gio.Settings("org.cinnamon.background") + self._add_wallpapers_dialog = AddWallpapersDialog() + + self._cinnamon_background_schema.connect("changed::mode", self._on_mode_changed) + + def _on_mode_changed(self, settings, key): + for i in self.mainbox.get_children(): + self.mainbox.remove(i) + if self._cinnamon_background_schema["mode"] == "slideshow": + self.mainbox.add(self.slideshow_pane) + self.add_wallpaper_button.hide() + self.remove_wallpaper_button.hide() + self.slideshow_pane.update_list() + else: + self.mainbox.add(self.wallpaper_pane) + self.add_wallpaper_button.show() + self.remove_wallpaper_button.show() + self.mainbox.show_all() + + def build(self): + # Clear all the widgets from the content box + widgets = self.content_box.get_children() + for widget in widgets: + self.content_box.remove(widget) + + topbox = Gtk.HBox() + self.content_box.pack_start(topbox, False, False, 0) + topbox.set_spacing(5) + + l = Gtk.Label(_("Mode")) + topbox.pack_start(l, False, False, 0) + self.background_mode = GSettingsComboBox("", "org.cinnamon.background", "mode", None, BACKGROUND_MODES).content_widget + self.background_mode.unparent() + topbox.pack_start(self.background_mode, False, False, 0) + + self.remove_wallpaper_button = Gtk.Button("-") + self.remove_wallpaper_button.set_no_show_all(True) + self.remove_wallpaper_button.set_tooltip_text(_("Remove wallpaper")) + self.remove_wallpaper_button.connect("clicked", lambda w: self._remove_selected_wallpaper()) + self.remove_wallpaper_button.set_sensitive(False) + topbox.pack_end(self.remove_wallpaper_button, False, False, 0) + self.add_wallpaper_button = Gtk.Button("+") + self.add_wallpaper_button.set_tooltip_text(_("Add wallpapers")) + self.add_wallpaper_button.connect("clicked", lambda w: self._add_wallpapers()) + self.add_wallpaper_button.set_no_show_all(True) + topbox.pack_end(self.add_wallpaper_button, False, False, 0) + + self.content_box.pack_start(Gtk.HSeparator(), False, False, 2) + + self.mainbox = Gtk.EventBox() + self.mainbox.set_visible_window(False) + self.content_box.pack_start(self.mainbox, True, True, 0) + + self.wallpaper_pane = BackgroundWallpaperPane(self, self._gnome_background_schema) + self.slideshow_pane = BackgroundSlideshowPane(self, self._gnome_background_schema, self._cinnamon_background_schema) + if self._cinnamon_background_schema["mode"] == "slideshow": + self.mainbox.add(self.slideshow_pane) + else: + self.mainbox.add(self.wallpaper_pane) + self.add_wallpaper_button.show() + self.remove_wallpaper_button.show() + + self.content_box.pack_start(Gtk.HSeparator(), False, False, 2) + + scrolled_window = Gtk.ScrolledWindow(); + scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.NEVER) + + expander = Gtk.Expander() + expander.set_label(_("Advanced options")) + scrolled_window.add_with_viewport(expander); + self.content_box.pack_start(scrolled_window, False, True, 0) + + advanced_options_box = Gtk.HBox() + expander.add(advanced_options_box) + advanced_options_box.set_spacing(10) + + l = Gtk.Label(_("Picture aspect")) + l.set_alignment(0, 0.5) + advanced_options_box.pack_start(l, False, False, 0) + self.picture_options = GSettingsComboBox("", "org.gnome.desktop.background", "picture-options", None, BACKGROUND_PICTURE_OPTIONS) + advanced_options_box.pack_start(self.picture_options, False, False, 0) + + l = Gtk.Label(_("Gradient")) + l.set_alignment(0, 0.5) + advanced_options_box.pack_start(l, False, False, 0) + self.color_shading_type = GSettingsComboBox("", "org.gnome.desktop.background", "color-shading-type", None, BACKGROUND_COLOR_SHADING_TYPES) + advanced_options_box.pack_start(self.color_shading_type, False, False, 0) + + hbox = Gtk.HBox() + l = Gtk.Label(_("Colors")) + hbox.pack_start(l, False, False, 2) + self.primary_color = GSettingsColorChooser("org.gnome.desktop.background", "primary-color", None) + hbox.pack_start(self.primary_color, False, False, 2) + self.secondary_color = GSettingsColorChooser("org.gnome.desktop.background", "secondary-color", None) + hbox.pack_start(self.secondary_color, False, False, 2) + advanced_options_box.pack_start(hbox, False, False, 0) + + def _add_wallpapers(self): + filenames = self._add_wallpapers_dialog.run() + if filenames: + dest_dir = os.path.join(os.getenv("HOME"), ".cinnamon", "backgrounds") + if not os.path.exists(dest_dir): + rec_mkdir(dest_dir) + for filename in filenames: + dest_filename = os.path.join(dest_dir, os.path.split(filename)[1]) + fs = open(filename) + fd = open(dest_filename, "w") + fd.write(fs.read()) + fs.close() + fd.close() + + self.wallpaper_pane.update_icon_view() + + def _remove_selected_wallpaper(self): + wallpaper = self.wallpaper_pane.get_selected_wallpaper() + os.unlink(wallpaper["filename"]) + self.wallpaper_pane.update_icon_view() + class ThemeViewSidePage (SidePage): def __init__(self, name, icon, content_box): SidePage.__init__(self, name, icon, content_box) @@ -162,21 +753,22 @@ def build(self): scrolledWindow.add_with_viewport(other_settings_box) other_settings_box.set_border_width(5) - windowThemeSwitcher = GSettingsComboBox(_("Window theme"), "org.gnome.desktop.wm.preferences", "theme", self._load_window_themes()) + windowThemeSwitcher = GSettingsComboBox(_("Window theme"), "org.gnome.desktop.wm.preferences", "theme", None, self._load_window_themes()) other_settings_box.pack_start(windowThemeSwitcher, False, False, 2) - menusHaveIconsCB = GSettingsCheckButton(_("Menus Have Icons"), "org.gnome.desktop.interface", "menus-have-icons") + menusHaveIconsCB = GSettingsCheckButton(_("Menus Have Icons"), "org.gnome.desktop.interface", "menus-have-icons", None) other_settings_box.pack_start(menusHaveIconsCB, False, False, 2) - buttonsHaveIconsCB = GSettingsCheckButton(_("Buttons Have Icons"), "org.gnome.desktop.interface", "buttons-have-icons") + buttonsHaveIconsCB = GSettingsCheckButton(_("Buttons Have Icons"), "org.gnome.desktop.interface", "buttons-have-icons", None) other_settings_box.pack_start(buttonsHaveIconsCB, False, False, 2) - alwaysUseLocationEntryCB = GSettingsCheckButton(_("Always Use Location Entry"), "org.gnome.nautilus.preferences", "always-use-location-entry") - other_settings_box.pack_start(alwaysUseLocationEntryCB, False, False, 2) - cursorThemeSwitcher = GSettingsComboBox(_("Cursor theme"), "org.gnome.desktop.interface", "cursor-theme", self._load_cursor_themes()) + if 'org.nemo' in Gio.Settings.list_schemas(): + alwaysUseLocationEntryCB = GSettingsCheckButton(_("Always Use Location Entry In Nemo"), "org.nemo.preferences", "show-location-entry", None) + other_settings_box.pack_start(alwaysUseLocationEntryCB, False, False, 2) + cursorThemeSwitcher = GSettingsComboBox(_("Cursor theme"), "org.gnome.desktop.interface", "cursor-theme", None, self._load_cursor_themes()) other_settings_box.pack_start(cursorThemeSwitcher, False, False, 2) - keybindingThemeSwitcher = GSettingsComboBox(_("Keybinding theme"), "org.gnome.desktop.interface", "gtk-key-theme", self._load_keybinding_themes()) + keybindingThemeSwitcher = GSettingsComboBox(_("Keybinding theme"), "org.gnome.desktop.interface", "gtk-key-theme", None, self._load_keybinding_themes()) other_settings_box.pack_start(keybindingThemeSwitcher, False, False, 2) - iconThemeSwitcher = GSettingsComboBox(_("Icon theme"), "org.gnome.desktop.interface", "icon-theme", self._load_icon_themes()) + iconThemeSwitcher = GSettingsComboBox(_("Icon theme"), "org.gnome.desktop.interface", "icon-theme", None, self._load_icon_themes()) other_settings_box.pack_start(iconThemeSwitcher, False, False, 2) - gtkThemeSwitcher = GSettingsComboBox(_("GTK+ theme"), "org.gnome.desktop.interface", "gtk-theme", self._load_gtk_themes()) + gtkThemeSwitcher = GSettingsComboBox(_("GTK+ theme"), "org.gnome.desktop.interface", "gtk-theme", None, self._load_gtk_themes()) other_settings_box.pack_start(gtkThemeSwitcher, False, False, 2) notebook.append_page(scrolledWindow, Gtk.Label(_("Other settings"))) @@ -498,20 +1090,39 @@ def on_my_value_changed(self, widget): class GSettingsCheckButton(Gtk.CheckButton): - def __init__(self, label, schema, key): + def __init__(self, label, schema, key, dep_key): self.key = key + self.dep_key = dep_key super(GSettingsCheckButton, self).__init__(label) self.settings = Gio.Settings.new(schema) self.set_active(self.settings.get_boolean(self.key)) self.settings.connect("changed::"+self.key, self.on_my_setting_changed) - self.connect('toggled', self.on_my_value_changed) - + self.connectorId = self.connect('toggled', self.on_my_value_changed) + self.dependency_invert = False + if self.dep_key is not None: + if self.dep_key[0] == '!': + self.dependency_invert = True + self.dep_key = self.dep_key[1:] + split = self.dep_key.split('/') + self.dep_settings = Gio.Settings.new(split[0]) + self.dep_key = split[1] + self.dep_settings.connect("changed::"+self.dep_key, self.on_dependency_setting_changed) + self.on_dependency_setting_changed(self, None) + def on_my_setting_changed(self, settings, key): - self.set_active(self.settings.get_boolean(self.key)) - + self.disconnect(self.connectorId) # panel-edit-mode can trigger changed:: twice in certain instances, + self.set_active(self.settings.get_boolean(self.key)) # so disconnect temporarily when we are simply updating the widget state + self.connectorId = self.connect('toggled', self.on_my_value_changed) + def on_my_value_changed(self, widget): self.settings.set_boolean(self.key, self.get_active()) - + + def on_dependency_setting_changed(self, settings, dep_key): + if not self.dependency_invert: + self.set_sensitive(self.dep_settings.get_boolean(self.dep_key)) + else: + self.set_sensitive(not self.dep_settings.get_boolean(self.dep_key)) + class DBusCheckButton(Gtk.CheckButton): def __init__(self, label, service, path, get_method, set_method): super(DBusCheckButton, self).__init__(label) @@ -524,10 +1135,21 @@ def __init__(self, label, service, path, get_method, set_method): def on_my_value_changed(self, widget): getattr(self.dbus_iface, self.dbus_set_method)(self.get_active()) - + +class NtpCheckButton(Gtk.CheckButton): + def __init__(self, label): + super(NtpCheckButton, self).__init__(label) + self.date_time_wrapper = DateTimeWrapper() + self.set_active(self.date_time_wrapper.get_using_ntp()) + self.connect('toggled', self.on_my_value_changed) + + def on_my_value_changed(self, widget): + self.date_time_wrapper.set_using_ntp(self.get_active()) + class GSettingsSpinButton(Gtk.HBox): - def __init__(self, label, schema, key, min, max, step, page, units): + def __init__(self, label, schema, key, dep_key, min, max, step, page, units): self.key = key + self.dep_key = dep_key super(GSettingsSpinButton, self).__init__() self.label = Gtk.Label(label) self.content_widget = Gtk.SpinButton() @@ -541,22 +1163,37 @@ def __init__(self, label, schema, key, min, max, step, page, units): self.content_widget.set_range(min, max) self.content_widget.set_increments(step, page) #self.content_widget.set_editable(False) - self.settings = Gio.Settings.new(schema) self.content_widget.set_value(self.settings.get_int(self.key)) self.settings.connect("changed::"+self.key, self.on_my_setting_changed) self.content_widget.connect('focus-out-event', self.on_my_value_changed) - + self.dependency_invert = False + if self.dep_key is not None: + if self.dep_key[0] == '!': + self.dependency_invert = True + self.dep_key = self.dep_key[1:] + split = self.dep_key.split('/') + self.dep_settings = Gio.Settings.new(split[0]) + self.dep_key = split[1] + self.dep_settings.connect("changed::"+self.dep_key, self.on_dependency_setting_changed) + self.on_dependency_setting_changed(self, None) + def on_my_setting_changed(self, settings, key): self.content_widget.set_value(self.settings.get_int(self.key)) - + def on_my_value_changed(self, widget, data): - print self.content_widget.get_value() self.settings.set_int(self.key, self.content_widget.get_value()) + def on_dependency_setting_changed(self, settings, dep_key): + if not self.dependency_invert: + self.set_sensitive(self.dep_settings.get_boolean(self.dep_key)) + else: + self.set_sensitive(not self.dep_settings.get_boolean(self.dep_key)) + class GSettingsEntry(Gtk.HBox): - def __init__(self, label, schema, key): + def __init__(self, label, schema, key, dep_key): self.key = key + self.dep_key = dep_key super(GSettingsEntry, self).__init__() self.label = Gtk.Label(label) self.content_widget = Gtk.Entry() @@ -566,18 +1203,34 @@ def __init__(self, label, schema, key): self.content_widget.set_text(self.settings.get_string(self.key)) self.settings.connect("changed::"+self.key, self.on_my_setting_changed) self.content_widget.connect('focus-out-event', self.on_my_value_changed) - - self.content_widget.show_all() - + self.content_widget.show_all() + self.dependency_invert = False + if self.dep_key is not None: + if self.dep_key[0] == '!': + self.dependency_invert = True + self.dep_key = self.dep_key[1:] + split = self.dep_key.split('/') + self.dep_settings = Gio.Settings.new(split[0]) + self.dep_key = split[1] + self.dep_settings.connect("changed::"+self.dep_key, self.on_dependency_setting_changed) + self.on_dependency_setting_changed(self, None) + def on_my_setting_changed(self, settings, key): self.content_widget.set_text(self.settings.get_string(self.key)) - + def on_my_value_changed(self, event, widget): self.settings.set_string(self.key, self.content_widget.get_text()) + def on_dependency_setting_changed(self, settings, dep_key): + if not self.dependency_invert: + self.set_sensitive(self.dep_settings.get_boolean(self.dep_key)) + else: + self.set_sensitive(not self.dep_settings.get_boolean(self.dep_key)) + class GSettingsFileChooser(Gtk.HBox): - def __init__(self, label, schema, key, show_none_cb = False): + def __init__(self, label, schema, key, dep_key, show_none_cb = False): self.key = key + self.dep_key = dep_key super(GSettingsFileChooser, self).__init__() self.label = Gtk.Label(label) self.content_widget = Gtk.FileChooserButton() @@ -597,8 +1250,18 @@ def __init__(self, label, schema, key, show_none_cb = False): self.content_widget.set_filename(value) self.content_widget.connect('file-set', self.on_my_value_changed) self.show_none_cb.connect('toggled', self.on_my_value_changed) - self.content_widget.show_all() + self.dependency_invert = False + if self.dep_key is not None: + if self.dep_key[0] == '!': + self.dependency_invert = True + self.dep_key = self.dep_key[1:] + split = self.dep_key.split('/') + self.dep_settings = Gio.Settings.new(split[0]) + self.dep_key = split[1] + self.dep_settings.connect("changed::"+self.dep_key, self.on_dependency_setting_changed) + self.on_dependency_setting_changed(self, None) + def on_my_value_changed(self, widget): if self.show_none_cb.get_active(): value = "" @@ -610,9 +1273,16 @@ def on_my_value_changed(self, widget): self.content_widget.set_sensitive(True) self.settings.set_string(self.key, value) + def on_dependency_setting_changed(self, settings, dep_key): + if not self.dependency_invert: + self.set_sensitive(self.dep_settings.get_boolean(self.dep_key)) + else: + self.set_sensitive(not self.dep_settings.get_boolean(self.dep_key)) + class GSettingsFontButton(Gtk.HBox): - def __init__(self, label, schema, key): + def __init__(self, label, schema, key, dep_key): self.key = key + self.dep_key = dep_key super(GSettingsFontButton, self).__init__() self.settings = Gio.Settings.new(schema) self.value = self.settings.get_string(key) @@ -627,9 +1297,26 @@ def __init__(self, label, schema, key): self.pack_start(self.content_widget, False, False, 2) self.content_widget.connect('font-set', self.on_my_value_changed) self.content_widget.show_all() + self.dependency_invert = False + if self.dep_key is not None: + if self.dep_key[0] == '!': + self.dependency_invert = True + self.dep_key = self.dep_key[1:] + split = self.dep_key.split('/') + self.dep_settings = Gio.Settings.new(split[0]) + self.dep_key = split[1] + self.dep_settings.connect("changed::"+self.dep_key, self.on_dependency_setting_changed) + self.on_dependency_setting_changed(self, None) + def on_my_value_changed(self, widget): self.settings.set_string(self.key, widget.get_font_name()) + def on_dependency_setting_changed(self, settings, dep_key): + if not self.dependency_invert: + self.set_sensitive(self.dep_settings.get_boolean(self.dep_key)) + else: + self.set_sensitive(not self.dep_settings.get_boolean(self.dep_key)) + class GConfFontButton(Gtk.HBox): def __init__(self, label, key): self.key = key @@ -651,8 +1338,9 @@ def on_my_value_changed(self, widget): self.settings.set_string(self.key, widget.get_font_name()) class GSettingsRange(Gtk.HBox): - def __init__(self, label, schema, key, **options): + def __init__(self, label, schema, key, dep_key, **options): self.key = key + self.dep_key = dep_key super(GSettingsRange, self).__init__() self.settings = Gio.Settings.new(schema) self.value = self.settings.get_double(self.key) @@ -669,12 +1357,30 @@ def __init__(self, label, schema, key, **options): self.pack_start(self.content_widget, True, True, 2) self.content_widget.connect('value-changed', self.on_my_value_changed) self.content_widget.show_all() + self.dependency_invert = False + if self.dep_key is not None: + if self.dep_key[0] == '!': + self.dependency_invert = True + self.dep_key = self.dep_key[1:] + split = self.dep_key.split('/') + self.dep_settings = Gio.Settings.new(split[0]) + self.dep_key = split[1] + self.dep_settings.connect("changed::"+self.dep_key, self.on_dependency_setting_changed) + self.on_dependency_setting_changed(self, None) + def on_my_value_changed(self, widget): self.settings.set_double(self.key, widget.get_value()) + def on_dependency_setting_changed(self, settings, dep_key): + if not self.dependency_invert: + self.set_sensitive(self.dep_settings.get_boolean(self.dep_key)) + else: + self.set_sensitive(not self.dep_settings.get_boolean(self.dep_key)) + class GSettingsRangeSpin(Gtk.HBox): - def __init__(self, label, schema, key, **options): + def __init__(self, label, schema, key, dep_key, **options): self.key = key + self.dep_key = dep_key super(GSettingsRangeSpin, self).__init__() self.label = Gtk.Label(label) self.content_widget = Gtk.SpinButton() @@ -696,6 +1402,16 @@ def __init__(self, label, schema, key, **options): self.settings.connect("changed::"+self.key, self.on_my_setting_changed) self.content_widget.connect('value-changed', self.on_my_value_changed) + self.dependency_invert = False + if self.dep_key is not None: + if self.dep_key[0] == '!': + self.dependency_invert = True + self.dep_key = self.dep_key[1:] + split = self.dep_key.split('/') + self.dep_settings = Gio.Settings.new(split[0]) + self.dep_key = split[1] + self.dep_settings.connect("changed::"+self.dep_key, self.on_dependency_setting_changed) + self.on_dependency_setting_changed(self, None) def on_my_setting_changed(self, settings, key): self.content_widget.set_value(self.settings.get_double(self.key)) @@ -703,9 +1419,16 @@ def on_my_setting_changed(self, settings, key): def on_my_value_changed(self, widget): self.settings.set_double(self.key, self.content_widget.get_value()) + def on_dependency_setting_changed(self, settings, dep_key): + if not self.dependency_invert: + self.set_sensitive(self.dep_settings.get_boolean(self.dep_key)) + else: + self.set_sensitive(not self.dep_settings.get_boolean(self.dep_key)) + class GSettingsComboBox(Gtk.HBox): - def __init__(self, label, schema, key, options): + def __init__(self, label, schema, key, dep_key, options): self.key = key + self.dep_key = dep_key super(GSettingsComboBox, self).__init__() self.settings = Gio.Settings.new(schema) self.value = self.settings.get_string(self.key) @@ -733,20 +1456,35 @@ def __init__(self, label, schema, key, options): self.pack_start(self.content_widget, False, False, 2) self.content_widget.connect('changed', self.on_my_value_changed) self.content_widget.show_all() - - + self.dependency_invert = False + self.dependency_invert = False + if self.dep_key is not None: + if self.dep_key[0] == '!': + self.dependency_invert = True + self.dep_key = self.dep_key[1:] + split = self.dep_key.split('/') + self.dep_settings = Gio.Settings.new(split[0]) + self.dep_key = split[1] + self.dep_settings.connect("changed::"+self.dep_key, self.on_dependency_setting_changed) + self.on_dependency_setting_changed(self, None) + def on_my_value_changed(self, widget): tree_iter = widget.get_active_iter() if tree_iter != None: value = self.model[tree_iter][0] - self.settings.set_string(self.key, value) + self.settings.set_string(self.key, value) + + def on_dependency_setting_changed(self, settings, dep_key): + if not self.dependency_invert: + self.set_sensitive(self.dep_settings.get_boolean(self.dep_key)) + else: + self.set_sensitive(not self.dep_settings.get_boolean(self.dep_key)) class TimeZoneSelectorWidget(Gtk.HBox): def __init__(self): super(TimeZoneSelectorWidget, self).__init__() - proxy = dbus.SystemBus().get_object("org.gnome.SettingsDaemon.DateTimeMechanism", "/") - self.dbus_iface = dbus.Interface(proxy, dbus_interface="org.gnome.SettingsDaemon.DateTimeMechanism") + self.date_time_wrapper = DateTimeWrapper() self.timezones = tz.load_db() @@ -792,7 +1530,7 @@ def on_city_changed(self, widget): tree_iter = widget.get_active_iter() if tree_iter != None: self.selected_city = self.city_model[tree_iter][0] - self.dbus_iface.SetTimezone(self.selected_region+"/"+self.selected_city) + self.date_time_wrapper.set_timezone(self.selected_region+"/"+self.selected_city) def on_region_changed(self, widget): tree_iter = widget.get_active_iter() if tree_iter != None: @@ -813,7 +1551,7 @@ def update_cities_list(self): if selected_city_iter is not None: self.city_widget.set_active_iter(selected_city_iter) def get_selected_zone(self): - tz = self.dbus_iface.GetTimezone() + tz = self.date_time_wrapper.get_timezone() if "/" in tz: i = tz.index("/") region = tz[:i] @@ -825,8 +1563,7 @@ def get_selected_zone(self): class ChangeTimeWidget(Gtk.HBox): def __init__(self): super(ChangeTimeWidget, self).__init__() - proxy = dbus.SystemBus().get_object("org.gnome.SettingsDaemon.DateTimeMechanism", "/") - self.dbus_iface = dbus.Interface(proxy, dbus_interface="org.gnome.SettingsDaemon.DateTimeMechanism") + self.date_time_wrapper = DateTimeWrapper() # Ensures we are setting the system time only when the user changes it self.changedOnTimeout = False @@ -952,7 +1689,7 @@ def _do_change_system_time(self): self._time_to_set = None self._setting_time_lock.release() - self.dbus_iface.SetTime(time_to_set) + self.date_time_wrapper.set_time(time_to_set) # Check whether another request to set the time was done since this thread started self._setting_time_lock.acquire() @@ -1076,7 +1813,17 @@ def on_my_value_changed(self, widget): else: right_items.append(value) self.settings.set_string(self.key, ','.join(str(item) for item in left_items) + ':' + ','.join(str(item) for item in right_items)) - + + +class IndentedHBox(Gtk.HBox): + def __init__(self): + super(IndentedHBox, self).__init__() + indent = Gtk.Label('\t') + self.pack_start(indent, False, False, 0) + + def add(self, item): + self.pack_start(item, False, False, 0) + class MainWindow: # Change pages @@ -1113,42 +1860,59 @@ def __init__(self): sidePage = SidePage(_("Menu"), "menu.svg", self.content_box) self.sidePages.append((sidePage, "menu")) - sidePage.add_widget(GSettingsEntry(_("Menu text"), "org.cinnamon", "menu-text")) - sidePage.add_widget(GSettingsFileChooser(_("Menu icon"), "org.cinnamon", "menu-icon", True)) - sidePage.add_widget(GSettingsSpinButton(_("Menu hover delay"), "org.cinnamon", "menu-hover-delay", 0, 2000, 50, 200, _("milliseconds"))) - sidePage.add_widget(GSettingsCheckButton(_("Activate menu on hover"), "org.cinnamon", "activate-menu-applet-on-hover")) - sidePage.add_widget(GSettingsCheckButton(_("Show bookmarks and places"), "org.cinnamon", "menu-show-places")) - sidePage.add_widget(GSettingsCheckButton(_("Show recent files"), "org.cinnamon", "menu-show-recent")) + sidePage.add_widget(GSettingsEntry(_("Menu text"), "org.cinnamon", "menu-text", None)) + sidePage.add_widget(GSettingsFileChooser(_("Menu icon"), "org.cinnamon", "menu-icon", None, True)) + sidePage.add_widget(GSettingsSpinButton(_("Menu hover delay"), "org.cinnamon", "menu-hover-delay", None, 0, 2000, 50, 200, _("milliseconds"))) + sidePage.add_widget(GSettingsCheckButton(_("Activate menu on hover"), "org.cinnamon", "activate-menu-applet-on-hover", None)) + sidePage.add_widget(GSettingsCheckButton(_("Show bookmarks and places"), "org.cinnamon", "menu-show-places", None)) + sidePage.add_widget(GSettingsCheckButton(_("Show recent files"), "org.cinnamon", "menu-show-recent", None)) sidePage = SidePage(_("Panel"), "panel.svg", self.content_box) self.sidePages.append((sidePage, "panel")) - sidePage.add_widget(GSettingsCheckButton(_("Auto-hide panel"), "org.cinnamon", "panel-autohide")) - sidePage.add_widget(GSettingsSpinButton(_("Show delay"), "org.cinnamon", "panel-show-delay", 0, 2000, 50, 200, _("milliseconds"))) - sidePage.add_widget(GSettingsSpinButton(_("Hide delay"), "org.cinnamon", "panel-hide-delay", 0, 2000, 50, 200, _("milliseconds"))) - + sidePage.add_widget(GSettingsCheckButton(_("Auto-hide panel"), "org.cinnamon", "panel-autohide", None)) + + box = IndentedHBox() + box.add(GSettingsSpinButton(_("Show delay"), "org.cinnamon", "panel-show-delay", "org.cinnamon/panel-autohide", 0, 2000, 50, 200, _("milliseconds"))) + sidePage.add_widget(box) + + box = IndentedHBox() + box.add(GSettingsSpinButton(_("Hide delay"), "org.cinnamon", "panel-hide-delay", "org.cinnamon/panel-autohide", 0, 2000, 50, 200, _("milliseconds"))) + sidePage.add_widget(box) + desktop_layouts = [["traditional", _("Traditional (panel at the bottom)")], ["flipped", _("Flipped (panel at the top)")], ["classic", _("Classic (panels at the top and at the bottom)")]] - desktop_layouts_combo = GSettingsComboBox(_("Panel layout"), "org.cinnamon", "desktop-layout", desktop_layouts) + desktop_layouts_combo = GSettingsComboBox(_("Panel layout"), "org.cinnamon", "desktop-layout", None, desktop_layouts) sidePage.add_widget(desktop_layouts_combo) label = Gtk.Label() label.set_markup("%s" % _("Note: If you change the layout you will need to restart Cinnamon.")) sidePage.add_widget(label) - sidePage.add_widget(GSettingsCheckButton(_("Use customized panel size (otherwise it's defined by the theme)"), "org.cinnamon", "panel-resizable")) - sidePage.add_widget(GSettingsSpinButton(_("Top panel height"), "org.cinnamon", "panel-top-height", 0, 2000, 1, 5, _("Pixels"))) - sidePage.add_widget(GSettingsSpinButton(_("Bottom panel height"), "org.cinnamon", "panel-bottom-height", 0, 2000, 1, 5, _("Pixels"))) - sidePage.add_widget(GSettingsCheckButton(_("Panel edit mode"), "org.cinnamon", "panel-edit-mode")) - + + sidePage.add_widget(GSettingsCheckButton(_("Use customized panel size (otherwise it's defined by the theme)"), "org.cinnamon", "panel-resizable", None)) + + box = IndentedHBox() + box.add(GSettingsCheckButton(_("Allow Cinnamon to scale panel text and icons according to the panel heights"), "org.cinnamon", "panel-scale-text-icons", "org.cinnamon/panel-resizable")) + sidePage.add_widget(box) + + box = IndentedHBox() + box.add(GSettingsSpinButton(_("Top panel height"), "org.cinnamon", "panel-top-height", "org.cinnamon/panel-resizable", 0, 2000, 1, 5, _("Pixels"))) + sidePage.add_widget(box) + + box = IndentedHBox() + box.add(GSettingsSpinButton(_("Bottom panel height"), "org.cinnamon", "panel-bottom-height", "org.cinnamon/panel-resizable", 0, 2000, 1, 5, _("Pixels"))) + sidePage.add_widget(box) + + sidePage.add_widget(GSettingsCheckButton(_("Panel edit mode"), "org.cinnamon", "panel-edit-mode", None)) sidePage = SidePage(_("Calendar"), "clock.svg", self.content_box) self.sidePages.append((sidePage, "calendar")) - sidePage.add_widget(GSettingsCheckButton(_("Show week dates in calendar"), "org.cinnamon.calendar", "show-weekdate")) - sidePage.add_widget(GSettingsEntry(_("Date format for the panel"), "org.cinnamon.calendar", "date-format")) - sidePage.add_widget(GSettingsEntry(_("Date format inside the date applet"), "org.cinnamon.calendar", "date-format-full")) + sidePage.add_widget(GSettingsCheckButton(_("Show week dates in calendar"), "org.cinnamon.calendar", "show-weekdate", None)) + sidePage.add_widget(GSettingsEntry(_("Date format for the panel"), "org.cinnamon.calendar", "date-format", None)) + sidePage.add_widget(GSettingsEntry(_("Date format inside the date applet"), "org.cinnamon.calendar", "date-format-full", None)) sidePage.add_widget(Gtk.LinkButton.new_with_label("http://www.foragoodstrftime.com/", _("Generate your own date formats"))) try: self.changeTimeWidget = ChangeTimeWidget() self.ntpCheckButton = None try: - self.ntpCheckButton = DBusCheckButton(_("Use network time"), "org.gnome.SettingsDaemon.DateTimeMechanism", "/", "GetUsingNtp", "SetUsingNtp") + self.ntpCheckButton = NtpCheckButton(_("Use network time")) sidePage.add_widget(self.ntpCheckButton) except: pass @@ -1166,37 +1930,38 @@ def __init__(self): sidePage = SidePage(_("Hot corner"), "overview.svg", self.content_box) self.sidePages.append((sidePage, "hotcorner")) - sidePage.add_widget(GSettingsCheckButton(_("Hot corner icon visible"), "org.cinnamon", "overview-corner-visible")) - sidePage.add_widget(GSettingsCheckButton(_("Hot corner enabled"), "org.cinnamon", "overview-corner-hover")) - box = Gtk.HBox() + sidePage.add_widget(GSettingsCheckButton(_("Hot corner icon visible"), "org.cinnamon", "overview-corner-visible", None)) + sidePage.add_widget(GSettingsCheckButton(_("Hot corner enabled"), "org.cinnamon", "overview-corner-hover", None)) + box = IndentedHBox() label = Gtk.Label() label.set_markup("%s" % _("Hot corner position:")) - box.pack_start(label, False, False, 0) + box.add(label) positions = [["topLeft", _("Top left")], ["topRight", _("Top right")], ["bottomLeft", _("Bottom left")], ["bottomRight", _("Bottom right")]] - combo = GSettingsComboBox("", "org.cinnamon", "overview-corner-position", positions) - box.pack_start(combo, False, False, 0) + box.add(GSettingsComboBox("", "org.cinnamon", "overview-corner-position", "org.cinnamon/overview-corner-hover", positions)) sidePage.add_widget(box) - box = Gtk.HBox() + box = IndentedHBox() label = Gtk.Label() label.set_markup("%s" % _("Hot corner function:")) - box.pack_start(label, False, False, 0) + box.add(label) cornerfunctions = [["expo", _("Workspace selection (ala Compiz Expo)")], ["scale", _("Window selection (ala Compiz Scale)")]] - combo = GSettingsComboBox("", "org.cinnamon", "overview-corner-functionality", cornerfunctions) - box.pack_start(combo, False, False, 0) + box.add(GSettingsComboBox("", "org.cinnamon", "overview-corner-functionality", "org.cinnamon/overview-corner-hover", cornerfunctions)) sidePage.add_widget(box) - sidePage.add_widget(GSettingsCheckButton(_("Expo applet: activate on hover"), "org.cinnamon", "expo-applet-hover")) - sidePage.add_widget(GSettingsCheckButton(_("Scale applet: activate on hover"), "org.cinnamon", "scale-applet-hover")) + sidePage.add_widget(GSettingsCheckButton(_("Expo applet: activate on hover"), "org.cinnamon", "expo-applet-hover", None)) + sidePage.add_widget(GSettingsCheckButton(_("Scale applet: activate on hover"), "org.cinnamon", "scale-applet-hover", None)) sidePage = ThemeViewSidePage(_("Themes"), "themes.svg", self.content_box) self.sidePages.append((sidePage, "themes")) - + sidePage = SidePage(_("Effects"), "desktop-effects.svg", self.content_box) self.sidePages.append((sidePage, "effects")) - sidePage.add_widget(GSettingsCheckButton(_("Enable desktop effects"), "org.cinnamon", "desktop-effects")) - sidePage.add_widget(GSettingsCheckButton(_("Enable desktop effects on dialog boxes"), "org.cinnamon", "desktop-effects-on-dialogs")) - + sidePage.add_widget(GSettingsCheckButton(_("Enable desktop effects"), "org.cinnamon", "desktop-effects", None)) + + box = IndentedHBox() + box.add(GSettingsCheckButton(_("Enable desktop effects on dialog boxes"), "org.cinnamon", "desktop-effects-on-dialogs", "org.cinnamon/desktop-effects")) + sidePage.add_widget(box) + # Destroy window effects transition_effects = [] transition_effects.append(["easeInQuad", "easeInQuad"]) @@ -1231,73 +1996,58 @@ def __init__(self): transition_effects.append(["easeInOutBounce", "easeInOutBounce"]) #CLOSING WINDOWS - box = Gtk.HBox() + box = IndentedHBox() label = Gtk.Label() label.set_markup("%s" % _("Closing windows:")) - box.pack_start(label, False, False, 0) + box.add(label) effects = [["none", _("None")], ["scale", _("Scale")], ["fade", _("Fade")]] - combo = GSettingsComboBox("", "org.cinnamon", "desktop-effects-close-effect", effects) - box.pack_start(combo, False, False, 0) - combo = GSettingsComboBox("", "org.cinnamon", "desktop-effects-close-transition", transition_effects) - box.pack_start(combo, False, False, 0) - spin = GSettingsSpinButton("", "org.cinnamon", "desktop-effects-close-time", 0, 2000, 50, 200, _("milliseconds")) - box.pack_start(spin, False, False, 0) + box.add(GSettingsComboBox("", "org.cinnamon", "desktop-effects-close-effect", "org.cinnamon/desktop-effects", effects)) + box.add(GSettingsComboBox("", "org.cinnamon", "desktop-effects-close-transition", "org.cinnamon/desktop-effects", transition_effects)) + box.add(GSettingsSpinButton("", "org.cinnamon", "desktop-effects-close-time", "org.cinnamon/desktop-effects", 0, 2000, 50, 200, _("milliseconds"))) sidePage.add_widget(box) #MAPPING WINDOWS - box = Gtk.HBox() + box = IndentedHBox() label = Gtk.Label() label.set_markup("%s" % _("Mapping windows:")) - box.pack_start(label, False, False, 0) + box.add(label) effects = [["none", _("None")], ["scale", _("Scale")], ["fade", _("Fade")]] - combo = GSettingsComboBox("", "org.cinnamon", "desktop-effects-map-effect", effects) - box.pack_start(combo, False, False, 0) - combo = GSettingsComboBox("", "org.cinnamon", "desktop-effects-map-transition", transition_effects) - box.pack_start(combo, False, False, 0) - spin = GSettingsSpinButton("", "org.cinnamon", "desktop-effects-map-time", 0, 2000, 50, 200, _("milliseconds")) - box.pack_start(spin, False, False, 0) + box.add(GSettingsComboBox("", "org.cinnamon", "desktop-effects-map-effect", "org.cinnamon/desktop-effects", effects)) + box.add(GSettingsComboBox("", "org.cinnamon", "desktop-effects-map-transition", "org.cinnamon/desktop-effects", transition_effects)) + box.add(GSettingsSpinButton("", "org.cinnamon", "desktop-effects-map-time", "org.cinnamon/desktop-effects", 0, 2000, 50, 200, _("milliseconds"))) sidePage.add_widget(box) #MINIMIZING WINDOWS - box = Gtk.HBox() + box = IndentedHBox() label = Gtk.Label() label.set_markup("%s" % _("Minimizing windows:")) - box.pack_start(label, False, False, 0) - effects = [["none", _("None")], ["traditional", _("Traditional")], ["scale", _("Scale")], ["fade", _("Fade")]] - combo = GSettingsComboBox("", "org.cinnamon", "desktop-effects-minimize-effect", effects) - box.pack_start(combo, False, False, 0) - combo = GSettingsComboBox("", "org.cinnamon", "desktop-effects-minimize-transition", transition_effects) - box.pack_start(combo, False, False, 0) - spin = GSettingsSpinButton("", "org.cinnamon", "desktop-effects-minimize-time", 0, 2000, 50, 200, _("milliseconds")) - box.pack_start(spin, False, False, 0) + box.add(label) + effects = [["none", _("None")], ["traditional", _("Traditional")], ["scale", _("Scale")], ["fade", _("Fade")]] + box.add(GSettingsComboBox("", "org.cinnamon", "desktop-effects-minimize-effect", "org.cinnamon/desktop-effects", effects)) + box.add(GSettingsComboBox("", "org.cinnamon", "desktop-effects-minimize-transition", "org.cinnamon/desktop-effects", transition_effects)) + box.add(GSettingsSpinButton("", "org.cinnamon", "desktop-effects-minimize-time", "org.cinnamon/desktop-effects", 0, 2000, 50, 200, _("milliseconds"))) sidePage.add_widget(box) #MAXIMIZING WINDOWS - box = Gtk.HBox() + box = IndentedHBox() label = Gtk.Label() label.set_markup("%s" % _("Maximizing windows:")) - box.pack_start(label, False, False, 0) + box.add(label) effects = [["none", _("None")], ["scale", _("Scale")]] - combo = GSettingsComboBox("", "org.cinnamon", "desktop-effects-maximize-effect", effects) - box.pack_start(combo, False, False, 0) - combo = GSettingsComboBox("", "org.cinnamon", "desktop-effects-maximize-transition", transition_effects) - box.pack_start(combo, False, False, 0) - spin = GSettingsSpinButton("", "org.cinnamon", "desktop-effects-maximize-time", 0, 2000, 50, 200, _("milliseconds")) - box.pack_start(spin, False, False, 0) + box.add(GSettingsComboBox("", "org.cinnamon", "desktop-effects-maximize-effect", "org.cinnamon/desktop-effects", effects)) + box.add(GSettingsComboBox("", "org.cinnamon", "desktop-effects-maximize-transition", "org.cinnamon/desktop-effects", transition_effects)) + box.add(GSettingsSpinButton("", "org.cinnamon", "desktop-effects-maximize-time", "org.cinnamon/desktop-effects", 0, 2000, 50, 200, _("milliseconds"))) sidePage.add_widget(box) #UNMAXIMIZING WINDOWS - box = Gtk.HBox() + box = IndentedHBox() label = Gtk.Label() label.set_markup("%s" % _("Unmaximizing windows:")) - box.pack_start(label, False, False, 0) - effects = [["none", _("None")]] - combo = GSettingsComboBox("", "org.cinnamon", "desktop-effects-unmaximize-effect", effects) - box.pack_start(combo, False, False, 0) - combo = GSettingsComboBox("", "org.cinnamon", "desktop-effects-unmaximize-transition", transition_effects) - box.pack_start(combo, False, False, 0) - spin = GSettingsSpinButton("", "org.cinnamon", "desktop-effects-unmaximize-time", 0, 2000, 50, 200, _("milliseconds")) - box.pack_start(spin, False, False, 0) + box.add(label) + effects = [["none", _("None")], ["scale", _("Scale")]] + box.add(GSettingsComboBox("", "org.cinnamon", "desktop-effects-unmaximize-effect", "org.cinnamon/desktop-effects", effects)) + box.add(GSettingsComboBox("", "org.cinnamon", "desktop-effects-unmaximize-transition", "org.cinnamon/desktop-effects", transition_effects)) + box.add(GSettingsSpinButton("", "org.cinnamon", "desktop-effects-unmaximize-time", "org.cinnamon/desktop-effects", 0, 2000, 50, 200, _("milliseconds"))) sidePage.add_widget(box) sidePage = AppletViewSidePage(_("Applets"), "applets.svg", self.content_box) @@ -1306,79 +2056,110 @@ def __init__(self): sidePage = ExtensionViewSidePage(_("Extensions"), "extensions.svg", self.content_box) self.sidePages.append((sidePage, "extensions")) - nautilus_desktop_schema = Gio.Settings.new("org.gnome.nautilus.desktop") - nautilus_desktop_keys = nautilus_desktop_schema.list_keys() - - sidePage = SidePage(_("Desktop"), "desktop.svg", self.content_box) - self.sidePages.append((sidePage, "desktop")) - sidePage.add_widget(GSettingsCheckButton(_("Have file manager handle the desktop"), "org.gnome.desktop.background", "show-desktop-icons")) - if "computer-icon-visible" in nautilus_desktop_keys: - sidePage.add_widget(GSettingsCheckButton(_("Computer icon visible on desktop"), "org.gnome.nautilus.desktop", "computer-icon-visible")) - if "home-icon-visible" in nautilus_desktop_keys: - sidePage.add_widget(GSettingsCheckButton(_("Home icon visible on desktop"), "org.gnome.nautilus.desktop", "home-icon-visible")) - if "network-icon-visible" in nautilus_desktop_keys: - sidePage.add_widget(GSettingsCheckButton(_("Network Servers icon visible on desktop"), "org.gnome.nautilus.desktop", "network-icon-visible")) - if "trash-icon-visible" in nautilus_desktop_keys: - sidePage.add_widget(GSettingsCheckButton(_("Trash icon visible on desktop"), "org.gnome.nautilus.desktop", "trash-icon-visible")) - if "volumes-visible" in nautilus_desktop_keys: - sidePage.add_widget(GSettingsCheckButton(_("Show mounted volumes on the desktop"), "org.gnome.nautilus.desktop", "volumes-visible")) - + if 'org.nemo' in Gio.Settings.list_schemas(): + nemo_desktop_schema = Gio.Settings.new("org.nemo.desktop") + nemo_desktop_keys = nemo_desktop_schema.list_keys() + + sidePage = SidePage(_("Desktop"), "desktop.svg", self.content_box) + self.sidePages.append((sidePage, "desktop")) + sidePage.add_widget(GSettingsCheckButton(_("Have file manager (Nemo) handle the desktop"), "org.gnome.desktop.background", "show-desktop-icons", None)) + if "computer-icon-visible" in nemo_desktop_keys: + box = IndentedHBox() + box.add(GSettingsCheckButton(_("Computer icon visible on desktop"), "org.nemo.desktop", "computer-icon-visible", "org.gnome.desktop.background/show-desktop-icons")) + sidePage.add_widget(box) + if "home-icon-visible" in nemo_desktop_keys: + box = IndentedHBox() + box.add(GSettingsCheckButton(_("Home icon visible on desktop"), "org.nemo.desktop", "home-icon-visible", "org.gnome.desktop.background/show-desktop-icons")) + sidePage.add_widget(box) + if "network-icon-visible" in nemo_desktop_keys: + box = IndentedHBox() + box.add(GSettingsCheckButton(_("Network Servers icon visible on desktop"), "org.nemo.desktop", "network-icon-visible", "org.gnome.desktop.background/show-desktop-icons")) + sidePage.add_widget(box) + if "trash-icon-visible" in nemo_desktop_keys: + box = IndentedHBox() + box.add(GSettingsCheckButton(_("Trash icon visible on desktop"), "org.nemo.desktop", "trash-icon-visible", "org.gnome.desktop.background/show-desktop-icons")) + sidePage.add_widget(box) + if "volumes-visible" in nemo_desktop_keys: + box = IndentedHBox() + box.add(GSettingsCheckButton(_("Show mounted volumes on the desktop"), "org.nemo.desktop", "volumes-visible", "org.gnome.desktop.background/show-desktop-icons")) + sidePage.add_widget(box) + sidePage = SidePage(_("Windows"), "windows.svg", self.content_box) self.sidePages.append((sidePage, "windows")) sidePage.add_widget(GSettingsComboBox(_("Action on title bar double-click"), - "org.gnome.desktop.wm.preferences", "action-double-click-titlebar", + "org.gnome.desktop.wm.preferences", "action-double-click-titlebar", None, [(i, i.replace("-", " ").title()) for i in ('toggle-shade', 'toggle-maximize', 'toggle-maximize-horizontally', 'toggle-maximize-vertically', 'minimize', 'shade', 'menu', 'lower', 'none')])) sidePage.add_widget(GSettingsComboBox(_("Action on title bar middle-click"), - "org.gnome.desktop.wm.preferences", "action-middle-click-titlebar", + "org.gnome.desktop.wm.preferences", "action-middle-click-titlebar", None, [(i, i.replace("-", " ").title()) for i in ('toggle-shade', 'toggle-maximize', 'toggle-maximize-horizontally', 'toggle-maximize-vertically', 'minimize', 'shade', 'menu', 'lower', 'none')])) sidePage.add_widget(GSettingsComboBox(_("Action on title bar right-click"), - "org.gnome.desktop.wm.preferences", "action-right-click-titlebar", + "org.gnome.desktop.wm.preferences", "action-right-click-titlebar", None, [(i, i.replace("-", " ").title()) for i in ('toggle-shade', 'toggle-maximize', 'toggle-maximize-horizontally', 'toggle-maximize-vertically', 'minimize', 'shade', 'menu', 'lower', 'none')])) sidePage.add_widget(GSettingsComboBox(_("Window focus mode"), - "org.gnome.desktop.wm.preferences", "focus-mode", + "org.gnome.desktop.wm.preferences", "focus-mode", None, [(i, i.title()) for i in ("click","sloppy","mouse")])) sidePage.add_widget(TitleBarButtonsOrderSelector()) - sidePage.add_widget(GSettingsCheckButton(_("Enable ALT+Tab outline and window preview"), "org.cinnamon", "enable-alttab-outline")) - sidePage.add_widget(GSettingsCheckButton(_("Enable Edge Tiling (\"Aero Snap\")"), "org.cinnamon.overrides", "edge-tiling")) - sidePage.add_widget(GSettingsCheckButton(_("Enable Edge Flip"), "org.cinnamon", "enable-edge-flip")) - sidePage.add_widget(GSettingsCheckButton(_("Attach dialog windows to their parent window's titlebar"), "org.cinnamon.overrides", "attach-modal-dialogs")) + sidePage.add_widget(GSettingsCheckButton(_("Enable Edge Tiling (\"Aero Snap\")"), "org.cinnamon.overrides", "edge-tiling", None)) + sidePage.add_widget(GSettingsCheckButton(_("Enable Edge Flip"), "org.cinnamon", "enable-edge-flip", None)) + sidePage.add_widget(GSettingsCheckButton(_("Attach dialog windows to their parent window's titlebar"), "org.cinnamon.overrides", "attach-modal-dialogs", None)) + alttab_styles = [["icons", _("Icons only")],["icons+thumbnails", _("Icons and thumbnails")],["icons+preview", _("Icons and window preview")],["preview", _("Window preview (no icons)")]] + alttab_styles_combo = GSettingsComboBox(_("ALT-tab switcher style"), "org.cinnamon", "alttab-switcher-style", None, alttab_styles) + sidePage.add_widget(alttab_styles_combo) + sidePage.add_widget(GSettingsCheckButton(_("Enable mouse-wheel scrolling in Window List applet"), "org.cinnamon", "window-list-applet-scroll", None)) sidePage = SidePage(_("Workspaces"), "workspaces.svg", self.content_box) self.sidePages.append((sidePage, "workspaces")) - sidePage.add_widget(GSettingsCheckButton(_("Enable workspace OSD"), "org.cinnamon", "workspace-osd-visible")) - sidePage.add_widget(GSettingsSpinButton(_("Workspace OSD duration"), "org.cinnamon", "workspace-osd-duration", 0, 2000, 50, 400, _("milliseconds"))) - sidePage.add_widget(GSettingsSpinButton(_("Workspace OSD horizontal position"), "org.cinnamon", "workspace-osd-x", 0, 100, 5, 50, _("percent of the monitor's width"))) - sidePage.add_widget(GSettingsSpinButton(_("Workspace OSD vertical position"), "org.cinnamon", "workspace-osd-y", 0, 100, 5, 50, _("percent of the monitor's height"))) - sidePage.add_widget(GSettingsCheckButton(_("Only use workspaces on primary monitor (requires Cinnamon restart)"), "org.cinnamon.overrides", "workspaces-only-on-primary")) - sidePage.add_widget(GSettingsCheckButton(_("Display Expo view as a grid"), "org.cinnamon", "workspace-expo-view-as-grid")) + sidePage.add_widget(GSettingsCheckButton(_("Enable workspace OSD"), "org.cinnamon", "workspace-osd-visible", None)) + + box = IndentedHBox() + box.add(GSettingsSpinButton(_("Workspace OSD duration"), "org.cinnamon", "workspace-osd-duration", "org.cinnamon/workspace-osd-visible", 0, 2000, 50, 400, _("milliseconds"))) + sidePage.add_widget(box) + + box = IndentedHBox() + box.add(GSettingsSpinButton(_("Workspace OSD horizontal position"), "org.cinnamon", "workspace-osd-x", "org.cinnamon/workspace-osd-visible", 0, 100, 5, 50, _("percent of the monitor's width"))) + sidePage.add_widget(box) + + box = IndentedHBox() + box.add(GSettingsSpinButton(_("Workspace OSD vertical position"), "org.cinnamon", "workspace-osd-y", "org.cinnamon/workspace-osd-visible", 0, 100, 5, 50, _("percent of the monitor's height"))) + sidePage.add_widget(box) + + sidePage.add_widget(GSettingsCheckButton(_("Only use workspaces on primary monitor (requires Cinnamon restart)"), "org.cinnamon.overrides", "workspaces-only-on-primary", None)) + sidePage.add_widget(GSettingsCheckButton(_("Display Expo view as a grid"), "org.cinnamon", "workspace-expo-view-as-grid", None)) sidePage = SidePage(_("Fonts"), "fonts.svg", self.content_box) self.sidePages.append((sidePage, "fonts")) - sidePage.add_widget(GSettingsRangeSpin(_("Text scaling factor"), "org.gnome.desktop.interface", "text-scaling-factor", adjustment_step = 0.1)) - sidePage.add_widget(GSettingsFontButton(_("Default font"), "org.gnome.desktop.interface", "font-name")) - sidePage.add_widget(GSettingsFontButton(_("Document font"), "org.gnome.desktop.interface", "document-font-name")) - sidePage.add_widget(GSettingsFontButton(_("Monospace font"), "org.gnome.desktop.interface", "monospace-font-name")) - sidePage.add_widget(GSettingsFontButton(_("Window title font"), "org.gnome.desktop.wm.preferences", "titlebar-font")) - sidePage.add_widget(GSettingsComboBox(_("Hinting"), "org.gnome.settings-daemon.plugins.xsettings", "hinting", [(i, i.title()) for i in ("none", "slight", "medium", "full")])) - sidePage.add_widget(GSettingsComboBox(_("Antialiasing"), "org.gnome.settings-daemon.plugins.xsettings", "antialiasing", [(i, i.title()) for i in ("none", "grayscale", "rgba")])) + sidePage.add_widget(GSettingsRangeSpin(_("Text scaling factor"), "org.gnome.desktop.interface", "text-scaling-factor", None, adjustment_step = 0.1)) + sidePage.add_widget(GSettingsFontButton(_("Default font"), "org.gnome.desktop.interface", "font-name", None)) + sidePage.add_widget(GSettingsFontButton(_("Document font"), "org.gnome.desktop.interface", "document-font-name", None)) + sidePage.add_widget(GSettingsFontButton(_("Monospace font"), "org.gnome.desktop.interface", "monospace-font-name", None)) + sidePage.add_widget(GSettingsFontButton(_("Window title font"), "org.gnome.desktop.wm.preferences", "titlebar-font", None)) + sidePage.add_widget(GSettingsComboBox(_("Hinting"), "org.gnome.settings-daemon.plugins.xsettings", "hinting", None, [(i, i.title()) for i in ("none", "slight", "medium", "full")])) + sidePage.add_widget(GSettingsComboBox(_("Antialiasing"), "org.gnome.settings-daemon.plugins.xsettings", "antialiasing", None, [(i, i.title()) for i in ("none", "grayscale", "rgba")])) sidePage = SidePage(_("General"), "general.svg", self.content_box) self.sidePages.append((sidePage, "general")) - sidePage.add_widget(GSettingsCheckButton(_("Log LookingGlass output to ~/.cinnamon/glass.log (Requires Cinnamon restart)"), "org.cinnamon", "enable-looking-glass-logs")) - sidePage.add_widget(GSettingsCheckButton(_("Emulate middle click by clicking both left and right buttons"), "org.gnome.settings-daemon.peripherals.mouse", "middle-button-enabled")) - sidePage.add_widget(GSettingsCheckButton(_("Display notifications"), "org.cinnamon", "display-notifications")) + sidePage.add_widget(GSettingsCheckButton(_("Log LookingGlass output to ~/.cinnamon/glass.log (Requires Cinnamon restart)"), "org.cinnamon", "enable-looking-glass-logs", None)) + sidePage.add_widget(GSettingsCheckButton(_("Emulate middle click by clicking both left and right buttons"), "org.gnome.settings-daemon.peripherals.mouse", "middle-button-enabled", None)) + sidePage.add_widget(GSettingsCheckButton(_("Display notifications"), "org.cinnamon", "display-notifications", None)) #sidePage = SidePage(_("Terminal"), "terminal", self.content_box) #self.sidePages.append(sidePage) #sidePage.add_widget(GConfCheckButton(_("Show fortune cookies"), "/desktop/linuxmint/terminal/show_fortunes")) + sidePage = BackgroundSidePage(_("Backgrounds"), "backgrounds.svg", self.content_box) + self.sidePages.append((sidePage, "backgrounds")) + # create the backing store for the side nav-view. self.store = Gtk.ListStore(str, GdkPixbuf.Pixbuf, object) sidePagesIters = {} for sidePage, sidePageID in self.sidePages: - img = GdkPixbuf.Pixbuf.new_from_file_at_size( "/usr/lib/cinnamon-settings/data/icons/%s" % sidePage.icon, 48, 48) + iconFile = "/usr/lib/cinnamon-settings/data/icons/%s" % sidePage.icon + if os.path.exists(iconFile): + img = GdkPixbuf.Pixbuf.new_from_file_at_size( iconFile, 48, 48) + else: + img = None sidePagesIters[sidePageID] = self.store.append([sidePage.name, img, sidePage]) # set up the side view - navigation. diff --git a/files/usr/lib/cinnamon-settings/cinnamon-settings.ui b/files/usr/lib/cinnamon-settings/cinnamon-settings.ui index 27dd138..381c751 100644 --- a/files/usr/lib/cinnamon-settings/cinnamon-settings.ui +++ b/files/usr/lib/cinnamon-settings/cinnamon-settings.ui @@ -64,7 +64,7 @@ True True True - 90 + 110 0 0 diff --git a/files/usr/lib/cinnamon-settings/data/icons/backgrounds.svg b/files/usr/lib/cinnamon-settings/data/icons/backgrounds.svg new file mode 100644 index 0000000..8d7405a --- /dev/null +++ b/files/usr/lib/cinnamon-settings/data/icons/backgrounds.svg @@ -0,0 +1,275 @@ + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/files/usr/lib/cinnamon-settings/imtools.py b/files/usr/lib/cinnamon-settings/imtools.py new file mode 100644 index 0000000..3520961 --- /dev/null +++ b/files/usr/lib/cinnamon-settings/imtools.py @@ -0,0 +1,1249 @@ +# Copyright (C) 2007-2010 www.stani.be +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see http://www.gnu.org/licenses/ + +import os +from cStringIO import StringIO +from itertools import cycle +from urllib import urlopen + +import Image +import ImageDraw +import ImageEnhance +import ImageOps, ImageChops, ImageFilter + +ALL_PALETTE_INDICES = set(range(256)) +CHECKBOARD = {} +COLOR_MAP = [255] * 128 + [0] * 128 +WWW_CACHE = {} + +EXT_BY_FORMATS = { + 'JPEG': ['JPG', 'JPEG', 'JPE'], + 'TIFF': ['TIF', 'TIFF'], + 'SVG': ['SVG', 'SVGZ'], +} +FORMATS_BY_EXT = {} +for format, exts in EXT_BY_FORMATS.items(): + for ext in exts: + FORMATS_BY_EXT[ext] = format + +CROSS = 'Cross' +ROUNDED = 'Rounded' +SQUARE = 'Square' + +CORNERS = [ROUNDED, SQUARE, CROSS] +CORNER_ID = 'rounded_corner_r%d_f%d' +CROSS_POS = (CROSS, CROSS, CROSS, CROSS) +ROUNDED_POS = (ROUNDED, ROUNDED, ROUNDED, ROUNDED) +ROUNDED_RECTANGLE_ID = 'rounded_rectangle_r%d_f%d_s%s_p%s' + +class InvalidWriteFormatError(Exception): + pass + + +def drop_shadow(image, horizontal_offset=5, vertical_offset=5, + background_color=(255, 255, 255, 0), shadow_color=0x444444, + border=8, shadow_blur=3, force_background_color=False, cache=None): + """Add a gaussian blur drop shadow to an image. + + :param image: The image to overlay on top of the shadow. + :param type: PIL Image + :param offset: + + Offset of the shadow from the image as an (x,y) tuple. + Can be positive or negative. + + :type offset: tuple of integers + :param background_color: Background color behind the image. + :param shadow_color: Shadow color (darkness). + :param border: + + Width of the border around the image. This must be wide + enough to account for the blurring of the shadow. + + :param shadow_blur: + + Number of times to apply the filter. More shadow_blur + produce a more blurred shadow, but increase processing time. + """ + if cache is None: + cache = {} + + if has_transparency(image) and image.mode != 'RGBA': + # Make sure 'LA' and 'P' with trasparency are handled + image = image.convert('RGBA') + + #get info + size = image.size + mode = image.mode + + back = None + + #assert image is RGBA + if mode != 'RGBA': + if mode != 'RGB': + image = image.convert('RGB') + mode = 'RGB' + #create cache id + id = ''.join([str(x) for x in ['shadow_', size, + horizontal_offset, vertical_offset, border, shadow_blur, + background_color, shadow_color]]) + + #look up in cache + if id in cache: + #retrieve from cache + back, back_size = cache[id] + + if back is None: + #size of backdrop + back_size = (size[0] + abs(horizontal_offset) + 2 * border, + size[1] + abs(vertical_offset) + 2 * border) + + #create shadow mask + if mode == 'RGBA': + image_mask = get_alpha(image) + shadow = Image.new('L', back_size, 0) + else: + image_mask = Image.new(mode, size, shadow_color) + shadow = Image.new(mode, back_size, background_color) + + shadow_left = border + max(horizontal_offset, 0) + shadow_top = border + max(vertical_offset, 0) + paste(shadow, image_mask, (shadow_left, shadow_top, + shadow_left + size[0], shadow_top + size[1])) + del image_mask # free up memory + + #blur shadow mask + + #Apply the filter to blur the edges of the shadow. Since a small + #kernel is used, the filter must be applied repeatedly to get a decent + #blur. + n = 0 + while n < shadow_blur: + shadow = shadow.filter(ImageFilter.BLUR) + n += 1 + + #create back + if mode == 'RGBA': + back = Image.new('RGBA', back_size, shadow_color) + back.putalpha(shadow) + del shadow # free up memory + else: + back = shadow + cache[id] = back, back_size + + #Paste the input image onto the shadow backdrop + image_left = border - min(horizontal_offset, 0) + image_top = border - min(vertical_offset, 0) + if mode == 'RGBA': + paste(back, image, (image_left, image_top), image) + if force_background_color: + mask = get_alpha(back) + paste(back, Image.new('RGB', back.size, background_color), + (0, 0), ImageChops.invert(mask)) + back.putalpha(mask) + else: + paste(back, image, (image_left, image_top)) + + return back + +def round_image(image, cache={}, round_all=True, rounding_type=None, + radius=100, opacity=255, pos=ROUNDED_POS, back_color='#FFFFFF'): + + if image.mode != 'RGBA': + image = image.convert('RGBA') + + if round_all: + pos = 4 * (rounding_type, ) + + mask = create_rounded_rectangle(image.size, cache, radius, opacity, pos) + + paste(image, Image.new('RGB', image.size, back_color), (0, 0), + ImageChops.invert(mask)) + image.putalpha(mask) + return image + +def create_rounded_rectangle(size=(600, 400), cache={}, radius=100, + opacity=255, pos=ROUNDED_POS): + #rounded_rectangle + im_x, im_y = size + rounded_rectangle_id = ROUNDED_RECTANGLE_ID % (radius, opacity, size, pos) + if rounded_rectangle_id in cache: + return cache[rounded_rectangle_id] + else: + #cross + cross_id = ROUNDED_RECTANGLE_ID % (radius, opacity, size, CROSS_POS) + if cross_id in cache: + cross = cache[cross_id] + else: + cross = cache[cross_id] = Image.new('L', size, 0) + draw = ImageDraw.Draw(cross) + draw.rectangle((radius, 0, im_x - radius, im_y), fill=opacity) + draw.rectangle((0, radius, im_x, im_y - radius), fill=opacity) + if pos == CROSS_POS: + return cross + #corner + corner_id = CORNER_ID % (radius, opacity) + if corner_id in cache: + corner = cache[corner_id] + else: + corner = cache[corner_id] = create_corner(radius, opacity) + #rounded rectangle + rectangle = Image.new('L', (radius, radius), 255) + rounded_rectangle = cross.copy() + for index, angle in enumerate(pos): + if angle == CROSS: + continue + if angle == ROUNDED: + element = corner + else: + element = rectangle + if index % 2: + x = im_x - radius + element = element.transpose(Image.FLIP_LEFT_RIGHT) + else: + x = 0 + if index < 2: + y = 0 + else: + y = im_y - radius + element = element.transpose(Image.FLIP_TOP_BOTTOM) + paste(rounded_rectangle, element, (x, y)) + cache[rounded_rectangle_id] = rounded_rectangle + return rounded_rectangle + +def create_corner(radius=100, opacity=255, factor=2): + corner = Image.new('L', (factor * radius, factor * radius), 0) + draw = ImageDraw.Draw(corner) + draw.pieslice((0, 0, 2 * factor * radius, 2 * factor * radius), + 180, 270, fill=opacity) + corner = corner.resize((radius, radius), Image.ANTIALIAS) + return corner + +def get_format(ext): + """Guess the image format by the file extension. + + :param ext: file extension + :type ext: string + :returns: image format + :rtype: string + + .. warning:: + + This is only meant to check before saving files. For existing files + open the image with PIL and check its format attribute. + + >>> get_format('jpg') + 'JPEG' + """ + ext = ext.lstrip('.').upper() + return FORMATS_BY_EXT.get(ext, ext) + +def open_image_data(data): + """Open image from format data. + + :param data: image format data + :type data: string + :returns: image + :rtype: pil.Image + """ + return Image.open(StringIO(data)) + + +def open_image_exif(uri): + """Open local files or remote files over http and transpose the + image to its exif orientation. + + :param uri: image location + :type uri: string + :returns: image + :rtype: pil.Image + """ + return transpose_exif(open_image(uri)) + + +class _ByteCounter: + """Helper class to count how many bytes are written to a file. + + .. see also:: :func:`get_size` + + >>> bc = _ByteCounter() + >>> bc.write('12345') + >>> bc.bytes + 5 + """ + def __init__(self): + self.bytes = 0 + + def write(self, data): + self.bytes += len(data) + + +def get_size(im, format, **options): + """Gets the size in bytes if the image would be written to a file. + + :param format: image file format (e.g. ``'JPEG'``) + :type format: string + :returns: the file size in bytes + :rtype: int + """ + try: + out = _ByteCounter() + im.save(out, format, **options) + return out.bytes + except AttributeError: + # fall back on full in-memory compression + out = StringIO() + im.save(out, format, **options) + return len(out.getvalue()) + + +def get_quality(im, size, format, down=0, up=100, delta=1000, options=None): + """Figure out recursively the quality save parameter to obtain a + certain image size. This mostly used for ``JPEG`` images. + + :param im: image + :type im: pil.Image + :param format: image file format (e.g. ``'JPEG'``) + :type format: string + :param down: minimum file size in bytes + :type down: int + :param up: maximum file size in bytes + :type up: int + :param delta: fault tolerance in bytes + :type delta: int + :param options: image save options + :type options: dict + :returns: save quality + :rtype: int + + Example:: + + filename = '/home/stani/sync/Desktop/IMGA3345.JPG' + im = Image.open(filename) + q = get_quality(im, 300000, "JPEG") + im.save(filename.replace('.jpg', '_sized.jpg')) + """ + if options is None: + options = {} + q = options['quality'] = (down + up) / 2 + if q == down or q == up: + return max(q, 1) + s = get_size(im, format, **options) + if abs(s - size) < delta: + return q + elif s > size: + return get_quality(im, size, format, down, up=q, options=options) + else: + return get_quality(im, size, format, down=q, up=up, options=options) + + +def fill_background_color(image, color): + """Fills given image with background color. + + :param image: source image + :type image: pil.Image + :param color: background color + :type color: tuple of int + :returns: filled image + :rtype: pil.Image + """ + if image.mode == 'LA': + image = image.convert('RGBA') + elif image.mode != 'RGBA' and\ + not (image.mode == 'P' and 'transparency' in image.info): + return image + if len(color) == 4 and color[-1] != 255: + mode = 'RGBA' + else: + mode = 'RGB' + back = Image.new(mode, image.size, color) + if (image.mode == 'P' and mode == 'RGBA'): + image = image.convert('RGBA') + if has_alpha(image): + paste(back, image, mask=image) + elif image.mode == 'P': + palette = image.getpalette() + index = image.info['transparency'] + palette[index * 3: index * 3 + 3] = color[:3] + image.putpalette(palette) + del image.info['transparency'] + back = image + else: + paste(back, image) + return back + + +def generate_layer(image_size, mark, method, + horizontal_offset, vertical_offset, + horizontal_justification, vertical_justification, + orientation, opacity): + """Generate new layer for backgrounds or watermarks on which a given + image ``mark`` can be positioned, scaled or repeated. + + :param image_size: size of the reference image + :type image_size: tuple of int + :param mark: image mark + :type mark: pil.Image + :param method: ``'Tile'``, ``'Scale'``, ``'By Offset'`` + :type method: string + :param horizontal_offset: horizontal offset + :type horizontal_offset: int + :param vertical_offset: vertical offset + :type vertical_offset: int + :param horizontal_justification: ``'Left'``, ``'Middle'``, ``'Right'`` + :type horizontal_justification: string + :param vertical_justification: ``'Top'``, ``'Middle'``, ``'Bottom'`` + :type vertical_justification: string + :param orientation: mark orientation (e.g. ``'ROTATE_270'``) + :type orientation: string + :param opacity: opacity within ``[0, 1]`` + :type opacity: float + :returns: generated layer + :rtype: pil.Image + + .. see also:: :func:`reduce_opacity` + """ + mark = convert_safe_mode(open_image(mark)) + opacity /= 100.0 + mark = reduce_opacity(mark, opacity) + layer = Image.new('RGBA', image_size, (0, 0, 0, 0)) + if method == 'Tile': + for y in range(0, image_size[1], mark.size[1]): + for x in range(0, image_size[0], mark.size[0]): + paste(layer, mark, (x, y)) + elif method == 'Scale': + # scale, but preserve the aspect ratio + ratio = min(float(image_size[0]) / mark.size[0], + float(image_size[1]) / mark.size[1]) + w = int(mark.size[0] * ratio) + h = int(mark.size[1] * ratio) + mark = mark.resize((w, h)) + paste(layer, mark, ((image_size[0] - w) / 2, + (image_size[1] - h) / 2)) + elif method == 'By Offset': + location = calculate_location( + horizontal_offset, vertical_offset, + horizontal_justification, vertical_justification, + image_size, mark.size) + if orientation: + orientation_value = getattr(Image, orientation) + mark = mark.transpose(orientation_value) + paste(layer, mark, location, force=True) + else: + raise ValueError('Unknown method "%s" for generate_layer.' % method) + return layer + + +def identity_color(image, value=0): + """Get a color with same color component values. + + >>> im = Image.new('RGB', (1,1)) + >>> identity_color(im, 2) + (2, 2, 2) + >>> im = Image.new('L', (1,1)) + >>> identity_color(im, 7) + 7 + """ + bands = image.getbands() + if len(bands) == 1: + return value + return tuple([value for band in bands]) + + +def blend(im1, im2, amount, color=None): + """Blend two images with each other. If the images differ in size + the color will be used for undefined pixels. + + :param im1: first image + :type im1: pil.Image + :param im2: second image + :type im2: pil.Image + :param amount: amount of blending + :type amount: int + :param color: color of undefined pixels + :type color: tuple + :returns: blended image + :rtype: pil.Image + """ + im2 = convert_safe_mode(im2) + if im1.size == im2.size: + im1 = convert(im1, im2.mode) + else: + if color is None: + expanded = Image.new(im2.mode, im2.size) + elif im2.mode in ('1', 'L') and type(color) != int: + expanded = Image.new(im2.mode, im2.size, color[0]) + else: + expanded = Image.new(im2.mode, im2.size, color) + im1 = im1.convert(expanded.mode) + we, he = expanded.size + wi, hi = im1.size + paste(expanded, im1, ((we - wi) / 2, (he - hi) / 2), + im1.convert('RGBA')) + im1 = expanded + return Image.blend(im1, im2, amount) + + +def reduce_opacity(im, opacity): + """Returns an image with reduced opacity if opacity is + within ``[0, 1]``. + + :param im: source image + :type im: pil.Image + :param opacity: opacity within ``[0, 1]`` + :type opacity: float + :returns im: image + :rtype: pil.Image + + >>> im = Image.new('RGBA', (1, 1), (255, 255, 255)) + >>> im = reduce_opacity(im, 0.5) + >>> im.getpixel((0,0)) + (255, 255, 255, 127) + """ + if opacity < 0 or opacity > 1: + return im + alpha = get_alpha(im) + alpha = ImageEnhance.Brightness(alpha).enhance(opacity) + put_alpha(im, alpha) + return im + + +def calculate_location(horizontal_offset, vertical_offset, + horizontal_justification, vertical_justification, + canvas_size, image_size): + """Calculate location based on offset and justification. Offsets + can be positive and negative. + + :param horizontal_offset: horizontal offset + :type horizontal_offset: int + :param vertical_offset: vertical offset + :type vertical_offset: int + :param horizontal_justification: ``'Left'``, ``'Middle'``, ``'Right'`` + :type horizontal_justification: string + :param vertical_justification: ``'Top'``, ``'Middle'``, ``'Bottom'`` + :type vertical_justification: string + :param canvas_size: size of the total canvas + :type canvas_size: tuple of int + :param image_size: size of the image/text which needs to be placed + :type image_size: tuple of int + :returns: location + :rtype: tuple of int + + .. see also:: :func:`generate layer` + + >>> calculate_location(50, 50, 'Left', 'Middle', (100,100), (10,10)) + (50, 45) + """ + canvas_width, canvas_height = canvas_size + image_width, image_height = image_size + + # check offsets + if horizontal_offset < 0: + horizontal_offset += canvas_width + if vertical_offset < 0: + vertical_offset += canvas_height + + # check justifications + if horizontal_justification == 'Left': + horizontal_delta = 0 + elif horizontal_justification == 'Middle': + horizontal_delta = -image_width / 2 + elif horizontal_justification == 'Right': + horizontal_delta = -image_width + + if vertical_justification == 'Top': + vertical_delta = 0 + elif vertical_justification == 'Middle': + vertical_delta = -image_height / 2 + elif vertical_justification == 'Bottom': + vertical_delta = -image_height + + return horizontal_offset + horizontal_delta, \ + vertical_offset + vertical_delta + + +#################################### +#### PIL helper functions #### +#################################### + + +def flatten(l): + """Flatten a list. + + :param l: list to be flattened + :type l: list + :returns: flattened list + :rtype: list + + >>> flatten([[1, 2], [3]]) + [1, 2, 3] + """ + return [item for sublist in l for item in sublist] + + +def has_alpha(image): + """Checks if the image has an alpha band. + i.e. the image mode is either RGBA or LA. + The transparency in the P mode doesn't count as an alpha band + + :param image: the image to check + :type image: PIL image object + :returns: True or False + :rtype: boolean + """ + return image.mode.endswith('A') + + +def has_transparency(image): + """Checks if the image has transparency. + The image has an alpha band or a P mode with transparency. + + :param image: the image to check + :type image: PIL image object + :returns: True or False + :rtype: boolean + """ + return (image.mode == 'P' and 'transparency' in image.info) or\ + has_alpha(image) + + +if Image.VERSION == '1.1.7': + + def split(image): + """Work around for bug in Pil 1.1.7 + + :param image: input image + :type image: PIL image object + :returns: the different color bands of the image (eg R, G, B) + :rtype: tuple + """ + image.load() + return image.split() +else: + + def split(image): + """Work around for bug in Pil 1.1.7 + + :param image: input image + :type image: PIL image object + :returns: the different color bands of the image (eg R, G, B) + :rtype: tuple + """ + return image.split() + + +def get_alpha(image): + """Gets the image alpha band. Can handles P mode images with transpareny. + Returns a band with all values set to 255 if no alpha band exists. + + :param image: input image + :type image: PIL image object + :returns: alpha as a band + :rtype: single band image object + """ + if has_alpha(image): + return split(image)[-1] + if image.mode == 'P' and 'transparency' in image.info: + return image.convert('RGBA').split()[-1] + # No alpha layer, create one. + return Image.new('L', image.size, 255) + + +def get_format_data(image, format): + """Convert the image in the file bytes of the image. By consequence + this byte data is different for the chosen format (``JPEG``, + ``TIFF``, ...). + + .. see also:: :func:`thumbnail.get_format_data` + + :param image: source image + :type impage: pil.Image + :param format: image file type format + :type format: string + :returns: byte data of the image + """ + f = StringIO() + convert_save_mode_by_format(image, format).save(f, format) + return f.getvalue() + + +def get_palette(image): + """Gets the palette of an image as a sequence of (r, g, b) tuples. + + :param image: image with a palette + :type impage: pil.Image + :returns: palette colors + :rtype: a sequence of (r, g, b) tuples + """ + palette = image.resize((256, 1)) + palette.putdata(range(256)) + return list(palette.convert("RGB").getdata()) + + +def get_used_palette_indices(image): + """Get used color indices in an image palette. + + :param image: image with a palette + :type impage: pil.Image + :returns: used colors of the palette + :rtype: set of integers (0-255) + """ + return set(image.getdata()) + + +def get_used_palette_colors(image): + """Get used colors in an image palette as a sequence of (r, g, b) tuples. + + :param image: image with a palette + :type impage: pil.Image + :returns: used colors of the palette + :rtype: sequence of (r, g, b) tuples + """ + used_indices = get_used_palette_indices(image) + if 'transparency' in image.info: + used_indices -= set([image.info['transparency']]) + n = len(used_indices) + palette = image.resize((n, 1)) + palette.putdata(used_indices) + return palette.convert("RGB").getdata() + + +def get_unused_palette_indices(image): + """Get unused color indices in an image palette. + + :param image: image with a palette + :type impage: pil.Image + :returns: unused color indices of the palette + :rtype: set of 0-255 + """ + return ALL_PALETTE_INDICES - get_used_palette_indices(image) + + +def fit_color_in_palette(image, color): + """Fit a color into a palette. If the color exists already in the palette + return its current index, otherwise add the color to the palette if + possible. Returns -1 for color index if all colors are used already. + + :param image: image with a palette + :type image: pil.Image + :param color: color to fit + :type color: (r, g, b) tuple + :returns: color index, (new) palette + :rtype: (r, g, b) tuple, sequence of (r, g, b) tuples + """ + palette = get_palette(image) + try: + index = palette.index(color) + except ValueError: + index = -1 + if index > -1: + # Check if it is not the transparent index, as that doesn't qualify. + try: + transparent = index == image.info['transparency'] + except KeyError: + transparent = False + # If transparent, look further + if transparent: + try: + index = palette[index + 1:].index(color) + index + 1 + except ValueError: + index = -1 + if index == -1: + unused = list(get_unused_palette_indices(image)) + if unused: + index = unused[0] + palette[index] = color # add color to palette + else: + palette = None # palette is full + return index, palette + + +def put_palette(image_to, image_from, palette=None): + """Copies the palette and transparency of one image to another. + + :param image_to: image with a palette + :type image_to: pil.Image + :param image_from: image with a palette + :type image_from: pil.Image + :param palette: image palette + :type palette: sequence of (r, g, b) tuples or None + """ + if palette == None: + palette = get_palette(image_from) + image_to.putpalette(flatten(palette)) + if 'transparency' in image_from.info: + image_to.info['transparency'] = image_from.info['transparency'] + + +def put_alpha(image, alpha): + """Copies the given band to the alpha layer of the given image. + + :param image: input image + :type image: PIL image object + :param alpha: the alpha band to copy + :type alpha: single band image object + """ + if image.mode in ['CMYK', 'YCbCr', 'P']: + image = image.convert('RGBA') + elif image.mode in ['1', 'F']: + image = image.convert('RGBA') + image.putalpha(alpha) + + +def remove_alpha(image): + """Returns a copy of the image after removing the alpha band or + transparency + + :param image: input image + :type image: PIL image object + :returns: the input image after removing the alpha band or transparency + :rtype: PIL image object + """ + if image.mode == 'RGBA': + return image.convert('RGB') + if image.mode == 'LA': + return image.convert('L') + if image.mode == 'P' and 'transparency' in image.info: + img = image.convert('RGB') + del img.info['transparency'] + return img + return image + + +def paste(destination, source, box=(0, 0), mask=None, force=False): + """"Pastes the source image into the destination image while using an + alpha channel if available. + + :param destination: destination image + :type destination: PIL image object + :param source: source image + :type source: PIL image object + :param box: + + The box argument is either a 2-tuple giving the upper left corner, + a 4-tuple defining the left, upper, right, and lower pixel coordinate, + or None (same as (0, 0)). If a 4-tuple is given, the size of the + pasted image must match the size of the region. + + :type box: tuple + :param mask: mask or None + + :type mask: bool or PIL image object + :param force: + + With mask: Force the invert alpha paste or not. + + Without mask: + + - If ``True`` it will overwrite the alpha channel of the destination + with the alpha channel of the source image. So in that case the + pixels of the destination layer will be abandonned and replaced + by exactly the same pictures of the destination image. This is mostly + what you need if you paste on a transparant canvas. + - If ``False`` this will use a mask when the image has an alpha + channel. In this case pixels of the destination image will appear + through where the source image is transparent. + + :type force: bool + """ + # Paste on top + if source == mask: + if has_alpha(source): + # invert_alpha = the transparant pixels of the destination + if has_alpha(destination) and (destination.size == source.size + or force): + invert_alpha = ImageOps.invert(get_alpha(destination)) + if invert_alpha.size != source.size: + # if sizes are not the same be careful! + # check the results visually + if len(box) == 2: + w, h = source.size + box = (box[0], box[1], box[0] + w, box[1] + h) + invert_alpha = invert_alpha.crop(box) + else: + invert_alpha = None + # we don't want composite of the two alpha channels + source_without_alpha = remove_alpha(source) + # paste on top of the opaque destination pixels + destination.paste(source_without_alpha, box, source) + if invert_alpha != None: + # the alpha channel is ok now, so save it + destination_alpha = get_alpha(destination) + # paste on top of the transparant destination pixels + # the transparant pixels of the destination should + # be filled with the color information from the source + destination.paste(source_without_alpha, box, invert_alpha) + # restore the correct alpha channel + destination.putalpha(destination_alpha) + else: + destination.paste(source, box) + elif mask: + destination.paste(source, box, mask) + else: + destination.paste(source, box) + if force and has_alpha(source): + destination_alpha = get_alpha(destination) + source_alpha = get_alpha(source) + destination_alpha.paste(source_alpha, box) + destination.putalpha(destination_alpha) + + +def auto_crop(image): + """Crops all transparent or black background from the image + :param image: input image + :type image: PIL image object + :returns: the cropped image + :rtype: PIL image object + """ + + alpha = get_alpha(image) + box = alpha.getbbox() + return convert_safe_mode(image).crop(box) + + +def convert(image, mode, *args, **keyw): + """Returns a converted copy of an image + + :param image: input image + :type image: PIL image object + :param mode: the new mode + :type mode: string + :param args: extra options + :type args: tuple of values + :param keyw: extra keyword options + :type keyw: dictionary of options + :returns: the converted image + :rtype: PIL image object + """ + if mode == 'P': + if image.mode == 'P': + return image + if image.mode in ['1', 'F']: + return image.convert('L').convert(mode, *args, **keyw) + if image.mode in ['RGBA', 'LA']: + alpha = get_alpha(image) + output = image.convert('RGB').convert( + mode, colors=255, *args, **keyw) + paste(output, + 255, alpha.point(COLOR_MAP)) + output.info['transparency'] = 255 + return output + return image.convert('RGB').convert(mode, *args, **keyw) + if image.mode == 'P' and mode == 'LA': + # A workaround for a PIL bug. + # Converting from P to LA directly doesn't work. + return image.convert('RGBA').convert('LA', *args, **keyw) + if has_transparency(image) and (not mode in ['RGBA', 'LA']): + if image.mode == 'P': + image = image.convert('RGBA') + del image.info['transparency'] + #image = fill_background_color(image, (255, 255, 255, 255)) + image = image.convert(mode, *args, **keyw) + return image + return image.convert(mode, *args, **keyw) + + +def convert_safe_mode(image): + """Converts image into a processing-safe mode. + + :param image: input image + :type image: PIL image object + :returns: the converted image + :rtype: PIL image object + """ + if image.mode in ['1', 'F']: + return image.convert('L') + if image.mode == 'P' and 'transparency' in image.info: + img = image.convert('RGBA') + del img.info['transparency'] + return img + if image.mode in ['P', 'YCbCr', 'CMYK', 'RGBX']: + return image.convert('RGB') + return image + + +def convert_save_mode_by_format(image, format): + """Converts image into a saving-safe mode. + + :param image: input image + :type image: PIL image object + :param format: target format + :type format: string + :returns: the converted image + :rtype: PIL image object + """ + #TODO: Extend this helper function to support other formats as well + if image.mode == 'P': + # Make sure P is handled correctly + if not format in ['GIF', 'PNG', 'TIFF', 'IM', 'PCX']: + image = remove_alpha(image) + if format == 'JPEG': + if image.mode in ['RGBA', 'P']: + return image.convert('RGB') + if image.mode in ['LA']: + return image.convert('L') + elif format == 'BMP': + if image.mode in ['LA']: + return image.convert('L') + if image.mode in ['P', 'RGBA', 'YCbCr', 'CMYK']: + return image.convert('RGB') + elif format == 'DIB': + if image.mode in ['YCbCr', 'CMYK']: + return image.convert('RGB') + elif format == 'EPS': + if image.mode in ['1', 'LA']: + return image.convert('L') + if image.mode in ['P', 'RGBA', 'YCbCr']: + return image.convert('RGB') + elif format == 'GIF': + return convert(image, 'P', palette=Image.ADAPTIVE) + elif format == 'PBM': + if image.mode != '1': + return image.convert('1') + elif format == 'PCX': + if image.mode in ['RGBA', 'CMYK', 'YCbCr']: + return image.convert('RGB') + if image.mode in ['LA', '1']: + return image.convert('L') + elif format == 'PDF': + if image.mode in ['LA']: + return image.convert('L') + if image.mode in ['RGBA', 'YCbCr']: + return image.convert('RGB') + elif format == 'PGM': + if image.mode != 'L': + return image.convert('L') + elif format == 'PPM': + if image.mode in ['P', 'CMYK', 'YCbCr']: + return image.convert('RGB') + if image.mode in ['LA']: + return image.convert('L') + elif format == 'PS': + if image.mode in ['1', 'LA']: + return image.convert('L') + if image.mode in ['P', 'RGBA', 'YCbCr']: + return image.convert('RGB') + elif format == 'XBM': + if not image.mode in ['1']: + return image.convert('1') + elif format == 'TIFF': + if image.mode in ['YCbCr']: + return image.convert('RGB') + elif format == 'PNG': + if image.mode in ['CMYK', 'YCbCr']: + return image.convert('RGB') + #for consistency return a copy! (thumbnail.py depends on it) + return image.copy() + + +def save_check_mode(image, filename, **options): + #save image with pil + save(image, filename, **options) + #verify saved file + try: + image_file = Image.open(filename) + image_file.verify() + except IOError: + # We can't verify the image mode with PIL, so issue no warnings. + return '' + if image.mode != image_file.mode: + return image_file.mode + return '' + + +def save_safely(image, filename): + """Saves an image with a filename and raise the specific + ``InvalidWriteFormatError`` in case of an error instead of a + ``KeyError``. It can also save IM files with unicode. + + :param image: image + :type image: pil.Image + :param filename: image filename + :type filename: string + """ + ext = os.path.splitext(filename)[-1] + format = get_format(ext[1:]) + image = convert_save_mode_by_format(image, format) + save(image, filename) + + +def get_reverse_transposition(transposition): + """Get the reverse transposition method. + + :param transposition: transpostion, e.g. ``Image.ROTATE_90`` + :returns: inverse transpostion, e.g. ``Image.ROTATE_270`` + """ + if transposition == Image.ROTATE_90: + return Image.ROTATE_270 + elif transposition == Image.ROTATE_270: + return Image.ROTATE_90 + return transposition + + +def get_exif_transposition(orientation): + """Get the transposition methods necessary to aling the image to + its exif orientation. + + :param orientation: exif orientation + :type orientation: int + :returns: (transposition methods, reverse transpostion methods) + :rtype: tuple + """ + #see EXIF.py + if orientation == 1: + transposition = transposition_reverse = () + elif orientation == 2: + transposition = Image.FLIP_LEFT_RIGHT, + transposition_reverse = Image.FLIP_LEFT_RIGHT, + elif orientation == 3: + transposition = Image.ROTATE_180, + transposition_reverse = Image.ROTATE_180, + elif orientation == 4: + transposition = Image.FLIP_TOP_BOTTOM, + transposition_reverse = Image.FLIP_TOP_BOTTOM, + elif orientation == 5: + transposition = Image.FLIP_LEFT_RIGHT, \ + Image.ROTATE_90 + transposition_reverse = Image.ROTATE_270, \ + Image.FLIP_LEFT_RIGHT + elif orientation == 6: + transposition = Image.ROTATE_270, + transposition_reverse = Image.ROTATE_90, + elif orientation == 7: + transposition = Image.FLIP_LEFT_RIGHT, \ + Image.ROTATE_270 + transposition_reverse = Image.ROTATE_90, \ + Image.FLIP_LEFT_RIGHT + elif orientation == 8: + transposition = Image.ROTATE_90, + transposition_reverse = Image.ROTATE_270, + else: + transposition = transposition_reverse = () + return transposition, transposition_reverse + + +def get_exif_orientation(image): + """Gets the exif orientation of an image. + + :param image: image + :type image: pil.Image + :returns: orientation + :rtype: int + """ + if not hasattr(image, '_getexif'): + return 1 + try: + _exif = image._getexif() + if not _exif: + return 1 + return _exif[0x0112] + except KeyError: + return 1 + + +def transpose(image, methods): + """Transpose with a sequence of transformations, mainly useful + for exif. + + :param image: image + :type image: pil.Image + :param methods: transposition methods + :type methods: list + :returns: transposed image + :rtype: pil.Image + """ + for method in methods: + image = image.transpose(method) + return image + + +def transpose_exif(image, reverse=False): + """Transpose an image to its exif orientation. + + :param image: image + :type image: pil.Image + :param reverse: False when opening, True when saving + :type reverse: bool + :returns: transposed image + :rtype: pil.Image + """ + orientation = get_exif_orientation(image) + transposition = get_exif_transposition(orientation)[int(reverse)] + if transposition: + return transpose(image, transposition) + return image + + +def checkboard(size, delta=8, fg=(128, 128, 128), bg=(204, 204, 204)): + """Draw an n x n checkboard, which is often used as background + for transparent images. The checkboards are stored in the + ``CHECKBOARD`` cache. + + :param delta: dimension of one square + :type delta: int + :param fg: foreground color + :type fg: tuple of int + :param bg: background color + :type bg: tuple of int + :returns: checkboard image + :rtype: pil.Image + """ + if not (size in CHECKBOARD): + dim = max(size) + n = int(dim / delta) + 1 # FIXME: now acts like square->nx, ny + + def sq_start(i): + "Return the x/y start coord of the square at column/row i." + return i * delta + + def square(i, j): + "Return the square corners" + return map(sq_start, [i, j, i + 1, j + 1]) + + image = Image.new("RGB", size, bg) + draw_square = ImageDraw.Draw(image).rectangle + squares = (square(i, j) + for i_start, j in zip(cycle((0, 1)), range(n)) + for i in range(i_start, n, 2)) + for sq in squares: + draw_square(sq, fill=fg) + CHECKBOARD[size] = image + return CHECKBOARD[size].copy() + + +def add_checkboard(image): + """"If the image has a transparent mask, a RGB checkerboard will be + drawn in the background. + + .. note:: + + In case of a thumbnail, the resulting image can not be used for + the cache, as it replaces the transparency layer with a non + transparent checkboard. + + :param image: image + :type image: pil.Image + :returns: image, with checkboard if transparant + :rtype: pil.Image + """ + if (image.mode == 'P' and 'transparency' in image.info) or\ + image.mode.endswith('A'): + #transparant image + image = image.convert('RGBA') + image_bg = checkboard(image.size) + paste(image_bg, image, (0, 0), image) + return image_bg + else: + return image diff --git a/files/usr/share/applications/cinnamon-settings.desktop b/files/usr/share/applications/cinnamon-settings.desktop index 2867538..2d5bf54 100644 --- a/files/usr/share/applications/cinnamon-settings.desktop +++ b/files/usr/share/applications/cinnamon-settings.desktop @@ -5,6 +5,5 @@ Exec=cinnamon-settings Icon=preferences-system Terminal=false Type=Application -Encoding=UTF-8 Categories=GNOME;GTK;Settings;DesktopSettings; StartupNotify=false diff --git a/files/usr/share/applications/cinnamon2d.desktop b/files/usr/share/applications/cinnamon2d.desktop index 5da42f9..5979642 100644 --- a/files/usr/share/applications/cinnamon2d.desktop +++ b/files/usr/share/applications/cinnamon2d.desktop @@ -6,8 +6,8 @@ Exec=/usr/bin/cinnamon2d X-GNOME-Bugzilla-Bugzilla=GNOME X-GNOME-Bugzilla-Product=cinnamon X-GNOME-Bugzilla-Component=general -X-GNOME-Bugzilla-Version=1.5.2 -Categories=GNOME;GTK;Core; +X-GNOME-Bugzilla-Version=1.6.0 +Categories=GNOME;GTK;System;Core; OnlyShowIn=GNOME; NoDisplay=true X-GNOME-Autostart-Phase=WindowManager diff --git a/files/usr/share/cinnamon/applets/a11y@cinnamon.org/applet.js b/files/usr/share/cinnamon/applets/a11y@cinnamon.org/applet.js index aec8678..ac9f746 100644 --- a/files/usr/share/cinnamon/applets/a11y@cinnamon.org/applet.js +++ b/files/usr/share/cinnamon/applets/a11y@cinnamon.org/applet.js @@ -29,15 +29,15 @@ const KEY_TEXT_SCALING_FACTOR = 'text-scaling-factor'; const HIGH_CONTRAST_THEME = 'HighContrast'; -function MyApplet(orientation) { - this._init(orientation); +function MyApplet(orientation, panel_height) { + this._init(orientation, panel_height); } MyApplet.prototype = { __proto__: Applet.IconApplet.prototype, - _init: function(orientation) { - Applet.IconApplet.prototype._init.call(this, orientation); + _init: function(orientation, panel_height) { + Applet.IconApplet.prototype._init.call(this, orientation, panel_height); try { this.set_applet_icon_symbolic_name("preferences-desktop-accessibility"); @@ -204,7 +204,7 @@ MyApplet.prototype = { }; -function main(metadata, orientation) { - let myApplet = new MyApplet(orientation); +function main(metadata, orientation, panel_height) { + let myApplet = new MyApplet(orientation, panel_height); return myApplet; } diff --git a/files/usr/share/cinnamon/applets/bluetooth@cinnamon.org/applet.js b/files/usr/share/cinnamon/applets/bluetooth@cinnamon.org/applet.js index a40c65a..22543c7 100644 --- a/files/usr/share/cinnamon/applets/bluetooth@cinnamon.org/applet.js +++ b/files/usr/share/cinnamon/applets/bluetooth@cinnamon.org/applet.js @@ -1,7 +1,17 @@ const Applet = imports.ui.applet; const Clutter = imports.gi.Clutter; const GLib = imports.gi.GLib; -const GnomeBluetoothApplet = imports.gi.GnomeBluetoothApplet; +var Bluetooth; +try { + Bluetooth = imports.gi.GnomeBluetooth; + new Bluetooth.Applet(); // try the 3.6 ABI + global.logError("Bluetooth Gnome 3.6 ABI"); +} +catch(e) { + // Fallback to the 3.4 ABI + Bluetooth = imports.gi.GnomeBluetoothApplet; + global.logError("Bluetooth Gnome 3.4 ABI"); +} const Lang = imports.lang; const St = imports.gi.St; @@ -187,35 +197,36 @@ PinNotification.prototype = { } } -function MyApplet(orientation) { - this._init(orientation); +function MyApplet(orientation, panel_height) { + this._init(orientation, panel_height); } MyApplet.prototype = { __proto__: Applet.TextIconApplet.prototype, - _init: function(orientation) { - Applet.TextIconApplet.prototype._init.call(this, orientation); + _init: function(orientation, panel_height) { + Applet.TextIconApplet.prototype._init.call(this, orientation, panel_height); try { this.menuManager = new PopupMenu.PopupMenuManager(this); this.menu = new Applet.AppletPopupMenu(this, orientation); this.menuManager.addMenu(this.menu); - this.set_applet_icon_symbolic_name('bluetooth-disabled'); + this.set_applet_icon_symbolic_name('bluetooth-disabled'); + this.set_applet_tooltip(_("Bluetooth")); GLib.spawn_command_line_sync ('pkill -f "^bluetooth-applet$"'); - this._applet = new GnomeBluetoothApplet.Applet(); + this._applet = new Bluetooth.Applet(); this._killswitch = new PopupMenu.PopupSwitchMenuItem(_("Bluetooth"), false); this._applet.connect('notify::killswitch-state', Lang.bind(this, this._updateKillswitch)); this._killswitch.connect('toggled', Lang.bind(this, function() { let current_state = this._applet.killswitch_state; - if (current_state != GnomeBluetoothApplet.KillswitchState.HARD_BLOCKED && - current_state != GnomeBluetoothApplet.KillswitchState.NO_ADAPTER) { + if (current_state != Bluetooth.KillswitchState.HARD_BLOCKED && + current_state != Bluetooth.KillswitchState.NO_ADAPTER) { this._applet.killswitch_state = this._killswitch.state ? - GnomeBluetoothApplet.KillswitchState.UNBLOCKED: - GnomeBluetoothApplet.KillswitchState.SOFT_BLOCKED; + Bluetooth.KillswitchState.UNBLOCKED: + Bluetooth.KillswitchState.SOFT_BLOCKED; } else this._killswitch.setToggleState(false); })); @@ -279,10 +290,10 @@ MyApplet.prototype = { _updateKillswitch: function() { let current_state = this._applet.killswitch_state; - let on = current_state == GnomeBluetoothApplet.KillswitchState.UNBLOCKED; - let has_adapter = current_state != GnomeBluetoothApplet.KillswitchState.NO_ADAPTER; - let can_toggle = current_state != GnomeBluetoothApplet.KillswitchState.NO_ADAPTER && - current_state != GnomeBluetoothApplet.KillswitchState.HARD_BLOCKED; + let on = current_state == Bluetooth.KillswitchState.UNBLOCKED; + let has_adapter = current_state != Bluetooth.KillswitchState.NO_ADAPTER; + let can_toggle = current_state != Bluetooth.KillswitchState.NO_ADAPTER && + current_state != Bluetooth.KillswitchState.HARD_BLOCKED; this._killswitch.setToggleState(on); if (can_toggle) @@ -341,7 +352,7 @@ MyApplet.prototype = { }, _updateDeviceItem: function(item, device) { - if (!device.can_connect && device.capabilities == GnomeBluetoothApplet.Capabilities.NONE) { + if (!device.can_connect && device.capabilities == Bluetooth.Capabilities.NONE) { item.destroy(); return; } @@ -370,7 +381,7 @@ MyApplet.prototype = { }, _createDeviceItem: function(device) { - if (!device.can_connect && device.capabilities == GnomeBluetoothApplet.Capabilities.NONE) + if (!device.can_connect && device.capabilities == Bluetooth.Capabilities.NONE) return null; let item = new PopupMenu.PopupSubMenuMenuItem(device.alias); @@ -425,12 +436,12 @@ MyApplet.prototype = { item.menu.addMenuItem(item._connectedMenuitem); } - if (device.capabilities & GnomeBluetoothApplet.Capabilities.OBEX_PUSH) { + if (device.capabilities & Bluetooth.Capabilities.OBEX_PUSH) { item.menu.addAction(_("Send Files..."), Lang.bind(this, function() { this._applet.send_to_address(device.bdaddr, device.alias); })); } - if (device.capabilities & GnomeBluetoothApplet.Capabilities.OBEX_FILE_TRANSFER) { + if (device.capabilities & Bluetooth.Capabilities.OBEX_FILE_TRANSFER) { item.menu.addAction(_("Browse Files..."), Lang.bind(this, function(event) { this._applet.browse_address(device.bdaddr, event.get_time(), Lang.bind(this, function(applet, result) { @@ -448,15 +459,15 @@ MyApplet.prototype = { } switch (device.type) { - case GnomeBluetoothApplet.Type.KEYBOARD: + case Bluetooth.Type.KEYBOARD: item.menu.addSettingsAction(_("Keyboard Settings"), 'gnome-keyboard-panel.desktop'); break; - case GnomeBluetoothApplet.Type.MOUSE: + case Bluetooth.Type.MOUSE: item.menu.addSettingsAction(_("Mouse Settings"), 'gnome-mouse-panel.desktop'); break; - case GnomeBluetoothApplet.Type.HEADSET: - case GnomeBluetoothApplet.Type.HEADPHONES: - case GnomeBluetoothApplet.Type.OTHER_AUDIO: + case Bluetooth.Type.HEADSET: + case Bluetooth.Type.HEADPHONES: + case Bluetooth.Type.OTHER_AUDIO: item.menu.addSettingsAction(_("Sound Settings"), 'gnome-sound-panel.desktop'); break; default: @@ -517,7 +528,7 @@ MyApplet.prototype = { } }; -function main(metadata, orientation) { - let myApplet = new MyApplet(orientation); +function main(metadata, orientation, panel_height) { + let myApplet = new MyApplet(orientation, panel_height); return myApplet; } diff --git a/files/usr/share/cinnamon/applets/brightness@cinnamon.org/applet.js b/files/usr/share/cinnamon/applets/brightness@cinnamon.org/applet.js index 1f1a4ce..75139fe 100644 --- a/files/usr/share/cinnamon/applets/brightness@cinnamon.org/applet.js +++ b/files/usr/share/cinnamon/applets/brightness@cinnamon.org/applet.js @@ -89,15 +89,15 @@ TextImageMenuItem.prototype = { } /* end of TextImageMenuItem */ -function MyApplet(orientation) { - this._init(orientation); +function MyApplet(orientation, panel_height) { + this._init(orientation, panel_height); } MyApplet.prototype = { __proto__: Applet.IconApplet.prototype, - _init: function(orientation) { - Applet.IconApplet.prototype._init.call(this, orientation); + _init: function(orientation, panel_height) { + Applet.IconApplet.prototype._init.call(this, orientation, panel_height); try { this._proxy = new PowerManagerProxy(DBus.session, PowerBusName, PowerObjectPath); @@ -231,8 +231,8 @@ MyApplet.prototype = { } }; -function main(metadata, orientation) { - let myApplet = new MyApplet(orientation); +function main(metadata, orientation, panel_height) { + let myApplet = new MyApplet(orientation, panel_height); return myApplet; } diff --git a/files/usr/share/cinnamon/applets/calendar@cinnamon.org/applet.js b/files/usr/share/cinnamon/applets/calendar@cinnamon.org/applet.js index 2b5dc82..fd54e45 100644 --- a/files/usr/share/cinnamon/applets/calendar@cinnamon.org/applet.js +++ b/files/usr/share/cinnamon/applets/calendar@cinnamon.org/applet.js @@ -25,15 +25,15 @@ function _onVertSepRepaint (area) cr.stroke(); }; -function MyApplet(orientation) { - this._init(orientation); +function MyApplet(orientation, panel_height) { + this._init(orientation, panel_height); } MyApplet.prototype = { __proto__: Applet.TextApplet.prototype, - _init: function(orientation) { - Applet.TextApplet.prototype._init.call(this, orientation); + _init: function(orientation, panel_height) { + Applet.TextApplet.prototype._init.call(this, orientation, panel_height); try { this.menuManager = new PopupMenu.PopupMenuManager(this); @@ -106,8 +106,10 @@ MyApplet.prototype = { let dateFormat = this._calendarSettings.get_string('date-format'); let dateFormatFull = this._calendarSettings.get_string('date-format-full'); let displayDate = new Date(); + let dateFormattedFull = displayDate.toLocaleFormat(dateFormatFull); this.set_applet_label(displayDate.toLocaleFormat(dateFormat)); - this._date.set_text(displayDate.toLocaleFormat(dateFormatFull)); + this._date.set_text(dateFormattedFull); + this.set_applet_tooltip(dateFormattedFull); Mainloop.timeout_add_seconds(1, Lang.bind(this, this._updateClockAndDate)); return false; @@ -156,7 +158,7 @@ MyApplet.prototype = { }; -function main(metadata, orientation) { - let myApplet = new MyApplet(orientation); +function main(metadata, orientation, panel_height) { + let myApplet = new MyApplet(orientation, panel_height); return myApplet; } diff --git a/files/usr/share/cinnamon/applets/expo@cinnamon.org/applet.js b/files/usr/share/cinnamon/applets/expo@cinnamon.org/applet.js index 2aeb587..e367b6d 100644 --- a/files/usr/share/cinnamon/applets/expo@cinnamon.org/applet.js +++ b/files/usr/share/cinnamon/applets/expo@cinnamon.org/applet.js @@ -3,15 +3,15 @@ const Lang = imports.lang; const Main = imports.ui.main; const Gtk = imports.gi.Gtk; -function MyApplet(metadata, orientation) { - this._init(metadata, orientation); +function MyApplet(metadata, orientation, panel_height) { + this._init(metadata, orientation, panel_height); } MyApplet.prototype = { __proto__: Applet.IconApplet.prototype, - _init: function(metadata, orientation) { - Applet.IconApplet.prototype._init.call(this, orientation); + _init: function(metadata, orientation, panel_height) { + Applet.IconApplet.prototype._init.call(this, orientation, panel_height); try { Gtk.IconTheme.get_default().append_search_path(metadata.path); @@ -56,7 +56,7 @@ MyApplet.prototype = { } }; -function main(metadata, orientation) { - let myApplet = new MyApplet(metadata, orientation); +function main(metadata, orientation, panel_height) { + let myApplet = new MyApplet(metadata, orientation, panel_height); return myApplet; } diff --git a/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/applet.js b/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/applet.js index ef82501..783899c 100644 --- a/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/applet.js +++ b/files/usr/share/cinnamon/applets/keyboard@cinnamon.org/applet.js @@ -32,15 +32,15 @@ LayoutMenuItem.prototype = { } }; -function MyApplet(orientation) { - this._init(orientation); +function MyApplet(orientation, panel_height) { + this._init(orientation, panel_height); } MyApplet.prototype = { __proto__: Applet.TextIconApplet.prototype, - _init: function(orientation) { - Applet.TextIconApplet.prototype._init.call(this, orientation); + _init: function(orientation, panel_height) { + Applet.TextIconApplet.prototype._init.call(this, orientation, panel_height); try { this.menuManager = new PopupMenu.PopupMenuManager(this); @@ -172,7 +172,7 @@ MyApplet.prototype = { } }; -function main(metadata, orientation) { - let myApplet = new MyApplet(orientation); +function main(metadata, orientation, panel_height) { + let myApplet = new MyApplet(orientation, panel_height); return myApplet; } diff --git a/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js b/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js index fddca03..076bbf4 100644 --- a/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js +++ b/files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js @@ -24,9 +24,11 @@ const ICON_SIZE = 16; const MAX_FAV_ICON_SIZE = 32; const CATEGORY_ICON_SIZE = 22; const APPLICATION_ICON_SIZE = 22; +const MAX_RECENT_FILES = 20; const USER_DESKTOP_PATH = FileUtils.getUserDesktopDir(); + let appsys = Cinnamon.AppSystem.get_default(); /* VisibleChildIterator takes a container (boxlayout, etc.) @@ -230,7 +232,7 @@ ApplicationButton.prototype = { _init: function(appsMenuButton, app) { GenericApplicationButton.prototype._init.call(this, appsMenuButton, app, true); - + this.category = new Array(); this.actor.set_style_class_name('menu-application-button'); this.icon = this.app.create_icon_texture(APPLICATION_ICON_SIZE); this.addActor(this.icon); @@ -261,86 +263,112 @@ ApplicationButton.prototype = { return this.actor; } }; -Signals.addSignalMethods(ApplicationButton.prototype); function PlaceButton(appsMenuButton, place, button_name) { this._init(appsMenuButton, place, button_name); } PlaceButton.prototype = { - _init: function(appsMenuButton,place, button_name) { + __proto__: PopupMenu.PopupBaseMenuItem.prototype, + + _init: function(appsMenuButton, place, button_name) { + PopupMenu.PopupBaseMenuItem.prototype._init.call(this, {hover: false}); + this.appsMenuButton = appsMenuButton; this.place = place; this.button_name = button_name; - this.actor = new St.Button({ reactive: true, style_class: 'menu-application-button', x_align: St.Align.START }); + this.actor.set_style_class_name('menu-application-button'); this.actor._delegate = this; - this.buttonbox = new St.BoxLayout(); - this.label = new St.Label({ text: this.button_name, style_class: 'menu-place-cat-button-label' }); + this.label = new St.Label({ text: this.button_name, style_class: 'menu-application-button-label' }); this.icon = place.iconFactory(APPLICATION_ICON_SIZE); - this.buttonbox.add_actor(this.icon); - this.buttonbox.add_actor(this.label); - this.actor.set_child(this.buttonbox); - this.actor.connect('clicked', Lang.bind(this, function() { + this.addActor(this.icon); + this.addActor(this.label); + }, + + _onButtonReleaseEvent: function (actor, event) { + if (event.get_button()==1){ this.place.launch(); - appsMenuButton.menu.close(); - })); + this.appsMenuButton.menu.close(); + } + }, + + activate: function(event) { + this.place.launch(); + this.appsMenuButton.menu.close(); } }; -Signals.addSignalMethods(PlaceButton.prototype); function RecentButton(appsMenuButton, file) { this._init(appsMenuButton, file); } RecentButton.prototype = { + __proto__: PopupMenu.PopupBaseMenuItem.prototype, + _init: function(appsMenuButton, file) { + PopupMenu.PopupBaseMenuItem.prototype._init.call(this, {hover: false}); this.file = file; + this.appsMenuButton = appsMenuButton; this.button_name = this.file.name; - this.actor = new St.Button({ reactive: true, style_class: 'menu-application-button', x_align: St.Align.START }); + this.actor.set_style_class_name('menu-application-button'); this.actor._delegate = this; - this.buttonbox = new St.BoxLayout(); - this.label = new St.Label({ text: this.button_name, style_class: 'menu-place-cat-button-label' }); + this.label = new St.Label({ text: this.button_name, style_class: 'menu-application-button-label' }); this.icon = file.createIcon(APPLICATION_ICON_SIZE); - this.buttonbox.add_actor(this.icon); - this.buttonbox.add_actor(this.label); - this.actor.set_child(this.buttonbox); - this.actor.connect('clicked', Lang.bind(this, function() { + this.addActor(this.icon); + this.addActor(this.label); + }, + + _onButtonReleaseEvent: function (actor, event) { + if (event.get_button()==1){ Gio.app_info_launch_default_for_uri(this.file.uri, global.create_app_launch_context()); - appsMenuButton.menu.close(); - })); + this.appsMenuButton.menu.close(); + } + }, + + activate: function(event) { + Gio.app_info_launch_default_for_uri(this.file.uri, global.create_app_launch_context()); + this.appsMenuButton.menu.close(); } }; -Signals.addSignalMethods(RecentButton.prototype); function RecentClearButton(appsMenuButton) { this._init(appsMenuButton); } RecentClearButton.prototype = { + __proto__: PopupMenu.PopupBaseMenuItem.prototype, + _init: function(appsMenuButton) { + PopupMenu.PopupBaseMenuItem.prototype._init.call(this, {hover: false}); + this.appsMenuButton = appsMenuButton; + this.actor.set_style_class_name('menu-application-button'); this.button_name = _("Clear list"); - this.actor = new St.Button({ reactive: true, style_class: 'menu-application-button', x_align: St.Align.START }); this.actor._delegate = this; - this.buttonbox = new St.BoxLayout(); - this.label = new St.Label({ text: this.button_name, style_class: 'menu-place-cat-button-label' }); - let icon = new St.Icon({ icon_name: 'edit-clear', icon_type: St.IconType.SYMBOLIC, icon_size: APPLICATION_ICON_SIZE }); - this.buttonbox.add_actor(icon); - this.buttonbox.add_actor(this.label); - this.actor.set_child(this.buttonbox); - this.actor.connect('clicked', Lang.bind(this, function() { - appsMenuButton.menu.close(); + this.label = new St.Label({ text: this.button_name, style_class: 'menu-application-button-label' }); + this.icon = new St.Icon({ icon_name: 'edit-clear', icon_type: St.IconType.SYMBOLIC, icon_size: APPLICATION_ICON_SIZE }); + this.addActor(this.icon); + this.addActor(this.label); + }, + + _onButtonReleaseEvent: function (actor, event) { + if (event.get_button()==1){ + this.appsMenuButton.menu.close(); let GtkRecent = new Gtk.RecentManager(); GtkRecent.purge_items(); - })); + } } }; -Signals.addSignalMethods(RecentClearButton.prototype); function CategoryButton(app) { this._init(app); } CategoryButton.prototype = { + __proto__: PopupMenu.PopupBaseMenuItem.prototype, + _init: function(category) { + PopupMenu.PopupBaseMenuItem.prototype._init.call(this, {hover: false}); + + this.actor.set_style_class_name('menu-category-button'); var label; if (category) { let icon = category.get_icon(); @@ -351,58 +379,52 @@ CategoryButton.prototype = { label = category.get_name(); } else label = _("All Applications"); - this.actor = new St.Button({ reactive: true, style_class: 'menu-category-button', x_align: St.Align.START }); + this.actor._delegate = this; - this.buttonbox = new St.BoxLayout(); - this.label = new St.Label({ text: label, style_class: 'menu-place-cat-button-label' }); + this.label = new St.Label({ text: label, style_class: 'menu-category-button-label' }); if (category && this.icon_name) { this.icon = new St.Icon({icon_name: this.icon_name, icon_size: CATEGORY_ICON_SIZE, icon_type: St.IconType.FULLCOLOR}); - this.buttonbox.add_actor(this.icon); + this.addActor(this.icon); } - this.buttonbox.add_actor(this.label); - this.actor.set_child(this.buttonbox); - //this.actor.set_tooltip_text(category.get_name()); + this.addActor(this.label); } }; -Signals.addSignalMethods(CategoryButton.prototype); function PlaceCategoryButton(app) { this._init(app); } PlaceCategoryButton.prototype = { + __proto__: PopupMenu.PopupBaseMenuItem.prototype, + _init: function(category) { - this.actor = new St.Button({ reactive: true, style_class: 'menu-category-button', x_align: St.Align.START }); + PopupMenu.PopupBaseMenuItem.prototype._init.call(this, {hover: false}); + this.actor.set_style_class_name('menu-category-button'); this.actor._delegate = this; - this.buttonbox = new St.BoxLayout(); - this.label = new St.Label({ text: _("Places"), style_class: 'menu-place-cat-button-label' }); + this.label = new St.Label({ text: _("Places"), style_class: 'menu-category-button-label' }); this.icon = new St.Icon({icon_name: "folder", icon_size: CATEGORY_ICON_SIZE, icon_type: St.IconType.FULLCOLOR}); - this.buttonbox.add_actor(this.icon); - this.buttonbox.add_actor(this.label); - this.actor.set_child(this.buttonbox); + this.addActor(this.icon); + this.addActor(this.label); } }; -Signals.addSignalMethods(PlaceCategoryButton.prototype); function RecentCategoryButton(app) { this._init(app); } RecentCategoryButton.prototype = { + __proto__: PopupMenu.PopupBaseMenuItem.prototype, + _init: function(category) { - this.actor = new St.Button({ reactive: true, style_class: 'menu-category-button', x_align: St.Align.START }); + PopupMenu.PopupBaseMenuItem.prototype._init.call(this, {hover: false}); + this.actor.set_style_class_name('menu-category-button'); this.actor._delegate = this; - this.buttonbox = new St.BoxLayout(); - this.label = new St.Label({ text: _("Recent Files"), style_class: 'menu-place-cat-button-label' }); + this.label = new St.Label({ text: _("Recent Files"), style_class: 'menu-category-button-label' }); this.icon = new St.Icon({icon_name: "folder-recent", icon_size: CATEGORY_ICON_SIZE, icon_type: St.IconType.FULLCOLOR}); - this.buttonbox.add_actor(this.icon); - this.buttonbox.add_actor(this.label); - this.actor.set_child(this.buttonbox); + this.addActor(this.icon); + this.addActor(this.label); } }; -Signals.addSignalMethods(RecentCategoryButton.prototype); - - function FavoritesButton(appsMenuButton, app, nbFavorites) { this._init(appsMenuButton, app, nbFavorites); @@ -625,15 +647,15 @@ FavoritesBox.prototype = { } } -function MyApplet(orientation) { - this._init(orientation); +function MyApplet(orientation, panel_height) { + this._init(orientation, panel_height); } MyApplet.prototype = { __proto__: Applet.TextIconApplet.prototype, - _init: function(orientation) { - Applet.TextIconApplet.prototype._init.call(this, orientation); + _init: function(orientation, panel_height) { + Applet.TextIconApplet.prototype._init.call(this, orientation, panel_height); try { this.set_applet_tooltip(_("Menu")); @@ -701,6 +723,7 @@ MyApplet.prototype = { this._activeContainer = null; this._activeActor = null; this._applicationsBoxWidth = 0; + this.menuIsOpening = false; this.RecentManager = new DocInfo.DocManager(); @@ -708,9 +731,6 @@ MyApplet.prototype = { appsys.connect('installed-changed', Lang.bind(this, this._refreshApps)); AppFavorites.getAppFavorites().connect('changed', Lang.bind(this, this._refreshFavs)); - this.menu.connect('open-state-changed', Lang.bind(this, this._onOpenStateToggled)); - - this.hover_delay = global.settings.get_int("menu-hover-delay") / 1000; global.settings.connect("changed::menu-hover-delay", Lang.bind(this, function() { this.hover_delay = global.settings.get_int("menu-hover-delay") / 1000; @@ -750,7 +770,6 @@ MyApplet.prototype = { this.menu.actor.add_style_class_name('menu-background'); this.menu.connect('open-state-changed', Lang.bind(this, this._onOpenStateChanged)); - this.menu.connect('open-state-changed', Lang.bind(this, this._onOpenStateToggled)); this._display(); }, @@ -782,7 +801,18 @@ MyApplet.prototype = { _onOpenStateChanged: function(menu, open) { if (open) { + this.menuIsOpening = true; this.actor.add_style_pseudo_class('active'); + global.stage.set_key_focus(this.searchEntry); + this._selectedItemIndex = null; + this._activeContainer = null; + this._activeActor = null; + let monitorHeight = Main.layoutManager.primaryMonitor.height; + let applicationsBoxHeight = this.applicationsBox.get_allocation_box().y2-this.applicationsBox.get_allocation_box().y1; + let scrollBoxHeight = (this.leftBox.get_allocation_box().y2-this.leftBox.get_allocation_box().y1) - + (this.searchBox.get_allocation_box().y2-this.searchBox.get_allocation_box().y1); + this.applicationsScrollBox.style = "height: "+scrollBoxHeight+"px;"; + this._select_category(null, this._allAppsCategoryButton); } else { this.actor.remove_style_pseudo_class('active'); if (this.searchActive) { @@ -793,6 +823,7 @@ MyApplet.prototype = { this._previousTreeItemIndex = null; this._previousTreeSelectedActor = null; this._previousSelectedActor = null; + this.closeApplicationsContextMenus(null, false); this._clearAllSelections(); } }, @@ -842,6 +873,7 @@ MyApplet.prototype = { index = this.appBoxIter.getAbsoluteIndexOfChild(item_actor); } else { this._previousSelectedActor = this.categoriesBox.get_child_at_index(index); + this._previousSelectedActor._delegate.isHovered = false; item_actor = this.catBoxIter.getPrevVisible(this._activeActor) index = this.catBoxIter.getAbsoluteIndexOfChild(item_actor); } @@ -852,6 +884,7 @@ MyApplet.prototype = { index = this.appBoxIter.getAbsoluteIndexOfChild(item_actor); } else { this._previousSelectedActor = this.categoriesBox.get_child_at_index(index); + this._previousSelectedActor._delegate.isHovered = false; item_actor = this.catBoxIter.getNextVisible(this._activeActor) index = this.catBoxIter.getAbsoluteIndexOfChild(item_actor); } @@ -874,9 +907,7 @@ MyApplet.prototype = { if (!item_actor || item_actor === this.searchEntry) { return false; } - // if (item_actor !== this._previousSelectedActor) { - item_actor._delegate.emit('enter-event'); - //} + item_actor._delegate.emit('enter-event'); return true; }, @@ -924,31 +955,6 @@ MyApplet.prototype = { this._previousSelectedActor.style_class = "menu-category-button"; } }, - - _onOpenStateToggled: function(menu, open) { - if (open) { - global.stage.set_key_focus(this.searchEntry); - this._selectedItemIndex = null; - this._activeContainer = null; - this._activeActor = null; - let monitorHeight = Main.layoutManager.primaryMonitor.height; - let applicationsBoxHeight = this.applicationsBox.get_allocation_box().y2-this.applicationsBox.get_allocation_box().y1; - let scrollBoxHeight = (this.leftBox.get_allocation_box().y2-this.leftBox.get_allocation_box().y1) - -(this.searchBox.get_allocation_box().y2-this.searchBox.get_allocation_box().y1); - - - - // if (scrollBoxHeight < (0.2*monitorHeight) && (scrollBoxHeight < applicationsBoxHeight)) { - // scrollBoxHeight = Math.min(0.2*monitorHeight, applicationsBoxHeight); - // } - this.applicationsScrollBox.style = "height: "+scrollBoxHeight+"px;"; - } else { - this.closeApplicationsContextMenus(null, false); - //this.resetSearch(); - //this._clearSelections(this.categoriesBox); - //this._clearSelections(this.applicationsBox); - } - }, _refreshApps : function() { this.applicationsBox.destroy_all_children(); @@ -960,22 +966,27 @@ MyApplet.prototype = { this.categoriesBox.destroy_all_children(); this._allAppsCategoryButton = new CategoryButton(null); - this._allAppsCategoryButton.actor.connect('clicked', Lang.bind(this, function() { - this._select_category(null, this._allAppsCategoryButton); - })); - this._addEnterEvent(this._allAppsCategoryButton, Lang.bind(this, function() { - if (!this.searchActive) { - this._allAppsCategoryButton.isHovered = true; - Tweener.addTween(this, { - time: this.hover_delay, - onComplete: function () { - if (this._allAppsCategoryButton.isHovered) { - this._allAppsCategoryButton.actor.style_class = "menu-category-button-selected"; - this._clearPrevCatSelection(this._allAppsCategoryButton.actor); - this._select_category(null, this._allAppsCategoryButton); - } - } - }); + this._addEnterEvent(this._allAppsCategoryButton, Lang.bind(this, function() { + if (!this.searchActive) { + this._allAppsCategoryButton.isHovered = true; + if (this.hover_delay > 0) { + Tweener.addTween(this, { + time: this.hover_delay, + onComplete: function () { + if (this._allAppsCategoryButton.isHovered) { + this._allAppsCategoryButton.actor.style_class = "menu-category-button-selected"; + this._clearPrevCatSelection(this._allAppsCategoryButton.actor); + this._select_category(null, this._allAppsCategoryButton); + } else { + this._allAppsCategoryButton.actor.style_class = "menu-category-button"; + } + } + }); + } else { + this._allAppsCategoryButton.actor.style_class = "menu-category-button-selected"; + this._clearPrevCatSelection(this._allAppsCategoryButton.actor); + this._select_category(null, this._allAppsCategoryButton); + } } })); this._allAppsCategoryButton.actor.connect('leave-event', Lang.bind(this, function () { @@ -1004,22 +1015,27 @@ MyApplet.prototype = { this._loadCategory(dir); if (this.applicationsByCategory[dir.get_menu_id()].length>0){ let categoryButton = new CategoryButton(dir); - categoryButton.actor.connect('clicked', Lang.bind(this, function() { - this._select_category(dir, categoryButton); - })); this._addEnterEvent(categoryButton, Lang.bind(this, function() { if (!this.searchActive) { categoryButton.isHovered = true; - Tweener.addTween(this, { - time: this.hover_delay, - onComplete: function () { - if (categoryButton.isHovered) { - categoryButton.actor.style_class = "menu-category-button-selected"; - this._clearPrevCatSelection(categoryButton.actor); - this._select_category(dir, categoryButton); + if (this.hover_delay > 0) { + Tweener.addTween(this, { + time: this.hover_delay, + onComplete: function () { + if (categoryButton.isHovered) { + categoryButton.actor.style_class = "menu-category-button-selected"; + this._clearPrevCatSelection(categoryButton.actor); + this._select_category(dir, categoryButton); + } else { + categoryButton.actor.style_class = "menu-category-button"; + } } - } - }); + }); + } else { + categoryButton.actor.style_class = "menu-category-button-selected"; + this._clearPrevCatSelection(categoryButton.actor); + this._select_category(dir, categoryButton); + } } })); categoryButton.actor.connect('leave-event', Lang.bind(this, function () { @@ -1049,9 +1065,6 @@ MyApplet.prototype = { // Now generate Places category and places buttons and add to the list if (this.showPlaces) { this.placesButton = new PlaceCategoryButton(); - this.placesButton.actor.connect('clicked', Lang.bind(this, function() { - this._displayButtons(null, -1); - })); this._addEnterEvent(this.placesButton, Lang.bind(this, function() { if (!this.searchActive) { this.placesButton.isHovered = true; @@ -1086,10 +1099,12 @@ MyApplet.prototype = { this._clearPrevAppSelection(button.actor); button.actor.style_class = "menu-application-button-selected"; this._scrollToButton(button); + this.selectedAppDescription.set_text(button.place.id.slice(16)); })); button.actor.connect('leave-event', Lang.bind(this, function() { button.actor.style_class = "menu-application-button"; this._previousSelectedActor = button.actor; + this.selectedAppDescription.set_text(""); })); this._placesButtons.push(button); this.applicationsBox.add_actor(button.actor); @@ -1098,9 +1113,6 @@ MyApplet.prototype = { // Now generate recent category and recent files buttons and add to the list if (this.showRecent) { this.recentButton = new RecentCategoryButton(); - this.recentButton.actor.connect('clicked', Lang.bind(this, function() { - this._displayButtons(null, null, -1); - })); this._addEnterEvent(this.recentButton, Lang.bind(this, function() { if (!this.searchActive) { this.recentButton.isHovered = true; @@ -1125,7 +1137,7 @@ MyApplet.prototype = { })); this.categoriesBox.add_actor(this.recentButton.actor); - for (let id = 0; id < 15 && id < this.RecentManager._infosByTimestamp.length; id++) { + for (let id = 0; id < MAX_RECENT_FILES && id < this.RecentManager._infosByTimestamp.length; id++) { let button = new RecentButton(this, this.RecentManager._infosByTimestamp[id]); this._addEnterEvent(button, Lang.bind(this, function() { this._clearPrevAppSelection(button.actor); @@ -1269,34 +1281,45 @@ MyApplet.prototype = { if (!dupe) { let applicationButton = new ApplicationButton(this, app); applicationButton.actor.connect('realize', Lang.bind(this, this._onApplicationButtonRealized)); - applicationButton.actor.connect('leave-event', Lang.bind(this, function() { - this._previousSelectedActor = applicationButton.actor; - applicationButton.actor.style_class = "menu-application-button"; - this.selectedAppTitle.set_text(""); - this.selectedAppDescription.set_text(""); - })); - this._addEnterEvent(applicationButton, Lang.bind(this, function() { - this.selectedAppTitle.set_text(applicationButton.app.get_name()); - if (applicationButton.app.get_description()) - this.selectedAppDescription.set_text(applicationButton.app.get_description()); - else - this.selectedAppDescription.set_text(""); - this._clearPrevAppSelection(applicationButton.actor); - applicationButton.actor.style_class = "menu-application-button-selected"; - this._scrollToButton(applicationButton); - })); + applicationButton.actor.connect('leave-event', Lang.bind(this, this._appLeaveEvent, applicationButton)); + this._addEnterEvent(applicationButton, Lang.bind(this, this._appEnterEvent, applicationButton)); this._applicationsButtons.push(applicationButton); + applicationButton.category.push(dir.get_menu_id()); this.applicationsByCategory[dir.get_menu_id()].push(app.get_name()); } else { - this.applicationsByCategory[dir.get_menu_id()].push(app.get_name()); + for (let i = 0; i < this._applicationsButtons.length; i++) { + if (this._applicationsButtons[i].app == app) { + this._applicationsButtons[i].category.push(dir.get_menu_id()); + } + } } } } else if (nextType == GMenu.TreeItemType.DIRECTORY) { - this._loadCategory(iter.get_directory(), top_dir); + subdir = iter.get_directory(); + this.applicationsByCategory[subdir.get_menu_id()] = new Array(); + this._loadCategory(subdir, top_dir); } } }, + _appLeaveEvent: function(a, b, applicationButton) { + this._previousSelectedActor = applicationButton.actor; + applicationButton.actor.style_class = "menu-application-button"; + this.selectedAppTitle.set_text(""); + this.selectedAppDescription.set_text(""); + }, + + _appEnterEvent: function(applicationButton) { + this.selectedAppTitle.set_text(applicationButton.app.get_name()); + if (applicationButton.app.get_description()) + this.selectedAppDescription.set_text(applicationButton.app.get_description()); + else + this.selectedAppDescription.set_text(""); + this._clearPrevAppSelection(applicationButton.actor); + applicationButton.actor.style_class = "menu-application-button-selected"; + this._scrollToButton(applicationButton); + }, + find_dupe: function(app) { let ret = false; for (let i = 0; i < this._applicationsButtons.length; i++) { @@ -1438,63 +1461,97 @@ MyApplet.prototype = { } }, - _displayButtons: function(apps, places, recent){ - if (apps) { - if (apps[0] == "all") { - for (let i = 0; i < this._applicationsButtons.length; i++) { - this._applicationsButtons[i].actor.show(); - } + _displayButtons: function(appCategory, places, recent, apps){ + if (appCategory) { + if (appCategory == "all") { + this._applicationsButtons.forEach( function (item, index) { + if (!item.actor.visible) { + item.actor.show(); + } + }); } else { - for (let i = 0; i < this._applicationsButtons.length; i++) { + this._applicationsButtons.forEach( function (item, index) { + if (item.category.indexOf(appCategory) != -1) { + if (!item.actor.visible) { + item.actor.show(); + } + } else { + if (item.actor.visible) { + item.actor.hide(); + } + } + }); + } + } else if (apps) { + for (let i = 0; i < this._applicationsButtons.length; i++) { if (apps.indexOf(this._applicationsButtons[i].name) != -1) { - this._applicationsButtons[i].actor.show(); + if (!this._applicationsButtons[i].actor.visible) { + this._applicationsButtons[i].actor.show(); + } } else { - this._applicationsButtons[i].actor.hide(); + if (this._applicationsButtons[i].actor.visible) { + this._applicationsButtons[i].actor.hide(); + } } - } } } else { - for (let i = 0; i < this._applicationsButtons.length; i++) { - this._applicationsButtons[i].actor.hide(); - } + this._applicationsButtons.forEach( function (item, index) { + if (item.actor.visible) { + item.actor.hide(); + } + }); } if (places) { if (places == -1) { - for (let i = 0; i < this._placesButtons.length; i++) { - this._placesButtons[i].actor.show(); - } + this._placesButtons.forEach( function (item, index) { + item.actor.show(); + }); } else { for (let i = 0; i < this._placesButtons.length; i++) { if (places.indexOf(this._placesButtons[i].button_name) != -1) { - this._placesButtons[i].actor.show(); + if (!this._placesButtons[i].actor.visible) { + this._placesButtons[i].actor.show(); + } } else { - this._placesButtons[i].actor.hide(); + if (this._placesButtons[i].actor.visible) { + this._placesButtons[i].actor.hide(); + } } } } } else { - for (let i = 0; i < this._placesButtons.length; i++) { - this._placesButtons[i].actor.hide(); - } + this._placesButtons.forEach( function (item, index) { + if (item.actor.visible) { + item.actor.hide(); + } + }); } if (recent) { if (recent == -1) { - for (let i = 0; i < this._recentButtons.length; i++) { - this._recentButtons[i].actor.show(); - } + this._recentButtons.forEach( function (item, index) { + if (!item.actor.visible) { + item.actor.show(); + } + }); } else { for (let i = 0; i < this._recentButtons.length; i++) { if (recent.indexOf(this._recentButtons[i].button_name) != -1) { - this._recentButtons[i].actor.show(); + if (!this._recentButtons[i].actor.visible) { + this._recentButtons[i].actor.show(); + } } else { - this._recentButtons[i].actor.hide(); + if (this._recentButtons[i].actor.visible) { + this._recentButtons[i].actor.hide(); + } } } } } else { - for (let i = 0; i < this._recentButtons.length; i++) { - this._recentButtons[i].actor.hide(); - } + this._recentButtons.forEach( function (item, index) { + if (item.actor.visible) { + item.actor.hide(); + } + }); } }, @@ -1523,40 +1580,43 @@ MyApplet.prototype = { }, _onSearchTextChanged: function (se, prop) { - this.searchActive = this.searchEntry.get_text() != ''; - this._clearAllSelections(); - if (this.searchActive) { - this.searchEntry.set_secondary_icon(this._searchActiveIcon); - if (this._searchIconClickedId == 0) { - this._searchIconClickedId = this.searchEntry.connect('secondary-icon-clicked', - Lang.bind(this, function() { - this.resetSearch(); - this._select_category(null, this._allAppsCategoryButton); - })); - } - - this._setCategoriesButtonActive(false); + if (this.menuIsOpening) { + this.menuIsOpening = false; + return; } else { - if (this._searchIconClickedId > 0) - this.searchEntry.disconnect(this._searchIconClickedId); - this._searchIconClickedId = 0; - - this.searchEntry.set_secondary_icon(this._searchInactiveIcon); - this._setCategoriesButtonActive(true); - this._select_category(null, this._allAppsCategoryButton); - } - if (!this.searchActive) { - if (this._searchTimeoutId > 0) { - Mainloop.source_remove(this._searchTimeoutId); - this._searchTimeoutId = 0; + this.searchActive = this.searchEntry.get_text() != ''; + this._clearAllSelections(); + if (this.searchActive) { + this.searchEntry.set_secondary_icon(this._searchActiveIcon); + if (this._searchIconClickedId == 0) { + this._searchIconClickedId = this.searchEntry.connect('secondary-icon-clicked', + Lang.bind(this, function() { + this.resetSearch(); + this._select_category(null, this._allAppsCategoryButton); + })); + } + this._setCategoriesButtonActive(false); + } else { + if (this._searchIconClickedId > 0) + this.searchEntry.disconnect(this._searchIconClickedId); + this._searchIconClickedId = 0; + this.searchEntry.set_secondary_icon(this._searchInactiveIcon); + this._setCategoriesButtonActive(true); + this._select_category(null, this._allAppsCategoryButton); } - return; + if (!this.searchActive) { + if (this._searchTimeoutId > 0) { + Mainloop.source_remove(this._searchTimeoutId); + this._searchTimeoutId = 0; + } + return; + } + if (this._searchTimeoutId > 0) + return; + this._searchTimeoutId = Mainloop.timeout_add(150, Lang.bind(this, this._doSearch)); } - if (this._searchTimeoutId > 0) - return; - this._searchTimeoutId = Mainloop.timeout_add(150, Lang.bind(this, this._doSearch)); }, - + _listBookmarks: function(pattern){ let bookmarks = Main.placesManager.getBookmarks(); var res = new Array(); @@ -1578,17 +1638,19 @@ MyApplet.prototype = { _listApplications: function(category_menu_id, pattern){ var applist = new Array(); if (category_menu_id) { - applist = this.applicationsByCategory[category_menu_id]; + applist = category_menu_id; } else { - applist.push('all'); + applist = "all"; } let res; - if (pattern){ + if (pattern) { + pattern = pattern.toLowerCase(); res = new Array(); for (var i in this._applicationsButtons) { let app = this._applicationsButtons[i].app; - if (app.get_name().toLowerCase().indexOf(pattern)!=-1 || (app.get_description() && app.get_description().toLowerCase().indexOf(pattern)!=-1) || - (app.get_id() && app.get_id().slice(0, -8).toLowerCase().indexOf(pattern)!=-1)) res.push(app.get_name()); + if (app.get_name().toLowerCase().indexOf(pattern)!=-1 || (app.get_description() && app.get_description().toLowerCase().indexOf(pattern)!=-1)) { + res.push(app.get_name()); + } } } else res = applist; return res; @@ -1627,7 +1689,7 @@ MyApplet.prototype = { recentResults.push(this._recentButtons[i].button_name); } - this._displayButtons(appResults, placesResults, recentResults); + this._displayButtons(null, placesResults, recentResults, appResults); this.appBoxIter.reloadVisible(); if (this.appBoxIter.getNumVisibleChildren() > 0) { @@ -1642,7 +1704,7 @@ MyApplet.prototype = { } }; -function main(metadata, orientation) { - let myApplet = new MyApplet(orientation); +function main(metadata, orientation, panel_height) { + let myApplet = new MyApplet(orientation, panel_height); return myApplet; } diff --git a/files/usr/share/cinnamon/applets/network@cinnamon.org/applet.js b/files/usr/share/cinnamon/applets/network@cinnamon.org/applet.js index c782a14..fe22419 100644 --- a/files/usr/share/cinnamon/applets/network@cinnamon.org/applet.js +++ b/files/usr/share/cinnamon/applets/network@cinnamon.org/applet.js @@ -1595,15 +1595,15 @@ NMMessageTraySource.prototype = { } }; -function MyApplet(orientation) { - this._init(orientation); +function MyApplet(orientation, panel_height) { + this._init(orientation, panel_height); } MyApplet.prototype = { __proto__: Applet.IconApplet.prototype, - _init: function(orientation) { - Applet.IconApplet.prototype._init.call(this, orientation); + _init: function(orientation, panel_height) { + Applet.IconApplet.prototype._init.call(this, orientation, panel_height); try { this.menuManager = new PopupMenu.PopupMenuManager(this); @@ -2186,7 +2186,7 @@ MyApplet.prototype = { }; -function main(metadata, orientation) { - let myApplet = new MyApplet(orientation); +function main(metadata, orientation, panel_height) { + let myApplet = new MyApplet(orientation, panel_height); return myApplet; } diff --git a/files/usr/share/cinnamon/applets/notifications@cinnamon.org/applet.js b/files/usr/share/cinnamon/applets/notifications@cinnamon.org/applet.js index 9e07de0..8dcfa15 100644 --- a/files/usr/share/cinnamon/applets/notifications@cinnamon.org/applet.js +++ b/files/usr/share/cinnamon/applets/notifications@cinnamon.org/applet.js @@ -14,15 +14,15 @@ const NotificationDestroyedReason = imports.ui.messageTray.NotificationDestroyed let MT = Main.messageTray; -function MyApplet(metadata, orientation) { - this._init(metadata, orientation); +function MyApplet(metadata, orientation, panel_height) { + this._init(metadata, orientation, panel_height); } MyApplet.prototype = { __proto__: Applet.TextIconApplet.prototype, - _init: function(metadata, orientation) { - Applet.TextIconApplet.prototype._init.call(this, orientation); + _init: function(metadata, orientation, panel_height) { + Applet.TextIconApplet.prototype._init.call(this, orientation, panel_height); try { Gtk.IconTheme.get_default().append_search_path(metadata.path); @@ -52,7 +52,7 @@ MyApplet.prototype = { this.clear_action = new PopupMenu.PopupMenuItem(_("Clear notifications")); this.menu.addMenuItem(this.clear_action); this.clear_action.connect('activate', Lang.bind(this, this._clear_all)); - + this.clear_action.actor.hide(); this.scrollview = new St.ScrollView({ x_fill: true, y_fill: true, y_align: St.Align.START}); this._maincontainer.add(this.scrollview); @@ -78,11 +78,14 @@ MyApplet.prototype = { notification.actor.unparent(); let existing_index = this.notifications.indexOf(notification); if (existing_index != -1) { + notification._inNotificationBin = true; notification.actor.reparent(this._notificationbin); notification.expand(); + notification._timeLabel.show(); this.update_list(); return; } + notification._inNotificationBin = true; this.notifications.push(notification); notification.expand(); this._notificationbin.add(notification.actor) @@ -90,6 +93,7 @@ MyApplet.prototype = { notification.actor.add_style_class_name('notification-applet-padding'); notification.connect('clicked', Lang.bind(this, this._item_clicked)); notification.connect('destroy', Lang.bind(this, this._item_clicked)); + notification._timeLabel.show(); this.update_list(); }, @@ -210,8 +214,8 @@ MyApplet.prototype = { } }; -function main(metadata, orientation) { - let myApplet = new MyApplet(metadata, orientation); +function main(metadata, orientation, panel_height) { + let myApplet = new MyApplet(metadata, orientation, panel_height); return myApplet; } diff --git a/files/usr/share/cinnamon/applets/panel-launchers@cinnamon.org/add-panel-launcher.glade b/files/usr/share/cinnamon/applets/panel-launchers@cinnamon.org/add-panel-launcher.glade new file mode 100644 index 0000000..41bcf04 --- /dev/null +++ b/files/usr/share/cinnamon/applets/panel-launchers@cinnamon.org/add-panel-launcher.glade @@ -0,0 +1,228 @@ + + + + + False + 5 + Add panel launcher... + False + center + list-add + dialog + + + + False + vertical + 2 + + + False + end + + + Add + False + True + True + True + False + + + + False + True + 0 + + + + + Cancel + False + True + True + True + False + + + + False + True + 1 + + + + + False + True + end + 0 + + + + + 549 + 110 + True + False + + + 430 + 85 + True + False + + + True + False + Name + + + 0 + 0 + 1 + 1 + + + + + True + False + Application + + + 0 + 1 + 1 + 1 + + + + + True + False + Icon + + + 0 + 2 + 1 + 1 + + + + + 300 + 35 + True + True + + + + + 1 + 0 + 1 + 1 + + + + + + + + 300 + 35 + True + True + + + + + 1 + 1 + 1 + 1 + + + + + 300 + 35 + True + True + + + + + 1 + 2 + 1 + 1 + + + + + True + False + start + vertical + False + False + + + + 2 + 1 + 1 + 1 + + + + + True + False + start + vertical + False + False + + + + 2 + 2 + 1 + 1 + + + + + + + 100 + 80 + True + False + gtk-execute + 6 + + + 441 + 11 + + + + + True + True + 1 + + + + + + add_button + cancel_button + + + diff --git a/files/usr/share/cinnamon/applets/panel-launchers@cinnamon.org/add-panel-launcher.py b/files/usr/share/cinnamon/applets/panel-launchers@cinnamon.org/add-panel-launcher.py new file mode 100755 index 0000000..34cc464 --- /dev/null +++ b/files/usr/share/cinnamon/applets/panel-launchers@cinnamon.org/add-panel-launcher.py @@ -0,0 +1,154 @@ +#! /usr/bin/python -OOt + +from gi.repository import Gtk +from gi.repository import Gdk +from gi.repository import Gio +from os.path import expanduser +from time import sleep as wait +import os +import os.path +import inspect +import sys +import gettext + +gettext.install("cinnamon", "/usr/share/cinnamon/locale") +Settings = Gio.Settings.new("org.cinnamon") + +_ = gettext.gettext + +class Namespace: pass + +iface = Namespace() + +oldDesktopName = "" +newDesktopName = "" +appName = "" +appPath = "" +iconPath = "" + +editMode = len(sys.argv) > 1 +if editMode: + oldDesktopName = sys.argv[1] + appName = sys.argv[2] + appPath = sys.argv[3] + iconPath = sys.argv[4] + +def updatePreviewIcon(name): + global iface + if os.path.exists(name): + iface.preview_icon.set_from_file(name) + else: + iface.preview_icon.set_from_icon_name(name, 6) + +def editOrAddLaunchers(): + makeLauncher() + global Settings, desktopName + desktopFiles = Settings.get_strv('panel-launchers') + if not editMode: + desktopFiles.append(newDesktopName) + else: + i = desktopFiles.index(oldDesktopName) + if i >= 0: + del desktopFiles[i] + desktopFiles.insert(i, newDesktopName) + Settings.set_strv('panel-launchers', desktopFiles) + Gtk.main_quit(None) + +def makeLauncher(): + global appName, appPath, iconPath, custom_launchers_path, newDesktopName + description = _("Custom Launcher") + i = 1 + dir = Gio.file_new_for_path(custom_launchers_path) + if not dir.query_exists(None): + dir.make_directory_with_parents(None) + + file = Gio.file_parse_name(custom_launchers_path + '/cinnamon-custom-launcher-' + str(i) + '.desktop') + while file.query_exists(None): + i = i + 1 + file = Gio.file_parse_name(custom_launchers_path + '/cinnamon-custom-launcher-' + str(i) + '.desktop') + file = open(custom_launchers_path+ '/cinnamon-custom-launcher-' + str(i) + '.desktop', "w") + + desktopEntry = "[Desktop Entry]\nName=" + appName + "\nExec=" + appPath + "\nType=Application\n" + desktopEntry = desktopEntry + "Description=" + description + "\n" + if iconPath == "": + iconPath = "application-x-executable" + desktopEntry += "Icon=" + iconPath + "\n" + print desktopEntry + file.write(desktopEntry) + file.close() + newDesktopName = 'cinnamon-custom-launcher-' + str(i) + '.desktop' + +class Handler: + def onDeleteWindow(self, *args): + Gtk.main_quit(*args) + + def onAdd(self, button): + global appPath, appName + if appPath == "" or appName == "": + return + else: + editOrAddLaunchers() + + def onIconPicked(self, *args): + global iconPath, iface + iconPath = iface.icon_picker.get_uri()[7:] + iface.icon_path.set_text(iconPath) + updatePreviewIcon(iconPath) + + def onAppPicked(self, *args): + global appPath, iface + appPath = iface.app_picker.get_uri()[7:] + iface.file_path.set_text(appPath) + + def onNameChanged(self, *args): + global appName, iface + appName = iface.app_name.get_text().strip() + + def onAppChanged(self, *args): + global appPath, iface + appPath = iface.file_path.get_text().strip() + + def onIconChanged(self, *args): + global iconPath, iface + iconPath = iface.icon_path.get_text().strip() + updatePreviewIcon(iconPath) + +builder = Gtk.Builder() + +userhome = expanduser("~") +custom_launchers_path = userhome + "/.cinnamon/panel-launchers" + +applet_dir = os.path.dirname(inspect.getfile(inspect.currentframe())) +builder.add_from_file(applet_dir + "/add-panel-launcher.glade") + +window = builder.get_object("add-panel-launcher-dialog") +builder.connect_signals(Handler()) + +iface.add_button = builder.get_object("add_button") +iface.cancel_button = builder.get_object("cancel_button") +iface.preview_icon = builder.get_object("icon") +iface.app_name = builder.get_object("app_name") +iface.file_path = builder.get_object("app_path") +iface.icon_path = builder.get_object("icon_path") +iface.app_picker = builder.get_object("app_picker") +iface.icon_picker = builder.get_object("icon_picker") + + +# set static translations (labels, etc..) +builder.get_object("name_label").set_markup(_("Name")) +builder.get_object("application_label").set_markup(_("Application")) +builder.get_object("icon_label").set_markup(_("Icon")) +builder.get_object("cancel_button").set_label(_("Cancel")) +builder.get_object("add-panel-launcher-dialog").set_title(_("Add panel launcher...")) + +if editMode: + iface.app_name.set_text(appName) + iface.file_path.set_text(appPath) + iface.icon_path.set_text(iconPath) + iface.add_button.set_label(_("Update")) + updatePreviewIcon(iconPath) +else: + iface.add_button.set_label(_("Add")) + +window.show_all() +Gtk.main() diff --git a/files/usr/share/cinnamon/applets/panel-launchers@cinnamon.org/applet.js b/files/usr/share/cinnamon/applets/panel-launchers@cinnamon.org/applet.js index a4cf23f..56c47e5 100644 --- a/files/usr/share/cinnamon/applets/panel-launchers@cinnamon.org/applet.js +++ b/files/usr/share/cinnamon/applets/panel-launchers@cinnamon.org/applet.js @@ -12,6 +12,18 @@ const GLib = imports.gi.GLib; const Tooltips = imports.ui.tooltips; const DND = imports.ui.dnd; const Tweener = imports.ui.tweener; +const Util = imports.misc.util; + +const DEFAULT_ICON_SIZE = 20; +const DEFAULT_ANIM_SIZE = 13; +const ICON_HEIGHT_FACTOR = .8; +const ICON_ANIM_FACTOR = .65; + +const PANEL_EDIT_MODE_KEY = 'panel-edit-mode'; +const PANEL_LAUNCHERS_KEY = 'panel-launchers'; +const PANEL_LAUNCHERS_DRAGGABLE_KEY = 'panel-launchers-draggable'; +const PANEL_RESIZABLE_KEY = 'panel-resizable'; +const PANEL_SCALE_TEXT_ICONS_KEY = 'panel-scale-text-icons'; let pressLauncher = null; @@ -19,118 +31,132 @@ function PanelAppLauncherMenu(launcher, orientation) { this._init(launcher, orientation); } +const APPLET_DIR = imports.ui.appletManager._find_applet('panel-launchers@cinnamon.org'); const CUSTOM_LAUNCHERS_PATH = GLib.get_home_dir() + '/.cinnamon/panel-launchers'; PanelAppLauncherMenu.prototype = { __proto__: PopupMenu.PopupMenu.prototype, - + _init: function(launcher, orientation) { - this._launcher = launcher; - + this._launcher = launcher; + PopupMenu.PopupMenu.prototype._init.call(this, launcher.actor, 0.0, orientation, 0); Main.uiGroup.add_actor(this.actor); this.actor.hide(); - + this.launchItem = new PopupMenu.PopupMenuItem(_("Launch")); this.addMenuItem(this.launchItem); this.launchItem.connect('activate', Lang.bind(this, this._onLaunchActivate)); - + this.addItem = new PopupMenu.PopupMenuItem(_("Add")); this.addMenuItem(this.addItem); this.addItem.connect('activate', Lang.bind(this, this._onAddActivate)); - + this.editItem = new PopupMenu.PopupMenuItem(_("Edit")); this.addMenuItem(this.editItem); this.editItem.connect('activate', Lang.bind(this, this._onEditActivate)); - + this.removeItem = new PopupMenu.PopupMenuItem(_("Remove")); this.addMenuItem(this.removeItem); - this.removeItem.connect('activate', Lang.bind(this, this._onRemoveActivate)); + this.removeItem.connect('activate', Lang.bind(this, this._onRemoveActivate)); }, - + _onLaunchActivate: function(actor, event) { this._launcher.launch(); }, - + _onRemoveActivate: function(actor, event) { - this._launcher.launchersBox.removeLauncher(this._launcher, this._launcher.is_custom()); + this._launcher.launchersBox.removeLauncher(this._launcher, this._launcher.isCustom()); this._launcher.actor.destroy(); }, - + _onAddActivate: function(actor, event) { this._launcher.launchersBox.showAddLauncherDialog(event.get_time()); }, - + _onEditActivate: function(actor, event) { this._launcher.launchersBox.showAddLauncherDialog(event.get_time(), this._launcher); } } -function PanelAppLauncher(launchersBox, app, appinfo, orientation) { - this._init(launchersBox, app, appinfo, orientation); +function PanelAppLauncher(launchersBox, app, appinfo, orientation, panel_height) { + this._init(launchersBox, app, appinfo, orientation, panel_height); } PanelAppLauncher.prototype = { - _init: function(launchersBox, app, appinfo, orientation) { + _init: function(launchersBox, app, appinfo, orientation, panel_height) { this.app = app; this.appinfo = appinfo; this.launchersBox = launchersBox; this.actor = new St.Bin({ style_class: 'panel-launcher', - reactive: true, - can_focus: true, - x_fill: true, - y_fill: false, - track_hover: true }); + reactive: true, + can_focus: true, + x_fill: true, + y_fill: false, + track_hover: true }); this.actor._delegate = this; this.actor.connect('button-release-event', Lang.bind(this, this._onButtonRelease)); this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress)); - - this._iconBox = new Cinnamon.Slicer({ name: 'panel-launcher-icon' }); + + this._iconBox = new St.Bin({ name: 'panel-launcher-icon' }); this._iconBox.connect('style-changed', Lang.bind(this, this._onIconBoxStyleChanged)); this._iconBox.connect('notify::allocation', Lang.bind(this, this._updateIconBoxClip)); this.actor.add_actor(this._iconBox); this._iconBottomClip = 0; - + + if (global.settings.get_boolean(PANEL_SCALE_TEXT_ICONS_KEY) && global.settings.get_boolean(PANEL_RESIZABLE_KEY)) { + this.icon_height = Math.floor(panel_height * ICON_HEIGHT_FACTOR); + this.icon_anim_height = Math.floor(panel_height * ICON_ANIM_FACTOR); + } else { + this.icon_height = DEFAULT_ICON_SIZE; + this.icon_anim_height = DEFAULT_ANIM_SIZE; + } this.icon = this._getIconActor(); this._iconBox.set_child(this.icon); - + this._menuManager = new PopupMenu.PopupMenuManager(this); this._menu = new PanelAppLauncherMenu(this, orientation); this._menuManager.addMenu(this._menu); - + let tooltipText; - if (this.is_custom()) tooltipText = appinfo.get_name(); + if (this.isCustom()) tooltipText = appinfo.get_name(); else tooltipText = app.get_name(); this._tooltip = new Tooltips.PanelItemTooltip(this, tooltipText, orientation); - + this._dragging = false; - let settings = new Gio.Settings({ schema: 'org.cinnamon'}); - if (settings.get_boolean('panel-launchers-draggable')){ - this._draggable = DND.makeDraggable(this.actor); - this._draggable.connect('drag-begin', Lang.bind(this, this._onDragBegin)); - this._draggable.connect('drag-cancelled', Lang.bind(this, this._onDragCancelled)); - this._draggable.connect('drag-end', Lang.bind(this, this._onDragEnd)); - } - }, - + this._draggable = DND.makeDraggable(this.actor); + + this._draggable.connect('drag-begin', Lang.bind(this, this._onDragBegin)); + this._draggable.connect('drag-cancelled', Lang.bind(this, this._onDragCancelled)); + this._draggable.connect('drag-end', Lang.bind(this, this._onDragEnd)); + + this._draggable.inhibit = !global.settings.get_boolean(PANEL_LAUNCHERS_DRAGGABLE_KEY) || global.settings.get_boolean(PANEL_EDIT_MODE_KEY); + global.settings.connect('changed::' + PANEL_LAUNCHERS_DRAGGABLE_KEY, Lang.bind(this, this._updateInhibit)); + global.settings.connect('changed::' + PANEL_EDIT_MODE_KEY, Lang.bind(this, this._updateInhibit)); + }, + _onDragBegin: function() { this._dragging = true; this._tooltip.hide(); this._tooltip.preventShow = true; }, - + _onDragEnd: function() { this._dragging = false; this._tooltip.preventShow = false; }, - + _onDragCancelled: function() { this._dragging = false; this._tooltip.preventShow = false; }, - + + _updateInhibit: function(){ + this._draggable.inhibit = !global.settings.get_boolean(PANEL_LAUNCHERS_DRAGGABLE_KEY) || global.settings.get_boolean(PANEL_EDIT_MODE_KEY); + }, + getDragActor: function() { return this._getIconActor(); }, @@ -140,69 +166,69 @@ PanelAppLauncher.prototype = { getDragActorSource: function() { return this.icon; }, - + _getIconActor: function() { - if (this.is_custom()) return St.TextureCache.get_default().load_gicon(null, this.appinfo.get_icon(), 20); - else return this.app.create_icon_texture(20); + if (this.isCustom()) return St.TextureCache.get_default().load_gicon(null, this.appinfo.get_icon(), this.icon_height); + else return this.app.create_icon_texture(this.icon_height); }, - + _animateIcon: function(step){ if (step>=3) return; Tweener.addTween(this.icon, - { width: 13, - height: 13, + { width: this.icon_anim_height, + height: this.icon_anim_height, time: 0.2, transition: 'easeOutQuad', onComplete: function(){ Tweener.addTween(this.icon, - { width: 20, - height: 20, - time: 0.2, - transition: 'easeOutQuad', - onComplete: function(){ - this._animateIcon(step+1); - }, - onCompleteScope: this - }); + { width: this.icon_height, + height: this.icon_height, + time: 0.2, + transition: 'easeOutQuad', + onComplete: function(){ + this._animateIcon(step+1); + }, + onCompleteScope: this + }); }, onCompleteScope: this }); }, - + launch: function() { let allocation = this._iconBox.get_allocation_box(); this._iconBox.width = allocation.x2 - allocation.x1; this._iconBox.height = allocation.y2 - allocation.y1; this._animateIcon(0); - if (this.is_custom()) this.appinfo.launch([], null); + if (this.isCustom()) this.appinfo.launch([], null); else this.app.open_new_window(-1); }, - - get_id: function() { - if (this.is_custom()) return Gio.file_new_for_path(this.appinfo.get_filename()).get_basename(); + + getId: function() { + if (this.isCustom()) return Gio.file_new_for_path(this.appinfo.get_filename()).get_basename(); else return this.app.get_id(); }, - - is_custom: function() { + + isCustom: function() { return (this.app==null); }, - + _onButtonPress: function(actor, event) { - pressLauncher = this.get_appname(); + pressLauncher = this.getAppname(); }, - + _onButtonRelease: function(actor, event) { - if (pressLauncher == this.get_appname()){ - let button = event.get_button(); + if (pressLauncher == this.getAppname()){ + let button = event.get_button(); if (button==1) { - if (this._menu.isOpen) this._menu.toggle(); - else this.launch(); + if (this._menu.isOpen) this._menu.toggle(); + else this.launch(); }else if (button==3) { - this._menu.toggle(); + this._menu.toggle(); } - } + } }, - + _onIconBoxStyleChanged: function() { let node = this._iconBox.get_theme_node(); this._iconBottomClip = node.get_length('panel-launcher-bottom-clip'); @@ -216,22 +242,22 @@ PanelAppLauncher.prototype = { else this._iconBox.remove_clip(); }, - - get_appinfo: function() { - if (this.is_custom()) return this.appinfo; + + getAppInfo: function() { + if (this.isCustom()) return this.appinfo; else return this.app.get_app_info(); }, - - get_command: function() { - return this.get_appinfo().get_commandline(); + + getCommand: function() { + return this.getAppInfo().get_commandline(); }, - - get_appname: function() { - return this.get_appinfo().get_name(); + + getAppname: function() { + return this.getAppInfo().get_name(); }, - - get_icon: function() { - let icon = this.get_appinfo().get_icon(); + + getIcon: function() { + let icon = this.getAppInfo().get_icon(); if (icon){ if (icon instanceof Gio.FileIcon) { return icon.get_file().get_path(); @@ -244,242 +270,49 @@ PanelAppLauncher.prototype = { } } -function AddLauncherDialog() { - this._init(); -} - -AddLauncherDialog.prototype = { - __proto__: ModalDialog.ModalDialog.prototype, - - _init: function() { - ModalDialog.ModalDialog.prototype._init.call(this, { styleClass: 'panel-launcher-add-dialog' }); - - let box; - let label; - - let box = new St.BoxLayout({ styleClass: 'panel-launcher-add-dialog-content-box' }); - let leftBox = new St.BoxLayout({vertical: true, styleClass: 'panel-launcher-add-dialog-content-box-left'}); - let rightBox = new St.BoxLayout({vertical: true, styleClass: 'panel-launcher-add-dialog-content-box-right'}); - - label = new St.Label(); - label.set_text(_("Name")); - leftBox.add(label, { x_align: St.Align.START, x_fill: true, x_expand: true }); - this._nameEntry = new St.Entry({ styleClass: 'panel-launcher-add-dialog-entry', can_focus: true }); - rightBox.add(this._nameEntry, { x_align: St.Align.END, x_fill: false, x_expand: false }); - - label = new St.Label(); - label.set_text(_("Command")); - leftBox.add(label, { x_align: St.Align.START, x_fill: true, x_expand: true }); - this._commandEntry = new St.Entry({ styleClass: 'panel-launcher-add-dialog-entry', can_focus: true }); - rightBox.add(this._commandEntry, { x_align: St.Align.END, x_fill: false, x_expand: false }); - - label = new St.Label(); - label.set_text(_("Icon")); - leftBox.add(label, { x_align: St.Align.START, x_fill: true, x_expand: true }); - this._iconEntry = new St.Entry({ styleClass: 'panel-launcher-add-dialog-entry', can_focus: true }); - rightBox.add(this._iconEntry, { x_align: St.Align.END, x_fill: false, x_expand: false }); - - box.add(leftBox); - box.add(rightBox); - this.contentLayout.add(box, { y_align: St.Align.START }); - - this._errorBox = new St.BoxLayout({ style_class: 'run-dialog-error-box' }); - this.contentLayout.add(this._errorBox, { expand: true }); - - let errorIcon = new St.Icon({ icon_name: 'dialog-error', icon_size: 24, style_class: 'run-dialog-error-icon' }); - - this._errorBox.add(errorIcon, { y_align: St.Align.MIDDLE }); - - this._commandError = false; - - this._errorMessage = new St.Label({ style_class: 'run-dialog-error-label' }); - this._errorMessage.clutter_text.line_wrap = true; - - this._errorBox.add(this._errorMessage, { expand: true, - y_align: St.Align.MIDDLE, - y_fill: false }); - - this._errorBox.hide(); - - this.connect('opened', Lang.bind(this, this._onOpened)); - - this._currentLauncher = null; - }, - - _onOpened: function() { - this._nameEntry.grab_key_focus(); - }, - - _validateAdd: function() { - if (this._nameEntry.clutter_text.get_text()==""){ - this._errorMessage.clutter_text.set_text(_("Name cannot be empty!")); - this._errorBox.show(); - return false; - } - if (this._commandEntry.clutter_text.get_text()==""){ - this._errorMessage.clutter_text.set_text(_("Command cannot be empty!")); - this._errorBox.show(); - return false; - } - - - let appid = this._saveNewLauncher(this._nameEntry.clutter_text.get_text(), this._commandEntry.clutter_text.get_text(), _("Custom Launcher"), this._iconEntry.clutter_text.get_text()); - - this.close(); - - if (this._currentLauncher) this.emit("launcher-updated", this._currentLauncher, appid); - else this.emit("launcher-created", appid); - - return true; - }, - - _saveNewLauncher: function(name, command, description, icon){ - let file; - let i; - if (this._currentLauncher && this._currentLauncher.is_custom()){ - file = Gio.file_parse_name(CUSTOM_LAUNCHERS_PATH+'/'+this._currentLauncher.get_id()); - file.delete(null); - }else{ - let dir = Gio.file_new_for_path(CUSTOM_LAUNCHERS_PATH); - if (!dir.query_exists(null)) dir.make_directory_with_parents(null); - i = 1; - file = Gio.file_parse_name(CUSTOM_LAUNCHERS_PATH+'/cinnamon-custom-launcher-'+i+'.desktop'); - while (file.query_exists(null)){ - i++; - file = Gio.file_parse_name(CUSTOM_LAUNCHERS_PATH+'/cinnamon-custom-launcher-'+i+'.desktop'); - } - } - - let desktopEntry = "[Desktop Entry]\nName="+name+"\nExec="+command+"\nType=Application\n"; - if (description) desktopEntry += "Description="+description+"\n"; - if (!icon && this._currentLauncher) icon = this._currentLauncher.get_icon(); - if (!icon) icon = "application-x-executable"; - desktopEntry += "Icon="+icon+"\n"; - - let fp = file.create(0, null); - fp.write(desktopEntry, null); - fp.close(null); - - if (this._currentLauncher && this._currentLauncher.is_custom()) return this._currentLauncher.get_id(); - else return 'cinnamon-custom-launcher-'+i+'.desktop'; - }, - - open: function(timestamp, launcher) { - this._currentLauncher = launcher; - - if (launcher){ - this._commandEntry.clutter_text.set_text(launcher.get_command()); - this._nameEntry.clutter_text.set_text(launcher.get_appname()); - if (launcher.get_icon()) this._iconEntry.clutter_text.set_text(launcher.get_icon()); - this._errorBox.hide(); - this.setButtons([ - { - label: _("Save"), - action: Lang.bind(this, this._validateAdd) - }, - { - label: _("Cancel"), - key: Clutter.KEY_Escape, - action: Lang.bind(this, function(){ - this.close(); - }) - } - ]); - }else{ - this._commandEntry.clutter_text.set_text(''); - this._nameEntry.clutter_text.set_text(''); - this._errorBox.hide(); - this.setButtons([ - { - label: _("Add"), - action: Lang.bind(this, this._validateAdd) - }, - { - label: _("Cancel"), - key: Clutter.KEY_Escape, - action: Lang.bind(this, function(){ - this.close(); - }) - } - ]); - } - - ModalDialog.ModalDialog.prototype.open.call(this, timestamp); - }, -} -Signals.addSignalMethods(AddLauncherDialog.prototype); - -function MyApplet(orientation) { - this._init(orientation); +function MyApplet(orientation, panel_height) { + this._init(orientation, panel_height); } MyApplet.prototype = { __proto__: Applet.Applet.prototype, - _init: function(orientation) { - Applet.Applet.prototype._init.call(this, orientation); - - try { + _init: function(orientation, panel_height) { + Applet.Applet.prototype._init.call(this, orientation, panel_height); + + try { this.orientation = orientation; this._dragPlaceholder = null; this._dragPlaceholderPos = -1; this._animatingPlaceholdersCount = 0; - + this.myactor = new St.BoxLayout({ name: 'panel-launchers-box', - style_class: 'panel-launchers-box' }); - - this._settings = new Gio.Settings({ schema: 'org.cinnamon' }); - this._settings.connect('changed::panel-launchers', Lang.bind(this, this._onSettingsChanged)); - - this._addLauncherDialog = new AddLauncherDialog(); - this._addLauncherDialog.connect("launcher-created", Lang.bind(this, this._onLauncherCreated)); - this._addLauncherDialog.connect("launcher-updated", Lang.bind(this, this._onLauncherUpdated)); - + style_class: 'panel-launchers-box' }); + global.settings.connect('changed::' + PANEL_LAUNCHERS_KEY, Lang.bind(this, this._onSettingsChanged)); + this._launchers = new Array(); - + this.reload(); - - this.actor.add(this.myactor); - this.actor.reactive = global.settings.get_boolean("panel-edit-mode"); - global.settings.connect('changed::panel-edit-mode', Lang.bind(this, this.on_panel_edit_mode_changed)); + + this.actor.add(this.myactor); + this.actor.reactive = global.settings.get_boolean(PANEL_EDIT_MODE_KEY); + global.settings.connect('changed::' + PANEL_EDIT_MODE_KEY, Lang.bind(this, this._onPanelEditModeChanged)); } catch (e) { global.logError(e); } }, - - on_applet_clicked: function(event) { - + + _onPanelEditModeChanged: function() { + this.actor.reactive = global.settings.get_boolean(PANEL_EDIT_MODE_KEY); }, - - on_panel_edit_mode_changed: function() { - this.actor.reactive = global.settings.get_boolean("panel-edit-mode"); - }, - + _onSettingsChanged: function() { this.reload(); }, - - _onLauncherUpdated: function(obj, launcher, appid){ - let desktopFiles = this._settings.get_strv('panel-launchers'); - let i = this._launchers.indexOf(launcher); - if (i>=0){ - desktopFiles.splice(i, 1); - desktopFiles.splice(i, 0, appid); - this._settings.set_strv('panel-launchers', desktopFiles); - } - }, - - _onLauncherCreated: function(obj, appid){ - if (appid){ - let desktopFiles = this._settings.get_strv('panel-launchers'); - desktopFiles.push(appid); - this._settings.set_strv('panel-launchers', desktopFiles); - } - }, - + loadApps: function() { - let desktopFiles = this._settings.get_strv('panel-launchers'); + let desktopFiles = global.settings.get_strv(PANEL_LAUNCHERS_KEY); let appSys = Cinnamon.AppSystem.get_default(); let apps = new Array(); for (var i in desktopFiles){ @@ -491,50 +324,63 @@ MyApplet.prototype = { } return apps; }, - + + on_panel_height_changed: function() { + this.reload(); + }, + reload: function() { this.myactor.destroy_children(); this._launchers = new Array(); - + let apps = this.loadApps(); for (var i in apps){ let app = apps[i]; - let launcher = new PanelAppLauncher(this, app[0], app[1], this.orientation); + let launcher = new PanelAppLauncher(this, app[0], app[1], this.orientation, this._panelHeight); this.myactor.add(launcher.actor); this._launchers.push(launcher); } }, - + removeLauncher: function(launcher, delete_file) { - let desktopFiles = this._settings.get_strv('panel-launchers'); + let desktopFiles = global.settings.get_strv(PANEL_LAUNCHERS_KEY); let i = this._launchers.indexOf(launcher); if (i>=0){ this._launchers.splice(i, 1); desktopFiles.splice(i, 1); - this._settings.set_strv('panel-launchers', desktopFiles); + global.settings.set_strv(PANEL_LAUNCHERS_KEY, desktopFiles); } if (delete_file){ - let appid = launcher.get_id(); + let appid = launcher.getId(); let file = new Gio.file_new_for_path(CUSTOM_LAUNCHERS_PATH+"/"+appid); if (file.query_exists(null)) file.delete(null); } }, - + moveLauncher: function(launcher, pos) { - let desktopFiles = this._settings.get_strv('panel-launchers'); + let desktopFiles = global.settings.get_strv(PANEL_LAUNCHERS_KEY); let origpos = this._launchers.indexOf(launcher); if (origpos>=0){ this._launchers.splice(origpos, 1); desktopFiles.splice(origpos, 1); - desktopFiles.splice(pos, 0, launcher.get_id()); - this._settings.set_strv('panel-launchers', desktopFiles); + desktopFiles.splice(pos, 0, launcher.getId()); + global.settings.set_strv(PANEL_LAUNCHERS_KEY, desktopFiles); } }, - + showAddLauncherDialog: function(timestamp, launcher){ - this._addLauncherDialog.open(timestamp, launcher); + if (launcher) { + let cl = APPLET_DIR.get_child('add-panel-launcher.py').get_path() + ' '; + cl += '"' + launcher.getId() + '" '; + cl += '"' + launcher.getAppname() + '" '; + cl += '"' + launcher.getCommand() + '" '; + cl += '"' + launcher.getIcon() + '"'; + Util.spawnCommandLine(cl); + } else { + Util.spawnCommandLine(APPLET_DIR.get_child('add-panel-launcher.py').get_path()); + } }, - + _clearDragPlaceholder: function() { if (this._dragPlaceholder) { this._dragPlaceholder.animateOutAndDestroy(); @@ -542,24 +388,24 @@ MyApplet.prototype = { this._dragPlaceholderPos = -1; } }, - + handleDragOver: function(source, actor, x, y, time) { if (!(source.isDraggableApp || (source instanceof PanelAppLauncher))) return DND.DragMotionResult.NO_DROP; - + let children = this.myactor.get_children(); let numChildren = children.length; let boxWidth = this.myactor.width; - + if (this._dragPlaceholder) { boxWidth -= this._dragPlaceholder.actor.width; numChildren--; } - + let launcherPos = this._launchers.indexOf(source); - + let pos = Math.round(x * numChildren / boxWidth); - - if (pos != this._dragPlaceholderPos && pos <= numChildren) { + + if (pos != this._dragPlaceholderPos && pos <= numChildren) { if (this._animatingPlaceholdersCount > 0) { let launchersChildren = children.filter(function(actor) { return actor._delegate instanceof PanelAppLauncher; @@ -575,9 +421,9 @@ MyApplet.prototype = { this._dragPlaceholder.animateOutAndDestroy(); this._animatingPlaceholdersCount++; this._dragPlaceholder.actor.connect('destroy', - Lang.bind(this, function() { - this._animatingPlaceholdersCount--; - })); + Lang.bind(this, function() { + this._animatingPlaceholdersCount--; + })); } this._dragPlaceholder = null; @@ -600,20 +446,19 @@ MyApplet.prototype = { this._dragPlaceholder.child.set_height (10); this.myactor.insert_actor(this._dragPlaceholder.actor, this._dragPlaceholderPos); - if (fadeIn) - this._dragPlaceholder.animateIn(); + if (fadeIn) this._dragPlaceholder.animateIn(); } - + return DND.DragMotionResult.MOVE_DROP; }, - + acceptDrop: function(source, actor, x, y, time) { if (!(source.isDraggableApp || (source instanceof PanelAppLauncher))) return DND.DragMotionResult.NO_DROP; - + let sourceId; - if (source instanceof PanelAppLauncher) sourceId = source.get_id(); + if (source instanceof PanelAppLauncher) sourceId = source.getId(); else sourceId = source.get_app_id(); - + let launcherPos = 0; let children = this.myactor.get_children(); for (let i = 0; i < this._dragPlaceholderPos; i++) { @@ -621,26 +466,24 @@ MyApplet.prototype = { children[i] == this._dragPlaceholder.actor) continue; - let childId = children[i]._delegate.get_id(); + let childId = children[i]._delegate.getId(); if (childId == sourceId) continue; launcherPos++; } if (source instanceof PanelAppLauncher) this.moveLauncher(source, launcherPos); else{ - let desktopFiles = this._settings.get_strv('panel-launchers'); + let desktopFiles = global.settings.get_strv(PANEL_LAUNCHERS_KEY); desktopFiles.splice(launcherPos, 0, sourceId); - this._settings.set_strv('panel-launchers', desktopFiles); + global.settings.set_strv(PANEL_LAUNCHERS_KEY, desktopFiles); } this._clearDragPlaceholder(); actor.destroy(); return true; } - - }; -function main(metadata, orientation) { - let myApplet = new MyApplet(orientation); - return myApplet; +function main(metadata, orientation, panel_height) { + let myApplet = new MyApplet(orientation, panel_height); + return myApplet; } diff --git a/files/usr/share/cinnamon/applets/power@cinnamon.org/applet.js b/files/usr/share/cinnamon/applets/power@cinnamon.org/applet.js index 7603904..0b5ba2e 100644 --- a/files/usr/share/cinnamon/applets/power@cinnamon.org/applet.js +++ b/files/usr/share/cinnamon/applets/power@cinnamon.org/applet.js @@ -128,16 +128,16 @@ DeviceItem.prototype = { } } -function MyApplet(orientation) { - this._init(orientation); +function MyApplet(orientation, panel_height) { + this._init(orientation, panel_height); } MyApplet.prototype = { __proto__: Applet.TextIconApplet.prototype, - _init: function(orientation) { - Applet.TextIconApplet.prototype._init.call(this, orientation); + _init: function(orientation, panel_height) { + Applet.TextIconApplet.prototype._init.call(this, orientation, panel_height); try { this.menuManager = new PopupMenu.PopupMenuManager(this); @@ -302,11 +302,16 @@ MyApplet.prototype = { })); }, + on_panel_height_changed: function() { + this._devicesChanged(); + }, + _devicesChanged: function() { + this.set_applet_icon_symbolic_name('battery-missing'); this._proxy.GetRemote('Icon', Lang.bind(this, function(icon, error) { if (icon) { - let gicon = Gio.icon_new_for_string(icon); - this._applet_icon.gicon = gicon; + let gicon = Gio.icon_new_for_string(icon); + this._applet_icon.gicon = gicon; this.actor.show(); } else { this.menu.close(); @@ -353,7 +358,7 @@ MyApplet.prototype = { }; -function main(metadata, orientation) { - let myApplet = new MyApplet(orientation); +function main(metadata, orientation, panel_height) { + let myApplet = new MyApplet(orientation, panel_height); return myApplet; } diff --git a/files/usr/share/cinnamon/applets/recent@cinnamon.org/applet.js b/files/usr/share/cinnamon/applets/recent@cinnamon.org/applet.js index b0037b5..56cc620 100644 --- a/files/usr/share/cinnamon/applets/recent@cinnamon.org/applet.js +++ b/files/usr/share/cinnamon/applets/recent@cinnamon.org/applet.js @@ -26,15 +26,15 @@ MyPopupMenuItem.prototype = } }; -function MyApplet(orientation) { - this._init(orientation); +function MyApplet(orientation, panel_height) { + this._init(orientation, panel_height); } MyApplet.prototype = { __proto__: Applet.IconApplet.prototype, - _init: function(orientation) { - Applet.IconApplet.prototype._init.call(this, orientation); + _init: function(orientation, panel_height) { + Applet.IconApplet.prototype._init.call(this, orientation, panel_height); try { this.set_applet_icon_symbolic_name("document-open-recent"); @@ -98,7 +98,7 @@ MyApplet.prototype = { } }; -function main(metadata, orientation) { - let myApplet = new MyApplet(orientation); +function main(metadata, orientation, panel_height) { + let myApplet = new MyApplet(orientation, panel_height); return myApplet; } diff --git a/files/usr/share/cinnamon/applets/removable-drives@cinnamon.org/applet.js b/files/usr/share/cinnamon/applets/removable-drives@cinnamon.org/applet.js index 82cafe5..c4fad21 100644 --- a/files/usr/share/cinnamon/applets/removable-drives@cinnamon.org/applet.js +++ b/files/usr/share/cinnamon/applets/removable-drives@cinnamon.org/applet.js @@ -38,15 +38,15 @@ DriveMenuItem.prototype = { } }; -function MyApplet(orientation) { - this._init(orientation); +function MyApplet(orientation, panel_height) { + this._init(orientation, panel_height); } MyApplet.prototype = { __proto__: Applet.IconApplet.prototype, - _init: function(orientation) { - Applet.IconApplet.prototype._init.call(this, orientation); + _init: function(orientation, panel_height) { + Applet.IconApplet.prototype._init.call(this, orientation, panel_height); try { this.set_applet_icon_symbolic_name("drive-harddisk"); @@ -64,7 +64,7 @@ MyApplet.prototype = { this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); this.menu.addAction(_("Open file manager"), function(event) { let appSystem = Cinnamon.AppSystem.get_default(); - let app = appSystem.lookup_app('nautilus.desktop'); + let app = appSystem.lookup_app('nemo.desktop'); app.activate_full(-1, event.get_time()); }); @@ -96,7 +96,7 @@ MyApplet.prototype = { }; -function main(metadata, orientation) { - let myApplet = new MyApplet(orientation); +function main(metadata, orientation, panel_height) { + let myApplet = new MyApplet(orientation, panel_height); return myApplet; } diff --git a/files/usr/share/cinnamon/applets/scale@cinnamon.org/applet.js b/files/usr/share/cinnamon/applets/scale@cinnamon.org/applet.js index 599e23d..f2b86a1 100644 --- a/files/usr/share/cinnamon/applets/scale@cinnamon.org/applet.js +++ b/files/usr/share/cinnamon/applets/scale@cinnamon.org/applet.js @@ -3,15 +3,15 @@ const Lang = imports.lang; const Main = imports.ui.main; const Gtk = imports.gi.Gtk; -function MyApplet(metadata, orientation) { - this._init(metadata, orientation); +function MyApplet(metadata, orientation, panel_height) { + this._init(metadata, orientation, panel_height); } MyApplet.prototype = { __proto__: Applet.IconApplet.prototype, - _init: function(metadata, orientation) { - Applet.IconApplet.prototype._init.call(this, orientation); + _init: function(metadata, orientation, panel_height) { + Applet.IconApplet.prototype._init.call(this, orientation, panel_height); try { Gtk.IconTheme.get_default().append_search_path(metadata.path); @@ -56,7 +56,7 @@ MyApplet.prototype = { } }; -function main(metadata, orientation) { - let myApplet = new MyApplet(metadata, orientation); +function main(metadata, orientation, panel_height) { + let myApplet = new MyApplet(metadata, orientation, panel_height); return myApplet; } diff --git a/files/usr/share/cinnamon/applets/settings@cinnamon.org/applet.js b/files/usr/share/cinnamon/applets/settings@cinnamon.org/applet.js index f5cb34a..8fab806 100644 --- a/files/usr/share/cinnamon/applets/settings@cinnamon.org/applet.js +++ b/files/usr/share/cinnamon/applets/settings@cinnamon.org/applet.js @@ -13,15 +13,15 @@ function ConfirmDialog(){ this._init(); } -function MyApplet(orientation) { - this._init(orientation); +function MyApplet(orientation, panel_height) { + this._init(orientation, panel_height); } MyApplet.prototype = { __proto__: Applet.IconApplet.prototype, - _init: function(orientation) { - Applet.IconApplet.prototype._init.call(this, orientation); + _init: function(orientation, panel_height) { + Applet.IconApplet.prototype._init.call(this, orientation, panel_height); try { this.set_applet_icon_symbolic_name("go-up"); @@ -53,7 +53,7 @@ MyApplet.prototype = { }; -function main(metadata, orientation) { - let myApplet = new MyApplet(orientation); +function main(metadata, orientation, panel_height) { + let myApplet = new MyApplet(orientation, panel_height); return myApplet; } diff --git a/files/usr/share/cinnamon/applets/show-desktop@cinnamon.org/applet.js b/files/usr/share/cinnamon/applets/show-desktop@cinnamon.org/applet.js index 3899d6b..ea5ef2b 100644 --- a/files/usr/share/cinnamon/applets/show-desktop@cinnamon.org/applet.js +++ b/files/usr/share/cinnamon/applets/show-desktop@cinnamon.org/applet.js @@ -1,15 +1,15 @@ const Applet = imports.ui.applet; const Lang = imports.lang; -function MyApplet(orientation) { - this._init(orientation); +function MyApplet(orientation, panel_height) { + this._init(orientation, panel_height); } MyApplet.prototype = { __proto__: Applet.IconApplet.prototype, - _init: function(orientation) { - Applet.IconApplet.prototype._init.call(this, orientation); + _init: function(orientation, panel_height) { + Applet.IconApplet.prototype._init.call(this, orientation, panel_height); try { this.set_applet_icon_name("desktop"); @@ -43,7 +43,7 @@ MyApplet.prototype = { } }; -function main(metadata, orientation) { - let myApplet = new MyApplet(orientation); +function main(metadata, orientation, panel_height) { + let myApplet = new MyApplet(orientation, panel_height); return myApplet; } diff --git a/files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js b/files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js index ff2fc36..1f22150 100644 --- a/files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js +++ b/files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js @@ -99,8 +99,8 @@ const MediaServer2PlayerIFace = { /* global values */ let icon_path = "/usr/share/cinnamon/theme/"; -let compatible_players = [ "clementine", "mpd", "exaile", "banshee", "rhythmbox", "rhythmbox3", "pragha", "quodlibet", "guayadeque", "amarok", "googlemusicframe", "xbmc", "xnoise", "gmusicbrowser", "spotify", "audacious", "vlc" ]; -let support_seek = [ "clementine", "banshee", "rhythmbox", "rhythmbox3", "pragha", "quodlibet", "amarok", "xnoise", "gmusicbrowser", "spotify", "vlc" ]; +let compatible_players = [ "clementine", "mpd", "exaile", "banshee", "rhythmbox", "rhythmbox3", "pragha", "quodlibet", "guayadeque", "amarok", "googlemusicframe", "xbmc", "xnoise", "gmusicbrowser", "spotify", "audacious", "vlc", "beatbox" ]; +let support_seek = [ "clementine", "banshee", "rhythmbox", "rhythmbox3", "pragha", "quodlibet", "amarok", "xnoise", "gmusicbrowser", "spotify", "vlc", "beatbox" ]; /* dummy vars for translation */ let x = _("Playing"); x = _("Paused"); @@ -694,15 +694,15 @@ MediaPlayerLauncher.prototype = { }; -function MyApplet(orientation) { - this._init(orientation); +function MyApplet(orientation, panel_height) { + this._init(orientation, panel_height); } MyApplet.prototype = { __proto__: Applet.IconApplet.prototype, - _init: function(orientation) { - Applet.IconApplet.prototype._init.call(this, orientation); + _init: function(orientation, panel_height) { + Applet.IconApplet.prototype._init.call(this, orientation, panel_height); try { this.menuManager = new PopupMenu.PopupMenuManager(this); @@ -741,13 +741,18 @@ MyApplet.prototype = { this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent)); + this.mute_out_switch = new PopupMenu.PopupSwitchMenuItem(_("Mute output"), false); + this.mute_in_switch = new PopupMenu.PopupSwitchMenuItem(_("Mute input"), false); + this._applet_context_menu.addMenuItem(this.mute_out_switch); + this._applet_context_menu.addMenuItem(this.mute_in_switch); + this.mute_out_switch.connect('toggled', Lang.bind(this, this._toggle_out_mute)); + this.mute_in_switch.connect('toggled', Lang.bind(this, this._toggle_in_mute)); + this._control.open(); this._volumeControlShown = false; this._showFixedElements(); - - } catch (e) { global.logError(e); @@ -758,6 +763,26 @@ MyApplet.prototype = { this.menu.toggle(); }, + _toggle_out_mute: function() { + if (this._output.is_muted) { + this._output.change_is_muted(false); + this.mute_out_switch.setToggleState(false); + } else { + this._output.change_is_muted(true); + this.mute_out_switch.setToggleState(true); + } + }, + + _toggle_in_mute: function() { + if (this._input.is_muted) { + this._input.change_is_muted(false); + this.mute_in_switch.setToggleState(false); + } else { + this._input.change_is_muted(true); + this.mute_in_switch.setToggleState(true); + } + }, + _onScrollEvent: function(actor, event) { let direction = event.get_scroll_direction(); let currentVolume = this._output.volume; @@ -934,11 +959,19 @@ MyApplet.prototype = { this._outputTitle.setIcon('audio-volume-muted'); this.set_applet_tooltip(_("Volume") + ": 0%"); this._outputTitle.setText(_("Volume") + ": 0%"); + this.mute_out_switch.setToggleState(true); } else { this.setIconName(this._volumeToIcon(this._output.volume)); this._outputTitle.setIcon(this._volumeToIcon(this._output.volume)); this.set_applet_tooltip(_("Volume") + ": " + Math.floor(this._output.volume / this._volumeMax * 100) + "%"); this._outputTitle.setText(_("Volume") + ": " + Math.floor(this._output.volume / this._volumeMax * 100) + "%"); + this.mute_out_switch.setToggleState(false); + } + } else if (property == '_input') { + if (muted) { + this.mute_in_switch.setToggleState(true); + } else { + this.mute_in_switch.setToggleState(false); } } }, @@ -1057,7 +1090,7 @@ MyApplet.prototype = { }; -function main(metadata, orientation) { - let myApplet = new MyApplet(orientation); +function main(metadata, orientation, panel_height) { + let myApplet = new MyApplet(orientation, panel_height); return myApplet; } diff --git a/files/usr/share/cinnamon/applets/systray@cinnamon.org/applet.js b/files/usr/share/cinnamon/applets/systray@cinnamon.org/applet.js index d1cdf5f..bd86120 100644 --- a/files/usr/share/cinnamon/applets/systray@cinnamon.org/applet.js +++ b/files/usr/share/cinnamon/applets/systray@cinnamon.org/applet.js @@ -1,30 +1,47 @@ +const Lang = imports.lang; +const St = imports.gi.St; + const Applet = imports.ui.applet; const Main = imports.ui.main; const PanelMenu = imports.ui.panelMenu; -const Lang = imports.lang; -function MyApplet(orientation) { - this._init(orientation); +const ICON_SCALE_FACTOR = .88; // for custom panel heights, 22 (default icon size) / 25 (default panel height) + +function MyApplet(orientation, panel_height) { + this._init(orientation, panel_height); } MyApplet.prototype = { __proto__: Applet.Applet.prototype, - _init: function(orientation) { - Applet.Applet.prototype._init.call(this, orientation); + _init: function(orientation, panel_height) { + Applet.Applet.prototype._init.call(this, orientation, panel_height); this.actor.remove_style_class_name("applet-box"); - try { - Main.statusIconDispatcher.connect('status-icon-added', Lang.bind(this, this._onTrayIconAdded)); - Main.statusIconDispatcher.connect('status-icon-removed', Lang.bind(this, this._onTrayIconRemoved)); - Main.statusIconDispatcher.connect('before-redisplay', Lang.bind(this, this._onBeforeRedisplay)); - } - catch (e) { - global.logError(e); - } + + this._signals = { added: null, + removed: null, + redisplay: null }; + + this.actor.style="spacing: 5px;"; }, on_applet_clicked: function(event) { + }, + + on_applet_removed_from_panel: function () { + Main.statusIconDispatcher.disconnect(this._signals.added); + Main.statusIconDispatcher.disconnect(this._signals.removed); + Main.statusIconDispatcher.disconnect(this._signals.redisplay); + }, + + on_applet_added_to_panel: function() { + this._signals.added = Main.statusIconDispatcher.connect('status-icon-added', Lang.bind(this, this._onTrayIconAdded)); + this._signals.removed = Main.statusIconDispatcher.connect('status-icon-removed', Lang.bind(this, this._onTrayIconRemoved)); + this._signals.redisplay = Main.statusIconDispatcher.connect('before-redisplay', Lang.bind(this, this._onBeforeRedisplay)); + }, + on_panel_height_changed: function() { + Main.statusIconDispatcher.redisplay(); }, _onBeforeRedisplay: function() { @@ -46,22 +63,28 @@ MyApplet.prototype = { global.log("Adding systray: " + role + " (" + icon.get_width() + "x" + icon.get_height() + "px)"); - let buttonBox = new PanelMenu.ButtonBox({ style_class: 'panel-status-button', reactive: true, track_hover: true }); - let box = buttonBox.actor; + let box = new St.Bin({ style_class: 'panel-status-button', reactive: true, track_hover: true}); + let iconParent = icon.get_parent(); + if (iconParent) iconParent.remove_actor(icon); box.add_actor(icon); this._insertStatusItem(box, -1); let width = 22; let height = 22; - let themeNode = buttonBox.actor.get_theme_node(); + let themeNode = box.get_theme_node(); if (themeNode.get_length('width')) { width = themeNode.get_length('width'); } if (themeNode.get_length('height')) { - height = themeNode.get_length('height'); + height = themeNode.get_length('height'); } - - if (buggyIcons.indexOf(role) != -1) { + + if (global.settings.get_boolean('panel-scale-text-icons')) { + width = Math.floor(this._panelHeight * ICON_SCALE_FACTOR); + height = Math.floor(this._panelHeight * ICON_SCALE_FACTOR); + } + + if (icon.get_width() == 1 || icon.get_height() == 1 || buggyIcons.indexOf(role) != -1) { icon.set_height(height); } else { @@ -75,7 +98,7 @@ MyApplet.prototype = { _onTrayIconRemoved: function(o, icon) { let box = icon.get_parent(); - if (box && box._delegate instanceof PanelMenu.ButtonBox) + if (box && box instanceof St.Bin) box.destroy(); }, @@ -99,7 +122,7 @@ MyApplet.prototype = { }; -function main(metadata, orientation) { - let myApplet = new MyApplet(orientation); +function main(metadata, orientation, panel_height) { + let myApplet = new MyApplet(orientation, panel_height); return myApplet; } diff --git a/files/usr/share/cinnamon/applets/trash@cinnamon.org/applet.js b/files/usr/share/cinnamon/applets/trash@cinnamon.org/applet.js index d8fbcfb..92764db 100644 --- a/files/usr/share/cinnamon/applets/trash@cinnamon.org/applet.js +++ b/files/usr/share/cinnamon/applets/trash@cinnamon.org/applet.js @@ -6,15 +6,15 @@ const Lang = imports.lang; const Clutter = imports.gi.Clutter; const Applet = imports.ui.applet; -function MyApplet(orientation) { - this._init(orientation); +function MyApplet(orientation, panel_height) { + this._init(orientation, panel_height); } MyApplet.prototype = { __proto__: Applet.IconApplet.prototype, - _init: function(orientation) { - Applet.IconApplet.prototype._init.call(this, orientation); + _init: function(orientation, panel_height) { + Applet.IconApplet.prototype._init.call(this, orientation, panel_height); try { this.set_applet_icon_symbolic_name("user-trash"); @@ -132,7 +132,7 @@ ConfirmEmptyTrashDialog.prototype = { } }; -function main(metadata, orientation) { - let myApplet = new MyApplet(orientation); +function main(metadata, orientation, panel_height) { + let myApplet = new MyApplet(orientation, panel_height); return myApplet; } diff --git a/files/usr/share/cinnamon/applets/window-list@cinnamon.org/applet.js b/files/usr/share/cinnamon/applets/window-list@cinnamon.org/applet.js index cbc06dd..96daa7c 100644 --- a/files/usr/share/cinnamon/applets/window-list@cinnamon.org/applet.js +++ b/files/usr/share/cinnamon/applets/window-list@cinnamon.org/applet.js @@ -11,8 +11,11 @@ const Meta = imports.gi.Meta; const Tooltips = imports.ui.tooltips; const DND = imports.ui.dnd; -const PANEL_ICON_SIZE = 24; +const PANEL_ICON_SIZE = 24; // this is for the spinner when loading +const DEFAULT_ICON_SIZE = 16; // too bad this can't be defined in theme (cinnamon-app.create_icon_texture returns a clutter actor, not a themable object - + // probably something that could be addressed const SPINNER_ANIMATION_TIME = 1; +const ICON_HEIGHT_FACTOR = .64; function AppMenuButtonRightClickMenu(actor, metaWindow, orientation) { @@ -29,7 +32,7 @@ AppMenuButtonRightClickMenu.prototype = { //Main.chrome.addActor(this.actor, { visibleInOverview: true, // affectsStruts: false }); this.actor.hide(); - + this.window_list = actor._delegate._applet._windows; actor.connect('key-press-event', Lang.bind(this, this._onSourceKeyPress)); this.connect('open-state-changed', Lang.bind(this, this._onToggled)); @@ -38,6 +41,12 @@ AppMenuButtonRightClickMenu.prototype = { this.itemCloseWindow = new PopupMenu.PopupMenuItem(_("Close")); this.itemCloseWindow.connect('activate', Lang.bind(this, this._onCloseWindowActivate)); + this.itemCloseAllWindows = new PopupMenu.PopupMenuItem(_("Close all")); + this.itemCloseAllWindows.connect('activate', Lang.bind(this, this._onCloseAllActivate)); + + this.itemCloseOtherWindows = new PopupMenu.PopupMenuItem(_("Close others")); + this.itemCloseOtherWindows.connect('activate', Lang.bind(this, this._onCloseOthersActivate)); + if (metaWindow.minimized) this.itemMinimizeWindow = new PopupMenu.PopupMenuItem(_("Restore")); else @@ -61,15 +70,21 @@ AppMenuButtonRightClickMenu.prototype = { this.addMenuItem(this.itemMoveToLeftWorkspace); this.addMenuItem(this.itemMoveToRightWorkspace); this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); + this.addMenuItem(this.itemCloseAllWindows); + this.addMenuItem(this.itemCloseOtherWindows); + this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); this.addMenuItem(this.itemMinimizeWindow); - this.addMenuItem(this.itemMaximizeWindow); + this.addMenuItem(this.itemMaximizeWindow); this.addMenuItem(this.itemCloseWindow); } else { - this.addMenuItem(this.itemCloseWindow); + this.addMenuItem(this.itemCloseWindow); this.addMenuItem(this.itemMaximizeWindow); this.addMenuItem(this.itemMinimizeWindow); this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); + this.addMenuItem(this.itemCloseOtherWindows); + this.addMenuItem(this.itemCloseAllWindows); + this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); this.addMenuItem(this.itemMoveToLeftWorkspace); this.addMenuItem(this.itemMoveToRightWorkspace); this.addMenuItem(this.itemOnAllWorkspaces); @@ -111,6 +126,28 @@ AppMenuButtonRightClickMenu.prototype = { this.destroy(); }, + _onCloseAllActivate: function(actor, event) { + let metas = new Array(); + for (let i = 0; i < this.window_list.length; i++) { + metas.push(this.window_list[i].metaWindow); + } + metas.forEach(Lang.bind(this, function(window) { + window.delete(global.get_current_time()); + })); + }, + + _onCloseOthersActivate: function(actor, event) { + let metas = new Array(); + for (let i = 0; i < this.window_list.length; i++) { + if (this.window_list[i].metaWindow != this.metaWindow) { + metas.push(this.window_list[i].metaWindow); + } + } + metas.forEach(Lang.bind(this, function(window) { + window.delete(global.get_current_time()); + })); + }, + _onMinimizeWindowActivate: function(actor, event){ if (this.metaWindow.minimized) { this.metaWindow.unminimize(global.get_current_time()); @@ -173,15 +210,15 @@ AppMenuButtonRightClickMenu.prototype = { }; -function AppMenuButton(applet, metaWindow, animation, orientation) { - this._init(applet, metaWindow, animation, orientation); +function AppMenuButton(applet, metaWindow, animation, orientation, panel_height) { + this._init(applet, metaWindow, animation, orientation, panel_height); } AppMenuButton.prototype = { // __proto__ : AppMenuButton.prototype, - _init: function(applet, metaWindow, animation, orientation) { + _init: function(applet, metaWindow, animation, orientation, panel_height) { this.actor = new St.Bin({ style_class: 'window-list-item-box', reactive: true, @@ -199,7 +236,7 @@ AppMenuButton.prototype = { this.actor.connect('button-release-event', Lang.bind(this, this._onButtonRelease)); this.metaWindow = metaWindow; - + this._applet = applet; let bin = new St.Bin({ name: 'appMenu' }); @@ -254,23 +291,25 @@ AppMenuButton.prototype = { this._container.add_actor(this._spinner.actor); this._spinner.actor.lower_bottom(); - let tracker = Cinnamon.WindowTracker.get_default(); - this.app = tracker.get_window_app(this.metaWindow); - let iconSize = 16; - let icon = this.app ? - this.app.create_icon_texture(iconSize) : - new St.Icon({ icon_name: 'application-default-icon', - icon_type: St.IconType.FULLCOLOR, - icon_size: iconSize }); + let tracker = Cinnamon.WindowTracker.get_default(); + this.app = tracker.get_window_app(this.metaWindow); + if (global.settings.get_boolean('panel-scale-text-icons') && global.settings.get_boolean('panel-resizable')) { + this.iconSize = Math.round(panel_height * ICON_HEIGHT_FACTOR); + } else { + this.iconSize = DEFAULT_ICON_SIZE; + } + let icon = this.app ? + this.app.create_icon_texture(this.iconSize) : + new St.Icon({ icon_name: 'application-default-icon', + icon_type: St.IconType.FULLCOLOR, + icon_size: this.iconSize }); let title = this.getDisplayTitle(); - if (metaWindow.minimized) this._label.set_text("[" + title + "]"); else this._label.set_text(title); this._iconBox.set_child(icon); - if(animation){ this.startAnimation(); this.stopAnimation(); @@ -290,12 +329,48 @@ AppMenuButton.prototype = { this.on_panel_edit_mode_changed(); global.settings.connect('changed::panel-edit-mode', Lang.bind(this, this.on_panel_edit_mode_changed)); + global.settings.connect('changed::window-list-applet-scroll', Lang.bind(this, this.on_scroll_mode_changed)); + this.window_list = this.actor._delegate._applet._windows; + this.scroll_connector = null; + this.on_scroll_mode_changed(); }, on_panel_edit_mode_changed: function() { this._draggable.inhibit = global.settings.get_boolean("panel-edit-mode"); }, - + + on_scroll_mode_changed: function() { + let scrollable = global.settings.get_boolean("window-list-applet-scroll"); + if (scrollable) { + this.scroll_connector = this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent)); + } else { + if (this.scroll_connector) { + this.actor.disconnect(this.scroll_connector); + this.scroll_connector = null; + } + } + }, + + _onScrollEvent: function(actor, event) { + let direction = event.get_scroll_direction(); + let current; + let num_windows = this.window_list.length; + for (let i = 0; i < num_windows; i++) { + if (this.window_list[i].metaWindow.has_focus()) { + current = i; + break; + } + } + let target; + if (direction == 1) { + target = ((current - 1) >= 0) ? (current - 1) : (num_windows - 1); + } + if (direction == 0) { + target = ((current + 1) <= num_windows - 1) ? (current + 1) : 0; + } + this.window_list[target].metaWindow.activate(global.get_current_time()); + }, + _onDragBegin: function() { this._tooltip.hide(); this._tooltip.preventShow = true; @@ -327,7 +402,7 @@ AppMenuButton.prototype = { let tracker = Cinnamon.WindowTracker.get_default(); let app = tracker.get_window_app(this.metaWindow); if ( app ) { - let icon = app.create_icon_texture(16); + let icon = app.create_icon_texture(this.iconSize); this._iconBox.set_child(icon); } if (this.metaWindow.has_focus() && !this.metaWindow.minimized) { @@ -483,7 +558,7 @@ AppMenuButton.prototype = { } this._iconBox.allocate(childBox, flags); - let iconWidth = 16; + let iconWidth = this.iconSize; [minWidth, minHeight, naturalWidth, naturalHeight] = this._label.get_preferred_size(); @@ -604,8 +679,6 @@ MyAppletBox.prototype = { this._clearDragPlaceholder(); actor.destroy(); - this._applet.saveWindowsOrder(); - return true; }, @@ -618,22 +691,20 @@ MyAppletBox.prototype = { } } -function MyApplet(orientation) { - this._init(orientation); +function MyApplet(orientation, panel_height) { + this._init(orientation, panel_height); } MyApplet.prototype = { __proto__: Applet.Applet.prototype, - _init: function(orientation) { - Applet.Applet.prototype._init.call(this, orientation); + _init: function(orientation, panel_height) { + Applet.Applet.prototype._init.call(this, orientation, panel_height); try { this.orientation = orientation; this.dragInProgress = false; - this._windows_order = {}; - this.myactorbox = new MyAppletBox(this); this.myactor = this.myactorbox.actor; @@ -654,12 +725,6 @@ MyApplet.prototype = { this.actor.set_style('margin-bottom: 0px;'); this.actor.set_style('padding-bottom: 0px;'); } - - - this._windows = new Array(); - - let tracker = Cinnamon.WindowTracker.get_default(); - tracker.connect('notify::focus-app', Lang.bind(this, this._onFocus)); this.isInteresting = function(metaWindow) { if (tracker.is_window_interesting(metaWindow)) { @@ -676,6 +741,10 @@ MyApplet.prototype = { return type === Meta.WindowType.DIALOG || type === Meta.WindowType.MODAL_DIALOG; }; + this._windows = new Array(); + + let tracker = Cinnamon.WindowTracker.get_default(); + tracker.connect('notify::focus-app', Lang.bind(this, this._onFocus)); this.switchWorkspaceHandler = global.window_manager.connect('switch-workspace', Lang.bind(this, this._refreshItems)); @@ -707,27 +776,21 @@ MyApplet.prototype = { global.screen.connect('notify::n-workspaces', Lang.bind(this, this._changeWorkspaces)); global.display.connect('window-demands-attention', Lang.bind(this, this._onWindowDemandsAttention)); + global.display.connect('window-marked-urgent', Lang.bind(this, this._onWindowDemandsAttention)); // this._container.connect('allocate', Lang.bind(Main.panel, this._allocateBoxes)); - global.settings.connect('changed::panel-edit-mode', Lang.bind(this, this.on_panel_edit_mode_changed)); + global.settings.connect('changed::panel-edit-mode', Lang.bind(this, this.on_panel_edit_mode_changed)); } catch (e) { global.logError(e); } }, - saveWindowsOrder: function() { - let order = []; - let children = this.myactor.get_children(); - for (var i in children) if (children[i]._delegate && children[i]._delegate.metaWindow) order.push(children[i]._delegate.metaWindow); - this._windows_order[global.screen.get_active_workspace()] = order; - }, - on_applet_clicked: function(event) { - }, - + }, + on_panel_edit_mode_changed: function() { this.actor.reactive = global.settings.get_boolean("panel-edit-mode"); }, @@ -746,41 +809,19 @@ MyApplet.prototype = { this._windows[i].doFocus(); } }, + + on_panel_height_changed: function() { + this._refreshItems(); + }, _refreshItems: function() { - /* "this.myactor.destroy_children()" produces mysterious warnings: - "Clutter-CRITICAL **: clutter_actor_unmap: assertion `CLUTTER_IS_ACTOR (self)' failed", - one for each child actor, so let's use a loop instead. */ - for ( let i = 0; i < this._windows.length; ++i ) { - this.myactor.remove_actor(this._windows[i].actor); - this._windows[i].actor.destroy(); - } - this._windows = new Array(); - - let metaWorkspace = global.screen.get_active_workspace(); - let windows = metaWorkspace.list_windows(); - windows.sort(Lang.bind(this, function(w1, w2) { - if (this._windows_order){ - let order = this._windows_order[metaWorkspace]; - if (order){ - let iw1 = order.indexOf(w1); - let iw2 = order.indexOf(w2); - if (iw1==-1) return 1; - else if (iw2==-1) return -1; - else return iw1 - iw2; - }else return w1.get_stable_sequence() - w2.get_stable_sequence; - }else return w1.get_stable_sequence() - w2.get_stable_sequence; - })); - - // Create list items for each window - for ( let i = 0; i < windows.length; ++i ) { - let metaWindow = windows[i]; - if (this.isInteresting(metaWindow)) { - let appbutton = new AppMenuButton(this, metaWindow, false, this.orientation); - this._windows.push(appbutton); - this.myactor.add(appbutton.actor); - } + let metaWindow = this._windows[i].metaWindow; + if (metaWindow.get_workspace().index() == global.screen.get_active_workspace_index() + || metaWindow.is_on_all_workspaces()) + this._windows[i].actor.show(); + else + this._windows[i].actor.hide(); } this._onFocus(); @@ -832,29 +873,22 @@ MyApplet.prototype = { }, _windowAdded: function(metaWorkspace, metaWindow) { - if ( metaWorkspace.index() != global.screen.get_active_workspace_index() ) { - return; - } - + if (!this.isInteresting(metaWindow)) + return; for ( let i=0; i 1 && !forceAppFocus) { this._thumbnailTimeoutId = Mainloop.timeout_add ( @@ -914,19 +927,20 @@ SwitcherList.prototype = { Signals.addSignalMethods(SwitcherList.prototype); -function AppIcon(app) { - this._init(app); +function AppIcon(window, showThumbnail) { + this._init(window, showThumbnail); } AppIcon.prototype = { - _init: function(window) { + _init: function(window, showThumbnail) { this.window = window; + this.showThumbnail = showThumbnail; let tracker = Cinnamon.WindowTracker.get_default(); this.app = tracker.get_window_app(window); this.actor = new St.BoxLayout({ style_class: 'alt-tab-app', vertical: true }); this.icon = null; - this._iconBin = new St.Bin({ x_fill: true, y_fill: true }); + this._iconBin = new St.Bin(); this.actor.add(this._iconBin, { x_fill: false, y_fill: false } ); let title = window.get_title(); @@ -943,11 +957,20 @@ AppIcon.prototype = { }, set_size: function(size) { - this.icon = this.app ? - this.app.create_icon_texture(size) : - new St.Icon({ icon_name: 'application-default-icon', - icon_type: St.IconType.FULLCOLOR, - icon_size: size }); + if (this.showThumbnail){ + let windowTexture = this.window.get_compositor_private().get_texture(); + let [width, height] = windowTexture.get_size(); + let scale = Math.min(size/Math.max(width, height), 1); + this.icon = new Clutter.Clone({source: windowTexture, + width: width * scale, + height: height * scale}); + } else { + this.icon = this.app ? + this.app.create_icon_texture(size) : + new St.Icon({ icon_name: 'application-default-icon', + icon_type: St.IconType.FULLCOLOR, + icon_size: size }); + } this._iconBin.set_size(size, size); this._iconBin.child = this.icon; } @@ -960,7 +983,7 @@ function AppSwitcher() { AppSwitcher.prototype = { __proto__ : SwitcherList.prototype, - _init : function(windows, altTabPopup) { + _init : function(windows, showThumbnails, altTabPopup) { SwitcherList.prototype._init.call(this, true); // Construct the AppIcons, add to the popup @@ -968,7 +991,7 @@ AppSwitcher.prototype = { let workspaceIcons = []; let otherIcons = []; for (let i = 0; i < windows.length; i++) { - let appIcon = new AppIcon(windows[i]); + let appIcon = new AppIcon(windows[i], showThumbnails); // Cache the window list now; we don't handle dynamic changes here, // and we don't want to be continually retrieving it appIcon.cachedWindows = [windows[i]]; @@ -1084,8 +1107,8 @@ AppSwitcher.prototype = { SwitcherList.prototype.highlight.call(this, n, justOutline); this._curApp = n; - - if (this._curApp != -1) { + + if (this._curApp != -1 && this._altTabPopup._thumbnailsEnabled && this._altTabPopup._iconsEnabled) { this._arrows[this._curApp].show(); } }, diff --git a/js/ui/applet.js b/js/ui/applet.js index 0ba9726..0e612b7 100644 --- a/js/ui/applet.js +++ b/js/ui/applet.js @@ -11,6 +11,11 @@ const Gtk = imports.gi.Gtk; const Util = imports.misc.util; const Pango = imports.gi.Pango; +const COLOR_ICON_HEIGHT_FACTOR = .875; // Panel height factor for normal color icons +const PANEL_FONT_DEFAULT_HEIGHT = 11.5; // px +const PANEL_SYMBOLIC_ICON_DEFAULT_HEIGHT = 1.14 * PANEL_FONT_DEFAULT_HEIGHT; // ems conversion +const DEFAULT_PANEL_HEIGHT = 25; + function MenuItem(label, icon, callback) { this._init(label, icon, callback); } @@ -20,6 +25,10 @@ MenuItem.prototype = { _init: function(text, icon, callback) { PopupMenu.PopupBaseMenuItem.prototype._init.call(this); + + this._text = text; + this._icon = icon; + this._callback = callback; this.icon = new St.Icon({ icon_name: icon, icon_type: St.IconType.FULLCOLOR, @@ -29,6 +38,10 @@ MenuItem.prototype = { this.addActor(this.label); this.connect('activate', callback); + }, + + clone: function(){ + return new MenuItem(this._text, this._icon, this._callback); } }; @@ -67,13 +80,13 @@ AppletPopupMenu.prototype = { } } -function Applet(orientation) { - this._init(orientation); +function Applet(orientation, panel_height) { + this._init(orientation, panel_height); } Applet.prototype = { - _init: function(orientation) { + _init: function(orientation, panel_height) { this.actor = new St.BoxLayout({ style_class: 'applet-box', reactive: true, track_hover: true }); this._applet_tooltip = new Tooltips.PanelItemTooltip(this, "", orientation); this.actor.connect('button-release-event', Lang.bind(this, this._onButtonReleaseEvent)); @@ -88,6 +101,7 @@ Applet.prototype = { this._newOrder = null; // Used when moving an applet this._panelLocation = null; // Backlink to the panel location our applet is in, set by Cinnamon. this._newPanelLocation = null; // Used when moving an applet + this._panelHeight = panel_height ? panel_height : 25; this._uuid = null; // Defined in gsettings, set by Cinnamon. this._hook = null; // Defined in metadata.json, set by appletManager this._dragging = false; @@ -96,10 +110,17 @@ Applet.prototype = { this._draggable.connect('drag-cancelled', Lang.bind(this, this._onDragCancelled)); this._draggable.connect('drag-end', Lang.bind(this, this._onDragEnd)); - this._applet_tooltip_text = ""; - - this._setAppletReactivity(); - global.settings.connect('changed::panel-edit-mode', Lang.bind(this, this._setAppletReactivity)); + this._scaleMode = false; + this._applet_tooltip_text = ""; + this._scaleMode = global.settings.get_boolean('panel-scale-text-icons') && global.settings.get_boolean('panel-resizable'); + this.context_menu_item_remove = null; + this.context_menu_separator = null; + + this._setAppletReactivity(); + global.settings.connect('changed::panel-edit-mode', Lang.bind(this, function() { + this._setAppletReactivity(); + this.finalizeContextMenu(); + })); }, _setAppletReactivity: function() { @@ -159,7 +180,24 @@ Applet.prototype = { on_applet_added_to_panel: function() { }, + on_applet_removed_from_panel: function() { + // Implemented by Applets, called by appletManager + // handles things that might cause a crash once the applet is + // no longer on the stage + }, + setOrientation: function (orientation) { + let menuItems = new Array(); + let oldMenuItems = this._applet_context_menu._getMenuItems(); + for (var i in oldMenuItems){ + if (oldMenuItems[i] instanceof MenuItem) { // in case some applets don't use the standards + if (oldMenuItems[i] !== this.context_menu_separator && oldMenuItems[i] !== this.context_menu_item_remove) { + menuItems.push(oldMenuItems[i].clone()); + } + } + } + this._menuManager.removeMenu(this._applet_context_menu); + this._applet_tooltip.destroy(); this._applet_tooltip = new Tooltips.PanelItemTooltip(this, this._applet_tooltip_text, orientation); @@ -168,6 +206,10 @@ Applet.prototype = { this._menuManager.addMenu(this._applet_context_menu); this.on_orientation_changed(orientation); + + if (this._applet_context_menu.numMenuItems == 0){ // Do not recreate the menu if the applet already handles it in on_orientation_changed + for (var i in menuItems) this._applet_context_menu.addMenuItem(menuItems[i]); + } this.finalizeContextMenu(); }, @@ -175,39 +217,79 @@ Applet.prototype = { on_orientation_changed: function(event) { // Implemented by Applets }, + + setPanelHeight: function (panel_height) { + if (panel_height && panel_height > 0) { + this._panelHeight = panel_height; + } + this.on_panel_height_changed(); + }, + + on_panel_height_changed: function() { + // Implemented byApplets + }, finalizeContextMenu: function () { - // Add default context menus - if (this._applet_context_menu._getMenuItems().length > 0) { - this._applet_context_menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); + // Add default context menus if we're in panel edit mode, ensure their removal if we're not + let isEditMode = global.settings.get_boolean('panel-edit-mode'); + let items = this._applet_context_menu._getMenuItems(); + if (isEditMode && items.indexOf(this.context_menu_item_remove) == -1) { + this.context_menu_item_remove = new MenuItem(_("Remove this applet"), Gtk.STOCK_REMOVE, Lang.bind(null, AppletManager._removeAppletFromPanel, this._uuid)); + this.context_menu_separator = new PopupMenu.PopupSeparatorMenuItem(); + if (this._applet_context_menu._getMenuItems().length > 0) { + this._applet_context_menu.addMenuItem(this.context_menu_separator); + } + this._applet_context_menu.addMenuItem(this.context_menu_item_remove); + } else { + if (items.indexOf(this.context_menu_separator) != -1) { + this.context_menu_separator.destroy(); + this.context_menu_separator = null; + } + if (items.indexOf(this.context_menu_item_remove) != -1) { + this.context_menu_item_remove.destroy(); + this.context_menu_item_remove = null; + } } - let context_menu_item_remove = new MenuItem(_("Remove this applet"), Gtk.STOCK_REMOVE, Lang.bind(null, AppletManager._removeAppletFromPanel, this._uuid)); - this._applet_context_menu.addMenuItem(context_menu_item_remove); }, - }; -function IconApplet(orientation) { - this._init(orientation); +function IconApplet(orientation, panel_height) { + this._init(orientation, panel_height); } IconApplet.prototype = { __proto__: Applet.prototype, - _init: function(orientation) { - Applet.prototype._init.call(this, orientation); + _init: function(orientation, panel_height) { + Applet.prototype._init.call(this, orientation, panel_height); this._applet_icon_box = new St.Bin(); - this.actor.add(this._applet_icon_box, { y_align: St.Align.MIDDLE, y_fill: false }); + this.actor.add(this._applet_icon_box, { y_align: St.Align.MIDDLE, y_fill: false }); + this.__icon_type = null; + this.__icon_name = null; }, set_applet_icon_name: function (icon_name) { - this._applet_icon = new St.Icon({icon_name: icon_name, icon_size: 22, icon_type: St.IconType.FULLCOLOR, reactive: true, track_hover: true, style_class: 'applet-icon' }); + if (this._scaleMode) { + this._applet_icon = new St.Icon({icon_name: icon_name, icon_size: this._panelHeight * COLOR_ICON_HEIGHT_FACTOR, + icon_type: St.IconType.FULLCOLOR, reactive: true, track_hover: true, style_class: 'applet-icon' }); + } else { + this._applet_icon = new St.Icon({icon_name: icon_name, icon_size: 22, icon_type: St.IconType.FULLCOLOR, reactive: true, track_hover: true, style_class: 'applet-icon' }); + } this._applet_icon_box.child = this._applet_icon; + this.__icon_type = St.IconType.FULLCOLOR; + this.__icon_name = icon_name; }, set_applet_icon_symbolic_name: function (icon_name) { - this._applet_icon = new St.Icon({icon_name: icon_name, icon_type: St.IconType.SYMBOLIC, reactive: true, track_hover: true, style_class: 'system-status-icon' }); + if (this._scaleMode) { + let height = (this._panelHeight / DEFAULT_PANEL_HEIGHT) * PANEL_SYMBOLIC_ICON_DEFAULT_HEIGHT; + this._applet_icon = new St.Icon({icon_name: icon_name, icon_size: height, icon_type: St.IconType.SYMBOLIC, reactive: true, track_hover: true, style_class: 'system-status-icon' }); + } else { + this._applet_icon = new St.Icon({icon_name: icon_name, icon_type: St.IconType.SYMBOLIC, reactive: true, track_hover: true, style_class: 'system-status-icon' }); + } this._applet_icon_box.child = this._applet_icon; + this.__icon_type = St.IconType.SYMBOLIC; + this.__icon_name = icon_name; }, set_applet_icon_path: function (icon_path) { @@ -216,51 +298,79 @@ IconApplet.prototype = { if (icon_path){ let file = Gio.file_new_for_path(icon_path); let icon_uri = file.get_uri(); - this._applet_icon = St.TextureCache.get_default().load_uri_async(icon_uri, 22, 22); + let square_size = 22; + if (this._scaleMode) { + square_size = Math.floor(this._panelHeight * COLOR_ICON_HEIGHT_FACTOR); + } + this._applet_icon = St.TextureCache.get_default().load_uri_async(icon_uri, square_size, square_size); this._applet_icon_box.child = this._applet_icon; } + this.__icon_type = -1; + this.__icon_name = icon_path; }, + + on_panel_height_changed: function() { + this._scaleMode = global.settings.get_boolean('panel-scale-text-icons') && global.settings.get_boolean('panel-resizable'); + if (this._applet_icon_box.child) { + this._applet_icon_box.child.destroy(); + } + switch (this.__icon_type) { + case St.IconType.FULLCOLOR: + this.set_applet_icon_name(this.__icon_name); + break; + case St.IconType.SYMBOLIC: + this.set_applet_icon_symbolic_name(this.__icon_name); + break; + case -1: + this.set_applet_icon_path(this.__icon_name); + break; + default: + break; + } + } }; -function TextApplet(orientation) { - this._init(orientation); +function TextApplet(orientation, panel_height) { + this._init(orientation, panel_height); } TextApplet.prototype = { __proto__: Applet.prototype, - _init: function(orientation) { - Applet.prototype._init.call(this, orientation); - this._applet_label = new St.Label({ reactive: true, track_hover: true, style_class: 'applet-label'}); - this._applet_label.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; + _init: function(orientation, panel_height) { + Applet.prototype._init.call(this, orientation, panel_height); + this._applet_label = new St.Label({ reactive: true, track_hover: true, style_class: 'applet-label'}); + this._label_height = (this._panelHeight / DEFAULT_PANEL_HEIGHT) * PANEL_FONT_DEFAULT_HEIGHT; + this._applet_label.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; this.actor.add(this._applet_label, { y_align: St.Align.MIDDLE, y_fill: false }); }, set_applet_label: function (text) { - this._applet_label.set_text(text); - }, + this._applet_label.clutter_text.set_text(text); + }, on_applet_added_to_panel: function() { } }; -function TextIconApplet(orientation) { - this._init(orientation); +function TextIconApplet(orientation, panel_height) { + this._init(orientation, panel_height); } TextIconApplet.prototype = { __proto__: IconApplet.prototype, - _init: function(orientation) { - IconApplet.prototype._init.call(this, orientation); - this._applet_label = new St.Label({ reactive: true, track_hover: true, style_class: 'applet-label'}); + _init: function(orientation, panel_height) { + IconApplet.prototype._init.call(this, orientation, panel_height); + this._applet_label = new St.Label({ reactive: true, track_hover: true, style_class: 'applet-label'}); + this._label_height = (this._panelHeight / DEFAULT_PANEL_HEIGHT) * PANEL_FONT_DEFAULT_HEIGHT; this._applet_label.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; this.actor.add(this._applet_label, { y_align: St.Align.MIDDLE, y_fill: false }); }, set_applet_label: function (text) { - this._applet_label.set_text(text); + this._applet_label.clutter_text.set_text(text); }, hide_applet_icon: function () { @@ -269,5 +379,5 @@ TextIconApplet.prototype = { on_applet_added_to_panel: function() { - } + } }; diff --git a/js/ui/appletManager.js b/js/ui/appletManager.js index 3333a70..da22bb6 100644 --- a/js/ui/appletManager.js +++ b/js/ui/appletManager.js @@ -85,7 +85,13 @@ function onEnabledAppletsChanged() { // Applet was removed let directory = _find_applet(uuid); if (directory != null) { - let applet = loadApplet(uuid, directory, orientation); + let applet = loadApplet(uuid, directory, orientation, panel.actor.get_height()); + try { + applet.on_applet_removed_from_panel(); + } catch (e) { + global.logError("Problem with applet: " + uuid + + " on_applet_removed_from_panel method: " + e); + } if (applet._panelLocation != null) { applet._panelLocation.remove_actor(applet.actor); applet._panelLocation = null; @@ -104,7 +110,7 @@ function onEnabledAppletsChanged() { catch(e) { global.logError('Failed to refresh list of applets ' + e); } - + Main.statusIconDispatcher.redisplay(); } @@ -155,7 +161,7 @@ function add_applet_to_panels(appletDefinition) { let directory = _find_applet(uuid); if (directory != null) { // Load the applet - let applet = loadApplet(uuid, directory, orientation); + let applet = loadApplet(uuid, directory, orientation, panel.actor.get_height()); applet._order = order; // Remove it from its previous panel location (if it had one) @@ -186,7 +192,7 @@ function add_applet_to_panels(appletDefinition) { location.add(applet.actor); } - applet._panelLocation = location; + applet._panelLocation = location; for (let i=0; i= 0; --i) { // start with bottom-most - let window = this._windows[i]; + let windows = this._windows.slice(); + windows.reverse(); // top-to-bottom order + for (let i = 0; i < windows.length; i++){ + let window = windows[i]; if (!window.origSet) { window.origX = window.actor.x; window.origY = window.actor.y; @@ -576,8 +545,8 @@ ExpoWorkspaceThumbnail.prototype = { } if ((window.metaWindow.maximized_horizontally && window.metaWindow.maximized_vertically) || window.metaWindow.get_layer() == Meta.StackLayer.FULLSCREEN){ - window.origX = 0; - window.origY = 0; + window.origX = this._porthole.x; + window.origY = this._porthole.y } if (row == nRows) @@ -585,8 +554,8 @@ ExpoWorkspaceThumbnail.prototype = { let scale = Math.min((maxWindowWidth / window.actor.width), (maxWindowHeight / window.actor.height)); scale = Math.min(1, scale); - let x = offset + (spacing * col) + (maxWindowWidth * (col - 1)) + ((maxWindowWidth - (window.actor.width * scale)) / 2); - let y = (spacing * row) + (maxWindowHeight * (row - 1)) + ((maxWindowHeight - (window.actor.height * scale)) / 2); + let x = this._porthole.x + offset + (spacing * col) + (maxWindowWidth * (col - 1)) + ((maxWindowWidth - (window.actor.width * scale)) / 2); + let y = this._porthole.y + (spacing * row) + (maxWindowHeight * (row - 1)) + ((maxWindowHeight - (window.actor.height * scale)) / 2); if (!window.metaWindow.showing_on_its_workspace()) { window.actor.set_position(window.icon.x, window.icon.y); @@ -699,7 +668,6 @@ ExpoWorkspaceThumbnail.prototype = { this.emit('remove-event'); Main._removeWorkspace(this.metaWorkspace); this.removed = true; - return true; }, // Draggable target interface @@ -740,6 +708,9 @@ ExpoWorkspaceThumbnail.prototype = { false, // don't create workspace time); + // normal hovering monitoring was turned off during drag + this.hovering = true; + this._overviewModeOn(); return true; } @@ -800,6 +771,9 @@ ExpoThumbnailsBox.prototype = { global.window_manager.connect('switch-workspace', Lang.bind(this, this._activeWorkspaceChanged)); + this._nWorkspacesChangedId = global.screen.connect('notify::n-workspaces', + Lang.bind(this, this._workspacesChanged)); + this._targetScale = 0; this._scale = 0; this._pendingScaleUpdate = false; @@ -810,35 +784,16 @@ ExpoThumbnailsBox.prototype = { this._stateCounts[ThumbnailState[key]] = 0; // The "porthole" is the portion of the screen that we show in the workspaces - let panelHeight = Main.panel.actor.height; - let monitor = Main.layoutManager.primaryMonitor; - let autohide = global.settings.get_boolean("panel-autohide"); - let desktop_layout = global.settings.get_string("desktop-layout"); - let portholeY = null; - let portholeHeight = null; - if (autohide){ - portholeY = 0; - portholeHeight = monitor.height; - } else if (desktop_layout == "traditional"){ - portholeY = 0; - portholeHeight = monitor.height - panelHeight; - } else if (desktop_layout == "flipped"){ - portholeY = panelHeight; - portholeHeight = monitor.height - panelHeight; - } else { - portholeY = panelHeight; - portholeHeight = monitor.height - (panelHeight * 2); - } - this._porthole = { - x: monitor.x, - y: portholeY, - width: monitor.width, - height: portholeHeight - }; + this._porthole = Main.layoutManager.getPorthole(); this.addThumbnails(0, global.screen.n_workspaces); this.button.raise_top(); + this.restackedNotifyId = + global.screen.connect('restacked', + Lang.bind(this, this.restack)); + this.restack(); + // apparently we get no direct call to show the initial // view, so we must force an explicit overviewModeOff display for (let i = 0; i < this._thumbnails.length; ++i) { @@ -935,14 +890,35 @@ ExpoThumbnailsBox.prototype = { return true; // handled }, + restack: function() { + let stack = global.get_window_actors(); + let stackIndices = {}; + + for (let i = 0; i < stack.length; i++) { + // Use the stable sequence for an integer to use as a hash key + stackIndices[stack[i].get_meta_window().get_stable_sequence()] = i; + } + + this.syncStacking(stackIndices); + }, + hide: function() { + if (this.restackedNotifyId > 0){ + global.screen.disconnect(this.restackedNotifyId); + this.restackedNotifyId = 0; + } if (this._switchWorkspaceNotifyId > 0) { global.window_manager.disconnect(this._switchWorkspaceNotifyId); this._switchWorkspaceNotifyId = 0; } + if (this._nWorkspacesChangedId > 0){ + global.screen.disconnect(this._nWorkspacesChangedId); + this._nWorkspacesChangedId = 0; + } - for (let w = 0; w < this._thumbnails.length; w++) + for (let w = 0; w < this._thumbnails.length; w++) { this._thumbnails[w].destroy(); + } this._thumbnails = []; }, @@ -962,9 +938,9 @@ ExpoThumbnailsBox.prototype = { } for (let k = start; k < start + count; k++) { let metaWorkspace = global.screen.get_workspace_by_index(k); - let thumbnail = new ExpoWorkspaceThumbnail(metaWorkspace); - thumbnail.setPorthole(this._porthole.x, this._porthole.y, - this._porthole.width, this._porthole.height); + let thumbnail = new ExpoWorkspaceThumbnail(metaWorkspace, this); + thumbnail.setPorthole(this._porthole); + this._thumbnails.push(thumbnail); if (metaWorkspace == global.screen.get_active_workspace()) { this._lastActiveWorkspace = thumbnail; @@ -1188,7 +1164,12 @@ ExpoThumbnailsBox.prototype = { let asGrid = global.settings.get_boolean("workspace-expo-view-as-grid"); let nColumns = asGrid ? Math.ceil(Math.sqrt(nWorkspaces)) : nWorkspaces; let nRows = Math.ceil(nWorkspaces/nColumns); - return [nColumns, nRows]; + let ratio = Math.floor(this._porthole.width / this._porthole.height); + if (nWorkspaces <= ratio) { + return [1, nWorkspaces]; + } else { + return [nColumns, nRows]; + } }, _getPreferredHeight: function(actor, forWidth, alloc) { @@ -1379,6 +1360,34 @@ ExpoThumbnailsBox.prototype = { this._lastActiveWorkspace.emit('allocated'); }, + _workspacesChanged: function() { + let oldNumWorkspaces = this._thumbnails.length; + let newNumWorkspaces = global.screen.n_workspaces; + let active = global.screen.get_active_workspace_index(); + + if (oldNumWorkspaces == newNumWorkspaces) + return; + if (newNumWorkspaces > oldNumWorkspaces) { + // Assume workspaces are only added at the end + this.addThumbnails(oldNumWorkspaces, newNumWorkspaces - oldNumWorkspaces); + } else { + // Assume workspaces are only removed sequentially + // (e.g. 2,3,4 - not 2,4,7) + let removedIndex = -1; + let removedNum = oldNumWorkspaces - newNumWorkspaces; + for (let w = 0; w < oldNumWorkspaces; w++) { + let metaWorkspace = global.screen.get_workspace_by_index(w); + if (this._thumbnails[w].metaWorkspace != metaWorkspace) { + removedIndex = w; + break; + } + } + if (removedIndex >= 0) { + this.removeThumbnails(removedIndex, removedNum); + } + } + }, + _activeWorkspaceChanged: function(wm, from, to, direction) { this._thumbnails[this._kbThumbnailIndex].showKeyboardSelectedState(false); this._kbThumbnailIndex = global.screen.get_active_workspace_index(); diff --git a/js/ui/expoView.js b/js/ui/expoView.js index b1f81ba..4a51fe2 100644 --- a/js/ui/expoView.js +++ b/js/ui/expoView.js @@ -8,7 +8,6 @@ const Signals = imports.signals; const DND = imports.ui.dnd; const Main = imports.ui.main; -const Workspace = imports.ui.workspace; const ExpoThumbnail = imports.ui.expoThumbnail; const WORKSPACE_SWITCH_TIME = 0.25; @@ -38,20 +37,12 @@ ExpoView.prototype = { controls.connect('scroll-event', Lang.bind(this, this._onScrollEvent)); - this._monitorIndex = Main.layoutManager.primaryIndex; - this._thumbnailsBox = new ExpoThumbnail.ExpoThumbnailsBox(); controls.add_actor(this._thumbnailsBox.actor); this._inDrag = false; this._cancelledDrag = false; - this._switchWorkspaceNotifyId = 0; - - this._nWorkspacesChangedId = 0; - this._itemDragBeginId = 0; - this._itemDragCancelledId = 0; - this._itemDragEndId = 0; this._windowDragBeginId = 0; this._windowDragCancelledId = 0; this._windowDragEndId = 0; @@ -65,19 +56,6 @@ ExpoView.prototype = { this._controls.show(); this._thumbnailsBox.show(); - this._workspaces = []; - for (let i = 0; i < global.screen.n_workspaces; i++) { - let metaWorkspace = global.screen.get_workspace_by_index(i); - this._workspaces[i] = new Workspace.Workspace(metaWorkspace, this._monitorIndex); - } - - this._restackedNotifyId = - global.screen.connect('restacked', - Lang.bind(this, this._onRestacked)); - - if (this._nWorkspacesChangedId == 0) - this._nWorkspacesChangedId = global.screen.connect('notify::n-workspaces', - Lang.bind(this, this._workspacesChanged)); if (this._windowDragBeginId == 0) this._windowDragBeginId = Main.expo.connect('window-drag-begin', Lang.bind(this, this._dragBegin)); @@ -88,33 +66,12 @@ ExpoView.prototype = { this._windowDragEndId = Main.expo.connect('window-drag-end', Lang.bind(this, this._dragEnd)); - this._onRestacked(); }, hide: function() { this._controls.hide(); this._thumbnailsBox.hide(); - if (this._restackedNotifyId > 0){ - global.screen.disconnect(this._restackedNotifyId); - this._restackedNotifyId = 0; - } - if (this._nWorkspacesChangedId > 0){ - global.screen.disconnect(this._nWorkspacesChangedId); - this._nWorkspacesChangedId = 0; - } - if (this._itemDragBeginId > 0) { - Main.expo.disconnect(this._itemDragBeginId); - this._itemDragBeginId = 0; - } - if (this._itemDragCancelledId > 0) { - Main.expo.disconnect(this._itemDragCancelledId); - this._itemDragCancelledId = 0; - } - if (this._itemDragEndId > 0) { - Main.expo.disconnect(this._itemDragEndId); - this._itemDragEndId = 0; - } if (this._windowDragBeginId > 0) { Main.expo.disconnect(this._windowDragBeginId); this._windowDragBeginId = 0; @@ -127,11 +84,6 @@ ExpoView.prototype = { Main.expo.disconnect(this._windowDragEndId); this._windowDragEndId = 0; } - - for (let w = 0; w < this._workspaces.length; w++) { - this._workspaces[w].disconnectAll(); - this._workspaces[w].destroy(); - } }, _getPreferredWidth: function (actor, forHeight, alloc) { @@ -148,59 +100,6 @@ ExpoView.prototype = { this._controls.allocate(box, flags); }, - _onRestacked: function() { - let stack = Main.getTabList(); - let stackIndices = {}; - - for (let i = 0; i < stack.length; i++) { - // Use the stable sequence for an integer to use as a hash key - stackIndices[stack[i].get_stable_sequence()] = stack.length - i; - } - - this._thumbnailsBox.syncStacking(stackIndices); - }, - - _workspacesChanged: function() { - let oldNumWorkspaces = this._workspaces.length; - let newNumWorkspaces = global.screen.n_workspaces; - let active = global.screen.get_active_workspace_index(); - - if (oldNumWorkspaces == newNumWorkspaces) - return; - let lostWorkspaces = []; - if (newNumWorkspaces > oldNumWorkspaces) { - // Assume workspaces are only added at the end - for (let w = oldNumWorkspaces; w < newNumWorkspaces; w++) { - let metaWorkspace = global.screen.get_workspace_by_index(w); - this._workspaces[w] = new Workspace.Workspace(metaWorkspace, this._monitorIndex); - } - - this._thumbnailsBox.addThumbnails(oldNumWorkspaces, newNumWorkspaces - oldNumWorkspaces); - } else { - // Assume workspaces are only removed sequentially - // (e.g. 2,3,4 - not 2,4,7) - let removedIndex; - let removedNum = oldNumWorkspaces - newNumWorkspaces; - for (let w = 0; w < oldNumWorkspaces; w++) { - let metaWorkspace = global.screen.get_workspace_by_index(w); - if (this._workspaces[w].metaWorkspace != metaWorkspace) { - removedIndex = w; - break; - } - } - - lostWorkspaces = this._workspaces.splice(removedIndex, - removedNum); - - for (let l = 0; l < lostWorkspaces.length; l++) { - lostWorkspaces[l].disconnectAll(); - lostWorkspaces[l].destroy(); - } - - this._thumbnailsBox.removeThumbnails(removedIndex, removedNum); - } - }, - _dragBegin: function() { this._inDrag = true; this._cancelledDrag = false; @@ -226,7 +125,6 @@ ExpoView.prototype = { _dragEnd: function() { this._inDrag = false; DND.removeDragMonitor(this._dragMonitor); - }, _onScrollEvent: function (actor, event) { diff --git a/js/ui/flashspot.js b/js/ui/flashspot.js new file mode 100644 index 0000000..a4692f2 --- /dev/null +++ b/js/ui/flashspot.js @@ -0,0 +1,49 @@ +// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- + +const Lang = imports.lang; + +const Lightbox = imports.ui.lightbox; +const Main = imports.ui.main; +const Tweener = imports.ui.tweener; + +const FLASHSPOT_ANIMATION_TIME = 0.15; // seconds + +function Flashspot(area) { + this._init(area); +} +Flashspot.prototype = { + __proto__: Lightbox.Lightbox.prototype, + + _init: function(area) { + Lightbox.Lightbox.prototype._init.call(this, Main.uiGroup, { + inhibitEvents: true, + width: area.width, + height: area.height + }); + this.actor.style_class = 'flashspot'; + this.actor.set_position(area.x, area.y); + }, + + fire: function() { + this.actor.opacity = 0; + Tweener.addTween(this.actor, + { opacity: 255, + time: FLASHSPOT_ANIMATION_TIME, + transition: 'linear', + onComplete: Lang.bind(this, this._onFireShowComplete) + }); + this.actor.show(); + }, + + _onFireShowComplete: function() { + Tweener.addTween(this.actor, + { opacity: 0, + time: FLASHSPOT_ANIMATION_TIME, + transition: 'linear', + onComplete: Lang.bind(this, function() { + this.destroy(); + }) + }); + } +}; + diff --git a/js/ui/layout.js b/js/ui/layout.js index 14cd6b7..9e1ec52 100644 --- a/js/ui/layout.js +++ b/js/ui/layout.js @@ -44,17 +44,11 @@ LayoutManager.prototype = { vertical: true }); this.panelBox2 = new St.BoxLayout({ name: 'panelBox', - vertical: true }); - - let autohide = global.settings.get_boolean("panel-autohide"); - if (autohide) { - this.addChrome(this.panelBox, { affectsStruts: false, addToWindowgroup: false }); - this.addChrome(this.panelBox2, { affectsStruts: false, addToWindowgroup: false }); - } - else { - this.addChrome(this.panelBox, { affectsStruts: true, addToWindowgroup: false }); - this.addChrome(this.panelBox2, { affectsStruts: true, addToWindowgroup: false }); - } + vertical: true }); + + this.addChrome(this.panelBox, { addToWindowgroup: false }); + this.addChrome(this.panelBox2, { addToWindowgroup: false }); + this._processPanelSettings(); this.panelBox.connect('allocation-changed', Lang.bind(this, this._updatePanelBarriers)); this.panelBox2.connect('allocation-changed', @@ -74,7 +68,10 @@ LayoutManager.prototype = { this._chrome.addActor(this._hotCorner.actor); this.enabledEdgeFlip = global.settings.get_boolean("enable-edge-flip"); global.settings.connect("changed::enable-edge-flip", Lang.bind(this, this._onEnableEdgeFlipChanged)); - global.settings.connect("changed::panel-autohide", Lang.bind(this, this._onPanelAutoHideChanged)); + global.settings.connect("changed::panel-autohide", Lang.bind(this, this._processPanelSettings)); + global.settings.connect("changed::panel-resizable", Lang.bind(this, this._processPanelSettings)); + global.settings.connect("changed::panel-bottom-height", Lang.bind(this, this._processPanelSettings)); + global.settings.connect("changed::panel-top-height", Lang.bind(this, this._processPanelSettings)); global.settings.connect("changed::overview-corner-visible", Lang.bind(this, this._onOverviewCornerVisibleChanged)); global.settings.connect("changed::overview-corner-hover", Lang.bind(this, this._onOverviewCornerHoverChanged)); global.settings.connect("changed::overview-corner-position", Lang.bind(this, this._updateBoxes)); @@ -85,7 +82,7 @@ LayoutManager.prototype = { }, _onEnableEdgeFlipChanged: function(){ - this.enableEdgeFlip = global.settings.get_boolean("enable-edge-flip"); + this.enabledEdgeFlip = global.settings.get_boolean("enable-edge-flip"); this.edgeRight.enabled = this.enabledEdgeFlip; this.edgeLeft.enabled = this.enabledEdgeFlip; }, @@ -120,16 +117,17 @@ LayoutManager.prototype = { } }, - _onPanelAutoHideChanged: function() { - let autohide = global.settings.get_boolean("panel-autohide"); - if (autohide) { - this._chrome.modifyActorParams(this.panelBox, { affectsStruts: false }); - this._chrome.modifyActorParams(this.panelBox2, { affectsStruts: false }); - } - else { - this._chrome.modifyActorParams(this.panelBox, { affectsStruts: true }); - this._chrome.modifyActorParams(this.panelBox2, { affectsStruts: true }); + _processPanelSettings: function() { + if (this._processPanelSettingsTimeout) { + Mainloop.source_remove(this._processPanelSettingsTimeout); } + // delay this action somewhat, to let others do their thing before us + this._processPanelSettingsTimeout = Mainloop.timeout_add(0, Lang.bind(this, function() { + this._processPanelSettingsTimeout = 0; + this._updateBoxes(); + this._chrome.modifyActorParams(this.panelBox, { affectsStruts: Main.panel && !Main.panel.isHideable() }); + this._chrome.modifyActorParams(this.panelBox2, { affectsStruts: Main.panel2 && !Main.panel2.isHideable() }); + })); }, _onOverviewCornerVisibleChanged: function() { @@ -175,74 +173,26 @@ LayoutManager.prototype = { }, _updateHotCorners: function() { - // destroy old hot corners - for (let i = 0; i < this._hotCorners.length; i++) - this._hotCorners[i].destroy(); - this._hotCorners = []; - - // build new hot corners - for (let i = 0; i < this.monitors.length; i++) { - if (i == this.primaryIndex) - continue; - - let monitor = this.monitors[i]; - let cornerX = this._rtl ? monitor.x + monitor.width : monitor.x; - let cornerY = monitor.y; - - let haveTopLeftCorner = true; - - // Check if we have a top left (right for RTL) corner. - // I.e. if there is no monitor directly above or to the left(right) - let besideX = this._rtl ? monitor.x + 1 : cornerX - 1; - let besideY = cornerY; - let aboveX = cornerX; - let aboveY = cornerY - 1; - - for (let j = 0; j < this.monitors.length; j++) { - if (i == j) - continue; - let otherMonitor = this.monitors[j]; - if (besideX >= otherMonitor.x && - besideX < otherMonitor.x + otherMonitor.width && - besideY >= otherMonitor.y && - besideY < otherMonitor.y + otherMonitor.height) { - haveTopLeftCorner = false; - break; - } - if (aboveX >= otherMonitor.x && - aboveX < otherMonitor.x + otherMonitor.width && - aboveY >= otherMonitor.y && - aboveY < otherMonitor.y + otherMonitor.height) { - haveTopLeftCorner = false; - break; - } - } - - if (!haveTopLeftCorner) - continue; - - let _hotCorner = new HotCorner(); - this._hotCorners.push(_hotCorner); - _hotCorner.actor.set_position(cornerX, cornerY); - this._chrome.addActor(_hotCorner.actor); - } - }, - - _updateBoxes: function() { let hotCornerPosition = global.settings.get_string("overview-corner-position"); + let x = this.primaryMonitor.x; + let y = this.primaryMonitor.y; if (hotCornerPosition == "topLeft") { - this._hotCorner.actor.set_position(this.primaryMonitor.x,this.primaryMonitor.y); - this.overviewCorner.set_position(this.primaryMonitor.x + 1, this.primaryMonitor.y + 1); + this._hotCorner.actor.set_position(x, y); + this.overviewCorner.set_position(x + 1, y + 1); } else if (hotCornerPosition == "topRight") { - this._hotCorner.actor.set_position(this.primaryMonitor.width - 1,this.primaryMonitor.y); - this.overviewCorner.set_position(this.primaryMonitor.width - 33, this.primaryMonitor.y + 1); + this._hotCorner.actor.set_position(x + this.primaryMonitor.width - 1, y); + this.overviewCorner.set_position(x + this.primaryMonitor.width - 33, y + 1); } else if (hotCornerPosition == "bottomLeft") { - this._hotCorner.actor.set_position(this.primaryMonitor.x,this.primaryMonitor.height - 1); - this.overviewCorner.set_position(this.primaryMonitor.x + 1, this.primaryMonitor.height - 33); + this._hotCorner.actor.set_position(x, this.primaryMonitor.height - 1); + this.overviewCorner.set_position(x + 1, this.primaryMonitor.height - 33); } else if (hotCornerPosition == "bottomRight") { - this._hotCorner.actor.set_position(this.primaryMonitor.width - 1,this.primaryMonitor.height - 1); - this.overviewCorner.set_position(this.primaryMonitor.width - 33, this.primaryMonitor.height - 33); + this._hotCorner.actor.set_position(x + this.primaryMonitor.width - 1, this.primaryMonitor.height - 1); + this.overviewCorner.set_position(x + this.primaryMonitor.width - 33, this.primaryMonitor.height - 33); } + }, + + _updateBoxes: function() { + this._updateHotCorners(); this.overviewCorner.set_size(32, 32); @@ -256,33 +206,68 @@ LayoutManager.prototype = { else this.overviewCorner.hide(); - // Need to use GSettings to get the panel height instead of hard-coding it - if (Main.desktop_layout == Main.LAYOUT_TRADITIONAL) { - let panelHeight = 25; - if (Main.panel) { - panelHeight = Main.panel.actor.get_height(); + let getPanelHeight = function(panel) { + let panelHeight = 0; + if (panel) { + panelHeight = panel.actor.get_height(); } - this.panelBox.set_position(this.bottomMonitor.x, this.bottomMonitor.y + this.bottomMonitor.height - panelHeight); - this.panelBox.set_size(this.bottomMonitor.width, panelHeight); + return panelHeight; + }; + + let p1height = getPanelHeight(Main.panel); + + if (Main.desktop_layout == Main.LAYOUT_TRADITIONAL) { + this.panelBox.set_size(this.bottomMonitor.width, p1height); + this.panelBox.set_position(this.bottomMonitor.x, this.bottomMonitor.y + this.bottomMonitor.height - p1height); } - else if (Main.desktop_layout == Main.LAYOUT_FLIPPED) { + else if (Main.desktop_layout == Main.LAYOUT_FLIPPED) { + this.panelBox.set_size(this.primaryMonitor.width, p1height); this.panelBox.set_position(this.primaryMonitor.x, this.primaryMonitor.y); - this.panelBox.set_size(this.primaryMonitor.width, -1); } else if (Main.desktop_layout == Main.LAYOUT_CLASSIC) { - let panelHeight = 25; - if (Main.panel2) { - panelHeight = Main.panel2.actor.get_height(); - } + let p2height = getPanelHeight(Main.panel2); + + this.panelBox.set_size(this.primaryMonitor.width, p1height); this.panelBox.set_position(this.primaryMonitor.x, this.primaryMonitor.y); - this.panelBox.set_size(this.primaryMonitor.width, -1); - this.panelBox2.set_position(this.bottomMonitor.x, this.bottomMonitor.y + this.bottomMonitor.height - panelHeight); - this.panelBox2.set_size(this.bottomMonitor.width, panelHeight); + + this.panelBox2.set_size(this.bottomMonitor.width, p2height); + this.panelBox2.set_position(this.bottomMonitor.x, this.bottomMonitor.y + this.bottomMonitor.height - p2height); } - + this.keyboardBox.set_position(this.bottomMonitor.x, this.bottomMonitor.y + this.bottomMonitor.height); this.keyboardBox.set_size(this.bottomMonitor.width, -1); + this._chrome._queueUpdateRegions(); + }, + + getPorthole: function() { + let porthole = {}; + let screen = {x: 0, y: 0, width: global.screen_width, height: global.screen_height}; + + let getPanelHeight = function(panel) { + let panelHeight = 0; + let hideable = true; + if (panel) { + panelHeight = panel.actor.get_height(); + hideable = panel.isHideable(); + } + return hideable ? 0 : panelHeight; + }; + let p1height = getPanelHeight(Main.panel); + if (Main.desktop_layout == Main.LAYOUT_TRADITIONAL) { + porthole.x = screen.x; porthole.y = screen.y; + porthole.width = screen.width; porthole.height = screen.height - p1height; + } + else if (Main.desktop_layout == Main.LAYOUT_FLIPPED) { + porthole.x = screen.x; porthole.y = screen.y + p1height; + porthole.width = screen.width; porthole.height = screen.height - p1height; + } + else if (Main.desktop_layout == Main.LAYOUT_CLASSIC) { + let p2height = getPanelHeight(Main.panel2); + porthole.x = screen.x; porthole.y = screen.y + p1height; + porthole.width = screen.width; porthole.height = screen.height - p1height - p2height; + } + return porthole; }, _updatePanelBarriers: function(panelBox) { @@ -313,10 +298,10 @@ LayoutManager.prototype = { else { let primary = this.primaryMonitor; leftPanelBarrier = global.create_pointer_barrier(primary.x, primary.y, - -primary.x, primary.y + panelBox.height, + primary.x, primary.y + panelBox.height, 1 /* BarrierPositiveX */); rightPanelBarrier = global.create_pointer_barrier(primary.x + primary.width, primary.y, - -primary.x + primary.width, primary.y + panelBox.height, + primary.x + primary.width, primary.y + panelBox.height, 4 /* BarrierNegativeX */); } } else { @@ -812,7 +797,7 @@ Chrome.prototype = { for (var i in params){ this._trackedActors[index][i] = params[i]; } - this.updateRegions(); + this._queueUpdateRegions(); }, _trackActor: function(actor, params) { @@ -956,7 +941,7 @@ Chrome.prototype = { }, thawUpdateRegions: function() { - this._freezeUpdateCount--; + this._freezeUpdateCount = --this._freezeUpdateCount >= 0 ? this.freezeUpdateCount : 0; this._queueUpdateRegions(); }, @@ -1038,11 +1023,14 @@ Chrome.prototype = { }, updateRegions: function() { + let primary = this._primaryMonitor; + if (!primary) return false; + let rects = [], struts = [], i; if (this._updateRegionIdle) { Mainloop.source_remove(this._updateRegionIdle); - delete this._updateRegionIdle; + this._updateRegionIdle = 0; } for (i = 0; i < this._trackedActors.length; i++) { @@ -1090,7 +1078,6 @@ Chrome.prototype = { // the width/height across the middle of the screen, then // we don't create a strut for it at all. let side; - let primary = this._primaryMonitor; if (x1 <= primary.x && x2 >= primary.x + primary.width) { if (y1 <= primary.y) side = Meta.Side.TOP; @@ -1138,7 +1125,15 @@ Chrome.prototype = { struts.push(strut); } - if (global.top_window_group.get_children().length == 0) + let enable_stage = true; + let top_windows = global.top_window_group.get_children(); + for (var i in top_windows){ + if (top_windows[i]._windowType != Meta.WindowType.TOOLTIP){ + enable_stage = false; + break; + } + } + if (enable_stage) global.set_stage_input_region(rects); else global.set_stage_input_region([]); diff --git a/js/ui/main.js b/js/ui/main.js index c118844..4aff139 100644 --- a/js/ui/main.js +++ b/js/ui/main.js @@ -249,13 +249,11 @@ function start() { panel = new Panel.Panel(true); panel.actor.add_style_class_name('panel-bottom'); layoutManager.panelBox.add(panel.actor); - layoutManager._updateBoxes(); } else if (desktop_layout == LAYOUT_FLIPPED) { panel = new Panel.Panel(false); panel.actor.add_style_class_name('panel-top'); layoutManager.panelBox.add(panel.actor); - layoutManager._updateBoxes(); } else if (desktop_layout == LAYOUT_CLASSIC) { panel = new Panel.Panel(false); @@ -264,9 +262,9 @@ function start() { panel2.actor.add_style_class_name('panel-bottom'); layoutManager.panelBox.add(panel.actor); layoutManager.panelBox2.add(panel2.actor); - layoutManager._updateBoxes(); } - + layoutManager._updateBoxes(); + wm = new WindowManager.WindowManager(); messageTray = new MessageTray.MessageTray(); keyboard = new Keyboard.Keyboard(); @@ -276,7 +274,7 @@ function start() { placesManager = new PlacesManager.PlacesManager(); automountManager = new AutomountManager.AutomountManager(); //autorunManager = new AutorunManager.AutorunManager(); - networkAgent = new NetworkAgent.NetworkAgent(); + //networkAgent = new NetworkAgent.NetworkAgent(); Meta.later_add(Meta.LaterType.BEFORE_REDRAW, _checkWorkspaces); @@ -297,7 +295,7 @@ function start() { // Provide the bus object for gnome-session to // initiate logouts. - EndSessionDialog.init(); + //EndSessionDialog.init(); // Attempt to become a PolicyKit authentication agent PolkitAuthenticationAgent.init() @@ -316,7 +314,7 @@ function start() { Scripting.runPerfScript(module, perfOutput); } - workspace_names = global.settings.get_strv("workspace-names"); + workspace_names = global.settings.get_strv("workspace-name-overrides"); global.screen.connect('notify::n-workspaces', _nWorkspacesChanged); @@ -330,6 +328,16 @@ function start() { applets = AppletManager.loadApplets(); } +function enablePanels() { + if (panel) panel.enable(); + if (panel2) panel2.enable(); +} + +function disablePanels() { + if (panel) panel.disable(); + if (panel2) panel2.disable(); +} + let _workspaces = []; let _checkWorkspacesId = 0; @@ -341,13 +349,35 @@ let _checkWorkspacesId = 0; */ const LAST_WINDOW_GRACE_TIME = 1000; +function _fillWorkspaceNames(index) { + // ensure that we have workspace names up to index + for (let i = index - workspace_names.length; i > 0; --i) { + workspace_names.push(''); + } +} + +function _trimWorkspaceNames(index) { + // trim empty or out-of-bounds names from the end. + for (let i = workspace_names.length - 1; + i >= 0 && (i >= nWorks || !workspace_names[i].length); --i) + { + workspace_names.pop(); + } +} + +function _makeDefaultWorkspaceName(index) { + return _("WORKSPACE") + " " + (index + 1).toString(); +} + function setWorkspaceName(index, name) { - if (index < workspace_names.length) { - name.trim(); - if (name != workspace_names[index]) { - workspace_names[index] = name; - global.settings.set_strv("workspace-names", workspace_names); - } + name.trim(); + if (name != getWorkspaceName(index)) { + _fillWorkspaceNames(index); + workspace_names[index] = (name == _makeDefaultWorkspaceName(index) ? + "" : + name); + _trimWorkspaceNames(); + global.settings.set_strv("workspace-name-overrides", workspace_names); } } @@ -358,15 +388,14 @@ function getWorkspaceName(index) { wsName.trim(); return wsName.length > 0 ? wsName : - _("WORKSPACE") + " " + (index + 1).toString(); + _makeDefaultWorkspaceName(index); } function _addWorkspace() { if (dynamicWorkspaces) return false; nWorks++; - global.settings.set_int("number-workspaces", nWorks); - workspace_names.push(""); + global.settings.set_int("number-workspaces", nWorks); _staticWorkspaces(); return true; } @@ -375,8 +404,12 @@ function _removeWorkspace(workspace) { if (nWorks == 1 || dynamicWorkspaces) return false; nWorks--; - let index = workspace.index(); - workspace_names.splice (index,1); + let index = workspace.index(); + if (index < workspace_names.length) { + workspace_names.splice (index,1); + } + _trimWorkspaceNames(); + global.settings.set_strv("workspace-name-overrides", workspace_names); global.settings.set_int("number-workspaces", nWorks); global.screen.remove_workspace(workspace, global.get_current_time()); return true; @@ -1078,13 +1111,18 @@ function getTabList(workspaceOpt, screenOpt) { // the correct tab order. let allwindows = display.get_tab_list(Meta.TabList.NORMAL_ALL, screen, workspace); + let registry = {}; // to avoid duplicates let tracker = Cinnamon.WindowTracker.get_default(); for (let i = 0; i < allwindows.length; ++i) { let window = allwindows[i]; + let seqno = window.get_stable_sequence(); // Add "normal" windows and those that don't have an "app". - if (normalLookup[window.get_stable_sequence()] === 1 || !tracker.get_window_app(window)) + if (normalLookup[seqno] === 1 || !tracker.get_window_app(window)) { - windows.push(window); + if (!registry[seqno]) { + windows.push(window); + registry[seqno] = true; + } } } return windows; diff --git a/js/ui/messageTray.js b/js/ui/messageTray.js index 5eaba26..3f17cfa 100644 --- a/js/ui/messageTray.js +++ b/js/ui/messageTray.js @@ -444,6 +444,7 @@ Notification.prototype = { this._scrollPolicy = Gtk.PolicyType.AUTOMATIC; this._imageBin = null; this._timestamp = new Date(); + this._inNotificationBin = false; let calendarSettings = new Gio.Settings({ schema: 'org.cinnamon.calendar' }); this.dateFormat = calendarSettings.get_string('date-format'); @@ -492,6 +493,7 @@ Notification.prototype = { this._titleLabel = new St.Label(); this._bannerBox.add_actor(this._titleLabel); this._bannerBox.add_actor(this._timeLabel); + this._timeLabel.hide(); this._bannerUrlHighlighter = new URLHighlighter(); this._bannerLabel = this._bannerUrlHighlighter.actor; this._bannerBox.add_actor(this._bannerLabel); @@ -509,6 +511,7 @@ Notification.prototype = { // remove any additional actors/action buttons previously added. update: function(title, banner, params) { this._timestamp = new Date(); + this._inNotificationBin = false; params = Params.parse(params, { customContent: false, body: null, icon: null, @@ -565,6 +568,7 @@ Notification.prototype = { title = title ? _fixMarkup(title.replace(/\n/g, ' '), params.titleMarkup) : ''; this._titleLabel.clutter_text.set_markup('' + title + ''); this._timeLabel.clutter_text.set_markup(this._timestamp.toLocaleTimeString(this.dateFormat)); + this._timeLabel.hide(); if (Pango.find_base_dir(title, -1) == Pango.Direction.RTL) this._titleDirection = St.TextDirection.RTL; else @@ -846,13 +850,20 @@ Notification.prototype = { titleBox.x2 = titleBoxW; timeBox.x2 = timeBoxW; } - timeBox.y1 = 0; - timeBox.y2 = timeNatH; - titleBox.y1 = timeNatH; - titleBox.y2 = timeNatH + titleNatH; + if (this._inNotificationBin) { + timeBox.y1 = 0; + timeBox.y2 = timeNatH; + titleBox.y1 = timeNatH; + titleBox.y2 = timeNatH + titleNatH; + } else { + titleBox.y1 = 0; + titleBox.y2 = titleNatH; + } this._titleLabel.allocate(titleBox, flags); - this._timeLabel.allocate(timeBox, flags); + if (this._inNotificationBin) { + this._timeLabel.allocate(timeBox, flags); + } this._titleFitsInBannerMode = (titleNatW <= availWidth); let bannerFits = true; @@ -874,8 +885,13 @@ Notification.prototype = { bannerFits = (bannerBox.x1 + bannerNatW <= availWidth); } - bannerBox.y1 = timeNatH; - bannerBox.y2 = timeNatH + titleNatH; + if (this._inNotificationBin) { + bannerBox.y1 = timeNatH; + bannerBox.y2 = timeNatH + titleNatH; + } else { + bannerBox.y1 = 0; + bannerBox.y2 = titleNatH; + } this._bannerLabel.allocate(bannerBox, flags); // Make _bannerLabel visible if the entire notification diff --git a/js/ui/overview.js b/js/ui/overview.js index d7fab55..78a281e 100644 --- a/js/ui/overview.js +++ b/js/ui/overview.js @@ -541,17 +541,7 @@ Overview.prototype = { this._background.show(); this._workspacesDisplay.show(); - - if (Main.panel) - Tweener.addTween(Main.panel.actor, { opacity: 0, - time: ANIMATION_TIME, - transition: 'easeOutQuad', - onComplete: function(){Main.panel.actor.hide();}}); - if (Main.panel2) - Tweener.addTween(Main.panel2.actor, { opacity: 0, - time: ANIMATION_TIME, - transition: 'easeOutQuad', - onComplete: function(){Main.panel2.actor.hide();}}); + Main.disablePanels(); this.workspaces = this._workspacesDisplay.workspacesView; global.overlay_group.add_actor(this.workspaces.actor); @@ -693,15 +683,7 @@ Overview.prototype = { this.animationInProgress = true; this._hideInProgress = true; - - if (Main.panel){ - Main.panel.actor.show(); - Tweener.addTween(Main.panel.actor, {opacity: 255, time: ANIMATION_TIME, transition: 'easeOutQuad'}); - } - if (Main.panel2){ - Main.panel2.actor.show(); - Tweener.addTween(Main.panel2.actor, {opacity: 255, time: ANIMATION_TIME, transition: 'easeOutQuad'}); - } + Main.enablePanels(); if (!this.workspaces.getActiveWorkspace().hasMaximizedWindows()) { this._desktopFade.opacity = 0; diff --git a/js/ui/panel.js b/js/ui/panel.js index a2ec7c1..773187a 100644 --- a/js/ui/panel.js +++ b/js/ui/panel.js @@ -22,7 +22,6 @@ const BUTTON_DND_ACTIVATION_TIMEOUT = 250; const ANIMATED_ICON_UPDATE_TIMEOUT = 100; const SPINNER_ANIMATION_TIME = 0.2; -const PANEL_HEIGHT = 25; const AUTOHIDE_ANIMATION_TIME = 0.2; const TIME_DELTA = 1500; @@ -606,12 +605,14 @@ Panel.prototype = { this.bottomPosition = bottomPosition; this._hidden = false; + this._disabled = false; this._hidetime = 0; this._hideable = global.settings.get_boolean("panel-autohide"); this._hideTimer = false; this._showTimer = false; - this._showDelay = global.settings.get_boolean("panel-show-delay"); - this._hideDelay = global.settings.get_boolean("panel-hide-delay"); + this._onPanelShowDelayChanged(); + this._onPanelHideDelayChanged(); + this._themeFontSize = null; this.actor = new Cinnamon.GenericContainer({ name: 'panel', reactive: true }); @@ -625,20 +626,12 @@ Panel.prototype = { this.actor.set_height(global.settings.get_int('panel-top-height')); } } - - Main.overview.connect('shown', Lang.bind(this, function () { - this.actor.add_style_class_name('in-overview'); - })); - Main.overview.connect('hiding', Lang.bind(this, function () { - this.actor.remove_style_class_name('in-overview'); - })); - - Main.expo.connect('shown', Lang.bind(this, function () { - this.actor.add_style_class_name('in-overview'); - })); - Main.expo.connect('hiding', Lang.bind(this, function () { - this.actor.remove_style_class_name('in-overview'); - })); + if (this.bottomPosition) { + global.settings.connect("changed::panel-bottom-height", Lang.bind(this, this._processPanelSize)); + } + else { + global.settings.connect("changed::panel-top-height", Lang.bind(this, this._processPanelSize)); + } this._menus = new PopupMenu.PopupMenuManager(this); @@ -675,7 +668,7 @@ Panel.prototype = { this.actor.connect('leave-event', Lang.bind(this, this._leavePanel)); this.actor.connect('enter-event', Lang.bind(this, this._enterPanel)); - global.settings.connect("changed::panel-autohide", Lang.bind(this, this._onPanelAutoHideChanged)); + global.settings.connect("changed::panel-autohide", Lang.bind(this, this._processPanelAutoHide)); global.settings.connect("changed::panel-show-delay", Lang.bind(this, this._onPanelShowDelayChanged)); global.settings.connect("changed::panel-hide-delay", Lang.bind(this, this._onPanelHideDelayChanged)); @@ -696,10 +689,15 @@ Panel.prototype = { this._setDNDstyle(); global.settings.connect("changed::panel-edit-mode", Lang.bind(this, this._setDNDstyle)); - global.settings.connect("changed::panel-resizable", Lang.bind(this, this._onPanelResizableChanged)); - this.actor.connect('style-changed', Lang.bind(this, this._onStyleChanged)); + global.settings.connect("changed::panel-resizable", Lang.bind(this, this._processPanelSize)); + global.settings.connect("changed::panel-scale-text-icons", Lang.bind(this, this._onScaleTextIconsChanged)) + this.actor.connect('style-changed', Lang.bind(this, this._processPanelSize)); }, + isHideable: function() { + return this._hideable; + }, + _setDNDstyle: function() { if (global.settings.get_boolean("panel-edit-mode")) { this._leftBox.add_style_pseudo_class('dnd'); @@ -720,7 +718,6 @@ Panel.prototype = { } } if (event.get_button()==3){ - global.log("3"); try { let [x, y] = event.get_coords(); let target = global.stage.get_actor_at_pos(Clutter.PickMode.ALL, x, y); @@ -762,55 +759,74 @@ Panel.prototype = { this._hideDelay = global.settings.get_int("panel-hide-delay"); }, - _onPanelAutoHideChanged: function() { - this._hideable = global.settings.get_boolean("panel-autohide"); - if (this._hidden == true && this._hideable == false) { - this._showPanel(); - } - if (this._hidden == false && this._hideable == true) { - this._hidePanel(); - } - }, - - _onPanelSizeChanged: function() { - let panelHeight; - if (this.bottomPosition) { - panelHeight = global.settings.get_int("panel-bottom-height"); - } - else { - panelHeight = global.settings.get_int("panel-top-height"); + _processPanelAutoHide: function() { + this._hideable = global.settings.get_boolean("panel-autohide"); + // Show a glimpse of the panel irrespective of the new setting, + // in order to force a region update. + // Techically, this should not be necessary if the function is called + // when auto-hide is in effect and is not changing, but experience + // shows that not flashing the panels may lead to "phantom panels" + // where the panels should be if auto-hide was on. + this._hidePanel(true); // force hide + this._showPanel(); + + if (this._hideable == true) { + this._hidePanel(); } - this.actor.set_height(panelHeight); - Main.layoutManager._updateBoxes(); }, - _onPanelResizableChanged: function() { + _processPanelSize: function() { + let panelHeight; let panelResizable = global.settings.get_boolean("panel-resizable"); if (panelResizable) { if (this.bottomPosition) { - this._onPanelSizeChangedId = global.settings.connect("changed::panel-bottom-height", Lang.bind(this, this._onPanelSizeChanged)); + panelHeight = global.settings.get_int("panel-bottom-height"); } else { - this._onPanelSizeChangedId = global.settings.connect("changed::panel-top-height", Lang.bind(this, this._onPanelSizeChanged)); + panelHeight = global.settings.get_int("panel-top-height"); } - this._onPanelSizeChanged(); } else { - if (this._onPanelSizeChangedId) { - global.settings.disconnect(this._onPanelSizeChangedId); - } let themeNode = this.actor.get_theme_node(); - let panelHeight = themeNode.get_length("height"); + panelHeight = themeNode.get_length("height"); if (!panelHeight || panelHeight == 0) { panelHeight = 25; } - this.actor.set_height(panelHeight); - Main.layoutManager._updateBoxes(); } + if (!this._themeFontSize) { + let themeNode = this.actor.get_theme_node(); + this._themeFontSize = themeNode.get_length("font-size"); + } + if (global.settings.get_boolean("panel-scale-text-icons") && global.settings.get_boolean("panel-resizable")) { + let textheight = (panelHeight / Applet.DEFAULT_PANEL_HEIGHT) * Applet.PANEL_FONT_DEFAULT_HEIGHT; + this.actor.set_style('font-size: ' + textheight + 'px;'); + } else { + this.actor.set_style('font-size: ' + this._themeFontSize + 'px;'); + } + this.actor.set_height(panelHeight); + this._processPanelAutoHide(); + AppletManager.updateAppletPanelHeights(); }, - _onStyleChanged: function() { - this._onPanelResizableChanged(); + _onScaleTextIconsChanged: function() { + let panelHeight; + if (this.bottomPosition) { + panelHeight = global.settings.get_int("panel-bottom-height"); + } + else { + panelHeight = global.settings.get_int("panel-top-height"); + } + if (!this._themeFontSize) { + let themeNode = this.actor.get_theme_node(); + this._themeFontSize = themeNode.get_length("font-size"); + } + if (global.settings.get_boolean("panel-scale-text-icons") && global.settings.get_boolean("panel-resizable")) { + let textheight = (panelHeight / Applet.DEFAULT_PANEL_HEIGHT) * Applet.PANEL_FONT_DEFAULT_HEIGHT; + this.actor.set_style('font-size: ' + textheight + 'px;'); + } else { + this.actor.set_style('font-size: ' + this._themeFontSize ? this._themeFontSize + 'px;' : '8.5pt;'); + } + AppletManager.updateAppletPanelHeights(true); }, _getPreferredWidth: function(actor, forHeight, alloc) { @@ -947,9 +963,30 @@ Panel.prototype = { } }, - + enable: function() { + this._disabled = false; + this.actor.show(); + Tweener.addTween(this.actor, { + opacity: 255, + time: AUTOHIDE_ANIMATION_TIME, + transition: 'easeOutQuad' + }); + }, + + disable: function() { + this._disabled = true; + Tweener.addTween(this.actor, { + opacity: 0, + time: AUTOHIDE_ANIMATION_TIME, + transition: 'easeOutQuad', + onComplete: this.actor.hide + }); + }, + _showPanel: function() { - if (this._hidden == false) return; + if (this._disabled) return; + + if (!this._hidden) return; if (Main.lookingGlass != null && Main.lookingGlass._open) { return; @@ -959,131 +996,82 @@ Panel.prototype = { Main.layoutManager._windowsRestacked(); let height = this.actor.get_height(); - - if (this.bottomPosition) { - let params = { y: height - 1, - time: AUTOHIDE_ANIMATION_TIME + 0.1, - transition: 'easeOutQuad' - }; - - Tweener.addTween(this._leftCorner.actor, params); - Tweener.addTween(this._rightCorner.actor, params); - - Tweener.addTween(this.actor.get_parent(), - { y: Main.layoutManager.bottomMonitor.y + Main.layoutManager.bottomMonitor.height - height, - time: AUTOHIDE_ANIMATION_TIME, - transition: 'easeOutQuad', - onUpdate: function() { - // Force the layout manager to update the input region - Main.layoutManager._chrome.updateRegions() - } - }); - - params = { opacity: 255, - time: AUTOHIDE_ANIMATION_TIME+0.2, - transition: 'easeOutQuad' - }; - - Tweener.addTween(this._leftBox, params); - Tweener.addTween(this._centerBox, params); - Tweener.addTween(this._rightBox, params); - } - else { - let params = { y: height - 1, - time: AUTOHIDE_ANIMATION_TIME + 0.1, - transition: 'easeOutQuad' - }; - - Tweener.addTween(this._leftCorner.actor, params); - Tweener.addTween(this._rightCorner.actor, params); - - Tweener.addTween(this.actor.get_parent(), - { y: Main.layoutManager.primaryMonitor.y, - time: AUTOHIDE_ANIMATION_TIME, - transition: 'easeOutQuad', - onUpdate: function() { - // Force the layout manager to update the input region - Main.layoutManager._chrome.updateRegions() - } - }); - - params = { opacity: 255, - time: AUTOHIDE_ANIMATION_TIME+0.2, - transition: 'easeOutQuad' - }; - - Tweener.addTween(this._leftBox, params); - Tweener.addTween(this._centerBox, params); - Tweener.addTween(this._rightBox, params); - } + let animationTime = AUTOHIDE_ANIMATION_TIME; + let y = this.bottomPosition ? + Main.layoutManager.bottomMonitor.y + Main.layoutManager.bottomMonitor.height - height : + Main.layoutManager.primaryMonitor.y; + + + let params = { y: height - 1, + time: animationTime + 0.1, + transition: 'easeOutQuad' + }; + + Tweener.addTween(this._leftCorner.actor, params); + Tweener.addTween(this._rightCorner.actor, params); + + Tweener.addTween(this.actor.get_parent(), + { y: y, + time: animationTime, + transition: 'easeOutQuad', + onUpdate: function() { + // Force the layout manager to update the input region + Main.layoutManager._chrome.updateRegions() + } + }); + + params = { opacity: 255, + time: animationTime+0.2, + transition: 'easeOutQuad' + }; + + Tweener.addTween(this._leftBox, params); + Tweener.addTween(this._centerBox, params); + Tweener.addTween(this._rightBox, params); this._hidden = false; }, - _hidePanel: function() { - if (Main.overview.visible || this._hideable == false || global.menuStackLength > 0 || this.isMouseOverPanel) return; + _hidePanel: function(force) { + if (this._disabled) return; + + if ((!this._hideable && !force) || global.menuStackLength > 0 || this.isMouseOverPanel) return; // Force the panel to be on top (hack to correct issues when switching workspace) Main.layoutManager._windowsRestacked(); let height = this.actor.get_height(); + let animationTime = AUTOHIDE_ANIMATION_TIME; + let y = this.bottomPosition ? + Main.layoutManager.bottomMonitor.y + Main.layoutManager.bottomMonitor.height - 1 : + Main.layoutManager.primaryMonitor.y - height + 1; + + Tweener.addTween(this.actor.get_parent(), { + y: y, + time: animationTime, + transition: 'easeOutQuad', + onUpdate: function() { + // Force the layout manager to update the input region + Main.layoutManager._chrome.updateRegions() + } + }); - if (this.bottomPosition) { - Tweener.addTween(this.actor.get_parent(), - { y: Main.layoutManager.bottomMonitor.y + Main.layoutManager.bottomMonitor.height - 1, - time: AUTOHIDE_ANIMATION_TIME, - transition: 'easeOutQuad', - onUpdate: function() { - // Force the layout manager to update the input region - Main.layoutManager._chrome.updateRegions() - } - }); - - let params = { y: 0, - time: AUTOHIDE_ANIMATION_TIME, - transition: 'easeOutQuad' - }; - - Tweener.addTween(this._leftCorner.actor, params); - Tweener.addTween(this._rightCorner.actor, params); - - params = { opacity: 0, - time: AUTOHIDE_ANIMATION_TIME - 0.1, - transition: 'easeOutQuad' - }; - - Tweener.addTween(this._leftBox, params); - Tweener.addTween(this._centerBox, params); - Tweener.addTween(this._rightBox, params); - } - else { - Tweener.addTween(this.actor.get_parent(), - { y: Main.layoutManager.primaryMonitor.y - height + 1, - time: AUTOHIDE_ANIMATION_TIME, - transition: 'easeOutQuad', - onUpdate: function() { - // Force the layout manager to update the input region - Main.layoutManager._chrome.updateRegions() - } - }); - - let params = { y: 0, - time: AUTOHIDE_ANIMATION_TIME, - transition: 'easeOutQuad' - }; - - Tweener.addTween(this._leftCorner.actor, params); - Tweener.addTween(this._rightCorner.actor, params); - - params = { opacity: 0, - time: AUTOHIDE_ANIMATION_TIME - 0.1, - transition: 'easeOutQuad' - }; - - Tweener.addTween(this._leftBox, params); - Tweener.addTween(this._centerBox, params); - Tweener.addTween(this._rightBox, params); - } + let params = { y: 0, + time: animationTime, + transition: 'easeOutQuad' + }; + + Tweener.addTween(this._leftCorner.actor, params); + Tweener.addTween(this._rightCorner.actor, params); + + params = { opacity: 0, + time: Math.max(0, animationTime - 0.1), + transition: 'easeOutQuad' + }; + + Tweener.addTween(this._leftBox, params); + Tweener.addTween(this._centerBox, params); + Tweener.addTween(this._rightBox, params); this._hidden = true; }, diff --git a/js/ui/placesManager.js b/js/ui/placesManager.js index 5c0cb74..3b3cd57 100644 --- a/js/ui/placesManager.js +++ b/js/ui/placesManager.js @@ -166,11 +166,11 @@ PlacesManager.prototype = { icon_size: size }); }, function (params) { - // BUG: nautilus-connect-server doesn't have a desktop file, so we can't + // BUG: nemo-connect-server doesn't have a desktop file, so we can't // launch it with the workspace from params. It's probably pretty rare // and odd to drag this place onto a workspace in any case - Util.spawn(['nautilus-connect-server']); + Util.spawn(['nemo-connect-server']); }); this._defaultPlaces.push(this._home); @@ -178,7 +178,7 @@ PlacesManager.prototype = { this._defaultPlaces.push(this._connect); /* - * Show devices, code more or less ported from nautilus-places-sidebar.c + * Show devices, code more or less ported from nemo-places-sidebar.c */ this._volumeMonitor = Gio.VolumeMonitor.get(); this._volumeMonitor.connect('volume-added', Lang.bind(this, this._updateDevices)); diff --git a/js/ui/popupMenu.js b/js/ui/popupMenu.js index ad8c83f..c980570 100644 --- a/js/ui/popupMenu.js +++ b/js/ui/popupMenu.js @@ -967,6 +967,7 @@ PopupMenuBase.prototype = { } if (menuItem == this._activeMenuItem) this._activeMenuItem = null; + this.length--; })); }, diff --git a/js/ui/radioButton.js b/js/ui/radioButton.js new file mode 100644 index 0000000..00a3413 --- /dev/null +++ b/js/ui/radioButton.js @@ -0,0 +1,167 @@ +const Clutter = imports.gi.Clutter; +const Pango = imports.gi.Pango; +const Cinnamon = imports.gi.Cinnamon; +const St = imports.gi.St; +const Signals = imports.signals; + +const Lang = imports.lang; + +function RadioButtonContainer() { + this._init(); +} +RadioButtonContainer.prototype = { + _init: function() { + this.actor = new Cinnamon.GenericContainer({ y_align: St.Align.MIDDLE }); + this.actor.connect('get-preferred-width', + Lang.bind(this, this._getPreferredWidth)); + this.actor.connect('get-preferred-height', + Lang.bind(this, this._getPreferredHeight)); + this.actor.connect('allocate', + Lang.bind(this, this._allocate)); + this.actor.connect('style-changed', Lang.bind(this, + function() { + let node = this.actor.get_theme_node(); + this._spacing = node.get_length('spacing'); + })); + this.actor.request_mode = Clutter.RequestMode.HEIGHT_FOR_WIDTH; + + this._box = new St.Bin(); + this.actor.add_actor(this._box); + + this.label = new St.Label(); + this.label.clutter_text.set_line_wrap(false); + this.label.clutter_text.set_ellipsize(Pango.EllipsizeMode.NONE); + this.actor.add_actor(this.label, { y_fill: true, y_align: St.Align.END }); + + this._spacing = 0; + }, + + _getPreferredWidth: function(actor, forHeight, alloc) { + let [minWidth, natWidth] = this._box.get_preferred_width(forHeight); + + alloc.min_size = minWidth + this._spacing; + alloc.natural_size = natWidth + this._spacing; + }, + + _getPreferredHeight: function(actor, forWidth, alloc) { + let [minBoxHeight, natBoxHeight] = + this._box.get_preferred_height(-1); + let [minLabelHeight, natLabelHeight] = + this.label.get_preferred_height(-1); + + alloc.min_size = Math.max(minBoxHeight, 2 * minLabelHeight); + alloc.natural_size = Math.max(natBoxHeight, 2 * natLabelHeight); + }, + + _allocate: function(actor, box, flags) { + let availWidth = box.x2 - box.x1; + let availHeight = box.y2 - box.y1; + + let childBox = new Clutter.ActorBox(); + let [minBoxWidth, natBoxWidth] = + this._box.get_preferred_width(-1); + let [minBoxHeight, natBoxHeight] = + this._box.get_preferred_height(-1); + childBox.x1 = box.x1; + childBox.x2 = box.x1 + natBoxWidth; + childBox.y1 = box.y1; + childBox.y2 = box.y1 + natBoxHeight; + this._box.allocate(childBox, flags); + + childBox.x1 = box.x1 + natBoxWidth + this._spacing; + childBox.x2 = availWidth - childBox.x1; + childBox.y1 = box.y1; + childBox.y2 = box.y2; + this.label.allocate(childBox, flags); + } +}; + +function RadioButton(label) { + this._init(label); +} + +RadioButton.prototype = { + _init: function(label) { + this.actor = new St.Button({ style_class: 'radiobutton', + button_mask: St.ButtonMask.ONE, + toggle_mode: true, + can_focus: true, + x_fill: true, + y_fill: true, + y_align: St.Align.MIDDLE }); + this._container = new RadioButtonContainer(); + this.actor.set_child(this._container.actor); + + if (label) + this.setLabel(label); + }, + + setLabel: function(label) { + this._container.label.set_text(label); + }, + + getLabelActor: function() { + return this._container.label; + } +}; + +function RadioButtonGroup() { + this._init(); +} + +RadioButtonGroup.prototype = { + _init: function() { + this.actor = new St.BoxLayout({ vertical: true, width: 250 }); + this._buttons = []; + this._activeId = null; + }, + + addButton: function(buttonId, label) { + this.radioButton = new RadioButton(label); + this.radioButton.actor.connect("clicked", + Lang.bind(this, function(actor) { + this.buttonClicked(actor, buttonId); + })); + + this._buttons.push({ id: buttonId, button: this.radioButton }); + this.actor.add(this.radioButton.actor, { x_fill: true, y_fill: false, y_align: St.Align.MIDDLE }); + }, + + radioChanged: function(actor) { + + }, + + buttonClicked: function(actor, buttonId) { + for (var i in this._buttons) { + if (buttonId != this._buttons[i]['id'] && this._buttons[i]['button'].actor.checked) { + this._buttons[i]['button'].actor.checked = false; + } + else if (buttonId == this._buttons[i]['id'] && !this._buttons[i]['button'].actor.checked) { + this._buttons[i]['button'].actor.checked = true; + } + } + + // Only trigger real changes to radio selection. + if (buttonId != this._activeId) { + this._activeId = buttonId; + this.emit('radio-changed', this._activeId); + } + }, + + setActive: function(buttonId) { + for (var i in this._buttons) { + this._buttons[i]['button'].actor.checked = + (buttonId == this._buttons[i]['id']); + } + + if (this._activeId != buttonId) { + this._activeId = buttonId; + this.emit('radio-changed', this._activeId); + } + }, + + getActive: function() { + return this._activeId; + } +} +Signals.addSignalMethods(RadioButtonGroup.prototype); \ No newline at end of file diff --git a/js/ui/tooltips.js b/js/ui/tooltips.js index 497a816..56c93cd 100644 --- a/js/ui/tooltips.js +++ b/js/ui/tooltips.js @@ -36,7 +36,7 @@ PanelItemTooltip.prototype = { _onEnterEvent: function(actor, event) { this.preventShow = false; - Tweener.addTween(this, {time: 0.3, onComplete: Lang.bind(this, this._onTimerComplete)}); + Tweener.addTween(this, {time: 0.3, onComplete: Lang.bind(this, this._onTimerComplete)}); this._mousePosition = event.get_coords(); }, @@ -74,15 +74,15 @@ PanelItemTooltip.prototype = { let tooltipTop; if (this.orientation == St.Side.BOTTOM) { monitor = Main.layoutManager.bottomMonitor; - tooltipTop = monitor.height-tooltipHeight-this._panelItem.actor.get_allocation_box().y2+this._panelItem.actor.get_allocation_box().y1; + tooltipTop = monitor.y+monitor.height-tooltipHeight-this._panelItem.actor.get_allocation_box().y2+this._panelItem.actor.get_allocation_box().y1; } else { monitor = Main.layoutManager.primaryMonitor; - tooltipTop = this._panelItem.actor.get_allocation_box().y2; + tooltipTop = monitor.y+this._panelItem.actor.get_allocation_box().y2; } var tooltipLeft = this._mousePosition[0]- Math.round(tooltipWidth/2); - if (tooltipLeft<0) tooltipLeft = 0; - if (tooltipLeft+tooltipWidth>monitor.width) tooltipLeft = monitor.width-tooltipWidth; + if (tooltipLeftmonitor.x+monitor.width) tooltipLeft = (monitor.x+monitor.width)-tooltipWidth; this._tooltip.set_position(tooltipLeft, tooltipTop); diff --git a/js/ui/viewSelector.js b/js/ui/viewSelector.js index e4ce25a..c62bc03 100644 --- a/js/ui/viewSelector.js +++ b/js/ui/viewSelector.js @@ -97,12 +97,12 @@ ViewSelector.prototype = { // The tab bar is located at the top of the view selector and // holds "normal" tab labels. this._tabBar = new Cinnamon.GenericContainer(); - this._tabBar.connect('get-preferred-width', + /* this._tabBar.connect('get-preferred-width', Lang.bind(this, this._getPreferredTabBarWidth)); this._tabBar.connect('get-preferred-height', Lang.bind(this, this._getPreferredTabBarHeight)); this._tabBar.connect('allocate', - Lang.bind(this, this._allocateTabBar)); + Lang.bind(this, this._allocateTabBar));*/ this.actor.add(this._tabBar); // Box to hold "normal" tab labels diff --git a/js/ui/windowManager.js b/js/ui/windowManager.js index 99a2cb7..d5f69e4 100644 --- a/js/ui/windowManager.js +++ b/js/ui/windowManager.js @@ -366,27 +366,28 @@ WindowManager.prototype = { let effect = "none"; let time = 0.25; try{ - effect = global.settings.get_string("desktop-effects-unmaximize-effect"); - transition = global.settings.get_string("desktop-effects-unmaximize-transition"); + effect = global.settings.get_string("desktop-effects-unmaximize-effect"); + transition = global.settings.get_string("desktop-effects-unmaximize-transition"); time = global.settings.get_int("desktop-effects-unmaximize-time") / 1000; } catch(e) { log(e); } - - if (effect == "scale") { - //Not available yet because it doesn't look good.. - this._unmaximizing.push(actor); + + if (effect == "scale") { + + this._unmaximizing.push(actor); - actor.opacity =0; - actor.move_anchor_point_from_gravity(Clutter.Gravity.CENTER); - + let scale_x = targetWidth / actor.width; + let scale_y = targetHeight / actor.height; + let anchor_x = (actor.x - targetX) * actor.width / (targetWidth - actor.width); + let anchor_y = (actor.y - targetY) * actor.height / (targetHeight - actor.height); + + actor.move_anchor_point(anchor_x, anchor_y); + Tweener.addTween(actor, - { x: targetX, - y: targetY, - height: targetHeight, - width: targetWidth, - opacity: 100, + { scale_x: scale_x, + scale_y: scale_y, time: time, transition: transition, onComplete: this._unmaximizeWindowDone, @@ -396,10 +397,11 @@ WindowManager.prototype = { onOverwriteScope: this, onOverwriteParams: [cinnamonwm, actor] }); + } else { cinnamonwm.completed_unmaximize(actor); - } + } }, _unmaximizeWindowDone : function(cinnamonwm, actor) { @@ -750,28 +752,30 @@ WindowManager.prototype = { cinnamonwm.completed_switch_workspace(); }, - + showWorkspaceOSD : function() { - if (global.settings.get_boolean("workspace-osd-visible")) { + if (global.settings.get_boolean("workspace-osd-visible")) { let current_workspace_index = global.screen.get_active_workspace_index(); - let monitor = Main.layoutManager.primaryMonitor; + let monitor = Main.layoutManager.primaryMonitor; let label = new St.Label({style_class:'workspace-osd'}); label.set_text(Main.getWorkspaceName(current_workspace_index)); - label.set_opacity = 0; - Main.layoutManager.addChrome(label, { visibleInFullscreen: false }); + label.set_opacity = 0; + Main.layoutManager.addChrome(label, { visibleInFullscreen: false, affectsInputRegion: false }); let workspace_osd_x = global.settings.get_int("workspace-osd-x"); let workspace_osd_y = global.settings.get_int("workspace-osd-y"); let x = (monitor.width * workspace_osd_x /100 - label.width/2); let y = (monitor.height * workspace_osd_y /100 - label.height/2); label.set_position(x, y); - let duration = global.settings.get_int("workspace-osd-duration") / 1000; - Tweener.addTween(label, { opacity: 255, - time: duration, - transition: 'linear', - onComplete: function() { Main.layoutManager.removeChrome(label); } }); + let duration = global.settings.get_int("workspace-osd-duration") / 1000; + Tweener.addTween(label, { opacity: 255, + time: duration, + transition: 'linear', + onComplete: function() { + Main.layoutManager.removeChrome(label); + }}); } }, - + _startAppSwitcher : function(display, screen, window, binding) { let tabPopup = new AltTab.AltTabPopup(); @@ -788,18 +792,22 @@ WindowManager.prototype = { _moveWindowToWorkspaceLeft : function(display, screen, window, binding) { let workspace = global.screen.get_active_workspace().get_neighbor(Meta.MotionDirection.LEFT) - window.change_workspace(workspace); - workspace.activate(global.get_current_time()); - window.raise(); - this.showWorkspaceOSD(); + if (workspace != global.screen.get_active_workspace()) { + window.change_workspace(workspace); + workspace.activate(global.get_current_time()); + window.raise(); + this.showWorkspaceOSD(); + } }, _moveWindowToWorkspaceRight : function(display, screen, window, binding) { let workspace = global.screen.get_active_workspace().get_neighbor(Meta.MotionDirection.RIGHT) - window.change_workspace(workspace); - workspace.activate(global.get_current_time()); - window.raise(); - this.showWorkspaceOSD(); + if (workspace != global.screen.get_active_workspace()) { + window.change_workspace(workspace); + workspace.activate(global.get_current_time()); + window.raise(); + this.showWorkspaceOSD(); + } }, _showWorkspaceSwitcher : function(display, screen, window, binding) { diff --git a/js/ui/workspace.js b/js/ui/workspace.js index 5a44ada..27624fd 100644 --- a/js/ui/workspace.js +++ b/js/ui/workspace.js @@ -37,11 +37,12 @@ const BUTTON_LAYOUT_KEY = 'button-layout'; // Each triplet is [xCenter, yCenter, scale] where the scale // is relative to the width of the workspace. const POSITIONS = { - 1: [[0.5, 0.5, 0.95]], - 2: [[0.25, 0.5, 0.48], [0.75, 0.5, 0.48]], - 3: [[0.25, 0.25, 0.48], [0.75, 0.25, 0.48], [0.5, 0.75, 0.48]], - 4: [[0.25, 0.25, 0.47], [0.75, 0.25, 0.47], [0.25, 0.75, 0.47], [0.75, 0.75, 0.47]], - 5: [[0.165, 0.25, 0.32], [0.495, 0.25, 0.32], [0.825, 0.25, 0.32], [0.25, 0.75, 0.32], [0.75, 0.75, 0.32]] + 1: [[0.5, 0.525, 0.875]], + 2: [[0.25, 0.525, 0.48], [0.75, 0.525, 0.48]], + 3: [[0.25, 0.275, 0.48], [0.75, 0.275, 0.48], [0.5, 0.75, 0.48]], + 4: [[0.25, 0.275, 0.47], [0.75, 0.275, 0.47], [0.25, 0.75, 0.47], [0.75, 0.75, 0.47]], + 5: [[0.165, 0.25, 0.32], [0.495, 0.25, 0.32], [0.825, 0.25, 0.32], [0.25, 0.75, 0.32], [0.75, 0.75, 0.32]], + 6: [[0.165, 0.25, 0.32], [0.495, 0.25, 0.32], [0.825, 0.25, 0.32], [0.165, 0.75, 0.32], [0.495, 0.75, 0.32], [0.825, 0.75, 0.32]] }; // Used in _orderWindowsPermutations, 5! = 120 which is probably the highest we can go const POSITIONING_PERMUTATIONS_MAX = 5; @@ -544,7 +545,7 @@ WindowOverlay.prototype = { chromeHeights: function () { return [this.closeButton.height - this.closeButton._overlap, - this.title.height + this.title._spacing]; + this._applicationIconBox.height + this.title._spacing]; }, /** @@ -726,16 +727,16 @@ Workspace.prototype = { this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); - let windows = Main.getTabList(this.metaWorkspace); + let windows = global.get_window_actors().filter(this._isMyWindow, this); + windows.reverse(); // we want the most-recently-used windows first // Create clones for windows that should be // visible in the Overview this._windows = []; this._windowOverlays = []; for (let i = 0; i < windows.length; i++) { - let window = windows[i].get_compositor_private(); - if (this._isOverviewWindow(window)) { - this._addWindowClone(window); + if (this._isOverviewWindow(windows[i])) { + this._addWindowClone(windows[i]); } } @@ -760,11 +761,10 @@ Workspace.prototype = { selectAnotherWindow: function(symbol) { let numWindows = this._windowOverlays.length; if (numWindows === 0) { - return; - } - if (this._kbWindowIndex > -1 && this._kbWindowIndex < numWindows) { - this._windowOverlays[this._kbWindowIndex].setSelected(false); + return false; } + let currentIndex = this._kbWindowIndex; + let nextIndex = -1; if (numWindows > 3 // grid navigation is not suited for a low window count && (symbol === Clutter.Down || symbol === Clutter.Up)) @@ -772,8 +772,8 @@ Workspace.prototype = { let numCols = Math.ceil(Math.sqrt(numWindows)); let numRows = Math.ceil(numWindows/numCols); - let curRow = Math.floor(this._kbWindowIndex/numCols); - let curCol = this._kbWindowIndex % numCols; + let curRow = Math.floor(currentIndex/numCols); + let curCol = currentIndex % numCols; let calcNewIndex = function(rowDelta) { let newIndex = (curRow + rowDelta) * numCols + curCol; @@ -805,20 +805,39 @@ Workspace.prototype = { }; if (symbol === Clutter.Down) { - this._kbWindowIndex = calcNewIndex(1); + nextIndex = calcNewIndex(1); } if (symbol === Clutter.Up) { - this._kbWindowIndex = calcNewIndex(-1); + nextIndex = calcNewIndex(-1); } } - else if (symbol === Clutter.Left || symbol === Clutter.Up) { - this._kbWindowIndex = (this._kbWindowIndex < 1 ? numWindows : this._kbWindowIndex) - 1; + else if (symbol === Clutter.Left || symbol === Clutter.ISO_Left_Tab || symbol === Clutter.Up) { + nextIndex = (currentIndex < 1 ? numWindows : currentIndex) - 1; + } + else if (symbol === Clutter.Right || symbol === Clutter.Tab || symbol === Clutter.Down) { + nextIndex = (currentIndex + 1) % numWindows; + } + else if (symbol === Clutter.Home) { + nextIndex = 0; + } + else if (symbol === Clutter.End) { + nextIndex = numWindows - 1; } - else if (symbol === Clutter.Right || symbol === Clutter.Down) { - this._kbWindowIndex = (this._kbWindowIndex + 1) % numWindows; + else { + return false; // not handled } - this._windowOverlays[this._kbWindowIndex].setSelected(true); + if (currentIndex !== nextIndex) { + if (currentIndex > -1 && currentIndex < numWindows) { + this._windowOverlays[currentIndex].setSelected(false); + } + } + + this._kbWindowIndex = currentIndex = nextIndex; + if (currentIndex > -1 && currentIndex < numWindows) { + this._windowOverlays[currentIndex].setSelected(true); + } + return true; }, activateSelectedWindow: function() { @@ -1068,22 +1087,17 @@ Workspace.prototype = { let buttonOuterWidth = 0; if (this._windowOverlays[0]) { - [buttonOuterHeight, captionHeight] = this._windowOverlays[0].chromeHeights(); + [buttonOuterHeight, captionIconHeight] = this._windowOverlays[0].chromeHeights(); buttonOuterWidth = this._windowOverlays[0].chromeWidth(); - } else - [buttonOuterHeight, captionHeight] = [0, 0]; - + } else { + [buttonOuterHeight, captionIconHeight] = [0, 0]; + } let scale = Math.min((width - buttonOuterWidth) / rect.width, - (height - buttonOuterHeight - captionHeight) / rect.height, + (height - buttonOuterHeight - captionIconHeight) / rect.height, 1.0); x = Math.floor(x + (width - scale * rect.width) / 2); - - // We want to center the window in case we have just one - if (metaWindow.get_workspace().n_windows == 1) - y = Math.floor(y + (height - scale * rect.height) / 2); - else - y = Math.floor(y + height - rect.height * scale - captionHeight); + y = Math.floor(y + ((height - (scale * rect.height)) / 2) - captionIconHeight); return [x, y, scale]; }, @@ -1517,7 +1531,8 @@ Workspace.prototype = { // Tests if @win should be shown in the Overview _isOverviewWindow : function (win) { - return true; + let tracker = Cinnamon.WindowTracker.get_default(); + return tracker.is_window_interesting(win.get_meta_window()); }, // Create a clone of a (non-desktop) window and add it to the window list @@ -1584,11 +1599,10 @@ Workspace.prototype = { let gridWidth = Math.ceil(Math.sqrt(numberOfWindows)); let gridHeight = Math.ceil(numberOfWindows / gridWidth); - let fraction = 0.95 * (1. / gridWidth); + let fraction = 0.825 * (1. / gridWidth); let xCenter = (.5 / gridWidth) + ((windowIndex) % gridWidth) / gridWidth; let yCenter = (.5 / gridHeight) + Math.floor((windowIndex / gridWidth)) / gridHeight; - return [xCenter, yCenter, fraction]; }, diff --git a/js/ui/workspacesView.js b/js/ui/workspacesView.js index 356ef5b..442b453 100644 --- a/js/ui/workspacesView.js +++ b/js/ui/workspacesView.js @@ -134,7 +134,7 @@ WorkspacesView.prototype = { // this should select the last active window if (this._workspaces.length > 0) { - this._workspaces[activeWorkspaceIndex].selectAnotherWindow(Clutter.Right); + this._workspaces[activeWorkspaceIndex].selectAnotherWindow(Clutter.Home); } }, @@ -150,12 +150,6 @@ WorkspacesView.prototype = { return true; } - if (symbol === Clutter.Left || symbol === Clutter.Up - || symbol === Clutter.Right || symbol === Clutter.Down) { - activeWorkspace.selectAnotherWindow(symbol); - return true; - } - if (symbol === Clutter.Return || symbol === Clutter.KEY_space) { if (activeWorkspace.activateSelectedWindow()) { return true; @@ -163,7 +157,7 @@ WorkspacesView.prototype = { Main.overview.hide(); return true; } - return false; + return activeWorkspace.selectAnotherWindow(symbol); }, setGeometry: function(x, y, width, height, spacing) { @@ -748,12 +742,12 @@ WorkspacesDisplay.prototype = { }, _onRestacked: function() { - let stack = Main.getTabList(); + let stack = global.get_window_actors(); let stackIndices = {}; for (let i = 0; i < stack.length; i++) { // Use the stable sequence for an integer to use as a hash key - stackIndices[stack[i].get_stable_sequence()] = stack.length - i; + stackIndices[stack[i].get_meta_window().get_stable_sequence()] = i; } this.workspacesView.syncStacking(stackIndices); diff --git a/makepot b/makepot index fa223d3..b7bac2d 100755 --- a/makepot +++ b/makepot @@ -1,3 +1,3 @@ #!/bin/bash -xgettext --language=C --keyword=_ --output=cinnamon.pot src/*.c src/*/*.c js/*/*.js files/usr/share/cinnamon/applets/*/applet.js files/usr/lib/cinnamon-settings/*.py +xgettext --language=C --keyword=_ --output=cinnamon.pot src/*.c src/*/*.c js/*/*.js files/usr/share/cinnamon/applets/*/applet.js files/usr/lib/cinnamon-settings/*.py files/usr/share/cinnamon/applets/*/*.py diff --git a/man/Makefile.am b/man/Makefile.am index fe2c44f..0e5893f 100644 --- a/man/Makefile.am +++ b/man/Makefile.am @@ -1 +1,8 @@ -dist_man_MANS = cinnamon.1 +dist_man_MANS = cinnamon.1 \ + cinnamon2d.1 \ + cinnamon-extension-tool.1 \ + cinnamon-launcher.1 \ + cinnamon-menu-editor.1 \ + cinnamon-settings.1 \ + gnome-session-cinnamon.1 \ + gnome-session-cinnamon2d.1 diff --git a/man/cinnamon-extension-tool.1 b/man/cinnamon-extension-tool.1 new file mode 100644 index 0000000..3c67b14 --- /dev/null +++ b/man/cinnamon-extension-tool.1 @@ -0,0 +1,14 @@ +.TH CINNAMON-EXTENSION-TOOL 1 2012-07-23 Cinnamon "cinnamon manual" +.SH NAME +cinnamon-extension-tool \- extension management program +.SH SYNOPSIS +.SY cinnamon-extension-tool +.OP \-\-create-extension +.br +.SH DESCRIPTION +.LP +\fBcinnamon-extension-tool\fP runs the graphical user interface allowing to +manage the extensions to be used in cinnamon. +.SH "SEE ALSO" +.BR cinnamon-settings (1) + diff --git a/man/cinnamon-launcher.1 b/man/cinnamon-launcher.1 new file mode 100644 index 0000000..281e3df --- /dev/null +++ b/man/cinnamon-launcher.1 @@ -0,0 +1,12 @@ +.TH CINNAMON-LAUNCHER 1 2012-07-29 Cinnamon "cinnamon manual" +.SH NAME +cinnamon-launcher \- Start or restart a cinnamon session +.SH SYNOPSIS +.SY cinnamon-launcher +.SH DESCRIPTION +.LP +\fBcinnamon-launcher\fP runs the graphical user interface allowing to +start or restart a session of cinnamon desktop. +.SH "SEE ALSO" +.BR cinnamon (1) + diff --git a/man/cinnamon-menu-editor.1 b/man/cinnamon-menu-editor.1 new file mode 100644 index 0000000..b80821e --- /dev/null +++ b/man/cinnamon-menu-editor.1 @@ -0,0 +1,12 @@ +.TH CINNAMON-MENU-EDITOR 1 2012-07-23 Cinnamon "cinnamon manual" +.SH NAME +cinnamon-menu-editor \- Editor for the panel menu +.SH SYNOPSIS +.SY cinnamon-menu-editor +.SH DESCRIPTION +.LP +\fBcinnamon-menu-editor\fP runs the graphical user interface allowing to +edit the menu displayed in cinnamon. +.SH "SEE ALSO" +.BR cinnamon-settings (1) + diff --git a/man/cinnamon-settings.1 b/man/cinnamon-settings.1 new file mode 100644 index 0000000..2bdc00d --- /dev/null +++ b/man/cinnamon-settings.1 @@ -0,0 +1,12 @@ +.TH CINNAMON-SETTINGS 1 2012-07-23 Cinnamon "cinnamon manual" +.SH NAME +cinnamon-settings \- Configuration panel for cinnamon +.SH SYNOPSIS +.SY cinnamon-settings +.SH DESCRIPTION +.LP +\fBcinnamon-settings\fP runs the graphical user interface allowing to +configure cinnamon +.SH "SEE ALSO" +.BR cinnamon-menu-editor (1) + diff --git a/man/cinnamon2d.1 b/man/cinnamon2d.1 new file mode 100644 index 0000000..961a7a5 --- /dev/null +++ b/man/cinnamon2d.1 @@ -0,0 +1 @@ +.so man1/cinnamon.1 diff --git a/man/gnome-session-cinnamon.1 b/man/gnome-session-cinnamon.1 new file mode 100644 index 0000000..63b59c1 --- /dev/null +++ b/man/gnome-session-cinnamon.1 @@ -0,0 +1 @@ +.so man1/gnome-session.1 diff --git a/man/gnome-session-cinnamon2d.1 b/man/gnome-session-cinnamon2d.1 new file mode 100644 index 0000000..63b59c1 --- /dev/null +++ b/man/gnome-session-cinnamon2d.1 @@ -0,0 +1 @@ +.so man1/gnome-session.1 diff --git a/src/Makefile-hotplug-sniffer.am b/src/Makefile-hotplug-sniffer.am index e38966f..b78ecf8 100644 --- a/src/Makefile-hotplug-sniffer.am +++ b/src/Makefile-hotplug-sniffer.am @@ -1,6 +1,6 @@ service_in_files += hotplug-sniffer/org.Cinnamon.HotplugSniffer.service.in -libexec_PROGRAMS += cinnamon-hotplug-sniffer +pkglibexec_PROGRAMS += cinnamon-hotplug-sniffer cinnamon_hotplug_sniffer_SOURCES = \ hotplug-sniffer/hotplug-mimetypes.h \ @@ -15,7 +15,7 @@ cinnamon_hotplug_sniffer_CFLAGS = \ $(CINNAMON_HOTPLUG_SNIFFER_CFLAGS) \ $(NULL) -cinnamon_hotplug_sniffer_LDFLAGS = \ +cinnamon_hotplug_sniffer_LDADD = \ $(CINNAMON_HOTPLUG_SNIFFER_LIBS) \ $(NULL) diff --git a/src/Makefile-st.am b/src/Makefile-st.am index e9ad239..3167bb2 100644 --- a/src/Makefile-st.am +++ b/src/Makefile-st.am @@ -175,7 +175,6 @@ libst_1_0_la_SOURCES = \ $(st_built_sources) \ $(NULL) libst_1_0_la_CPPFLAGS = $(st_cflags) -libst_1_0_la_LDFLAGS = $(LDADD) noinst_PROGRAMS += test-theme diff --git a/src/Makefile-tray.am b/src/Makefile-tray.am index 1377e0c..2d32c9f 100644 --- a/src/Makefile-tray.am +++ b/src/Makefile-tray.am @@ -49,7 +49,6 @@ libtray_la_SOURCES = \ $(tray_built_sources) \ $(NULL) libtray_la_CPPFLAGS = $(tray_cflags) -libtray_la_LDFLAGS = $(LDADD) CLEANFILES += $(TRAY_STAMP_FILES) $(BUILT_SOURCES) diff --git a/src/Makefile.am b/src/Makefile.am index d6507c5..63193f0 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -3,7 +3,7 @@ BUILT_SOURCES = CLEANFILES = EXTRA_DIST = bin_SCRIPTS = -libexec_PROGRAMS = +pkglibexec_PROGRAMS = noinst_LTLIBRARIES = noinst_PROGRAMS = service_in_files = @@ -22,7 +22,9 @@ service_DATA = $(service_in_files:.service.in=.service) %.service: %.service.in Makefile $(AM_V_GEN) \ [ -d $(@D) ] || $(mkdir_p) $(@D) ; \ - sed -e "s|\@libexecdir\@|$(libexecdir)|" $< > $@.tmp && mv $@.tmp $@ + sed -e "s|\@libexecdir\@|$(libexecdir)|" \ + -e "s|\@pkglibexecdir\@|$(pkglibexecdir)|" $< > $@.tmp && mv $@.tmp $@ + CLEANFILES += $(service_DATA) CLEANFILES += $(gir_DATA) $(typelib_DATA) @@ -54,6 +56,7 @@ generated_script_substitutions = \ -e "s|@libdir[@]|$(libdir)|" \ -e "s|@JHBUILD_TYPELIBDIR[@]|$(JHBUILD_TYPELIBDIR)|" \ -e "s|@pkgdatadir[@]|$(pkgdatadir)|" \ + -e "s|@pkglibexecdir[@]|$(pkglibexecdir)|" \ -e "s|@PYTHON[@]|$(PYTHON)|" \ -e "s|@VERSION[@]|$(VERSION)|" \ -e "s|@sysconfdir[@]|$(sysconfdir)|" @@ -82,6 +85,7 @@ cinnamon_cflags = \ -DCINNAMON_LIBEXECDIR=\"$(libexecdir)\" \ -DCINNAMON_DATADIR=\"$(pkgdatadir)\" \ -DCINNAMON_PKGLIBDIR=\"$(pkglibdir)\" \ + -DCINNAMON_PKGLIBEXECDIR=\"$(pkglibexecdir)\" \ -DJSDIR=\"$(pkgdatadir)/js\" privlibdir = $(pkglibdir) @@ -110,6 +114,8 @@ cinnamon_public_headers_h = \ cinnamon-mount-operation.h \ cinnamon-network-agent.h \ cinnamon-perf-log.h \ + cinnamon-screenshot.h \ + cinnamon-screen-grabber.h \ cinnamon-slicer.h \ cinnamon-stack.h \ cinnamon-tray-icon.h \ @@ -147,6 +153,8 @@ libcinnamon_la_SOURCES = \ cinnamon-perf-log.c \ cinnamon-polkit-authentication-agent.h \ cinnamon-polkit-authentication-agent.c \ + cinnamon-screenshot.c \ + cinnamon-screen-grabber.c \ cinnamon-slicer.c \ cinnamon-stack.c \ cinnamon-tray-icon.c \ @@ -191,7 +199,7 @@ endif BUILD_RECORDER ######################################## -libexec_PROGRAMS += cinnamon-perf-helper +pkglibexec_PROGRAMS += cinnamon-perf-helper cinnamon_perf_helper_SOURCES = cinnamon-perf-helper.c cinnamon_perf_helper_CPPFLAGS = $(CINNAMON_PERF_HELPER_CFLAGS) @@ -272,7 +280,7 @@ Cinnamon_0_1_gir_CFLAGS = $(libcinnamon_la_CPPFLAGS) -I $(srcdir) Cinnamon_0_1_gir_LIBS = libcinnamon.la Cinnamon_0_1_gir_FILES = $(libcinnamon_la_gir_sources) Cinnamon_0_1_gir_SCANNERFLAGS = --include-uninstalled=$(builddir)/St-1.0.gir \ - --add-include-path=$(MUFFIN_GIR_DIR) -L $(BLUETOOTH_DIR) + --add-include-path=$(MUFFIN_GIR_DIR) $(if $(BLUETOOTH_DIR),-L $(BLUETOOTH_DIR),) INTROSPECTION_GIRS += Cinnamon-0.1.gir CLEANFILES += Cinnamon-0.1.gir diff --git a/src/cinnamon-app-system.c b/src/cinnamon-app-system.c index 85717da..d0d66db 100644 --- a/src/cinnamon-app-system.c +++ b/src/cinnamon-app-system.c @@ -102,7 +102,7 @@ cinnamon_app_system_init (CinnamonAppSystem *self) NULL, (GDestroyNotify)g_object_unref); - /* For now, we want to pick up Evince, Nautilus, etc. We'll + /* For now, we want to pick up Evince, Nemo, etc. We'll * handle NODISPLAY semantics at a higher level or investigate them * case by case. */ diff --git a/src/cinnamon-app.c b/src/cinnamon-app.c index b58c3cb..d13784f 100644 --- a/src/cinnamon-app.c +++ b/src/cinnamon-app.c @@ -1083,6 +1083,7 @@ cinnamon_app_launch (CinnamonApp *app, gboolean ret; CinnamonGlobal *global; MetaScreen *screen; + GdkDisplay *gdisplay; if (startup_id) *startup_id = NULL; @@ -1101,6 +1102,7 @@ cinnamon_app_launch (CinnamonApp *app, global = cinnamon_global_get (); screen = cinnamon_global_get_screen (global); + gdisplay = gdk_screen_get_display (cinnamon_global_get_gdk_screen (global)); if (timestamp == 0) timestamp = cinnamon_global_get_current_time (global); @@ -1108,7 +1110,7 @@ cinnamon_app_launch (CinnamonApp *app, if (workspace < 0) workspace = meta_screen_get_active_workspace_index (screen); - context = gdk_app_launch_context_new (); + context = gdk_display_get_app_launch_context (gdisplay); gdk_app_launch_context_set_timestamp (context, timestamp); gdk_app_launch_context_set_desktop (context, workspace); diff --git a/src/cinnamon-global-private.h b/src/cinnamon-global-private.h index 9f20970..c120361 100644 --- a/src/cinnamon-global-private.h +++ b/src/cinnamon-global-private.h @@ -16,18 +16,4 @@ GjsContext *_cinnamon_global_get_gjs_context (CinnamonGlobal *global); gboolean _cinnamon_global_check_xdnd_event (CinnamonGlobal *global, XEvent *xev); -/* Used for async screenshot grabbing */ -typedef struct _screenshot_data { - CinnamonGlobal *global; - - char *filename; - - int x; - int y; - int width; - int height; - - CinnamonGlobalScreenshotCallback callback; -} _screenshot_data; - #endif /* __CINNAMON_GLOBAL_PRIVATE_H__ */ diff --git a/src/cinnamon-global.c b/src/cinnamon-global.c index 7bfbeef..54d930b 100644 --- a/src/cinnamon-global.c +++ b/src/cinnamon-global.c @@ -634,6 +634,7 @@ cinnamon_global_set_cursor (CinnamonGlobal *global, break; case CINNAMON_CURSOR_POINTING_HAND: cursor_type = GDK_HAND2; + break; case CINNAMON_CURSOR_DND_UNSUPPORTED_TARGET: cursor_type = GDK_X_CURSOR; break; @@ -645,7 +646,7 @@ cinnamon_global_set_cursor (CinnamonGlobal *global, gdk_window_set_cursor (global->stage_gdk_window, cursor); - gdk_cursor_unref (cursor); + g_object_unref (cursor); } /** @@ -1437,7 +1438,7 @@ cinnamon_global_get_memory_info (CinnamonGlobal *global, JSContext *context; gint64 now; - memset (meminfo, 0, sizeof (meminfo)); + memset (meminfo, 0, sizeof (*meminfo)); #ifdef HAVE_MALLINFO { struct mallinfo info = mallinfo (); @@ -1536,9 +1537,18 @@ cinnamon_global_get_pointer (CinnamonGlobal *global, int *y, ClutterModifierType *mods) { + GdkDeviceManager *gmanager; + GdkDevice *gdevice; + GdkScreen *gscreen; GdkModifierType raw_mods; - - gdk_display_get_pointer (global->gdk_display, NULL, x, y, &raw_mods); + + gmanager = gdk_display_get_device_manager (global->gdk_display); + gdevice = gdk_device_manager_get_client_pointer (gmanager); + gdk_device_get_position (gdevice, &gscreen, x, y); + gdk_device_get_state (gdevice, + gdk_screen_get_root_window (gscreen), + NULL, + &raw_mods); *mods = raw_mods & GDK_MODIFIER_MASK; } @@ -1554,10 +1564,19 @@ void cinnamon_global_sync_pointer (CinnamonGlobal *global) { int x, y; + GdkDeviceManager *gmanager; + GdkDevice *gdevice; + GdkScreen *gscreen; GdkModifierType mods; ClutterMotionEvent event; - gdk_display_get_pointer (global->gdk_display, NULL, &x, &y, &mods); + gmanager = gdk_display_get_device_manager (global->gdk_display); + gdevice = gdk_device_manager_get_client_pointer (gmanager); + gdk_device_get_position (gdevice, &gscreen, &x, &y); + gdk_device_get_state (gdevice, + gdk_screen_get_root_window (gscreen), + NULL, + &mods); event.type = CLUTTER_MOTION; event.time = cinnamon_global_get_current_time (global); @@ -1670,7 +1689,8 @@ cinnamon_global_create_app_launch_context (CinnamonGlobal *global) { GdkAppLaunchContext *context; - context = gdk_app_launch_context_new (); + context = gdk_display_get_app_launch_context (global->gdk_display); + gdk_app_launch_context_set_timestamp (context, cinnamon_global_get_current_time (global)); // Make sure that the app is opened on the current workspace even if @@ -1910,231 +1930,3 @@ gboolean _cinnamon_global_check_xdnd_event (CinnamonGlobal *global, return FALSE; } - -static void -grab_screenshot (ClutterActor *stage, - _screenshot_data *screenshot_data) -{ - MetaScreen *screen = cinnamon_global_get_screen (screenshot_data->global); - cairo_status_t status; - cairo_surface_t *image; - guchar *data; - int width, height; - - meta_screen_get_size (screen, &width, &height); - image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); - data = cairo_image_surface_get_data (image); - - cogl_flush(); - - cogl_read_pixels (0, 0, width, height, COGL_READ_PIXELS_COLOR_BUFFER, CLUTTER_CAIRO_FORMAT_ARGB32, data); - - cairo_surface_mark_dirty (image); - - if (meta_screen_get_n_monitors (screen) > 1) - { - cairo_region_t *screen_region = cairo_region_create (); - cairo_region_t *stage_region; - MetaRectangle monitor_rect; - cairo_rectangle_int_t stage_rect; - int i; - cairo_t *cr; - - for (i = meta_screen_get_n_monitors (screen) - 1; i >= 0; i--) - { - meta_screen_get_monitor_geometry (screen, i, &monitor_rect); - cairo_region_union_rectangle (screen_region, (const cairo_rectangle_int_t *) &monitor_rect); - } - - stage_rect.x = 0; - stage_rect.y = 0; - stage_rect.width = width; - stage_rect.height = height; - - stage_region = cairo_region_create_rectangle ((const cairo_rectangle_int_t *) &stage_rect); - cairo_region_xor (stage_region, screen_region); - cairo_region_destroy (screen_region); - - cr = cairo_create (image); - - for (i = 0; i < cairo_region_num_rectangles (stage_region); i++) - { - cairo_rectangle_int_t rect; - cairo_region_get_rectangle (stage_region, i, &rect); - cairo_rectangle (cr, (double) rect.x, (double) rect.y, (double) rect.width, (double) rect.height); - cairo_fill (cr); - } - - cairo_destroy (cr); - cairo_region_destroy (stage_region); - } - - - status = cairo_surface_write_to_png (image, screenshot_data->filename); - cairo_surface_destroy (image); - - if (screenshot_data->callback) - screenshot_data->callback (screenshot_data->global, status == CAIRO_STATUS_SUCCESS); - - g_signal_handlers_disconnect_by_func (stage, (void *)grab_screenshot, (gpointer)screenshot_data); - g_free (screenshot_data->filename); - g_free (screenshot_data); -} - -static void -grab_area_screenshot (ClutterActor *stage, - _screenshot_data *screenshot_data) -{ - cairo_status_t status; - cairo_surface_t *image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, screenshot_data->width, screenshot_data->height); - guchar *data = cairo_image_surface_get_data (image); - - cogl_flush(); - - cogl_read_pixels (screenshot_data->x, screenshot_data->y, screenshot_data->width, screenshot_data->height, - COGL_READ_PIXELS_COLOR_BUFFER, CLUTTER_CAIRO_FORMAT_ARGB32, data); - - cairo_surface_mark_dirty (image); - status = cairo_surface_write_to_png (image, screenshot_data->filename); - cairo_surface_destroy (image); - - if (screenshot_data->callback) - screenshot_data->callback (screenshot_data->global, status == CAIRO_STATUS_SUCCESS); - - g_signal_handlers_disconnect_by_func (stage, (void *)grab_area_screenshot, (gpointer)screenshot_data); - g_free (screenshot_data->filename); - g_free (screenshot_data); -} - -/** - * cinnamon_global_screenshot: - * @global: the #CinnamonGlobal - * @filename: The filename for the screenshot - * @callback: (scope async): function to call returning success or failure - * of the async grabbing - * - * Takes a screenshot of the whole screen - * in @filename as png image. - * - */ -void -cinnamon_global_screenshot (CinnamonGlobal *global, - const char *filename, - CinnamonGlobalScreenshotCallback callback) -{ - ClutterActor *stage; - _screenshot_data *data = g_new0 (_screenshot_data, 1); - - data->global = global; - data->filename = g_strdup (filename); - data->callback = callback; - - stage = CLUTTER_ACTOR (cinnamon_global_get_stage (global)); - - g_signal_connect_after (stage, "paint", G_CALLBACK (grab_screenshot), (gpointer)data); - - clutter_actor_queue_redraw (stage); -} - -/** - * cinnamon_global_screenshot_area: - * @global: the #CinnamonGlobal - * @x: The X coordinate of the area - * @y: The Y coordinate of the area - * @width: The width of the area - * @height: The height of the area - * @filename: The filename for the screenshot - * @callback: (scope async): function to call returning success or failure - * of the async grabbing - * - * Takes a screenshot of the passed in area and saves it - * in @filename as png image. - * - */ -void -cinnamon_global_screenshot_area (CinnamonGlobal *global, - int x, - int y, - int width, - int height, - const char *filename, - CinnamonGlobalScreenshotCallback callback) -{ - ClutterActor *stage; - _screenshot_data *data = g_new0 (_screenshot_data, 1); - - data->global = global; - data->filename = g_strdup (filename); - data->x = x; - data->y = y; - data->width = width; - data->height = height; - data->callback = callback; - - stage = CLUTTER_ACTOR (cinnamon_global_get_stage (global)); - - g_signal_connect_after (stage, "paint", G_CALLBACK (grab_area_screenshot), (gpointer)data); - - clutter_actor_queue_redraw (stage); -} - -/** - * cinnamon_global_screenshot_window: - * @global: the #CinnamonGlobal - * @include_frame: Whether to include the frame or not - * @filename: The filename for the screenshot - * - * Takes a screenshot of the focused window (optionally omitting the frame) - * in @filename as png image. - * - * Return value: success or failure. - */ -gboolean -cinnamon_global_screenshot_window (CinnamonGlobal *global, - gboolean include_frame, - const char *filename) -{ - CoglHandle texture; - cairo_surface_t *image; - guchar *data; - - MetaScreen *screen = meta_plugin_get_screen (global->plugin); - MetaDisplay *display = meta_screen_get_display (screen); - MetaWindow *window = meta_display_get_focus_window (display); - ClutterActor *window_actor; - - cairo_status_t status; - - window_actor = CLUTTER_ACTOR (meta_window_get_compositor_private (window)); - texture = clutter_texture_get_cogl_texture (CLUTTER_TEXTURE (meta_window_actor_get_texture (META_WINDOW_ACTOR (window_actor)))); - - if (!include_frame) - { - MetaRectangle *window_rect = meta_window_get_rect (window); - texture = cogl_texture_new_from_sub_texture (texture, - window_rect->x, - window_rect->y, - window_rect->width, - window_rect->height); - - image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, - window_rect->width, - window_rect->height); - } - else - image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, - clutter_actor_get_width (window_actor), - clutter_actor_get_height (window_actor)); - - data = cairo_image_surface_get_data (image); - - cogl_flush(); - - cogl_texture_get_data (texture, CLUTTER_CAIRO_FORMAT_ARGB32, 0, data); - - cairo_surface_mark_dirty (image); - status = cairo_surface_write_to_png (image, filename); - cairo_surface_destroy (image); - - return status == CAIRO_STATUS_SUCCESS; -} diff --git a/src/cinnamon-global.h b/src/cinnamon-global.h index 13dca2d..44d0857 100644 --- a/src/cinnamon-global.h +++ b/src/cinnamon-global.h @@ -144,23 +144,6 @@ void cinnamon_global_init_xdnd (CinnamonGlobal *global); void cinnamon_global_reexec_self (CinnamonGlobal *global); -typedef void (*CinnamonGlobalScreenshotCallback) (CinnamonGlobal *global, gboolean success); - -void cinnamon_global_screenshot_area (CinnamonGlobal *global, - int x, - int y, - int width, - int height, - const char *filename, - CinnamonGlobalScreenshotCallback callback); - -gboolean cinnamon_global_screenshot_window (CinnamonGlobal *global, - gboolean include_frame, - const char *filename); - -void cinnamon_global_screenshot (CinnamonGlobal *global, - const char *filename, - CinnamonGlobalScreenshotCallback callback); G_END_DECLS #endif /* __CINNAMON_GLOBAL_H__ */ diff --git a/src/cinnamon-gtk-embed.c b/src/cinnamon-gtk-embed.c index 2d501fd..37a03a9 100644 --- a/src/cinnamon-gtk-embed.c +++ b/src/cinnamon-gtk-embed.c @@ -136,10 +136,11 @@ cinnamon_gtk_embed_get_preferred_width (ClutterActor *actor, if (embed->priv->window && gtk_widget_get_visible (GTK_WIDGET (embed->priv->window))) { - GtkRequisition requisition; - gtk_widget_size_request (GTK_WIDGET (embed->priv->window), &requisition); + GtkRequisition min_req, natural_req; + gtk_widget_get_preferred_size (GTK_WIDGET (embed->priv->window), &min_req, &natural_req); - *min_width_p = *natural_width_p = requisition.width; + *min_width_p = min_req.width; + *natural_width_p = natural_req.width; } else *min_width_p = *natural_width_p = 0; @@ -156,10 +157,11 @@ cinnamon_gtk_embed_get_preferred_height (ClutterActor *actor, if (embed->priv->window && gtk_widget_get_visible (GTK_WIDGET (embed->priv->window))) { - GtkRequisition requisition; - gtk_widget_size_request (GTK_WIDGET (embed->priv->window), &requisition); + GtkRequisition min_req, natural_req; + gtk_widget_get_preferred_size (GTK_WIDGET (embed->priv->window), &min_req, &natural_req); - *min_height_p = *natural_height_p = requisition.height; + *min_height_p = min_req.height; + *natural_height_p = natural_req.height; } else *min_height_p = *natural_height_p = 0; diff --git a/src/cinnamon-jhbuild.in b/src/cinnamon-jhbuild.in index 00f6a46..960e04b 100755 --- a/src/cinnamon-jhbuild.in +++ b/src/cinnamon-jhbuild.in @@ -182,7 +182,7 @@ def start_perf_helper(): if running_from_source_tree: perf_helper_path = os.path.join(self_dir, "cinnamon-perf-helper") else: - perf_helper_path = "@libexecdir@/cinnamon-perf-helper" + perf_helper_path = "@pkglibexecdir@/cinnamon-perf-helper" subprocess.Popen([perf_helper_path]) wait_for_dbus_name (PERF_HELPER_NAME) diff --git a/src/cinnamon-recorder.c b/src/cinnamon-recorder.c index c6f49b7..5fe1ce9 100644 --- a/src/cinnamon-recorder.c +++ b/src/cinnamon-recorder.c @@ -50,7 +50,9 @@ struct _CinnamonRecorder { gboolean have_pointer; int pointer_x; int pointer_y; - + + guint height_adjust; // Y adjustment from bottom panel, if any + gboolean have_xfixes; int xfixes_event_base; @@ -156,6 +158,9 @@ G_DEFINE_TYPE(CinnamonRecorder, cinnamon_recorder, G_TYPE_OBJECT); */ #define DEFAULT_MEMORY_TARGET (512*1024) +#define PANEL_HEIGHT_KEY "panel-bottom-height" +#define SCHEMA_CINNAMON "org.cinnamon" + /* Create an emblem to show at the lower-left corner of the stage while * recording. The emblem is drawn *after* we record the frame so doesn't * show up in the frame. @@ -261,7 +266,20 @@ cinnamon_recorder_init (CinnamonRecorder *recorder) gst_init (NULL, NULL); cinnamon_recorder_src_register (); - + + // Get bottom panel height from settings so we don't overlap it + // with our recording indicators. + GVariant *variant = NULL; + GSettings *desktop_settings; + + desktop_settings = g_settings_new (SCHEMA_CINNAMON); + variant = g_settings_get_value (desktop_settings, PANEL_HEIGHT_KEY); + g_variant_get (variant, "i", &recorder->height_adjust); + + g_variant_unref (variant); + g_object_unref (desktop_settings); + + recorder->recording_icon = create_recording_icon (); recorder->memory_target = get_memory_target(); @@ -484,14 +502,14 @@ recorder_draw_buffer_meter (CinnamonRecorder *recorder) fill_level = MIN (60, (recorder->memory_used * 60) / recorder->memory_target); /* A hollow rectangle filled from the left to fill_level */ - cogl_rectangle (recorder->stage_width - 64, recorder->stage_height - 10, - recorder->stage_width - 2, recorder->stage_height - 9); - cogl_rectangle (recorder->stage_width - 64, recorder->stage_height - 9, - recorder->stage_width - (63 - fill_level), recorder->stage_height - 3); - cogl_rectangle (recorder->stage_width - 3, recorder->stage_height - 9, - recorder->stage_width - 2, recorder->stage_height - 3); - cogl_rectangle (recorder->stage_width - 64, recorder->stage_height - 3, - recorder->stage_width - 2, recorder->stage_height - 2); + cogl_rectangle (recorder->stage_width - 64, recorder->stage_height - recorder->height_adjust - 10, + recorder->stage_width - 2, recorder->stage_height - recorder->height_adjust - 9); + cogl_rectangle (recorder->stage_width - 64, recorder->stage_height - recorder->height_adjust - 9, + recorder->stage_width - (63 - fill_level), recorder->stage_height - recorder->height_adjust - 3); + cogl_rectangle (recorder->stage_width - 3, recorder->stage_height - recorder->height_adjust - 9, + recorder->stage_width - 2, recorder->stage_height - recorder->height_adjust - 3); + cogl_rectangle (recorder->stage_width - 64, recorder->stage_height - recorder->height_adjust - 3, + recorder->stage_width - 2, recorder->stage_height - recorder->height_adjust - 2); } /* We want to time-stamp each frame based on the actual time it was @@ -557,8 +575,8 @@ recorder_on_stage_paint (ClutterActor *actor, recorder_record_frame (recorder); cogl_set_source_texture (recorder->recording_icon); - cogl_rectangle (recorder->stage_width - 32, recorder->stage_height - 42, - recorder->stage_width, recorder->stage_height - 10); + cogl_rectangle (recorder->stage_width - 32, recorder->stage_height - recorder->height_adjust - 42, + recorder->stage_width, recorder->stage_height - recorder->height_adjust - 10); } if (recorder->state == RECORDER_STATE_RECORDING || recorder->memory_used != 0) diff --git a/src/cinnamon-screen-grabber.c b/src/cinnamon-screen-grabber.c new file mode 100644 index 0000000..1ee7a8e --- /dev/null +++ b/src/cinnamon-screen-grabber.c @@ -0,0 +1,210 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + +#include + +#include +#include +#include +#include +#include + +#include "cinnamon-screen-grabber.h" + +PFNGLBINDBUFFERARBPROC pf_glBindBufferARB; +PFNGLBUFFERDATAARBPROC pf_glBufferDataARB; +PFNGLDELETEBUFFERSARBPROC pf_glDeleteBuffersARB; +PFNGLGENBUFFERSARBPROC pf_glGenBuffersARB; +PFNGLMAPBUFFERARBPROC pf_glMapBufferARB; +PFNGLUNMAPBUFFERARBPROC pf_glUnmapBufferARB; + +struct _CinnamonScreenGrabberClass +{ + GObjectClass parent_class; +}; + +struct _CinnamonScreenGrabber +{ + GObject parent_instance; + + int have_pixel_buffers; + int have_pack_invert; + int width, height; + GLuint pixel_buffer; +}; + +G_DEFINE_TYPE(CinnamonScreenGrabber, cinnamon_screen_grabber, G_TYPE_OBJECT); + +static void +cinnamon_screen_grabber_finalize (GObject *gobject) +{ + CinnamonScreenGrabber *grabber = CINNAMON_SCREEN_GRABBER (gobject); + + if (grabber->pixel_buffer != 0) + pf_glDeleteBuffersARB (1, &grabber->pixel_buffer); +} + +static void +cinnamon_screen_grabber_class_init (CinnamonScreenGrabberClass *grabber_class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (grabber_class); + + gobject_class->finalize = cinnamon_screen_grabber_finalize; +} + +static void +cinnamon_screen_grabber_init (CinnamonScreenGrabber *grabber) +{ + grabber->have_pixel_buffers = -1; + grabber->width = -1; + grabber->height= -1; + grabber->pixel_buffer = 0; +} + +CinnamonScreenGrabber * +cinnamon_screen_grabber_new (void) +{ + return g_object_new (CINNAMON_TYPE_SCREEN_GRABBER, NULL); +} + +/** + * cinnamon_screen_grabber_grab: + * x: X coordinate of the rectangle to grab + * y: Y coordinate of the rectangle to grab + * width: width of the rectangle to grab + * height: heigth of the rectangle to grab + * + * Grabs pixel data from a portion of the screen. + * + * Return value: buffer holding the grabbed data. The data is stored as 32-bit + * words with native-endian xRGB pixels (i.e., the same as CAIRO_FORMAT_RGB24) + * with no padding on the rows. So, the size of the buffer is width * height * 4 + * bytes. Free with g_free(). + **/ +guchar * +cinnamon_screen_grabber_grab (CinnamonScreenGrabber *grabber, + int x, + int y, + int width, + int height) +{ + guchar *data; + gsize row_bytes; + gsize data_size; + + row_bytes = width * 4; + data_size = row_bytes * height; + data = g_malloc (data_size); + + if (grabber->have_pixel_buffers == -1) + { + const GLubyte* extensions = glGetString (GL_EXTENSIONS); + grabber->have_pixel_buffers = strstr ((const char *)extensions, "GL_EXT_pixel_buffer_object") != NULL; + grabber->have_pack_invert = strstr ((const char *)extensions, "GL_MESA_pack_invert") != NULL; + } + + if (grabber->have_pixel_buffers) + { + GLubyte *mapped_data; + GLint old_swap_bytes, old_lsb_first, old_row_length, old_skip_pixels, old_skip_rows, old_alignment; + GLint old_pack_invert = GL_FALSE; + GLint vp_size[4]; + guchar *src_row, *dest_row; + int i; + + cogl_flush (); + + if (pf_glBindBufferARB == NULL) + { + pf_glBindBufferARB = (PFNGLBINDBUFFERARBPROC) cogl_get_proc_address ("glBindBufferARB"); + pf_glBufferDataARB = (PFNGLBUFFERDATAARBPROC) cogl_get_proc_address ("glBufferDataARB"); + pf_glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC) cogl_get_proc_address ("glDeleteBuffersARB"); + pf_glGenBuffersARB = (PFNGLGENBUFFERSARBPROC) cogl_get_proc_address ("glGenBuffersARB"); + pf_glMapBufferARB = (PFNGLMAPBUFFERARBPROC) cogl_get_proc_address ("glMapBufferARB"); + pf_glUnmapBufferARB = (PFNGLUNMAPBUFFERARBPROC) cogl_get_proc_address ("glUnmapBufferARB"); + } + + glGetIntegerv (GL_PACK_SWAP_BYTES, &old_swap_bytes); + glGetIntegerv (GL_PACK_LSB_FIRST, &old_lsb_first); + glGetIntegerv (GL_PACK_ROW_LENGTH, &old_row_length); + glGetIntegerv (GL_PACK_SKIP_PIXELS, &old_skip_pixels); + glGetIntegerv (GL_PACK_SKIP_ROWS, &old_skip_rows); + glGetIntegerv (GL_PACK_ALIGNMENT, &old_alignment); + + glPixelStorei (GL_PACK_SWAP_BYTES, GL_FALSE); + glPixelStorei (GL_PACK_LSB_FIRST, GL_FALSE); + glPixelStorei (GL_PACK_ROW_LENGTH, 0); + glPixelStorei (GL_PACK_SKIP_PIXELS, 0); + glPixelStorei (GL_PACK_SKIP_ROWS, 0); + glPixelStorei (GL_PACK_ALIGNMENT, 1); + + if (grabber->have_pack_invert) + { + glGetIntegerv (GL_PACK_INVERT_MESA, &old_pack_invert); + glPixelStorei (GL_PACK_INVERT_MESA, GL_FALSE); + } + + if (grabber->pixel_buffer != 0 && + (grabber->width != width || + grabber->height != height)) + { + pf_glDeleteBuffersARB (1, &grabber->pixel_buffer); + grabber->pixel_buffer = 0; + } + + if (grabber->pixel_buffer == 0) + { + pf_glGenBuffersARB (1, &grabber->pixel_buffer); + + pf_glBindBufferARB (GL_PIXEL_PACK_BUFFER_ARB, grabber->pixel_buffer); + pf_glBufferDataARB (GL_PIXEL_PACK_BUFFER_ARB, data_size, 0, GL_STREAM_READ_ARB); + + grabber->width = width; + grabber->height = height; + } + else + { + pf_glBindBufferARB (GL_PIXEL_PACK_BUFFER_ARB, grabber->pixel_buffer); + } + + /* In OpenGL, (x,y) specifies the bottom-left corner rather than the + * top-left */ + glGetIntegerv (GL_VIEWPORT, vp_size); + y = vp_size[3] - (y + height); + glReadPixels (x, y, width, height, GL_BGRA, GL_UNSIGNED_BYTE, 0); + + mapped_data = pf_glMapBufferARB (GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY_ARB); + + src_row = mapped_data + (height - 1) * row_bytes; + dest_row = data; + + for (i = 0; i < height; i++) + { + memcpy (dest_row, src_row, row_bytes); + src_row -= row_bytes; + dest_row += row_bytes; + } + + pf_glUnmapBufferARB (GL_PIXEL_PACK_BUFFER_ARB); + pf_glBindBufferARB (GL_PIXEL_PACK_BUFFER_ARB, 0); + + glPixelStorei (GL_PACK_SWAP_BYTES, old_swap_bytes); + glPixelStorei (GL_PACK_LSB_FIRST, old_lsb_first); + glPixelStorei (GL_PACK_ROW_LENGTH, old_row_length); + glPixelStorei (GL_PACK_SKIP_PIXELS, old_skip_pixels); + glPixelStorei (GL_PACK_SKIP_ROWS, old_skip_rows); + glPixelStorei (GL_PACK_ALIGNMENT, old_alignment); + + if (grabber->have_pack_invert) + glPixelStorei (GL_PACK_INVERT_MESA, old_pack_invert); + } + else + { + cogl_read_pixels (x, y, + width, height, + COGL_READ_PIXELS_COLOR_BUFFER, + CLUTTER_CAIRO_FORMAT_ARGB32, + data); + } + + return data; +} diff --git a/src/cinnamon-screen-grabber.h b/src/cinnamon-screen-grabber.h new file mode 100644 index 0000000..b956cdc --- /dev/null +++ b/src/cinnamon-screen-grabber.h @@ -0,0 +1,44 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +#ifndef __CINNAMON_SCREEN_GRABBER_H__ +#define __CINNAMON_SCREEN_GRABBER_H__ + +#include + +G_BEGIN_DECLS + +/** + * SECTION:cinnamon-screen-grabber + * @short_description: Grab pixel data from the screen + * + * The #CinnamonScreenGrabber object is used to download previous drawn + * content to the screen. It internally uses pixel-buffer objects if + * available, otherwise falls back to cogl_read_pixels(). + * + * If you are repeatedly grabbing images of the same size from the + * screen, it makes sense to create one #CinnamonScreenGrabber and keep + * it around. Otherwise, it's fine to simply create one as needed and + * then get rid of it. + */ + +typedef struct _CinnamonScreenGrabber CinnamonScreenGrabber; +typedef struct _CinnamonScreenGrabberClass CinnamonScreenGrabberClass; + +#define CINNAMON_TYPE_SCREEN_GRABBER (cinnamon_screen_grabber_get_type ()) +#define CINNAMON_SCREEN_GRABBER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), CINNAMON_TYPE_SCREEN_GRABBER, CinnamonScreenGrabber)) +#define CINNAMON_SCREEN_GRABBER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CINNAMON_TYPE_SCREEN_GRABBER, CinnamonScreenGrabberClass)) +#define CINNAMON_IS_SCREEN_GRABBER(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), CINNAMON_TYPE_SCREEN_GRABBER)) +#define CINNAMON_IS_SCREEN_GRABBER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CINNAMON_TYPE_SCREEN_GRABBER)) +#define CINNAMON_SCREEN_GRABBER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CINNAMON_TYPE_SCREEN_GRABBER, CinnamonScreenGrabberClass)) + +GType cinnamon_screen_grabber_get_type (void) G_GNUC_CONST; + +CinnamonScreenGrabber *cinnamon_screen_grabber_new (void); +guchar * cinnamon_screen_grabber_grab (CinnamonScreenGrabber *grabber, + int x, + int y, + int width, + int height); + +G_END_DECLS + +#endif /* __CINNAMON_SCREEN_GRABBER_H__ */ diff --git a/src/cinnamon-screenshot.c b/src/cinnamon-screenshot.c new file mode 100644 index 0000000..18f938e --- /dev/null +++ b/src/cinnamon-screenshot.c @@ -0,0 +1,404 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cinnamon-global.h" +#include "cinnamon-screen-grabber.h" +#include "cinnamon-screenshot.h" + +struct _CinnamonScreenshotClass +{ + GObjectClass parent_class; +}; + +struct _CinnamonScreenshot +{ + GObject parent_instance; + + CinnamonGlobal *global; +}; + +/* Used for async screenshot grabbing */ +typedef struct _screenshot_data { + CinnamonScreenshot *screenshot; + + char *filename; + + cairo_surface_t *image; + cairo_rectangle_int_t screenshot_area; + + gboolean include_cursor; + + CinnamonScreenshotCallback callback; +} _screenshot_data; + +G_DEFINE_TYPE(CinnamonScreenshot, cinnamon_screenshot, G_TYPE_OBJECT); + +static void +cinnamon_screenshot_class_init (CinnamonScreenshotClass *screenshot_class) +{ + (void) screenshot_class; +} + +static void +cinnamon_screenshot_init (CinnamonScreenshot *screenshot) +{ + screenshot->global = cinnamon_global_get (); +} + +static void +on_screenshot_written (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + _screenshot_data *screenshot_data = (_screenshot_data*) user_data; + if (screenshot_data->callback) + screenshot_data->callback (screenshot_data->screenshot, + g_simple_async_result_get_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (result)), + &screenshot_data->screenshot_area); + + cairo_surface_destroy (screenshot_data->image); + g_object_unref (screenshot_data->screenshot); + g_free (screenshot_data->filename); + g_free (screenshot_data); +} + +static void +write_screenshot_thread (GSimpleAsyncResult *result, + GObject *object, + GCancellable *cancellable) +{ + cairo_status_t status; + _screenshot_data *screenshot_data = g_async_result_get_user_data (G_ASYNC_RESULT (result)); + g_assert (screenshot_data != NULL); + + status = cairo_surface_write_to_png (screenshot_data->image, screenshot_data->filename); + g_simple_async_result_set_op_res_gboolean (result, status == CAIRO_STATUS_SUCCESS); +} + +static void +do_grab_screenshot (_screenshot_data *screenshot_data, + int x, + int y, + int width, + int height) +{ + CinnamonScreenGrabber *grabber; + static const cairo_user_data_key_t key; + guchar *data; + + grabber = cinnamon_screen_grabber_new (); + data = cinnamon_screen_grabber_grab (grabber, x, y, width, height); + g_object_unref (grabber); + + screenshot_data->image = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_RGB24, + width, height, width * 4); + cairo_surface_set_user_data (screenshot_data->image, &key, + data, (cairo_destroy_func_t)g_free); +} + +static void +_draw_cursor_image (cairo_surface_t *surface, + cairo_rectangle_int_t area) +{ + XFixesCursorImage *cursor_image; + + cairo_surface_t *cursor_surface; + cairo_region_t *screenshot_region; + cairo_t *cr; + + guchar *data; + int stride; + int i, j; + + cursor_image = XFixesGetCursorImage (clutter_x11_get_default_display ()); + + if (!cursor_image) + return; + + screenshot_region = cairo_region_create_rectangle (&area); + + if (!cairo_region_contains_point (screenshot_region, cursor_image->x, cursor_image->y)) + { + XFree (cursor_image); + cairo_region_destroy (screenshot_region); + return; + } + + cursor_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, cursor_image->width, cursor_image->height); + + /* The pixel data (in typical Xlib breakage) is longs even on + * 64-bit platforms, so we have to data-convert there. For simplicity, + * just do it always + */ + data = cairo_image_surface_get_data (cursor_surface); + stride = cairo_image_surface_get_stride (cursor_surface); + for (i = 0; i < cursor_image->height; i++) + for (j = 0; j < cursor_image->width; j++) + *(guint32 *)(data + i * stride + 4 * j) = cursor_image->pixels[i * cursor_image->width + j]; + + cairo_surface_mark_dirty (cursor_surface); + + cr = cairo_create (surface); + cairo_set_source_surface (cr, + cursor_surface, + cursor_image->x - cursor_image->xhot - area.x, + cursor_image->y - cursor_image->yhot - area.y); + cairo_paint (cr); + + cairo_destroy (cr); + cairo_surface_destroy (cursor_surface); + cairo_region_destroy (screenshot_region); + XFree (cursor_image); +} + +static void +grab_screenshot (ClutterActor *stage, + _screenshot_data *screenshot_data) +{ + MetaScreen *screen = cinnamon_global_get_screen (screenshot_data->screenshot->global); + int width, height; + GSimpleAsyncResult *result; + + meta_screen_get_size (screen, &width, &height); + + do_grab_screenshot (screenshot_data, 0, 0, width, height); + + if (meta_screen_get_n_monitors (screen) > 1) + { + cairo_region_t *screen_region = cairo_region_create (); + cairo_region_t *stage_region; + MetaRectangle monitor_rect; + cairo_rectangle_int_t stage_rect; + int i; + cairo_t *cr; + + for (i = meta_screen_get_n_monitors (screen) - 1; i >= 0; i--) + { + meta_screen_get_monitor_geometry (screen, i, &monitor_rect); + cairo_region_union_rectangle (screen_region, (const cairo_rectangle_int_t *) &monitor_rect); + } + + stage_rect.x = 0; + stage_rect.y = 0; + stage_rect.width = width; + stage_rect.height = height; + + stage_region = cairo_region_create_rectangle ((const cairo_rectangle_int_t *) &stage_rect); + cairo_region_xor (stage_region, screen_region); + cairo_region_destroy (screen_region); + + cr = cairo_create (screenshot_data->image); + + for (i = 0; i < cairo_region_num_rectangles (stage_region); i++) + { + cairo_rectangle_int_t rect; + cairo_region_get_rectangle (stage_region, i, &rect); + cairo_rectangle (cr, (double) rect.x, (double) rect.y, (double) rect.width, (double) rect.height); + cairo_fill (cr); + } + + cairo_destroy (cr); + cairo_region_destroy (stage_region); + } + + screenshot_data->screenshot_area.x = 0; + screenshot_data->screenshot_area.y = 0; + screenshot_data->screenshot_area.width = width; + screenshot_data->screenshot_area.height = height; + + if (screenshot_data->include_cursor) + _draw_cursor_image (screenshot_data->image, screenshot_data->screenshot_area); + + g_signal_handlers_disconnect_by_func (stage, (void *)grab_screenshot, (gpointer)screenshot_data); + + result = g_simple_async_result_new (NULL, on_screenshot_written, (gpointer)screenshot_data, grab_screenshot); + g_simple_async_result_run_in_thread (result, write_screenshot_thread, G_PRIORITY_DEFAULT, NULL); + g_object_unref (result); +} + +static void +grab_area_screenshot (ClutterActor *stage, + _screenshot_data *screenshot_data) +{ + GSimpleAsyncResult *result; + + do_grab_screenshot (screenshot_data, + screenshot_data->screenshot_area.x, + screenshot_data->screenshot_area.y, + screenshot_data->screenshot_area.width, + screenshot_data->screenshot_area.height); + + if (screenshot_data->include_cursor) + _draw_cursor_image (screenshot_data->image, screenshot_data->screenshot_area); + + g_signal_handlers_disconnect_by_func (stage, (void *)grab_area_screenshot, (gpointer)screenshot_data); + result = g_simple_async_result_new (NULL, on_screenshot_written, (gpointer)screenshot_data, grab_area_screenshot); + g_simple_async_result_run_in_thread (result, write_screenshot_thread, G_PRIORITY_DEFAULT, NULL); + g_object_unref (result); +} + +/** + * cinnamon_screenshot_screenshot: + * @screenshot: the #CinnamonScreenshot + * @include_cursor: Whether to include the cursor or not + * @filename: The filename for the screenshot + * @callback: (scope async): function to call returning success or failure + * of the async grabbing + * + * Takes a screenshot of the whole screen + * in @filename as png image. + * + */ +void +cinnamon_screenshot_screenshot (CinnamonScreenshot *screenshot, + gboolean include_cursor, + const char *filename, + CinnamonScreenshotCallback callback) +{ + ClutterActor *stage; + _screenshot_data *data = g_new0 (_screenshot_data, 1); + + data->screenshot = g_object_ref (screenshot); + data->filename = g_strdup (filename); + data->callback = callback; + data->include_cursor = include_cursor; + + stage = CLUTTER_ACTOR (cinnamon_global_get_stage (screenshot->global)); + + g_signal_connect_after (stage, "paint", G_CALLBACK (grab_screenshot), (gpointer)data); + + clutter_actor_queue_redraw (stage); +} + +/** + * cinnamon_screenshot_screenshot_area: + * @screenshot: the #CinnamonScreenshot + * @x: The X coordinate of the area + * @y: The Y coordinate of the area + * @width: The width of the area + * @height: The height of the area + * @filename: The filename for the screenshot + * @callback: (scope async): function to call returning success or failure + * of the async grabbing + * + * Takes a screenshot of the passed in area and saves it + * in @filename as png image. + * + */ +void +cinnamon_screenshot_screenshot_area (CinnamonScreenshot *screenshot, + gboolean include_cursor, + int x, + int y, + int width, + int height, + const char *filename, + CinnamonScreenshotCallback callback) +{ + ClutterActor *stage; + _screenshot_data *data = g_new0 (_screenshot_data, 1); + + data->screenshot = g_object_ref (screenshot); + data->filename = g_strdup (filename); + data->screenshot_area.x = x; + data->screenshot_area.y = y; + data->screenshot_area.width = width; + data->screenshot_area.height = height; + data->callback = callback; + data->include_cursor = include_cursor; + + stage = CLUTTER_ACTOR (cinnamon_global_get_stage (screenshot->global)); + + g_signal_connect_after (stage, "paint", G_CALLBACK (grab_area_screenshot), (gpointer)data); + + clutter_actor_queue_redraw (stage); +} + +/** + * cinnamon_screenshot_screenshot_window: + * @screenshot: the #CinnamonScreenshot + * @include_frame: Whether to include the frame or not + * @include_cursor: Whether to include the cursor or not + * @filename: The filename for the screenshot + * @callback: (scope async): function to call returning success or failure + * of the async grabbing + * + * Takes a screenshot of the focused window (optionally omitting the frame) + * in @filename as png image. + * + */ +void +cinnamon_screenshot_screenshot_window (CinnamonScreenshot *screenshot, + gboolean include_frame, + gboolean include_cursor, + const char *filename, + CinnamonScreenshotCallback callback) +{ + GSimpleAsyncResult *result; + + _screenshot_data *screenshot_data = g_new0 (_screenshot_data, 1); + + MetaScreen *screen = cinnamon_global_get_screen (screenshot->global); + MetaDisplay *display = meta_screen_get_display (screen); + MetaWindow *window = meta_display_get_focus_window (display); + ClutterActor *window_actor; + gfloat actor_x, actor_y; + MetaShapedTexture *stex; + MetaRectangle rect; + cairo_rectangle_int_t clip; + + screenshot_data->screenshot = g_object_ref (screenshot); + screenshot_data->filename = g_strdup (filename); + screenshot_data->callback = callback; + + window_actor = CLUTTER_ACTOR (meta_window_get_compositor_private (window)); + clutter_actor_get_position (window_actor, &actor_x, &actor_y); + + if (include_frame || !meta_window_get_frame (window)) + { + meta_window_get_outer_rect (window, &rect); + + screenshot_data->screenshot_area.x = rect.x; + screenshot_data->screenshot_area.y = rect.y; + + clip.x = rect.x - (gint) actor_x; + clip.y = rect.y - (gint) actor_y; + } + else + { + rect = *meta_window_get_rect (window); + + screenshot_data->screenshot_area.x = (gint) actor_x + rect.x; + screenshot_data->screenshot_area.y = (gint) actor_y + rect.y; + + clip.x = rect.x; + clip.y = rect.y; + } + + clip.width = screenshot_data->screenshot_area.width = rect.width; + clip.height = screenshot_data->screenshot_area.height = rect.height; + + stex = META_SHAPED_TEXTURE (meta_window_actor_get_texture (META_WINDOW_ACTOR (window_actor))); + screenshot_data->image = meta_shaped_texture_get_image (stex, &clip); + + if (include_cursor) + _draw_cursor_image (screenshot_data->image, screenshot_data->screenshot_area); + + result = g_simple_async_result_new (NULL, on_screenshot_written, (gpointer)screenshot_data, cinnamon_screenshot_screenshot_window); + g_simple_async_result_run_in_thread (result, write_screenshot_thread, G_PRIORITY_DEFAULT, NULL); + g_object_unref (result); +} + +CinnamonScreenshot * +cinnamon_screenshot_new (void) +{ + return g_object_new (CINNAMON_TYPE_SCREENSHOT, NULL); +} diff --git a/src/cinnamon-screenshot.h b/src/cinnamon-screenshot.h new file mode 100644 index 0000000..d65a70c --- /dev/null +++ b/src/cinnamon-screenshot.h @@ -0,0 +1,52 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +#ifndef __CINNAMON_SCREENSHOT_H__ +#define __CINNAMON_SCREENSHOT_H__ + +/** + * SECTION:cinnamon-screenshot + * @short_description: Grabs screenshots of areas and/or windows + * + * The #CinnamonScreenshot object is used to take screenshots of screen + * areas or windows and write them out as png files. + * + */ + +typedef struct _CinnamonScreenshot CinnamonScreenshot; +typedef struct _CinnamonScreenshotClass CinnamonScreenshotClass; + +#define CINNAMON_TYPE_SCREENSHOT (cinnamon_screenshot_get_type ()) +#define CINNAMON_SCREENSHOT(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), CINNAMON_TYPE_SCREENSHOT, CinnamonScreenshot)) +#define CINNAMON_SCREENSHOT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CINNAMON_TYPE_SCREENSHOT, CinnamonScreenshotClass)) +#define CINNAMON_IS_SCREENSHOT(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), CINNAMON_TYPE_SCREENSHOT)) +#define CINNAMON_IS_SCREENSHOT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CINNAMON_TYPE_SCREENSHOT)) +#define CINNAMON_SCREENSHOT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CINNAMON_TYPE_SCREENSHOT, CinnamonScreenshotClass)) + +GType cinnamon_screenshot_get_type (void) G_GNUC_CONST; + +CinnamonScreenshot *cinnamon_screenshot_new (void); + +typedef void (*CinnamonScreenshotCallback) (CinnamonScreenshot *screenshot, + gboolean success, + cairo_rectangle_int_t *screenshot_area); + +void cinnamon_screenshot_screenshot_area (CinnamonScreenshot *screenshot, + gboolean include_cursor, + int x, + int y, + int width, + int height, + const char *filename, + CinnamonScreenshotCallback callback); + +void cinnamon_screenshot_screenshot_window (CinnamonScreenshot *screenshot, + gboolean include_frame, + gboolean include_cursor, + const char *filename, + CinnamonScreenshotCallback callback); + +void cinnamon_screenshot_screenshot (CinnamonScreenshot *screenshot, + gboolean include_cursor, + const char *filename, + CinnamonScreenshotCallback callback); + +#endif /* ___CINNAMON_SCREENSHOT_H__ */ diff --git a/src/cinnamon-util.c b/src/cinnamon-util.c index e6f600f..c292fa3 100644 --- a/src/cinnamon-util.c +++ b/src/cinnamon-util.c @@ -85,7 +85,7 @@ cinnamon_util_get_file_display_for_common_files (GFile *file) { g_object_unref (compare); /* Translators: this is the same string as the one found in - * nautilus */ + * nemo */ return g_strdup (_("Home")); } @@ -94,7 +94,7 @@ cinnamon_util_get_file_display_for_common_files (GFile *file) { g_object_unref (compare); /* Translators: this is the same string as the one found in - * nautilus */ + * nemo */ return g_strdup (_("File System")); } g_object_unref (compare); diff --git a/src/cinnamon-wm.c b/src/cinnamon-wm.c index 1a0c724..555e013 100644 --- a/src/cinnamon-wm.c +++ b/src/cinnamon-wm.c @@ -78,7 +78,7 @@ cinnamon_wm_class_init (CinnamonWMClass *klass) 0, NULL, NULL, _cinnamon_marshal_VOID__OBJECT_INT_INT_INT_INT, - G_TYPE_NONE, 1, + G_TYPE_NONE, 5, META_TYPE_WINDOW_ACTOR, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT); cinnamon_wm_signals[MAP] = g_signal_new ("map", diff --git a/src/hotplug-sniffer/org.Cinnamon.HotplugSniffer.service.in b/src/hotplug-sniffer/org.Cinnamon.HotplugSniffer.service.in index 2c47a0f..e03758c 100644 --- a/src/hotplug-sniffer/org.Cinnamon.HotplugSniffer.service.in +++ b/src/hotplug-sniffer/org.Cinnamon.HotplugSniffer.service.in @@ -1,3 +1,3 @@ [D-BUS Service] Name=org.Cinnamon.HotplugSniffer -Exec=@libexecdir@/cinnamon-hotplug-sniffer +Exec=@pkglibexecdir@/cinnamon-hotplug-sniffer diff --git a/src/st/st-box-layout.c b/src/st/st-box-layout.c index 390e547..5f9f639 100644 --- a/src/st/st-box-layout.c +++ b/src/st/st-box-layout.c @@ -797,10 +797,11 @@ st_box_layout_allocate (ClutterActor *actor, else if (shrink_amount > 0) child_allocated -= shrinks[i].shrink_amount; - if (flip) + if (flip) { next_position = position - child_allocated; if (xalign == ST_ALIGN_CENTER_SPECIAL && next_position < content_box.x1) next_position = content_box.x1; + } else { next_position = position + child_allocated; if (xalign == ST_ALIGN_CENTER_SPECIAL && next_position > content_box.x2) diff --git a/src/st/st-entry.c b/src/st/st-entry.c index 72a32e5..8d5f1ba 100644 --- a/src/st/st-entry.c +++ b/src/st/st-entry.c @@ -548,38 +548,6 @@ st_entry_pick (ClutterActor *actor, clutter_actor_paint (priv->secondary_icon); } -static void -st_entry_map (ClutterActor *actor) -{ - StEntryPrivate *priv = ST_ENTRY (actor)->priv; - - CLUTTER_ACTOR_CLASS (st_entry_parent_class)->map (actor); - - clutter_actor_map (priv->entry); - - if (priv->primary_icon) - clutter_actor_map (priv->primary_icon); - - if (priv->secondary_icon) - clutter_actor_map (priv->secondary_icon); -} - -static void -st_entry_unmap (ClutterActor *actor) -{ - StEntryPrivate *priv = ST_ENTRY (actor)->priv; - - CLUTTER_ACTOR_CLASS (st_entry_parent_class)->unmap (actor); - - clutter_actor_unmap (priv->entry); - - if (priv->primary_icon) - clutter_actor_unmap (priv->primary_icon); - - if (priv->secondary_icon) - clutter_actor_unmap (priv->secondary_icon); -} - static void st_entry_clipboard_callback (StClipboard *clipboard, const gchar *text, @@ -695,8 +663,6 @@ st_entry_class_init (StEntryClass *klass) actor_class->allocate = st_entry_allocate; actor_class->paint = st_entry_paint; actor_class->pick = st_entry_pick; - actor_class->map = st_entry_map; - actor_class->unmap = st_entry_unmap; actor_class->key_press_event = st_entry_key_press_event; actor_class->key_focus_in = st_entry_key_focus_in; diff --git a/src/st/st-icon.c b/src/st/st-icon.c index d6a32f9..2569573 100644 --- a/src/st/st-icon.c +++ b/src/st/st-icon.c @@ -303,6 +303,13 @@ st_icon_style_changed (StWidget *widget) st_shadow_unref (priv->shadow_spec); priv->shadow_spec = NULL; } + + if (priv->shadow_material) + { + cogl_handle_unref (priv->shadow_material); + priv->shadow_material = COGL_INVALID_HANDLE; + } + priv->shadow_spec = st_theme_node_get_shadow (theme_node, "icon-shadow"); if (priv->shadow_spec && priv->shadow_spec->inset) diff --git a/src/st/st-im-text.c b/src/st/st-im-text.c index a4f9693..e0bdff5 100644 --- a/src/st/st-im-text.c +++ b/src/st/st-im-text.c @@ -88,12 +88,15 @@ st_im_text_dispose (GObject *object) { StIMTextPrivate *priv = ST_IM_TEXT (object)->priv; - g_signal_handlers_disconnect_by_func (priv->im_context, - (void *) st_im_text_commit_cb, - object); + if (priv->im_context != NULL) + { + g_signal_handlers_disconnect_by_func (priv->im_context, + (void *) st_im_text_commit_cb, + object); - g_object_unref (priv->im_context); - priv->im_context = NULL; + g_object_unref (priv->im_context); + priv->im_context = NULL; + } G_OBJECT_CLASS (st_im_text_parent_class)->dispose (object); } diff --git a/src/st/st-label.c b/src/st/st-label.c index ce11304..7cbb45d 100644 --- a/src/st/st-label.c +++ b/src/st/st-label.c @@ -253,26 +253,6 @@ st_label_paint (ClutterActor *actor) clutter_actor_paint (priv->label); } -static void -st_label_map (ClutterActor *actor) -{ - StLabelPrivate *priv = ST_LABEL (actor)->priv; - - CLUTTER_ACTOR_CLASS (st_label_parent_class)->map (actor); - - clutter_actor_map (priv->label); -} - -static void -st_label_unmap (ClutterActor *actor) -{ - StLabelPrivate *priv = ST_LABEL (actor)->priv; - - CLUTTER_ACTOR_CLASS (st_label_parent_class)->unmap (actor); - - clutter_actor_unmap (priv->label); -} - static void st_label_class_init (StLabelClass *klass) { @@ -291,8 +271,6 @@ st_label_class_init (StLabelClass *klass) actor_class->allocate = st_label_allocate; actor_class->get_preferred_width = st_label_get_preferred_width; actor_class->get_preferred_height = st_label_get_preferred_height; - actor_class->map = st_label_map; - actor_class->unmap = st_label_unmap; widget_class->style_changed = st_label_style_changed; widget_class->get_accessible_type = st_label_accessible_get_type; @@ -481,12 +459,24 @@ st_label_accessible_init (StLabelAccessible *self) /* initialization done on AtkObject->initialize */ } +static void +label_text_notify_cb (StLabel *label, + GParamSpec *pspec, + AtkObject *accessible) +{ + g_object_notify (G_OBJECT (accessible), "accessible-name"); +} + static void st_label_accessible_initialize (AtkObject *obj, gpointer data) { ATK_OBJECT_CLASS (st_label_accessible_parent_class)->initialize (obj, data); + g_signal_connect (data, "notify::text", + G_CALLBACK (label_text_notify_cb), + obj); + obj->role = ATK_ROLE_LABEL; } diff --git a/src/st/st-scroll-bar.c b/src/st/st-scroll-bar.c index c01d281..4e88170 100644 --- a/src/st/st-scroll-bar.c +++ b/src/st/st-scroll-bar.c @@ -244,36 +244,12 @@ st_scroll_bar_pick (ClutterActor *actor, clutter_actor_paint (priv->handle); } -static void -st_scroll_bar_map (ClutterActor *actor) -{ - StScrollBarPrivate *priv = ST_SCROLL_BAR (actor)->priv; - - CLUTTER_ACTOR_CLASS (st_scroll_bar_parent_class)->map (actor); - - clutter_actor_map (priv->bw_stepper); - clutter_actor_map (priv->fw_stepper); - clutter_actor_map (priv->trough); - - if (priv->handle) - clutter_actor_map (priv->handle); -} - static void st_scroll_bar_unmap (ClutterActor *actor) { - StScrollBarPrivate *priv = ST_SCROLL_BAR (actor)->priv; - CLUTTER_ACTOR_CLASS (st_scroll_bar_parent_class)->unmap (actor); stop_scrolling (ST_SCROLL_BAR (actor)); - - clutter_actor_unmap (priv->bw_stepper); - clutter_actor_unmap (priv->fw_stepper); - clutter_actor_unmap (priv->trough); - - if (priv->handle) - clutter_actor_unmap (priv->handle); } static void @@ -689,7 +665,6 @@ st_scroll_bar_class_init (StScrollBarClass *klass) actor_class->paint = st_scroll_bar_paint; actor_class->pick = st_scroll_bar_pick; actor_class->scroll_event = st_scroll_bar_scroll_event; - actor_class->map = st_scroll_bar_map; actor_class->unmap = st_scroll_bar_unmap; widget_class->style_changed = st_scroll_bar_style_changed; diff --git a/src/st/st-texture-cache.c b/src/st/st-texture-cache.c index f3de06f..5b9bb4d 100644 --- a/src/st/st-texture-cache.c +++ b/src/st/st-texture-cache.c @@ -1357,9 +1357,9 @@ load_sliced_image (GSimpleAsyncResult *result, width = gdk_pixbuf_get_width (pix); height = gdk_pixbuf_get_height (pix); - for (y = 0; y < height; y += data->grid_width) + for (y = 0; y < height; y += data->grid_height) { - for (x = 0; x < width; x += data->grid_height) + for (x = 0; x < width; x += data->grid_width) { GdkPixbuf *pixbuf = gdk_pixbuf_new_subpixbuf (pix, x, y, data->grid_width, data->grid_height); g_assert (pixbuf != NULL); @@ -1659,7 +1659,7 @@ st_texture_cache_load_icon_name (StTextureCache *cache, case ST_ICON_FADED: themed = g_themed_icon_new_with_default_fallbacks (name); cache_key = g_strdup_printf ("faded-icon:%s,size=%d", name, size); - data.name = name; + data.name = g_strdup (name); data.size = size; cogltexture = st_texture_cache_load (st_texture_cache_get_default (), cache_key, @@ -1667,6 +1667,7 @@ st_texture_cache_load_icon_name (StTextureCache *cache, create_faded_icon_cpu, &data, NULL); + g_free (data.name); g_free (cache_key); if (cogltexture != COGL_INVALID_HANDLE) diff --git a/src/st/st-theme-context.c b/src/st/st-theme-context.c index 80a00e5..836b9db 100644 --- a/src/st/st-theme-context.c +++ b/src/st/st-theme-context.c @@ -130,13 +130,20 @@ on_stage_destroy (ClutterStage *stage) g_object_unref (context); } +static gboolean +emit_changed (StThemeContext *context) +{ + g_signal_emit (context, signals[CHANGED], 0); + return FALSE; +} + static void st_theme_context_changed (StThemeContext *context) { StThemeNode *old_root = context->root_node; context->root_node = NULL; - g_signal_emit (context, signals[CHANGED], 0); + emit_changed (context); if (old_root) g_object_unref (old_root); @@ -149,8 +156,9 @@ on_icon_theme_changed (StTextureCache *cache, /* Note that an icon theme change isn't really a change of the StThemeContext; * the style information has changed. But since the style factors into the * icon_name => icon lookup, faking a theme context change is a good way - * to force users such as StIcon to look up icons again */ - st_theme_context_changed (context); + * to force users such as StIcon to look up icons again. Don't bother recreating + * the root node, though. */ + g_idle_add ((GSourceFunc) emit_changed, context); } /** diff --git a/src/st/st-theme-node-drawing.c b/src/st/st-theme-node-drawing.c index 29b2d57..6c62a20 100644 --- a/src/st/st-theme-node-drawing.c +++ b/src/st/st-theme-node-drawing.c @@ -1822,7 +1822,7 @@ st_theme_node_paint_sliced_border_image (StThemeNode *node, cogl_set_source (material); { - GLfloat rectangles[] = + float rectangles[] = { /* top left corner */ 0, 0, border_left, border_top, diff --git a/src/st/st-types.h b/src/st/st-types.h index 69492ef..f8f4ca5 100644 --- a/src/st/st-types.h +++ b/src/st/st-types.h @@ -14,15 +14,13 @@ * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ - +#ifndef __ST_TYPES_H__ +#define __ST_TYPES_H__ #if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION) #error "Only can be included directly.h" #endif -#ifndef __ST_TYPES_H__ -#define __ST_TYPES_H__ - #include #include #include diff --git a/src/st/st-widget.c b/src/st/st-widget.c index 2b7ea39..7684422 100644 --- a/src/st/st-widget.c +++ b/src/st/st-widget.c @@ -593,8 +593,9 @@ st_widget_get_theme_node (StWidget *widget) if (stage == NULL) { - g_error ("st_widget_get_theme_node called on the widget %s which is not in the stage.", + g_critical ("st_widget_get_theme_node called on the widget %s which is not in the stage.", st_describe_actor (CLUTTER_ACTOR (widget))); + return NULL; } if (parent_node == NULL) @@ -1026,7 +1027,7 @@ st_widget_class_init (StWidgetClass *klass) */ void st_widget_set_theme (StWidget *actor, - StTheme *theme) + StTheme *theme) { StWidgetPrivate *priv = actor->priv; @@ -1034,11 +1035,11 @@ st_widget_set_theme (StWidget *actor, priv = actor->priv; - if (theme !=priv->theme) + if (theme != priv->theme) { if (priv->theme) g_object_unref (priv->theme); - priv->theme = g_object_ref (priv->theme); + priv->theme = g_object_ref (theme); st_widget_style_changed (actor); @@ -2110,18 +2111,17 @@ st_describe_actor (ClutterActor *actor) if (name) g_string_append_printf (desc, " \"%s\"", name); - if (!append_actor_text (desc, actor) && CLUTTER_IS_CONTAINER (actor)) + if (!append_actor_text (desc, actor)) { GList *children, *l; /* Do a limited search of @actor's children looking for a label */ - children = clutter_container_get_children (CLUTTER_CONTAINER (actor)); + children = clutter_actor_get_children (actor); for (l = children, i = 0; l && i < 20; l = l->next, i++) { if (append_actor_text (desc, l->data)) break; - else if (CLUTTER_IS_CONTAINER (l->data)) - children = g_list_concat (children, clutter_container_get_children (l->data)); + children = g_list_concat (children, clutter_actor_get_children (l->data)); } g_list_free (children); } diff --git a/src/st/test-theme.c b/src/st/test-theme.c index 73dbc93..d6a8f7b 100644 --- a/src/st/test-theme.c +++ b/src/st/test-theme.c @@ -354,7 +354,7 @@ test_pseudo_class (void) /* Test the StWidget add/remove pseudo_class interfaces */ label = st_label_new ("foo"); - clutter_container_add_actor (CLUTTER_CONTAINER (stage), CLUTTER_ACTOR (label)); + clutter_actor_add_child (stage, CLUTTER_ACTOR (label)); labelNode = st_widget_get_theme_node (label); assert_foreground_color (labelNode, "label", 0x000000ff);