container_lists.py 13 KB
Newer Older
1
# Copyright (c) 2014-2020 Cedric Bellegarde <cedric.bellegarde@adishatz.org>
Cédric Bellegarde's avatar
Cédric Bellegarde committed
2 3 4 5 6 7 8 9 10 11 12
# 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/>.

13
from gi.repository import Gtk, GLib, Pango
Cédric Bellegarde's avatar
Cédric Bellegarde committed
14 15 16

from lollypop.logger import Logger
from lollypop.selectionlist import SelectionList
Cédric Bellegarde's avatar
Cédric Bellegarde committed
17
from lollypop.define import App, Type, SelectionListMask, StorageType, ViewType
18
from lollypop.shown import ShownLists
19
from lollypop.helper_gestures import GesturesHelper
20
from lollypop.view import View
21
from lollypop.utils import emit_signal, get_default_storage_type, get_icon_name
22 23 24 25 26 27 28


class NoneView(View):
    """
        A view that do nothing
    """

29
    def __init__(self):
Cédric Bellegarde's avatar
Cédric Bellegarde committed
30
        View.__init__(self, StorageType.COLLECTION, ViewType.DEFAULT)
31

32 33 34
    @property
    def args(self):
        return None
Cédric Bellegarde's avatar
Cédric Bellegarde committed
35 36 37 38 39 40 41 42 43 44 45


class ListsContainer:
    """
        Selections lists management for main view
    """

    def __init__(self):
        """
            Init container
        """
46 47
        self.__left_list = None
        self.__right_list = None
Cédric Bellegarde's avatar
Cédric Bellegarde committed
48 49 50 51 52

    def setup_lists(self):
        """
            Setup container lists
        """
53 54 55 56 57
        self._sidebar = SelectionList(SelectionListMask.SIDEBAR)
        self._sidebar.show()
        self._sidebar.listbox.connect("row-activated",
                                      self.__on_sidebar_activated)
        self._sidebar.connect("populated", self.__on_sidebar_populated)
58
        self._main_widget.insert_column(0)
59 60 61
        self._sidebar.set_mask(SelectionListMask.SIDEBAR)
        items = ShownLists.get(SelectionListMask.SIDEBAR)
        self._sidebar.populate(items)
62

Cédric Bellegarde's avatar
Cédric Bellegarde committed
63
    @property
64
    def sidebar(self):
Cédric Bellegarde's avatar
Cédric Bellegarde committed
65 66 67 68
        """
            Get first SelectionList
            @return SelectionList
        """
69
        return self._sidebar
Cédric Bellegarde's avatar
Cédric Bellegarde committed
70

71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
    @property
    def left_list(self):
        """
            Get left selection list
            @return SelectionList
        """
        if self.__left_list is None:
            self.__left_list = SelectionList(SelectionListMask.VIEW)
            self.__left_list.listbox.connect("row-activated",
                                             self.__on_left_list_activated)
        return self.__left_list

    @property
    def right_list(self):
        """
            Get right selection list
Cédric Bellegarde's avatar
Cédric Bellegarde committed
87 88
            @return SelectionList
        """
89 90 91 92
        def on_unmap(widget):
            """
                Hide right list on left list hidden
            """
93 94
            if not App().window.is_adaptive:
                self._hide_right_list()
95

96 97 98 99 100 101 102
        if self.__right_list is None:
            eventbox = Gtk.EventBox.new()
            eventbox.set_size_request(50, -1)
            eventbox.show()
            self.__right_list_grid = Gtk.Grid()
            style_context = self.__right_list_grid.get_style_context()
            style_context.add_class("left-gradient")
103
            style_context.add_class("opacity-transition-fast")
104 105 106
            self.__right_list = SelectionList(SelectionListMask.VIEW)
            self.__right_list.show()
            self.__gesture = GesturesHelper(
107
                eventbox, primary_press_callback=self._hide_right_list)
108 109 110 111
            self.__right_list.listbox.connect("row-activated",
                                              self.__on_right_list_activated)
            self.__right_list_grid.add(eventbox)
            self.__right_list_grid.add(self.__right_list)
Cédric Bellegarde's avatar
Cédric Bellegarde committed
112
            self.__left_list.overlay.add_overlay(self.__right_list_grid)
113
            self.__left_list.connect("unmap", on_unmap)
Cédric Bellegarde's avatar
Cédric Bellegarde committed
114 115
            self.__left_list.connect("populated", self.__on_list_populated)
            self.__right_list.connect("populated", self.__on_list_populated)
116
        return self.__right_list
Cédric Bellegarde's avatar
Cédric Bellegarde committed
117

118 119 120 121 122 123 124 125 126 127 128 129 130 131
    @property
    def sidebar_id(self):
        """
            Get sidebar id for current state
            @return int
        """
        if self.right_list.selected_ids:
            ids = self.right_list.selected_ids
        elif self.left_list.selected_ids:
            ids = self.left_list.selected_ids
        else:
            ids = self.sidebar.selected_ids
        return ids[0] if ids else Type.NONE

