window.py 21.3 KB
Newer Older
Emily Gonyer's avatar
Emily Gonyer committed
1
2
3
4
5
6
7
8
9
# Copyright (c) 2013 Eslam Mostafa <cseslam@gmail.com>
# Copyright (c) 2013 Vadim Rutkovsky <vrutkovs@redhat.com>
# Copyright (c) 2013 Sai Suman Prayaga <suman.sai14@gmail.com>
# Copyright (c) 2013 Arnel A. Borja <kyoushuu@yahoo.com>
# Copyright (c) 2013 Shivani Poddar <shivani.poddar92@gmail.com>
# Copyright (c) 2013 Manish Sinha <manishsinha@ubuntu.com>
# Copyright (c) 2013 Seif Lotfy <seif@gmail.com>
# Copyright (c) 2013 Guillaume Quintard <guillaume.quintard@gmail.com>
#
Arnel A. Borja's avatar
Arnel A. Borja committed
10
11
12
13
# GNOME Music is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
Emily Gonyer's avatar
Emily Gonyer committed
14
#
Arnel A. Borja's avatar
Arnel A. Borja committed
15
16
17
18
# GNOME Music 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.
Emily Gonyer's avatar
Emily Gonyer committed
19
20
#
# You should have received a copy of the GNU General Public License along
Arnel A. Borja's avatar
Arnel A. Borja committed
21
22
23
24
25
26
27
28
29
30
# with GNOME Music; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# The GNOME Music authors hereby grant permission for non-GPL compatible
# GStreamer plugins to be used and distributed together with GStreamer
# and GNOME Music.  This permission is above and beyond the permissions
# granted by the GPL license by which GNOME Music is covered.  If you
# modify this code, you may extend this exception to your version of the
# code, but you are not obligated to do so.  If you do not wish to do so,
# delete this exception statement from your version.
Emily Gonyer's avatar
Emily Gonyer committed
31

32
from gi.repository import Gtk, Gdk, Gio, GLib
Marinus Schraal's avatar
Marinus Schraal committed
33
from gettext import gettext as _
Eslam Mostafa's avatar
Eslam Mostafa committed
34

Marinus Schraal's avatar
Marinus Schraal committed
35
from gnomemusic import log
36
from gnomemusic import TrackerWrapper
37
from gnomemusic.toolbar import Toolbar, ToolbarState
38
from gnomemusic.player import Player, SelectionToolbar, RepeatType
Vadim Rutkovsky's avatar
Vadim Rutkovsky committed
39
from gnomemusic.query import Query
Marinus Schraal's avatar
Marinus Schraal committed
40
from gnomemusic.utils import View
Marinus Schraal's avatar
Marinus Schraal committed
41
42
from gnomemusic.views.albumsview import AlbumsView
from gnomemusic.views.artistsview import ArtistsView
43
from gnomemusic.views.emptyview import EmptyView
Marinus Schraal's avatar
Marinus Schraal committed
44
from gnomemusic.views.emptysearchview import EmptySearchView
45
from gnomemusic.views.initialstateview import InitialStateView
Marinus Schraal's avatar
Marinus Schraal committed
46
47
48
from gnomemusic.views.searchview import SearchView
from gnomemusic.views.songsview import SongsView
from gnomemusic.views.playlistview import PlaylistView
49
from gnomemusic.widgets.notificationspopup import NotificationsPopup
50
from gnomemusic.widgets.playlistdialog import PlaylistDialog
Sai Suman Prayaga's avatar
Sai Suman Prayaga committed
51
from gnomemusic.playlists import Playlists
52
from gnomemusic.grilo import grilo
Marinus Schraal's avatar
Marinus Schraal committed
53

Vadim Rutkovsky's avatar
Vadim Rutkovsky committed
54
55
import logging
logger = logging.getLogger(__name__)
Eslam Mostafa's avatar
Eslam Mostafa committed
56

57
tracker = TrackerWrapper().tracker
58
playlists = Playlists.get_default()
Eslam Mostafa's avatar
Eslam Mostafa committed
59

60

Eslam Mostafa's avatar
Eslam Mostafa committed
61
class Window(Gtk.ApplicationWindow):
62

