diff --git a/data/de.coderkun.mcg.gschema.xml b/data/de.coderkun.mcg.gschema.xml
index 89e599a..57e9896 100644
--- a/data/de.coderkun.mcg.gschema.xml
+++ b/data/de.coderkun.mcg.gschema.xml
@@ -42,7 +42,7 @@
Window maximized state.
-
+
1
Last selected panel
The index of the last selected panel.
diff --git a/data/gtk.glade b/data/gtk.glade
index 0e8d916..9192112 100644
--- a/data/gtk.glade
+++ b/data/gtk.glade
@@ -369,11 +369,13 @@
True
False
-
-
+
True
False
- True
-
+
True
False
-
+
- cover-spinner
+ False
+ True
+ 0
-
+
True
- True
- False
- False
-
+ False
-
+
True
False
-
- True
- False
-
-
-
- True
- False
- 0
-
-
-
+
-
-
-
-
- cover-scroll
- 1
-
-
-
-
- True
- True
- 0
-
-
-
-
- True
- False
- slide-right
-
-
- True
- True
- never
- never
-
-
- True
- False
- none
-
-
+
+
True
False
vertical
-
+
+
+
+
True
False
- 5
- 5
- 5
- True
+ vertical
+ 10
-
+
True
False
- Album
- True
+ dialog-information-symbolic
+ 6
- 0
- 0
+ False
+ True
+ 0
-
+
True
False
- Date
- True
+ 2
+ 5
+
+
+ True
+ False
+ start
+ File:
+
+
+ 0
+ 0
+
+
+
+
+ True
+ False
+ start
+ Audio:
+
+
+ 0
+ 1
+
+
+
+
+ True
+ False
+ start
+ Bitrate:
+
+
+ 0
+ 2
+
+
+
+
+ True
+ False
+ start
+ Error:
+
+
+ 0
+ 3
+
+
+
+
+ True
+ False
+ start
+ <i>none</i>
+ True
+ True
+
+
+ 1
+ 2
+
+
+
+
+ True
+ False
+ start
+ <i>none</i>
+ True
+ True
+
+
+ 1
+ 1
+
+
+
+
+ True
+ False
+ start
+ <i>none</i>
+ True
+ True
+
+
+ 1
+ 0
+
+
+
+
+ True
+ False
+ start
+ <i>none</i>
+ True
+ True
+
+
+ 1
+ 3
+
+
- 0
- 1
+ False
+ True
+ 1
-
-
- True
- False
- Artist
- True
-
-
- 0
- 2
-
-
-
False
True
- 10
- 0
-
-
-
-
- True
- True
- start
- vertical
- False
-
-
-
-
- True
- True
1
+
+ False
+ True
+ 1
+
+
+
+
+ page0
+ Status
+
+
+
+
+ True
+ False
+
+
+
+
+
+ True
+ False
+ vertical
+
+
+
+
+
+ True
+ False
+ vertical
+ 10
+
+
+ True
+ False
+ starred-symbolic
+ 6
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ 2
+ 5
+
+
+ True
+ False
+ end
+ right
+ True
+
+
+ 0
+ 0
+
+
+
+
+ True
+ False
+ end
+ right
+
+
+ 0
+ 1
+
+
+
+
+ True
+ False
+ end
+ right
+
+
+ 0
+ 2
+
+
+
+
+ True
+ False
+ start
+ Albums
+
+
+ 1
+ 1
+
+
+
+
+ True
+ False
+ start
+ Songs
+
+
+ 1
+ 2
+
+
+
+
+ True
+ False
+ start
+ Artists
+
+
+ 1
+ 0
+
+
+
+
+ True
+ False
+ end
+ right
+
+
+ 0
+ 3
+
+
+
+
+ True
+ False
+ start
+ Seconds
+
+
+ 1
+ 3
+
+
+
+
+ True
+ False
+ end
+ right
+
+
+ 0
+ 6
+
+
+
+
+ True
+ False
+ end
+ right
+
+
+ 0
+ 5
+
+
+
+
+ True
+ False
+
+
+ 0
+ 4
+ 2
+
+
+
+
+ True
+ False
+ start
+ Seconds played
+
+
+ 1
+ 5
+
+
+
+
+ True
+ False
+ Seconds running
+
+
+ 1
+ 6
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ page1
+ Statistics
+ 1
+
+
+
+
+ True
+ False
+
+
+
+
+
+ True
+ False
+ vertical
+
+
+
+
+
+ True
+ False
+ vertical
+ 10
+
+
+ True
+ False
+ audio-speakers-symbolic
+ 6
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ none
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ page2
+ Audio Devices
+ 2
+
+
+
+
+ True
+ True
+ 1
+
+
+
+
+ server
+ Server
+
+
+
+
+ True
+ False
+ True
+
+
+ True
+ False
+
+
+ True
+ False
+
+
+ cover-spinner
+
+
+
+
+ True
+ True
+ False
+ False
+
+
+
+ True
+ False
+
+
+ True
+ False
+
+
+
+ True
+ False
+ 0
+
+
+
+
+
+
+
+
+ cover-scroll
+ 1
+
+
+
+
+ True
+ True
+ 0
+
+
+
+
+ True
+ False
+ slide-right
+
+
+ True
+ True
+ never
+ never
+
+
+ True
+ False
+ none
+
+
+ True
+ False
+ vertical
+
+
+ True
+ False
+ 5
+ 5
+ 5
+ True
+
+
+ True
+ False
+ Album
+ True
+
+
+ 0
+ 0
+
+
+
+
+ True
+ False
+ Date
+ True
+
+
+ 0
+ 1
+
+
+
+
+ True
+ False
+ Artist
+ True
+
+
+ 0
+ 2
+
+
+
+
+
+ False
+ True
+ 10
+ 0
+
+
+
+
+ True
+ True
+ start
+ vertical
+ False
+
+
+
+
+ True
+ True
+ 1
+
+
+
+
+
+
+ False
+ True
+ 1
+
- False
- True
+ cover
+ Cover
1
+
+
+ True
+ False
+ slide-left-right
+
+
+ True
+ False
+ vertical
+
+
+ True
+ True
+
+
+ True
+ True
+ 0
+ horizontal
+ 0
+ 0
+ 1
+ 0
+ True
+
+
+
+
+
+
+
+
+ True
+ True
+ 0
+
+
+
+
+ True
+ False
+ slide-up
+
+
+ True
+ False
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ page2
+ page2
+
+
+
+
+ True
+ False
+ vertical
+
+
+ True
+ False
+
+
+ True
+ False
+
+
+ standalone-spinne
+
+
+
+
+ True
+ True
+ False
+ False
+
+
+ True
+ False
+
+
+ True
+ False
+ gtk-missing-image
+ 6
+
+
+
+
+
+
+ standalone-scroll
+ 1
+
+
+
+
+ True
+ True
+ 0
+
+
+
+
+ True
+ False
+
+
+ False
+ True
+ 1
+
+
+
+
+ page1
+ page1
+ 1
+
+
+
+
+ playlist
+ Playlist
+ 2
+
+
+
+
+ True
+ False
+ slide-left-right
+
+
+ True
+ False
+ vertical
+
+
+ True
+ True
+ False
+
+
+
+ True
+ True
+ edit-find-symbolic
+ False
+ False
+ search library
+
+
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ none
+
+
+ True
+ False
+ 0.5
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ True
+ True
+
+
+ True
+ True
+ 0
+ horizontal
+ 0
+ 0
+ 1
+ 0
+ True
+
+
+
+
+
+
+
+
+ True
+ True
+ 2
+
+
+
+
+ True
+ False
+ none
+
+
+ True
+ False
+
+
+
+
+ False
+ True
+ 3
+
+
+
+
+ page0
+ page0
+
+
+
+
+ True
+ False
+ vertical
+
+
+ True
+ False
+
+
+ True
+ False
+
+
+ standalone-spinne
+
+
+
+
+ True
+ True
+ False
+ False
+
+
+
+ True
+ False
+
+
+ True
+ False
+ gtk-missing-image
+ 6
+
+
+
+
+
+
+ standalone-scroll
+ 1
+
+
+
+
+ True
+ True
+ 0
+
+
+
+
+ True
+ False
+
+
+ False
+ True
+ 1
+
+
+
+
+ page1
+ page1
+ 1
+
+
+
+
+ library
+ Library
+ 3
+
+
- cover
- Cover
+ page0
+ page0
1
-
-
- True
- False
- slide-left-right
-
-
- True
- False
- vertical
-
-
- True
- True
-
-
- True
- True
- 0
- horizontal
- 0
- 0
- 1
- 0
- True
-
-
-
-
-
-
-
-
- True
- True
- 0
-
-
-
-
- True
- False
- slide-up
-
-
- True
- False
-
-
-
-
- False
- True
- 1
-
-
-
-
- page2
- page2
-
-
-
-
- True
- False
- vertical
-
-
- True
- False
-
-
- True
- False
-
-
- standalone-spinne
-
-
-
-
- True
- True
- False
- False
-
-
- True
- False
-
-
- True
- False
- gtk-missing-image
- 6
-
-
-
-
-
-
- standalone-scroll
- 1
-
-
-
-
- True
- True
- 0
-
-
-
-
- True
- False
-
-
- False
- True
- 1
-
-
-
-
- page1
- page1
- 1
-
-
-
-
- playlist
- Playlist
- 2
-
-
-
-
- True
- False
- slide-left-right
-
-
- True
- False
- vertical
-
-
- True
- True
- False
-
-
-
- True
- True
- edit-find-symbolic
- False
- False
- search library
-
-
-
-
-
- False
- True
- 0
-
-
-
-
- True
- False
- none
-
-
- True
- False
- 0.5
-
-
-
-
- False
- True
- 1
-
-
-
-
- True
- True
-
-
- True
- True
- 0
- horizontal
- 0
- 0
- 1
- 0
- True
-
-
-
-
-
-
-
-
-
- True
- True
- 2
-
-
-
-
- True
- False
- none
-
-
- True
- False
-
-
-
-
- False
- True
- 3
-
-
-
-
- page0
- page0
-
-
-
-
- True
- False
- vertical
-
-
- True
- False
-
-
- True
- False
-
-
- standalone-spinne
-
-
-
-
- True
- True
- False
- False
-
-
-
- True
- False
-
-
- True
- False
- gtk-missing-image
- 6
-
-
-
-
-
-
- standalone-scroll
- 1
-
-
-
-
- True
- True
- 0
-
-
-
-
- True
- False
-
-
- False
- True
- 1
-
-
-
-
- page1
- page1
- 1
-
-
-
-
- library
- Library
- 3
-
-
-1
@@ -1190,15 +1700,43 @@
4
-
-
-
-
diff --git a/locale/de/LC_MESSAGES/mcg.mo b/locale/de/LC_MESSAGES/mcg.mo
index f9e520a..c32a515 100644
Binary files a/locale/de/LC_MESSAGES/mcg.mo and b/locale/de/LC_MESSAGES/mcg.mo differ
diff --git a/locale/de/LC_MESSAGES/mcg.po b/locale/de/LC_MESSAGES/mcg.po
index e7e3011..597cb16 100644
--- a/locale/de/LC_MESSAGES/mcg.po
+++ b/locale/de/LC_MESSAGES/mcg.po
@@ -1,15 +1,15 @@
msgid ""
msgstr ""
"Project-Id-Version: CoverGrid (mcg)\n"
-"POT-Creation-Date: 2017-09-10 14:16+0200\n"
-"PO-Revision-Date: 2017-09-10 14:16+0200\n"
+"POT-Creation-Date: 2017-12-25 17:10+0100\n"
+"PO-Revision-Date: 2017-12-25 17:10+0100\n"
"Last-Translator: coderkun \n"
"Language-Team: \n"
"Language: de\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Generator: Poedit 2.0.3\n"
+"X-Generator: Poedit 2.0.5\n"
"X-Poedit-Basepath: ../../..\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Poedit-SourceCharset: UTF-8\n"
@@ -58,87 +58,147 @@ msgstr "nach Titel"
msgid "sort by year"
msgstr "nach Jahr"
-#: data/gtk.glade:436
+#: data/gtk.glade:437
msgid "Enter hostname or IP address"
msgstr "Hostnamen oder IP-Adresse eingeben"
-#: data/gtk.glade:448
+#: data/gtk.glade:449
msgid "Enter URL or local path"
msgstr "URL oder lokalen Pfad eingeben"
-#: data/gtk.glade:460
+#: data/gtk.glade:461
msgid "Enter password or leave blank"
msgstr "Passwort eingeben oder leer lassen"
-#: data/gtk.glade:485
+#: data/gtk.glade:487
msgid "Host:"
msgstr "Host:"
-#: data/gtk.glade:497
+#: data/gtk.glade:499
msgid "Port:"
msgstr "Port:"
-#: data/gtk.glade:509
+#: data/gtk.glade:511
msgid "Password:"
msgstr "Passwort:"
-#: data/gtk.glade:521
+#: data/gtk.glade:523
msgid "Image Directory:"
msgstr "Bildordner:"
-#: data/gtk.glade:545 data/gtk.menu.ui:24
-msgid "Connection"
-msgstr "Verbindung"
+#: data/gtk.glade:619
+msgid "File:"
+msgstr "Datei:"
-#: data/gtk.glade:714 data/gtk.menu.ui:30
+#: data/gtk.glade:631
+msgid "Audio:"
+msgstr "Audio:"
+
+#: data/gtk.glade:643
+msgid "Bitrate:"
+msgstr "Bitrate:"
+
+#: data/gtk.glade:655
+msgid "Error:"
+msgstr "Fehler:"
+
+#: data/gtk.glade:667 data/gtk.glade:681 data/gtk.glade:695 data/gtk.glade:709
+msgid "none"
+msgstr "nichts"
+
+#: data/gtk.glade:742
+msgid "Status"
+msgstr "Status"
+
+#: data/gtk.glade:827
+msgid "Albums"
+msgstr "Alben"
+
+#: data/gtk.glade:839
+msgid "Songs"
+msgstr "Songs"
+
+#: data/gtk.glade:851
+msgid "Artists"
+msgstr "Künstler"
+
+#: data/gtk.glade:875
+msgid "Seconds"
+msgstr "Sekunden"
+
+#: data/gtk.glade:922
+msgid "Seconds played"
+msgstr "Sekunden gespielt"
+
+#: data/gtk.glade:933
+msgid "Seconds running"
+msgstr "Sekunden laufend"
+
+#: data/gtk.glade:964
+msgid "Statistics"
+msgstr "Statistiken"
+
+#: data/gtk.glade:1034
+msgid "Audio Devices"
+msgstr "Audiogeräte"
+
+#: data/gtk.glade:1048
+msgid "Server"
+msgstr "Server"
+
+#: data/gtk.glade:1217 data/gtk.menu.ui:30
msgid "Cover"
msgstr "Cover"
-#: data/gtk.glade:856 data/gtk.menu.ui:36
+#: data/gtk.glade:1359 data/gtk.menu.ui:36
msgid "Playlist"
msgstr "Wiedergabeliste"
-#: data/gtk.glade:883
+#: data/gtk.glade:1386
msgid "search library"
msgstr "Bibliothek durchsuchen"
-#: data/gtk.glade:1042 data/gtk.menu.ui:42
+#: data/gtk.glade:1545 data/gtk.menu.ui:42
msgid "Library"
msgstr "Bibliothek"
-#: data/gtk.glade:1114 data/gtk.shortcuts.ui:74
+#: data/gtk.glade:1624 data/gtk.shortcuts.ui:74
msgid "Connect or disconnect"
msgstr "Die Verbindung herstellen oder trennen"
-#: data/gtk.glade:1136 data/gtk.shortcuts.ui:81
+#: data/gtk.glade:1646 data/gtk.shortcuts.ui:81
msgid "Switch between play and pause"
msgstr "Zwischen Abspielen und Pause wechseln"
-#: data/gtk.glade:1156
+#: data/gtk.glade:1666
msgid "Adjust the volume"
msgstr "Die Lautstärke anpassen"
-#: data/gtk.glade:1214 data/gtk.shortcuts.ui:101
+#: data/gtk.glade:1710
+msgid "Connect to MPD"
+msgstr "Zu MPD verbinden"
+
+#: data/gtk.glade:1752 data/gtk.shortcuts.ui:101
msgid "Show the cover in fullscreen mode"
msgstr "Das Cover im Vollbildmodus anzeigen"
-#: data/gtk.glade:1237 data/gtk.glade:1386
+#: data/gtk.glade:1775 data/gtk.glade:1924
msgid "Settings and actions"
msgstr "Einstellungen und Aktionen"
-#: data/gtk.glade:1285 data/gtk.glade:1364
+#: data/gtk.glade:1823 data/gtk.glade:1902
msgid "Select multiple albums"
msgstr "Mehrere Alben auswählen"
-#: data/gtk.glade:1307 data/gtk.shortcuts.ui:88
+#: data/gtk.glade:1845 data/gtk.shortcuts.ui:88
msgid "Clear the playlist"
msgstr "Die Wiedergabeliste leeren"
-#: data/gtk.glade:1341 data/gtk.shortcuts.ui:114
+#: data/gtk.glade:1879 data/gtk.shortcuts.ui:114
msgid "Search the library"
msgstr "Die Bibliothek durchsuchen"
-#: data/gtk.glade:1431
+#: data/gtk.glade:1969
msgid ""
"CoverGrid is a client for the Music Player Daemon, focusing on albums "
"instead of single tracks."
@@ -158,6 +218,10 @@ msgstr "Abspielen"
msgid "Clear Playlist"
msgstr "Playlist leeren"
+#: data/gtk.menu.ui:24
+msgid "Connection"
+msgstr "Verbindung"
+
#: data/gtk.menu.ui:50
msgid "Keyboard Shortcuts"
msgstr "Tastenkombinationen"
@@ -229,6 +293,3 @@ msgstr "{} mit {}"
#~ msgid "Show the keyboard shortcuts (this dialog)"
#~ msgstr "Die Tastenkombinationen anzeigen (dieser Dialog)"
-
-#~ msgid "Server"
-#~ msgstr "Server"
diff --git a/locale/en/LC_MESSAGES/mcg.mo b/locale/en/LC_MESSAGES/mcg.mo
index d1b8c35..d14c7d7 100644
Binary files a/locale/en/LC_MESSAGES/mcg.mo and b/locale/en/LC_MESSAGES/mcg.mo differ
diff --git a/locale/en/LC_MESSAGES/mcg.po b/locale/en/LC_MESSAGES/mcg.po
index bdc09fe..dfd501b 100644
--- a/locale/en/LC_MESSAGES/mcg.po
+++ b/locale/en/LC_MESSAGES/mcg.po
@@ -1,15 +1,15 @@
msgid ""
msgstr ""
"Project-Id-Version: CoverGrid (mcg)\n"
-"POT-Creation-Date: 2017-09-10 14:15+0200\n"
-"PO-Revision-Date: 2017-09-10 14:16+0200\n"
+"POT-Creation-Date: 2017-12-25 17:08+0100\n"
+"PO-Revision-Date: 2017-12-25 17:08+0100\n"
"Last-Translator: coderkun \n"
"Language-Team: \n"
"Language: en\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Generator: Poedit 2.0.3\n"
+"X-Generator: Poedit 2.0.5\n"
"X-Poedit-Basepath: ../../..\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Poedit-SearchPath-0: data/gtk.glade\n"
@@ -57,87 +57,147 @@ msgstr "by Title"
msgid "sort by year"
msgstr "by Year"
-#: data/gtk.glade:436
+#: data/gtk.glade:437
msgid "Enter hostname or IP address"
msgstr "Enter hostname or IP address"
-#: data/gtk.glade:448
+#: data/gtk.glade:449
msgid "Enter URL or local path"
msgstr "Enter URL or local path"
-#: data/gtk.glade:460
+#: data/gtk.glade:461
msgid "Enter password or leave blank"
msgstr "Enter password or leave blank"
-#: data/gtk.glade:485
+#: data/gtk.glade:487
msgid "Host:"
msgstr "Host:"
-#: data/gtk.glade:497
+#: data/gtk.glade:499
msgid "Port:"
msgstr "Port:"
-#: data/gtk.glade:509
+#: data/gtk.glade:511
msgid "Password:"
msgstr "Password:"
-#: data/gtk.glade:521
+#: data/gtk.glade:523
msgid "Image Directory:"
msgstr "Image Directory:"
-#: data/gtk.glade:545 data/gtk.menu.ui:24
-msgid "Connection"
-msgstr "Connection"
+#: data/gtk.glade:619
+msgid "File:"
+msgstr "File:"
-#: data/gtk.glade:714 data/gtk.menu.ui:30
+#: data/gtk.glade:631
+msgid "Audio:"
+msgstr "Audio:"
+
+#: data/gtk.glade:643
+msgid "Bitrate:"
+msgstr "Bitrate:"
+
+#: data/gtk.glade:655
+msgid "Error:"
+msgstr "Error:"
+
+#: data/gtk.glade:667 data/gtk.glade:681 data/gtk.glade:695 data/gtk.glade:709
+msgid "none"
+msgstr "none"
+
+#: data/gtk.glade:742
+msgid "Status"
+msgstr "Status"
+
+#: data/gtk.glade:827
+msgid "Albums"
+msgstr "Albums"
+
+#: data/gtk.glade:839
+msgid "Songs"
+msgstr "Songs"
+
+#: data/gtk.glade:851
+msgid "Artists"
+msgstr "Artists"
+
+#: data/gtk.glade:875
+msgid "Seconds"
+msgstr "Seconds"
+
+#: data/gtk.glade:922
+msgid "Seconds played"
+msgstr "Seconds"
+
+#: data/gtk.glade:933
+msgid "Seconds running"
+msgstr "Seconds running"
+
+#: data/gtk.glade:964
+msgid "Statistics"
+msgstr "Statistics"
+
+#: data/gtk.glade:1034
+msgid "Audio Devices"
+msgstr "Audio Devices"
+
+#: data/gtk.glade:1048
+msgid "Server"
+msgstr "Server"
+
+#: data/gtk.glade:1217 data/gtk.menu.ui:30
msgid "Cover"
msgstr "Cover"
-#: data/gtk.glade:856 data/gtk.menu.ui:36
+#: data/gtk.glade:1359 data/gtk.menu.ui:36
msgid "Playlist"
msgstr "Playlist"
-#: data/gtk.glade:883
+#: data/gtk.glade:1386
msgid "search library"
msgstr "search library"
-#: data/gtk.glade:1042 data/gtk.menu.ui:42
+#: data/gtk.glade:1545 data/gtk.menu.ui:42
msgid "Library"
msgstr "Library"
-#: data/gtk.glade:1114 data/gtk.shortcuts.ui:74
+#: data/gtk.glade:1624 data/gtk.shortcuts.ui:74
msgid "Connect or disconnect"
msgstr "Connect or disconnect"
-#: data/gtk.glade:1136 data/gtk.shortcuts.ui:81
+#: data/gtk.glade:1646 data/gtk.shortcuts.ui:81
msgid "Switch between play and pause"
msgstr "Switch between play and pause"
-#: data/gtk.glade:1156
+#: data/gtk.glade:1666
msgid "Adjust the volume"
msgstr "Adjust the volume"
-#: data/gtk.glade:1214 data/gtk.shortcuts.ui:101
+#: data/gtk.glade:1710
+msgid "Connect to MPD"
+msgstr "Connect to MPD"
+
+#: data/gtk.glade:1752 data/gtk.shortcuts.ui:101
msgid "Show the cover in fullscreen mode"
msgstr "Show the cover in fullscreen mode"
-#: data/gtk.glade:1237 data/gtk.glade:1386
+#: data/gtk.glade:1775 data/gtk.glade:1924
msgid "Settings and actions"
msgstr "Settings and actions"
-#: data/gtk.glade:1285 data/gtk.glade:1364
+#: data/gtk.glade:1823 data/gtk.glade:1902
msgid "Select multiple albums"
msgstr "Select multiple albums"
-#: data/gtk.glade:1307 data/gtk.shortcuts.ui:88
+#: data/gtk.glade:1845 data/gtk.shortcuts.ui:88
msgid "Clear the playlist"
msgstr "Clear the playlist"
-#: data/gtk.glade:1341 data/gtk.shortcuts.ui:114
+#: data/gtk.glade:1879 data/gtk.shortcuts.ui:114
msgid "Search the library"
msgstr "Search the library"
-#: data/gtk.glade:1431
+#: data/gtk.glade:1969
msgid ""
"CoverGrid is a client for the Music Player Daemon, focusing on albums "
"instead of single tracks."
@@ -157,6 +217,10 @@ msgstr "Play"
msgid "Clear Playlist"
msgstr "Clear Playlist"
+#: data/gtk.menu.ui:24
+msgid "Connection"
+msgstr "Connection"
+
#: data/gtk.menu.ui:50
msgid "Keyboard Shortcuts"
msgstr "Keyboard Shortcuts"
@@ -228,6 +292,3 @@ msgstr "{} feat. {}"
#~ msgid "_Quit"
#~ msgstr "_Quit"
-
-#~ msgid "Server"
-#~ msgstr "Server"
diff --git a/mcg/client.py b/mcg/client.py
index 2ddd75a..ca9102d 100644
--- a/mcg/client.py
+++ b/mcg/client.py
@@ -108,10 +108,14 @@ class Client(Base):
SIGNAL_CONNECTION = 'connection'
# Signal: status
SIGNAL_STATUS = 'status'
+ # Signal: stats
+ SIGNAL_STATS = 'stats'
# Signal: load albums
SIGNAL_LOAD_ALBUMS = 'load-albums'
# Signal: load playlist
SIGNAL_LOAD_PLAYLIST = 'load-playlist'
+ # Signal: load audio output devices
+ SIGNAL_LOAD_OUTPUT_DEVICES = 'load-output-devices'
# Signal: error
SIGNAL_ERROR = 'error'
@@ -174,6 +178,25 @@ class Client(Base):
self._add_action(self._get_status)
+ def get_stats(self):
+ """Load statistics."""
+ self._logger.info("get stats")
+ self._add_action(self._get_stats)
+
+
+ def get_output_devices(self):
+ """Determine the list of audio output devices."""
+ self._logger.info("get output devices")
+ self._add_action(self._get_output_devices)
+
+
+ def enable_output_device(self, device, enabled):
+ """Enable/disable an audio output device."""
+ self._logger.info("enable output device")
+ self._add_action(self._enable_output_device, device, enabled)
+
+
+
def load_albums(self):
self._logger.info("load albums")
self._add_action(self._load_albums)
@@ -343,6 +366,9 @@ class Client(Base):
self.load_albums()
self.load_playlist()
self.get_status()
+ if subsystems['changed'] == 'output':
+ self.get_output_devices()
+ self.get_status()
def _noidle(self):
@@ -375,10 +401,14 @@ class Client(Base):
if 'error' in status:
error = status['error']
# Album
+ file = None
album = None
pos = 0
song = self._parse_dict(self._call("currentsong"))
if song:
+ # File
+ if 'file' in song:
+ file = song['file']
# Track
track = self._extract_playlist_track(song)
if track:
@@ -391,7 +421,66 @@ class Client(Base):
album = palbum
break
pos = pos - len(palbum.get_tracks())
- self._callback(Client.SIGNAL_STATUS, state, album, pos, time, volume, error)
+ # Audio
+ audio = None
+ if 'audio' in status:
+ audio = status['audio']
+ # Bitrate
+ bitrate = None
+ if 'bitrate' in status:
+ bitrate = status['bitrate']
+ self._callback(Client.SIGNAL_STATUS, state, album, pos, time, volume, file, audio, bitrate, error)
+
+
+ def _get_stats(self):
+ """Action: Perform the real statistics gathering."""
+ self._logger.info("getting statistics")
+ stats = self._parse_dict(self._call("stats"))
+ self._logger.debug("stats: %r", stats)
+
+ # Artists
+ artists = 0
+ if 'artists' in stats:
+ artists = int(stats['artists'])
+ # Albums
+ albums = 0
+ if 'albums' in stats:
+ albums = int(stats['albums'])
+ # Songs
+ songs = 0
+ if 'songs' in stats:
+ songs = int(stats['songs'])
+ # Database playtime
+ dbplaytime = 0
+ if 'db_playtime' in stats:
+ dbplaytime = stats['db_playtime']
+ # Playtime
+ playtime = 0
+ if 'playtime' in stats:
+ playtime = stats['playtime']
+ # Uptime
+ uptime = 0
+ if 'uptime' in stats:
+ uptime = stats['uptime']
+ self._callback(Client.SIGNAL_STATS, artists, albums, songs, dbplaytime, playtime, uptime)
+
+
+ def _get_output_devices(self):
+ """Action: Perform the real loading of output devices."""
+ devices = []
+ for output in self._parse_list(self._call('outputs'), ['outputid']):
+ device = OutputDevice(output['outputid'], output['outputname'])
+ device.set_enabled(int(output['outputenabled']) == 1)
+ devices.append(device)
+ self._callback(Client.SIGNAL_LOAD_OUTPUT_DEVICES, devices)
+
+
+ def _enable_output_device(self, device, enabled):
+ """Action: Perform the real enabling/disabling of an output device."""
+ if enabled:
+ self._call('enableoutput ', device.get_id())
+ else:
+ self._call('disableoutput ', device.get_id())
def _load_albums(self):
@@ -684,6 +773,33 @@ class Client(Base):
+class OutputDevice:
+
+
+ def __init__(self, id, name):
+ self._id = id
+ self._name = name
+ self._enabled = None
+
+
+ def get_id(self):
+ return self._id
+
+
+ def get_name(self):
+ return self._name
+
+
+ def set_enabled(self, enabled):
+ self._enabled = enabled
+
+
+ def is_enabled(self):
+ return self._enabled
+
+
+
+
class MCGAlbum:
DEFAULT_ALBUM = 'Various'
_FILE_NAMES = ['cover', 'folder']
diff --git a/mcg/widgets.py b/mcg/widgets.py
index 88278ce..4e35103 100644
--- a/mcg/widgets.py
+++ b/mcg/widgets.py
@@ -88,7 +88,7 @@ class Window():
SETTING_SORT_ORDER = 'sort-order'
SETTING_SORT_TYPE = 'sort-type'
STOCK_ICON_DEFAULT = 'image-x-generic-symbolic'
- _PANEL_INDEX_CONNECTION = 0
+ _PANEL_INDEX_SERVER = 0
_PANEL_INDEX_COVER = 1
_PANEL_INDEX_PLAYLIST = 2
_PANEL_INDEX_LIBRARY = 3
@@ -107,8 +107,10 @@ class Window():
self._maximized = self._settings.get_boolean(Window.SETTING_WINDOW_MAXIMIZED)
self._fullscreened = False
+ # Login screen
+ self._connection_panel = ConnectionPanel(builder)
# Panels
- self._panels.append(ConnectionPanel(builder))
+ self._panels.append(ServerPanel(builder))
self._panels.append(CoverPanel(builder))
self._panels.append(PlaylistPanel(builder))
self._panels.append(LibraryPanel(builder))
@@ -117,6 +119,7 @@ class Window():
# InfoBar
self._infobar = InfoBar(builder)
# Stack
+ self._content_stack = builder.get_object('contentstack')
self._stack = builder.get_object('panelstack')
# Header
self._header_bar = HeaderBar(builder)
@@ -125,11 +128,11 @@ class Window():
# Properties
self._header_bar.set_sensitive(False, False)
- self._panels[Window._PANEL_INDEX_CONNECTION].set_host(self._settings.get_string(Window.SETTING_HOST))
- self._panels[Window._PANEL_INDEX_CONNECTION].set_port(self._settings.get_int(Window.SETTING_PORT))
+ self._connection_panel.set_host(self._settings.get_string(Window.SETTING_HOST))
+ self._connection_panel.set_port(self._settings.get_int(Window.SETTING_PORT))
if use_keyring:
- self._panels[Window._PANEL_INDEX_CONNECTION].set_password(keyring.get_password(ZeroconfProvider.KEYRING_SYSTEM, ZeroconfProvider.KEYRING_USERNAME))
- self._panels[Window._PANEL_INDEX_CONNECTION].set_image_dir(self._settings.get_string(Window.SETTING_IMAGE_DIR))
+ self._connection_panel.set_password(keyring.get_password(ZeroconfProvider.KEYRING_SYSTEM, ZeroconfProvider.KEYRING_USERNAME))
+ self._connection_panel.set_image_dir(self._settings.get_string(Window.SETTING_IMAGE_DIR))
self._panels[Window._PANEL_INDEX_COVER].set_tracklist_size(self._settings.get_enum(Window.SETTING_TRACKLIST_SIZE))
self._panels[Window._PANEL_INDEX_PLAYLIST].set_item_size(self._settings.get_int(Window.SETTING_ITEM_SIZE))
self._panels[Window._PANEL_INDEX_LIBRARY].set_item_size(self._settings.get_int(Window.SETTING_ITEM_SIZE))
@@ -141,7 +144,8 @@ class Window():
self._header_bar.connect('toolbar-connect', self.on_header_bar_connect)
self._header_bar.connect('toolbar-playpause', self.on_header_bar_playpause)
self._header_bar.connect('toolbar-set-volume', self.on_header_bar_set_volume)
- self._panels[Window._PANEL_INDEX_CONNECTION].connect('connection-changed', self.on_connection_panel_connection_changed)
+ self._connection_panel.connect('connection-changed', self.on_connection_panel_connection_changed)
+ self._panels[Window._PANEL_INDEX_SERVER].connect('change-output-device', self.on_server_panel_output_device_changed)
self._panels[Window._PANEL_INDEX_COVER].connect('toggle-fullscreen', self.on_cover_panel_toggle_fullscreen)
self._panels[Window._PANEL_INDEX_COVER].connect('tracklist-size-changed', self.on_cover_panel_tracklist_size_changed)
self._panels[Window._PANEL_INDEX_COVER].connect('set-song', self.on_cover_panel_set_song)
@@ -157,6 +161,8 @@ class Window():
self._panels[Window._PANEL_INDEX_LIBRARY].connect('sort-type-changed', self.on_library_panel_sort_type_changed)
self._mcg.connect_signal(client.Client.SIGNAL_CONNECTION, self.on_mcg_connect)
self._mcg.connect_signal(client.Client.SIGNAL_STATUS, self.on_mcg_status)
+ self._mcg.connect_signal(client.Client.SIGNAL_STATS, self.on_mcg_stats)
+ self._mcg.connect_signal(client.Client.SIGNAL_LOAD_OUTPUT_DEVICES, self.on_mcg_load_output_devices)
self._mcg.connect_signal(client.Client.SIGNAL_LOAD_PLAYLIST, self.on_mcg_load_playlist)
self._mcg.connect_signal(client.Client.SIGNAL_LOAD_ALBUMS, self.on_mcg_load_albums)
self._mcg.connect_signal(client.Client.SIGNAL_ERROR, self.on_mcg_error)
@@ -172,7 +178,7 @@ class Window():
}
handlers.update(self._header_bar.get_signal_handlers())
handlers.update(self._infobar.get_signal_handlers())
- handlers.update(self._panels[Window._PANEL_INDEX_CONNECTION].get_signal_handlers())
+ handlers.update(self._connection_panel.get_signal_handlers())
handlers.update(self._panels[Window._PANEL_INDEX_COVER].get_signal_handlers())
handlers.update(self._panels[Window._PANEL_INDEX_PLAYLIST].get_signal_handlers())
handlers.update(self._panels[Window._PANEL_INDEX_LIBRARY].get_signal_handlers())
@@ -183,7 +189,7 @@ class Window():
if self._maximized:
self._appwindow.maximize()
self._appwindow.show_all()
- self._stack.set_visible_child(self._panels[Window._PANEL_INDEX_CONNECTION].get())
+ self._content_stack.set_visible_child(self._connection_panel.get())
if self._settings.get_boolean(Window.SETTING_CONNECTED):
self._connect()
@@ -298,6 +304,10 @@ class Window():
self._mcg.play_album_from_playlist(album)
+ def on_server_panel_output_device_changed(self, widget, device, enabled):
+ self._mcg.enable_output_device(device, enabled)
+
+
def on_cover_panel_toggle_fullscreen(self, widget):
if not self._fullscreened:
self._appwindow.fullscreen()
@@ -346,6 +356,8 @@ class Window():
self._mcg.load_playlist()
self._mcg.load_albums()
self._mcg.get_status()
+ self._mcg.get_stats()
+ self._mcg.get_output_devices()
self._connect_action.set_state(GLib.Variant.new_boolean(True))
self._play_action.set_enabled(True)
self._clear_playlist_action.set_enabled(True)
@@ -358,7 +370,7 @@ class Window():
self._panel_action.set_enabled(False)
- def on_mcg_status(self, state, album, pos, time, volume, error):
+ def on_mcg_status(self, state, album, pos, time, volume, file, audio, bitrate, error):
# Album
GObject.idle_add(self._panels[Window._PANEL_INDEX_COVER].set_album, album)
if not album and self._fullscreened:
@@ -374,6 +386,8 @@ class Window():
self._play_action.set_state(GLib.Variant.new_boolean(False))
# Volume
GObject.idle_add(self._header_bar.set_volume, volume)
+ # Status
+ self._panels[Window._PANEL_INDEX_SERVER].set_status(file, audio, bitrate, error)
# Error
if error is None:
self._infobar.hide()
@@ -381,12 +395,20 @@ class Window():
self._show_error(error)
+ def on_mcg_stats(self, artists, albums, songs, dbplaytime, playtime, uptime):
+ self._panels[Window._PANEL_INDEX_SERVER].set_stats(artists, albums, songs, dbplaytime, playtime, uptime)
+
+
+ def on_mcg_load_output_devices(self, devices):
+ self._panels[Window._PANEL_INDEX_SERVER].set_output_devices(devices)
+
+
def on_mcg_load_playlist(self, playlist):
- self._panels[self._PANEL_INDEX_PLAYLIST].set_playlist(self._panels[self._PANEL_INDEX_CONNECTION].get_host(), playlist)
+ self._panels[self._PANEL_INDEX_PLAYLIST].set_playlist(self._connection_panel.get_host(), playlist)
def on_mcg_load_albums(self, albums):
- self._panels[self._PANEL_INDEX_LIBRARY].set_albums(self._panels[self._PANEL_INDEX_CONNECTION].get_host(), albums)
+ self._panels[self._PANEL_INDEX_LIBRARY].set_albums(self._connection_panel.get_host(), albums)
def on_mcg_error(self, error):
@@ -424,17 +446,16 @@ class Window():
# Private methods
def _connect(self):
- connection_panel = self._panels[Window._PANEL_INDEX_CONNECTION]
- connection_panel.get().set_sensitive(False)
+ self._connection_panel.get().set_sensitive(False)
self._header_bar.set_sensitive(False, True)
if self._mcg.is_connected():
self._mcg.disconnect()
self._settings.set_boolean(Window.SETTING_CONNECTED, False)
else:
- host = connection_panel.get_host()
- port = connection_panel.get_port()
- password = connection_panel.get_password()
- image_dir = connection_panel.get_image_dir()
+ host = self._connection_panel.get_host()
+ port = self._connection_panel.get_port()
+ password = self._connection_panel.get_password()
+ image_dir = self._connection_panel.get_image_dir()
self._mcg.connect(host, port, password, image_dir)
self._settings.set_boolean(Window.SETTING_CONNECTED, True)
@@ -442,6 +463,7 @@ class Window():
def _connect_connected(self):
self._header_bar.connected()
self._header_bar.set_sensitive(True, False)
+ self._content_stack.set_visible_child(self._stack)
self._stack.set_visible_child(self._panels[self._settings.get_int(Window.SETTING_PANEL)].get())
@@ -451,8 +473,8 @@ class Window():
self._header_bar.disconnected()
self._header_bar.set_sensitive(False, False)
self._save_visible_panel()
- self._stack.set_visible_child(self._panels[Window._PANEL_INDEX_CONNECTION].get())
- self._panels[Window._PANEL_INDEX_CONNECTION].get().set_sensitive(True)
+ self._content_stack.set_visible_child(self._connection_panel.get())
+ self._connection_panel.get().set_sensitive(True)
def _fullscreen(self, fullscreened_new):
@@ -469,8 +491,7 @@ class Window():
def _save_visible_panel(self):
panels = [panel.get() for panel in self._panels]
panel_index_selected = panels.index(self._stack.get_visible_child())
- if panel_index_selected > 0:
- self._settings.set_int(Window.SETTING_PANEL, panel_index_selected)
+ self._settings.set_int(Window.SETTING_PANEL, panel_index_selected)
def _set_menu_visible_panel(self):
@@ -508,6 +529,8 @@ class HeaderBar(GObject.GObject):
# Widgets
self._header_bar = builder.get_object('headerbar')
+ self._title_stack = builder.get_object('headerbar-title-stack')
+ self._connection_label = builder.get_object('headerbar-connectionn-label')
self._stack_switcher = StackSwitcher(builder)
self._button_connect = builder.get_object('headerbar-connection')
self._button_playpause = builder.get_object('headerbar-playpause')
@@ -582,6 +605,7 @@ class HeaderBar(GObject.GObject):
self._button_connect.handler_unblock_by_func(
self.on_connection_active_notify
)
+ self._title_stack.set_visible_child(self._stack_switcher.get())
def disconnected(self):
@@ -593,6 +617,7 @@ class HeaderBar(GObject.GObject):
self._button_connect.handler_unblock_by_func(
self.on_connection_active_notify
)
+ self._title_stack.set_visible_child(self._connection_label)
def set_play(self):
@@ -675,8 +700,7 @@ class ConnectionPanel(GObject.GObject):
self._profile = None
# Widgets
- self._panel = builder.get_object('server-panel')
- self._toolbar = builder.get_object('server-toolbar')
+ self._panel = builder.get_object('connection-panel')
# Zeroconf
self._zeroconf_list = builder.get_object('server-zeroconf-list')
self._zeroconf_list.set_model(self._services)
@@ -701,10 +725,6 @@ class ConnectionPanel(GObject.GObject):
return self._panel
- def get_toolbar(self):
- return self._toolbar
-
-
def get_signal_handlers(self):
return {
'on_server-zeroconf-list-selection_changed': self.on_service_selected,
@@ -792,6 +812,118 @@ class ConnectionPanel(GObject.GObject):
+class ServerPanel(GObject.GObject):
+ __gsignals__ = {
+ 'change-output-device': (GObject.SIGNAL_RUN_FIRST, None, (GObject.TYPE_PYOBJECT,bool,)),
+ }
+
+
+ def __init__(self, builder):
+ GObject.GObject.__init__(self)
+ self._none_label = ""
+ self._output_buttons = {}
+
+ # Widgets
+ self._panel = builder.get_object('server-panel')
+ self._toolbar = builder.get_object('server-toolbar')
+ self._stack = builder.get_object('server-stack')
+
+ # Status widgets
+ self._status_file = builder.get_object('server-status-file')
+ self._status_audio = builder.get_object('server-status-audio')
+ self._status_bitrate = builder.get_object('server-status-bitrate')
+ self._status_error = builder.get_object('server-status-error')
+ self._none_label = self._status_file.get_label()
+
+ # Stats widgets
+ self._stats_artists = builder.get_object('server-stats-artists')
+ self._stats_albums = builder.get_object('server-stats-albums')
+ self._stats_songs = builder.get_object('server-stats-songs')
+ self._stats_dbplaytime = builder.get_object('server-stats-dbplaytime')
+ self._stats_playtime = builder.get_object('server-stats-playtime')
+ self._stats_uptime = builder.get_object('server-stats-uptime')
+
+ # Audio ouptut devices widgets
+ self._output_devices = builder.get_object('server-output-devices')
+
+
+ def get(self):
+ return self._panel
+
+
+ def get_toolbar(self):
+ return self._toolbar
+
+
+ def on_output_device_toggled(self, widget, device):
+ self.emit('change-output-device', device, widget.get_active())
+
+
+ def set_status(self, file, audio, bitrate, error):
+ if file:
+ file = GObject.markup_escape_text(file)
+ else:
+ file = self._none_label
+ self._status_file.set_markup(file)
+ # Audio information
+ if audio:
+ parts = audio.split(":")
+ if len(parts) == 3:
+ audio = "{} Hz, {} bit, {} channels".format(parts[0], parts[1], parts[2])
+ else:
+ audio = self._none_label
+ self._status_audio.set_markup(audio)
+ # Bitrate
+ if bitrate:
+ bitrate = bitrate + " kb/s"
+ else:
+ bitrate = self._none_label
+ self._status_bitrate.set_markup(bitrate)
+ # Error
+ if error:
+ error = GObject.markup_escape_text(error)
+ else:
+ error = self._none_label
+ self._status_error.set_markup(error)
+
+
+ def set_stats(self, artists, albums, songs, dbplaytime, playtime, uptime):
+ self._stats_artists.set_text(str(artists))
+ self._stats_albums.set_text(str(albums))
+ self._stats_songs.set_text(str(songs))
+ self._stats_dbplaytime.set_text(str(dbplaytime))
+ self._stats_playtime.set_text(str(playtime))
+ self._stats_uptime.set_text(str(uptime))
+
+
+ def set_output_devices(self, devices):
+ device_ids = []
+
+ # Add devices
+ for device in devices:
+ device_ids.append(device.get_id())
+ if device.get_id() in self._output_buttons.keys():
+ self._output_buttons[device.get_id()].freeze_notify()
+ self._output_buttons[device.get_id()].set_active(device.is_enabled())
+ self._output_buttons[device.get_id()].thaw_notify()
+ else:
+ button = Gtk.CheckButton(device.get_name())
+ if device.is_enabled():
+ button.set_active(True)
+ handler = button.connect('toggled', self.on_output_device_toggled, device)
+ self._output_devices.insert(button, -1)
+ self._output_buttons[device.get_id()] = button
+ self._output_devices.show_all()
+
+ # Remove devices
+ for id in self._output_buttons.keys():
+ if id not in device_ids:
+ self._output_devices.remove(self._output_buttons[id].get_parent())
+
+
+
+
+
class CoverPanel(GObject.GObject):
__gsignals__ = {
'toggle-fullscreen': (GObject.SIGNAL_RUN_FIRST, None, ()),