<commit_before>//////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2007-2016 musikcube team
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//    * Redistributions of source code must retain the above copyright notice,
//      this list of conditions and the following disclaimer.
//
//    * Redistributions in binary form must reproduce the above copyright
//      notice, this list of conditions and the following disclaimer in the
//      documentation and/or other materials provided with the distribution.
//
//    * Neither the name of the author nor the names of other contributors may
//      be used to endorse or promote products derived from this software
//      without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"

#include <cursespp/Colors.h>
#include <cursespp/Screen.h>
#include <cursespp/Text.h>
#include <core/library/LocalLibraryConstants.h>
#include <app/util/Hotkeys.h>
#include <glue/query/NowPlayingTrackListQuery.h>
#include <glue/util/Duration.h>
#include "NowPlayingLayout.h"

#include <set>
#include <boost/format.hpp>

using namespace musik::core::library::constants;

using namespace musik::core;
using namespace musik::core::audio;
using namespace musik::core::library;
using namespace musik::box;
using namespace musik::glue;
using namespace cursespp;

static std::set<std::string> EDIT_KEYS;

static std::string formatWithAlbum(TrackPtr track, size_t width);

NowPlayingLayout::NowPlayingLayout(
    musik::core::audio::PlaybackService& playback,
    musik::core::LibraryPtr library)
: LayoutBase()
, playback(playback)
, library(library)
, reselectIndex(-1) {
    this->InitializeWindows();
    this->playback.Shuffled.connect(this, &NowPlayingLayout::OnPlaybackShuffled);

    EDIT_KEYS = {
        Hotkeys::Get(Hotkeys::PlayQueueMoveUp),
        Hotkeys::Get(Hotkeys::PlayQueueMoveDown),
        Hotkeys::Get(Hotkeys::PlayQueueDelete)
    };
}

NowPlayingLayout::~NowPlayingLayout() {

}

void NowPlayingLayout::OnLayout() {
    this->trackList->MoveAndResize(
        0,
        0,
        this->GetWidth(),
        this->GetHeight());

    this->trackList->SetFocusOrder(1);
}

void NowPlayingLayout::InitializeWindows() {
    this->trackList.reset(new TrackListView(
        this->playback,
        this->library,
        std::bind(formatWithAlbum, std::placeholders::_1, std::placeholders::_2)));

    this->trackList->Requeried.connect(this, &NowPlayingLayout::OnTrackListRequeried);
    this->AddWindow(this->trackList);
}

void NowPlayingLayout::OnVisibilityChanged(bool visible) {
    LayoutBase::OnVisibilityChanged(visible);

    if (visible) {
        this->RequeryTrackList();
    }
    else {
        this->trackList->Clear();
    }
}

void NowPlayingLayout::OnTrackListRequeried() {
    if (playback.Count()) {
        if (this->reselectIndex == -1) {
            size_t index = playback.GetIndex();

            if (index == (size_t)-1) { /* not playing? */
                this->trackList->SetSelectedIndex(0);
                this->trackList->ScrollTo(0);
            }
            else { /* playing... */
                this->trackList->SetSelectedIndex(index);
                this->trackList->ScrollTo(index == 0 ? index : index - 1);
            }
        }
        else { /* requeried due to edit, so reselect... */
            this->trackList->SetSelectedIndex((int) this->reselectIndex);
            auto pos = this->trackList->GetScrollPosition();
            int first = (int) pos.firstVisibleEntryIndex;
            int last = (int) first + pos.visibleEntryCount;
            int index = (int) this->reselectIndex;
            if (index < first || index >= last) {
                this->trackList->ScrollTo(this->reselectIndex);
            }
        }
    }

    this->reselectIndex = -1;
}

void NowPlayingLayout::OnPlaybackShuffled(bool shuffled) {
    this->RequeryTrackList();
}

void NowPlayingLayout::RequeryTrackList() {
    this->trackList->Requery(std::shared_ptr<TrackListQueryBase>(
        new NowPlayingTrackListQuery(this->library, this->playback)));
}

bool NowPlayingLayout::KeyPress(const std::string& key) {
    if (key == "KEY_ENTER") {
        this->playback.Play(this->trackList->GetSelectedIndex());
        return true;
    }
    else if (ProcessEditOperation(key)) {
        return true;
    }

    return LayoutBase::KeyPress(key);
}

