mirror of
https://github.com/clangen/musikcube.git
synced 2024-10-02 04:52:32 +00:00
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:
commit
6e06fe7f59
@ -7,7 +7,7 @@ cmake_minimum_required(VERSION 3.0)
|
|||||||
|
|
||||||
project(musikcube)
|
project(musikcube)
|
||||||
set (musikcube_VERSION_MAJOR 0)
|
set (musikcube_VERSION_MAJOR 0)
|
||||||
set (musikcube_VERSION_MINOR 94)
|
set (musikcube_VERSION_MINOR 95)
|
||||||
set (musikcube_VERSION_PATCH 0)
|
set (musikcube_VERSION_PATCH 0)
|
||||||
set (musikcube_VERSION "${musikcube_VERSION_MAJOR}.${musikcube_VERSION_MINOR}.${musikcube_VERSION_PATCH}")
|
set (musikcube_VERSION "${musikcube_VERSION_MAJOR}.${musikcube_VERSION_MINOR}.${musikcube_VERSION_PATCH}")
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
%define name musikcube
|
%define name musikcube
|
||||||
%define build_timestamp %{lua: print(os.date("%Y%m%d"))}
|
%define build_timestamp %{lua: print(os.date("%Y%m%d"))}
|
||||||
%define version 0.94.0
|
%define version 0.95.0
|
||||||
Name: %{name}
|
Name: %{name}
|
||||||
Version: %{version}
|
Version: %{version}
|
||||||
Release: %{dist}
|
Release: %{dist}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
name: musikcube # you probably want to 'snapcraft register <name>'
|
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
|
summary: a terminal-based music player, metadata indexer, and server
|
||||||
description: |
|
description: |
|
||||||
musikcube is a fully functional terminal-based music player, library, and
|
musikcube is a fully functional terminal-based music player, library, and
|
||||||
|
@ -15,6 +15,7 @@ set(CORE_SOURCES
|
|||||||
./audio/Visualizer.cpp
|
./audio/Visualizer.cpp
|
||||||
./db/Connection.cpp
|
./db/Connection.cpp
|
||||||
./db/ScopedTransaction.cpp
|
./db/ScopedTransaction.cpp
|
||||||
|
./db/SqliteExtensions.cpp
|
||||||
./db/Statement.cpp
|
./db/Statement.cpp
|
||||||
./i18n/Locale.cpp
|
./i18n/Locale.cpp
|
||||||
./io/DataStreamFactory.cpp
|
./io/DataStreamFactory.cpp
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
#include "pch.hpp"
|
#include "pch.hpp"
|
||||||
|
|
||||||
#include <musikcore/db/Connection.h>
|
#include <musikcore/db/Connection.h>
|
||||||
|
#include <musikcore/db/SqliteExtensions.h>
|
||||||
#include <sqlite/sqlite3.h>
|
#include <sqlite/sqlite3.h>
|
||||||
|
|
||||||
static std::mutex globalMutex;
|
static std::mutex globalMutex;
|
||||||
@ -116,6 +117,8 @@ int Connection::LastModifiedRowCount() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Connection::Initialize(unsigned int cache) {
|
void Connection::Initialize(unsigned int cache) {
|
||||||
|
SqliteExtensions::Register(this->connection);
|
||||||
|
|
||||||
sqlite3_enable_shared_cache(1);
|
sqlite3_enable_shared_cache(1);
|
||||||
sqlite3_busy_timeout(this->connection, 10000);
|
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,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 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 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.
|
sqlite3_exec(this->connection, "PRAGMA temp_store=MEMORY", nullptr, nullptr, nullptr); // MEMORY, not file. More speed.
|
||||||
|
916
src/musikcore/db/SqliteExtensions.cpp
Normal file
916
src/musikcore/db/SqliteExtensions.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} } }
|
||||||
|
|
51
src/musikcore/db/SqliteExtensions.h
Normal file
51
src/musikcore/db/SqliteExtensions.h
Normal 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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} } }
|
||||||
|
|
@ -305,6 +305,7 @@ ITrackList* LocalMetadataProxy::QueryTracks(const char* query, int limit, int of
|
|||||||
std::shared_ptr<SearchTrackListQuery> search(
|
std::shared_ptr<SearchTrackListQuery> search(
|
||||||
new SearchTrackListQuery(
|
new SearchTrackListQuery(
|
||||||
this->library,
|
this->library,
|
||||||
|
SearchTrackListQuery::MatchType::Substring,
|
||||||
std::string(query ? query : ""),
|
std::string(query ? query : ""),
|
||||||
TrackSortType::Album));
|
TrackSortType::Album));
|
||||||
|
|
||||||
@ -456,6 +457,7 @@ IValueList* LocalMetadataProxy::QueryCategoryWithPredicate(
|
|||||||
|
|
||||||
std::shared_ptr<CategoryListQuery> search(
|
std::shared_ptr<CategoryListQuery> search(
|
||||||
new CategoryListQuery(
|
new CategoryListQuery(
|
||||||
|
CategoryListQuery::MatchType::Substring,
|
||||||
type,
|
type,
|
||||||
{ field, predicateId },
|
{ field, predicateId },
|
||||||
std::string(filter ? filter : "")));
|
std::string(filter ? filter : "")));
|
||||||
@ -480,7 +482,10 @@ IValueList* LocalMetadataProxy::QueryCategoryWithPredicates(
|
|||||||
auto predicateList = toPredicateList(predicates, predicateCount);
|
auto predicateList = toPredicateList(predicates, predicateCount);
|
||||||
|
|
||||||
auto query = std::make_shared<CategoryListQuery>(
|
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);
|
this->library->EnqueueAndWait(query);
|
||||||
|
|
||||||
|
@ -50,6 +50,11 @@ namespace musik { namespace core { namespace library { namespace query {
|
|||||||
public sigslot::has_slots<>
|
public sigslot::has_slots<>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
enum class MatchType : int {
|
||||||
|
Substring = 1,
|
||||||
|
Regex = 2
|
||||||
|
};
|
||||||
|
|
||||||
QueryBase()
|
QueryBase()
|
||||||
: status(IQuery::Idle)
|
: status(IQuery::Idle)
|
||||||
, options(0)
|
, options(0)
|
||||||
|
@ -56,35 +56,44 @@ static const std::string kUnfilteredPlaylistsQuery =
|
|||||||
static const std::string kFilteredPlaylistsQuery =
|
static const std::string kFilteredPlaylistsQuery =
|
||||||
"SELECT DISTINCT id, name "
|
"SELECT DISTINCT id, name "
|
||||||
"FROM playlists"
|
"FROM playlists"
|
||||||
"WHERE LOWER(name) LIKE LOWER(?) "
|
"WHERE LOWER(name) {{match_type}} ? "
|
||||||
"ORDER BY name;";
|
"ORDER BY name;";
|
||||||
|
|
||||||
const std::string CategoryListQuery::kQueryName = "CategoryListQuery";
|
const std::string CategoryListQuery::kQueryName = "CategoryListQuery";
|
||||||
|
|
||||||
|
static std::string getMatchType(CategoryListQuery::MatchType matchType) {
|
||||||
|
return matchType == CategoryListQuery::MatchType::Regex ? "REGEXP" : "LIKE";
|
||||||
|
}
|
||||||
|
|
||||||
CategoryListQuery::CategoryListQuery() {
|
CategoryListQuery::CategoryListQuery() {
|
||||||
}
|
}
|
||||||
|
|
||||||
CategoryListQuery::CategoryListQuery(
|
CategoryListQuery::CategoryListQuery(
|
||||||
const std::string& trackField, const std::string& filter)
|
MatchType matchType,
|
||||||
: CategoryListQuery(trackField, category::PredicateList(), filter) {
|
const std::string& trackField,
|
||||||
|
const std::string& filter)
|
||||||
|
: CategoryListQuery(matchType, trackField, category::PredicateList(), filter) {
|
||||||
}
|
}
|
||||||
|
|
||||||
CategoryListQuery::CategoryListQuery(
|
CategoryListQuery::CategoryListQuery(
|
||||||
|
MatchType matchType,
|
||||||
const std::string& trackField,
|
const std::string& trackField,
|
||||||
const category::Predicate predicate,
|
const category::Predicate predicate,
|
||||||
const std::string& filter)
|
const std::string& filter)
|
||||||
: CategoryListQuery(trackField, category::PredicateList{ predicate }, filter) {
|
: CategoryListQuery(matchType, trackField, category::PredicateList{ predicate }, filter) {
|
||||||
}
|
}
|
||||||
|
|
||||||
CategoryListQuery::CategoryListQuery(
|
CategoryListQuery::CategoryListQuery(
|
||||||
|
MatchType matchType,
|
||||||
const std::string& trackField,
|
const std::string& trackField,
|
||||||
const category::PredicateList predicates,
|
const category::PredicateList predicates,
|
||||||
const std::string& filter)
|
const std::string& filter)
|
||||||
: trackField(trackField)
|
: matchType(matchType)
|
||||||
|
, trackField(trackField)
|
||||||
, filter(filter) {
|
, filter(filter) {
|
||||||
result.reset(new SdkValueList());
|
result.reset(new SdkValueList());
|
||||||
|
|
||||||
if (this->filter.size()) {
|
if (this->filter.size() && this->matchType == MatchType::Substring) {
|
||||||
/* transform "FilteR" => "%filter%" */
|
/* transform "FilteR" => "%filter%" */
|
||||||
std::string wild = this->filter;
|
std::string wild = this->filter;
|
||||||
std::transform(wild.begin(), wild.end(), wild.begin(), tolower);
|
std::transform(wild.begin(), wild.end(), wild.begin(), tolower);
|
||||||
@ -134,6 +143,8 @@ void CategoryListQuery::QueryPlaylist(musik::core::db::Connection& db) {
|
|||||||
? kFilteredPlaylistsQuery
|
? kFilteredPlaylistsQuery
|
||||||
: kUnfilteredPlaylistsQuery;
|
: kUnfilteredPlaylistsQuery;
|
||||||
|
|
||||||
|
category::ReplaceAll(query, "{{match_type}}", getMatchType(matchType));
|
||||||
|
|
||||||
Statement stmt(query.c_str() , db);
|
Statement stmt(query.c_str() , db);
|
||||||
|
|
||||||
if (filtered) {
|
if (filtered) {
|
||||||
@ -157,6 +168,7 @@ void CategoryListQuery::QueryRegular(musik::core::db::Connection &db) {
|
|||||||
if (this->filter.size()) {
|
if (this->filter.size()) {
|
||||||
regularFilter = category::REGULAR_FILTER;
|
regularFilter = category::REGULAR_FILTER;
|
||||||
category::ReplaceAll(regularFilter, "{{table}}", prop.first);
|
category::ReplaceAll(regularFilter, "{{table}}", prop.first);
|
||||||
|
category::ReplaceAll(regularFilter, "{{match_type}}", getMatchType(matchType));
|
||||||
args.push_back(category::StringArgument(this->filter));
|
args.push_back(category::StringArgument(this->filter));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,6 +196,7 @@ void CategoryListQuery::QueryExtended(musik::core::db::Connection &db) {
|
|||||||
if (this->filter.size()) {
|
if (this->filter.size()) {
|
||||||
extendedFilter = category::EXTENDED_FILTER;
|
extendedFilter = category::EXTENDED_FILTER;
|
||||||
args.push_back(category::StringArgument(this->filter));
|
args.push_back(category::StringArgument(this->filter));
|
||||||
|
category::ReplaceAll(extendedFilter, "{{match_type}}", getMatchType(matchType));
|
||||||
}
|
}
|
||||||
|
|
||||||
category::ReplaceAll(query, "{{regular_predicates}}", regular);
|
category::ReplaceAll(query, "{{regular_predicates}}", regular);
|
||||||
@ -242,6 +255,7 @@ std::string CategoryListQuery::SerializeQuery() {
|
|||||||
query["options"] = {
|
query["options"] = {
|
||||||
{ "trackField", this->trackField },
|
{ "trackField", this->trackField },
|
||||||
{ "filter", this->filter },
|
{ "filter", this->filter },
|
||||||
|
{ "matchType", this->matchType },
|
||||||
{ "outputType", this->outputType },
|
{ "outputType", this->outputType },
|
||||||
{ "regularPredicateList", PredicateListToJson(this->regular) },
|
{ "regularPredicateList", PredicateListToJson(this->regular) },
|
||||||
{ "extendedPredicateList", PredicateListToJson(this->extended) }
|
{ "extendedPredicateList", PredicateListToJson(this->extended) }
|
||||||
@ -269,6 +283,7 @@ std::shared_ptr<CategoryListQuery> CategoryListQuery::DeserializeQuery(const std
|
|||||||
std::shared_ptr<CategoryListQuery> result(new CategoryListQuery());
|
std::shared_ptr<CategoryListQuery> result(new CategoryListQuery());
|
||||||
result->trackField = options.value("trackField", "");
|
result->trackField = options.value("trackField", "");
|
||||||
result->filter = options.value("filter", "");
|
result->filter = options.value("filter", "");
|
||||||
|
result->matchType = options.value("matchType", MatchType::Substring);
|
||||||
result->outputType = (OutputType)options.value("outputType", OutputType::Regular);
|
result->outputType = (OutputType)options.value("outputType", OutputType::Regular);
|
||||||
PredicateListFromJson(options["regularPredicateList"], result->regular);
|
PredicateListFromJson(options["regularPredicateList"], result->regular);
|
||||||
PredicateListFromJson(options["extendedPredicateList"], result->extended);
|
PredicateListFromJson(options["extendedPredicateList"], result->extended);
|
||||||
|
@ -52,15 +52,18 @@ namespace musik { namespace core { namespace library { namespace query {
|
|||||||
using Result = SdkValueList::Shared;
|
using Result = SdkValueList::Shared;
|
||||||
|
|
||||||
CategoryListQuery(
|
CategoryListQuery(
|
||||||
|
MatchType matchType,
|
||||||
const std::string& trackField,
|
const std::string& trackField,
|
||||||
const std::string& filter = "");
|
const std::string& filter = "");
|
||||||
|
|
||||||
CategoryListQuery(
|
CategoryListQuery(
|
||||||
|
MatchType matchType,
|
||||||
const std::string& trackField,
|
const std::string& trackField,
|
||||||
const category::Predicate predicate,
|
const category::Predicate predicate,
|
||||||
const std::string& filter = "");
|
const std::string& filter = "");
|
||||||
|
|
||||||
CategoryListQuery(
|
CategoryListQuery(
|
||||||
|
MatchType matchType,
|
||||||
const std::string& trackField,
|
const std::string& trackField,
|
||||||
const category::PredicateList predicate,
|
const category::PredicateList predicate,
|
||||||
const std::string& filter = "");
|
const std::string& filter = "");
|
||||||
@ -100,6 +103,7 @@ namespace musik { namespace core { namespace library { namespace query {
|
|||||||
|
|
||||||
std::string trackField;
|
std::string trackField;
|
||||||
std::string filter;
|
std::string filter;
|
||||||
|
MatchType matchType;
|
||||||
OutputType outputType;
|
OutputType outputType;
|
||||||
category::PredicateList regular, extended;
|
category::PredicateList regular, extended;
|
||||||
Result result;
|
Result result;
|
||||||
|
@ -61,9 +61,13 @@ using namespace boost::algorithm;
|
|||||||
const std::string SearchTrackListQuery::kQueryName = "SearchTrackListQuery";
|
const std::string SearchTrackListQuery::kQueryName = "SearchTrackListQuery";
|
||||||
|
|
||||||
SearchTrackListQuery::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->library = library;
|
||||||
|
this->matchType = matchType;
|
||||||
this->sortType = sort;
|
this->sortType = sort;
|
||||||
this->filter = filter;
|
this->filter = filter;
|
||||||
|
|
||||||
@ -105,11 +109,10 @@ bool SearchTrackListQuery::OnRun(Connection& db) {
|
|||||||
headers.reset(new std::set<size_t>());
|
headers.reset(new std::set<size_t>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool useRegex = (matchType == MatchType::Regex);
|
||||||
bool hasFilter = (this->filter.size() > 0);
|
bool hasFilter = (this->filter.size() > 0);
|
||||||
|
|
||||||
std::string lastAlbum;
|
std::string lastAlbum;
|
||||||
size_t index = 0;
|
size_t index = 0;
|
||||||
|
|
||||||
std::string query;
|
std::string query;
|
||||||
|
|
||||||
if (hasFilter) {
|
if (hasFilter) {
|
||||||
@ -119,9 +122,11 @@ bool SearchTrackListQuery::OnRun(Connection& db) {
|
|||||||
"WHERE "
|
"WHERE "
|
||||||
" tracks.visible=1 AND "
|
" tracks.visible=1 AND "
|
||||||
+ this->orderByPredicate +
|
+ 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 "
|
" AND tracks.album_id=al.id AND tracks.visual_genre_id=gn.id AND tracks.visual_artist_id=ar.id "
|
||||||
"ORDER BY " + this->orderBy + " ";
|
"ORDER BY " + this->orderBy + " ";
|
||||||
|
|
||||||
|
ReplaceAll(query, "{{match_type}}", useRegex ? "REGEXP" : "LIKE");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
query =
|
query =
|
||||||
@ -139,11 +144,13 @@ bool SearchTrackListQuery::OnRun(Connection& db) {
|
|||||||
Statement trackQuery(query.c_str(), db);
|
Statement trackQuery(query.c_str(), db);
|
||||||
|
|
||||||
if (hasFilter) {
|
if (hasFilter) {
|
||||||
std::string filterWithWildcard = "%" + trim_copy(to_lower_copy(filter)) + "%";
|
std::string patternToMatch = useRegex
|
||||||
trackQuery.BindText(0, filterWithWildcard);
|
? filter : "%" + trim_copy(to_lower_copy(filter)) + "%";
|
||||||
trackQuery.BindText(1, filterWithWildcard);
|
|
||||||
trackQuery.BindText(2, filterWithWildcard);
|
trackQuery.BindText(0, patternToMatch);
|
||||||
trackQuery.BindText(3, filterWithWildcard);
|
trackQuery.BindText(1, patternToMatch);
|
||||||
|
trackQuery.BindText(2, patternToMatch);
|
||||||
|
trackQuery.BindText(3, patternToMatch);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (trackQuery.Step() == Row) {
|
while (trackQuery.Step() == Row) {
|
||||||
@ -173,7 +180,8 @@ std::string SearchTrackListQuery::SerializeQuery() {
|
|||||||
{ "name", kQueryName },
|
{ "name", kQueryName },
|
||||||
{ "options", {
|
{ "options", {
|
||||||
{ "filter", filter },
|
{ "filter", filter },
|
||||||
{ "sortType", sortType}
|
{ "matchType", matchType },
|
||||||
|
{ "sortType", sortType }
|
||||||
}}
|
}}
|
||||||
};
|
};
|
||||||
return FinalizeSerializedQueryWithLimitAndOffset(output);
|
return FinalizeSerializedQueryWithLimitAndOffset(output);
|
||||||
@ -194,6 +202,7 @@ std::shared_ptr<SearchTrackListQuery> SearchTrackListQuery::DeserializeQuery(mus
|
|||||||
auto options = nlohmann::json::parse(data)["options"];
|
auto options = nlohmann::json::parse(data)["options"];
|
||||||
auto result = std::make_shared<SearchTrackListQuery>(
|
auto result = std::make_shared<SearchTrackListQuery>(
|
||||||
library,
|
library,
|
||||||
|
options.value("matchType", MatchType::Substring),
|
||||||
options["filter"].get<std::string>(),
|
options["filter"].get<std::string>(),
|
||||||
options["sortType"].get<TrackSortType>());
|
options["sortType"].get<TrackSortType>());
|
||||||
result->ExtractLimitAndOffsetFromDeserializedQuery(options);
|
result->ExtractLimitAndOffsetFromDeserializedQuery(options);
|
||||||
|
@ -45,6 +45,7 @@ namespace musik { namespace core { namespace library { namespace query {
|
|||||||
|
|
||||||
SearchTrackListQuery(
|
SearchTrackListQuery(
|
||||||
musik::core::ILibraryPtr library,
|
musik::core::ILibraryPtr library,
|
||||||
|
MatchType matchType,
|
||||||
const std::string& filter,
|
const std::string& filter,
|
||||||
TrackSortType sort);
|
TrackSortType sort);
|
||||||
|
|
||||||
@ -70,6 +71,7 @@ namespace musik { namespace core { namespace library { namespace query {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
musik::core::ILibraryPtr library;
|
musik::core::ILibraryPtr library;
|
||||||
|
MatchType matchType;
|
||||||
bool parseHeaders;
|
bool parseHeaders;
|
||||||
std::string orderBy;
|
std::string orderBy;
|
||||||
std::string orderByPredicate;
|
std::string orderByPredicate;
|
||||||
|
@ -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_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_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 =
|
static const std::string EXTENDED_INNER_JOIN =
|
||||||
"INNER JOIN ( "
|
"INNER JOIN ( "
|
||||||
@ -193,7 +193,7 @@ namespace musik { namespace core { namespace library { namespace query {
|
|||||||
and other supplementary information. */
|
and other supplementary information. */
|
||||||
|
|
||||||
static const std::string ALBUM_LIST_FILTER =
|
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 =
|
static const std::string ALBUM_LIST_QUERY =
|
||||||
"SELECT DISTINCT "
|
"SELECT DISTINCT "
|
||||||
|
@ -514,6 +514,7 @@
|
|||||||
<ClCompile Include="audio\Visualizer.cpp" />
|
<ClCompile Include="audio\Visualizer.cpp" />
|
||||||
<ClCompile Include="c_context.cpp" />
|
<ClCompile Include="c_context.cpp" />
|
||||||
<ClCompile Include="c_interface_wrappers.cpp" />
|
<ClCompile Include="c_interface_wrappers.cpp" />
|
||||||
|
<ClCompile Include="db\SqliteExtensions.cpp" />
|
||||||
<ClCompile Include="debug.cpp" />
|
<ClCompile Include="debug.cpp" />
|
||||||
<ClCompile Include="i18n\Locale.cpp" />
|
<ClCompile Include="i18n\Locale.cpp" />
|
||||||
<ClCompile Include="io\DataStreamFactory.cpp" />
|
<ClCompile Include="io\DataStreamFactory.cpp" />
|
||||||
@ -595,6 +596,7 @@
|
|||||||
<ClInclude Include="audio\MasterTransport.h" />
|
<ClInclude Include="audio\MasterTransport.h" />
|
||||||
<ClInclude Include="audio\Streams.h" />
|
<ClInclude Include="audio\Streams.h" />
|
||||||
<ClInclude Include="audio\Visualizer.h" />
|
<ClInclude Include="audio\Visualizer.h" />
|
||||||
|
<ClInclude Include="db\SqliteExtensions.h" />
|
||||||
<ClInclude Include="debug.h" />
|
<ClInclude Include="debug.h" />
|
||||||
<ClInclude Include="i18n\Locale.h" />
|
<ClInclude Include="i18n\Locale.h" />
|
||||||
<ClInclude Include="io\DataStreamFactory.h" />
|
<ClInclude Include="io\DataStreamFactory.h" />
|
||||||
|
@ -262,6 +262,9 @@
|
|||||||
<ClCompile Include="net\RawWebSocketClient.cpp">
|
<ClCompile Include="net\RawWebSocketClient.cpp">
|
||||||
<Filter>src\net</Filter>
|
<Filter>src\net</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="db\SqliteExtensions.cpp">
|
||||||
|
<Filter>src\db</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="pch.hpp">
|
<ClInclude Include="pch.hpp">
|
||||||
@ -642,5 +645,8 @@
|
|||||||
<ClInclude Include="net\RawWebSocketClient.h">
|
<ClInclude Include="net\RawWebSocketClient.h">
|
||||||
<Filter>src\net</Filter>
|
<Filter>src\net</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="db\SqliteExtensions.h">
|
||||||
|
<Filter>src\db</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
@ -37,10 +37,10 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#define VERSION_MAJOR 0
|
#define VERSION_MAJOR 0
|
||||||
#define VERSION_MINOR 94
|
#define VERSION_MINOR 95
|
||||||
#define VERSION_PATCH 0
|
#define VERSION_PATCH 0
|
||||||
#define VERSION_COMMIT_HASH "#d85c1002"
|
#define VERSION_COMMIT_HASH "#79637136"
|
||||||
#define VERSION "0.94.0"
|
#define VERSION "0.95.0"
|
||||||
|
|
||||||
namespace musik {
|
namespace musik {
|
||||||
namespace cube {
|
namespace cube {
|
||||||
|
@ -88,15 +88,21 @@ CategorySearchLayout::~CategorySearchLayout() {
|
|||||||
|
|
||||||
void CategorySearchLayout::LoadLastSession() {
|
void CategorySearchLayout::LoadLastSession() {
|
||||||
auto session = Preferences::ForComponent(components::Session);
|
auto session = Preferences::ForComponent(components::Session);
|
||||||
|
|
||||||
const std::string lastFilter = session->GetString(keys::LastCategoryFilter);
|
const std::string lastFilter = session->GetString(keys::LastCategoryFilter);
|
||||||
if (lastFilter.size()) {
|
if (lastFilter.size()) {
|
||||||
this->input->SetText(lastFilter);
|
this->input->SetText(lastFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this->matchType = static_cast<MatchType>(session->GetInt(
|
||||||
|
keys::LastCategoryFilterMatchType,
|
||||||
|
static_cast<int>(MatchType::Substring)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CategorySearchLayout::SaveSession() {
|
void CategorySearchLayout::SaveSession() {
|
||||||
auto session = Preferences::ForComponent(components::Session);
|
auto session = Preferences::ForComponent(components::Session);
|
||||||
session->SetString(keys::LastCategoryFilter, this->input->GetText().c_str());
|
session->SetString(keys::LastCategoryFilter, this->input->GetText().c_str());
|
||||||
|
session->SetInt(keys::LastCategoryFilterMatchType, static_cast<int>(this->matchType));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CategorySearchLayout::OnLayout() {
|
void CategorySearchLayout::OnLayout() {
|
||||||
@ -106,7 +112,10 @@ void CategorySearchLayout::OnLayout() {
|
|||||||
size_t inputWidth = cx / 2;
|
size_t inputWidth = cx / 2;
|
||||||
size_t inputX = x + ((cx - inputWidth) / 2);
|
size_t inputX = x + ((cx - inputWidth) / 2);
|
||||||
this->input->MoveAndResize(inputX, 0, cx / 2, SEARCH_HEIGHT);
|
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 labelY = SEARCH_HEIGHT;
|
||||||
size_t categoryWidth = cx / 3;
|
size_t categoryWidth = cx / 3;
|
||||||
@ -134,9 +143,9 @@ void CategorySearchLayout::InitializeWindows(musik::core::audio::PlaybackService
|
|||||||
|
|
||||||
void CategorySearchLayout::Requery() {
|
void CategorySearchLayout::Requery() {
|
||||||
const std::string& value = this->input->GetText();
|
const std::string& value = this->input->GetText();
|
||||||
this->albums->Requery(value);
|
this->albums->Requery(this->matchType, value);
|
||||||
this->artists->Requery(value);
|
this->artists->Requery(this->matchType, value);
|
||||||
this->genres->Requery(value);
|
this->genres->Requery(this->matchType, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CategorySearchLayout::FocusInput() {
|
void CategorySearchLayout::FocusInput() {
|
||||||
@ -198,6 +207,10 @@ bool CategorySearchLayout::KeyPress(const std::string& key) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (Hotkeys::Is(Hotkeys::SearchInputToggleMatchType, key)) {
|
||||||
|
this->ToggleMatchType();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return LayoutBase::KeyPress(key);
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -74,6 +74,8 @@ namespace musik {
|
|||||||
virtual void ProcessMessage(musik::core::runtime::IMessage &message);
|
virtual void ProcessMessage(musik::core::runtime::IMessage &message);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
using MatchType = musik::core::library::query::QueryBase::MatchType;
|
||||||
|
|
||||||
void InitializeWindows(musik::core::audio::PlaybackService& playback);
|
void InitializeWindows(musik::core::audio::PlaybackService& playback);
|
||||||
void OnCategoryEntryActivated(cursespp::ListWindow* sender, size_t index);
|
void OnCategoryEntryActivated(cursespp::ListWindow* sender, size_t index);
|
||||||
void Requery();
|
void Requery();
|
||||||
@ -85,7 +87,11 @@ namespace musik {
|
|||||||
cursespp::TextInput* sender,
|
cursespp::TextInput* sender,
|
||||||
std::string value);
|
std::string value);
|
||||||
|
|
||||||
|
void ToggleMatchType();
|
||||||
|
void SetMatchType(MatchType matchType);
|
||||||
|
|
||||||
musik::core::ILibraryPtr library;
|
musik::core::ILibraryPtr library;
|
||||||
|
MatchType matchType{ MatchType::Substring };
|
||||||
std::shared_ptr<musik::core::Preferences> prefs;
|
std::shared_ptr<musik::core::Preferences> prefs;
|
||||||
std::shared_ptr<CategoryListView> albums;
|
std::shared_ptr<CategoryListView> albums;
|
||||||
std::shared_ptr<CategoryListView> artists;
|
std::shared_ptr<CategoryListView> artists;
|
||||||
|
@ -86,15 +86,21 @@ TrackSearchLayout::~TrackSearchLayout() {
|
|||||||
|
|
||||||
void TrackSearchLayout::LoadLastSession() {
|
void TrackSearchLayout::LoadLastSession() {
|
||||||
auto session = Preferences::ForComponent(components::Session);
|
auto session = Preferences::ForComponent(components::Session);
|
||||||
|
|
||||||
const std::string lastFilter = session->GetString(keys::LastTrackFilter);
|
const std::string lastFilter = session->GetString(keys::LastTrackFilter);
|
||||||
if (lastFilter.size()) {
|
if (lastFilter.size()) {
|
||||||
this->input->SetText(lastFilter);
|
this->input->SetText(lastFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this->matchType = static_cast<MatchType>(session->GetInt(
|
||||||
|
keys::LastTrackFilterMatchType,
|
||||||
|
static_cast<int>(MatchType::Substring)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void TrackSearchLayout::SaveSession() {
|
void TrackSearchLayout::SaveSession() {
|
||||||
auto session = Preferences::ForComponent(components::Session);
|
auto session = Preferences::ForComponent(components::Session);
|
||||||
session->SetString(keys::LastTrackFilter, this->input->GetText().c_str());
|
session->SetString(keys::LastTrackFilter, this->input->GetText().c_str());
|
||||||
|
session->SetInt(keys::LastTrackFilterMatchType, static_cast<int>(this->matchType));
|
||||||
}
|
}
|
||||||
|
|
||||||
void TrackSearchLayout::OnLayout() {
|
void TrackSearchLayout::OnLayout() {
|
||||||
@ -104,7 +110,10 @@ void TrackSearchLayout::OnLayout() {
|
|||||||
size_t inputWidth = cx / 2;
|
size_t inputWidth = cx / 2;
|
||||||
size_t inputX = x + ((cx - inputWidth) / 2);
|
size_t inputX = x + ((cx - inputWidth) / 2);
|
||||||
this->input->MoveAndResize(inputX, y, cx / 2, SEARCH_HEIGHT);
|
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(
|
this->trackList->MoveAndResize(
|
||||||
x,
|
x,
|
||||||
@ -149,7 +158,8 @@ void TrackSearchLayout::Requery() {
|
|||||||
const std::string& filter = this->input->GetText();
|
const std::string& filter = this->input->GetText();
|
||||||
const TrackSortType sortOrder = getDefaultTrackSort(this->prefs);
|
const TrackSortType sortOrder = getDefaultTrackSort(this->prefs);
|
||||||
this->trackList->Requery(std::shared_ptr<TrackListQueryBase>(
|
this->trackList->Requery(std::shared_ptr<TrackListQueryBase>(
|
||||||
new SearchTrackListQuery(this->library, filter, sortOrder)));
|
new SearchTrackListQuery(
|
||||||
|
this->library, this->matchType, filter, sortOrder)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void TrackSearchLayout::PlayFromTop() {
|
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) {
|
bool TrackSearchLayout::KeyPress(const std::string& key) {
|
||||||
if (Hotkeys::Is(Hotkeys::Down, key)) {
|
if (Hotkeys::Is(Hotkeys::Down, key)) {
|
||||||
if (this->GetFocus() == this->input) {
|
if (this->GetFocus() == this->input) {
|
||||||
@ -210,5 +233,9 @@ bool TrackSearchLayout::KeyPress(const std::string& key) {
|
|||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
else if (Hotkeys::Is(Hotkeys::SearchInputToggleMatchType, key)) {
|
||||||
|
this->ToggleMatchType();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return LayoutBase::KeyPress(key);
|
return LayoutBase::KeyPress(key);
|
||||||
}
|
}
|
||||||
|
@ -70,6 +70,8 @@ namespace musik {
|
|||||||
virtual void OnLayout();
|
virtual void OnLayout();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
using MatchType = musik::core::library::query::QueryBase::MatchType;
|
||||||
|
|
||||||
void SaveSession();
|
void SaveSession();
|
||||||
void InitializeWindows();
|
void InitializeWindows();
|
||||||
void Requery();
|
void Requery();
|
||||||
@ -82,8 +84,12 @@ namespace musik {
|
|||||||
|
|
||||||
void OnEnterPressed(cursespp::TextInput* sender);
|
void OnEnterPressed(cursespp::TextInput* sender);
|
||||||
|
|
||||||
|
void ToggleMatchType();
|
||||||
|
void SetMatchType(MatchType matchType);
|
||||||
|
|
||||||
musik::core::audio::PlaybackService& playback;
|
musik::core::audio::PlaybackService& playback;
|
||||||
musik::core::ILibraryPtr library;
|
musik::core::ILibraryPtr library;
|
||||||
|
MatchType matchType{ MatchType::Substring };
|
||||||
std::shared_ptr<musik::core::Preferences> prefs;
|
std::shared_ptr<musik::core::Preferences> prefs;
|
||||||
std::shared_ptr<TrackListView> trackList;
|
std::shared_ptr<TrackListView> trackList;
|
||||||
std::shared_ptr<cursespp::TextInput> input;
|
std::shared_ptr<cursespp::TextInput> input;
|
||||||
|
@ -151,8 +151,8 @@ static void queryPlaylists(
|
|||||||
ILibraryPtr library,
|
ILibraryPtr library,
|
||||||
std::function<void(std::shared_ptr<CategoryListQuery>)> callback)
|
std::function<void(std::shared_ptr<CategoryListQuery>)> callback)
|
||||||
{
|
{
|
||||||
std::shared_ptr<CategoryListQuery> query(
|
std::shared_ptr<CategoryListQuery> query(new CategoryListQuery(
|
||||||
new CategoryListQuery(Playlists::TABLE_NAME, ""));
|
CategoryListQuery::MatchType::Substring, Playlists::TABLE_NAME, ""));
|
||||||
|
|
||||||
library->Enqueue(query, [callback, query](auto q) {
|
library->Enqueue(query, [callback, query](auto q) {
|
||||||
callback(query->GetStatus() == IQuery::Finished
|
callback(query->GetStatus() == IQuery::Finished
|
||||||
|
@ -104,6 +104,8 @@ static std::unordered_map<std::string, Id> NAME_TO_ID = {
|
|||||||
{ "track_list_previous_group", Id::TrackListPreviousGroup },
|
{ "track_list_previous_group", Id::TrackListPreviousGroup },
|
||||||
{ "track_list_play_from_top", Id::TrackListPlayFromTop },
|
{ "track_list_play_from_top", Id::TrackListPlayFromTop },
|
||||||
|
|
||||||
|
{ "search_input_toggle_match_type", Id::SearchInputToggleMatchType },
|
||||||
|
|
||||||
{ "lyrics_retry", Id::LyricsRetry },
|
{ "lyrics_retry", Id::LyricsRetry },
|
||||||
|
|
||||||
{ "playback_toggle_mute", Id::ToggleMute },
|
{ "playback_toggle_mute", Id::ToggleMute },
|
||||||
@ -196,6 +198,8 @@ static std::unordered_map<Id, std::string, EnumHasher> ID_TO_DEFAULT = {
|
|||||||
{ Id::TrackListPreviousGroup, "[" },
|
{ Id::TrackListPreviousGroup, "[" },
|
||||||
{ Id::TrackListPlayFromTop, "M-P" },
|
{ Id::TrackListPlayFromTop, "M-P" },
|
||||||
|
|
||||||
|
{ Id::SearchInputToggleMatchType, "M-m" },
|
||||||
|
|
||||||
{ Id::LyricsRetry, "r" },
|
{ Id::LyricsRetry, "r" },
|
||||||
|
|
||||||
{ Id::ToggleMute, "m" },
|
{ Id::ToggleMute, "m" },
|
||||||
|
@ -115,6 +115,9 @@ namespace musik {
|
|||||||
TrackListPreviousGroup,
|
TrackListPreviousGroup,
|
||||||
TrackListPlayFromTop,
|
TrackListPlayFromTop,
|
||||||
|
|
||||||
|
/* search input */
|
||||||
|
SearchInputToggleMatchType,
|
||||||
|
|
||||||
/* lyrics */
|
/* lyrics */
|
||||||
LyricsRetry,
|
LyricsRetry,
|
||||||
|
|
||||||
|
@ -51,7 +51,9 @@ namespace musik { namespace cube { namespace prefs {
|
|||||||
const std::string keys::LastBrowseCategoryId = "LastBrowseCategoryId";
|
const std::string keys::LastBrowseCategoryId = "LastBrowseCategoryId";
|
||||||
const std::string keys::LastBrowseDirectoryRoot = "LastBrowseDirectoryRoot";
|
const std::string keys::LastBrowseDirectoryRoot = "LastBrowseDirectoryRoot";
|
||||||
const std::string keys::LastCategoryFilter = "LastCategoryFilter";
|
const std::string keys::LastCategoryFilter = "LastCategoryFilter";
|
||||||
|
const std::string keys::LastCategoryFilterMatchType = "LastCategoryFilterMatchType";
|
||||||
const std::string keys::LastTrackFilter = "LastTrackFilter";
|
const std::string keys::LastTrackFilter = "LastTrackFilter";
|
||||||
|
const std::string keys::LastTrackFilterMatchType = "LastTrackFilterMatchType";
|
||||||
const std::string keys::TrackSearchSortOrder = "TrackSearchSortOrder";
|
const std::string keys::TrackSearchSortOrder = "TrackSearchSortOrder";
|
||||||
const std::string keys::CategoryTrackListSortOrder = "CategoryTrackListSortOrder";
|
const std::string keys::CategoryTrackListSortOrder = "CategoryTrackListSortOrder";
|
||||||
const std::string keys::RatingPositiveChar = "RatingPositiveChar";
|
const std::string keys::RatingPositiveChar = "RatingPositiveChar";
|
||||||
|
@ -53,7 +53,9 @@ namespace musik { namespace cube { namespace prefs {
|
|||||||
extern const std::string LastBrowseCategoryId;
|
extern const std::string LastBrowseCategoryId;
|
||||||
extern const std::string LastBrowseDirectoryRoot;
|
extern const std::string LastBrowseDirectoryRoot;
|
||||||
extern const std::string LastCategoryFilter;
|
extern const std::string LastCategoryFilter;
|
||||||
|
extern const std::string LastCategoryFilterMatchType;
|
||||||
extern const std::string LastTrackFilter;
|
extern const std::string LastTrackFilter;
|
||||||
|
extern const std::string LastTrackFilterMatchType;
|
||||||
extern const std::string TrackSearchSortOrder;
|
extern const std::string TrackSearchSortOrder;
|
||||||
extern const std::string CategoryTrackListSortOrder;
|
extern const std::string CategoryTrackListSortOrder;
|
||||||
extern const std::string RatingPositiveChar;
|
extern const std::string RatingPositiveChar;
|
||||||
|
@ -97,6 +97,7 @@ void CategoryListView::OnVisibilityChanged(bool visible) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CategoryListView::RequeryWithField(
|
void CategoryListView::RequeryWithField(
|
||||||
|
MatchType matchType,
|
||||||
const std::string& fieldName,
|
const std::string& fieldName,
|
||||||
const std::string& filter,
|
const std::string& filter,
|
||||||
const int64_t selectAfterQuery)
|
const int64_t selectAfterQuery)
|
||||||
@ -105,24 +106,45 @@ void CategoryListView::RequeryWithField(
|
|||||||
this->activeQuery->Cancel();
|
this->activeQuery->Cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this->matchType = matchType;
|
||||||
this->fieldName = fieldName;
|
this->fieldName = fieldName;
|
||||||
this->fieldIdColumn = getFieldIdColumn(fieldName);
|
this->fieldIdColumn = getFieldIdColumn(fieldName);
|
||||||
this->selectAfterQuery = selectAfterQuery;
|
this->selectAfterQuery = selectAfterQuery;
|
||||||
this->filter = filter;
|
this->filter = filter;
|
||||||
this->activeQuery.reset(new CategoryListQuery(fieldName, filter));
|
this->activeQuery = std::make_shared<CategoryListQuery>(matchType, fieldName, filter);
|
||||||
this->library->Enqueue(activeQuery);
|
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) {
|
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) {
|
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() {
|
void CategoryListView::Reset() {
|
||||||
this->metadata.reset(new SdkValueList()); /* ugh */
|
this->metadata = std::make_shared<SdkValueList>(); /* ugh */
|
||||||
this->OnAdapterChanged();
|
this->OnAdapterChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,7 +188,7 @@ void CategoryListView::SetFieldName(const std::string& fieldName) {
|
|||||||
this->metadata.reset();
|
this->metadata.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
this->Requery();
|
this->Requery(this->matchType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,6 +55,8 @@ namespace musik {
|
|||||||
public sigslot::has_slots<>
|
public sigslot::has_slots<>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
using MatchType = musik::core::library::query::QueryBase::MatchType;
|
||||||
|
|
||||||
CategoryListView(
|
CategoryListView(
|
||||||
musik::core::audio::PlaybackService& playback,
|
musik::core::audio::PlaybackService& playback,
|
||||||
musik::core::ILibraryPtr library,
|
musik::core::ILibraryPtr library,
|
||||||
@ -62,17 +64,30 @@ namespace musik {
|
|||||||
|
|
||||||
virtual ~CategoryListView();
|
virtual ~CategoryListView();
|
||||||
|
|
||||||
|
void RequeryWithField(
|
||||||
|
MatchType matchType,
|
||||||
|
const std::string& fieldName,
|
||||||
|
const std::string& filter = "",
|
||||||
|
const int64_t selectAfterQuery = -1LL);
|
||||||
|
|
||||||
void RequeryWithField(
|
void RequeryWithField(
|
||||||
const std::string& fieldName,
|
const std::string& fieldName,
|
||||||
const std::string& filter = "",
|
const std::string& filter = "",
|
||||||
const int64_t selectAfterQuery = -1LL);
|
const int64_t selectAfterQuery = -1LL);
|
||||||
|
|
||||||
void Requery(
|
void Requery(
|
||||||
|
MatchType matchType,
|
||||||
const std::string& filter = "",
|
const std::string& filter = "",
|
||||||
const int64_t selectAfterQuery = -1LL);
|
const int64_t selectAfterQuery = -1LL);
|
||||||
|
|
||||||
|
void Requery(
|
||||||
|
const std::string& filter,
|
||||||
|
const int64_t selectAfterQuery);
|
||||||
|
|
||||||
void Requery(const int64_t selectAfterQuery);
|
void Requery(const int64_t selectAfterQuery);
|
||||||
|
|
||||||
|
void Requery();
|
||||||
|
|
||||||
void Reset();
|
void Reset();
|
||||||
|
|
||||||
int64_t GetSelectedId();
|
int64_t GetSelectedId();
|
||||||
@ -121,6 +136,7 @@ namespace musik {
|
|||||||
|
|
||||||
std::string fieldName, fieldIdColumn;
|
std::string fieldName, fieldIdColumn;
|
||||||
std::string filter;
|
std::string filter;
|
||||||
|
MatchType matchType{ MatchType::Substring };
|
||||||
int64_t selectAfterQuery;
|
int64_t selectAfterQuery;
|
||||||
musik::core::library::query::CategoryListQuery::Result metadata;
|
musik::core::library::query::CategoryListQuery::Result metadata;
|
||||||
};
|
};
|
||||||
|
@ -51,48 +51,50 @@ using namespace boost::filesystem;
|
|||||||
indicies we'll use to store them */
|
indicies we'll use to store them */
|
||||||
#define THEME_COLOR_BACKGROUND 16
|
#define THEME_COLOR_BACKGROUND 16
|
||||||
#define THEME_COLOR_FOREGROUND 17
|
#define THEME_COLOR_FOREGROUND 17
|
||||||
#define THEME_COLOR_FOCUSED_BORDER 18
|
#define THEME_COLOR_FRAME_FOCUSED 18
|
||||||
#define THEME_COLOR_TEXT_FOCUSED 19
|
#define THEME_COLOR_FRAME_IMPORTANT 19
|
||||||
#define THEME_COLOR_TEXT_ACTIVE 20
|
#define THEME_COLOR_TEXT_FOCUSED 20
|
||||||
#define THEME_COLOR_TEXT_DISABLED 21
|
#define THEME_COLOR_TEXT_ACTIVE 21
|
||||||
#define THEME_COLOR_TEXT_HIDDEN 22
|
#define THEME_COLOR_TEXT_DISABLED 22
|
||||||
#define THEME_COLOR_TEXT_WARNING 23
|
#define THEME_COLOR_TEXT_HIDDEN 23
|
||||||
#define THEME_COLOR_TEXT_ERROR 24
|
#define THEME_COLOR_TEXT_WARNING 24
|
||||||
#define THEME_COLOR_OVERLAY_BACKGROUND 25
|
#define THEME_COLOR_TEXT_ERROR 25
|
||||||
#define THEME_COLOR_OVERLAY_FOREGROUND 26
|
#define THEME_COLOR_OVERLAY_BACKGROUND 26
|
||||||
#define THEME_COLOR_OVERLAY_BORDER 27
|
#define THEME_COLOR_OVERLAY_FOREGROUND 27
|
||||||
#define THEME_COLOR_OVERLAY_FOCUSED_BORDER 28
|
#define THEME_COLOR_OVERLAY_BORDER 28
|
||||||
#define THEME_COLOR_OVERLAY_FOCUSED_TEXT 29
|
#define THEME_COLOR_OVERLAY_FOCUSED_FRAME 29
|
||||||
#define THEME_COLOR_SHORTCUTS_BACKGROUND 30
|
#define THEME_COLOR_OVERLAY_FOCUSED_TEXT 30
|
||||||
#define THEME_COLOR_SHORTCUTS_FOREGROUND 31
|
#define THEME_COLOR_SHORTCUTS_BACKGROUND 31
|
||||||
#define THEME_COLOR_SHORTCUTS_BACKGROUND_FOCUSED 32
|
#define THEME_COLOR_SHORTCUTS_FOREGROUND 32
|
||||||
#define THEME_COLOR_SHORTCUTS_FOREGROUND_FOCUSED 33
|
#define THEME_COLOR_SHORTCUTS_BACKGROUND_FOCUSED 33
|
||||||
#define THEME_COLOR_BUTTON_BACKGROUND_NORMAL 34
|
#define THEME_COLOR_SHORTCUTS_FOREGROUND_FOCUSED 34
|
||||||
#define THEME_COLOR_BUTTON_FOREGROUND_NORMAL 35
|
#define THEME_COLOR_BUTTON_BACKGROUND_NORMAL 35
|
||||||
#define THEME_COLOR_BUTTON_BACKGROUND_ACTIVE 36
|
#define THEME_COLOR_BUTTON_FOREGROUND_NORMAL 36
|
||||||
#define THEME_COLOR_BUTTON_FOREGROUND_ACTIVE 37
|
#define THEME_COLOR_BUTTON_BACKGROUND_ACTIVE 37
|
||||||
#define THEME_COLOR_BANNER_BACKGROUND 38
|
#define THEME_COLOR_BUTTON_FOREGROUND_ACTIVE 38
|
||||||
#define THEME_COLOR_BANNER_FOREGROUND 39
|
#define THEME_COLOR_BANNER_BACKGROUND 39
|
||||||
#define THEME_COLOR_LIST_HEADER_BACKGROUND 40
|
#define THEME_COLOR_BANNER_FOREGROUND 40
|
||||||
#define THEME_COLOR_LIST_HEADER_FOREGROUND 41
|
#define THEME_COLOR_LIST_HEADER_BACKGROUND 41
|
||||||
#define THEME_COLOR_LIST_HEADER_HIGHLIGHTED_BACKGROUND 42
|
#define THEME_COLOR_LIST_HEADER_FOREGROUND 42
|
||||||
#define THEME_COLOR_LIST_HEADER_HIGHLIGHTED_FOREGROUND 43
|
#define THEME_COLOR_LIST_HEADER_HIGHLIGHTED_BACKGROUND 43
|
||||||
#define THEME_COLOR_LIST_ITEM_HIGHLIGHTED_BACKGROUND 44
|
#define THEME_COLOR_LIST_HEADER_HIGHLIGHTED_FOREGROUND 44
|
||||||
#define THEME_COLOR_LIST_ITEM_HIGHLIGHTED_FOREGROUND 45
|
#define THEME_COLOR_LIST_ITEM_HIGHLIGHTED_BACKGROUND 45
|
||||||
#define THEME_COLOR_LIST_ITEM_ACTIVE_BACKGROUND 46
|
#define THEME_COLOR_LIST_ITEM_HIGHLIGHTED_FOREGROUND 46
|
||||||
#define THEME_COLOR_LIST_ITEM_ACTIVE_FOREGROUND 47
|
#define THEME_COLOR_LIST_ITEM_ACTIVE_BACKGROUND 47
|
||||||
#define THEME_COLOR_LIST_ITEM_ACTIVE_HIGHLIGHTED_BACKGROUND 48
|
#define THEME_COLOR_LIST_ITEM_ACTIVE_FOREGROUND 48
|
||||||
#define THEME_COLOR_LIST_ITEM_ACTIVE_HIGHLIGHTED_FOREGROUND 49
|
#define THEME_COLOR_LIST_ITEM_ACTIVE_HIGHLIGHTED_BACKGROUND 49
|
||||||
#define THEME_COLOR_FOOTER_BACKGROUND 50
|
#define THEME_COLOR_LIST_ITEM_ACTIVE_HIGHLIGHTED_FOREGROUND 50
|
||||||
#define THEME_COLOR_FOOTER_FOREGROUND 51
|
#define THEME_COLOR_FOOTER_BACKGROUND 51
|
||||||
#define THEME_COLOR_HEADER_BACKGROUND 52
|
#define THEME_COLOR_FOOTER_FOREGROUND 52
|
||||||
#define THEME_COLOR_HEADER_FOREGROUND 53
|
#define THEME_COLOR_HEADER_BACKGROUND 53
|
||||||
|
#define THEME_COLOR_HEADER_FOREGROUND 54
|
||||||
|
|
||||||
/* user-readable names for the color identifiers above. these are
|
/* user-readable names for the color identifiers above. these are
|
||||||
used as key names in the config files */
|
used as key names in the config files */
|
||||||
#define JSON_KEY_COLOR_BACKGROUND "background"
|
#define JSON_KEY_COLOR_BACKGROUND "background"
|
||||||
#define JSON_KEY_COLOR_FOREGROUND "foreground"
|
#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_FOCUSED "text_focused"
|
||||||
#define JSON_KEY_COLOR_TEXT_ACTIVE "text_active"
|
#define JSON_KEY_COLOR_TEXT_ACTIVE "text_active"
|
||||||
#define JSON_KEY_COLOR_TEXT_DISABLED "text_disabled"
|
#define JSON_KEY_COLOR_TEXT_DISABLED "text_disabled"
|
||||||
@ -222,7 +224,8 @@ struct Theme {
|
|||||||
/* main */
|
/* main */
|
||||||
background.Set(THEME_COLOR_BACKGROUND, 24, 24, 20, -1);
|
background.Set(THEME_COLOR_BACKGROUND, 24, 24, 20, -1);
|
||||||
foreground.Set(THEME_COLOR_FOREGROUND, 220, 220, 220, COLOR_256_OFFWHITE);
|
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 */
|
/* text */
|
||||||
textFocused.Set(THEME_COLOR_TEXT_FOCUSED, 220, 82, 86, COLOR_256_RED);
|
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);
|
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);
|
overlayForeground.Set(THEME_COLOR_OVERLAY_FOREGROUND, 220, 220, 220, COLOR_256_OFFWHITE);
|
||||||
overlayBorder.Set(THEME_COLOR_OVERLAY_BORDER, 102, 217, 238, COLOR_256_BLUE);
|
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);
|
overlayFocusedText.Set(THEME_COLOR_OVERLAY_FOCUSED_TEXT, 220, 82, 86, COLOR_256_RED);
|
||||||
|
|
||||||
/* shortcut bar */
|
/* shortcut bar */
|
||||||
@ -299,7 +302,7 @@ struct Theme {
|
|||||||
/* actually read the theme values! */
|
/* actually read the theme values! */
|
||||||
this->background.Set(colors.value(JSON_KEY_COLOR_BACKGROUND, unset));
|
this->background.Set(colors.value(JSON_KEY_COLOR_BACKGROUND, unset));
|
||||||
this->foreground.Set(colors.value(JSON_KEY_COLOR_FOREGROUND, 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->textFocused.Set(colors.value(JSON_KEY_COLOR_TEXT_FOCUSED, unset));
|
||||||
this->textActive.Set(colors.value(JSON_KEY_COLOR_TEXT_ACTIVE, unset));
|
this->textActive.Set(colors.value(JSON_KEY_COLOR_TEXT_ACTIVE, unset));
|
||||||
this->textDisabled.Set(colors.value(JSON_KEY_COLOR_TEXT_DISABLED, 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->overlayBackground.Set(colors.value(JSON_KEY_COLOR_OVERLAY_BACKGROUND, unset));
|
||||||
this->overlayForeground.Set(colors.value(JSON_KEY_COLOR_OVERLAY_FOREGROUND, unset));
|
this->overlayForeground.Set(colors.value(JSON_KEY_COLOR_OVERLAY_FOREGROUND, unset));
|
||||||
this->overlayBorder.Set(colors.value(JSON_KEY_COLOR_OVERLAY_BORDER, 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->overlayFocusedText.Set(colors.value(JSON_KEY_COLOR_OVERLAY_FOCUSED_TEXT, unset));
|
||||||
this->shortcutsBackground.Set(colors.value(JSON_KEY_COLOR_SHORTCUTS_BACKGROUND, unset));
|
this->shortcutsBackground.Set(colors.value(JSON_KEY_COLOR_SHORTCUTS_BACKGROUND, unset));
|
||||||
this->shortcutsForeground.Set(colors.value(JSON_KEY_COLOR_SHORTCUTS_FOREGROUND, 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->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));
|
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->fn = fn;
|
||||||
this->name = data.value("name", "unnamed");
|
this->name = data.value("name", "unnamed");
|
||||||
|
|
||||||
@ -362,8 +378,9 @@ struct Theme {
|
|||||||
|
|
||||||
/* main */
|
/* main */
|
||||||
init_pair(Color::ContentColorDefault, foregroundId, backgroundId);
|
init_pair(Color::ContentColorDefault, foregroundId, backgroundId);
|
||||||
init_pair(Color::FrameColorDefault, foregroundId, backgroundId);
|
init_pair(Color::FrameDefault, foregroundId, backgroundId);
|
||||||
init_pair(Color::FrameColorFocused, focusedBorder.Id(mode, COLOR_RED),backgroundId);
|
init_pair(Color::FrameFocused, focusedFrame.Id(mode, COLOR_RED),backgroundId);
|
||||||
|
init_pair(Color::FrameImportant, importantFrame.Id(mode, COLOR_BLUE), backgroundId);
|
||||||
|
|
||||||
/* text */
|
/* text */
|
||||||
init_pair(Color::TextDefault, foregroundId, backgroundId);
|
init_pair(Color::TextDefault, foregroundId, backgroundId);
|
||||||
@ -378,10 +395,10 @@ struct Theme {
|
|||||||
int overlayBgId = overlayBackground.Id(mode, -1);
|
int overlayBgId = overlayBackground.Id(mode, -1);
|
||||||
init_pair(Color::OverlayFrame, overlayBorder.Id(mode, COLOR_BLUE), overlayBgId);
|
init_pair(Color::OverlayFrame, overlayBorder.Id(mode, COLOR_BLUE), overlayBgId);
|
||||||
init_pair(Color::OverlayContent, overlayForeground.Id(mode, -1), 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::OverlayTextFocused, overlayFocusedText.Id(mode, COLOR_RED), overlayBgId);
|
||||||
init_pair(Color::OverlayListFrame, foregroundId, 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 */
|
/* shortcuts */
|
||||||
init_pair(
|
init_pair(
|
||||||
@ -461,7 +478,8 @@ struct Theme {
|
|||||||
/* main */
|
/* main */
|
||||||
ThemeColor background;
|
ThemeColor background;
|
||||||
ThemeColor foreground;
|
ThemeColor foreground;
|
||||||
ThemeColor focusedBorder;
|
ThemeColor focusedFrame;
|
||||||
|
ThemeColor importantFrame;
|
||||||
|
|
||||||
/* text */
|
/* text */
|
||||||
ThemeColor textFocused;
|
ThemeColor textFocused;
|
||||||
@ -475,7 +493,7 @@ struct Theme {
|
|||||||
ThemeColor overlayBackground;
|
ThemeColor overlayBackground;
|
||||||
ThemeColor overlayForeground;
|
ThemeColor overlayForeground;
|
||||||
ThemeColor overlayBorder;
|
ThemeColor overlayBorder;
|
||||||
ThemeColor overlayFocusedBorder;
|
ThemeColor overlayFocusedFrame;
|
||||||
ThemeColor overlayFocusedText;
|
ThemeColor overlayFocusedText;
|
||||||
|
|
||||||
/* shortcut bar */
|
/* shortcut bar */
|
||||||
|
@ -168,9 +168,9 @@ Window::Window(IWindow *parent) {
|
|||||||
this->lastAbsoluteX = 0;
|
this->lastAbsoluteX = 0;
|
||||||
this->lastAbsoluteY = 0;
|
this->lastAbsoluteY = 0;
|
||||||
this->contentColor = Color(Color::ContentColorDefault);
|
this->contentColor = Color(Color::ContentColorDefault);
|
||||||
this->frameColor = Color(Color::FrameColorDefault);
|
this->frameColor = Color(Color::FrameDefault);
|
||||||
this->focusedContentColor = Color(Color::ContentColorDefault);
|
this->focusedContentColor = Color(Color::ContentColorDefault);
|
||||||
this->focusedFrameColor = Color(Color::FrameColorFocused);
|
this->focusedFrameColor = Color(Color::FrameFocused);
|
||||||
this->drawFrame = true;
|
this->drawFrame = true;
|
||||||
this->isVisibleInParent = false;
|
this->isVisibleInParent = false;
|
||||||
this->isDirty = true;
|
this->isDirty = true;
|
||||||
@ -484,7 +484,7 @@ void Window::SetFrameColor(Color color) {
|
|||||||
ASSERT_MAIN_THREAD();
|
ASSERT_MAIN_THREAD();
|
||||||
|
|
||||||
this->frameColor = (color == Color::Default)
|
this->frameColor = (color == Color::Default)
|
||||||
? Color::FrameColorDefault : color;
|
? Color::FrameDefault : color;
|
||||||
|
|
||||||
this->RepaintBackground();
|
this->RepaintBackground();
|
||||||
this->Redraw();
|
this->Redraw();
|
||||||
@ -494,7 +494,7 @@ void Window::SetFocusedFrameColor(Color color) {
|
|||||||
ASSERT_MAIN_THREAD();
|
ASSERT_MAIN_THREAD();
|
||||||
|
|
||||||
this->focusedFrameColor = (color == Color::Default)
|
this->focusedFrameColor = (color == Color::Default)
|
||||||
? Color::FrameColorFocused : color;
|
? Color::FrameFocused : color;
|
||||||
|
|
||||||
this->RepaintBackground();
|
this->RepaintBackground();
|
||||||
this->Redraw();
|
this->Redraw();
|
||||||
|
@ -52,33 +52,34 @@ namespace cursespp {
|
|||||||
ListItemHeaderHighlighted = 6,
|
ListItemHeaderHighlighted = 6,
|
||||||
|
|
||||||
ContentColorDefault = 7,
|
ContentColorDefault = 7,
|
||||||
FrameColorDefault = 8,
|
FrameDefault = 8,
|
||||||
FrameColorFocused = 9,
|
FrameFocused = 9,
|
||||||
|
FrameImportant = 10,
|
||||||
|
|
||||||
TextDefault = 10,
|
TextDefault = 11,
|
||||||
TextDisabled = 11,
|
TextDisabled = 12,
|
||||||
TextFocused = 12,
|
TextFocused = 13,
|
||||||
TextActive = 13,
|
TextActive = 14,
|
||||||
TextWarning = 14,
|
TextWarning = 15,
|
||||||
TextError = 15,
|
TextError = 16,
|
||||||
TextHidden = 16,
|
TextHidden = 17,
|
||||||
|
|
||||||
ButtonDefault = 17,
|
ButtonDefault = 18,
|
||||||
ButtonHighlighted = 18,
|
ButtonHighlighted = 19,
|
||||||
|
|
||||||
ShortcutRowDefault = 19,
|
ShortcutRowDefault = 20,
|
||||||
ShortcutRowFocused = 20,
|
ShortcutRowFocused = 21,
|
||||||
|
|
||||||
OverlayFrame = 21,
|
OverlayFrame = 22,
|
||||||
OverlayContent = 22,
|
OverlayContent = 23,
|
||||||
OverlayTextInputFrame = 23,
|
OverlayTextInputFrame = 24,
|
||||||
OverlayTextFocused = 24,
|
OverlayTextFocused = 25,
|
||||||
OverlayListFrame = 25,
|
OverlayListFrame = 26,
|
||||||
OverlayListFrameFocused = 26,
|
OverlayListFrameFocused = 27,
|
||||||
|
|
||||||
Header = 27,
|
Header = 28,
|
||||||
Footer = 28,
|
Footer = 29,
|
||||||
Banner = 29
|
Banner = 30
|
||||||
};
|
};
|
||||||
|
|
||||||
Color() {
|
Color() {
|
||||||
|
@ -37,6 +37,7 @@
|
|||||||
"browse_no_subdirectories_toast": "there are no more sub-directories.\n\npress '%s' to play the selection.",
|
"browse_no_subdirectories_toast": "there are no more sub-directories.\n\npress '%s' to play the selection.",
|
||||||
|
|
||||||
"search_filter_hint": "search",
|
"search_filter_hint": "search",
|
||||||
|
"search_regex_hint": "regex",
|
||||||
|
|
||||||
"console_list_title": "debug logs",
|
"console_list_title": "debug logs",
|
||||||
"console_version": "musikcube version %s",
|
"console_version": "musikcube version %s",
|
||||||
|
@ -10,10 +10,14 @@
|
|||||||
"hex": "#fbf1c7",
|
"hex": "#fbf1c7",
|
||||||
"palette": 229
|
"palette": 229
|
||||||
},
|
},
|
||||||
"focused_border": {
|
"frame_focused": {
|
||||||
"hex": "#fb4934",
|
"hex": "#fb4934",
|
||||||
"palette": 167
|
"palette": 167
|
||||||
},
|
},
|
||||||
|
"frame_important": {
|
||||||
|
"hex": "#458588",
|
||||||
|
"palette": 66
|
||||||
|
},
|
||||||
"text_focused": {
|
"text_focused": {
|
||||||
"hex": "#fb4934",
|
"hex": "#fb4934",
|
||||||
"palette": 167
|
"palette": 167
|
||||||
|
@ -10,10 +10,14 @@
|
|||||||
"hex": "#eee8d5",
|
"hex": "#eee8d5",
|
||||||
"palette": 254
|
"palette": 254
|
||||||
},
|
},
|
||||||
"focused_border": {
|
"frame_focused": {
|
||||||
"hex": "#dc322f",
|
"hex": "#dc322f",
|
||||||
"palette": 160
|
"palette": 160
|
||||||
},
|
},
|
||||||
|
"frame_important": {
|
||||||
|
"hex": "#268bd2",
|
||||||
|
"palette": 33
|
||||||
|
},
|
||||||
"text_focused": {
|
"text_focused": {
|
||||||
"hex": "#dc322f",
|
"hex": "#dc322f",
|
||||||
"palette": 160
|
"palette": 160
|
||||||
|
@ -10,10 +10,14 @@
|
|||||||
"hex": "#073642",
|
"hex": "#073642",
|
||||||
"palette": 235
|
"palette": 235
|
||||||
},
|
},
|
||||||
"focused_border": {
|
"frame_focused": {
|
||||||
"hex": "#dc322f",
|
"hex": "#dc322f",
|
||||||
"palette": 160
|
"palette": 160
|
||||||
},
|
},
|
||||||
|
"frame_important": {
|
||||||
|
"hex": "#268bd2",
|
||||||
|
"palette": 33
|
||||||
|
},
|
||||||
"text_focused": {
|
"text_focused": {
|
||||||
"hex": "#dc322f",
|
"hex": "#dc322f",
|
||||||
"palette": 160
|
"palette": 160
|
||||||
|
Binary file not shown.
Loading…
Reference in New Issue
Block a user