63
64
65
    def __repr__(self):
        return '<Window>'

Vadim Rutkovsky's avatar
Vadim Rutkovsky committed
66
    @log
Eslam Mostafa's avatar
Eslam Mostafa committed
67
    def __init__(self, app):
68
69
        super().__init__(application=app, title=_("Music"))

70
        self.settings = Gio.Settings.new('org.gnome.Music')
71
        self.add_action(self.settings.create_action('repeat'))
72
73
74
75
76
77
78
        select_all = Gio.SimpleAction.new('select_all', None)
        select_all.connect('activate', self._select_all)
        self.add_action(select_all)
        select_none = Gio.SimpleAction.new('select_none', None)
        select_none.connect('activate', self._select_none)
        self.add_action(select_none)

79
        self.set_size_request(200, 100)
80
        self.set_default_icon_name('org.gnome.Music')
81

82
83
84
        self.prev_view = None
        self.curr_view = None

85
86
87
88
89
90
91
92
93
94
95
96
97
        size_setting = self.settings.get_value('window-size')
        if isinstance(size_setting[0], int) and isinstance(size_setting[1], int):
            self.resize(size_setting[0], size_setting[1])

        position_setting = self.settings.get_value('window-position')
        if len(position_setting) == 2 \
           and isinstance(position_setting[0], int) \
           and isinstance(position_setting[1], int):
            self.move(position_setting[0], position_setting[1])

        if self.settings.get_value('window-maximized'):
            self.maximize()

Vadim Rutkovsky's avatar
Vadim Rutkovsky committed
98
        self._setup_view()
99
100
        self.notifications_popup = NotificationsPopup()
        self._overlay.add_overlay(self.notifications_popup)
Vadim Rutkovsky's avatar
Vadim Rutkovsky committed
101

102
103
104
        self._media_keys_proxy = None
        self._init_media_keys_proxy()

105
        self.window_size_update_timeout = None
106
        self.connect("window-state-event", self._on_window_state_event)
107
        self.connect("configure-event", self._on_configure_event)
108
109
        grilo.connect('changes-pending', self._on_changes_pending)

110
    @log
111
    def _on_changes_pending(self, data=None):
Marinus Schraal's avatar
Marinus Schraal committed
112
        # FIXME: This is not working right.
113
114
        def songs_available_cb(available):
            if available:
Marinus Schraal's avatar
Marinus Schraal committed
115
                if self.views[View.ALBUM] == self.views[-1]:
116
117
118
119
120
121
                    view = self.views.pop()
                    view.destroy()
                    self._switch_to_player_view()
                    self.toolbar._search_button.set_sensitive(True)
                    self.toolbar._select_button.set_sensitive(True)
                    self.toolbar.show_stack()
122
            elif (self.toolbar.selection_mode is False
123
124
125
126
127
128
129
130
131
132
133
                    and len(self.views) != 1):
                self._stack.disconnect(self._on_notify_model_id)
                self.disconnect(self._key_press_event_id)
                view_count = len(self.views)
                for i in range(0, view_count):
                    view = self.views.pop()
                    view.destroy()
                self.toolbar.hide_stack()
                self._switch_to_empty_view()

        grilo.songs_available(songs_available_cb)
134

135
    @log
136
    def _on_configure_event(self, widget, event):
137
138
        if self.window_size_update_timeout is None:
            self.window_size_update_timeout = GLib.timeout_add(500, self.store_window_size_and_position, widget)
139
140
141

    @log
    def store_window_size_and_position(self, widget):
142
143
144
145
146
        size = widget.get_size()
        self.settings.set_value('window-size', GLib.Variant('ai', [size[0], size[1]]))

        position = widget.get_position()
        self.settings.set_value('window-position', GLib.Variant('ai', [position[0], position[1]]))
147
148
149
        GLib.source_remove(self.window_size_update_timeout)
        self.window_size_update_timeout = None
        return False
150

Vadim Rutkovsky's avatar
Vadim Rutkovsky committed
151
    @log
152
    def _on_window_state_event(self, widget, event):
153
154
        self.settings.set_boolean('window-maximized', 'GDK_WINDOW_STATE_MAXIMIZED' in event.new_window_state.value_names)

