Merge pull request #379 from clangen/clangen/accents-and-regexps

Add support for regular expressions and smarter accent-related searching.
This commit is contained in:
casey langen 2020-11-08 19:01:13 -08:00 committed by GitHub
commit 6e06fe7f59
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 1281 additions and 116 deletions

View File

@ -7,7 +7,7 @@ cmake_minimum_required(VERSION 3.0)
project(musikcube)
set (musikcube_VERSION_MAJOR 0)
set (musikcube_VERSION_MINOR 94)
set (musikcube_VERSION_MINOR 95)
set (musikcube_VERSION_PATCH 0)
set (musikcube_VERSION "${musikcube_VERSION_MAJOR}.${musikcube_VERSION_MINOR}.${musikcube_VERSION_PATCH}")

View File

@ -1,6 +1,6 @@
%define name musikcube
%define build_timestamp %{lua: print(os.date("%Y%m%d"))}
%define version 0.94.0
%define version 0.95.0
Name: %{name}
Version: %{version}
Release: %{dist}

View File

@ -1,5 +1,5 @@
name: musikcube # you probably want to 'snapcraft register <name>'
version: 0.94.0
version: 0.95.0
summary: a terminal-based music player, metadata indexer, and server
description: |
musikcube is a fully functional terminal-based music player, library, and

View File