bool NowPlayingLayout::ProcessEditOperation(const std::string& key) {
    if (EDIT_KEYS.find(key) != EDIT_KEYS.end()) {
        if (!playback.IsShuffled()) {
            PlaybackService::Editor editor = this->playback.Edit();
            size_t selected = this->trackList->GetSelectedIndex();

            if (Hotkeys::Is(Hotkeys::PlayQueueMoveUp, key)) {
                if (selected > 0) {
                    size_t to = selected - 1;
                    editor.Move(selected, to);
                    this->reselectIndex = (int)to;
                }<commit_msg>Cleaned up an edge case where adding stuff to the play queue while not playing, then switching to the play queue, would result in nothing being selected.<commit_after>//////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2007-2016 musikcube team
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//    * Redistributions of source code must retain the above copyright notice,
//      this list of conditions and the following disclaimer.
//
//    * Redistributions in binary form must reproduce the above copyright
//      notice, this list of conditions and the following disclaimer in the
//      documentation and/or other materials provided with the distribution.
//
//    * Neither the name of the author nor the names of other contributors may
//      be used to endorse or promote products derived from this software
//      without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"

#include <cursespp/Colors.h>
#include <cursespp/Screen.h>
#include <cursespp/Text.h>
#include <core/library/LocalLibraryConstants.h>
#include <app/util/Hotkeys.h>
#include <glue/query/NowPlayingTrackListQuery.h>
#include <glue/util/Duration.h>
#include "NowPlayingLayout.h"

#include <set>
#include <boost/format.hpp>

using namespace musik::core::library::constants;

using namespace musik::core;
using namespace musik::core::audio;
using namespace musik::core::library;
using namespace musik::box;
using namespace musik::glue;
using namespace cursespp;

static std::set<std::string> EDIT_KEYS;

static std::string formatWithAlbum(TrackPtr track, size_t width);

NowPlayingLayout::NowPlayingLayout(
    musik::core::audio::PlaybackService& playback,
    musik::core::LibraryPtr library)
: LayoutBase()
, playback(playback)
, library(library)
, reselectIndex(-1) {
    this->InitializeWindows();
    this->playback.Shuffled.connect(this, &NowPlayingLayout::OnPlaybackShuffled);

    EDIT_KEYS = {
        Hotkeys::Get(Hotkeys::PlayQueueMoveUp),
        Hotkeys::Get(Hotkeys::PlayQueueMoveDown),
        Hotkeys::Get(Hotkeys::PlayQueueDelete)
    };
}

NowPlayingLayout::~NowPlayingLayout() {

}

void NowPlayingLayout::OnLayout() {
    this->trackList->MoveAndResize(
        0,
        0,
        this->GetWidth(),
        this->GetHeight());

    this->trackList->SetFocusOrder(1);
}

void NowPlayingLayout::InitializeWindows() {
    this->trackList.reset(new TrackListView(
        this->playback,
        this->library,
        std::bind(formatWithAlbum, std::placeholders::_1, std::placeholders::_2)));

    this->trackList->Requeried.connect(this, &NowPlayingLayout::OnTrackListRequeried);
    this->AddWindow(this->trackList);
}

void NowPlayingLayout::OnVisibilityChanged(bool visible) {
    LayoutBase::OnVisibilityChanged(visible);

    if (visible) {
        this->RequeryTrackList();
    }
    else {
        this->trackList->Clear();
    }
}

void NowPlayingLayout::OnTrackListRequeried() {
    if (playback.Count()) {
        if (this->reselectIndex == -1) {
            size_t index = playback.GetIndex();

            if (index == (size_t)-1) { /* not playing? */
                this->trackList->SetSelectedIndex(0);
                this->trackList->ScrollTo(0);
            }
            else { /* playing... */
                this->trackList->SetSelectedIndex(index);
                this->trackList->ScrollTo(index == 0 ? index : index - 1);
            }
        }
        else { /* requeried due to edit, so reselect... */
            this->trackList->SetSelectedIndex((int) this->reselectIndex);
            auto pos = this->trackList->GetScrollPosition();
            int first = (int) pos.firstVisibleEntryIndex;
            int last = (int) first + pos.visibleEntryCount;
            int index = (int) this->reselectIndex;
            if (index < first || index >= last) {
                this->trackList->ScrollTo(this->reselectIndex);
            }
        }

        /* if after a bunch of monkeying around there's still nothing
        selected, but we have contents, let's just select the first item */
        if (this->trackList->GetSelectedIndex() == (size_t)-1) {
            this->trackList->SetSelectedIndex(0);
            this->trackList->ScrollTo(0);
        }
    }

    this->reselectIndex = -1;
}

void NowPlayingLayout::OnPlaybackShuffled(bool shuffled) {
    this->RequeryTrackList();
}

void NowPlayingLayout::RequeryTrackList() {
    this->trackList->Requery(std::shared_ptr<TrackListQueryBase>(
        new NowPlayingTrackListQuery(this->library, this->playback)));
}

bool NowPlayingLayout::KeyPress(const std::string& key) {
    if (key == "KEY_ENTER") {
        size_t index = this->trackList->GetSelectedIndex();
        if (index != (size_t)-1) {
            this->playback.Play(index);
            return true;
        }
    }
    else if (ProcessEditOperation(key)) {
        return true;
    }

    return LayoutBase::KeyPress(key);
}

bool NowPlayingLayout::ProcessEditOperation(const std::string& key) {
    if (EDIT_KEYS.find(key) != EDIT_KEYS.end()) {
        if (!playback.IsShuffled()) {
            PlaybackService::Editor editor = this->playback.Edit();
            size_t selected = this->trackList->GetSelectedIndex();

            if (Hotkeys::Is(Hotkeys::PlayQueueMoveUp, key)) {
                if (selected > 0) {
                    size_t to = selected - 1;
                    editor.Move(selected, to);
                    this->reselectIndex = (int)to;
                }
{'fraction_non_alphanumeric': 0.14099783080260303, 'fraction_numerical': 0.0040170322165983775, 'mean_word_length': 2.774408732565191, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 133, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6950794', 'n_tokens_mistral': 3540, 'n_tokens_neox': 3348, 'n_words': 924}