Vadim Rutkovsky's avatar
Vadim Rutkovsky committed
155
    @log
156
157
158
159
160
161
162
163
164
165
166
167
168
    def _init_media_keys_proxy(self):
        def name_appeared(connection, name, name_owner, data=None):
            Gio.DBusProxy.new_for_bus(
                Gio.BusType.SESSION,
                Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES, None,
                "org.gnome.SettingsDaemon.MediaKeys",
                "/org/gnome/SettingsDaemon/MediaKeys",
                "org.gnome.SettingsDaemon.MediaKeys", None,
                self._media_keys_proxy_ready)

        Gio.bus_watch_name(
            Gio.BusType.SESSION, "org.gnome.SettingsDaemon.MediaKeys",
            Gio.BusNameWatcherFlags.NONE, name_appeared, None)
169
170

    @log
171
    def _media_keys_proxy_ready(self, proxy, result, data=None):
172
        try:
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
            self._media_keys_proxy = proxy.new_finish(result)
        except GLib.Error as e:
            logger.warning(
                "Error: Failed to contact settings daemon:", e.message)
            return

        self._grab_media_player_keys()
        self._media_keys_proxy.connect(
            "g-signal", self._handle_media_keys)
        self.connect("focus-in-event", self._grab_media_player_keys)

    @log
    def _grab_media_player_keys(self, window=None, event=None):
        def proxy_call_finished(proxy, result, data=None):
            try:
                proxy.call_finish(result)
            except GLib.Error as e:
                logger.warning(
                    "Error: Failed to grab mediaplayer keys: {}".format(
                        e.message))

        self._media_keys_proxy.call(
            "GrabMediaPlayerKeys", GLib.Variant("(su)", ("Music", 0)),
            Gio.DBusCallFlags.NONE, -1, None, proxy_call_finished)
Eslam Mostafa's avatar
Eslam Mostafa committed
197

Vadim Rutkovsky's avatar
Vadim Rutkovsky committed
198
    @log
199
    def _handle_media_keys(self, proxy, sender, signal, parameters):
200
201
        app, response = parameters.unpack()
        if app != "Music":
Eslam Mostafa's avatar
Eslam Mostafa committed
202
            return
203
204

        if "Play" in response:
205
            self.player.play_pause()
206
        elif "Stop" in response:
Eslam Mostafa's avatar
Eslam Mostafa committed
207
            self.player.Stop()
208
        elif "Next" in response:
209
            self.player.play_next()
210
        elif "Previous" in response:
211
            self.player.play_previous()
Eslam Mostafa's avatar
Eslam Mostafa committed
212

Vadim Rutkovsky's avatar
Vadim Rutkovsky committed
213
    @log
214
    def _setup_view(self):
Kris Thomsen's avatar
Kris Thomsen committed
215
        self._box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
216
        self.player = Player(self)
217
        self.selection_toolbar = SelectionToolbar()
Eslam Mostafa's avatar
Eslam Mostafa committed
218
        self.toolbar = Toolbar()
Marinus Schraal's avatar
Marinus Schraal committed
219
        self.views = [None] * len(View)
220
221
        self._stack = Gtk.Stack(
            transition_type=Gtk.StackTransitionType.CROSSFADE,
Eslam Mostafa's avatar
Eslam Mostafa committed
222
            transition_duration=100,
223
            homogeneous=False,
Sai Suman Prayaga's avatar
Sai Suman Prayaga committed
224
225
            visible=True,
            can_focus=False)
226
227
228
229
230

        # Add the 'background' styleclass so it properly hides the
        # bottom line of the searchbar
        self._stack.get_style_context().add_class('background')

231
232
        self._overlay = Gtk.Overlay(child=self._stack)
        self._overlay.add_overlay(self.toolbar.dropdown)
233
        self.set_titlebar(self.toolbar.header_bar)
Seif Lotfy's avatar
Seif Lotfy committed
234
        self._box.pack_start(self.toolbar.searchbar, False, False, 0)
235
        self._box.pack_start(self._overlay, True, True, 0)
236
237
        self._box.pack_start(self.player.actionbar, False, False, 0)
        self._box.pack_start(self.selection_toolbar.actionbar, False, False, 0)