@ -15,6 +15,7 @@ set(CORE_SOURCES
./audio/Visualizer.cpp
./db/Connection.cpp
./db/ScopedTransaction.cpp
./db/SqliteExtensions.cpp
./db/Statement.cpp
./i18n/Locale.cpp
./io/DataStreamFactory.cpp

View File

@ -35,6 +35,7 @@
#include "pch.hpp"
#include <musikcore/db/Connection.h>
#include <musikcore/db/SqliteExtensions.h>
#include <sqlite/sqlite3.h>
static std::mutex globalMutex;
@ -116,6 +117,8 @@ int Connection::LastModifiedRowCount() {
}
void Connection::Initialize(unsigned int cache) {
SqliteExtensions::Register(this->connection);
sqlite3_enable_shared_cache(1);
sqlite3_busy_timeout(this->connection, 10000);
@ -133,7 +136,7 @@ void Connection::Initialize(unsigned int cache) {
sqlite3_exec(this->connection,cacheSize.c_str(), nullptr, nullptr, nullptr); // size * 1.5kb = 6Mb cache
}
sqlite3_exec(this->connection, "PRAGMA case_sensitive_like=0", nullptr, nullptr, nullptr); // More speed if case insensitive
//sqlite3_exec(this->connection, "PRAGMA case_sensitive_like=0", nullptr, nullptr, nullptr); // More speed if case insensitive
sqlite3_exec(this->connection, "PRAGMA count_changes=0", nullptr, nullptr, nullptr); // If set it counts changes on SQL UPDATE. More speed when not.
sqlite3_exec(this->connection, "PRAGMA legacy_file_format=OFF", nullptr, nullptr, nullptr); // No reason to be backwards compatible :)
sqlite3_exec(this->connection, "PRAGMA temp_store=MEMORY", nullptr, nullptr, nullptr); // MEMORY, not file. More speed.

View File

@ -0,0 +1,916 @@
//////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2004-2020 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 "pch.hpp"
#include <sqlite/sqlite3.h>
#include <musikcore/db/SqliteExtensions.h>
#include <unordered_map>
#include <regex>
/* NOTE: nearly all of the source here was pulled in from sqlite3.c except for the
extension registration and accentToChar mapping; this is basically a copy/paste of
the default LIKE operator that supports coercing accented characters to standard
english characters to make it easier to search. */
#ifndef UINT32_TYPE
# ifdef HAVE_UINT32_T
# define UINT32_TYPE uint32_t
# else
# define UINT32_TYPE unsigned int
# endif
#endif
#ifndef UINT8_TYPE
# ifdef HAVE_UINT8_T
# define UINT8_TYPE uint8_t
# else
# define UINT8_TYPE unsigned char
# endif
#endif
typedef UINT8_TYPE u8; /* 1-byte unsigned integer */
typedef UINT32_TYPE u32; /* 4-byte unsigned integer */
/*
** Mapping from accented UTF8 sequence to a regular english char
*/
static std::unordered_map<u32, u32> accentToChar = {
{ (u32)192 /* À */ , (u32)'A' },
{ (u32)193 /* Á */ , (u32)'A' },
{ (u32)194 /* Â */ , (u32)'A' },
{ (u32)195 /* Ã */ , (u32)'A' },
{ (u32)196 /* Ä */ , (u32)'A' },
{ (u32)197 /* Å */ , (u32)'A' },
{ (u32)199 /* Ç */ , (u32)'C' },
{ (u32)200 /* È */ , (u32)'E' },
{ (u32)201 /* É */ , (u32)'E' },
{ (u32)202 /* Ê */ , (u32)'E' },
{ (u32)203 /* Ë */ , (u32)'E' },
{ (u32)204 /* Ì */ , (u32)'I' },
{ (u32)205 /* Í */ , (u32)'I' },
{ (u32)206 /* Î */ , (u32)'I' },
{ (u32)207 /* Ï */ , (u32)'I' },
{ (u32)209 /* Ñ */ , (u32)'N' },
{ (u32)210 /* Ò */ , (u32)'O' },
{ (u32)211 /* Ó */ , (u32)'O' },
{ (u32)212 /* Ô */ , (u32)'O' },
{ (u32)213 /* Õ */ , (u32)'O' },
{ (u32)214 /* Ö */ , (u32)'O' },
{ (u32)217 /* Ù */ , (u32)'U' },
{ (u32)218 /* Ú */ , (u32)'U' },
{ (u32)219 /* Û */ , (u32)'U' },
{ (u32)220 /* Ü */ , (u32)'U' },
{ (u32)221 /* Ý */ , (u32)'Y' },
{ (u32)223 /* ß */ , (u32)'s' },
{ (u32)224 /* à */ , (u32)'a' },
{ (u32)225 /* á */ , (u32)'a' },
{ (u32)226 /* â */ , (u32)'a' },
{ (u32)227 /* ã */ , (u32)'a' },
{ (u32)228 /* ä */ , (u32)'a' },
{ (u32)229 /* å */ , (u32)'a' },
{ (u32)231 /* ç */ , (u32)'c' },
{ (u32)232 /* è */ , (u32)'e' },
{ (u32)233 /* é */ , (u32)'e' },
{ (u32)234 /* ê */ , (u32)'e' },
{ (u32)235 /* ë */ , (u32)'e' },
{ (u32)236 /* ì */ , (u32)'i' },
{ (u32)237 /* í */ , (u32)'i' },
{ (u32)238 /* î */ , (u32)'i' },
{ (u32)239 /* ï */ , (u32)'i' },
{ (u32)241 /* ñ */ , (u32)'n' },
{ (u32)242 /* ò */ , (u32)'o' },
{ (u32)243 /* ó */ , (u32)'o' },
{ (u32)244 /* ô */ , (u32)'o' },
{ (u32)245 /* õ */ , (u32)'o' },
{ (u32)246 /* ö */ , (u32)'o' },
{ (u32)249 /* ù */ , (u32)'u' },
{ (u32)250 /* ú */ , (u32)'u' },
{ (u32)251 /* û */ , (u32)'u' },
{ (u32)252 /* ü */ , (u32)'u' },
{ (u32)253 /* ý */ , (u32)'y' },
{ (u32)255 /* ÿ */ , (u32)'y' },
{ (u32)256 /* Ā */ , (u32)'A' },
{ (u32)257 /* ā */ , (u32)'a' },
{ (u32)258 /* Ă */ , (u32)'A' },
{ (u32)259 /* ă */ , (u32)'a' },
{ (u32)260 /* Ą */ , (u32)'A' },
{ (u32)261 /* ą */ , (u32)'a' },
{ (u32)262 /* Ć */ , (u32)'C' },
{ (u32)263 /* ć */ , (u32)'c' },
{ (u32)264 /* Ĉ */ , (u32)'C' },
{ (u32)265 /* ĉ */ , (u32)'c' },
{ (u32)266 /* Ċ */ , (u32)'C' },
{ (u32)267 /* ċ */ , (u32)'c' },
{ (u32)268 /* Č */ , (u32)'C' },
{ (u32)269 /* č */ , (u32)'c' },
{ (u32)270 /* Ď */ , (u32)'D' },
{ (u32)271 /* ď */ , (u32)'d' },
{ (u32)272 /* Đ */ , (u32)'D' },
{ (u32)273 /* đ */ , (u32)'d' },
{ (u32)274 /* Ē */ , (u32)'E' },
{ (u32)275 /* ē */ , (u32)'e' },
{ (u32)276 /* Ĕ */ , (u32)'E' },
{ (u32)277 /* ĕ */ , (u32)'e' },
{ (u32)278 /* Ė */ , (u32)'E' },
{ (u32)279 /* ė */ , (u32)'e' },
{ (u32)280 /* Ę */ , (u32)'E' },
{ (u32)281 /* ę */ , (u32)'e' },
{ (u32)282 /* Ě */ , (u32)'E' },
{ (u32)283 /* ě */ , (u32)'e' },
{ (u32)284 /* Ĝ */ , (u32)'G' },
{ (u32)285 /* ĝ */ , (u32)'g' },
{ (u32)286 /* Ğ */ , (u32)'G' },
{ (u32)287 /* ğ */ , (u32)'g' },
{ (u32)288 /* Ġ */ , (u32)'G' },
{ (u32)289 /* ġ */ , (u32)'g' },
{ (u32)290 /* Ģ */ , (u32)'G' },
{ (u32)291 /* ģ */ , (u32)'g' },
{ (u32)292 /* Ĥ */ , (u32)'H' },
{ (u32)293 /* ĥ */ , (u32)'h' },
{ (u32)294 /* Ħ */ , (u32)'H' },
{ (u32)295 /* ħ */ , (u32)'h' },
{ (u32)296 /* Ĩ */ , (u32)'I' },
{ (u32)297 /* ĩ */ , (u32)'i' },
{ (u32)298 /* Ī */ , (u32)'I' },
{ (u32)299 /* ī */ , (u32)'i' },
{ (u32)300 /* Ĭ */ , (u32)'I' },
{ (u32)301 /* ĭ */ , (u32)'i' },
{ (u32)302 /* Į */ , (u32)'I' },
{ (u32)303 /* į */ , (u32)'i' },
{ (u32)304 /* İ */ , (u32)'I' },
{ (u32)305 /* ı */ , (u32)'i' },
{ (u32)308 /* Ĵ */ , (u32)'J' },
{ (u32)309 /* ĵ */ , (u32)'j' },
{ (u32)310 /* Ķ */ , (u32)'K' },
{ (u32)311 /* ķ */ , (u32)'k' },
{ (u32)312 /* ĸ */ , (u32)'k' },
{ (u32)313 /* Ĺ */ , (u32)'L' },
{ (u32)314 /* ĺ */ , (u32)'l' },
{ (u32)315 /* Ļ */ , (u32)'L' },
{ (u32)316 /* ļ */ , (u32)'l' },
{ (u32)317 /* Ľ */ , (u32)'L' },
{ (u32)318 /* ľ */ , (u32)'l' },
{ (u32)319 /* Ŀ */ , (u32)'L' },
{ (u32)320 /* ŀ */ , (u32)'l' },
{ (u32)321 /* Ł */ , (u32)'L' },
{ (u32)322 /* ł */ , (u32)'l' },
{ (u32)323 /* Ń */ , (u32)'N' },
{ (u32)324 /* ń */ , (u32)'n' },
{ (u32)325 /* Ņ */ , (u32)'N' },
{ (u32)326 /* ņ */ , (u32)'n' },
{ (u32)327 /* Ň */ , (u32)'N' },
{ (u32)328 /* ň */ , (u32)'n' },
{ (u32)329 /* ʼn */ , (u32)'N' },
{ (u32)330 /* Ŋ */ , (u32)'n' },
{ (u32)331 /* ŋ */ , (u32)'N' },
{ (u32)332 /* Ō */ , (u32)'O' },
{ (u32)333 /* ō */ , (u32)'o' },
{ (u32)334 /* Ŏ */ , (u32)'O' },
{ (u32)335 /* ŏ */ , (u32)'o' },
{ (u32)336 /* Ő */ , (u32)'O' },
{ (u32)337 /* ő */ , (u32)'o' },
{ (u32)340 /* Ŕ */ , (u32)'R' },
{ (u32)341 /* ŕ */ , (u32)'r' },
{ (u32)342 /* Ŗ */ , (u32)'R' },
{ (u32)343 /* ŗ */ , (u32)'r' },
{ (u32)344 /* Ř */ , (u32)'R' },
{ (u32)345 /* ř */ , (u32)'r' },
{ (u32)346 /* Ś */ , (u32)'S' },
{ (u32)347 /* ś */ , (u32)'s' },
{ (u32)348 /* Ŝ */ , (u32)'S' },
{ (u32)349 /* ŝ */ , (u32)'s' },
{ (u32)350 /* Ş */ , (u32)'S' },
{ (u32)351 /* ş */ , (u32)'s' },
{ (u32)352 /* Š */ , (u32)'S' },
{ (u32)353 /* š */ , (u32)'s' },
{ (u32)354 /* Ţ */ , (u32)'T' },
{ (u32)355 /* ţ */ , (u32)'t' },
{ (u32)356 /* Ť */ , (u32)'T' },
{ (u32)357 /* ť */ , (u32)'t' },
{ (u32)358 /* Ŧ */ , (u32)'T' },
{ (u32)359 /* ŧ */ , (u32)'t' },
{ (u32)360 /* Ũ */ , (u32)'U' },
{ (u32)361 /* ũ */ , (u32)'u' },
{ (u32)362 /* Ū */ , (u32)'U' },
{ (u32)363 /* ū */ , (u32)'u' },
{ (u32)364 /* Ŭ */ , (u32)'U' },
{ (u32)365 /* ŭ */ , (u32)'u' },
{ (u32)366 /* Ů */ , (u32)'U' },
{ (u32)367 /* ů */ , (u32)'u' },
{ (u32)368 /* Ű */ , (u32)'U' },
{ (u32)369 /* ű */ , (u32)'u' },
{ (u32)370 /* Ų */ , (u32)'U' },
{ (u32)371 /* ų */ , (u32)'u' },
{ (u32)372 /* Ŵ */ , (u32)'W' },
{ (u32)373 /* ŵ */ , (u32)'w' },
{ (u32)374 /* Ŷ */ , (u32)'Y' },
{ (u32)375 /* ŷ */ , (u32)'y' },
{ (u32)376 /* Ÿ */ , (u32)'Y' },
{ (u32)377 /* Ź */ , (u32)'Z' },
{ (u32)377 /* Ź */ , (u32)'Z' },
{ (u32)377 /* Ź */ , (u32)'Z' },
{ (u32)377 /* Ź */ , (u32)'Z' },
{ (u32)377 /* Ź */ , (u32)'Z' },
{ (u32)377 /* Ź */ , (u32)'Z' },
{ (u32)377 /* Ź */ , (u32)'Z' },
{ (u32)377 /* Ź */ , (u32)'Z' },
{ (u32)377 /* Ź */ , (u32)'Z' },
{ (u32)377 /* Ź */ , (u32)'Z' },
{ (u32)377 /* Ź */ , (u32)'Z' },
{ (u32)378 /* ź */ , (u32)'z' },
{ (u32)378 /* ź */ , (u32)'z' },
{ (u32)378 /* ź */ , (u32)'z' },
{ (u32)378 /* ź */ , (u32)'z' },
{ (u32)378 /* ź */ , (u32)'z' },
{ (u32)378 /* ź */ , (u32)'z' },
{ (u32)378 /* ź */ , (u32)'z' },
{ (u32)378 /* ź */ , (u32)'z' },
{ (u32)378 /* ź */ , (u32)'z' },
{ (u32)378 /* ź */ , (u32)'z' },
{ (u32)378 /* ź */ , (u32)'z' },
{ (u32)379 /* Ż */ , (u32)'Z' },
{ (u32)379 /* Ż */ , (u32)'Z' },
{ (u32)379 /* Ż */ , (u32)'Z' },
{ (u32)379 /* Ż */ , (u32)'Z' },
{ (u32)379 /* Ż */ , (u32)'Z' },
{ (u32)379 /* Ż */ , (u32)'Z' },
{ (u32)379 /* Ż */ , (u32)'Z' },
{ (u32)379 /* Ż */ , (u32)'Z' },
{ (u32)379 /* Ż */ , (u32)'Z' },
{ (u32)379 /* Ż */ , (u32)'Z' },
{ (u32)379 /* Ż */ , (u32)'Z' },
{ (u32)380 /* ż */ , (u32)'z' },
{ (u32)380 /* ż */ , (u32)'z' },
{ (u32)380 /* ż */ , (u32)'z' },
{ (u32)380 /* ż */ , (u32)'z' },
{ (u32)380 /* ż */ , (u32)'z' },
{ (u32)380 /* ż */ , (u32)'z' },
{ (u32)380 /* ż */ , (u32)'z' },
{ (u32)380 /* ż */ , (u32)'z' },
{ (u32)380 /* ż */ , (u32)'z' },
{ (u32)380 /* ż */ , (u32)'z' },
{ (u32)380 /* ż */ , (u32)'z' },
{ (u32)381 /* Ž */ , (u32)'Z' },
{ (u32)381 /* Ž */ , (u32)'Z' },
{ (u32)381 /* Ž */ , (u32)'Z' },
{ (u32)381 /* Ž */ , (u32)'Z' },
{ (u32)381 /* Ž */ , (u32)'Z' },
{ (u32)381 /* Ž */ , (u32)'Z' },
{ (u32)381 /* Ž */ , (u32)'Z' },
{ (u32)381 /* Ž */ , (u32)'Z' },
{ (u32)381 /* Ž */ , (u32)'Z' },
{ (u32)381 /* Ž */ , (u32)'Z' },
{ (u32)381 /* Ž */ , (u32)'Z' },
{ (u32)382 /* ž */ , (u32)'z' },
{ (u32)382 /* ž */ , (u32)'z' },
{ (u32)382 /* ž */ , (u32)'z' },
{ (u32)382 /* ž */ , (u32)'z' },
{ (u32)382 /* ž */ , (u32)'z' },
{ (u32)382 /* ž */ , (u32)'z' },
{ (u32)382 /* ž */ , (u32)'z' },
{ (u32)382 /* ž */ , (u32)'z' },
{ (u32)382 /* ž */ , (u32)'z' },
{ (u32)382 /* ž */ , (u32)'z' },
{ (u32)382 /* ž */ , (u32)'z' },
{ (u32)383 /* ſ */ , (u32)'s' },
{ (u32)383 /* ſ */ , (u32)'s' },
{ (u32)383 /* ſ */ , (u32)'s' },
{ (u32)383 /* ſ */ , (u32)'s' },
{ (u32)383 /* ſ */ , (u32)'s' },
{ (u32)383 /* ſ */ , (u32)'s' },
{ (u32)383 /* ſ */ , (u32)'s' },
{ (u32)383 /* ſ */ , (u32)'s' },
{ (u32)383 /* ſ */ , (u32)'s' },
{ (u32)383 /* ſ */ , (u32)'s' },
{ (u32)383 /* ſ */ , (u32)'s' },
};
/*
** Check to see if this machine uses EBCDIC. (Yes, believe it or
** not, there are still machines out there that use EBCDIC.)
*/
#if 'A' == '\301'
# define SQLITE_EBCDIC 1
#else
# define SQLITE_ASCII 1
#endif
/* An array to map all upper-case characters into their corresponding
** lower-case character.
**
** SQLite only considers US-ASCII (or EBCDIC) characters. We do not
** handle case conversions for the UTF character set since the tables
** involved are nearly as big or bigger than SQLite itself.
*/
const unsigned char sqlite3UpperToLower[] = {
#ifdef SQLITE_ASCII
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 97, 98, 99,100,101,102,103,
104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,
122, 91, 92, 93, 94, 95, 96, 97, 98, 99,100,101,102,103,104,105,106,107,
108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,
126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,
144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,
162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,
180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,
198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,
216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,
234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,
252,253,254,255
#endif
#ifdef SQLITE_EBCDIC
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* 0x */
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, /* 1x */
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, /* 2x */
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, /* 3x */
64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, /* 4x */
80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, /* 5x */
96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111, /* 6x */
112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, /* 7x */
128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, /* 8x */
144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, /* 9x */
160,161,162,163,164,165,166,167,168,169,170,171,140,141,142,175, /* Ax */
176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, /* Bx */
192,129,130,131,132,133,134,135,136,137,202,203,204,205,206,207, /* Cx */
208,145,146,147,148,149,150,151,152,153,218,219,220,221,222,223, /* Dx */
224,225,162,163,164,165,166,167,168,169,234,235,236,237,238,239, /* Ex */
240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255, /* Fx */
#endif
};
/*
** Maximum length (in bytes) of the pattern in a LIKE or GLOB
** operator.
*/
#ifndef SQLITE_MAX_LIKE_PATTERN_LENGTH
# define SQLITE_MAX_LIKE_PATTERN_LENGTH 50000
#endif
/*
** Possible error returns from patternMatch()
*/
#define SQLITE_MATCH 0
#define SQLITE_NOMATCH 1
#define SQLITE_NOWILDCARDMATCH 2
/*
** A structure defining how to do GLOB-style comparisons.
*/
struct compareInfo {
u8 matchAll; /* "*" or "%" */
u8 matchOne; /* "?" or "_" */
u8 matchSet; /* "[" or 0 */
u8 noCase; /* true to ignore case differences */
};
static const struct compareInfo likeInfoNorm = { '%', '_', 0, 1 };
/*
** Assuming zIn points to the first byte of a UTF-8 character,
** advance zIn to point to the first byte of the next UTF-8 character.
*/
#define SQLITE_SKIP_UTF8(zIn) { \
if( (*(zIn++))>=0xc0 ){ \
while( (*zIn & 0xc0)==0x80 ){ zIn++; } \
} \
}
/*
** The following macros mimic the standard library functions toupper(),
** isspace(), isalnum(), isdigit() and isxdigit(), respectively. The
** sqlite versions only work for ASCII characters, regardless of locale.
*/
#ifdef SQLITE_ASCII
# define sqlite3Toupper(x) ((x)&~(sqlite3CtypeMap[(unsigned char)(x)]&0x20))
# define sqlite3Isspace(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x01)
# define sqlite3Isalnum(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x06)
# define sqlite3Isalpha(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x02)
# define sqlite3Isdigit(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x04)
# define sqlite3Isxdigit(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x08)
# define sqlite3Tolower(x) (sqlite3UpperToLower[(unsigned char)(x)])
# define sqlite3Isquote(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x80)
#else
# define sqlite3Toupper(x) toupper((unsigned char)(x))
# define sqlite3Isspace(x) isspace((unsigned char)(x))
# define sqlite3Isalnum(x) isalnum((unsigned char)(x))
# define sqlite3Isalpha(x) isalpha((unsigned char)(x))
# define sqlite3Isdigit(x) isdigit((unsigned char)(x))
# define sqlite3Isxdigit(x) isxdigit((unsigned char)(x))
# define sqlite3Tolower(x) tolower((unsigned char)(x))
# define sqlite3Isquote(x) ((x)=='"'||(x)=='\''||(x)=='['||(x)=='`')
#endif
/*
** For LIKE and GLOB matching on EBCDIC machines, assume that every
** character is exactly one byte in size. Also, provde the Utf8Read()
** macro for fast reading of the next character in the common case where
** the next character is ASCII.
*/
#if defined(SQLITE_EBCDIC)
# define sqlite3Utf8Read(A) (*((*A)++))
# define Utf8Read(A) (*(A++))
#else
# define Utf8Read(A) (A[0]<0x80?*(A++):sqlite3Utf8Read(&A))
#endif
/*
** This lookup table is used to help decode the first byte of
** a multi-byte UTF8 character.
*/
static const unsigned char sqlite3Utf8Trans1[] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00,
};
/*
** The following 256 byte lookup table is used to support SQLites built-in
** equivalents to the following standard library functions:
**
** isspace() 0x01
** isalpha() 0x02
** isdigit() 0x04
** isalnum() 0x06
** isxdigit() 0x08
** toupper() 0x20
** SQLite identifier character 0x40
** Quote character 0x80
**
** Bit 0x20 is set if the mapped character requires translation to upper
** case. i.e. if the character is a lower-case ASCII character.
** If x is a lower-case ASCII character, then its upper-case equivalent
** is (x - 0x20). Therefore toupper() can be implemented as:
**
** (x & ~(map[x]&0x20))
**
** The equivalent of tolower() is implemented using the sqlite3UpperToLower[]
** array. tolower() is used more often than toupper() by SQLite.
**
** Bit 0x40 is set if the character is non-alphanumeric and can be used in an
** SQLite identifier. Identifiers are alphanumerics, "_", "$", and any
** non-ASCII UTF character. Hence the test for whether or not a character is
** part of an identifier is 0x46.
*/
const unsigned char sqlite3CtypeMap[256] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 00..07 ........ */
0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, /* 08..0f ........ */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 10..17 ........ */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 18..1f ........ */
0x01, 0x00, 0x80, 0x00, 0x40, 0x00, 0x00, 0x80, /* 20..27 !"#$%&' */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 28..2f ()*+,-./ */
0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, /* 30..37 01234567 */
0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 38..3f 89:;<=>? */
0x00, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x02, /* 40..47 @ABCDEFG */
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 48..4f HIJKLMNO */
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 50..57 PQRSTUVW */
0x02, 0x02, 0x02, 0x80, 0x00, 0x00, 0x00, 0x40, /* 58..5f XYZ[\]^_ */
0x80, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x22, /* 60..67 `abcdefg */
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, /* 68..6f hijklmno */
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, /* 70..77 pqrstuvw */
0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, /* 78..7f xyz{|}~. */
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 80..87 ........ */
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 88..8f ........ */
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 90..97 ........ */
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 98..9f ........ */
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* a0..a7 ........ */
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* a8..af ........ */
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* b0..b7 ........ */
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* b8..bf ........ */
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* c0..c7 ........ */
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* c8..cf ........ */
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* d0..d7 ........ */
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* d8..df ........ */
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* e0..e7 ........ */
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* e8..ef ........ */
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* f0..f7 ........ */
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 /* f8..ff ........ */
};
/*
** pZ is a UTF-8 encoded unicode string. If nByte is less than zero,
** return the number of unicode characters in pZ up to (but not including)
** the first 0x00 byte. If nByte is not less than zero, return the
** number of unicode characters in the first nByte of pZ (or up to
** the first 0x00, whichever comes first).
*/
int sqlite3Utf8CharLen(const char* zIn, int nByte) {
int r = 0;
const u8* z = (const u8*)zIn;
const u8* zTerm;
if (nByte >= 0) {
zTerm = &z[nByte];
}
else {
zTerm = (const u8*)(-1);
}
assert(z <= zTerm);
while (*z != 0 && z < zTerm) {
SQLITE_SKIP_UTF8(z);
r++;
}
return r;
}
u32 sqlite3Utf8Read(
const unsigned char** pz /* Pointer to string from which to read char */
) {
unsigned int c;
/* Same as READ_UTF8() above but without the zTerm parameter.
** For this routine, we assume the UTF8 string is always zero-terminated.
*/
c = *((*pz)++);
if (c >= 0xc0) {
c = sqlite3Utf8Trans1[c - 0xc0];
while ((*(*pz) & 0xc0) == 0x80) {
c = (c << 6) + (0x3f & *((*pz)++));
}
if (c < 0x80
|| (c & 0xFFFFF800) == 0xD800
|| (c & 0xFFFFFFFE) == 0xFFFE) {
c = 0xFFFD;
}
}
auto it = accentToChar.find(c);
return it != accentToChar.end() ? it->second : c;
}
/*
** Compare two UTF-8 strings for equality where the first string is
** a GLOB or LIKE expression. Return values:
**
** SQLITE_MATCH: Match
** SQLITE_NOMATCH: No match
** SQLITE_NOWILDCARDMATCH: No match in spite of having * or % wildcards.
**
** Globbing rules:
**
** '*' Matches any sequence of zero or more characters.
**
** '?' Matches exactly one character.
**
** [...] Matches one character from the enclosed list of
** characters.
**
** [^...] Matches one character not in the enclosed list.
**
** With the [...] and [^...] matching, a ']' character can be included
** in the list by making it the first character after '[' or '^'. A
** range of characters can be specified using '-'. Example:
** "[a-z]" matches any single lower-case letter. To match a '-', make
** it the last character in the list.
**
** Like matching rules:
**
** '%' Matches any sequence of zero or more characters
**
*** '_' Matches any one character
**
** Ec Where E is the "esc" character and c is any other
** character, including '%', '_', and esc, match exactly c.
**
** The comments within this routine usually assume glob matching.
**
** This routine is usually quick, but can be N**2 in the worst case.
*/
static int patternCompare(
const u8* zPattern, /* The glob pattern */
const u8* zString, /* The string to compare against the glob */
const struct compareInfo* pInfo, /* Information about how to do the compare */
u32 matchOther /* The escape char (LIKE) or '[' (GLOB) */
) {
u32 c, c2; /* Next pattern and input string chars */
u32 matchOne = pInfo->matchOne; /* "?" or "_" */
u32 matchAll = pInfo->matchAll; /* "*" or "%" */
u8 noCase = pInfo->noCase; /* True if uppercase==lowercase */
const u8* zEscaped = 0; /* One past the last escaped input char */
while ((c = Utf8Read(zPattern)) != 0) {
if (c == matchAll) { /* Match "*" */
/* Skip over multiple "*" characters in the pattern. If there
** are also "?" characters, skip those as well, but consume a
** single character of the input string for each "?" skipped */
while ((c = Utf8Read(zPattern)) == matchAll || c == matchOne) {
if (c == matchOne && sqlite3Utf8Read(&zString) == 0) {
return SQLITE_NOWILDCARDMATCH;
}
}
if (c == 0) {
return SQLITE_MATCH; /* "*" at the end of the pattern matches */
}
else if (c == matchOther) {
if (pInfo->matchSet == 0) {
c = sqlite3Utf8Read(&zPattern);
if (c == 0) return SQLITE_NOWILDCARDMATCH;
}
else {
/* "[...]" immediately follows the "*". We have to do a slow
** recursive search in this case, but it is an unusual case. */
assert(matchOther < 0x80); /* '[' is a single-byte character */
while (*zString) {
int bMatch = patternCompare(&zPattern[-1], zString, pInfo, matchOther);
if (bMatch != SQLITE_NOMATCH) return bMatch;
SQLITE_SKIP_UTF8(zString);
}
return SQLITE_NOWILDCARDMATCH;
}
}
/* At this point variable c contains the first character of the
** pattern string past the "*". Search in the input string for the
** first matching character and recursively continue the match from
** that point.
**
** For a case-insensitive search, set variable cx to be the same as
** c but in the other case and search the input string for either
** c or cx.
*/
if (c <= 0x80) {
char zStop[3];
int bMatch;
if (noCase) {
zStop[0] = sqlite3Toupper(c);
zStop[1] = sqlite3Tolower(c);
zStop[2] = 0;
}
else {
zStop[0] = c;
zStop[1] = 0;
}
while (1) {
zString += strcspn((const char*)zString, zStop);
if (zString[0] == 0) break;
zString++;
bMatch = patternCompare(zPattern, zString, pInfo, matchOther);
if (bMatch != SQLITE_NOMATCH) return bMatch;
}
}
else {
int bMatch;
while ((c2 = Utf8Read(zString)) != 0) {
if (c2 != c) continue;
bMatch = patternCompare(zPattern, zString, pInfo, matchOther);
if (bMatch != SQLITE_NOMATCH) return bMatch;
}
}
return SQLITE_NOWILDCARDMATCH;
}
if (c == matchOther) {
if (pInfo->matchSet == 0) {
c = sqlite3Utf8Read(&zPattern);
if (c == 0) return SQLITE_NOMATCH;
zEscaped = zPattern;
}
else {
u32 prior_c = 0;
int seen = 0;
int invert = 0;
c = sqlite3Utf8Read(&zString);
if (c == 0) return SQLITE_NOMATCH;
c2 = sqlite3Utf8Read(&zPattern);
if (c2 == '^') {
invert = 1;
c2 = sqlite3Utf8Read(&zPattern);
}
if (c2 == ']') {
if (c == ']') seen = 1;
c2 = sqlite3Utf8Read(&zPattern);
}
while (c2 && c2 != ']') {
if (c2 == '-' && zPattern[0] != ']' && zPattern[0] != 0 && prior_c > 0) {
c2 = sqlite3Utf8Read(&zPattern);
if (c >= prior_c && c <= c2) seen = 1;
prior_c = 0;
}
else {
if (c == c2) {
seen = 1;
}
prior_c = c2;
}
c2 = sqlite3Utf8Read(&zPattern);
}
if (c2 == 0 || (seen ^ invert) == 0) {
return SQLITE_NOMATCH;
}
continue;
}
}
c2 = Utf8Read(zString);
if (c == c2) continue;
if (noCase && sqlite3Tolower(c) == sqlite3Tolower(c2) && c < 0x80 && c2 < 0x80) {
continue;
}
if (c == matchOne && zPattern != zEscaped && c2 != 0) continue;
return SQLITE_NOMATCH;
}
return *zString == 0 ? SQLITE_MATCH : SQLITE_NOMATCH;
}
/*
** Implementation of the like() SQL function. This function implements
** the build-in LIKE operator. The first argument to the function is the
** pattern and the second argument is the string. So, the SQL statements:
**
** A LIKE B
**
** is implemented as like(B,A).
**
** This same function (with a different compareInfo structure) computes
** the GLOB operator.
*/
static void likeFunc(
sqlite3_context* context,
int argc,
sqlite3_value** argv
) {
const unsigned char* zA, * zB;
u32 escape;
sqlite3* db = sqlite3_context_db_handle(context);
struct compareInfo* pInfo = (compareInfo*) sqlite3_user_data(context);
struct compareInfo backupInfo;
#ifdef SQLITE_LIKE_DOESNT_MATCH_BLOBS
if (sqlite3_value_type(argv[0]) == SQLITE_BLOB
|| sqlite3_value_type(argv[1]) == SQLITE_BLOB
) {
sqlite3_result_int(context, 0);
return;
}
#endif
/* Limit the length of the LIKE or GLOB pattern to avoid problems
** of deep recursion and N*N behavior in patternCompare().
*/
if (sqlite3_value_bytes(argv[0]) > SQLITE_MAX_LIKE_PATTERN_LENGTH) {
sqlite3_result_error(context, "LIKE or GLOB pattern too complex", -1);
return;
}
if (argc == 3) {
/* The escape character string must consist of a single UTF-8 character.
** Otherwise, return an error.
*/
const unsigned char* zEsc = sqlite3_value_text(argv[2]);
if (zEsc == 0) return;
if (sqlite3Utf8CharLen((char*)zEsc, -1) != 1) {
sqlite3_result_error(context,
"ESCAPE expression must be a single character", -1);
return;
}
escape = sqlite3Utf8Read(&zEsc);
if (escape == pInfo->matchAll || escape == pInfo->matchOne) {
memcpy(&backupInfo, pInfo, sizeof(backupInfo));
pInfo = &backupInfo;
if (escape == pInfo->matchAll) pInfo->matchAll = 0;
if (escape == pInfo->matchOne) pInfo->matchOne = 0;
}
}
else {
escape = pInfo->matchSet;
}
zB = sqlite3_value_text(argv[0]);
zA = sqlite3_value_text(argv[1]);
if (zA && zB) {
sqlite3_result_int(context,
patternCompare(zB, zA, pInfo, escape) == SQLITE_MATCH);
}
}
/*
** Function to delete compiled regexp objects. Registered as
** a destructor function with sqlite3_set_auxdata().
*/
static void regexpDelete(void* p) {
std::regex* regex = (std::regex*) p;
delete regex;
}
/*
** Implementation of SQLite REGEXP operator. This scalar function takes
** two arguments. The first is a regular expression pattern to compile
** the second is a string to match against that pattern. If either
** argument is an SQL NULL, then NULL Is returned. Otherwise, the result
** is 1 if the string matches the pattern, or 0 otherwise.
**
** SQLite maps the regexp() function to the regexp() operator such
** that the following two are equivalent:
**
** zString REGEXP zPattern
** regexp(zPattern, zString)
**
** Uses standard C++ std::regex
*/
static auto kRegexFlags =
std::regex::icase |
std::regex::optimize |
std::regex::collate |
std::regex::ECMAScript;
static auto kMatchFlags = std::regex_constants::match_any;
static void regexpFunc(sqlite3_context* context, int nArg, sqlite3_value** apArg) {
const char* matchAgainst = (const char*) sqlite3_value_text(apArg[1]);
if (!matchAgainst) {
return;
}
std::regex* regex = (std::regex*) sqlite3_get_auxdata(context, 0);
if (!regex) {
const char* pattern = (const char*) sqlite3_value_text(apArg[0]);
if (!pattern) {
return;
}
try {
regex = new std::regex(pattern, kRegexFlags);
}
catch (std::regex_error) {
return;
}
sqlite3_set_auxdata(context, 0, regex, regexpDelete);
}
/* Return 1 or 0. */
sqlite3_result_int(context, std::regex_search(matchAgainst, *regex, kMatchFlags) ? 1 : 0);
}
namespace musik { namespace core { namespace db {
namespace SqliteExtensions {
int Register(sqlite3* db) {
static const struct Scalar {
const char* zName; /* Function name */
unsigned char nArg; /* Number of arguments */
unsigned int enc; /* Optimal text encoding */
void (*xFunc)(sqlite3_context*, int, sqlite3_value**);
} scalars[] = {
{"regexp", 2, SQLITE_ANY | SQLITE_DETERMINISTIC| SQLITE_INNOCUOUS, regexpFunc},
{"like", 2, SQLITE_UTF8 | SQLITE_DETERMINISTIC | SQLITE_INNOCUOUS, likeFunc},
{"like", 3, SQLITE_UTF8 | SQLITE_DETERMINISTIC | SQLITE_INNOCUOUS, likeFunc},
};
int rc = SQLITE_OK;
for (int i = 0; rc == SQLITE_OK && i < (int)(sizeof(scalars) / sizeof(scalars[0])); i++) {
const struct Scalar* p = &scalars[i];
rc = sqlite3_create_function(
db,
p->zName,
p->nArg,
p->enc,
(void*)&likeInfoNorm,
p->xFunc,
0,
0);
}
return rc;
}
}
} } }