132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
##############
# PROTECTED  #
##############
    def _show_genres_list(self, selection_list):
        """
            Setup list for genres
            @param list as SelectionList
        """
        def load():
            genres = App().genres.get()
            return genres
        selection_list.set_mask(SelectionListMask.GENRES)
        App().task_helper.run(load, callback=(selection_list.populate,))

    def _show_artists_list(self, selection_list, genre_ids=[]):
        """
            Setup list for artists
            @param list as SelectionList
            @param genre_ids as [int]
        """
        def load():
153
            storage_type = get_default_storage_type()
154
            artists = App().artists.get(genre_ids, storage_type)
155 156 157 158
            return artists
        selection_list.set_mask(SelectionListMask.ARTISTS)
        App().task_helper.run(load, callback=(selection_list.populate,))

159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
    def _show_right_list(self):
        """
            Show right list
        """
        if self.__right_list is not None:
            self.__right_list_grid.show()
            self.__right_list_grid.set_state_flags(Gtk.StateFlags.VISITED,
                                                   False)
            self.set_focused_view(self.right_list)

    def _hide_right_list(self, *ignore):
        """
            Hide right list
        """
        if self.__right_list is not None:
            self.__right_list_grid.unset_state_flags(Gtk.StateFlags.VISITED)
            GLib.timeout_add(200, self.__right_list_grid.hide)
            self.__right_list.clear()
177
            self.set_focused_view(self.left_list)
178

Cédric Bellegarde's avatar
Cédric Bellegarde committed
179 180 181
############
# PRIVATE  #
############
182
    def __on_sidebar_activated(self, listbox, row):
Cédric Bellegarde's avatar
Cédric Bellegarde committed
183 184
        """
            Update view based on selected object
185 186
            @param listbox as Gtk.ListBox
            @param row as Gtk.ListBoxRow
Cédric Bellegarde's avatar
Cédric Bellegarde committed
187
        """
188
        Logger.debug("Container::__on_sidebar_activated()")
Cédric Bellegarde's avatar
Cédric Bellegarde committed
189
        view = None
190
        focus_set = False
191
        selected_id = self._sidebar.selected_id
192
        if selected_id is None:
193
            return
Cédric Bellegarde's avatar
Cédric Bellegarde committed
194
        # Update lists
195 196 197 198 199 200
        if selected_id in [Type.ARTISTS_LIST, Type.GENRES_LIST] and\
                self.type_ahead.get_reveal_child() and\
                self.left_list.get_visible():
            self.set_focused_view(self.left_list)
            focus_set = True
        elif selected_id == Type.ARTISTS_LIST:
201
            self._show_artists_list(self.left_list)
202
            self._hide_right_list()
Cédric Bellegarde's avatar
Cédric Bellegarde committed
203
            self.left_list.show()
204 205
            self.set_focused_view(self.left_list)
            focus_set = True
206
        elif selected_id == Type.GENRES_LIST:
207
            self._show_genres_list(self.left_list)
208
            self._hide_right_list()
Cédric Bellegarde's avatar
Cédric Bellegarde committed
209
            self.left_list.show()
210 211
            self.set_focused_view(self.left_list)
            focus_set = True
Cédric Bellegarde's avatar
Cédric Bellegarde committed
212
        else:
Cédric Bellegarde's avatar
Cédric Bellegarde committed
213
            self.left_list.hide()
214
            self.left_list.clear()
215

216
        storage_type = get_default_storage_type()
217 218 219 220 221
        if selected_id in [Type.ARTISTS_LIST, Type.GENRES_LIST] and not\
                App().window.is_adaptive:
            view = NoneView()
            view.show()
        elif selected_id == Type.PLAYLISTS:
222
            view = self._get_view_playlists()
223
        elif selected_id == Type.LYRICS:
224
            view = self._get_view_lyrics()
225
        elif selected_id == Type.CURRENT:
226
            view = self.get_view_current()
227
        elif selected_id == Type.SEARCH:
228
            view = self.get_view_search()
229
        elif selected_id == Type.SUGGESTIONS:
230
            view = self._get_view_suggestions(storage_type)
231 232 233
        elif selected_id in [Type.POPULARS,
                             Type.LOVED,
                             Type.RECENTS,
234
                             Type.LITTLE,
235
                             Type.RANDOMS]:
236
            view = self._get_view_albums([selected_id], [], storage_type)
237 238
        elif selected_id == Type.WEB:
            view = self._get_view_albums([selected_id], [], StorageType.SAVED)