Eslam Mostafa's avatar
Eslam Mostafa committed
238
        self.add(self._box)
239

240
241
242
243
244
        def songs_available_cb(available):
            if available:
                self._switch_to_player_view()
            else:
                self._switch_to_empty_view()
245
246
247

        Query()
        if Query.music_folder:
248
            grilo.songs_available(songs_available_cb)
Eslam Mostafa's avatar
Eslam Mostafa committed
249
        else:
250
            self._switch_to_empty_view()
Eslam Mostafa's avatar
Eslam Mostafa committed
251

252
        self.toolbar._search_button.connect('toggled', self._on_search_toggled)
Sai Suman Prayaga's avatar
Sai Suman Prayaga committed
253
254
255
        self.toolbar.connect('selection-mode-changed', self._on_selection_mode_changed)
        self.selection_toolbar._add_to_playlist_button.connect(
            'clicked', self._on_add_to_playlist_button_clicked)
256

Arnel Borja's avatar
Arnel Borja committed
257
        self.toolbar.set_state(ToolbarState.MAIN)
258
        self.toolbar.header_bar.show()
259
        self._overlay.show()
260
        self.player.actionbar.show_all()
Eslam Mostafa's avatar
Eslam Mostafa committed
261
262
263
        self._box.show()
        self.show()

Vadim Rutkovsky's avatar
Vadim Rutkovsky committed
264
    @log
265
    def _switch_to_empty_view(self):
Vadim Rutkovsky's avatar
Vadim Rutkovsky committed
266
267
268
        did_initial_state = self.settings.get_boolean('did-initial-state')
        view_class = None
        if did_initial_state:
269
            view_class = EmptyView
Vadim Rutkovsky's avatar
Vadim Rutkovsky committed
270
        else:
271
            view_class = InitialStateView
Marinus Schraal's avatar
Marinus Schraal committed
272
        self.views[View.ALBUM] = view_class(self, self.player)
Vadim Rutkovsky's avatar
Vadim Rutkovsky committed
273

Marinus Schraal's avatar
Marinus Schraal committed
274
        self._stack.add_titled(self.views[View.ALBUM], _("Empty"), _("Empty"))
275
276
277
        self.toolbar._search_button.set_sensitive(False)
        self.toolbar._select_button.set_sensitive(False)

Vadim Rutkovsky's avatar
Vadim Rutkovsky committed
278
    @log
279
    def _switch_to_player_view(self):
Vadim Rutkovsky's avatar
Vadim Rutkovsky committed
280
        self.settings.set_boolean('did-initial-state', True)
281
282
283
284
        self._on_notify_model_id = self._stack.connect('notify::visible-child', self._on_notify_mode)
        self.connect('destroy', self._notify_mode_disconnect)
        self._key_press_event_id = self.connect('key_press_event', self._on_key_press)

Marinus Schraal's avatar
Marinus Schraal committed
285
286
287
288
289
290
        self.views[View.ALBUM] = AlbumsView(self, self.player)
        self.views[View.ARTIST] = ArtistsView(self, self.player)
        self.views[View.SONG] = SongsView(self, self.player)
        self.views[View.PLAYLIST] = PlaylistView(self, self.player)
        self.views[View.SEARCH] = SearchView(self, self.player)
        self.views[View.EMPTY_SEARCH] = EmptySearchView(self, self.player)
291
292

        for i in self.views:
293
294
295
296
            if i.title:
                self._stack.add_titled(i, i.name, i.title)
            else:
                self._stack.add_named(i, i.name)
297
298
299

        self.toolbar.set_stack(self._stack)
        self.toolbar.searchbar.show()
300
        self.toolbar.dropdown.show()
301

302
303
        for i in self.views:
            GLib.idle_add(i.populate)
304

Vadim Rutkovsky's avatar
Vadim Rutkovsky committed
305
    @log
306
    def _select_all(self, action=None, param=None):
307
        if not self.toolbar.selection_mode:
308
            return
Arnel Borja's avatar
Arnel Borja committed
309
        if self.toolbar._state == ToolbarState.MAIN:
310
            view = self._stack.get_visible_child()
Sai Suman Prayaga's avatar
Sai Suman Prayaga committed
311
        else:
312
313
314
            view = self._stack.get_visible_child().get_visible_child()

        view.select_all()
Sai Suman Prayaga's avatar
Sai Suman Prayaga committed
315

Vadim Rutkovsky's avatar
Vadim Rutkovsky committed
316
    @log
317
    def _select_none(self, action=None, param=None):
318
        if not self.toolbar.selection_mode:
319
            return
Arnel Borja's avatar
Arnel Borja committed
320
        if self.toolbar._state == ToolbarState.MAIN:
321
            view = self._stack.get_visible_child()
Marinus Schraal's avatar
Marinus Schraal committed
322
            view.unselect_all()
Sai Suman Prayaga's avatar
Sai Suman Prayaga committed
323
        else:
324
            view = self._stack.get_visible_child().get_visible_child()
Marinus Schraal's avatar
Marinus Schraal committed
325
            view.select_none()
Sai Suman Prayaga's avatar
Sai Suman Prayaga committed
326

Vadim Rutkovsky's avatar
Vadim Rutkovsky committed
327
    @log
Vadim Rutkovsky's avatar
Vadim Rutkovsky committed
328
    def _on_key_press(self, widget, event):
329
        modifiers = Gtk.accelerator_get_default_mod_mask()
330
331
332
333
334
335
336
337
338
339
340
341
342
        modifiers = (event.state & modifiers)

        if modifiers != 0:
            control_mask = Gdk.ModifierType.CONTROL_MASK
            shift_mask = Gdk.ModifierType.SHIFT_MASK
            mod1_mask = Gdk.ModifierType.MOD1_MASK

            if (event.keyval == Gdk.KEY_a
                    and modifiers == control_mask):
                self._select_all()
            if (event.keyval == Gdk.KEY_A
                    and modifiers == (shift_mask | control_mask)):
                self._select_none()
343
            # Open search bar on Ctrl + F
344
            if ((event.keyval == Gdk.KEY_f and modifiers == control_mask)
345
                    and not self.views[View.PLAYLIST].rename_active
346
                    and self.toolbar._state != ToolbarState.SEARCH_VIEW):
347
                self.toolbar.searchbar.toggle()
348
349
            # Play / Pause on Ctrl + SPACE
            if (event.keyval == Gdk.KEY_space
350
                    and modifiers == control_mask):
FlorianDarfeuille's avatar
FlorianDarfeuille committed
351
                self.player.play_pause()
352
353
            # Play previous on Ctrl + B
            if (event.keyval == Gdk.KEY_b
354
                    and modifiers == control_mask):
355
356
357
                self.player.play_previous()
            # Play next on Ctrl + N
            if (event.keyval == Gdk.KEY_n
358
                    and modifiers == control_mask):
359
360
361
                self.player.play_next()
            # Toggle repeat on Ctrl + R
            if (event.keyval == Gdk.KEY_r
362
                    and modifiers == control_mask):
363
364
365
366
367
368
                if self.player.get_repeat_mode() == RepeatType.SONG:
                    self.player.set_repeat_mode(RepeatType.NONE)
                else:
                    self.player.set_repeat_mode(RepeatType.SONG)
            # Toggle shuffle on Ctrl + S
            if (event.keyval == Gdk.KEY_s
369
                    and modifiers == control_mask):
370
371
372
373
                if self.player.get_repeat_mode() == RepeatType.SHUFFLE:
                    self.player.set_repeat_mode(RepeatType.NONE)
                else:
                    self.player.set_repeat_mode(RepeatType.SHUFFLE)
374
            # Go back from Album view on Alt + Left
375
376
            if (event.keyval == Gdk.KEY_Left
                    and modifiers == mod1_mask):
377
                self.toolbar.on_back_button_clicked()
378
            if ((event.keyval in [Gdk.KEY_1, Gdk.KEY_KP_1])
379
                    and modifiers == control_mask):
380
                self._toggle_view(View.ALBUM)
381
            if ((event.keyval in [Gdk.KEY_2, Gdk.KEY_KP_2])
382
                    and modifiers == control_mask):
383
                self._toggle_view(View.ARTIST)
384
            if ((event.keyval in [Gdk.KEY_3, Gdk.KEY_KP_3])
385
                    and modifiers == control_mask):