View File

@ -0,0 +1,51 @@
//////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2004-2020 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.
//
//////////////////////////////////////////////////////////////////////////////
#pragma once
#include <musikcore/config.h>
#include <map>
struct sqlite3;
namespace musik { namespace core { namespace db {
namespace SqliteExtensions {
int Register(sqlite3* db);
}
} } }

View File

@ -305,6 +305,7 @@ ITrackList* LocalMetadataProxy::QueryTracks(const char* query, int limit, int of
std::shared_ptr<SearchTrackListQuery> search(
new SearchTrackListQuery(
this->library,
SearchTrackListQuery::MatchType::Substring,
std::string(query ? query : ""),
TrackSortType::Album));
@ -456,6 +457,7 @@ IValueList* LocalMetadataProxy::QueryCategoryWithPredicate(
std::shared_ptr<CategoryListQuery> search(
new CategoryListQuery(
CategoryListQuery::MatchType::Substring,
type,
{ field, predicateId },
std::string(filter ? filter : "")));
@ -480,7 +482,10 @@ IValueList* LocalMetadataProxy::QueryCategoryWithPredicates(
auto predicateList = toPredicateList(predicates, predicateCount);
auto query = std::make_shared<CategoryListQuery>(
type, predicateList, std::string(filter ? filter : ""));
CategoryListQuery::MatchType::Substring,
type,
predicateList,
std::string(filter ? filter : ""));
this->library->EnqueueAndWait(query);

View File

@ -50,6 +50,11 @@ namespace musik { namespace core { namespace library { namespace query {
public sigslot::has_slots<>
{
public:
enum class MatchType : int {
Substring = 1,
Regex = 2
};
QueryBase()
: status(IQuery::Idle)
, options(0)

View File

@ -56,35 +56,44 @@ static const std::string kUnfilteredPlaylistsQuery =
static const std::string kFilteredPlaylistsQuery =
"SELECT DISTINCT id, name "
"FROM playlists"
"WHERE LOWER(name) LIKE LOWER(?) "
"WHERE LOWER(name) {{match_type}} ? "
"ORDER BY name;";
const std::string CategoryListQuery::kQueryName = "CategoryListQuery";
static std::string getMatchType(CategoryListQuery::MatchType matchType) {
return matchType == CategoryListQuery::MatchType::Regex ? "REGEXP" : "LIKE";
}
CategoryListQuery::CategoryListQuery() {
}
CategoryListQuery::CategoryListQuery(
const std::string& trackField, const std::string& filter)
: CategoryListQuery(trackField, category::PredicateList(), filter) {
MatchType matchType,
const std::string& trackField,
const std::string& filter)
: CategoryListQuery(matchType, trackField, category::PredicateList(), filter) {
}
CategoryListQuery::CategoryListQuery(
MatchType matchType,
const std::string& trackField,
const category::Predicate predicate,
const std::string& filter)
: CategoryListQuery(trackField, category::PredicateList{ predicate }, filter) {
: CategoryListQuery(matchType, trackField, category::PredicateList{ predicate }, filter) {
}
CategoryListQuery::CategoryListQuery(
MatchType matchType,
const std::string& trackField,
const category::PredicateList predicates,
const std::string& filter)
: trackField(trackField)
: matchType(matchType)
, trackField(trackField)
, filter(filter) {
result.reset(new SdkValueList());
if (this->filter.size()) {
if (this->filter.size() && this->matchType == MatchType::Substring) {
/* transform "FilteR" => "%filter%" */
std::string wild = this->filter;
std::transform(wild.begin(), wild.end(), wild.begin(), tolower);
@ -134,6 +143,8 @@ void CategoryListQuery::QueryPlaylist(musik::core::db::Connection& db) {
? kFilteredPlaylistsQuery
: kUnfilteredPlaylistsQuery;
category::ReplaceAll(query, "{{match_type}}", getMatchType(matchType));
Statement stmt(query.c_str() , db);
if (filtered) {
@ -157,6 +168,7 @@ void CategoryListQuery::QueryRegular(musik::core::db::Connection &db) {
if (this->filter.size()) {
regularFilter = category::REGULAR_FILTER;
category::ReplaceAll(regularFilter, "{{table}}", prop.first);
category::ReplaceAll(regularFilter, "{{match_type}}", getMatchType(matchType));
args.push_back(category::StringArgument(this->filter));
}
@ -184,6 +196,7 @@ void CategoryListQuery::QueryExtended(musik::core::db::Connection &db) {
if (this->filter.size()) {
extendedFilter = category::EXTENDED_FILTER;
args.push_back(category::StringArgument(this->filter));
category::ReplaceAll(extendedFilter, "{{match_type}}", getMatchType(matchType));
}
category::ReplaceAll(query, "{{regular_predicates}}", regular);
@ -242,6 +255,7 @@ std::string CategoryListQuery::SerializeQuery() {
query["options"] = {
{ "trackField", this->trackField },
{ "filter", this->filter },
{ "matchType", this->matchType },
{ "outputType", this->outputType },
{ "regularPredicateList", PredicateListToJson(this->regular) },
{ "extendedPredicateList", PredicateListToJson(this->extended) }
@ -269,6 +283,7 @@ std::shared_ptr<CategoryListQuery> CategoryListQuery::DeserializeQuery(const std
std::shared_ptr<CategoryListQuery> result(new CategoryListQuery());
result->trackField = options.value("trackField", "");
result->filter = options.value("filter", "");
result->matchType = options.value("matchType", MatchType::Substring);
result->outputType = (OutputType)options.value("outputType", OutputType::Regular);
PredicateListFromJson(options["regularPredicateList"], result->regular);
PredicateListFromJson(options["extendedPredicateList"], result->extended);

View File

@ -52,15 +52,18 @@ namespace musik { namespace core { namespace library { namespace query {
using Result = SdkValueList::Shared;
CategoryListQuery(
MatchType matchType,
const std::string& trackField,
const std::string& filter = "");
CategoryListQuery(
MatchType matchType,
const std::string& trackField,
const category::Predicate predicate,
const std::string& filter = "");
CategoryListQuery(
MatchType matchType,
const std::string& trackField,
const category::PredicateList predicate,
const std::string& filter = "");
@ -100,6 +103,7 @@ namespace musik { namespace core { namespace library { namespace query {
std::string trackField;
std::string filter;
MatchType matchType;
OutputType outputType;
category::PredicateList regular, extended;
Result result;

View File

@ -61,9 +61,13 @@ using namespace boost::algorithm;
const std::string SearchTrackListQuery::kQueryName = "SearchTrackListQuery";
SearchTrackListQuery::SearchTrackListQuery(
ILibraryPtr library, const std::string& filter, TrackSortType sort)
ILibraryPtr library,
MatchType matchType,
const std::string& filter,
TrackSortType sort)
{
this->library = library;
this->matchType = matchType;
this->sortType = sort;
this->filter = filter;
@ -105,11 +109,10 @@ bool SearchTrackListQuery::OnRun(Connection& db) {
headers.reset(new std::set<size_t>());
}
bool useRegex = (matchType == MatchType::Regex);
bool hasFilter = (this->filter.size() > 0);
std::string lastAlbum;
size_t index = 0;
std::string query;
if (hasFilter) {
@ -119,9 +122,11 @@ bool SearchTrackListQuery::OnRun(Connection& db) {
"WHERE "
" tracks.visible=1 AND "
+ this->orderByPredicate +
"(tracks.title LIKE ? OR al.name LIKE ? OR ar.name LIKE ? OR gn.name LIKE ?) "
"(tracks.title {{match_type}} ? OR al.name {{match_type}} ? OR ar.name {{match_type}} ? OR gn.name {{match_type}} ?) "
" AND tracks.album_id=al.id AND tracks.visual_genre_id=gn.id AND tracks.visual_artist_id=ar.id "
"ORDER BY " + this->orderBy + " ";
ReplaceAll(query, "{{match_type}}", useRegex ? "REGEXP" : "LIKE");
}
else {
query =
@ -139,11 +144,13 @@ bool SearchTrackListQuery::OnRun(Connection& db) {
Statement trackQuery(query.c_str(), db);
if (hasFilter) {
std::string filterWithWildcard = "%" + trim_copy(to_lower_copy(filter)) + "%";
trackQuery.BindText(0, filterWithWildcard);
trackQuery.BindText(1, filterWithWildcard);
trackQuery.BindText(2, filterWithWildcard);
trackQuery.BindText(3, filterWithWildcard);
std::string patternToMatch = useRegex
? filter : "%" + trim_copy(to_lower_copy(filter)) + "%";
trackQuery.BindText(0, patternToMatch);
trackQuery.BindText(1, patternToMatch);
trackQuery.BindText(2, patternToMatch);
trackQuery.BindText(3, patternToMatch);
}
while (trackQuery.Step() == Row) {
@ -173,7 +180,8 @@ std::string SearchTrackListQuery::SerializeQuery() {
{ "name", kQueryName },
{ "options", {
{ "filter", filter },
{ "sortType", sortType}
{ "matchType", matchType },
{ "sortType", sortType }
}}
};
return FinalizeSerializedQueryWithLimitAndOffset(output);
@ -194,6 +202,7 @@ std::shared_ptr<SearchTrackListQuery> SearchTrackListQuery::DeserializeQuery(mus
auto options = nlohmann::json::parse(data)["options"];
auto result = std::make_shared<SearchTrackListQuery>(
library,
options.value("matchType", MatchType::Substring),
options["filter"].get<std::string>(),
options["sortType"].get<TrackSortType>());
result->ExtractLimitAndOffsetFromDeserializedQuery(options);

View File

@ -45,6 +45,7 @@ namespace musik { namespace core { namespace library { namespace query {
SearchTrackListQuery(
musik::core::ILibraryPtr library,
MatchType matchType,
const std::string& filter,
TrackSortType sort);
@ -70,6 +71,7 @@ namespace musik { namespace core { namespace library { namespace query {
private:
musik::core::ILibraryPtr library;
MatchType matchType;
bool parseHeaders;
std::string orderBy;
std::string orderByPredicate;

View File

@ -71,10 +71,10 @@ namespace musik { namespace core { namespace library { namespace query {
};
static const std::string REGULAR_PREDICATE = " tracks.{{fk_id}}=? ";
static const std::string REGULAR_FILTER = " AND LOWER({{table}}.name) LIKE ? ";
static const std::string REGULAR_FILTER = " AND LOWER({{table}}.name) {{match_type}} ? ";
static const std::string EXTENDED_PREDICATE = " (key=? AND meta_value_id=?) ";
static const std::string EXTENDED_FILTER = " AND LOWER(extended_metadata.value) LIKE ?";
static const std::string EXTENDED_FILTER = " AND LOWER(extended_metadata.value) {{match_type}} ?";
static const std::string EXTENDED_INNER_JOIN =
"INNER JOIN ( "
@ -193,7 +193,7 @@ namespace musik { namespace core { namespace library { namespace query {
and other supplementary information. */
static const std::string ALBUM_LIST_FILTER =
" AND (LOWER(album) like ? OR LOWER(album_artist) like ?) ";
" AND (LOWER(album) LIKE ? OR LOWER(album_artist) LIKE ?) ";
static const std::string ALBUM_LIST_QUERY =
"SELECT DISTINCT "

View File

@ -514,6 +514,7 @@
<ClCompile Include="audio\Visualizer.cpp" />
<ClCompile Include="c_context.cpp" />
<ClCompile Include="c_interface_wrappers.cpp" />
<ClCompile Include="db\SqliteExtensions.cpp" />
<ClCompile Include="debug.cpp" />
<ClCompile Include="i18n\Locale.cpp" />
<ClCompile Include="io\DataStreamFactory.cpp" />
@ -595,6 +596,7 @@
<ClInclude Include="audio\MasterTransport.h" />
<ClInclude Include="audio\Streams.h" />
<ClInclude Include="audio\Visualizer.h" />
<ClInclude Include="db\SqliteExtensions.h" />
<ClInclude Include="debug.h" />
<ClInclude Include="i18n\Locale.h" />
<ClInclude Include="io\DataStreamFactory.h" />

View File

@ -262,6 +262,9 @@
<ClCompile Include="net\RawWebSocketClient.cpp">
<Filter>src\net</Filter>
</ClCompile>
<ClCompile Include="db\SqliteExtensions.cpp">
<Filter>src\db</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.hpp">
@ -642,5 +645,8 @@
<ClInclude Include="net\RawWebSocketClient.h">
<Filter>src\net</Filter>
</ClInclude>
<ClInclude Include="db\SqliteExtensions.h">
<Filter>src\db</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@ -37,10 +37,10 @@
#include <string>
#define VERSION_MAJOR 0
#define VERSION_MINOR 94
#define VERSION_MINOR 95
#define VERSION_PATCH 0
#define VERSION_COMMIT_HASH "#d85c1002"
#define VERSION "0.94.0"
#define VERSION_COMMIT_HASH "#79637136"
#define VERSION "0.95.0"
namespace musik {
namespace cube {

View File

@ -88,15 +88,21 @@ CategorySearchLayout::~CategorySearchLayout() {
void CategorySearchLayout::LoadLastSession() {
auto session = Preferences::ForComponent(components::Session);
const std::string lastFilter = session->GetString(keys::LastCategoryFilter);
if (lastFilter.size()) {
this->input->SetText(lastFilter);
}
this->matchType = static_cast<MatchType>(session->GetInt(
keys::LastCategoryFilterMatchType,
static_cast<int>(MatchType::Substring)));
}
void CategorySearchLayout::SaveSession() {
auto session = Preferences::ForComponent(components::Session);
session->SetString(keys::LastCategoryFilter, this->input->GetText().c_str());
session->SetInt(keys::LastCategoryFilterMatchType, static_cast<int>(this->matchType));
}
void CategorySearchLayout::OnLayout() {
@ -106,7 +112,10 @@ void CategorySearchLayout::OnLayout() {
size_t inputWidth = cx / 2;
size_t inputX = x + ((cx - inputWidth) / 2);
this->input->MoveAndResize(inputX, 0, cx / 2, SEARCH_HEIGHT);
this->input->SetHint(_TSTR("search_filter_hint"));
bool inputIsRegex = this->matchType == MatchType::Regex;
this->input->SetHint(_TSTR(inputIsRegex ? "search_regex_hint" : "search_filter_hint"));
this->input->SetFocusedFrameColor(inputIsRegex ? Color::FrameImportant : Color::FrameFocused);
size_t labelY = SEARCH_HEIGHT;
size_t categoryWidth = cx / 3;
@ -134,9 +143,9 @@ void CategorySearchLayout::InitializeWindows(musik::core::audio::PlaybackService
void CategorySearchLayout::Requery() {
const std::string& value = this->input->GetText();
this->albums->Requery(value);
this->artists->Requery(value);
this->genres->Requery(value);
this->albums->Requery(this->matchType, value);
this->artists->Requery(this->matchType, value);
this->genres->Requery(this->matchType, value);
}
void CategorySearchLayout::FocusInput() {
@ -198,6 +207,10 @@ bool CategorySearchLayout::KeyPress(const std::string& key) {
return true;
}
}
else if (Hotkeys::Is(Hotkeys::SearchInputToggleMatchType, key)) {
this->ToggleMatchType();
return true;
}
return LayoutBase::KeyPress(key);
}
@ -211,3 +224,16 @@ void CategorySearchLayout::ProcessMessage(IMessage &message) {
}
}
void CategorySearchLayout::ToggleMatchType() {
const bool isRegex = this->matchType == MatchType::Regex;
this->SetMatchType(isRegex ? MatchType::Substring : MatchType::Regex);
}
void CategorySearchLayout::SetMatchType(MatchType matchType) {
if (matchType != this->matchType) {
this->matchType = matchType;
this->Layout();
this->Requery();
}
}

View File

@ -74,6 +74,8 @@ namespace musik {
virtual void ProcessMessage(musik::core::runtime::IMessage &message);
private:
using MatchType = musik::core::library::query::QueryBase::MatchType;
void InitializeWindows(musik::core::audio::PlaybackService& playback);
void OnCategoryEntryActivated(cursespp::ListWindow* sender, size_t index);
void Requery();
@ -85,7 +87,11 @@ namespace musik {
cursespp::TextInput* sender,
std::string value);
void ToggleMatchType();
void SetMatchType(MatchType matchType);
musik::core::ILibraryPtr library;
MatchType matchType{ MatchType::Substring };
std::shared_ptr<musik::core::Preferences> prefs;
std::shared_ptr<CategoryListView> albums;
std::shared_ptr<CategoryListView> artists;

View File

@ -86,15 +86,21 @@ TrackSearchLayout::~TrackSearchLayout() {
void TrackSearchLayout::LoadLastSession() {
auto session = Preferences::ForComponent(components::Session);
const std::string lastFilter = session->GetString(keys::LastTrackFilter);
if (lastFilter.size()) {
this->input->SetText(lastFilter);
}
this->matchType = static_cast<MatchType>(session->GetInt(
keys::LastTrackFilterMatchType,
static_cast<int>(MatchType::Substring)));
}
void TrackSearchLayout::SaveSession() {
auto session = Preferences::ForComponent(components::Session);
session->SetString(keys::LastTrackFilter, this->input->GetText().c_str());
session->SetInt(keys::LastTrackFilterMatchType, static_cast<int>(this->matchType));
}
void TrackSearchLayout::OnLayout() {
@ -104,7 +110,10 @@ void TrackSearchLayout::OnLayout() {
size_t inputWidth = cx / 2;
size_t inputX = x + ((cx - inputWidth) / 2);
this->input->MoveAndResize(inputX, y, cx / 2, SEARCH_HEIGHT);
this->input->SetHint(_TSTR("search_filter_hint"));
bool inputIsRegex = this->matchType == MatchType::Regex;
this->input->SetHint(_TSTR(inputIsRegex ? "search_regex_hint" : "search_filter_hint"));
this->input->SetFocusedFrameColor(inputIsRegex ? Color::FrameImportant : Color::FrameFocused);
this->trackList->MoveAndResize(
x,
@ -149,7 +158,8 @@ void TrackSearchLayout::Requery() {
const std::string& filter = this->input->GetText();
const TrackSortType sortOrder = getDefaultTrackSort(this->prefs);
this->trackList->Requery(std::shared_ptr<TrackListQueryBase>(
new SearchTrackListQuery(this->library, filter, sortOrder)));
new SearchTrackListQuery(
this->library, this->matchType, filter, sortOrder)));
}
void TrackSearchLayout::PlayFromTop() {
@ -187,6 +197,19 @@ void TrackSearchLayout::OnEnterPressed(cursespp::TextInput* sender) {
}
}
void TrackSearchLayout::ToggleMatchType() {
const bool isRegex = this->matchType == MatchType::Regex;
this->SetMatchType(isRegex ? MatchType::Substring : MatchType::Regex);
}
void TrackSearchLayout::SetMatchType(MatchType matchType) {
if (matchType != this->matchType) {
this->matchType = matchType;
this->Layout();
this->Requery();
}
}
bool TrackSearchLayout::KeyPress(const std::string& key) {
if (Hotkeys::Is(Hotkeys::Down, key)) {
if (this->GetFocus() == this->input) {
@ -210,5 +233,9 @@ bool TrackSearchLayout::KeyPress(const std::string& key) {
});
return true;
}
else if (Hotkeys::Is(Hotkeys::SearchInputToggleMatchType, key)) {
this->ToggleMatchType();
return true;
}
return LayoutBase::KeyPress(key);
}

View File

@ -70,6 +70,8 @@ namespace musik {
virtual void OnLayout();
private:
using MatchType = musik::core::library::query::QueryBase::MatchType;
void SaveSession();
void InitializeWindows();
void Requery();
@ -82,8 +84,12 @@ namespace musik {
void OnEnterPressed(cursespp::TextInput* sender);
void ToggleMatchType();
void SetMatchType(MatchType matchType);
musik::core::audio::PlaybackService& playback;
musik::core::ILibraryPtr library;
MatchType matchType{ MatchType::Substring };
std::shared_ptr<musik::core::Preferences> prefs;
std::shared_ptr<TrackListView> trackList;
std::shared_ptr<cursespp::TextInput> input;

View File

@ -151,8 +151,8 @@ static void queryPlaylists(
ILibraryPtr library,
std::function<void(std::shared_ptr<CategoryListQuery>)> callback)
{
std::shared_ptr<CategoryListQuery> query(
new CategoryListQuery(Playlists::TABLE_NAME, ""));
std::shared_ptr<CategoryListQuery> query(new CategoryListQuery(
CategoryListQuery::MatchType::Substring, Playlists::TABLE_NAME, ""));
library->Enqueue(query, [callback, query](auto q) {
callback(query->GetStatus() == IQuery::Finished

View File

@ -104,6 +104,8 @@ static std::unordered_map<std::string, Id> NAME_TO_ID = {
{ "track_list_previous_group", Id::TrackListPreviousGroup },
{ "track_list_play_from_top", Id::TrackListPlayFromTop },
{ "search_input_toggle_match_type", Id::SearchInputToggleMatchType },
{ "lyrics_retry", Id::LyricsRetry },
{ "playback_toggle_mute", Id::ToggleMute },
@ -196,6 +198,8 @@ static std::unordered_map<Id, std::string, EnumHasher> ID_TO_DEFAULT = {
{ Id::TrackListPreviousGroup, "[" },
{ Id::TrackListPlayFromTop, "M-P" },
{ Id::SearchInputToggleMatchType, "M-m" },
{ Id::LyricsRetry, "r" },
{ Id::ToggleMute, "m" },

View File

@ -115,6 +115,9 @@ namespace musik {
TrackListPreviousGroup,
TrackListPlayFromTop,
/* search input */
SearchInputToggleMatchType,
/* lyrics */
LyricsRetry,

View File

@ -51,7 +51,9 @@ namespace musik { namespace cube { namespace prefs {
const std::string keys::LastBrowseCategoryId = "LastBrowseCategoryId";
const std::string keys::LastBrowseDirectoryRoot = "LastBrowseDirectoryRoot";
const std::string keys::LastCategoryFilter = "LastCategoryFilter";
const std::string keys::LastCategoryFilterMatchType = "LastCategoryFilterMatchType";
const std::string keys::LastTrackFilter = "LastTrackFilter";
const std::string keys::LastTrackFilterMatchType = "LastTrackFilterMatchType";
const std::string keys::TrackSearchSortOrder = "TrackSearchSortOrder";
const std::string keys::CategoryTrackListSortOrder = "CategoryTrackListSortOrder";
const std::string keys::RatingPositiveChar = "RatingPositiveChar";

View File

@ -53,7 +53,9 @@ namespace musik { namespace cube { namespace prefs {
extern const std::string LastBrowseCategoryId;
extern const std::string LastBrowseDirectoryRoot;
extern const std::string LastCategoryFilter;
extern const std::string LastCategoryFilterMatchType;
extern const std::string LastTrackFilter;
extern const std::string LastTrackFilterMatchType;
extern const std::string TrackSearchSortOrder;
extern const std::string CategoryTrackListSortOrder;
extern const std::string RatingPositiveChar;

View File

@ -97,6 +97,7 @@ void CategoryListView::OnVisibilityChanged(bool visible) {
}
void CategoryListView::RequeryWithField(
MatchType matchType,
const std::string& fieldName,
const std::string& filter,
const int64_t selectAfterQuery)
@ -105,24 +106,45 @@ void CategoryListView::RequeryWithField(
this->activeQuery->Cancel();
}
this->matchType = matchType;
this->fieldName = fieldName;
this->fieldIdColumn = getFieldIdColumn(fieldName);
this->selectAfterQuery = selectAfterQuery;
this->filter = filter;
this->activeQuery.reset(new CategoryListQuery(fieldName, filter));
this->activeQuery = std::make_shared<CategoryListQuery>(matchType, fieldName, filter);
this->library->Enqueue(activeQuery);
}
void CategoryListView::RequeryWithField(
const std::string& fieldName,
const std::string& filter,
const int64_t selectAfterQuery)
{
this->RequeryWithField(this->matchType, fieldName, filter, selectAfterQuery);
}
void CategoryListView::Requery(
MatchType matchType,
const std::string& filter,
const int64_t selectAfterQuery)
{
this->RequeryWithField(matchType, this->fieldName, filter, selectAfterQuery);
}
void CategoryListView::Requery(const std::string& filter, const int64_t selectAfterQuery) {
this->RequeryWithField(this->fieldName, filter, selectAfterQuery);
this->RequeryWithField(this->matchType, this->fieldName, filter, selectAfterQuery);
}
void CategoryListView::Requery(const int64_t selectAfterQuery) {
this->RequeryWithField(this->fieldName, this->filter, selectAfterQuery);
this->RequeryWithField(this->matchType, this->fieldName, this->filter, selectAfterQuery);
}
void CategoryListView::Requery() {
this->RequeryWithField(this->matchType, this->fieldName, "", -1LL);
}
void CategoryListView::Reset() {
this->metadata.reset(new SdkValueList()); /* ugh */
this->metadata = std::make_shared<SdkValueList>(); /* ugh */
this->OnAdapterChanged();
}
@ -166,7 +188,7 @@ void CategoryListView::SetFieldName(const std::string& fieldName) {
this->metadata.reset();
}
this->Requery();
this->Requery(this->matchType);
}
}

View File

@ -55,6 +55,8 @@ namespace musik {
public sigslot::has_slots<>
{
public:
using MatchType = musik::core::library::query::QueryBase::MatchType;
CategoryListView(
musik::core::audio::PlaybackService& playback,
musik::core::ILibraryPtr library,
@ -62,17 +64,30 @@ namespace musik {
virtual ~CategoryListView();
void RequeryWithField(
MatchType matchType,
const std::string& fieldName,
const std::string& filter = "",
const int64_t selectAfterQuery = -1LL);
void RequeryWithField(
const std::string& fieldName,
const std::string& filter = "",
const int64_t selectAfterQuery = -1LL);
void Requery(
MatchType matchType,
const std::string& filter = "",
const int64_t selectAfterQuery = -1LL);
void Requery(
const std::string& filter,
const int64_t selectAfterQuery);
void Requery(const int64_t selectAfterQuery);
void Requery();
void Reset();
int64_t GetSelectedId();
@ -121,6 +136,7 @@ namespace musik {
std::string fieldName, fieldIdColumn;
std::string filter;
MatchType matchType{ MatchType::Substring };
int64_t selectAfterQuery;
musik::core::library::query::CategoryListQuery::Result metadata;
};

View File

@ -51,48 +51,50 @@ using namespace boost::filesystem;
indicies we'll use to store them */
#define THEME_COLOR_BACKGROUND 16
#define THEME_COLOR_FOREGROUND 17
#define THEME_COLOR_FOCUSED_BORDER 18
#define THEME_COLOR_TEXT_FOCUSED 19
#define THEME_COLOR_TEXT_ACTIVE 20
#define THEME_COLOR_TEXT_DISABLED 21
#define THEME_COLOR_TEXT_HIDDEN 22
#define THEME_COLOR_TEXT_WARNING 23
#define THEME_COLOR_TEXT_ERROR 24
#define THEME_COLOR_OVERLAY_BACKGROUND 25
#define THEME_COLOR_OVERLAY_FOREGROUND 26
#define THEME_COLOR_OVERLAY_BORDER 27
#define THEME_COLOR_OVERLAY_FOCUSED_BORDER 28
#define THEME_COLOR_OVERLAY_FOCUSED_TEXT 29
#define THEME_COLOR_SHORTCUTS_BACKGROUND 30
#define THEME_COLOR_SHORTCUTS_FOREGROUND 31
#define THEME_COLOR_SHORTCUTS_BACKGROUND_FOCUSED 32
#define THEME_COLOR_SHORTCUTS_FOREGROUND_FOCUSED 33
#define THEME_COLOR_BUTTON_BACKGROUND_NORMAL 34
#define THEME_COLOR_BUTTON_FOREGROUND_NORMAL 35
#define THEME_COLOR_BUTTON_BACKGROUND_ACTIVE 36
#define THEME_COLOR_BUTTON_FOREGROUND_ACTIVE 37
#define THEME_COLOR_BANNER_BACKGROUND 38
#define THEME_COLOR_BANNER_FOREGROUND 39
#define THEME_COLOR_LIST_HEADER_BACKGROUND 40
#define THEME_COLOR_LIST_HEADER_FOREGROUND 41
#define THEME_COLOR_LIST_HEADER_HIGHLIGHTED_BACKGROUND 42
#define THEME_COLOR_LIST_HEADER_HIGHLIGHTED_FOREGROUND 43
#define THEME_COLOR_LIST_ITEM_HIGHLIGHTED_BACKGROUND 44
#define THEME_COLOR_LIST_ITEM_HIGHLIGHTED_FOREGROUND 45
#define THEME_COLOR_LIST_ITEM_ACTIVE_BACKGROUND 46
#define THEME_COLOR_LIST_ITEM_ACTIVE_FOREGROUND 47
#define THEME_COLOR_LIST_ITEM_ACTIVE_HIGHLIGHTED_BACKGROUND 48
#define THEME_COLOR_LIST_ITEM_ACTIVE_HIGHLIGHTED_FOREGROUND 49
#define THEME_COLOR_FOOTER_BACKGROUND 50
#define THEME_COLOR_FOOTER_FOREGROUND 51
#define THEME_COLOR_HEADER_BACKGROUND 52
#define THEME_COLOR_HEADER_FOREGROUND 53
#define THEME_COLOR_FRAME_FOCUSED 18
#define THEME_COLOR_FRAME_IMPORTANT 19
#define THEME_COLOR_TEXT_FOCUSED 20
#define THEME_COLOR_TEXT_ACTIVE 21
#define THEME_COLOR_TEXT_DISABLED 22
#define THEME_COLOR_TEXT_HIDDEN 23
#define THEME_COLOR_TEXT_WARNING 24
#define THEME_COLOR_TEXT_ERROR 25
#define THEME_COLOR_OVERLAY_BACKGROUND 26
#define THEME_COLOR_OVERLAY_FOREGROUND 27
#define THEME_COLOR_OVERLAY_BORDER 28
#define THEME_COLOR_OVERLAY_FOCUSED_FRAME 29
#define THEME_COLOR_OVERLAY_FOCUSED_TEXT 30
#define THEME_COLOR_SHORTCUTS_BACKGROUND 31
#define THEME_COLOR_SHORTCUTS_FOREGROUND 32
#define THEME_COLOR_SHORTCUTS_BACKGROUND_FOCUSED 33
#define THEME_COLOR_SHORTCUTS_FOREGROUND_FOCUSED 34
#define THEME_COLOR_BUTTON_BACKGROUND_NORMAL 35
#define THEME_COLOR_BUTTON_FOREGROUND_NORMAL 36
#define THEME_COLOR_BUTTON_BACKGROUND_ACTIVE 37
#define THEME_COLOR_BUTTON_FOREGROUND_ACTIVE 38
#define THEME_COLOR_BANNER_BACKGROUND 39
#define THEME_COLOR_BANNER_FOREGROUND 40
#define THEME_COLOR_LIST_HEADER_BACKGROUND 41
#define THEME_COLOR_LIST_HEADER_FOREGROUND 42
#define THEME_COLOR_LIST_HEADER_HIGHLIGHTED_BACKGROUND 43
#define THEME_COLOR_LIST_HEADER_HIGHLIGHTED_FOREGROUND 44
#define THEME_COLOR_LIST_ITEM_HIGHLIGHTED_BACKGROUND 45
#define THEME_COLOR_LIST_ITEM_HIGHLIGHTED_FOREGROUND 46
#define THEME_COLOR_LIST_ITEM_ACTIVE_BACKGROUND 47
#define THEME_COLOR_LIST_ITEM_ACTIVE_FOREGROUND 48
#define THEME_COLOR_LIST_ITEM_ACTIVE_HIGHLIGHTED_BACKGROUND 49
#define THEME_COLOR_LIST_ITEM_ACTIVE_HIGHLIGHTED_FOREGROUND 50
#define THEME_COLOR_FOOTER_BACKGROUND 51
#define THEME_COLOR_FOOTER_FOREGROUND 52
#define THEME_COLOR_HEADER_BACKGROUND 53
#define THEME_COLOR_HEADER_FOREGROUND 54
/* user-readable names for the color identifiers above. these are
used as key names in the config files */
#define JSON_KEY_COLOR_BACKGROUND "background"
#define JSON_KEY_COLOR_FOREGROUND "foreground"
#define JSON_KEY_COLOR_FOCUSED_BORDER "focused_border"
#define JSON_KEY_COLOR_FRAME_FOCUSED "frame_focused"
#define JSON_KEY_COLOR_FRAME_IMPORTANT "frame_important"
#define JSON_KEY_COLOR_TEXT_FOCUSED "text_focused"
#define JSON_KEY_COLOR_TEXT_ACTIVE "text_active"
#define JSON_KEY_COLOR_TEXT_DISABLED "text_disabled"
@ -222,7 +224,8 @@ struct Theme {
/* main */
background.Set(THEME_COLOR_BACKGROUND, 24, 24, 20, -1);
foreground.Set(THEME_COLOR_FOREGROUND, 220, 220, 220, COLOR_256_OFFWHITE);
focusedBorder.Set(THEME_COLOR_FOCUSED_BORDER, 220, 82, 86, COLOR_256_RED);
focusedFrame.Set(THEME_COLOR_FRAME_FOCUSED, 220, 82, 86, COLOR_256_RED);
importantFrame.Set(THEME_COLOR_FRAME_IMPORTANT, 102, 217, 238, COLOR_256_BLUE);
/* text */
textFocused.Set(THEME_COLOR_TEXT_FOCUSED, 220, 82, 86, COLOR_256_RED);
@ -236,7 +239,7 @@ struct Theme {
overlayBackground.Set(THEME_COLOR_OVERLAY_BACKGROUND, 66, 66, 56, COLOR_256_MEDIUM_GRAY);
overlayForeground.Set(THEME_COLOR_OVERLAY_FOREGROUND, 220, 220, 220, COLOR_256_OFFWHITE);
overlayBorder.Set(THEME_COLOR_OVERLAY_BORDER, 102, 217, 238, COLOR_256_BLUE);
overlayFocusedBorder.Set(THEME_COLOR_OVERLAY_FOCUSED_BORDER, 220, 82, 86, COLOR_256_RED);
overlayFocusedFrame.Set(THEME_COLOR_OVERLAY_FOCUSED_FRAME, 220, 82, 86, COLOR_256_RED);
overlayFocusedText.Set(THEME_COLOR_OVERLAY_FOCUSED_TEXT, 220, 82, 86, COLOR_256_RED);
/* shortcut bar */
@ -299,7 +302,7 @@ struct Theme {
/* actually read the theme values! */
this->background.Set(colors.value(JSON_KEY_COLOR_BACKGROUND, unset));
this->foreground.Set(colors.value(JSON_KEY_COLOR_FOREGROUND, unset));
this->focusedBorder.Set(colors.value(JSON_KEY_COLOR_FOCUSED_BORDER, unset));
this->importantFrame.Set(colors.value(JSON_KEY_COLOR_FRAME_IMPORTANT, unset));
this->textFocused.Set(colors.value(JSON_KEY_COLOR_TEXT_FOCUSED, unset));
this->textActive.Set(colors.value(JSON_KEY_COLOR_TEXT_ACTIVE, unset));
this->textDisabled.Set(colors.value(JSON_KEY_COLOR_TEXT_DISABLED, unset));
@ -309,7 +312,7 @@ struct Theme {
this->overlayBackground.Set(colors.value(JSON_KEY_COLOR_OVERLAY_BACKGROUND, unset));
this->overlayForeground.Set(colors.value(JSON_KEY_COLOR_OVERLAY_FOREGROUND, unset));
this->overlayBorder.Set(colors.value(JSON_KEY_COLOR_OVERLAY_BORDER, unset));
this->overlayFocusedBorder.Set(colors.value(JSON_KEY_COLOR_OVERLAY_FOCUSED_BORDER, unset));
this->overlayFocusedFrame.Set(colors.value(JSON_KEY_COLOR_OVERLAY_FOCUSED_BORDER, unset));
this->overlayFocusedText.Set(colors.value(JSON_KEY_COLOR_OVERLAY_FOCUSED_TEXT, unset));
this->shortcutsBackground.Set(colors.value(JSON_KEY_COLOR_SHORTCUTS_BACKGROUND, unset));
this->shortcutsForeground.Set(colors.value(JSON_KEY_COLOR_SHORTCUTS_FOREGROUND, unset));
@ -336,6 +339,19 @@ struct Theme {
this->listActiveHighlightedBackground.Set(colors.value(JSON_KEY_COLOR_LIST_ITEM_ACTIVE_HIGHLIGHTED_BACKGROUND, unset));
this->listActiveHighlightedForeground.Set(colors.value(JSON_KEY_COLOR_LIST_ITEM_ACTIVE_HIGHLIGHTED_FOREGROUND, unset));
/* these ones require special logic because they were renamed and we
may need to load them by their original names. */
auto keyWithFallback = [](nlohmann::json& json, const std::string& key, const std::string& fallback) -> std::string {
return json.find(key) != json.end() ? key : fallback;
};
const std::string focusedFrameKey =
keyWithFallback(colors, JSON_KEY_COLOR_FRAME_FOCUSED, "focused_border");
this->focusedFrame.Set(colors.value(focusedFrameKey, unset));
/* finalize */
this->fn = fn;
this->name = data.value("name", "unnamed");
@ -362,8 +378,9 @@ struct Theme {
/* main */
init_pair(Color::ContentColorDefault, foregroundId, backgroundId);
init_pair(Color::FrameColorDefault, foregroundId, backgroundId);
init_pair(Color::FrameColorFocused, focusedBorder.Id(mode, COLOR_RED),backgroundId);
init_pair(Color::FrameDefault, foregroundId, backgroundId);
init_pair(Color::FrameFocused, focusedFrame.Id(mode, COLOR_RED),backgroundId);
init_pair(Color::FrameImportant, importantFrame.Id(mode, COLOR_BLUE), backgroundId);
/* text */
init_pair(Color::TextDefault, foregroundId, backgroundId);
@ -378,10 +395,10 @@ struct Theme {
int overlayBgId = overlayBackground.Id(mode, -1);
init_pair(Color::OverlayFrame, overlayBorder.Id(mode, COLOR_BLUE), overlayBgId);
init_pair(Color::OverlayContent, overlayForeground.Id(mode, -1), overlayBgId);
init_pair(Color::OverlayTextInputFrame, overlayFocusedBorder.Id(mode, COLOR_RED), overlayBgId);
init_pair(Color::OverlayTextInputFrame, overlayFocusedFrame.Id(mode, COLOR_RED), overlayBgId);
init_pair(Color::OverlayTextFocused, overlayFocusedText.Id(mode, COLOR_RED), overlayBgId);
init_pair(Color::OverlayListFrame, foregroundId, overlayBgId);
init_pair(Color::OverlayListFrameFocused, focusedBorder.Id(mode, COLOR_RED), overlayBgId);
init_pair(Color::OverlayListFrameFocused, focusedFrame.Id(mode, COLOR_RED), overlayBgId);
/* shortcuts */
init_pair(
@ -461,7 +478,8 @@ struct Theme {
/* main */
ThemeColor background;
ThemeColor foreground;
ThemeColor focusedBorder;
ThemeColor focusedFrame;
ThemeColor importantFrame;
/* text */
ThemeColor textFocused;
@ -475,7 +493,7 @@ struct Theme {
ThemeColor overlayBackground;
ThemeColor overlayForeground;
ThemeColor overlayBorder;
ThemeColor overlayFocusedBorder;
ThemeColor overlayFocusedFrame;
ThemeColor overlayFocusedText;
/* shortcut bar */

View File

@ -168,9 +168,9 @@ Window::Window(IWindow *parent) {
this->lastAbsoluteX = 0;
this->lastAbsoluteY = 0;
this->contentColor = Color(Color::ContentColorDefault);
this->frameColor = Color(Color::FrameColorDefault);
this->frameColor = Color(Color::FrameDefault);
this->focusedContentColor = Color(Color::ContentColorDefault);
this->focusedFrameColor = Color(Color::FrameColorFocused);
this->focusedFrameColor = Color(Color::FrameFocused);
this->drawFrame = true;
this->isVisibleInParent = false;
this->isDirty = true;
@ -484,7 +484,7 @@ void Window::SetFrameColor(Color color) {
ASSERT_MAIN_THREAD();
this->frameColor = (color == Color::Default)
? Color::FrameColorDefault : color;
? Color::FrameDefault : color;
this->RepaintBackground();
this->Redraw();
@ -494,7 +494,7 @@ void Window::SetFocusedFrameColor(Color color) {
ASSERT_MAIN_THREAD();
this->focusedFrameColor = (color == Color::Default)
? Color::FrameColorFocused : color;
? Color::FrameFocused : color;
this->RepaintBackground();
this->Redraw();

View File

@ -52,33 +52,34 @@ namespace cursespp {
ListItemHeaderHighlighted = 6,
ContentColorDefault = 7,
FrameColorDefault = 8,
FrameColorFocused = 9,
FrameDefault = 8,
FrameFocused = 9,
FrameImportant = 10,
TextDefault = 10,
TextDisabled = 11,
TextFocused = 12,
TextActive = 13,
TextWarning = 14,
TextError = 15,
TextHidden = 16,
TextDefault = 11,
TextDisabled = 12,
TextFocused = 13,
TextActive = 14,
TextWarning = 15,
TextError = 16,
TextHidden = 17,
ButtonDefault = 17,
ButtonHighlighted = 18,
ButtonDefault = 18,
ButtonHighlighted = 19,
ShortcutRowDefault = 19,
ShortcutRowFocused = 20,
ShortcutRowDefault = 20,
ShortcutRowFocused = 21,
OverlayFrame = 21,
OverlayContent = 22,
OverlayTextInputFrame = 23,
OverlayTextFocused = 24,
OverlayListFrame = 25,
OverlayListFrameFocused = 26,
OverlayFrame = 22,
OverlayContent = 23,
OverlayTextInputFrame = 24,
OverlayTextFocused = 25,
OverlayListFrame = 26,
OverlayListFrameFocused = 27,
Header = 27,
Footer = 28,
Banner = 29
Header = 28,
Footer = 29,
Banner = 30
};
Color() {

View File

@ -37,6 +37,7 @@
"browse_no_subdirectories_toast": "there are no more sub-directories.\n\npress '%s' to play the selection.",
"search_filter_hint": "search",
"search_regex_hint": "regex",
"console_list_title": "debug logs",
"console_version": "musikcube version %s",

View File

@ -10,10 +10,14 @@
"hex": "#fbf1c7",
"palette": 229
},
"focused_border": {
"frame_focused": {
"hex": "#fb4934",
"palette": 167
},
"frame_important": {
"hex": "#458588",
"palette": 66
},
"text_focused": {
"hex": "#fb4934",
"palette": 167

View File

@ -10,10 +10,14 @@
"hex": "#eee8d5",
"palette": 254
},
"focused_border": {
"frame_focused": {
"hex": "#dc322f",
"palette": 160
},
"frame_important": {
"hex": "#268bd2",
"palette": 33
},
"text_focused": {
"hex": "#dc322f",
"palette": 160

View File

@ -10,10 +10,14 @@
"hex": "#073642",
"palette": 235
},
"focused_border": {
"frame_focused": {
"hex": "#dc322f",
"palette": 160
},
"frame_important": {
"hex": "#268bd2",
"palette": 33
},
"text_focused": {
"hex": "#dc322f",
"palette": 160

Binary file not shown.