239
        elif selected_id == Type.RADIOS:
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
            message = """Radio support has been removed, sorry for that.
            For a better radio player, use Shortwave:
            """
            view = View(StorageType.ALL, ViewType.DEFAULT)
            image = Gtk.Image.new_from_icon_name(get_icon_name(Type.RADIOS),
                                                 Gtk.IconSize.INVALID)
            image.set_pixel_size(256)
            image.set_property("expand", True)
            style = image.get_style_context()
            style.add_class("dim-label")
            label = Gtk.Label()
            style = label.get_style_context()
            style.add_class("dim-label")
            style.add_class("text-xx-large")
            label.set_markup("%s" % GLib.markup_escape_text(message))
            label.set_line_wrap_mode(Pango.WrapMode.WORD)
            label.set_line_wrap(True)
            button = Gtk.LinkButton.new(
                "https://flathub.org/apps/details/de.haeckerfelix.Shortwave")
            grid = Gtk.Grid()
            grid.set_valign(Gtk.Align.CENTER)
            grid.set_row_spacing(20)
            grid.set_orientation(Gtk.Orientation.VERTICAL)
            grid.add(image)
            grid.add(label)
            grid.add(button)
            view.add_widget(grid)
            view.show_all()
268
        elif selected_id == Type.YEARS:
269
            view = self._get_view_albums_decades(storage_type)
270
        elif selected_id == Type.GENRES:
271
            view = self._get_view_genres(storage_type)
272
        elif selected_id == Type.ARTISTS:
273
            view = self._get_view_artists_rounded(storage_type)
274
        elif selected_id == Type.ALL:
275
            view = self._get_view_albums([selected_id], [], storage_type)
276
        elif selected_id == Type.COMPILATIONS:
277
            view = self._get_view_albums([selected_id], [], storage_type)
278
        if view is not None and view not in self._stack.get_children():
279
            view.show()
280
            self._stack.add(view)
281 282
        # If we are in paned stack mode, show list two if wanted
        if App().window.is_adaptive\
283
                and selected_id in [Type.ARTISTS_LIST, Type.GENRES_LIST]:
284
            self._stack.set_visible_child(self.left_list)
285
        elif view is not None:
286
            self._stack.set_visible_child(view)
287 288
            if not focus_set:
                self.set_focused_view(view)
Cédric Bellegarde's avatar
Cédric Bellegarde committed
289

290
    def __on_sidebar_populated(self, selection_list):
Cédric Bellegarde's avatar
Cédric Bellegarde committed
291 292 293
        """
            @param selection_list as SelectionList
        """
Cédric Bellegarde's avatar
Cédric Bellegarde committed
294 295
        if App().settings.get_value("save-state"):
            self._stack.load_history()
Cédric Bellegarde's avatar
Cédric Bellegarde committed
296
            emit_signal(self, "can-go-back-changed", self.can_go_back)
Cédric Bellegarde's avatar
Cédric Bellegarde committed
297 298 299
        else:
            startup_id = App().settings.get_value("startup-id").get_int32()
            if startup_id == -1:
300
                if not App().window.is_adaptive:
301
                    selection_list.select_first()
Cédric Bellegarde's avatar
Cédric Bellegarde committed
302 303
            else:
                selection_list.select_ids([startup_id], True)
Cédric Bellegarde's avatar
Cédric Bellegarde committed
304

305
    def __on_left_list_activated(self, listbox, row):
Cédric Bellegarde's avatar
Cédric Bellegarde committed
306 307
        """
            Update view based on selected object
308 309
            @param listbox as Gtk.ListBox
            @param row as Gtk.ListBoxRow
Cédric Bellegarde's avatar
Cédric Bellegarde committed
310
        """
311 312
        Logger.debug("Container::__on_left_list_activated()")
        selected_ids = self.left_list.selected_ids
313
        view = None
314
        storage_type = get_default_storage_type()
315
        if self.left_list.mask & SelectionListMask.GENRES:
316
            if not App().window.is_adaptive:
317
                view = self._get_view_albums(selected_ids, [], storage_type)
318
            self._show_artists_list(self.right_list, selected_ids)
319
            self._show_right_list()
320
        else:
321
            view = self._get_view_artists([], selected_ids, storage_type)
322
            self.set_focused_view(view)
323 324 325 326
        if view is not None:
            view.show()
            self._stack.add(view)
            self._stack.set_visible_child(view)
Cédric Bellegarde's avatar
Cédric Bellegarde committed
327

328
    def __on_right_list_activated(self, listbox, row):
329
        """
330 331 332
            Update view based on selected object
            @param listbox as Gtk.ListBox
            @param row as Gtk.ListBoxRow
333
        """
334
        storage_type = get_default_storage_type()
335 336
        genre_ids = self.left_list.selected_ids
        artist_ids = self.right_list.selected_ids
337
        view = self._get_view_artists(genre_ids, artist_ids, storage_type)
338 339 340
        view.show()
        self._stack.add(view)
        self._stack.set_visible_child(view)
341
        self.set_focused_view(view)
Cédric Bellegarde's avatar
Cédric Bellegarde committed
342 343 344 345 346 347 348

    def __on_list_populated(self, selection_list):
        """
            Select pending ids
            @param selection_list as SelectionList
        """
        selection_list.select_pending_ids()