386
                self._toggle_view(View.SONG)
387
            if ((event.keyval in [Gdk.KEY_4, Gdk.KEY_KP_4])
388
                    and modifiers == control_mask):
389
                self._toggle_view(View.PLAYLIST)
390
        else:
Jean Felder's avatar
Jean Felder committed
391
392
393
394
395
396
397
398
399
400
401
402
403
            if (event.keyval == Gdk.KEY_AudioPlay
                    or event.keyval == Gdk.KEY_AudioPause):
                self.player.play_pause()

            if event.keyval == Gdk.KEY_AudioStop:
                self.player.Stop()

            if event.keyval == Gdk.KEY_AudioPrev:
                self.player.play_prev()

            if event.keyval == Gdk.KEY_AudioNext:
                self.player.play_next()

Marinus Schraal's avatar
Marinus Schraal committed
404
405
406
407
            child = self._stack.get_visible_child()
            if (event.keyval == Gdk.KEY_Delete
                    and child == self.views[View.PLAYLIST]):
                self.views[View.PLAYLIST].remove_playlist()
408
409
            # Close search bar after Esc is pressed
            if event.keyval == Gdk.KEY_Escape:
410
                self.toolbar.searchbar.reveal(False)
411
                # Also disable selection
412
                if self.toolbar.selection_mode:
413
414
                    self.toolbar.set_selection_mode(False)

415
416
417
418
419
        # Open the search bar when typing printable chars.
        key_unic = Gdk.keyval_to_unicode(event.keyval)
        if ((not self.toolbar.searchbar.get_search_mode()
                and not event.keyval == Gdk.KEY_space)
                and GLib.unichar_isprint(chr(key_unic))
420
421
                and (modifiers == Gdk.ModifierType.SHIFT_MASK
                     or modifiers == 0)
422
423
                and not self.views[View.PLAYLIST].rename_active
                and self.toolbar._state != ToolbarState.SEARCH_VIEW):
424
            self.toolbar.searchbar.reveal(True)
Vadim Rutkovsky's avatar
Vadim Rutkovsky committed
425

426
427
428
429
430
431
432
433
434
435
436
    @log
    def do_button_release_event(self, event):
        """Override default button release event

        :param Gdk.EventButton event: Button event
        """
        __, code = event.get_button()
        # Mouse button 8 is the navigation button
        if code == 8:
            self.toolbar.on_back_button_clicked()

Vadim Rutkovsky's avatar
Vadim Rutkovsky committed
437
    @log
438
    def _notify_mode_disconnect(self, data=None):
439
        self.player.Stop()
440
        self.notifications_popup.terminate_pending()
441
        self._stack.disconnect(self._on_notify_model_id)
442

Vadim Rutkovsky's avatar
Vadim Rutkovsky committed
443
    @log
444
    def _on_notify_mode(self, stack, param):
445
446
447
        self.prev_view = self.curr_view
        self.curr_view = stack.get_visible_child()

448
        # Switch to all albums view when we're clicking Albums
Marinus Schraal's avatar
Marinus Schraal committed
449
450
451
        if (self.curr_view == self.views[View.ALBUM]
                and not (self.prev_view == self.views[View.SEARCH]
                    or self.prev_view == self.views[View.EMPTY_SEARCH])):
452
453
            self.curr_view.set_visible_child(self.curr_view._grid)

454
        # Slide out sidebar on switching to Artists or Playlists view
Marinus Schraal's avatar
Marinus Schraal committed
455
456
        if self.curr_view == self.views[View.ARTIST] or \
           self.curr_view == self.views[View.PLAYLIST]:
457
458
            self.curr_view.stack.set_visible_child_name('dummy')
            self.curr_view.stack.set_visible_child_name('sidebar')
Marinus Schraal's avatar
Marinus Schraal committed
459
        if self.curr_view != self.views[View.SEARCH] and self.curr_view != self.views[View.EMPTY_SEARCH]:
460
            self.toolbar.searchbar.reveal(False)
Eslam Mostafa's avatar
Eslam Mostafa committed
461

462
463
464
465
466
467
468
469
        # Disable the selection button for the EmptySearch and Playlist
        # view
        no_selection_mode = [
            self.views[View.EMPTY_SEARCH],
            self.views[View.PLAYLIST]
        ]
        self.toolbar._select_button.set_sensitive(
            self.curr_view not in no_selection_mode)
470

471
472
        # Disable renaming playlist if it was active when leaving
        # Playlist view
Marinus Schraal's avatar
Marinus Schraal committed
473
474
475
        if (self.prev_view == self.views[View.PLAYLIST]
                and self.views[View.PLAYLIST].rename_active):
            self.views[View.PLAYLIST].disable_rename_playlist()
476

Vadim Rutkovsky's avatar
Vadim Rutkovsky committed
477
    @log
478
479
    def _toggle_view(self, view_enum):
        self._stack.set_visible_child(self.views[view_enum])
480

Vadim Rutkovsky's avatar
Vadim Rutkovsky committed
481
    @log
482
    def _on_search_toggled(self, button, data=None):
483
        self.toolbar.searchbar.reveal(
Marinus Schraal's avatar
Marinus Schraal committed
484
485
486
487
            button.get_active(), self.curr_view != self.views[View.SEARCH])
        if (not button.get_active()
                and (self.curr_view == self.views[View.SEARCH]
                    or self.curr_view == self.views[View.EMPTY_SEARCH])):
488
            child = self.curr_view.get_visible_child()
489
490
            if self.toolbar._state == ToolbarState.MAIN:
                # We should get back to the view before the search
Marinus Schraal's avatar
Marinus Schraal committed
491
492
493
                self._stack.set_visible_child(
                    self.views[View.SEARCH].previous_view)
            elif (self.views[View.SEARCH].previous_view == self.views[View.ALBUM]
494
495
                    and child != self.curr_view._album_widget
                    and child != self.curr_view._artist_albums_widget):
Marinus Schraal's avatar
Marinus Schraal committed
496
                self._stack.set_visible_child(self.views[View.ALBUM])
497

498
            if self.toolbar.selection_mode:
499
                self.toolbar.set_selection_mode(False)
Sai Suman Prayaga's avatar
Sai Suman Prayaga committed
500

Vadim Rutkovsky's avatar
Vadim Rutkovsky committed
501
    @log
Sai Suman Prayaga's avatar
Sai Suman Prayaga committed
502
    def _on_selection_mode_changed(self, widget, data=None):
503
        if self.toolbar.selection_mode is False:
504
505
            self._on_changes_pending()
        else:
Marinus Schraal's avatar
Marinus Schraal committed
506
507
            child = self._stack.get_visible_child()
            in_playlist = (child == self.views[View.PLAYLIST])
Sai Suman Prayaga's avatar
Sai Suman Prayaga committed
508
509
            self.selection_toolbar._add_to_playlist_button.set_visible(not in_playlist)

Vadim Rutkovsky's avatar
Vadim Rutkovsky committed
510
    @log
Sai Suman Prayaga's avatar
Sai Suman Prayaga committed
511
    def _on_add_to_playlist_button_clicked(self, widget):
Marinus Schraal's avatar
Marinus Schraal committed
512
        if self._stack.get_visible_child() == self.views[View.PLAYLIST]:
Sai Suman Prayaga's avatar
Sai Suman Prayaga committed
513
514
            return

515
516
        def callback(selected_songs):
            if len(selected_songs) < 1:
Sai Suman Prayaga's avatar
Sai Suman Prayaga committed
517
518
                return

Marinus Schraal's avatar
Marinus Schraal committed
519
            playlist_dialog = PlaylistDialog(
520
                self, self.views[View.PLAYLIST].pls_todelete)
Yash Singh's avatar
Yash Singh committed
521
            if playlist_dialog.run() == Gtk.ResponseType.ACCEPT:
522
523
                playlists.add_to_playlist(
                    playlist_dialog.get_selected(), selected_songs)
Sai Suman Prayaga's avatar
Sai Suman Prayaga committed
524
            self.toolbar.set_selection_mode(False)
Yash Singh's avatar
Yash Singh committed
525
            playlist_dialog.destroy()
Sai Suman Prayaga's avatar
Sai Suman Prayaga committed
526

527
        self._stack.get_visible_child().get_selected_songs(callback)