pax_global_header00006660000000000000000000000064122460351130014507gustar00rootroot0000000000000052 comment=67e6d9d27efa18baae6e62263f074f7959e751b3 kid3-3.0.2/000077500000000000000000000000001224603511300123435ustar00rootroot00000000000000kid3-3.0.2/AUTHORS000066400000000000000000000000551224603511300134130ustar00rootroot00000000000000Urs Fleisch kid3-3.0.2/CMakeLists.txt000066400000000000000000000373241224603511300151140ustar00rootroot00000000000000# Build Kid3 using CMake # # Linux: # cmake -D CMAKE_BUILD_TYPE=Release -D WITH_APPS=Qt -D CMAKE_INSTALL_PREFIX=/usr ../kid3 # make # make install/strip DESTDIR=$(pwd)/inst # or # cpack # # Windows: # set INCLUDE=%MSYSDIR%\local\include # set LIB=%MSYSDIR%\local\lib # cmake -G "MinGW Makefiles" -D CMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX= ..\kid3 # mingw32-make # cpack # # Mac OS X: # cmake -G "Unix Makefiles" -D CMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX= ../kid3 # make # cpack project(kid3) cmake_minimum_required(VERSION 2.8) set(BUILD_SHARED_LIBS ON CACHE BOOL "build shared libraries") set(WITH_QT4 OFF CACHE BOOL "force use of Qt4") set(WITH_QT5 OFF CACHE BOOL "force use of Qt5") set(WITH_TAGLIB ON CACHE BOOL "build with TagLib") set(WITH_MP4V2 OFF CACHE BOOL "build with mp4v2") set(WITH_ID3LIB ON CACHE BOOL "build with id3lib") set(WITH_VORBIS ON CACHE BOOL "build with Ogg/Vorbis") set(WITH_FLAC ON CACHE BOOL "build with FLAC") set(WITH_CHROMAPRINT ON CACHE BOOL "build with Chromaprint") set(WITH_FFMPEG OFF CACHE BOOL "force use of FFmpeg for Chromaprint decoding") set(WITH_CHROMAPRINT_FFMPEG OFF CACHE BOOL "link FFmpeg only for Chromaprint FFT") set(WITH_GSTREAMER OFF CACHE BOOL "force use of GStreamer for Chromaprint decoding") set(WITH_QAUDIODECODER OFF CACHE BOOL "force use of QAudioDecoder for Chromaprint decoding") set(WITH_PHONON ON CACHE BOOL "build with Phonon") set(WITH_GCC_PCH OFF CACHE BOOL "enable precompiled headers") if (APPLE OR WIN32) set(WITH_DBUS OFF CACHE BOOL "build with QtDBus") set(WITH_APPS "Qt;CLI" CACHE STRING "build applications (Qt;CLI;Test)") else (APPLE OR WIN32) set(WITH_DBUS ON CACHE BOOL "build with QtDBus") set(WITH_APPS "Qt;CLI;KDE" CACHE STRING "build applications (Qt;CLI;KDE;Test)") endif (APPLE OR WIN32) if (WIN32) set(WITH_READLINE OFF CACHE BOOL "build with readline") else (WIN32) set(WITH_READLINE ON CACHE BOOL "build with readline") endif (WIN32) if (DEFINED WITH_KDE) unset(WITH_KDE CACHE) message(FATAL_ERROR "The variable WITH_KDE is no longer used. The KDE and Qt " "applications can now be built together using common " "libraries. Use WITH_APPS with a value of \"KDE\" for " "the KDE application, \"Qt\" for the Qt application, " "\"CLI\" for the command line application or " "\"Qt;CLI;KDE\" for all. Default is -DWITH_APPS=" "\"${WITH_APPS}\".") endif (DEFINED WITH_KDE) if (WITH_APPS) string(TOLOWER "${WITH_APPS}" _apps_lower) foreach (_app ${_apps_lower}) if (_app STREQUAL "qt") set(BUILD_QT_APP ON) elseif (_app STREQUAL "cli") set(BUILD_CLI_APP ON) elseif (_app STREQUAL "kde") set(BUILD_KDE_APP ON) elseif (_app STREQUAL "test") set(BUILD_TEST_APP ON) else () message(SEND_ERROR "Invalid value ${_app} in WITH_APPS. " "Supported applications are \"Qt;KDE;Test\".") endif () endforeach () endif (WITH_APPS) # Version information set(CPACK_PACKAGE_VERSION_MAJOR 3) set(CPACK_PACKAGE_VERSION_MINOR 0) set(CPACK_PACKAGE_VERSION_PATCH 2) set(KID3_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}") set(CPACK_PACKAGE_VERSION ${KID3_VERSION}) #set(CPACK_PACKAGE_VERSION "git20131012") set(RELEASE_YEAR 2013) # Platform specific default installation paths if (APPLE) set(WITH_DATAROOTDIR_DEFAULT "kid3.app/Contents/Resources") set(WITH_DOCDIR_DEFAULT "kid3.app/Contents/Resources") set(WITH_TRANSLATIONSDIR_DEFAULT "kid3.app/Contents/Resources") set(WITH_BINDIR_DEFAULT ".") set(WITH_LIBDIR_DEFAULT "kid3.app/Contents/MacOS") set(WITH_PLUGINSDIR_DEFAULT "kid3.app/Contents/PlugIns") elseif (WIN32) set(WITH_DATAROOTDIR_DEFAULT ".") set(WITH_DOCDIR_DEFAULT ".") set(WITH_TRANSLATIONSDIR_DEFAULT ".") set(WITH_BINDIR_DEFAULT ".") set(WITH_LIBDIR_DEFAULT ".") set(WITH_PLUGINSDIR_DEFAULT "./plugins") else (APPLE) set(WITH_DATAROOTDIR_DEFAULT "share") set(WITH_DOCDIR_DEFAULT "share/doc/kid3-qt") if (BUILD_KDE_APP OR BUILD_SHARED_LIBS) set(WITH_TRANSLATIONSDIR_DEFAULT "share/kid3/translations") else (BUILD_KDE_APP OR BUILD_SHARED_LIBS) set(WITH_TRANSLATIONSDIR_DEFAULT "share/kid3-qt/translations") endif (BUILD_KDE_APP OR BUILD_SHARED_LIBS) set(WITH_BINDIR_DEFAULT "bin") set(WITH_LIBDIR_DEFAULT "lib${LIB_SUFFIX}/kid3") set(WITH_PLUGINSDIR_DEFAULT "${WITH_LIBDIR_DEFAULT}/plugins") endif (APPLE) set(WITH_DATAROOTDIR ${WITH_DATAROOTDIR_DEFAULT} CACHE STRING "data root directory relative to CMAKE_INSTALL_PREFIX") set(WITH_DOCDIR ${WITH_DOCDIR_DEFAULT} CACHE STRING "documentation directory relative to CMAKE_INSTALL_PREFIX") set(WITH_TRANSLATIONSDIR ${WITH_TRANSLATIONSDIR_DEFAULT} CACHE STRING "translations directory relative to CMAKE_INSTALL_PREFIX") set(WITH_BINDIR ${WITH_BINDIR_DEFAULT} CACHE STRING "binary directory relative to CMAKE_INSTALL_PREFIX") set(WITH_LIBDIR ${WITH_LIBDIR_DEFAULT} CACHE STRING "library directory relative to CMAKE_INSTALL_PREFIX") set(WITH_PLUGINSDIR ${WITH_PLUGINSDIR_DEFAULT} CACHE STRING "plugin directory relative to CMAKE_INSTALL_PREFIX") set(WITH_MANDIR ${WITH_DATAROOTDIR}/man CACHE STRING "man documentation directory relative to CMAKE_INSTALL_PREFIX") if (CMAKE_INSTALL_PREFIX) set(CMAKE_INSTALL_PREFIX_SLASH "${CMAKE_INSTALL_PREFIX}/") else (CMAKE_INSTALL_PREFIX) set(CMAKE_INSTALL_PREFIX_SLASH "") endif (CMAKE_INSTALL_PREFIX) if (APPLE) file(RELATIVE_PATH CFG_DATAROOTDIR "/kid3.app/Contents/MacOS" /${WITH_DATAROOTDIR}) file(RELATIVE_PATH CFG_DOCDIR "/kid3.app/Contents/MacOS" /${WITH_DOCDIR}) file(RELATIVE_PATH CFG_TRANSLATIONSDIR "/kid3.app/Contents/MacOS" /${WITH_TRANSLATIONSDIR}) file(RELATIVE_PATH CFG_PLUGINSDIR "/kid3.app/Contents/MacOS" /${WITH_PLUGINSDIR}) else (APPLE) set(CFG_DATAROOTDIR ${CMAKE_INSTALL_PREFIX_SLASH}${WITH_DATAROOTDIR}) set(CFG_DOCDIR ${CMAKE_INSTALL_PREFIX_SLASH}${WITH_DOCDIR}) set(CFG_TRANSLATIONSDIR ${CMAKE_INSTALL_PREFIX_SLASH}${WITH_TRANSLATIONSDIR}) file(RELATIVE_PATH CFG_PLUGINSDIR /${WITH_BINDIR} /${WITH_PLUGINSDIR}) endif (APPLE) find_package(Threads) find_package(Perl REQUIRED) include (CheckCXXSourceCompiles) include (CheckCXXCompilerFlag) include (CheckLibraryExists) # Find Qt set(_qt5Dir) set(QT_EXECUTABLE_COMPILE_FLAGS) if (NOT WITH_QT4) string(REGEX MATCH "^(.*[Qq]t5.*).bin.qmake.*" _qt5Dir "${QT_QMAKE_EXECUTABLE}") if (_qt5Dir) set(_qt5Dir ${CMAKE_MATCH_1}) endif (_qt5Dir) endif (NOT WITH_QT4) if (NOT WITH_QT5 AND NOT _qt5Dir) set(_QT4_COMPONENTS QtCore QtGui QtNetwork QtXml) if (WIN32) set(QT_USE_QTMAIN TRUE) endif (WIN32) set(QT_USE_QTDBUS ${WITH_DBUS}) find_package(Qt4 COMPONENTS ${_QT4_COMPONENTS}) if (Qt4_FOUND OR QT4_FOUND) include (${QT_USE_FILE}) if (NOT WITH_QT4) message(STATUS "Qt4 found, explicitly set WITH_QT5 or QT_QMAKE_EXECUTABLE to use a different version.") endif (NOT WITH_QT4) endif (Qt4_FOUND OR QT4_FOUND) endif (NOT WITH_QT5 AND NOT _qt5Dir) if (NOT WITH_QT4 AND NOT Qt4_FOUND AND NOT QT4_FOUND) if (_qt5Dir) set(Qt5Core_DIR "${_qt5Dir}/lib/cmake/Qt5Core") find_package(Qt5Core) if (Qt5Core_FOUND) message(STATUS "Qt5Core found in ${_qt5Dir}") set(Qt5Gui_DIR "${_qt5Dir}/lib/cmake/Qt5Gui") set(Qt5Widgets_DIR "${_qt5Dir}/lib/cmake/Qt5Widgets") set(Qt5Network_DIR "${_qt5Dir}/lib/cmake/Qt5Network") set(Qt5Xml_DIR "${_qt5Dir}/lib/cmake/Qt5Xml") set(Qt5Multimedia_DIR "${_qt5Dir}/lib/cmake/Qt5Multimedia") set(Qt5LinguistTools_DIR "${_qt5Dir}/lib/cmake/Qt5LinguistTools") set(Qt5Test_DIR "${_qt5Dir}/lib/cmake/Qt5Test") if (WITH_DBUS) set(Qt5DBus_DIR "${_qt5Dir}/lib/cmake/Qt5DBus") endif (WITH_DBUS) endif (Qt5Core_FOUND) else (_qt5Dir) find_package(Qt5Core) endif (_qt5Dir) if (Qt5Core_FOUND) set(HAVE_QT5 1) set(WITH_PHONON OFF) set(QT_INCLUDES) set(QT_DEFINITIONS) set(QT_LIBRARIES) set(_qt5Components Core Gui Widgets Network Xml Multimedia LinguistTools Test) if (WITH_DBUS) set(_qt5Components ${_qt5Components} DBus) endif (WITH_DBUS) foreach (_qt5Component ${_qt5Components}) find_package("Qt5${_qt5Component}") if (Qt5${_qt5Component}_FOUND) set(QT_INCLUDES ${QT_INCLUDES} ${Qt5${_qt5Component}_INCLUDE_DIRS}) set(QT_DEFINITIONS ${QT_DEFINITIONS} ${Qt5${_qt5Component}_DEFINITIONS}) set(QT_LIBRARIES ${QT_LIBRARIES} ${Qt5${_qt5Component}_LIBRARIES}) endif (Qt5${_qt5Component}_FOUND) endforeach () if (Qt5DBus_FOUND) set(HAVE_QTDBUS 1) endif (Qt5DBus_FOUND) set(QT_EXECUTABLE_COMPILE_FLAGS ${Qt5Core_EXECUTABLE_COMPILE_FLAGS}) get_target_property(QT_LIBRARY_DIR Qt5::Core LOCATION) get_filename_component(QT_LIBRARY_DIR ${QT_LIBRARY_DIR} PATH) get_target_property(QT_LUPDATE_EXECUTABLE Qt5::lupdate LOCATION) get_target_property(QT_PLUGINS_DIR Qt5::QJpegPlugin LOCATION) if (QT_PLUGINS_DIR) get_filename_component(QT_PLUGINS_DIR ${QT_PLUGINS_DIR} PATH) get_filename_component(QT_PLUGINS_DIR ${QT_PLUGINS_DIR} PATH) elseif (_qt5Dir) set(QT_PLUGINS_DIR ${_qt5Dir}/plugins) else (QT_PLUGINS_DIR) # Before Qt 5.2, there seems to be no way to get the plugins directory. # Assume it is on the same level as the bin directory. get_filename_component(QT_PLUGINS_DIR ${QT_LUPDATE_EXECUTABLE} PATH) get_filename_component(QT_PLUGINS_DIR ${QT_PLUGINS_DIR} PATH) set(QT_PLUGINS_DIR ${QT_PLUGINS_DIR}/plugins) endif (QT_PLUGINS_DIR) macro(qt4_add_translation) qt5_add_translation(${ARGV}) endmacro(qt4_add_translation) macro(qt4_wrap_cpp) qt5_wrap_cpp(${ARGV}) endmacro(qt4_wrap_cpp) macro(qt4_add_resources) qt5_add_resources(${ARGV}) endmacro(qt4_add_resources) get_target_property(_qmakeLocation Qt5::qmake LOCATION) message(STATUS "Found Qt-Version ${Qt5Core_VERSION_STRING} (using ${_qmakeLocation})") endif (Qt5Core_FOUND) endif (NOT WITH_QT4 AND NOT Qt4_FOUND AND NOT QT4_FOUND) if (NOT Qt4_FOUND AND NOT QT4_FOUND AND NOT Qt5Core_FOUND) message(FATAL_ERROR "Neither Qt4 nor Qt5 found.") endif (NOT Qt4_FOUND AND NOT QT4_FOUND AND NOT Qt5Core_FOUND) # Check that QT_NO_DEBUG is defined for release configurations foreach (_buildType RELWITHDEBINFO RELEASE MINSIZEREL) if (NOT CMAKE_CXX_FLAGS_${_buildType} MATCHES "-DQT_NO_DEBUG") set(CMAKE_CXX_FLAGS_${_buildType} "${CMAKE_CXX_FLAGS_${_buildType}} -DQT_NO_DEBUG") endif () endforeach () ### Check for xsltproc find_program(XSLTPROC xsltproc DOC "xsltproc transforms XML via XSLT" PATHS $ENV{XSLTPROCDIR}) if (NOT XSLTPROC) message(FATAL_ERROR "Could not find xsltproc") endif (NOT XSLTPROC) ### Check for HTML docbook.xsl file(GLOB _versionedStyleSheetDir /usr/share/xml/docbook/xsl-stylesheets-*) find_path(DOCBOOK_XSL_DIR html/docbook.xsl PATHS ${WITH_DOCBOOKDIR} /usr/share/xml/docbook/stylesheet/nwalsh /usr/share/xml/docbook/stylesheet/nwalsh/current /usr/share/xml/docbook/stylesheet/docbook-xsl /usr/share/xml/docbook/stylesheet/docbook-xsl-ns /usr/share/sgml/docbook/xsl-stylesheets /usr/share/apps/ksgmltools2/docbook/xsl ${_versionedStyleSheetDir} $ENV{DOCBOOKDIR} $ENV{HOME}/docbook-xsl-1.72.0 NO_DEFAULT_PATH) if (NOT DOCBOOK_XSL_DIR) message(FATAL_ERROR "Could not find HTML docbook.xsl") endif (NOT DOCBOOK_XSL_DIR) set(BASIC_LIBRARIES ${QT_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) if (APPLE) set(BASIC_LIBRARIES ${BASIC_LIBRARIES} "-framework CoreServices") endif (APPLE) if (WIN32) link_directories($ENV{LIB}) endif (WIN32) if (BUILD_SHARED_LIBS) add_definitions(-DKID3_SHARED) # the RPATH to be used when installing, but only if it's not a system directory # see http://www.cmake.org/Wiki/CMake_RPATH_handling list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_PREFIX}/${WITH_LIBDIR}" _isSystemDir) if ("${_isSystemDir}" STREQUAL "-1") set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${WITH_LIBDIR}") endif ("${_isSystemDir}" STREQUAL "-1") endif (BUILD_SHARED_LIBS) ### Check for zlib if (WIN32) if (NOT MSVC) find_library(_ZLIB_PATH_NAME z) if (_ZLIB_PATH_NAME) get_filename_component(_ZLIB_PATH ${_ZLIB_PATH_NAME} PATH) else (_ZLIB_PATH_NAME) message(FATAL_ERROR "Could not find zlib") endif (_ZLIB_PATH_NAME) set(ZLIB_LIBRARIES "-L${_ZLIB_PATH}" -lz) endif (NOT MSVC) else (WIN32) set(ZLIB_LIBRARIES -lz) endif (WIN32) ### Check for libQtDBus if (WITH_DBUS AND QT_QTDBUS_LIBRARY) set(HAVE_QTDBUS 1) endif (WITH_DBUS AND QT_QTDBUS_LIBRARY) ### Check for mntent.h include(CheckIncludeFile) check_include_file("mntent.h" HAVE_MNTENT_H) set(QT_DEFINITIONS ${QT_DEFINITIONS} -DQT_ASCII_CAST_WARNINGS -DQT_NO_CAST_TO_ASCII) ### Check for Phonon set(PHONON_LIBRARY) set(PHONON_INCLUDE_DIR) if (WITH_PHONON) find_library(PHONON_LIBRARY NAMES phonon phonon4 HINTS ${QT_LIBRARY_DIR}) find_path(PHONON_INCLUDE_DIR NAMES phonon/phononnamespace.h HINTS ${QT_INCLUDE_DIR} ${INCLUDE_INSTALL_DIR} ${QT_LIBRARY_DIR} ${QT_HEADERS_DIR}/phonon ${QT_LIBRARY_DIR}/phonon.framework/Headers) if (PHONON_LIBRARY AND PHONON_INCLUDE_DIR) message(STATUS "Phonon found: ${PHONON_LIBRARY}") set(HAVE_PHONON 1) set(BASIC_LIBRARIES ${BASIC_LIBRARIES} ${PHONON_LIBRARY}) else (PHONON_LIBRARY AND PHONON_INCLUDE_DIR) message(FATAL_ERROR "Could not find Phonon") endif (PHONON_LIBRARY AND PHONON_INCLUDE_DIR) endif (WITH_PHONON) if (CMAKE_COMPILER_IS_GNUCC) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wundef -Wcast-align -Wall -W -Wpointer-arith -D_REENTRANT") endif (CMAKE_COMPILER_IS_GNUCC) if (CMAKE_COMPILER_IS_GNUCXX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wundef -Wcast-align -Wall -W -Wpointer-arith -fno-exceptions -fno-check-new -fno-common -D_REENTRANT") check_cxx_compiler_flag(-Woverloaded-virtual _HAVE_W_OVERLOADED_VIRTUAL) if(_HAVE_W_OVERLOADED_VIRTUAL) set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Woverloaded-virtual") endif(_HAVE_W_OVERLOADED_VIRTUAL) endif (CMAKE_COMPILER_IS_GNUCXX) if (MSVC) # Treat wchar_t as built-in type, else QString::fromWCharArray is unresolved add_definitions("/Zc:wchar_t-") endif (MSVC) if (APPLE) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mmacosx-version-min=10.5") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=10.5") endif (APPLE) add_definitions(${QT_DEFINITIONS} -DHAVE_CONFIG_H=1 -DQT_NO_EXCEPTIONS -DQT_NO_STL) include_directories(${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} ${QT_INCLUDES} ${PHONON_INCLUDE_DIR}) if (NOT WIN32) check_cxx_compiler_flag(-fvisibility=hidden _HAVE_GCC_VISIBILITY) if (_HAVE_GCC_VISIBILITY) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden -fvisibility-inlines-hidden") endif (_HAVE_GCC_VISIBILITY) endif (NOT WIN32) if (UNIX AND NOT APPLE) set(KID3_EXECUTABLE kid3-qt) else (UNIX AND NOT APPLE) set(KID3_EXECUTABLE kid3) endif (UNIX AND NOT APPLE) configure_file(config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h) add_subdirectory(src) add_subdirectory(doc) add_subdirectory(po) # To create a package, run cpack if (APPLE) set(CPACK_BINARY_DRAGNDROP ON) set(CPACK_GENERATOR DragNDrop) elseif (WIN32) set(CPACK_GENERATOR ZIP) else (APPLE) set(CPACK_GENERATOR DEB) endif (APPLE) if (BUILD_KDE_APP OR APPLE OR WIN32) set(CPACK_PACKAGE_NAME "kid3") set(CPACK_DEBIAN_PACKAGE_SECTION "kde") else (BUILD_KDE_APP OR APPLE OR WIN32) set(CPACK_PACKAGE_NAME "kid3-qt") set(CPACK_DEBIAN_PACKAGE_SECTION "sound") endif (BUILD_KDE_APP OR APPLE OR WIN32) set(CPACK_PACKAGE_CONTACT "ufleisch@users.sourceforge.net") set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Audio tag editor") set(CPACK_STRIP_FILES ON) set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "http://kid3.sourceforge.net/") set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) include(CPack) kid3-3.0.2/COPYING000066400000000000000000000355641224603511300134130ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS kid3-3.0.2/ChangeLog000066400000000000000000000433131224603511300141210ustar00rootroot00000000000000Wed Nov 27 17:39:57 CET 2013 Urs Fleisch * Release 3.0.2 * Fixed: + Translations and handbook are not found on Mac OS X 10.9 Mavericks. + Application termination when main window is closed while handbook is open. + Tag 1 genre combobox is empty if "show only custom genres" is set. + Session restoration. + Detection of installed Qt 5. + KDE application does not find libraries if prefix is not /usr. + Build if all translations are disabled. * Improved: + Added new genres introduced in Winamp 5.6. Tue Oct 29 18:15:04 CET 2013 Urs Fleisch * Release 3.0.1 * Fixed: + Filename format configuration can not be changed. + Menus get broken when toolbar is changed in KDE version. + Incorrect conversion of configuration in KDE version. + Unintended result selection in first track of fingerprint import. + Crash at exit after canceling fingerprint import with GStreamer. + Prefer GStreamer 0.10 over 1.0 to avoid hang in Phonon. + Build with readline needing termcap (as used in Slackware). + Build with FFmpeg 2.0 (as used in openSUSE 12.3). + Honor LIB_SUFFIX if set (as used in Fedora). Mon Oct 14 21:30:42 CEST 2013 Urs Fleisch * Release 3.0 * New: + Command-line interface kid3-cli. + Use common shared libraries for KDE, Qt-only and CLI versions. + Plugins for metadata libraries and importers. + Support Opus files with TagLib 1.9. + Support GStreamer 1.0. + Support libavresample 0.0.3. * Improved: + Editing without leaving the keyboard. + When saving a file fails, tell user if it is not writable. + Escape metacharacters in HTML export. + Updated Czech translation. + Updated Finnish translation. + Cleaned up configuration option sections. * Fixed: + Crash when saving non-FLAC file with flac extension. + Support minimum Mac OS X version 10.5. + Load Qt libraries only from bundle on Mac OS X. Sat Mar 2 09:36:02 CET 2013 Urs Fleisch * Release 2.3 * New: + Automatic batch import for multiple directories. + Import catalog number and release country from Discogs and MusicBrainz. + Quick access tags are configurable for tag 2. + Setting for default file name to save cover art. + Select all in directory. + Apply text encoding. + Support Qt 5.0. * Improved: + Better responsiveness when working with a huge number of files, long operations can be aborted (filtering, renaming, expanding). + Support custom frame names in formats and imports. + Display accuracy and cover art URL for imports. + Added more unified frame types. + GUI and usability. * Fixed: + Swapped mapping of too and enc for M4A files. + Compatibility of ID3v2.4.0 COMM frames with iTunes. + Do not remove spaces in Vorbis comment field names. + Crash and decoding for fingerprints with libav 9.1. Mon Dec 3 11:09:12 CET 2012 Urs Fleisch * Release 2.2.1 * Improved: + Updated Czech translation. * Fixed: + Fixed selection of language and handbook with Qt 4.8. Fri Oct 26 17:07:27 CEST 2012 Urs Fleisch * Release 2.2 * New: + Option to load last opened file on startup. + Option to use locale for character conversion. + Support new features of TagLib 1.8: More ID3 frames, ID3v2.3, tracker modules (MOD, S3M, IT, XM). + Support use of GStreamer instead of FFmpeg for Chromaprint decoding. + Support building with latest libav/FFmpeg libraries. * Improved: + Extract year from "YYYY-MM.." date frame for %{year} format code. + Character conversion for roman numbers. + Dutch translation. + Finnish translation. * Fixed: + Avoid crash when exporting album cover while editing multiple files. + Check if file format supported before converting to ID3v2.3.0. + Rewritten Discogs import to use Discogs API v2.0. Mon May 7 21:20:06 CEST 2012 Urs Fleisch * Release 2.1 * New: + Keyboard shortcuts configuration for Qt-only version. + Use Chromaprint for import from MusicBrainz Fingerprint, is now also available on Windows and Mac OS X. + Serbian translation. + Support build with Qt5. * Improved: + Support most frames in format codes with %{framename}. + Support iTunes ID3v2.4 frames TCMP, TSO2, TSOC. + Option to use the native file dialogs for Qt-only version. + Handling of pictures in WMA files. + Use themed icons. + Building with shared libraries on KDE. + Handle carriage return characters in output from user commands. * Fixed: + Windows: Avoid truncation of file when renaming with illegal characters. + Limit number of open file handles. + Nicer icons, install SVG instead of SVGZ in kid3-qt. + Fixed import from Amazon. + Adapted to Discogs server update. Sat Oct 29 11:23:46 CEST 2011 Urs Fleisch * Release 2.0.1 * Fixed: + Prevent cursor from jumping to end in format line edits. + Correctly update file selection after add, edit or delete frame. + Build system finds DocBook XSL on various Linux distributions, finds Phonon on Ubuntu 11.10, can build with shared libraries. + Correctly set bundle version on Mac OS X. + Czech translation. Tue Aug 30 19:02:37 CEST 2011 Urs Fleisch * Release 2.0 * New: + Import from tags to extract and move information between tags. + Play a track on double click (optional). + Searching in help browser. + Move files to trash instead of deleting them. + Automatic setting of checkboxes when frame is changed. + Support for APE tags with TagLib 1.7. * Improved: + Major refactoring to improve software architecture, separated GUI from application logic using the Qt 4 Model/View architecture. This required dropping support for Qt 3 and KDE 3. Unified build system using CMake for KDE and Qt-only versions. + The file list is updated on file system changes and is faster. + Import sub-dialogs are modeless and do not block import dialog. + Import table is editable and has optional columns for file names and paths (selectable with context menu). + The tracks to import can be selected, e.g. to import only one CD from a double-CD album. + Number Tracks can number the tracks of multiple directories. + Multiple directories can be selected with Rename Directory. + M4A support with TagLib (can fully replace libmp4v2). + Exports are displayed in a table if they contain tabulators. + Adding and editing formats is now more user friendly. + GUI and usability. * Fixed: + Genre and track total in M4A files with TagLib. + Execution of user action without command. + Adapted to Discogs server update. Sat Jan 15 16:52:22 CET 2011 Urs Fleisch * Release 1.6 * Improved: + The total number of tracks can be set in the "Number Tracks" dialog. + All numeric fields can be padded with zeros. + Parts of the filename can be ignored when generating tags from the filename. + Updated Estonian, Italian and Czech translations. * Fixed: + Track number digits option works now with ID3v2.4 and UTF8/UTF16. + Adapted to Discogs server update. Sun Sep 26 14:14:55 CEST 2010 Urs Fleisch * Release 1.5 * New: + Versions without KDE (kid3-qt, Windows, Mac OS X) now have also icons, a toolbar, and functions "Open Recent", "Show Statusbar". + A simple audio player is available via "Tools" and context menu. + Option "Auto Hide Tags" to hide unused tags. + Option to set number of digits in track number. + Support for cover art in WMA files. + Context menu items "Expand all" and "Collapse all". + Double click on picture to add or edit cover art. + Chinese translation. * Improved: + Faster filter. + Frame difference shows all frames of the selected files. + The buttons "From Filename" are now beside the corresponding format and named "To Tag 1" and "To Tag 2". The buttons "From Tag 1" and "From Tag 2" are beside their format. * Fixed: + Setting pictures using D-Bus function setFrame(). + Correct extensions for ".aac" and ".mp2" files when generating filenames from tags. + Correct setting of URL field in WXXX frames when set in table. + Do not lose focus in frame tables when window is deactivated. + MIME types, documentation adapted for KDE 4.5. Sun Feb 28 15:50:51 CET 2010 Urs Fleisch * Release 1.4 * New: + Support for cover art in Ogg files. + Import from Amazon. + Separate formats for "to filename" and "from filename". + Czech translation. * Improved: + Use of UTF8 and UTF16 encoding when non-ASCII characters are used. + GUI and usability. * Fixed: + Correctly set tags when tags of multiple files are selected, edited, and then copied to the other tag. + Adapted to Discogs server update. Fri Oct 23 20:55:17 CEST 2009 Urs Fleisch * Release 1.3 * New: + Advanced playlist dialog. + Support for WMA/ASF, AIFF and WAV files. + Estonian, Finnish and Turkish translations. * Improved: + When changing the current file while a tag field is being edited, the changes are preserved. + Translation system. + Settings in Number Tracks dialog can be saved. + Filter files without attached pictures. + Support for libmp4v2 1.9 and TagLib 1.6. * Fixed: + Show album art when multiple files are selected. + From Tag 2 option can be restored in Rename Directory dialog. + Picture download. + Adapted to Discogs server update. Thu Apr 2 19:44:10 CEST 2009 Urs Fleisch * Release 1.2 * New: + Import of album cover art from various sources. + Mark changed tag fields and filenames. + Option to store ID3v2 genre as text instead of numeric string. + Track number format with configurable amount of leading zeros. * Improved: + Directory deb to generate Debian packages for KDE 4 and Qt 4 or KDE 3 and Qt3. + Proxy authentication with Qt 4. + Display and editing of COMM, PRIV, UFID, MCDI and POPM frames. + Technical details (bitrate, codec, ...) for export and filter. * Fixed: + Format replacements %c, %y, ... are replaced with empty strings if the corresponding field is empty + Charset for gnudb.org import. + ID3v1 fields are displayed correctly after saving when the ID3v2.3 encoding is set to UTF16. + Correct header information for MP3 files with attached picture or without ID3v1 tag. + Adapted to Discogs server update. Sat Sep 27 15:51:35 CEST 2008 Urs Fleisch * Release 1.1 * New: + Dutch translation. + New general frame types Album Artist, Grouping, Lyrics, Media, Remixer. + Setting character encoding for ID3v1.1 tags. + Import additional tags from Discogs and MusicBrainz. * Improved: + All frame types (not only Artist, Album, ...) can be used for all operations (e.g. import, export, tag <-> file name, rename, ...). * Fixed: + Build for KDE 3 without id3lib or TagLib + Suppress using unsupported UTF-8 for ID3v2.3 + Qt 4, KDE 4: Wrong track times in import dialog when some track times are missing. + KDE 4: Translations with arguments (%1, %2). + Windows: Displaying pictures with JPEG plugin. + MacOS X: Dropping and displaying pictures. Sat Mar 1 15:44:56 CET 2008 Urs Fleisch * Release 1.0 * New: + File filter. + D-Bus interface to control application by scripts. + Long format codes %{title}, %{album}, ... and tooltips for formats. + Support for FLAC pictures. + Polish translation. * Improved: + GUI and usability. + Rename directory wizard. + Use xdg-open as the default web browser. + Display description instead of TXXX and WXXX. + Add pictures to files by drag and drop and from clipboard. + Compilation with GCC 4.3 and compiler detection for kid3-qt. + Support for TagLib 1.5. * Fixed: + KDE 4 docs in correct directory. + Crash when TSST frame was added to ID3v2.3.0 tag. + Disappearing ID3v1 genres. + Possible crash when import or export format was added. Thu Nov 15 21:52:36 CET 2007 Urs Fleisch * Release 0.10 * New: + All frames can be edited in tables, not only the standard tags. + Support for MP4/AAC, MP2, Speex, TrueAudio, and WavPack files. + Default encoding can be configured. + Italian translation. + Support build with KDE 4. * Improved: + Conversion between ID3v2.3 and ID3v2.4 for all supported frames. + GUI and usability. + Qt4 version builds without Qt3Support module * Fixed: + Correct kid3-qt icon. + kid3-qt builds correctly without id3lib or without MusicBrainz. + Remove deleted user actions from configuration. + Allow new export/import formats with empty fields. + Reread file after conversion from ID3v2.4 to ID3v2.3. + Rename directory does not rename when tags are empty. Sat May 5 14:22:28 CEST 2007 Urs Fleisch * Release 0.9 * New: + Hierarchical directory tree instead of flat file list. + Enhanced context menu commands, can be used to browse for lyrics and album art. + Import from gnudb.org. + Conversion between ID3v2.3 and ID3v2.4 tags. + Editor for custom genres. + Option to mark truncated ID3v1.1 fields. * Improved: + Frames are listed alphabetically and can be edited and deleted in multiple files. + Import from TrackType.org (formerly freedb2.org) * Fixed: + MusicBrainz fingerprint import. Tue Nov 21 23:39:09 CET 2006 Urs Fleisch * Release 0.8.1 * Fixed: + A new Custom genre was added on termination. Sat Nov 11 11:02:44 CET 2006 Urs Fleisch * Release 0.8 * New: + Import from freedb2.org, Discogs and MusicBrainz release database. + Support for ID3v2.4 and MPC using TagLib. + Context specific help in dialogs. + Support build with Qt4. * Improved: + Show tag formats. + Remove non-letter characters before matching by title in import dialog. + Buttons to save dialog specific settings. * Fixed: + Allow building with tunepimp 0.5.x. + Place kid3.desktop in /usr/share/applications/kde/. + Fix inadvertent changes of ID3v2.3 genre strings. + Remove trailing zeroes in unicode strings. Wed Jun 7 21:14:24 CEST 2006 Urs Fleisch * Release 0.7 * New: + Export dialog can export tags as CSV, HTML, playlists, Kover XML and other formats. CSV files can be imported again. + Show/hide ID3v1.1/ID3v2.3 controls. + Custom strings can be used for ID3v2.3, Ogg/Vorbis and FLAC genres. + Automatic numbering of tracks. * Improved: + Direct menus for freedb.org and MusicBrainz import. + freedb.org import has suggested search string, search history. + Imported tracks can be reordered to match track number, title or length. + Format while editing affects most operations, is split into separate options for filenames and tags, menu commands to apply filename format and tag format. + Case conversion is done before string replacements, so that "case exceptions" can be corrected. + Builds with libtunepimp 0.4.x. * Fixed: + Remove temporary Ogg file when filename and tags are changed. + Mark file as changed when Ogg genre is changed. + Correct length restriction for ID3v1.1 comment. + Open directories with special characters from the command line. + Import from file/clipboard does not use durations from previous freedb.org import. Mon Oct 24 16:59:35 CEST 2005 Urs Fleisch * Release 0.6 * Support for Ogg/Vorbis and FLAC files * Import from MusicBrainz * Usability improvements Mon Jul 26 20:05:14 CEST 2004 Urs Fleisch * Release 0.5 * When importing, check the length and the count of the tracks * Keyboard shortcut configuration * Rename and create directories from tags * Display information about tags, bitrate, length, ... Sat Jan 24 13:39:44 CET 2004 Urs Fleisch * Release 0.4 * Direct import from freedb.org * freedesktop.org compliant kid3.desktop file * Accept freedb.org input if there is no year or genre information * File renaming works on Windows filesystems if case changed * Context menu in string replacement table * Progress bar while saving directory Sat Oct 18 16:40:42 CEST 2003 Urs Fleisch * Release 0.3 * Automatic case conversion and string replacements * Import of album data from freedb.org and other sources * Improved Windows (Qt only) version: persisted configuration, online help * Support files for KDevelop and Visual C++ * Improved Unicode support, however some bugs in id3lib have to be fixed before this is of real use Sat Sep 06 2003 Egor S. Orlov 0.2.1-alt0.1 * Added russian translation Thu May 13 07:37:07 CEST 2003 Urs Fleisch * Release 0.2.1 * Now uses the standard automake/autoconf build process provided by kapptemplate. Sat Apr 26 08:38:14 CEST 2003 Urs Fleisch * Release 0.2 * Use QScrollView for control widgets at the right side and separate them from the filelist by a QSplitter, so that the window can be resized to a small size. * Show busy cursor while reading and writing files. Thu Jan 16 19:41:21 CET 2003 Urs Fleisch * Started ChangeLog. kid3-3.0.2/Doxyfile000066400000000000000000002362721224603511300140650ustar00rootroot00000000000000# Doxyfile 1.8.4 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a double hash (##) is considered a comment and is placed # in front of the TAG it is preceding . # All text after a hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" "). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or sequence of words) that should # identify the project. Note that if you do not use Doxywizard you need # to put quotes around the project name if it contains spaces. PROJECT_NAME = kid3 # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = 3.0.2 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer # a quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = # With the PROJECT_LOGO tag one can specify an logo or icon that is # included in the documentation. The maximum height of the logo should not # exceed 55 pixels and the maximum width should not exceed 200 pixels. # Doxygen will copy the logo to the output directory. PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Latvian, Lithuanian, Norwegian, Macedonian, # Persian, Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, # Slovak, Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = NO # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. Note that you specify absolute paths here, but also # relative paths, which will be relative from the directory where doxygen is # started. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful if your file system # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = yes # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding # "class=itcl::class" will allow you to use the command class in the # itcl::class meaning. TCL_SUBST = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, # and language is one of the parsers supported by doxygen: IDL, Java, # Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, # C++. For instance to make doxygen treat .inc files as Fortran files (default # is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note # that for custom extensions you also need to set FILE_PATTERNS otherwise the # files are not read by doxygen. EXTENSION_MAPPING = # If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all # comments according to the Markdown format, which allows for more readable # documentation. See http://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you # can mix doxygen, HTML, and XML commands with Markdown formatting. # Disable only in case of backward compatibilities issues. MARKDOWN_SUPPORT = YES # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by by putting a % sign in front of the word # or globally by setting AUTOLINK_SUPPORT to NO. AUTOLINK_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also makes the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES (the # default) will make doxygen replace the get and set methods by a property in # the documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and # unions are shown inside the group in which they are included (e.g. using # @ingroup) instead of on a separate page (for HTML and Man pages) or # section (for LaTeX and RTF). INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and # unions with only public data fields or simple typedef fields will be shown # inline in the documentation of the scope in which they are defined (i.e. file, # namespace, or group documentation), provided this scope is documented. If set # to NO (the default), structs, classes, and unions are shown on a separate # page (for HTML and Man pages) or section (for LaTeX and RTF). INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO # The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This # cache is used to resolve symbols given their name and scope. Since this can # be an expensive process and often the same symbol appear multiple times in # the code, doxygen keeps a cache of pre-resolved symbols. If the cache is too # small doxygen will become slower. If the cache is too large, memory is wasted. # The cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid # range is 0..9, the default is 0, corresponding to a cache size of 2^16 = 65536 # symbols. LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_PACKAGE tag is set to YES all members with package or internal # scope will be included in the documentation. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespaces are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen # will list include files with double quotes in the documentation # rather than with sharp brackets. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen # will sort the (brief and detailed) documentation of class members so that # constructors and destructors are listed first. If set to NO (the default) # the constructors will appear in the respective orders defined by # SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. # This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO # and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to # do proper type resolution of all parameters of a function it will reject a # match between the prototype and the implementation of a member function even # if there is only one candidate or it is obvious which candidate to choose # by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen # will still accept a match between prototype and implementation in such cases. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if section-label ... \endif # and \cond section-label ... \endcond blocks. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or macro consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and macros in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. # This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. # You can optionally specify a file name after the option, if omitted # DoxygenLayout.xml will be used as the name of the layout file. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files # containing the references data. This must be a list of .bib files. The # .bib extension is automatically appended if omitted. Using this command # requires the bibtex tool to be installed. See also # http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style # of the bibliography can be controlled using LATEX_BIB_STYLE. To use this # feature you need bibtex and perl available in the search path. Do not use # file names with spaces, bibtex cannot handle them. CITE_BIB_FILES = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # The WARN_NO_PARAMDOC option can be enabled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text " # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = src # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh # *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py # *.f90 *.f *.for *.vhd *.vhdl FILE_PATTERNS = *.cpp \ *.h \ *.hpp # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = yes # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = kid3/allsys.h \ src/plugins/oggflacmetadata/vcedit.h \ src/plugins/taglibmetadata/taglibext \ src/test # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = *.moc.cpp # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = * # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. # If FILTER_PATTERNS is specified, this tag will be ignored. # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. # Doxygen will compare the file name with each pattern and apply the # filter if there is a match. # The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty or if # non of the patterns match the file name, INPUT_FILTER is applied. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) # and it is also possible to disable source filtering for a specific pattern # using *.ext= (so without naming a filter). This option only has effect when # FILTER_SOURCE_FILES is enabled. FILTER_SOURCE_PATTERNS = # If the USE_MD_FILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page # (index.html). This can be useful if you have a project on for instance GitHub # and want reuse the introduction page also for the doxygen output. USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C, C++ and Fortran comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. # Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = NO # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. Note that when using a custom header you are responsible # for the proper inclusion of any scripts and style sheets that doxygen # needs, which is dependent on the configuration options used. # It is advised to generate a default header using "doxygen -w html # header.html footer.html stylesheet.css YourConfigFile" and then modify # that header. Note that the header is subject to change so you typically # have to redo this when upgrading to a newer version of doxygen or when # changing the value of configuration settings such as GENERATE_TREEVIEW! HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If left blank doxygen will # generate a default style sheet. Note that it is recommended to use # HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this # tag will in the future become obsolete. HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify an additional # user-defined cascading style sheet that is included after the standard # style sheets created by doxygen. Using this option one can overrule # certain style aspects. This is preferred over using HTML_STYLESHEET # since it does not replace the standard style sheet and is therefor more # robust against future updates. Doxygen will copy the style sheet file to # the output directory. HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that # the files will be copied as-is; there are no commands or markers available. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. # Doxygen will adjust the colors in the style sheet and background images # according to this color. Hue is specified as an angle on a colorwheel, # see http://en.wikipedia.org/wiki/Hue for more information. # For instance the value 0 represents red, 60 is yellow, 120 is green, # 180 is cyan, 240 is blue, 300 purple, and 360 is red again. # The allowed range is 0 to 359. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of # the colors in the HTML output. For a value of 0 the output will use # grayscales only. A value of 255 will produce the most vivid colors. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to # the luminance component of the colors in the HTML output. Values below # 100 gradually make the output lighter, whereas values above 100 make # the output darker. The value divided by 100 is the actual gamma applied, # so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, # and 100 does not change the gamma. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting # this to NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. HTML_DYNAMIC_SECTIONS = NO # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of # entries shown in the various tree structured indices initially; the user # can expand and collapse entries dynamically later on. Doxygen will expand # the tree to such a level that at most the specified number of entries are # visible (unless a fully collapsed tree already exceeds this amount). # So setting the number of entries 1 will produce a full collapsed tree by # default. 0 is a special value representing an infinite number of entries # and will result in a full expanded tree by default. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely # identify the documentation publisher. This should be a reverse domain-name # style string, e.g. com.mycompany.MyDocSet.documentation. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated # that can be used as input for Qt's qhelpgenerator to generate a # Qt Compressed Help (.qch) of the generated HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to # add. For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see # # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's # filter section matches. # # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, form an Eclipse help # plugin. To install this plugin and make it available under the help contents # menu in Eclipse, the contents of the directory containing the HTML and XML # files needs to be copied into the plugins directory of eclipse. The name of # the directory within the plugins directory should be the same as # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before # the help appears. GENERATE_ECLIPSEHELP = NO # A unique identifier for the eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have # this name. ECLIPSE_DOC_ID = org.doxygen.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) # at top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. Since the tabs have the same information as the # navigation tree you can set this option to NO if you already set # GENERATE_TREEVIEW to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. # Since the tree basically has the same information as the tab index you # could consider to set DISABLE_INDEX to NO when enabling this option. GENERATE_TREEVIEW = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values # (range [0,1..20]) that doxygen will group on one line in the generated HTML # documentation. Note that a value of 0 will completely suppress the enum # values from appearing in the overview section. ENUM_VALUES_PER_LINE = 4 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open # links to external symbols imported via tag files in a separate window. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are # not supported properly for IE 6.0, but are supported on all modern browsers. # Note that when changing this option you need to delete any form_*.png files # in the HTML output before the changes have effect. FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax # (see http://www.mathjax.org) which uses client side Javascript for the # rendering instead of using prerendered bitmaps. Use this if you do not # have LaTeX installed or if you want to formulas look prettier in the HTML # output. When enabled you may also need to install MathJax separately and # configure the path to it using the MATHJAX_RELPATH option. USE_MATHJAX = NO # When MathJax is enabled you can set the default output format to be used for # the MathJax output. Supported types are HTML-CSS, NativeMML (i.e. MathML) and # SVG. The default value is HTML-CSS, which is slower, but has the best # compatibility. MATHJAX_FORMAT = HTML-CSS # When MathJax is enabled you need to specify the location relative to the # HTML output directory using the MATHJAX_RELPATH option. The destination # directory should contain the MathJax.js script. For instance, if the mathjax # directory is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to # the MathJax Content Delivery Network so you can quickly see the result without # installing MathJax. # However, it is strongly recommended to install a local # copy of MathJax from http://www.mathjax.org before deployment. MATHJAX_RELPATH = http://www.mathjax.org/mathjax # The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension # names that should be enabled during MathJax rendering. MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript # pieces of code that will be used on startup of the MathJax code. MATHJAX_CODEFILE = # When the SEARCHENGINE tag is enabled doxygen will generate a search box # for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using # HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets # (GENERATE_DOCSET) there is already a search function so this one should # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. SEARCHENGINE = NO # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a web server instead of a web client using Javascript. # There are two flavours of web server based search depending on the # EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for # searching and an index file used by the script. When EXTERNAL_SEARCH is # enabled the indexing and searching needs to be provided by external tools. # See the manual for details. SERVER_BASED_SEARCH = NO # When EXTERNAL_SEARCH is enabled doxygen will no longer generate the PHP # script for searching. Instead the search results are written to an XML file # which needs to be processed by an external indexer. Doxygen will invoke an # external search engine pointed to by the SEARCHENGINE_URL option to obtain # the search results. Doxygen ships with an example indexer (doxyindexer) and # search engine (doxysearch.cgi) which are based on the open source search # engine library Xapian. See the manual for configuration details. EXTERNAL_SEARCH = NO # The SEARCHENGINE_URL should point to a search engine hosted by a web server # which will returned the search results when EXTERNAL_SEARCH is enabled. # Doxygen ships with an example search engine (doxysearch) which is based on # the open source search engine library Xapian. See the manual for configuration # details. SEARCHENGINE_URL = # When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed # search data is written to a file for indexing by an external tool. With the # SEARCHDATA_FILE tag the name of this file can be specified. SEARCHDATA_FILE = searchdata.xml # When SERVER_BASED_SEARCH AND EXTERNAL_SEARCH are both enabled the # EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is # useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple # projects and redirect the results back to the right project. EXTERNAL_SEARCH_ID = # The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen # projects other than the one defined by this configuration file, but that are # all added to the same external search index. Each project needs to have a # unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id # of to a relative location where the documentation can be found. # The format is: EXTRA_SEARCH_MAPPINGS = id1=loc1 id2=loc2 ... EXTRA_SEARCH_MAPPINGS = #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. # Note that when enabling USE_PDFLATEX this option is only used for # generating bitmaps for formulas in the HTML output, but not in the # Makefile that is written to the output directory. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, letter, legal and # executive. If left blank a4 will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for # the generated latex document. The footer should contain everything after # the last chapter. If it is left blank doxygen will generate a # standard footer. Notice: only use this tag if you know what you are doing! LATEX_FOOTER = # The LATEX_EXTRA_FILES tag can be used to specify one or more extra images # or other source files which should be copied to the LaTeX output directory. # Note that the files will be copied as-is; there are no commands or markers # available. LATEX_EXTRA_FILES = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = NO # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = NO # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE is set to YES then doxygen will include # source code with syntax highlighting in the LaTeX output. # Note that which sources are shown also depends on other settings # such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See # http://en.wikipedia.org/wiki/BibTeX for more info. LATEX_BIB_STYLE = plain #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load style sheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = no # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options related to the DOCBOOK output #--------------------------------------------------------------------------- # If the GENERATE_DOCBOOK tag is set to YES Doxygen will generate DOCBOOK files # that can be used to generate PDF. GENERATE_DOCBOOK = NO # The DOCBOOK_OUTPUT tag is used to specify where the DOCBOOK pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be put in # front of it. If left blank docbook will be used as the default path. DOCBOOK_OUTPUT = docbook #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. # This is useful # if you want to understand what is going on. # On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # pointed to by INCLUDE_PATH will be searched when a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = HAVE_FLAC \ HAVE_FLAC_PICTURE \ HAVE_CHROMAPRINT \ HAVE_AV_AUDIO_CONVERT \ HAVE_VORBIS \ HAVE_QTDBUS \ HAVE_PHONON \ HAVE_MNTENT_H # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition that # overrules the definition found in the source code. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all references to function-like macros # that are alone on a line, have an all uppercase name, and do not end with a # semicolon, because these will confuse the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. For each # tag file the location of the external documentation should be added. The # format of a tag file without this location is as follows: # # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths # or URLs. Note that each tag file must have a unique name (where the name does # NOT include the path). If a tag file is not located in the directory in which # doxygen is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # If the EXTERNAL_PAGES tag is set to YES all external pages will be listed # in the related pages index. If set to NO, only the current project's # pages will be listed. EXTERNAL_PAGES = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option also works with HAVE_DOT disabled, but it is recommended to # install and use dot, since it yields more powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = NO # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is # allowed to run in parallel. When set to 0 (the default) doxygen will # base this on the number of processors available in the system. You can set it # explicitly to a value larger than 0 to get control over the balance # between CPU load and processing speed. DOT_NUM_THREADS = 0 # By default doxygen will use the Helvetica font for all dot files that # doxygen generates. When you want a differently looking font you can specify # the font name using DOT_FONTNAME. You need to make sure dot is able to find # the font, which can be done by putting it in a standard location or by setting # the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the # directory containing the font. DOT_FONTNAME = # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the Helvetica font. # If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to # set the path where dot can find it. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If the UML_LOOK tag is enabled, the fields and methods are shown inside # the class node. If there are many fields or methods and many nodes the # graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS # threshold limits the number of items for each type to make the size more # manageable. Set this to 0 for no limit. Note that the threshold may be # exceeded by 50% before the limit is enforced. UML_LIMIT_NUM_FIELDS = 10 # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will generate a graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are svg, png, jpg, or gif. # If left blank png will be used. If you choose svg you need to set # HTML_FILE_EXTENSION to xhtml in order to make the SVG files # visible in IE 9+ (other browsers do not have this requirement). DOT_IMAGE_FORMAT = png # If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to # enable generation of interactive SVG images that allow zooming and panning. # Note that this requires a modern browser other than Internet Explorer. # Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you # need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files # visible. Older versions of IE do not have SVG support. INTERACTIVE_SVG = NO # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MSCFILE_DIRS tag can be used to specify one or more directories that # contain msc files that are included in the documentation (see the # \mscfile command). MSCFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 1000 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES kid3-3.0.2/INSTALL000066400000000000000000000031271224603511300133770ustar00rootroot00000000000000* TO INSTALL ** Requirements Qt, optional KDE, id3lib, TagLib, libogg, libvorbis, libvorbisfile, libFLAC++, libFLAC, Chromaprint, mp4v2. ** Linux *** Compile for KDE 4 cmake . -DWITH_APPS=KDE make make install *** Compile for Qt 4 (no KDE) cmake . -DCMAKE_BUILD_TYPE=Release -DWITH_APPS=Qt make make install The install directory can be assigned to DESTDIR *** Create Debian Packages Make sure that you have installed all packages listed in deb/control, section Build-Depends. ./build-deb.sh *** Create RPM Package Copy kid3.spec into the SPEC directory and the tar.gz archive into the SOURCES directory. rpmbuild -ba kid3.spec ** Windows - Install Qt/Windows Open Source Edition, CMake, MinGW and MSYS - Compile and install the libraries with MinGW and MSYS: id3lib, libvorbis, libogg, FLAC, TagLib using the script buildlibs.sh - Install xsltproc and an XSL stylesheet to transform Docbook to HTML (http://sourceforge.net/projects/docbook) - Point environment variables to the installed programs: * %PERLDIR%\bin\perl * %XSLTPROCDIR%\xsltproc * %DOCBOOKDIR%\html\docbook.xsl * %DUMPBINDIR%\dumpbin * %CMAKEDIR%\bin\cmake - start win32/buildkid3.bat to build and create a ZIP-file ** Mac OS X - Install Qt libraries for Mac, CMake and XCode - Compile and install the libraries id3lib, libvorbis, libogg, FLAC, TagLib using the script buildlibs.sh - Install xsltproc and an XSL stylesheet to transform Docbook to HTML (http://sourceforge.net/projects/docbook) in $HOME/docbook-xsl-1.72.0/html/docbook.xsl - start macosx/buildkid3.sh to build and create a dmg image kid3-3.0.2/LICENSE000066400000000000000000000011241224603511300133460ustar00rootroot00000000000000Kid3 is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Kid3 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . kid3-3.0.2/NEWS000066400000000000000000000000011224603511300130310ustar00rootroot00000000000000 kid3-3.0.2/README000066400000000000000000000015041224603511300132230ustar00rootroot00000000000000Kid3 - Efficient Audio Tagger With Kid3 you can: - Edit ID3v1.1 tags - Edit all ID3v2.3 and ID3v2.4 frames - Convert between ID3v1.1, ID3v2.3 and ID3v2.4 tags - Edit tags in MP3, Ogg/Vorbis, FLAC, MPC, APE, MP4/AAC, MP2, Speex, TrueAudio, WavPack, WMA, WAV, AIFF files and tracker modules. - Edit tags of multiple files, e.g. the artist, album, year and genre of all files of an album typically have the same values and can be set together. - Generate tags from filenames - Generate tags from the contents of tag fields - Generate filenames from tags - Rename directories from tags - Generate playlist files - Automatic case conversion and string translation - Import and export album data - Import from gnudb.org, TrackType.org, MusicBrainz, Discogs, Amazon Kid3 uses Qt and runs under GNU/Linux (KDE or only Qt) and Windows. kid3-3.0.2/build-deb.sh000077500000000000000000000005131224603511300145300ustar00rootroot00000000000000#!/bin/sh # Build Debian package. cp -R deb debian rm -rf debian/source debian/watch if test "$(lsb_release -sc)" = "squeeze"; then sed -i '/lib\(av\|chromaprint\)/ d' debian/control sed -i 's/-DWITH_MP4V2=/-DWITH_CHROMAPRINT=OFF -DWITH_MP4V2=/; s/dh_builddeb -- -Zxz/dh_builddeb/' debian/rules fi dpkg-buildpackage -rfakeroot kid3-3.0.2/buildlibs.sh000077500000000000000000001004321224603511300146530ustar00rootroot00000000000000#!/bin/bash # This script can be used to build static libraries for the Windows and Mac # versions of Kid3. Linux and BSD users do not need it because the libraries # can be installed from their repositories. # # First you have to install the necessary tools: # # For Windows: # # Building the libraries needs msys/MinGW, CMake, yasm. # You should use the MinGW which comes with Qt and add msys tools to build # the libraries. # Download yasm from # http://www.tortall.net/projects/yasm/releases/yasm-1.2.0-win32.exe # and copy it into msys /bin as yasm.exe. # Start the msys shell, add cmake to the path and start this script. # When the script has run successfully, the libraries are installed below # /usr/local/ in msys. You can then proceed to the Kid3 build. # # Building Kid3 needs MinGW, CMake, Qt, xsltproc, html\docbook.xsl, dumpbin. # Dumpbin is needed for the final packages and can be found in the MS SDK or # MS Visual C++ Express Edition. Set the environment variables in # win32/buildkid3.bat, so that these tools can be found, then start # buildkid3.bat from a Windows command prompt. # # You can also build a Windows version from Linux using the MinGW cross # compiler. Set compiler="cross-mingw" below. # # For Mac: XCode, Qt, html\docbook.xsl. XCode and Qt should be installed at # the default location, docbook.xsl in # $HOME/docbook-xsl-1.72.0/html/docbook.xsl. # # The source code for the libraries is downloaded from Debian and Ubuntu # repositories. If the files are no longer available, use a later version, # it should still work. # # buildlibs.sh will download, build and install zlib, libogg, libvorbis, # flac, id3lib, taglib, libav, chromaprint. You are then ready to build Kid3 # from the win32 or macosx directories by starting buildkid3.bat (Windows) or # buildkid3.sh (Mac). # Exit if an error occurs set -e thisdir=$(pwd) kernel=$(uname) test ${kernel:0:5} = "MINGW" && kernel="MINGW" compiler="gcc" qt_version=4.8.5 zlib_version=1.2.8 libogg_version=1.3.1 libav_version=0.8.7 # Uncomment for debug build #ENABLE_DEBUG=--enable-debug #CMAKE_BUILD_TYPE_DEBUG="-DCMAKE_BUILD_TYPE=Debug" if ! which cmake >/dev/null; then echo cmake not found. return exit 1 fi if test $kernel = "MINGW"; then CMAKE_OPTIONS="-G \"MSYS Makefiles\" -DCMAKE_INSTALL_PREFIX=/usr/local" elif test $kernel = "Darwin"; then CMAKE_OPTIONS="-G \"Unix Makefiles\"" fi if test "$compiler" = "cross-mingw"; then CMAKE_OPTIONS="$CMAKE_OPTIONS -DCMAKE_TOOLCHAIN_FILE=$thisdir/source/mingw.cmake" CONFIGURE_OPTIONS="--host=i586-mingw32msvc" fi if test $kernel = "Darwin" && test $(uname -m) = "x86_64"; then #ARCH=i386 if test "$ARCH" = "i386"; then # To build a 32-bit Mac OS X version of Kid3 use: # cmake -G "Unix Makefiles" -DCMAKE_CXX_FLAGS="-arch i386" -DCMAKE_C_FLAGS="-arch i386" -DCMAKE_EXE_LINKER_FLAGS="-arch i386" -DQT_QMAKE_EXECUTABLE=/usr/local/Trolltech/Qt-4.8.5-i386/bin/qmake -DCMAKE_BUILD_TYPE=Release -DWITH_FFMPEG=ON -DCMAKE_INSTALL_PREFIX= ../kid3 # Building multiple architectures needs ARCH_FLAG="-arch i386 -arch x86_64", # CONFIGURE_OPTIONS="--disable-dependency-tracking", but it fails with libav. ARCH_FLAG="-arch i386" else ARCH_FLAG="-Xarch_x86_64" fi CMAKE_OPTIONS="$CMAKE_OPTIONS -DCMAKE_C_FLAGS=\"-O2 $ARCH_FLAG -mmacosx-version-min=10.5\" -DCMAKE_CXX_FLAGS=\"-O2 $ARCH_FLAG -mmacosx-version-min=10.5 -fvisibility=hidden -fvisibility-inlines-hidden\"" export CFLAGS="-O2 $ARCH_FLAG -mmacosx-version-min=10.5" export CXXFLAGS="-O2 $ARCH_FLAG -mmacosx-version-min=10.5" export LDFLAGS="$ARCH_FLAG -mmacosx-version-min=10.5" fi if which wget >/dev/null; then DOWNLOAD=wget else DOWNLOAD="curl -skfLO" fi fixcmakeinst() { if test -d inst && test $kernel = "MINGW"; then cd inst if test -d prg; then rm -rf usr mv prg/msys usr rmdir prg elif test -d msys; then rm -rf usr mv msys/1.0 usr rmdir msys elif test -d MinGW; then mv MinGW usr fi cd .. fi } # Download sources test -d source || mkdir source cd source test -f flac_1.3.0-2.debian.tar.gz || $DOWNLOAD http://ftp.de.debian.org/debian/pool/main/f/flac/flac_1.3.0-2.debian.tar.gz test -f flac_1.3.0.orig.tar.xz || $DOWNLOAD http://ftp.de.debian.org/debian/pool/main/f/flac/flac_1.3.0.orig.tar.xz test -f id3lib3.8.3_3.8.3-15.debian.tar.gz || $DOWNLOAD http://ftp.de.debian.org/debian/pool/main/i/id3lib3.8.3/id3lib3.8.3_3.8.3-15.debian.tar.gz test -f id3lib3.8.3_3.8.3.orig.tar.gz || $DOWNLOAD http://ftp.de.debian.org/debian/pool/main/i/id3lib3.8.3/id3lib3.8.3_3.8.3.orig.tar.gz test -f libogg_1.3.1-1.diff.gz || $DOWNLOAD http://ftp.de.debian.org/debian/pool/main/libo/libogg/libogg_1.3.1-1.diff.gz test -f libogg_1.3.1.orig.tar.gz || $DOWNLOAD http://ftp.de.debian.org/debian/pool/main/libo/libogg/libogg_1.3.1.orig.tar.gz test -f libvorbis_1.3.2-1.3.diff.gz || $DOWNLOAD http://ftp.de.debian.org/debian/pool/main/libv/libvorbis/libvorbis_1.3.2-1.3.diff.gz test -f libvorbis_1.3.2.orig.tar.gz || $DOWNLOAD http://ftp.de.debian.org/debian/pool/main/libv/libvorbis/libvorbis_1.3.2.orig.tar.gz test -f taglib-1.9.tar.gz || $DOWNLOAD http://taglib.github.io/releases/taglib-1.9.tar.gz test -f zlib_1.2.8.dfsg-1.debian.tar.gz || $DOWNLOAD http://ftp.de.debian.org/debian/pool/main/z/zlib/zlib_1.2.8.dfsg-1.debian.tar.gz test -f zlib_1.2.8.dfsg.orig.tar.gz || $DOWNLOAD http://ftp.de.debian.org/debian/pool/main/z/zlib/zlib_1.2.8.dfsg.orig.tar.gz # With the new libav 9.5, some M4A fingerprints are not recognized, # so we'll stick with the old. if test "$libav_version" = "0.8.7"; then test -f libav_0.8.7.orig.tar.xz || $DOWNLOAD http://ftp.de.debian.org/debian/pool/main/liba/libav/libav_0.8.7.orig.tar.xz test -f libav_0.8.7-1.debian.tar.gz || $DOWNLOAD http://ftp.de.debian.org/debian/pool/main/liba/libav/libav_0.8.7-1.debian.tar.gz else test -f libav_9.9.orig.tar.xz || $DOWNLOAD http://ftp.de.debian.org/debian/pool/main/liba/libav/libav_9.9.orig.tar.xz test -f libav_9.9-1.debian.tar.gz || $DOWNLOAD http://ftp.de.debian.org/debian/pool/main/liba/libav/libav_9.9-1.debian.tar.gz fi test -f chromaprint_0.7.orig.tar.gz || $DOWNLOAD http://ftp.de.debian.org/debian/pool/main/c/chromaprint/chromaprint_0.7.orig.tar.gz test -f chromaprint_0.7-2.debian.tar.gz || $DOWNLOAD http://ftp.de.debian.org/debian/pool/main/c/chromaprint/chromaprint_0.7-2.debian.tar.gz #test -f mp4v2_1.9.1+svn479~dfsg0.orig.tar.bz2 || #$DOWNLOAD http://ftp.de.debian.org/debian/pool/main/m/mp4v2/mp4v2_1.9.1+svn479~dfsg0.orig.tar.bz2 #test -f mp4v2_1.9.1+svn479~dfsg0-3.debian.tar.gz || #$DOWNLOAD http://ftp.de.debian.org/debian/pool/main/m/mp4v2/mp4v2_1.9.1+svn479~dfsg0-3.debian.tar.gz # Create patch files if test "$compiler" = "cross-mingw"; then test -f mingw.cmake || cat >mingw.cmake <fink_flac.patch <<"EOF" diff -ruN flac-1.2.1/patches/fixrpath.sh flac-1.2.1.new/patches/fixrpath.sh --- flac-1.2.1/patches/fixrpath.sh 1969-12-31 19:00:00.000000000 -0500 +++ flac-1.2.1.new/patches/fixrpath.sh 2008-02-18 10:51:07.000000000 -0500 @@ -0,0 +1,28 @@ +#!/bin/sh +# $Id: fixrpath,v 1.1 2004/05/27 10:48:25 kobras Exp $ +# libtool -rpath workaround based on a suggestion by Yann Dirson +# +# +# It is supposed to be inserted in configure.in, but I didn't want +# to re-run autoconf (since that bloats the Debian diff unnecessarily), +# so I just patch libtool after running configure. -- Richard Braakman +# +# +# The version of libtool included with LessTif unfortunately insists on +# linking with -rpath, i.e. hardwiring locations. This is not desirable. +# +# The dummy define is improbable enough not to conflict with anything; it is +# just here to fool libtool by making it believe it gave some useful info to +# gcc. +# +# This will also patch the generated libtool to explicitly +# link libraries against the libraries they depend on. (particularly libc) + +for i in libtool libtool-disable-static; do +sed < $i > $i-2 \ + -e 's/^hardcode_libdir_flag_spec.*$/hardcode_libdir_flag_spec=" -D__LIBTOOL_IS_A_FOOL__ "/' \ + -e '/^archive_cmds="/s/"$/ \$deplibs"/' +mv $i-2 $i +chmod 755 $i +done + diff -ruN flac-1.2.1/patches/ltmain.sh.patch flac-1.2.1.new/patches/ltmain.sh.patch --- flac-1.2.1/patches/ltmain.sh.patch 1969-12-31 19:00:00.000000000 -0500 +++ flac-1.2.1.new/patches/ltmain.sh.patch 2008-02-18 10:48:01.000000000 -0500 @@ -0,0 +1,11 @@ +--- ltmain.sh.orig Fri Feb 4 21:22:19 2005 ++++ ltmain.sh Wed Feb 23 19:09:37 2005 +@@ -2280,7 +2280,7 @@ + { test "$prefer_static_libs" = no || test -z "$old_library"; }; then + if test "$installed" = no; then + notinst_deplibs="$notinst_deplibs $lib" +- need_relink=yes ++ need_relink=no + fi + # This is a shared library + diff -ruN flac-1.2.1/patches/nasm.h.patch flac-1.2.1.new/patches/nasm.h.patch --- flac-1.2.1/patches/nasm.h.patch 1969-12-31 19:00:00.000000000 -0500 +++ flac-1.2.1.new/patches/nasm.h.patch 2007-01-25 21:34:54.000000000 -0500 @@ -0,0 +1,14 @@ +--- src/libFLAC/ia32/nasm.h~ 2005-01-25 13:14:22.000000000 +0900 ++++ src/libFLAC/ia32/nasm.h 2006-03-15 18:07:23.000000000 +0900 +@@ -49,6 +49,11 @@ + %idefine code_section section .text align=16 + %idefine data_section section .data align=32 + %idefine bss_section section .bss align=32 ++%elifdef OBJ_FORMAT_macho ++ %define FLAC__PUBLIC_NEEDS_UNDERSCORE ++ %idefine code_section section .text ++ %idefine data_section section .data ++ %idefine bss_section section .bss + %else + %error unsupported object format! + %endif diff -ru flac-1.3.0/src/plugin_xmms/Makefile.in flac-1.3.0.new/src/plugin_xmms/Makefile.in --- flac-1.3.0/src/plugin_xmms/Makefile.in 2013-05-27 10:11:57.000000000 +0200 +++ flac-1.3.0.new/src/plugin_xmms/Makefile.in 2013-10-16 13:30:02.000000000 +0200 @@ -361,7 +361,6 @@ $(top_builddir)/src/share/replaygain_synthesis/libreplaygain_synthesis.la \ $(top_builddir)/src/share/utf8/libutf8.la \ $(top_builddir)/src/libFLAC/libFLAC.la \ - -L$(top_builddir)/src/libFLAC/.libs \ @XMMS_LIBS@ \ @LIBICONV@ EOF test -f flac_1.2.1_size_t_max_patch.diff || cat >flac_1.2.1_size_t_max_patch.diff <<"EOF" diff -ru flac-1.2.1.orig/include/share/alloc.h flac-1.2.1/include/share/alloc.h --- flac-1.2.1.orig/include/share/alloc.h Wed Sep 12 06:32:22 2007 +++ flac-1.2.1/include/share/alloc.h Mon Mar 3 18:57:14 2008 @@ -33,6 +33,10 @@ #endif #include /* for size_t, malloc(), etc */ +#if defined __MINGW32__ && !defined SIZE_T_MAX +# define SIZE_T_MAX UINT_MAX +#endif + #ifndef SIZE_MAX # ifndef SIZE_T_MAX # ifdef _MSC_VER EOF test -f id3lib-3.8.3_mingw.patch || cat >id3lib-3.8.3_mingw.patch <<"EOF" diff -ru id3lib-3.8.3.orig/configure.in id3lib-3.8.3/configure.in --- id3lib-3.8.3.orig/configure.in 2012-02-05 13:09:59 +0100 +++ id3lib-3.8.3/configure.in 2012-02-05 13:16:33 +0100 @@ -222,7 +222,7 @@ AC_LANG_CPLUSPLUS AC_CHECK_HEADERS(libcw/sys.h) AC_CHECK_HEADERS(cctype climits cstdio cstdlib bitset cstring) -AC_CHECK_HEADERS(fstream iostream iomanip vector \ +AC_CHECK_HEADERS(fstream iostream vector \ ,,AC_MSG_ERROR([Missing a vital header file for id3lib - download them here: http://gcc.gnu.org/libstdc++/ or better - compile a newer compiler like gcc3.x]) ) AC_CHECK_HEADERS( \ @@ -248,10 +248,10 @@ AM_CONDITIONAL(ID3_NEEDGETOPT_LONG, test x$ac_cv_func_getopt_long = xno) AC_CHECK_FUNCS(mkstemp) -AC_CHECK_FUNCS( - truncate \ - ,,AC_MSG_ERROR([Missing a vital function for id3lib]) -) +#AC_CHECK_FUNCS( +# truncate \ +# ,,AC_MSG_ERROR([Missing a vital function for id3lib]) +#) dnl Checks for typedefs, structures, and compiler characteristics. AC_TYPE_SIZE_T diff -ru id3lib-3.8.3.orig/include/id3/globals.h id3lib-3.8.3/include/id3/globals.h --- id3lib-3.8.3.orig/include/id3/globals.h 2012-02-05 13:09:59 +0100 +++ id3lib-3.8.3/include/id3/globals.h 2012-02-05 13:15:42 +0100 @@ -41,7 +41,7 @@ * we prefix variable declarations so they can * properly get exported in windows dlls. */ -#ifdef WIN32 +#ifdef __MSVC_VER # define LINKOPTION_STATIC 1 //both for use and creation of static lib # define LINKOPTION_CREATE_DYNAMIC 2 //should only be used by prj/id3lib.dsp # define LINKOPTION_USE_DYNAMIC 3 //if your project links id3lib dynamic EOF test -f id3lib-fix-utf16-stringlists.patch || cat >id3lib-fix-utf16-stringlists.patch <taglib-msvc.patch <<"EOF" diff -ru taglib-1.8.orig/CMakeLists.txt taglib-1.8/CMakeLists.txt --- taglib-1.8.orig/CMakeLists.txt Thu Sep 6 20:03:15 2012 +++ taglib-1.8/CMakeLists.txt Fri Feb 22 06:41:36 2013 @@ -31,6 +31,10 @@ set(LIB_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/lib${LIB_SUFFIX}" CACHE PATH "The subdirectory relative to the install prefix where libraries will be installed (default is /lib${LIB_SUFFIX})" FORCE) set(INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/include" CACHE PATH "The subdirectory to the header prefix" FORCE) +if(MSVC) + add_definitions(/Zc:wchar_t-) +endif(MSVC) + if(APPLE) option(BUILD_FRAMEWORK "Build an OS X framework" OFF) set(FRAMEWORK_INSTALL_DIR "/Library/Frameworks" CACHE STRING "Directory to install frameworks to.") EOF if test "$libav_version" = "0.8.7"; then test -f libav_sws.patch || cat >libav_sws.patch <<"EOF" --- cmdutils.c.org 2011-09-17 13:36:43.000000000 -0700 +++ cmdutils.c 2011-09-17 15:54:37.453134577 -0700 @@ -311,6 +311,11 @@ const AVOption *oc, *of, *os; char opt_stripped[128]; const char *p; +// SPage: avoid sws_get_class failure +#if !CONFIG_SWSCALE +# define sws_get_class(x) 0 +#endif + const AVClass *cc = avcodec_get_class(), *fc = avformat_get_class(), *sc = sws_get_class(); if (!(p = strchr(opt, ':'))) EOF fi cd .. # Extract and patch sources echo "### Extracting zlib" if ! test -d zlib-1.2.8; then tar xzf source/zlib_1.2.8.dfsg.orig.tar.gz cd zlib-1.2.8/ tar xzf ../source/zlib_1.2.8.dfsg-1.debian.tar.gz || true echo Can be ignored: Cannot create symlink to debian.series for f in $(cat debian/patches/debian.series); do patch -p1 win32/VS2008/libogg.vsprops cd .. fi echo "### Extracting libflac" if ! test -d flac-1.3.0; then unxz -c source/flac_1.3.0.orig.tar.xz | tar x cd flac-1.3.0/ tar xzf ../source/flac_1.3.0-2.debian.tar.gz for f in $(cat debian/patches/series); do patch -p1 makefile.win32 cd .. fi echo "### Extracting taglib" if ! test -d taglib-1.9; then tar xzf source/taglib-1.9.tar.gz cd taglib-1.9/ patch -p1 <../source/taglib-msvc.patch cd .. fi echo "### Extracting libav" if test "$libav_version" = "0.8.7"; then if ! test -d libav-0.8.7; then unxz -c source/libav_0.8.7.orig.tar.xz | tar x cd libav-0.8.7/ tar xzf ../source/libav_0.8.7-1.debian.tar.gz oldifs=$IFS IFS=' ' for f in $(cat debian/patches/series); do if test "${f:0:1}" != "#"; then patch -p1 Makefile.inst mkdir -p inst/usr/local make install -f Makefile.inst prefix=`pwd`/inst/usr/local fi cd inst tar czf ../../bin/zlib-1.2.8.tgz usr cd ../.. echo "### Building libogg" cd libogg-1.3.1/ test -f Makefile || ./configure --enable-shared=no --enable-static=yes $ENABLE_DEBUG $CONFIGURE_OPTIONS make mkdir -p inst make install DESTDIR=`pwd`/inst cd inst tar czf ../../bin/libogg-1.3.1.tgz usr cd ../.. echo "### Building libvorbis" cd libvorbis-1.3.2/ if test "$compiler" = "cross-mingw"; then test -f Makefile || PKG_CONFIG= ./configure --enable-shared=no --enable-static=yes --with-ogg=$thisdir/libogg-$libogg_version/inst/usr/local $ENABLE_DEBUG $CONFIGURE_OPTIONS else test -f Makefile || ./configure --enable-shared=no --enable-static=yes --with-ogg=$thisdir/libogg-$libogg_version/inst/usr/local $ENABLE_DEBUG $CONFIGURE_OPTIONS fi make mkdir -p inst make install DESTDIR=`pwd`/inst cd inst tar czf ../../bin/libvorbis-1.3.2.tgz usr cd ../.. echo "### Building libflac" cd flac-1.3.0/ configure_args="--enable-shared=no --enable-static=yes --with-ogg=$thisdir/libogg-$libogg_version/inst/usr/local $ENABLE_DEBUG $CONFIGURE_OPTIONS" if test $kernel = "Darwin"; then configure_args="$configure_args --disable-asm-optimizations" fi test -f Makefile || ./configure $configure_args make mkdir -p inst make install DESTDIR=`pwd`/inst cd inst tar czf ../../bin/flac-1.3.0.tgz usr cd ../.. echo "### Building id3lib" cd id3lib-3.8.3/ autoconf test -f Makefile || CPPFLAGS=-I/usr/local/include LDFLAGS=-L/usr/local/lib ./configure --enable-shared=no --enable-static=yes $ENABLE_DEBUG $CONFIGURE_OPTIONS SED=sed make mkdir -p inst make install DESTDIR=`pwd`/inst cd inst tar czf ../../bin/id3lib-3.8.3.tgz usr cd ../.. echo "### Building taglib" cd taglib-1.9/ test -f Makefile || eval cmake -DWITH_ASF=ON -DWITH_MP4=ON -DINCLUDE_DIRECTORIES=/usr/local/include -DLINK_DIRECTORIES=/usr/local/lib -DENABLE_STATIC=ON -DZLIB_ROOT=../zlib-$zlib_version/inst/usr/local $CMAKE_BUILD_TYPE_DEBUG $CMAKE_OPTIONS make mkdir -p inst make install DESTDIR=`pwd`/inst fixcmakeinst cd inst tar czf ../../bin/taglib-1.9.tgz usr cd ../.. echo "### Building libav" if test "$libav_version" = "0.8.7"; then cd libav-0.8.7 # configure needs yasm and pr # On msys, make >= 3.81 is needed. # Most options taken from # http://oxygene.sk/lukas/2011/04/minimal-audio-only-ffmpeg-build-with-mingw32/ # Disable-sse avoids a SEGFAULT under MinGW. # Later versions (tested with libav-HEAD-5d2be71) do not have # --enable-ffmpeg and additionally need --disable-mmx --disable-mmxext. # The two --disable-hwaccel were added for MinGW-builds GCC 4.7.2. if test "$compiler" = "cross-mingw"; then sed -i 's/^\(.*-Werror=missing-prototypes\)/#\1/' ./configure AV_CONFIGURE_OPTIONS="--cross-prefix=i586-mingw32msvc- --arch=x86 --target-os=mingw32 --sysinclude=/usr/i586-mingw32msvc/include" fi ./configure \ --enable-memalign-hack \ --disable-shared \ --enable-static \ --disable-debug \ --disable-avdevice \ --disable-avfilter \ --disable-pthreads \ --disable-swscale \ --enable-ffmpeg \ --disable-network \ --disable-muxers \ --disable-demuxers \ --disable-sse \ --enable-rdft \ --enable-demuxer=aac \ --enable-demuxer=ac3 \ --enable-demuxer=ape \ --enable-demuxer=asf \ --enable-demuxer=flac \ --enable-demuxer=matroska_audio \ --enable-demuxer=mp3 \ --enable-demuxer=mpc \ --enable-demuxer=mov \ --enable-demuxer=mpc8 \ --enable-demuxer=ogg \ --enable-demuxer=tta \ --enable-demuxer=wav \ --enable-demuxer=wv \ --disable-bsfs \ --disable-filters \ --disable-parsers \ --enable-parser=aac \ --enable-parser=ac3 \ --enable-parser=mpegaudio \ --disable-protocols \ --enable-protocol=file \ --disable-indevs \ --disable-outdevs \ --disable-encoders \ --disable-decoders \ --enable-decoder=aac \ --enable-decoder=ac3 \ --enable-decoder=alac \ --enable-decoder=ape \ --enable-decoder=flac \ --enable-decoder=mp1 \ --enable-decoder=mp2 \ --enable-decoder=mp3 \ --enable-decoder=mpc7 \ --enable-decoder=mpc8 \ --enable-decoder=tta \ --enable-decoder=vorbis \ --enable-decoder=wavpack \ --enable-decoder=wmav1 \ --enable-decoder=wmav2 \ --enable-decoder=pcm_alaw \ --enable-decoder=pcm_dvd \ --enable-decoder=pcm_f32be \ --enable-decoder=pcm_f32le \ --enable-decoder=pcm_f64be \ --enable-decoder=pcm_f64le \ --enable-decoder=pcm_s16be \ --enable-decoder=pcm_s16le \ --enable-decoder=pcm_s16le_planar \ --enable-decoder=pcm_s24be \ --enable-decoder=pcm_daud \ --enable-decoder=pcm_s24le \ --enable-decoder=pcm_s32be \ --enable-decoder=pcm_s32le \ --enable-decoder=pcm_s8 \ --enable-decoder=pcm_u16be \ --enable-decoder=pcm_u16le \ --enable-decoder=pcm_u24be \ --enable-decoder=pcm_u24le \ --enable-decoder=rawvideo \ --disable-hwaccel=h264_dxva2 \ --disable-hwaccel=mpeg2_dxva2 $AV_CONFIGURE_OPTIONS make mkdir -p inst make install DESTDIR=`pwd`/inst cd inst tar czf ../../bin/libav-0.8.7.tgz usr cd ../.. else cd libav-9.9 # configure needs yasm and pr # On msys, make >= 3.81 is needed. # Most options taken from # http://oxygene.sk/lukas/2011/04/minimal-audio-only-ffmpeg-build-with-mingw32/ # Disable-sse avoids a SEGFAULT under MinGW. # Later versions (tested with libav-HEAD-5d2be71) do not have # --enable-ffmpeg and additionally need --disable-mmx --disable-mmxext. # The two --disable-hwaccel were added for MinGW-builds GCC 4.7.2. if test "$compiler" = "cross-mingw"; then sed -i 's/^\(.*-Werror=missing-prototypes\)/#\1/' ./configure AV_CONFIGURE_OPTIONS="--cross-prefix=i586-mingw32msvc- --arch=x86 --target-os=mingw32 --sysinclude=/usr/i586-mingw32msvc/include" fi ./configure \ --enable-memalign-hack \ --disable-shared \ --enable-static \ --disable-debug \ --disable-avdevice \ --disable-avfilter \ --disable-pthreads \ --disable-swscale \ --disable-network \ --disable-muxers \ --disable-demuxers \ --disable-sse \ --enable-rdft \ --enable-demuxer=aac \ --enable-demuxer=ac3 \ --enable-demuxer=ape \ --enable-demuxer=asf \ --enable-demuxer=flac \ --enable-demuxer=matroska_audio \ --enable-demuxer=mp3 \ --enable-demuxer=mpc \ --enable-demuxer=mov \ --enable-demuxer=mpc8 \ --enable-demuxer=ogg \ --enable-demuxer=tta \ --enable-demuxer=wav \ --enable-demuxer=wv \ --disable-bsfs \ --disable-filters \ --disable-parsers \ --enable-parser=aac \ --enable-parser=ac3 \ --enable-parser=mpegaudio \ --disable-protocols \ --enable-protocol=file \ --disable-indevs \ --disable-outdevs \ --disable-encoders \ --disable-decoders \ --enable-decoder=aac \ --enable-decoder=ac3 \ --enable-decoder=alac \ --enable-decoder=ape \ --enable-decoder=flac \ --enable-decoder=mp1 \ --enable-decoder=mp2 \ --enable-decoder=mp3 \ --enable-decoder=mpc7 \ --enable-decoder=mpc8 \ --enable-decoder=tta \ --enable-decoder=vorbis \ --enable-decoder=wavpack \ --enable-decoder=wmav1 \ --enable-decoder=wmav2 \ --enable-decoder=pcm_alaw \ --enable-decoder=pcm_dvd \ --enable-decoder=pcm_f32be \ --enable-decoder=pcm_f32le \ --enable-decoder=pcm_f64be \ --enable-decoder=pcm_f64le \ --enable-decoder=pcm_s16be \ --enable-decoder=pcm_s16le \ --enable-decoder=pcm_s16le_planar \ --enable-decoder=pcm_s24be \ --enable-decoder=pcm_daud \ --enable-decoder=pcm_s24le \ --enable-decoder=pcm_s32be \ --enable-decoder=pcm_s32le \ --enable-decoder=pcm_s8 \ --enable-decoder=pcm_u16be \ --enable-decoder=pcm_u16le \ --enable-decoder=pcm_u24be \ --enable-decoder=pcm_u24le \ --enable-decoder=rawvideo \ --disable-hwaccel=h264_dxva2 \ --disable-hwaccel=mpeg2_dxva2 $AV_CONFIGURE_OPTIONS make mkdir -p inst make install DESTDIR=`pwd`/inst cd inst tar czf ../../bin/libav-9.9.tgz usr cd ../.. fi echo "### Building chromaprint" # The zlib library path was added for MinGW-builds GCC 4.7.2. cd chromaprint-0.7/ test -f Makefile || eval cmake -DBUILD_EXAMPLES=ON -DBUILD_SHARED_LIBS=OFF -DEXTRA_LIBS=\"-L$thisdir/zlib-$zlib_version/inst/usr/local/lib -lz\" -DFFMPEG_ROOT=$thisdir/libav-$libav_version/inst/usr/local $CMAKE_BUILD_TYPE_DEBUG $CMAKE_OPTIONS mkdir -p inst make install DESTDIR=`pwd`/inst fixcmakeinst cd inst tar czf ../../bin/chromaprint-0.7.tgz usr cd ../.. #echo "### Building mp4v2" # #cd mp4v2-1.9.1+svn479~dfsg0/ #test -f Makefile || ./configure --enable-shared=no --enable-static=yes --disable-gch $CONFIGURE_OPTIONS #mkdir inst #make install DESTDIR=`pwd`/inst #cd inst #tar czf ../../bin/mp4v2-1.9.1+svn479.tgz usr #cd ../.. echo "### Installing to root directory" BUILDROOT=/ if test $kernel = "Linux"; then test -d buildroot || mkdir buildroot BUILDROOT=`pwd`/buildroot/ # Static build can be tested from Linux in kid3 directory if ! test -d kid3; then mkdir kid3 if test "$compiler" = "cross-mingw"; then cat >kid3/build.sh <kid3/make_package.sh <<"EOF" #!/bin/sh VERSION=$(grep VERSION config.h | cut -d'"' -f2) INSTDIR=kid3-$VERSION-win32 QT_PREFIX=$(sed "s/set(QT_PREFIX \(.*\))/\1/;q" ../source/mingw.cmake) QT_BIN_DIR=${QT_PREFIX}bin QT_TRANSLATIONS_DIR=${QT_PREFIX}translations MINGW_DIR=/windows/msys/1.0/mingw/bin make install/strip DESTDIR=$(pwd)/$INSTDIR echo "### Ignore make error" cp -f po/*.qm doc/*/kid3*.html $INSTDIR for f in QtCore4.dll QtNetwork4.dll QtGui4.dll QtXml4.dll phonon4.dll; do cp $QT_BIN_DIR/$f $INSTDIR done for f in libgcc_s_dw2-1.dll; do cp $MINGW_DIR/$f $INSTDIR done for f in po/*.qm; do l=${f#*_}; l=${l%.qm}; test -f $QT_TRANSLATIONS_DIR/qt_$l.qm && cp $QT_TRANSLATIONS_DIR/qt_$l.qm $INSTDIR done rm -f $INSTDIR.zip 7z a $INSTDIR.zip $INSTDIR EOF chmod +x kid3/make_package.sh else cat >kid3/build.sh <<"EOF" BUILDPREFIX=$(cd ..; pwd)/buildroot/usr/local export PKG_CONFIG_PATH=$BUILDPREFIX/lib/pkgconfig cmake -DWITH_TAGLIB=OFF -DHAVE_TAGLIB=1 -DTAGLIB_LIBRARIES:STRING="-L$BUILDPREFIX/lib -ltag" -DTAGLIB_CFLAGS:STRING="-I$BUILDPREFIX/include/taglib -I$BUILDPREFIX/include -DTAGLIB_STATIC" -DCMAKE_CXX_FLAGS_DEBUG:STRING="-g -DID3LIB_LINKOPTION=1 -DFLAC__NO_DLL" -DCMAKE_INCLUDE_PATH=$BUILDPREFIX/include -DCMAKE_LIBRARY_PATH=$BUILDPREFIX/lib -DCMAKE_PROGRAM_PATH=$BUILDPREFIX/bin -DWITH_FFMPEG=ON -DFFMPEG_ROOT=$BUILDPREFIX -DCMAKE_BUILD_TYPE=Debug -DBUILD_SHARED_LIBS=OFF -DWITH_GCC_PCH=OFF -DWITH_APPS="Qt;CLI" -DCMAKE_INSTALL_PREFIX= -DWITH_BINDIR=. -DWITH_DATAROOTDIR=. -DWITH_DOCDIR=. -DWITH_TRANSLATIONSDIR=. ../../kid3 EOF fi chmod +x kid3/build.sh fi elif test $kernel = "Darwin"; then sudo chmod go+w ${BUILDROOT}usr/local fi tar xzf bin/zlib-${zlib_version}.tgz -C $BUILDROOT tar xzf bin/libogg-${libogg_version}.tgz -C $BUILDROOT tar xzf bin/libvorbis-1.3.2.tgz -C $BUILDROOT tar xzf bin/flac-1.3.0.tgz -C $BUILDROOT tar xzf bin/id3lib-3.8.3.tgz -C $BUILDROOT tar xzf bin/taglib-1.9.tgz -C $BUILDROOT tar xzf bin/libav-${libav_version}.tgz -C $BUILDROOT tar xzf bin/chromaprint-0.7.tgz -C $BUILDROOT #tar xzf bin/mp4v2-1.9.1+svn479.tgz -C $BUILDROOT if test $kernel = "Darwin"; then sudo chmod go-w ${BUILDROOT}usr/local fi fi echo "### Built successfully" kid3-3.0.2/config.h.cmake000066400000000000000000000012221224603511300150350ustar00rootroot00000000000000/* config.h. Generated by cmake from config.h.cmake */ #ifndef CONFIG_H #define CONFIG_H #define VERSION "@CPACK_PACKAGE_VERSION@" #define RELEASE_YEAR "@RELEASE_YEAR@" /* Define if you have QtDBus installed */ #cmakedefine HAVE_QTDBUS 1 /* Define if you have Phonon installed */ #cmakedefine HAVE_PHONON 1 /* Define if mntent.h is available */ #cmakedefine HAVE_MNTENT_H 1 #cmakedefine CFG_DATAROOTDIR "@CFG_DATAROOTDIR@" #cmakedefine CFG_DOCDIR "@CFG_DOCDIR@" #cmakedefine CFG_TRANSLATIONSDIR "@CFG_TRANSLATIONSDIR@" /* Path to plugins directory relative from directory containing executable */ #cmakedefine CFG_PLUGINSDIR "@CFG_PLUGINSDIR@" #endif kid3-3.0.2/deb/000077500000000000000000000000001224603511300130755ustar00rootroot00000000000000kid3-3.0.2/deb/changelog000066400000000000000000000257231224603511300147600ustar00rootroot00000000000000kid3 (3.0.2-0) unstable; urgency=low * New upstream release. -- Urs Fleisch Wed, 27 Nov 2013 17:39:57 +0100 kid3 (3.0.1-0) unstable; urgency=low * New upstream release. -- Urs Fleisch Tue, 29 Oct 2013 18:17:24 +0100 kid3 (3.0-0) unstable; urgency=low * New upstream release. -- Urs Fleisch Mon, 14 Oct 2013 21:32:58 +0200 kid3 (2.3-1) experimental; urgency=low * New upstream release. -- Patrick Matthäi Thu, 28 Mar 2013 11:23:13 +0100 kid3 (2.2.1-1) experimental; urgency=low * New upstream release. -- Patrick Matthäi Fri, 07 Dec 2012 10:36:24 +0100 kid3 (2.2-1) experimental; urgency=low * New upstream release. * Bump Standards-Version to 3.9.4 (no changes needed). * Switch to xz compression and add a Pre-Depends on dpkg. * Add lintian overrides for false positive hardening-no-fortify-functions warnings. -- Patrick Matthäi Mon, 12 Nov 2012 13:21:29 +0100 kid3 (2.1-2) unstable; urgency=low * Remove patch 01-gcc4.7-ftbfs, it was merged with 2.1 to another location. -- Patrick Matthäi Mon, 14 May 2012 18:27:44 +0200 kid3 (2.1-1) unstable; urgency=low * New upstream release. - Add new build dependencies libchromaprint-dev, libavformat-dev, libavcodec-dev and pkg-config. -- Patrick Matthäi Sun, 13 May 2012 18:09:20 +0200 kid3 (2.0.1-4) unstable; urgency=low * Add patch 01-gcc4.7-ftbfs to fix a FTBFS with GCC 4.7. Closes: #667227 -- Patrick Matthäi Wed, 04 Apr 2012 16:32:21 +0200 kid3 (2.0.1-3) unstable; urgency=low * Bump Standards-Version to 3.9.3 (no changed needed). * Wrap all debian/control fields. -- Patrick Matthäi Fri, 24 Feb 2012 19:22:47 +0100 kid3 (2.0.1-2) unstable; urgency=low * Do not build the package with libtunepimp. Closes: #657018 * Add missing build-arch and build-indep debian/rules target. * Add doc-base registration for the kid3-qt package. * Repair broken common symlinks in the kid3 package and add an lintian overwrite for it (false positive). Closes: #583333 -- Patrick Matthäi Wed, 25 Jan 2012 16:07:08 +0100 kid3 (2.0.1-1) unstable; urgency=low * New upstream release. [ Patrick Matthäi ] * Adjust build system. * Add build dependency xsltproc. [ Ana Beatriz Guerrero Lopez ] * Some more adjustments to the build system taken from upstream's deb/ * directory. -- Ana Beatriz Guerrero Lopez Sun, 13 Nov 2011 16:34:13 +0100 kid3 (1.6-2) unstable; urgency=low * Bump debhelper to version 7. * Bump Standards-Version to 3.9.2 (no changes needed). * Wrap build dependencies. * Build with enabled hardening-wrapper. -- Patrick Matthäi Sat, 30 Apr 2011 20:10:56 +0200 kid3 (1.6-1) unstable; urgency=low * New upstream release. * Remove odd build dependency libqt3-i18n. * Uploading to unstable. -- Patrick Matthäi Sat, 05 Feb 2011 14:14:11 +0100 kid3 (1.5-1) experimental; urgency=low * New upstream release. * Bump Standards-Version to 3.9.1 (no changes needed). -- Patrick Matthäi Mon, 08 Nov 2010 20:51:56 +0100 kid3 (1.4-1) unstable; urgency=low * New upstream release. * Bump Standards-Version to 3.8.4 (no changes needed). * Convert package to the 3.0 (quilt) format. -- Patrick Matthäi Sat, 06 Mar 2010 11:11:43 +0100 kid3 (1.3-1) unstable; urgency=low * New upstream release. -- Patrick Matthäi Sat, 07 Nov 2009 09:22:58 +0100 kid3 (1.2-2) unstable; urgency=low * Bump Standards-Version to 3.8.3 (no changes needed). * Remove dh_desktop, it is deprecated. -- Patrick Matthäi Sun, 01 Nov 2009 14:04:25 +0100 kid3 (1.2-1) unstable; urgency=low [ Ana Beatriz Guerrero Lopez ] * New upstream release, switch to Qt3 and KDE 4. Closes: #526313 * Update copyright years. [ Patrick Matthäi ] * Some minor packaging cleanups (remove useless whitespaces at EOL etc.). * Bump Standards-Version to 3.8.1 (no changes needed). * Add myself to uploaders. * Refer in debian/copyright to the GPL-2 file instead of GPL. This fixes the lintian warning copyright-refers-to-symlink-license. * Adjust build dependencies, so that kid3 builds against Qt4 and KDE4 libs. -- Patrick Matthäi Wed, 20 May 2009 16:12:30 +0200 kid3 (1.1-1) unstable; urgency=low * New upstream release: - Add support for .oga (Closes: #498352) * Add call to dh_icons and dh_desktop. (Closes: #454526) * Update do debhelper compat 6. * Remove build depend on quilt. * Update to Standards-Version: 3.8.0, no changes required. -- Ana Beatriz Guerrero Lopez Thu, 15 Jan 2009 19:15:40 +0100 kid3 (1.0-1) unstable; urgency=low * New upstream release: - Includes polish translation. (Closes: #465675) * Add build-depend on libqt3-i18n to build translations. * Update description in control file and manpages. -- Ana Beatriz Guerrero Lopez Tue, 08 Apr 2008 10:32:37 +0200 kid3 (0.10-1) unstable; urgency=low * New upstream release: - Allow configuration of default charset for new tags. (Closes: #447699) * Remove all the patches added in the 0.9-X uploads. * Move Homepage into control field. * Update Standards-Version to 3.7.3 * Add Vcs-Svn and Vcs-Browser fields. -- Ana Beatriz Guerrero Lopez Wed, 05 Dec 2007 00:18:59 +0100 kid3 (0.9-4) unstable; urgency=low * Fix clean rule to allow package be built twice in a row. (Closes: #442615) -- Ana Beatriz Guerrero Lopez Wed, 26 Sep 2007 09:22:56 +0200 kid3 (0.9-3) unstable; urgency=low * Add patch to use custom import/export formats with empty fields. (Closes: #432953) -- Ana Beatriz Guerrero Lopez Mon, 16 Jul 2007 20:58:45 +0200 kid3 (0.9-2) unstable; urgency=low [ Mark Purcell ] * Install debian/watch file * Add myself to uploaders * Add debian/rules get-orig-source target for http://buildserver.net [Ana Beatriz Guerrero Lopez] * Package split, now it provides: kid3 (integrated with KDE) and kid3-qt (for non-KDE users). * Improve package descriptions. Thanks Urs! * Added post-release patches: kid3-0.9-desktop, kid3-0.9-kid3-qt-icon and kid3-0.9-remove_user_actions * New needed b-d libvorbis-dev, libtag1-dev, docbook-utils and quilt. -- Debian KDE Extras Team Sun, 08 Jul 2007 05:31:05 +0200 kid3 (0.9-1) unstable; urgency=low * New upstream release. (Closes: #423464, #423468, #428872) -- Ana Beatriz Guerrero Lopez Sun, 17 Jun 2007 23:48:03 +0200 kid3 (0.8.1-3) unstable; urgency=low * Rebuild due to FLAC 1.1.4 transition. (Closes: #426651) -- Ana Beatriz Guerrero Lopez Tue, 05 Jun 2007 20:19:35 +0100 kid3 (0.8.1-2) unstable; urgency=low * Upload to unstable. -- Ana Beatriz Guerrero Lopez Sat, 19 May 2007 11:36:19 +0100 kid3 (0.8.1-1) experimental; urgency=low * New upstream release. * Compiled against libtunepimp 0.5.2. (Closes: #397325) * Removed relibtoolization at build time, does not seem necessary now. -- Ana Beatriz Guerrero Lopez Fri, 24 Nov 2006 01:29:55 +0100 kid3 (0.7-3) unstable; urgency=low * New maintainter. (Closes: #393170) * Bumped compat to 5. * Added space to homepage pseudo header. * Moved .desktop file to the right place. -- Ana Beatriz Guerrero Lopez Fri, 27 Oct 2006 19:28:50 +0200 kid3 (0.7-2) unstable; urgency=low * Orphaning this package, setting maintainer field to packages@qa.debian.org. -- Aurelien Jarno Sun, 15 Oct 2006 02:37:49 +0200 kid3 (0.7-1) unstable; urgency=low * New upstream version: - Export dialog - Show/hide ID3v1.1/ID3v2.3 (closes: bug#308121) - Custom genres - Automatic track numbering - Improved import (search history, track reordering) - Split filename and tag format into separate options and commands * Bumped Standards-Version to 3.7.2 (no changes). -- Aurelien Jarno Thu, 13 Jul 2006 12:24:01 +0200 kid3 (0.6-3) unstable; urgency=low * The "I am really upset" release: when changing the API of a library, please either provide the two versions of the library, or at least coordinate the changes with other maintainers. * Build-depends on libtunepimp3-dev instead of libtunepimp2-dev. * Fixes to use the new API. -- Aurelien Jarno Thu, 6 Apr 2006 17:17:14 +0200 kid3 (0.6-2) unstable; urgency=low * Relibtoolize the package to get rid of a lot of unneeded dependencies, including libfreetype6. -- Aurelien Jarno Thu, 24 Nov 2005 12:16:38 +0100 kid3 (0.6-1) unstable; urgency=low * Added a patch to remove the old .ogg file when both the tags and the filename are changed. Patch by Urs Fleisch, thanks (closes: bug#339066). -- Aurelien Jarno Mon, 14 Nov 2005 21:14:17 +0100 kid3 (0.6-0) unstable; urgency=low * New upstream version: - support for Ogg/Vorbis and FLAC files (closes: bug#308120) - import from MusicBrainz - usability improvements (closes: bug#308122) * Bumped Standards-Version to 3.6.2 (no changes). -- Aurelien Jarno Mon, 31 Oct 2005 10:27:12 +0100 kid3 (0.5-6) unstable; urgency=low * Applied a patch from Urs Fleisch to fix a bug appearing when moving a tag from ID3v2 to ID3v1 (closes: bug#320077). -- Aurelien Jarno Sat, 10 Sep 2005 01:08:30 +0200 kid3 (0.5-5) unstable; urgency=low * Tightened the build-dependency on libid3-3.8.3-dev to (>= 3.8.3-4.2) (closes: bug#325611). -- Aurelien Jarno Tue, 30 Aug 2005 07:48:14 +0200 kid3 (0.5-4) unstable; urgency=low * C++ transition: build-depends on libqt3-mt-dev (>= 3:3.3.4-7) and kdelibs4-dev (>= 4:3.4.2-1). -- Aurelien Jarno Thu, 18 Aug 2005 10:53:10 +0200 kid3 (0.5-3) unstable; urgency=low * Fixed po/de.po (closes: bug#314026). -- Aurelien Jarno Wed, 15 Jun 2005 11:34:40 +0200 kid3 (0.5-2) unstable; urgency=low * Included patch #1079111 (from kid3.sf.net) to handle filenames with accents correctly (closes: bug#291925). -- Aurelien Jarno Fri, 28 Jan 2005 02:12:50 +0100 kid3 (0.5-1) unstable; urgency=low * New upstream version (closes: bug#273073). -- Aurelien Jarno Sun, 26 Sep 2004 00:22:06 +0200 kid3 (0.4-2) unstable; urgency=low * Improved description (closes: bug#246411). -- Aurelien Jarno Thu, 29 Apr 2004 02:11:57 +0200 kid3 (0.4-1) unstable; urgency=low * Initial Release (closes: bug#205938). -- Aurelien Jarno Sat, 17 Apr 2004 19:02:02 +0200 kid3-3.0.2/deb/compat000066400000000000000000000000021224603511300142730ustar00rootroot000000000000009 kid3-3.0.2/deb/control000066400000000000000000000100531224603511300144770ustar00rootroot00000000000000Source: kid3 Section: kde Priority: optional Maintainer: Debian KDE Extras Team Uploaders: Ana Beatriz Guerrero Lopez , Mark Purcell , Patrick Matthäi Build-Depends: debhelper (>= 9), libqt4-dev, kdelibs5-dev, cmake, libid3-3.8.3-dev, libflac++-dev, libvorbis-dev, libtag1-dev, libavformat-dev, libavcodec-dev, libchromaprint-dev, libreadline-dev, hardening-wrapper, pkg-config, lsb-release, docbook-xsl, xsltproc Standards-Version: 3.9.4 Homepage: http://kid3.sourceforge.net/ Vcs-Svn: svn://anonscm.debian.org/pkg-kde/kde-extras/kid3/trunk/ Vcs-Browser: http://anonscm.debian.org/viewvc/pkg-kde/kde-extras/kid3/trunk/ Package: kid3-core Architecture: any Pre-Depends: dpkg (>= 1.15.6~) Depends: ${shlibs:Depends}, ${misc:Depends} Description: Audio tag editor core libraries and data This package contains common libraries and data used by both kid3 and kid3-qt. Package: kid3 Architecture: any Pre-Depends: dpkg (>= 1.15.6~) Depends: kid3-core, ${shlibs:Depends}, ${misc:Depends} Description: KDE audio tag editor With Kid3, an ID3 tag editor for KDE you can: * Edit ID3v1.1 tags in your MP3 files * Edit all ID3v2.3 and ID3v2.4 frames in your MP3 files * Convert between ID3v1.1, ID3v2.3 and ID3v2.4 tags * Edit tags in MP3, Ogg/Vorbis, FLAC, MPC, APE, MP4/AAC, MP2, Speex, TrueAudio, WavPack, WMA, WAV, AIFF files and tracker modules. * Edit tags of multiple files, e.g. the artist, album, year and genre of all files of an album typically have the same values and can be set together * Generate tags from filenames * Generate tags from the contents of tag fields * Generate filenames from tags * Generate playlist files * Automatic case conversion and string translation * Import and export album data * Import from gnudb.org, TrackType.org, MusicBrainz, Discogs, Amazon . This package uses KDE libraries, if you do not use KDE you should use kid3-qt. Package: kid3-qt Section: sound Architecture: any Pre-Depends: dpkg (>= 1.15.6~) Depends: kid3-core, ${shlibs:Depends}, ${misc:Depends} Description: Audio tag editor With Kid3, an audio tag editor you can: * Edit ID3v1.1 tags in your MP3 files * Edit all ID3v2.3 and ID3v2.4 frames in your MP3 files * Convert between ID3v1.1, ID3v2.3 and ID3v2.4 tags * Edit tags in MP3, Ogg/Vorbis, FLAC, MPC, APE, MP4/AAC, MP2, Speex, TrueAudio, WavPack, WMA, WAV, AIFF files and tracker modules. * Edit tags of multiple files, e.g. the artist, album, year and genre of all files of an album typically have the same values and can be set together * Generate tags from filenames * Generate tags from the contents of tag fields * Generate filenames from tags * Generate playlist files * Automatic case conversion and string translation * Import and export album data * Import from gnudb.org, TrackType.org, MusicBrainz, Discogs, Amazon . This package does not use KDE libraries, if you use KDE you should use kid3. Package: kid3-cli Section: sound Architecture: any Pre-Depends: dpkg (>= 1.15.6~) Depends: kid3-core, ${shlibs:Depends}, ${misc:Depends} Suggests: kid3-qt Description: Audio tag editor With Kid3, an audio tag editor you can: * Edit ID3v1.1 tags in your MP3 files * Edit all ID3v2.3 and ID3v2.4 frames in your MP3 files * Convert between ID3v1.1, ID3v2.3 and ID3v2.4 tags * Edit tags in MP3, Ogg/Vorbis, FLAC, MPC, APE, MP4/AAC, MP2, Speex, TrueAudio, WavPack, WMA, WAV, AIFF files and tracker modules. * Edit tags of multiple files, e.g. the artist, album, year and genre of all files of an album typically have the same values and can be set together * Generate tags from filenames * Generate tags from the contents of tag fields * Generate filenames from tags * Generate playlist files * Automatic case conversion and string translation * Import and export album data * Import from gnudb.org, TrackType.org, MusicBrainz, Discogs, Amazon . This package contains a command line interface for Kid3, for a GUI you can use kid3-qt or kid3. kid3-3.0.2/deb/copyright000066400000000000000000000014341224603511300150320ustar00rootroot00000000000000This package was debianized by Aurelien Jarno on Sat, 17 Apr 2004 19:02:08 +0100. Maintenance was taken over by Ana Beatriz Guerrero López on Fri, 27 Oct 2006 19:26:06 +0200 It was downloaded from http://kid3.sourceforge.net/ Upstream Author: Urs Fleisch Copyright: Copyright 2003-2013 Urs Fleisch License: This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. On Debian GNU/Linux systems, the complete text of the GNU General Public License can be found in `/usr/share/common-licenses/GPL-2'. kid3-3.0.2/deb/kid3-cli.1000066400000000000000000000000251224603511300145530ustar00rootroot00000000000000.so man1/kid3-core.1 kid3-3.0.2/deb/kid3-cli.de.1000066400000000000000000000000251224603511300151420ustar00rootroot00000000000000.so man1/kid3-core.1 kid3-3.0.2/deb/kid3-cli.install000066400000000000000000000000221224603511300160560ustar00rootroot00000000000000/usr/bin/kid3-cli kid3-3.0.2/deb/kid3-cli.manpages000066400000000000000000000000411224603511300162040ustar00rootroot00000000000000deb/kid3-cli.1 deb/kid3-cli.de.1 kid3-3.0.2/deb/kid3-core.install000066400000000000000000000001451224603511300162450ustar00rootroot00000000000000/usr/lib/kid3/* /usr/share/dbus-1/interfaces/net.sourceforge.Kid3.xml /usr/share/kid3/translations/* kid3-3.0.2/deb/kid3-core.manpages000066400000000000000000000000771224603511300163760ustar00rootroot00000000000000kid3-build/doc/en/kid3-core.1 kid3-build/doc/de/kid3-core.de.1 kid3-3.0.2/deb/kid3-qt.1000066400000000000000000000000251224603511300144300ustar00rootroot00000000000000.so man1/kid3-core.1 kid3-3.0.2/deb/kid3-qt.de.1000066400000000000000000000000251224603511300150170ustar00rootroot00000000000000.so man1/kid3-core.1 kid3-3.0.2/deb/kid3-qt.doc-base000066400000000000000000000011271224603511300157510ustar00rootroot00000000000000Document: kid3-qt Title: The Kid3 Handbook Author: Urs Fleisch Abstract: Kid3 is an application to edit the ID3v1 and ID3v2 tags in MP3 files in an efficient way. Also tags in Ogg/Vorbis, FLAC, MPC, APE, MP4/AAC, MP2, Speex, TrueAudio, WavPack, WMA, WAV, AIFF files and Tracker modules are supported. It is easy to set tags of multiple files to the same values (e.g. album, artist, year and genre in all files of the same album) and generate the tags from the file name or vice versa. Section: Sound Format: HTML Index: /usr/share/doc/kid3-qt/kid3_en.html Files: /usr/share/doc/kid3-qt/*.html kid3-3.0.2/deb/kid3-qt.install000066400000000000000000000001741224603511300157430ustar00rootroot00000000000000/usr/bin/kid3-qt /usr/share/applications/kid3-qt.desktop /usr/share/doc/kid3-qt/* /usr/share/icons/hicolor/*/apps/kid3-qt.* kid3-3.0.2/deb/kid3-qt.manpages000066400000000000000000000000371224603511300160660ustar00rootroot00000000000000deb/kid3-qt.1 deb/kid3-qt.de.1 kid3-3.0.2/deb/kid3.1000066400000000000000000000000251224603511300140060ustar00rootroot00000000000000.so man1/kid3-core.1 kid3-3.0.2/deb/kid3.de.1000066400000000000000000000000251224603511300143750ustar00rootroot00000000000000.so man1/kid3-core.1 kid3-3.0.2/deb/kid3.install000066400000000000000000000002441224603511300153170ustar00rootroot00000000000000/usr/bin/kid3 /usr/share/applications/kde4/kid3.desktop /usr/share/doc/kde/HTML/*/kid3/* /usr/share/icons/hicolor/*/apps/kid3.* /usr/share/kde4/apps/kid3/kid3ui.rc kid3-3.0.2/deb/kid3.links000066400000000000000000000002161224603511300147700ustar00rootroot00000000000000usr/share/doc/kde/HTML/en/common usr/share/doc/kde/HTML/en/kid3/common usr/share/doc/kde/HTML/en/common usr/share/doc/kde/HTML/de/kid3/common kid3-3.0.2/deb/kid3.manpages000066400000000000000000000000311224603511300154360ustar00rootroot00000000000000deb/kid3.1 deb/kid3.de.1 kid3-3.0.2/deb/rules000077500000000000000000000034371224603511300141640ustar00rootroot00000000000000#!/usr/bin/make -f %: dh $@ --builddirectory=kid3-build DEB_CMAKE_PREFIX = /usr DEB_CONFIG_INSTALL_DIR = $(DEB_CMAKE_PREFIX)/share/kde4/config DEB_HTML_INSTALL_DIR = $(DEB_CMAKE_PREFIX)/share/doc/kde/HTML override_dh_auto_configure: dh_auto_configure -- \ -DSYSCONF_INSTALL_DIR=/etc \ -DCMAKE_INSTALL_PREFIX=$(DEB_CMAKE_PREFIX) \ -DCONFIG_INSTALL_DIR=$(DEB_CONFIG_INSTALL_DIR) \ -DDATA_INSTALL_DIR=$(DEB_CMAKE_PREFIX)/share/kde4/apps \ -DHTML_INSTALL_DIR=$(DEB_HTML_INSTALL_DIR) \ -DKCFG_INSTALL_DIR=$(DEB_CMAKE_PREFIX)/share/kde4/config.kcfg \ -DLIB_INSTALL_DIR=$(DEB_CMAKE_PREFIX)/lib/kid3 \ -DCMAKE_C_COMPILER:FILEPATH="$(CC)" \ -DCMAKE_CXX_COMPILER:FILEPATH="$(CXX)" \ -DCMAKE_C_FLAGS="$(CFLAGS)" \ -DCMAKE_CXX_FLAGS="$(CXXFLAGS)" override_dh_auto_build: dh_auto_build cp kid3-build/doc/en/kid3.1 kid3-build/doc/en/kid3-core.1 cp kid3-build/doc/de/kid3.1 kid3-build/doc/de/kid3-core.de.1 override_dh_auto_install: dh_auto_install rm $(CURDIR)/debian/tmp/usr/share/doc/kde/HTML/*/kid3/common override_dh_installdocs: dh_installdocs -A AUTHORS README override_dh_installchangelogs: dh_installchangelogs ChangeLog override_dh_makeshlibs: dh_makeshlibs --noscripts override_dh_compress: dh_compress --exclude=.docbook override_dh_builddeb: dh_builddeb -- -Zxz DEBVERSION:=$(shell head -n 1 debian/changelog \ | sed -e 's/^[^(]*(\([^)]*\)).*/\1/') UPVERSION:=$(shell echo $(DEBVERSION) | sed -e 's/^.*://' -e 's/-[0-9.]*$$//' -e 's/.dfsg$$//') FILENAME := kid3_$(UPVERSION).orig.tar.gz UPFILENAME := kid3-$(UPVERSION).tar.gz URL = http://heanet.dl.sourceforge.net/kid3/$(UPFILENAME) get-orig-source: @@dh_testdir @@[ -d ../tarballs/. ]||mkdir -p ../tarballs @@echo Downloading $(UPFILENAME) from $(URL) ... @@wget -N -nv -T10 -t3 -O ../tarballs/$(FILENAME) $(URL) kid3-3.0.2/deb/source/000077500000000000000000000000001224603511300143755ustar00rootroot00000000000000kid3-3.0.2/deb/source/format000066400000000000000000000000141224603511300156030ustar00rootroot000000000000003.0 (quilt) kid3-3.0.2/deb/watch000066400000000000000000000003171224603511300141270ustar00rootroot00000000000000# See uscan(1) for format # Compulsory line, this is a version 3 file version=3 # Uncomment to find new files on sourceforge, for debscripts >= 2.9 http://sf.net/kid3/kid3-(.*)\.tar\.gz debian svn-upgrade kid3-3.0.2/doc/000077500000000000000000000000001224603511300131105ustar00rootroot00000000000000kid3-3.0.2/doc/CMakeLists.txt000066400000000000000000000063221224603511300156530ustar00rootroot00000000000000# Create HTML handbook from DocBook file in language directory macro (kid3_create_handbook _docbook) get_filename_component(_input ${_docbook} ABSOLUTE) get_filename_component(_lang ${CMAKE_CURRENT_SOURCE_DIR} NAME) set(_output ${CMAKE_CURRENT_BINARY_DIR}/kid3_${_lang}.html) set(_fixdocbook_pl ${CMAKE_CURRENT_SOURCE_DIR}/../fixdocbook.pl) set(_fixhtml_pl ${CMAKE_CURRENT_SOURCE_DIR}/../fixhtml.pl) set(_docbook_xsl ${DOCBOOK_XSL_DIR}/html/docbook.xsl) if (WIN32) # The expressions in pipes do not work for Windows, # so temporary files are used. set(_tmp_xml ${CMAKE_CURRENT_BINARY_DIR}/tmp.xml) set(_tmp_html ${CMAKE_CURRENT_BINARY_DIR}/tmp.html) add_custom_command(OUTPUT ${_tmp_xml} COMMAND ${PERL_EXECUTABLE} ${_fixdocbook_pl} <${_input} >${_tmp_xml} DEPENDS ${_input} ) add_custom_command(OUTPUT ${_tmp_html} COMMAND ${XSLTPROC} --novalid --nonet ${_docbook_xsl} ${_tmp_xml} >${_tmp_html} DEPENDS ${_tmp_xml} ) add_custom_command(OUTPUT ${_output} COMMAND ${PERL_EXECUTABLE} -n ${_fixhtml_pl} <${_tmp_html} >${_output} DEPENDS ${_tmp_html} ) else (WIN32) add_custom_command(OUTPUT ${_output} COMMAND ${PERL_EXECUTABLE} ${_fixdocbook_pl} <${_input} | ${XSLTPROC} --novalid --nonet ${_docbook_xsl} - | ${PERL_EXECUTABLE} -n ${_fixhtml_pl} >${_output} DEPENDS ${_input} ) endif (WIN32) add_custom_target("${_lang}-html-handbook" ALL DEPENDS ${_output}) install(FILES ${_output} DESTINATION ${WITH_DOCDIR}) endmacro (kid3_create_handbook) if (UNIX) find_program(GZIP_EXECUTABLE gzip) # Create man page from DocBook file in language directory macro (kid3_create_manpage _docbook) get_filename_component(_input ${_docbook} ABSOLUTE) get_filename_component(_lang ${CMAKE_CURRENT_SOURCE_DIR} NAME) set(_output ${CMAKE_CURRENT_BINARY_DIR}/kid3.1) set(_fixdocbook_pl ${CMAKE_CURRENT_SOURCE_DIR}/../fixmandocbook.pl) set(_docbook_xsl ${DOCBOOK_XSL_DIR}/manpages/docbook.xsl) add_custom_command(OUTPUT ${_output} COMMAND ${PERL_EXECUTABLE} ${_fixdocbook_pl} <${_input} | ${XSLTPROC} --novalid --nonet ${_docbook_xsl} - DEPENDS ${_input} ) add_custom_command(OUTPUT ${_output}.gz COMMAND ${GZIP_EXECUTABLE} -c ${_output} >${_output}.gz DEPENDS ${_output} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} ) add_custom_target("${_lang}-man-handbook" ALL DEPENDS ${_output}.gz) if (_lang STREQUAL "en") set(_install_dir ${WITH_MANDIR}/man1) else (_lang STREQUAL "en") set(_install_dir ${WITH_MANDIR}/${_lang}/man1) endif (_lang STREQUAL "en") install(FILES ${_output}.gz DESTINATION ${_install_dir}) if (BUILD_QT_APP) install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink kid3.1.gz \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${_install_dir}/kid3-qt.1.gz\")") endif (BUILD_QT_APP) if (BUILD_CLI_APP) install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink kid3.1.gz \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${_install_dir}/kid3-cli.1.gz\")") endif (BUILD_CLI_APP) endmacro (kid3_create_manpage) endif (UNIX) add_subdirectory(en) add_subdirectory(de) kid3-3.0.2/doc/de/000077500000000000000000000000001224603511300135005ustar00rootroot00000000000000kid3-3.0.2/doc/de/CMakeLists.txt000066400000000000000000000004571224603511300162460ustar00rootroot00000000000000if (BUILD_KDE_APP) find_package(KDE4 REQUIRED) kde4_create_handbook(index.docbook INSTALL_DESTINATION ${HTML_INSTALL_DIR}/de SUBDIR kid3) endif (BUILD_KDE_APP) if (BUILD_QT_APP) kid3_create_handbook(index.docbook) endif (BUILD_QT_APP) if (UNIX) kid3_create_manpage(index.docbook) endif (UNIX) kid3-3.0.2/doc/de/index.docbook000066400000000000000000004376711224603511300161730ustar00rootroot00000000000000 Kid3'> ]> Das Kid3 Handbuch Urs Fleisch
ufleisch@users.sourceforge.net
Software Entwicklung
2013 Urs Fleisch &FDLNotice; 2013-11-27 3.0.2 &kid3; ist eine Anwendung zum effizienten Editieren von ID3v1 und ID3v2 Tags in MP3 Dateien. Auch die Tags in Ogg/Vorbis, FLAC, MPC, APE, MP4/AAC, MP2, Speex, TrueAudio, WavPack, WMA, WAV, AIFF Dateien und Tracker Modulen (MOD, S3M, IT, XM) werden unterstützt. Es ist einfach, Tags in mehreren Dateien auf die gleichen Werte zu setzen (z.B. Album, Interpret, Jahr und Stil in allen Dateien eines Albums), Tags aus den Dateinamen zu generieren oder umgekehrt. KDE kdemultimedia MP3 ID3 ID3v1 ID3v2 Ogg Vorbis FLAC MPC APE Musepack MP4 M4A MP2 Speex TrueAudio WavPack WMA WAV AIFF MOD S3M IT XM
Übersicht kid3 DATEI kid3-qt DATEI kid3-cli DATEI Optionen DATEI Wenn DATEI der Pfad auf ein Verzeichnis ist, so wird es geöffnet. Wenn einer oder mehrere Datei-Pfade gegeben werden, so wird deren gemeinsames Verzeichnis geöffnet und die Dateien werden ausgewählt. kid3 Hilfe zu verfügbaren Optionen anzeigen. Spezielle Optionen zu Qt anzeigen, siehe qtoptions7 . Spezielle Optionen zu KDE anzeigen, siehe kdeoptions7 . Alle Optionen anzeigen. Autoren-Information anzeigen. Versionsinformation anzeigen. Lizenz-Informationen anzeigen. kid3-qt Siehe qtoptions 7, die Optionen haben jedoch einen "-" als Präfix, und nicht "&doublehyphen;". kid3-cli Führe einen Befehl aus. Mehrere Optionen sind möglich, die Befehle werden in der entsprechenden Reihenfolge ausgeführt. Siehe den Abschnitt über kid3-cli für eine Beschreibung der verfügbaren Befehle. | Hilfe zu verfügbaren Optionen und Befehlen anzeigen. Einleitung &kid3; ist eine Anwendung zum effizienten Editieren von ID3v1 und ID3v2 Tags in MP3 Dateien. Diese Tags können zwar mit den meisten MP3 Player Programmen editiert werden, jedoch nicht auf eine sehr komfortable und effiziente Art. Darüber hinaus werden auch die Tags in Ogg/Vorbis, FLAC, MPC, APE, MP4/AAC, MP2, Speex, TrueAudio, WavPack, WMA, WAV, AIFF Dateien und Tracker Modulen (MOD, S3M, IT, XM) unterstützt. &kid3; kann weder MP3 grabben noch codieren, es ist darauf ausgelegt, die ID3 Tags von allen Dateien eines Albums auf eine möglichst effiziente Art zu editieren, d.h. mit so wenig Mausklicks und Tastendrücken wie möglich. Während die meisten anderen Programme entweder ID3v1 oder ID3v2 Tags editieren können, hat &kid3; volle Kontrolle über die Tags beider Versionen, es kann zwischen den beiden Formaten konvertieren und hat Zugang zu allen ID3v2 Frames. Tags mehrerer Dateien können gleichzeitig auf dieselben Werte gesetzt werden, so beispielsweise Interpret, Album, Jahr und Stil aller Dateien eines Albums. Wenn die Information für die Tags schon im Dateinamen enthalten ist, können die Tags automatisch aus dem Dateinamen generiert werden. Es ist auch möglich, den Dateinamen in einem beliebigen Format aus den Werten in den Tags zu setzen. Das Editieren wird darüber hinaus durch automatische Ersetzung von Zeichenketten erleichtert, z.B. um ungültige Zeichen aus Dateinamen zu entfernen. Automatische Groß- und Kleinschreibung macht es einfach, eine konsistente Namensschreibweise in allen Tags zu verwenden. Die Tag-Information für ganze Alben kann über gnudb.org, TrackType.org, MusicBrainz, Discogs, Amazon oder von anderen Quellen mit Albuminformationen importiert werden. Das verwendete Format ist mit regulären Ausdrücken frei konfigurierbar. Probleme und Vorschläge für neue Funktionen können dem Autor gemeldet werden. Benutzen von Kid3 Funktionsmerkmale von Kid3 Editieren von ID3v1.1 Tags Editieren von allen ID3v2.3 und ID3v2.4 Frames Editieren der Tags von mehreren Dateien Konvertieren zwischen ID3v1 und ID3v2 Tags Editieren von MP3, Ogg/Vorbis, FLAC, MPC, APE, MP4/AAC, MP2, Speex, TrueAudio, WavPack, WMA, WAV und AIFF Tags Generieren der Tags aus dem Dateinamen Generieren der Tags aus dem Inhalt der Tagfelder Generieren des Dateinamens aus den Tags Generieren und Ändern des Verzeichnisnamens aus den Tags Generieren von Stücklisten Automatische Groß-/Kleinschreibung und Zeichenersetzung Import von gnudb.org, TrackType.org, MusicBrainz, Discogs, Amazon und anderen Quellen Export als CSV, HTML, Stückliste, Kover XML oder in anderen Formaten. Exportierte CSV-Dateien können wieder importiert werden. Arbeiten anhand eines Beispiels Dieser Abschnitt beschreibt eine typische Sitzung mit &kid3;. Nehmen wir an, wir haben ein Verzeichnis mit MP3 Dateien der Stücke aus dem Album "Let's Tag" von der Gruppe "One Hit Wonder". Das Verzeichnis ist im "Interpret - Album" Format benannt, in unserem Fall also One Hit Wonder - Let's Tag. Das Verzeichnis enthält die Stücke im "Nummer Titel.mp3" Format, was auch Sinn macht, weil die Dateinamen kurz werden (wichtig bei den kleinen Anzeigen mobiler MP3 Player) und alphabetisch in der richtigen Reihenfolge geordnet werden (wichtig falls ein Hardware MP3 Player die Stücke in alphabetischer Reihenfolge abspielt oder in der Reihenfolge, wie sie auf CD gebrannt wurde - diese Reihenfolge ist bei mkisofs alphabetisch). Darüber hinaus sind Interpret und Album bereits im Verzeichnisnamen enthalten und brauchen nicht im Dateinamen wiederholt zu werden. Doch zurück zu unserem Beispiel - das Verzeichnis sieht folgendermaßen aus: 01 Intro.mp3 02 We Only Got This One.mp3 03 Outro.mp3 Diese Dateien haben noch keine Tags, wir werden diese mit &kid3; erzeugen. Wir benutzen Öffnen (Datei Menü oder Werkzeugleiste) und wählen eine der Dateien in diesem Verzeichnis. Alle Dateien werden im Dateilistenfeld angezeigt. Aus Bequemlichkeit wollen wir die Tags aus den Dateinamen erzeugen. Daher wählen wir alle Dateien im Listenfeld aus und klicken auf Nach: Tag 1 in der Datei Sektion. Dadurch werden Titel, Interpret, Album und Nummer in allen Dateien gesetzt. Um die Werte für Jahr und Stil in allen Dateien zu setzen, lassen wir alle Dateien selektiert und tippen "2002" in das Feld Jahr und wählen "Pop" im Stil Auswahlfeld. Um einzig diese zwei Werte zu verändern, werden ihre Markierungsfelder automatisch aktiviert, und alle anderen Markierungsfelder bleiben leer. Wird nun durch Klick auf die erste Datei die Auswahl verändert, so sieht man, dass die Tags für die erste Datei die korrekten Werte enthalten. Die Tags der anderen Dateien können auch überprüft werden, indem eine nach der andern selektiert wird. Wenn wir mit den Tags zufrieden sind, können die Änderungen mit Speichern (Datei Menü oder Werkzeugleiste) übernommen werden. Wählen von Stückliste erstellen im Datei Menü erzeugt die Datei One Hit Wonder - Let's Tag.m3u im Verzeichnis. Menüeinträge Die Elemente der Benutzeroberfläche Die &kid3; Benutzeroberfläche ist in fünf Sektionen aufgeteilt: Links finden sich das Dateilistenfeld und das Verzeichnislistenfeld, die rechte Seite enthält die Datei, Tag 1 und Tag 2 Sektionen. Dateiliste Die Dateiliste enthält die Namen aller Dateien im geöffneten Verzeichnis, welche den gewählten Dateinamenfilter (typischerweise *.mp3 *.ogg *.flac *.mpc *.aac *.m4a *.m4b *.m4p *.mp4 *.mp2 *.spx *.tta *.wv *.wma *.wav *.aiff *.ape) erfüllen. Eine einzelne oder mehrere Dateien können ausgewählt werden. Um keine Datei auszuwählen, kann in den leeren Bereich unterhalb der Dateieinträge geklickt werden. Die Auswahl bestimmt die Dateien, welche durch die unten beschriebenen Operationen verändert werden. Links von den Namen kann ein Symbol dargestellt werden: eine Diskette, um zu zeigen, dass die Datei verändert wurde, oder Information über die Tags welche vorhanden sind (V1, V2, V1V2 oder NO TAG, wenn nichts dargestellt wird, wurde die Datei noch nicht eingelesen). Verzeichnisse werden mit Ordner-Symbolen gekennzeichnet. Wenn ein Verzeichnis geöffnet wird, so werden seine Dateien in einem hierarchischen Baum dargestellt. Operationen können auf Dateien aus verschiedenen Verzeichnissen angewendet werden, was nützlich ist, wenn die Musik-Dateien so organisiert werden, dass pro Interpret ein Ordner erstellt wird, der wiederum ein Verzeichnis für jedes Album enthält. Bei einem Rechtsklick in der Dateiliste öffnet sich ein Kontext-Menü mit folgenden Befehlen: Alle ausklappen: Klappt alle Verzeichnisbäume auf Alle einklappen: Klappt alle Verzeichnisbäume ein Umbenennen: Ändert den Namen der Datei In den Mülleimer werfen: Verschiebt die Datei in den Mülleimer Wiedergabe: Spielt die Datei ab, siehe Wiedergabe Bei den weiteren Menüeinträgen handelt es sich um Benutzerbefehle, welche in Kid3 einrichten bei Benutzerbefehle definiert werden können. Dort kann auch die Wiedergabe von Dateien per Doppelklick aktiviert werden. Verzeichnisliste Die Verzeichnisliste enthält die Verzeichnisse, welche das geöffnete Verzeichnis enthält, sowie das aktuelle (.) und das übergeordnete (..) Verzeichnis. Sie erlaubt den raschen Wechsel zwischen Verzeichnissen, ohne den Umweg über die Öffnen... Funktion oder "Drag and Drop". Datei Zeigt Informationen über Codierung (MP3, Ogg, FLAC, MPC, APE, MP2, MP4, AAC, Speex, TrueAudio, WavPack, WMA, WAV, AIFF), Bit- und Samplerate, Kanäle und die Länge der Datei an. Das Name Editierfeld enthält den Dateiname wenn nur eine einzelne Datei ausgewählt ist. Wird dieses Feld verändert, so wird die Datei umbenannt, wenn Speichern ausgeführt wird. Das Format Auswahl- und Editierfeld enthält das Format, welches verwendet wird, wenn der Dateiname aus dem ersten oder zweiten Tag erzeugt wird. Der Dateiname kann beliebige Zeichen enthalten, es kann sogar ein Verzeichnisteil abgetrennt durch einen Schrägstrich angegeben werden, doch dieses Verzeichnis muss bereits existieren, damit das Umbenennen erfolgreich ist. Die folgenden Codes können verwendet werden, um Werte aus den Tags im Dateinamen einzusetzen: %s %{title} Titel (Song) %a %{artist} Interpret (Artist) %l %{album} Album %c %{comment} Kommentar (Comment) %y %{year} Jahr (Year) %t %{track} Nummer (Track, z.B. 01) %t %{track.n} Nummer mit Feldlänge n (z.B. 001 für %{track.3}) %T %{tracknumber} Nummer (Track, z.B. 1) %g %{genre} Stil (Genre) %{ignore} Wird ignoriert wenn Tags aus dem Dateinamen erzeugt werden Die Formatcodes sind nicht auf die oben gegebenen Beispiele beschränkt. Alle Framenamen können benutzt werden, sowohl vereinheitlichte wie %{bpm} als auch formatspezifische wie %{popm}. Ein zweites Format Auswahlfeld (mit Pfeil runter) wird verwendet, um die Tags aus dem Dateinamen zu generieren. Falls der Dateinamen nicht diesem Format entspricht, werden noch einige andere, gängige Formate ausprobiert. Einige häufig verwendete Dateinamen-Formate sind bereits vordefiniert und über das Auswahlfeld verfügbar, aber es ist auch möglich, ein eigenes Format in das Eingabefeld einzutragen. Von: Tag 1, Tag 2: Setzt den Dateinamen gemäß dem ausgewählten Format und dem ersten Tag bzw. zweiten Tag. Nach: Tag 1, Tag 2: Die Tags werden anhand des Dateinamens gesetzt. Dabei wird zuerst das in Format ausgewählte Dateinamenformat probiert. Falls der vorhandene Dateinamen nicht diesem Format entspricht, werden noch folgende Formate versucht: Interpret - Album/Nummer Titel Album/Nummer - Interpret - Titel /Interpret - Album - Nummer - Titel Album/Interpret - Nummer - Titel Album/Interpret - Titel Artist/Album/Track Song Wenn eine einzelne Datei ausgewählt wurde, werden die Eingabefelder mit den aus dem Dateinamen extrahierten Werten gefüllt. Bei Mehrfachselektion werden die Werte direkt gemäß den Dateinamen gesetzt. Tag 1 Die Eingabefelder für Titel, Interpret, Album, Kommentar, Jahr, Nummer und Stil werden benutzt um die entsprechenden Werte im ersten Tag der ausgewählten Dateien zu editieren. Die Werte werden verändert, wenn die Dateiauswahl geändert wird oder vor Operationen wie Speichern und Beenden und wenn die entsprechenden Markierungsfelder links aktiviert sind. Dies ist nützlich, um nur bestimmte Werte zu verändern und die übrigen Werte nicht anzurühren. Wenn eine einzelne Datei ausgewählt ist, werden alle Markierungsfelder aktiviert und die Eingabefelder enthalten die Werte aus den Tags in der Datei. Wenn ein Tag nicht vorhanden ist, werden die leeren Werte angezeigt, d.h. eine leere Zeichenkette für die Felder Titel, Interpret, Album und Kommentar, 0 für Jahr und Nummer und ein leerer Auswahleintrag bei Stil. Die Werte können editiert werden und werden für die ausgewählte Datei beim Ändern der Auswahl gesetzt, sofern das entsprechende Markierungsfeld aktiviert ist. Die Datei wird dann durch ein Disketten-Symbol als modifiziert gekennzeichnet, doch die eigentliche Datei bleibt unverändert, bis Speichern ausgeführt wird. Wenn mehrere Dateien ausgewählt werden, dann werden nur diejenigen Werte angezeigt, welche bei allen ausgewählten Dateien identisch sind. In allen übrigen Editierfeldern werden die leeren Werte (siehe oben) angezeigt. Alle Markierungsfelder sind inaktiv, um ungewolltes Verändern mehrerer Dateien zu verhindern. Soll ein Wert für alle selektierten Dateien verändert werden, so kann er editiert werden, und das entsprechende Markierungsfeld wird gesetzt. Der Wert wird dann für alle ausgewählten Dateien verändert wenn die Auswahl geändert wird und kann mit Speichern gesichert werden. Die Markierungsfelder bestimmen auch, auf welche Felder die meisten anderen Befehle angewendet werden. So werden beim Kopieren, Einfügen und Transferieren zwischen Tags 1 und 2 nur markierte Felder verwendet. Um die Bedienung zu vereinfachen, wenn mehrere Dateien ausgewählt sind und daher alle Markierungsfelder inaktiv sind, verhalten sich diese Befehle wenn alle Markierungsfelder inaktiv sind gleich als wären alle markiert. Von Tag 2: Die Tag 1 Felder werden auf die gleichen Werte gesetzt wie die entsprechenden Tag 2 Werte. Bei Mehrfachselektion werden die Tags der Dateien direkt gesetzt. Kopieren: Die Kopierablage wird mit den Tag 1 Werten gefüllt. Bei folgenden Einfüge-Operationen werden nur solche Werte gesetzt, bei welchen hier das Markierungsfeld aktiviert war. Einfügen: Fügt die Werte aus der Kopierablage in die Eingabefelder ein. Löschen: Diese Funktion setzt alle Editierfelder auf die leeren Werte, entfernt also alle Werte. Die gespeicherte Datei enthält dann kein Tag 1 mehr. Tag 2 Die Eingabefelder funktionieren gleich wie bei Tag 1 beschrieben, die Länge der Zeichenketten ist jedoch nicht begrenzt. Beim Stil können nebst den vorgegebenen auch eigene Namen verwendet werden - eine Eingabe ins Editierfeld genügt. Das Tag 2 kann nicht bloß dieselben Werte enthalten wie das Tag 1, das Format ist vielmehr auf flexible Art aus mehreren Elementen aufgebaut, welche wiederum aus verschiedenen Feldern bestehen. Die Tag 2 Tabelle zeigt alle Elemente an, welche in der ausgewählten Datei vorhanden sind. Bearbeiten: Ein Fenster wird geöffnet, in welchem alle Felder des gewählten Elementes editiert werden können. Wenn mehrere Dateien ausgewählt sind, so werden die editierten Felder bei allen ausgewählten Dateien angewendet, welche solch ein Element enthalten. Hinzufügen: Ein Auswahlfenster mit allen unterstützten Elementtypen wird angezeigt und ein Element des gewählten Typs kann editiert und zu der Datei hinzugefügt werden. Dies funktioniert auch, wenn mehrere Dateien ausgewählt sind; das Element wird dann bei allen ausgewählten Dateien hinzugefügt. Löschen: Löscht das ausgewählte Element in den ausgewählten Dateien. Albumcover hierhin ziehen wird angezeigt wenn die Datei kein Album Cover Bild enthält. Bilder kann man mittels "Drag and Drop" vom Browser oder Dateimanager hinzufügen; sie werden dann hier angezeigt. Das Editieren oder Hinzufügen eines Bild-Elements kann mit einem Doppelklick ausgelöst werden. Frame-Liste &kid3; kann die meisten Frames aller unterstützten Dateiformate editieren. Gebräuchliche Frames können über vereinheitlichte Namen angesprochen werden, so dass sie auch zwischen unterschiedlichen Dateiformaten ausgetauscht werden können. Frames ohne vereinheitlichte Namen können über die formatspezifische Bezeichnung erreicht werden. Zuordnung der vereinheitlichten Frame-Namen in den unterschiedlichen Formaten Vereinheitlicht ID3v2.3 ID3v2.4 MP4 ASF VorbisTitel TIT2 TIT2 ©nam Title TITLEInterpret TPE1 TPE1 ©ART Author ARTISTAlbum TALB TALB ©alb WM/AlbumTitle ALBUMKommentar COMM COMM ©cmt Description COMMENTDatum TYER TDRC ©day WM/Year DATETracknummer TRCK TRCK trkn WM/TrackNumber TRACKNUMBERStil TCON TCON ©gen WM/Genre GENREAlbuminterpret TPE2 TPE2 aART WM/AlbumArtist ALBUMARTISTArrangierung IPLS TIPL ARRANGER WM/Producer ARRANGERAutor TOLY TOLY AUTHOR AUTHORBPM TBPM TBPM tmpo WM/BeatsPerMinute BPMKatalognummer TXXX:CATALOGNUMBER TXXX:CATALOGNUMBER CATALOGNUMBERKompilation TCMP TCMP cpil COMPILATIONKomponist TCOM TCOM ©wrt WM/Composer COMPOSERLeiter/Dirigent TPE3 TPE3 CONDUCTOR WM/Conductor CONDUCTORCopyright TCOP TCOP cprt Copyright COPYRIGHTCD-Nummer TPOS TPOS disk WM/PartOfSet DISCNUMBERKodiert von TENC TENC ©enc WM/EncodedBy ENCODED-BYKodierungseinstellungen TSSE TSSE ©too WM/EncodingSettings ENCODERSETTINGSAufnahmedatum TDEN WM/EncodingTime ENCODINGTIMEGruppierung TIT1 TIT1 ©grp WM/ContentGroupDescription GROUPINGAnfangstonart TKEY TKEY WM/InitialKey INITIALKEYISRC TSRC TSRC ISRC WM/ISRC ISRCSprache TLAN TLAN LANGUAGE WM/Language LANGUAGETexter TEXT TEXT LYRICIST WM/Writer LYRICISTText USLT USLT ©lyr WM/Lyrics LYRICSMedium TMED TMED SOURCEMEDIA SOURCEMEDIAStimmung TMOO WM/Mood MOODOriginalalbum TOAL TOAL ORIGINALALBUM WM/OriginalAlbumTitle ORIGINALALBUMOriginalkünstler TOPE TOPE ORIGINALARTIST WM/OriginalArtist ORIGINALARTISTOriginaldatum TORY TDOR ORIGINALDATE WM/OriginalReleaseYear ORIGINALDATETeil TSST PART WM/SubTitleDescription PARTInterpret IPLS TMCL PERFORMER PERFORMERBild APIC APIC covr WM/Picture METADATA_BLOCK_PICTUREHerausgeber TPUB TPUB PUBLISHER WM/Publisher PUBLISHERVeröffentlichungsland TXXX:RELEASECOUNTRY TXXX:RELEASECOUNTRY RELEASECOUNTRYRemixer TPE4 TPE4 REMIXER WM/ModifiedBy REMIXERSortiername des Albums TSOA TSOA soal WM/AlbumSortOrder ALBUMSORTSortiername des Albuminterpreten TSO2 TSO2 soaa ALBUMARTISTSORTSortiername des Interpreten TSOP TSOP soar WM/ArtistSortOrder ARTISTSORTSortiername des Komponisten TSOC TSOC soco COMPOSERSORTSortiername des Titels TSOT TSOT sonm WM/TitleSortOrder TITLESORTUntertitel TIT3 TIT3 SUBTITLE WM/SubTitle SUBTITLEWebseite WOAR WOAR WEBSITE WM/AuthorURL WEBSITEWWW Audio-Datei WOAF WOAF WM/AudioFileURL WWWAUDIOFILEWWW Audio-Quelle WOAS WOAS WM/AudioSourceURL WWWAUDIOSOURCE
Bemerkungen zu den Zuordnungen der vereinheitlichten Frame-Namen: Die Zahl der vereinheitlichten Frame-Namen ist insofern beschränkt, als dass in allen unterstützten Dateiformaten eine sinnvolle Zuordnung möglich sein soll. Die meisten Tags unterstützen Frames mit beliebigen Namen; diese werden dann verwendet, wenn kein spezifisches Frame zur Verfügung steht (z.B. die Namen in Großbuchstaben in der Spalte MP4). Falls keine solche Möglichkeit besteht, so kann es vorkommen, dass für ein Format bestimmte Frame-Typen nicht unterstützt werden können, so Autor und Interpret für ASF (WMA). Die Zuordnungen sind nicht rein willkürlich, sondern orientieren sich daran, wie die Frames in anderen Programmen und Geräten verwendet werden. So legt das ID3v2-Frame "TPE2 - Band/Orchester/Begleitung" nicht unbedingt die Verwendung als Albuminterpret nahe, doch ist dies die gebräuchlichste Verwendung. Die eigentliche Bedeutung bei ID3v2 ist dann wiederum der Grund, wieso dieses Frame beim Importieren (z.B. von Discogs) für das Orchester verwendet wird, obwohl dies für andere Tag-Formate etwas sonderbar erscheinen mag. Die Zuordnung ist nicht immer eindeutig. So wird bei ID3v2.3 sowohl für Arrangierung als auch für Interpret ein IPLS-Frame verwendet. Beim Zurücklesen wird dann für beide Typen "Arrangierung" angezeigt. Die Frames Arrangierung und Interpret verwenden ein besonderes Format für ihren Inhalt: "Mitwirkung 1|Mitwirkender 1|Mitwirkung 2|Mitwirkender 2|...", beispielsweise "Chorus Master|Ernst Dunshirn|Soprano Vocals|Anna Netrebko". Dadurch wird für die IPLS bzw. TIPL/TMCL Frames in MP3-Dateien eine Zeichenkettenliste mit dem spezifizierten Inhalt erzeugt (das "|" dient als Trennzeichen zwischen den einzelnen Zeichenketten). Entsprechende Werte werden auch beim Import von Daten von Servern, welche diese Informationen bieten, gesetzt.
Das Menü Datei CtrlO Datei Öffnen... Ein Verzeichnis wird geöffnet, und alle Dateien, welche den gewählten Dateinamenfilter erfüllen, werden im Dateilistenfeld angezeigt. Die gewählte Datei wird selektiert. Datei Zuletzt geöffnete Dateien Ein kürzlich geöffnetes Verzeichnis wird geöffnet. CtrlD Datei Verzeichnis öffnen... Ein Verzeichnis wird geöffnet, und alle Dateien, welche den gewählten Dateinamenfilter erfüllen, werden im Dateilistenfeld angezeigt. CtrlS Datei Speichern Speichert alle veränderten Dateien im Verzeichnis. Die veränderten Dateien sind durch ein Diskettensymbol gekennzeichnet. Wenn Dateinamen verändert wurden, so werden die entsprechenden Dateien umbenannt. Datei Zuletzt gespeicherte Fassung Macht die Änderungen an einer oder mehreren Dateien rückgängig. Wenn keine Dateien im Dateilistenfeld selektiert sind, so werden die Änderungen an allen Dateien rückgängig gemacht, ansonsten nur an den ausgewählten Dateien. Datei Importieren... Der Import Dialog wird verwendet, um Daten direkt von einem freedb.org Server, von einem MusicBrainz Server, von Discogs, Amazon oder anderen Quellen mit Albumtitellisten zu importieren. Zum Importieren von einem freedb.org Server wird Von Server: gnudb.org oder TrackType.org gewählt. Der zu suchende Interpret und der Name des Albums können in den zwei oberen Feldern eingegeben werden, und die gefundenen Alben werden nach Klick auf Suchen dargestellt, sobald sie von www.gnudb.org empfangen werden. Die Track Daten eines Albums werden nach Auswahl geladen, der verwendete freedb.org Server kann gewählt werden, ebenso der CGI Pfad. Die importierten Daten werden in der Vorschau des Import Dialogfensters dargestellt. Wenn man mit den Daten in der Vorschau zufrieden ist, können sie mit OK importiert werden. Zum Importieren vom Discogs Server wird Discogs gewählt. Wie bei Von gnudb.org können auch hier Artist und Album eingegeben werden und es wird ebenfalls eine Liste mit Alben zur Auswahl gestellt. Wenn Standard Tags markiert ist, werden grundlegende Informationen importiert, so z.B. Interpret, Album und Titel. Wenn Zusätzliche Tags markiert ist, werden mehr verfügbare Informationen importiert, z.B. Interpreten, Arrangierer oder der Herausgeber. Wenn Cover-Bilder markiert ist, werden Cover-Bilder heruntergeladen, falls vorhanden. Zum Importieren von Amazon wird Amazon gewählt. Wie bei Von gnudb.org können auch hier Artist und Album eingegeben werden und es wird ebenfalls eine Liste mit Alben zur Auswahl gestellt. Wenn Zusätzliche Tags markiert ist, werden mehr verfügbare Informationen importiert, z.B. Interpreten, Arrangierer oder der Herausgeber. Wenn Cover-Bilder markiert ist, werden Cover-Bilder heruntergeladen, falls vorhanden. In der gleichen Art kann mit Von MusicBrainz Album auch in der Album-Datenbank von MusicBrainz gesucht werden. Die Bedienung ist wie bei gnudb.org beschrieben. Zum Importieren von einem MusicBrainz Server dient der Dialog, der nach Klicken auf Von MusicBrainz Fingerprint erscheint. Wie bei freedb kann auch hier der Server ausgewählt werden. Unten ist eine Tabelle, welche die importierten Track Daten darstellt. Die rechte Spalte zeigt dabei den Zustand der MusicBrainz-Abfrage, welche gleich mit dem Öffnen des Dialogs mit "Suche läuft" beginnt. Es folgen dann ein Abfrage des Fingerprints und, falls diese kein Ergebnis liefert, eine Suche anhand der in der Datei vorhandenen Tags. Für eine erfolgreiche MusicBrainz-Suche kann es daher hilfreich sein, vorhandene Informationen (z.B. Interpret und Album) vorangehend in den Tags zu speichern. Wenn ein Resultat gefunden wurde, so endet die Suche im Zustand "Erkannt", andernfalls wurde nichts gefunden oder gleich mehrere mögliche Resultate, von den der Benutzer eines auswählen kann. OK und Anwenden übernehmen die importierten Daten, Abbrechen schließt den Dialog. Das Schließen des Dialogs kann eine Weile dauern, da noch die ganze MusicBrainz-Maschinerie runtergefahren werden muss. Zum Importieren von Albumdaten in Textform öffnet man einen Unterdialog mit Von Datei/Zwischenablage. Dort stehen mehrere vorkonfigurierte Formate zur Verfügung. Die ersten zwei, "CSV unquoted" und "CSV quoted", dienen dazu, Daten welche über die Export Funktion exportiert wurden, wieder zu importieren. Diese Daten können natürlich von einer Tabellenkalkulation bearbeitet werden. CSV-Daten aus einer Tabellenkalkulation müssen mit Tabulator als Trennzeichen geschrieben werden und sollten sich über "CSV quoted" einlesen lassen. Dieses ist flexibler als "CSV unquoted", dafür dürfen die enthaltenen Felder keine Anführungszeichen enthalten. Falls nur von &kid3; exportiert und wieder importiert wird, so sollte dies problemlos mit "CSV unquoted" möglich sein. Das nächste Format, "freedb HTML text", kann benutzt werden, um Informationen aus einer HTML-Seite von freedb.org zu kopieren. Dazu sucht man sich in freedb das Album, und wenn die gewünschte Information im Browser angezeigt wird, kann sie selektiert und in die Zwischenablage kopiert werden. Nach einem Klick auf Von Zwischenablage werden die importierten Daten in der Tabelle angezeigt und können mit OK übernommen werden. Das Ziel (Tag 1, Tag 2 oder Tag 1 und Tag 2) kann mit einem Auswahlfeld festgelegt werden. Die Titel in der Dateiliste sollten in der korrekten Reihenfolge angezeigt sein um die richtigen Tags zu kriegen. Dies ist kein Problem, wenn sie nummeriert sind. Das nächste vorkonfigurierte Format, "freedb HTML source", kann dann benutzt werden, wenn die Import-Daten als HTML-Dokument vorliegen. Solch eine Datei kann im Datei Dialog, welcher nach Klick auf Von Datei erscheint, ausgewählt werden. Natürlich können die Daten auch in einem Editor kopiert und dann über die Zwischenablage übernommen werden. Dieses Format kann für Offline Import nützlich sein, obschon die HTML-Datei auch von einem Browser angezeigt und im ersten Format über die Zwischenablage importiert werden kann. Weitere vorkonfigurierte Formate, z.B. "Track Title Time" stehen zur Verfügung. Mit Klick auf Hinzufügen kann ein leeres benutzerdefiniertes Format erzeugt und vom Benutzer gesetzt werden. Die zwei Zeilen unterhalb des Formatnamens können hierzu mit regulären Ausdrücken gefüllt werden. Der erste Ausdruck wird einmal angewendet, um albumspezifische Daten wie Interpret, Album, Jahr und Stil zu extrahieren. Die zweite Zeile wird vom Dokumentanfang bis zum Schluss für jeden Titel probiert, um Titeldaten zu kriegen, also Nummer und Titel. In den regulären Ausdrücken können alle von Qt unterstützten Funktionen eingesetzt werden; also das meiste von dem, was Perl bietet. Klammerkonstrukte "(..)" werden benützt, um Felddaten zu extrahieren und werden von einem &kid3;-spezifischen Feldcode eingeleitet. Die Codes sind dieselben wie beim Dateinamenformat, auch hier sind nebst den unten aufgelisteten Codes beliebige Framenamen möglich: %s %{title} Titel (Song) %a %{artist} Interpret (Artist) %l %{album} Album %c %{comment} Kommentar (Comment) %y %{year} Jahr (Year) %t %{track} Nummer (Track) %g %{genre} Stil (Genre) %d %{duration} Länge (Dauer) Beispiel: Ein regulärer Ausdruck für die zweite Zeile, um Titel aus einer .m3u Stückliste zu holen, könnte "%{track}(\d+)\s+%{title}(\S[^\r\n]*)\.mp3[\r\n]" sein. Alle Formate können geändert werden, indem der Name und die regulären Ausdrücke editiert werden und Einstellungen speichern gedrückt wird. Gespeichert werden sie in der Datei kid3rc im Konfigurationsverzeichnis. Diese Datei kann auch direkt verändert werden, um mehr Importformate zu haben, oder man kann sie löschen, um wieder zu den Standardeinstellungen zu kommen. Formate können mit Entfernen gelöscht werden. Genauigkeit zeigt eine Abschätzung, wie gut die importierten Daten zu den Stücken passen. Dazu werden die Längen oder die Dateinamen verglichen um den Grad an Übereinstimmung in Prozent zu errechnen. Cover-Bild zeigt die URL des Cover-Bildes, welches heruntergeladen wird. Um zu prüfen, ob die importierten Stücke den vorhandenen Dateien entsprechen, können die Längen verglichen werden. Diese Option kann mit dem Markierungsfeld Teste maximal erlaubten Zeitunterschied eingeschaltet werden und der maximal erlaubten Zeitunterschied kann in Sekunden eingestellt werden. Wird eine Überschreitung dieser Toleranzschwelle festgestellt, so wird die entsprechende Länge in der Vorschau rot dargestellt. Falls nun die vorhandenen Dateien in einer anderen Reihenfolge als die importierten Stücke vorliegen, so sollte die Zuordnung korrigiert werden. Diese Arbeit wird durch die Zuordnen mit Knöpfe Länge, Nummer und Titel erleichtert, welche die Stücke dem jeweiligen Feld entsprechend zuordnen. Sollte die Zuordnung noch manuell korrigiert werden müssen, so kann ein Track nach Drücken der Steuerungs-Taste (Ctrl/Strg) mit der linken Maustaste gefasst und dann an die gewünschte Stelle gezogen werden. Wenn der Import Dialog geöffnet wird, enthält die Tabelle die aktuell vorhandenen Werte der Tags. Der Tag Typ (Tag 1, Tag 2, Tag 1 und Tag 2) kann über das Ziel Auswahlfeld selektiert werden. Der Knopf rechts von diesem Auswahlfeld dient dazu, die Tabelle auf die in den Tags gespeicherten Werte zurückzusetzen. Über die Markierungsfelder in der ersten Tabellenspalte können die Stücke gewählt werden, deren Tags importiert werden sollen. Dies kann z.B. dann nützlich sein, wenn ein Verzeichnis alle Stücke eines Doppelalbums enthält, aber bloß die Stücke der zweiten CD importiert werden sollen. Um die Dateien, welche importiert werden, zu identifizieren, können die Dateinamen oder die vollständigen Pfade in der Tabelle dargestellt werden. Diese Einstellung ist über das Kontextmenü, welches bei Rechtsklick auf die Kopfzeile erscheint, möglich. Die Werte in der Import-Tabelle lassen sich editieren. Die momentan in den Tags vorhandenen Werte lassen sich mit dem Rückgängig-Knopf rechts vom Ziel-Listenfeld wiederherstellen. Dies ist auch dann nützlich, wenn man das Ziel verändert hat und die dort vorhandenen Werte in der Tabelle sehen möchte. Wie in fast allen Dialogen dient der Einstellungen speichern Knopf dazu, die dialogspezifischen Optionen und die Fenstergröße persistent zu speichern. Von Tags führt zu einem Unterdialog, mit welchem sich Tag Frames mit dem Inhalt aus anderen Tag Frames füllen lassen. Dies kann dazu benutzt werden, um einfach Informationen zwischen Tags zu kopieren oder einen Teil des Inhaltes eines Tag Frames zu extrahieren und in einem anderen Tag Frame einzufügen. Wie beim Import von Datei/Clipboard stehen frei konfigurierbare Formate zur Verfügung, um verschiedene Operationen durchzuführen. Vorkonfigurierte Formate erlauben es beispielsweise, den Wert des Interpreten zum Album Interpreten, Komponisten oder Leiter zu kopieren, bei Titeln, welche die Tracknummer enthalten, diese zu extrahieren und im Tracknummer-Frame zu speichern. Ebenso lässt sich ein Untertitel aus dem Titel extrahieren. Das Erstellen eines eigenen Importformates wird im folgenden anhand eines Beispieles erklärt. Die Information im Untertitel soll auch im Kommentar gesetzt werden. Dazu wird ein neues Format mit Hinzufügen erstellt und ein neuer Name gesetzt, in diesem Beispiel also "Subtitle to Comment". Nun gibt man unter Quelle "%{subtitle}" und unter Ermittlung "%{comment}(.*)" ein und klickt Einstellungen speichern. Der Ausdruck in Quelle kann aus Format Codes für beliebige Tag Frames bestehen, wobei natürlich auch mehrere Codes verwendet werden können, um den Inhalt verschiedener Frames zu kombinieren. Für jeden Track wird aus den Tags und dem Quelle Format ein Text erzeugt, und auf diesen wird dann der reguläre Ausdruck aus Ermittlung angewendet, um neue Tag Werte zu setzen. Dieser reguläre Ausdruck enthält Klammerkonstrukte "(..)", um Felddaten zu extrahieren, und die Klammer-Konstrukte werden vom Format Code des Tag Frames eingeleitet. Es funktioniert also gleich wie bei den Formaten zum Import von Datei/Clipboard . Datei Importieren von gnudb.org... Importieren von einem freedb.org Server mit Album-Suche auf gnudb.org. Dieser Menüpunkt öffnet denselben Import Dialog wie Importieren..., öffnet jedoch direkt auch den gnudb.org Dialog. Datei Importieren von TrackType.org... Importieren vom TrackType.org Server. Dieser Menüpunkt öffnet denselben Import Dialog wie Importieren..., öffnet jedoch direkt auch den TrackType.org Dialog. Datei Importieren von Discogs... Importieren vom Discogs Server. Dieser Menüpunkt öffnet denselben Import Dialog wie Importieren..., öffnet jedoch direkt auch den Discogs Dialog. Datei Importieren von Amazon... Importieren von Amazon. Dieser Menüpunkt öffnet denselben Import Dialog wie Importieren..., öffnet jedoch direkt auch den Amazon Dialog. Datei Importieren von MusicBrainz Album... Importieren von der MusicBrainz Album Datenbank. Dieser Menüpunkt öffnet denselben Import Dialog wie Importieren..., öffnet jedoch direkt auch den Von MusicBrainz Album Dialog. Datei Importieren von MusicBrainz Fingerprint... Importieren von einem MusicBrainz Server. Dieser Menüpunkt öffnet denselben Import Dialog wie Importieren..., öffnet jedoch direkt auch den Von MusicBrainz Fingerprint Dialog. Datei Automatisch importieren... Automatisch importieren lassen sich Informationen für mehrere Alben von verschiedenen Web-Diensten. Wenn Verzeichnisse in der Dateiliste selektiert sind, so werden Metadaten für die Stücke der gewählten Verzeichnisse importiert. Wenn kein Verzeichnis ausgewählt ist, so werden Informationen für alle Verzeichnisse innerhalb der Dateiliste importiert. Der Tag Typ (Tag 1, Tag 2, Tag 1 und Tag 2) kann über das Ziel Auswahlfeld selektiert werden. Profile bestimmen, von welchen Servern die Album-Informationen geholt werden. Einige Profile sind bereits vordefiniert (All, MusicBrainz, Discogs, Cover Art), eigene Profile können mit dem Hinzufügen Knopf rechts von der Profil Auswahlliste erstellt werden. Die Tabelle darunter zeigt die Server, welche benutzt werden, wenn mit dem ausgewählten Profil importiert wird. Der Import-Prozess für ein Album wird beendet, wenn alle geforderten Informationen gefunden wurden, daher ist die Reihenfolge der Server in der Tabelle wichtig. Sie kann mit den Nach oben und Nach unten Knöpfen verändert werden. Mit Bearbeiten kann ein bestehender Eintrag verändert werden. Die Server Auswahl stellt dieselben Server zur Verfügung wie die Import-Funktionen. Standard Tags, Zusätzliche Tags und Cover-Bilder bestimmen die Informationen, welche vom Server geholt werden sollen. Die Genauigkeit schließlich bestimmt die minimale Übereinstimmung, welche erreicht werden muss, damit die importierten Daten akzeptiert werden. Ist sie ungenügend, so wird ein Import mit dem nächsten Server versucht. Dieser Dialog mit den Server-Eigenschaften wird auch angezeigt wenn Hinzufügen gewählt wird, um einen neuen Eintrag zu erstellen. Bestehende Zeilen können mit Entfernen gelöscht werden. Um einen automatischen Import mit dem ausgewählten Profil zu beginnen wird Starten gedrückt. Details über den laufenden Prozess werden im Dialog oben angezeigt. Abbrechen führt zu einer vorzeitigen Beendigung des Imports. Datei Nach Cover-Bildern suchen... Der Dialog Nach Cover-Bildern suchen hilft bei der Suche nach Album Cover-Bildern. Interpret/Album werden wenn möglich aus den Tags übernommen. Quelle stellt eine Vielzahl von Websites mit Cover-Bildern zur Verfügung. Unter dem Namen ist die URL mit Interpret und Album als Parametern angegeben. Für URL-codierte Werte können dabei %u{artist} und %u{album} angegeben werden oder auch andere Werte aus den Tags, wie in Kid3 einrichten bei Benutzerbefehle beschrieben. Nach "Custom Source" können auch eigene Quellen hinzugefügt werden. Dazu wird "Custom Source" durch den Namen der Quelle ersetzt, dann Return gedrückt, die URL eingegeben und mit Einstellungen speichern gesichert. Das resultierende Browser-Kommando wird oben im Dialog dargestellt und nach Drücken des Browser-Knopfes ausgeführt. Der Browser, welcher in den Einstellungen konfiguriert werden kann, wird mit der gewählten Quelle gestartet. Das gewünschte Cover-Bild kann dann aus dem Browser in das &kid3; Fenster gezogen werden und wird im Bild-Feld von Tag 2 der selektierten Dateien gesetzt. Da nicht alle Browser Drag'n'Drop von Bildern unterstützen und häufig die Bilder auf Websites mit einer URL unterlegt sind, erhält &kid3; in solchen Fällen bloß die entsprechende URL und nicht das Bild. Wenn die URL auf ein Bild zeigt, wird dieses heruntergeladen. Falls die URL jedoch auf sonst eine Website hinweist, so muss von dieser auf das gewünschte Cover-Bild geschlossen werden. Dies ist die Aufgabe der Tabelle URL Ersetzung. Die linke Spalte Vergleich enthält einen regulären Ausdruck, welcher mit der URL verglichen wird. Wenn eine Übereinstimmung festgestellt wird, so werden die erhaltenen Klammer-Werte im Muster der Spalte URL des Bildes ersetzt (\1 usw.). Der ersetzte reguläre Ausdrucks enthält dann die URL des Bildes. Auf diese Weise können auch Bilder von Amazon, Google Images, usw. per Drag'n'Drop importiert werden. Natürlich können auch hier eigene Ersetzungen erfasst werden. Datei Exportieren... Der Export Dialog wird verwendet, um die Daten aus den Tags in einer Datei oder der Zwischenablage zu speichern. Das oberste Editierfeld stellt einen einfachen Editor zur Verfügung, welcher eine Vorschau der zu exportierenden Daten bietet. Falls die Export-Daten Tabulatorzeichen enthalten, wird der Export in einer Tabelle dargestellt. Die Daten werden aus den Tags der Dateien im aktuellen Verzeichnis generiert, und zwar gemäß dem unten eingestellten Format. Diese Formateinstellung ist gleich aufgebaut wie im Import Dialog: Im obersten Feld ist der Titel (z.B. "CSV unquoted"), dann folgt der Header - dieser wird nur am Anfang der Datei generiert. Nun folgt das Track-Format, es wird für jeden Track angewendet. Schließlich gibt es noch einen Trailer, welcher die Datei noch abschließt. Die Formatfelder enthalten keine regulären Ausdrücke wie im Import Dialog, sondern bloß Ausgabeformate, bei denen spezielle %-Ausdrücke durch Werte aus den Tags ersetzt werden. Das ganze verhält sich also gleich wie beim Dateinamenformat, es werden auch dieselben Codes verwendet, plus ein paar zusätzliche. Nicht nur die unten aufgelisteten Codes sind möglich, es können die Namen aller Tag Frames verwendet werden. %s %{title} Titel (Song) %a %{artist} Interpret (Artist) %l %{album} Album %c %{comment} Kommentar (Comment) %y %{year} Jahr (Year) %t %{track} Nummer (Track) %t %{track.n} Nummer mit Feldlänge n (z.B. 001 für %{track.3}) %T %{tracknumber} Nummer (Track), ohne führende Null %g %{genre} Stil (Genre) %f %{file} Dateiname (File name) %p %{filepath} Pfadname %u %{url} URL %d %{duration} Dauer in Minuten:Sekunden %D %{seconds} Dauer in Sekunden %n %{tracks} Anzahl Tracks im Album %e %{extension} Dateiendung %O %{tag1} Das Format von Tag 1 (ID3v1.1 oder leer falls nicht vorhanden) %o %{tag2} Das Format von Tag 2 (ID3v2.3.0, ID3v2.4.0, ID3v2.2.0, ID3v2.2.1, Vorbis, APE, MP4, ASF oder leer falls nicht vorhanden) %b %{bitrate} Bitrate in kbit/s %v %{vbr} VBR oder leer (nur für ID3v2.3 mit id3lib) %r %{samplerate} Samplerate in Hz %m %{mode} Kanalmodus (Stereo oder Joint Stereo) %h %{channels} Anzahl Kanäle (1 oder 2) %k %{codec} Codec (z.B. MPEG 1 Layer 3, MP4, Ogg Vorbis, FLAC, MPC, APE, ASF, AIFF, WAV) Mit diesen Formatcodes sind bereits einige Formate vordefiniert. "CSV unquoted" trennt die einzelnen Felder durch Tabulatoren. Daten in diesem Format können einfach wieder über das gleichnamige Importformat in &kid3; importiert werden. "CSV quoted" setzt die Felder zusätzlich noch in Anführungszeichen, was den Import in Tabellenkalkulationen erleichtert. Dafür sollten in den Feldern keine Anführungszeichen vorkommen. "Extended M3U" und "Extended PLS" erzeugen Stücklisten mit Zusatzinformationen und absoluten Pfadnamen. "HTML" dient dazu, eine HTML-Seite mit verlinkten Tracks zu erstellen. "Kover XML" generiert eine XML-Datei, welche vom Cover-Druck-Programm Kover gelesen werden kann. "Technical Details" liefert Informationen über Bitrate, Samplerate, Kanäle, usw. "Custom Format" schließlich ist frei für ein eigenes Format. Natürlich können auch die anderen Formate beliebig angepasst werden, oder es können noch mehr Formate in der Datei kid3rc im Konfigurationsverzeichnis hinzugefügt werden. Die Quelle der Daten (Tag 1 oder Tag 2) kann mit einem Auswahlfeld festgelegt werden. Ein Klick auf In Datei bzw. In Zwischenablage speichert die Daten in einer Datei oder der Zwischenablage. OK und Abbrechen beenden den Dialog, wobei OK die aktuellen Einstellungen übernimmt. Datei Stückliste erstellen... Erzeugt eine Stückliste. Das Format und der Inhalt der Stückliste können mit verschiedenen Optionen bestimmt werden. Wie Verzeichnisname generiert den Dateinamen der Wiedergabeliste aus dem Namen des Verzeichnisses. Es kann aber auch ein Format mit Werten aus den Tags benutzt werden, z.B. "%{artist} - %{album}", um die Namen von Interpret und Album im Dateinamen zu haben. Es können dieselben Format Codes wie bei Exportieren verwendet werden. Die Dateierweiterung richtet sich nach dem Format der Stückliste. Wo die Wiedergabeliste erstellt wird, kann bestimmt werden mit der Auswahlliste Erstelle in Aktuellem Verzeichnis Die Stückliste wird im aktuellen Verzeichnis erstellt und enthält nur Dateien aus diesem Verzeichnis. Jedem Ordner In jedem Verzeichnis, welches gelistete Dateien enthält, wird eine eigene Stückliste erstellt, welche die Dateien aus dem jeweiligen Verzeichnis enthält. Basisordner Nur eine einzige Stückliste wird im Basisverzeichnis (d.h. dem Verzeichnis der Dateiliste) erstellt, und sie enthält die gelisteten Dateien aus dem Basisverzeichnis und allen Unterverzeichnissen. Das Format der Wiedergabeliste kann M3U, PLS oder XSPF sein. Nur ausgewählte Dateien einfügen führt dazu, dass nur ausgewählte Dateien in der Stückliste erscheinen. Wenn ein Verzeichnis ausgewählt ist, so gelten all seine Dateien ebenfalls als selektiert. Wenn diese Option nicht aktiviert ist, gelangen alle Audio-Dateien in die Stückliste. Sortiere nach Dateiname ordnet die Dateien wie üblich nach dem Dateinamen in der Wiedergabeliste an. Mit Sortiere nach Tag-Feld ist es möglich, eine Format-Zeichenkette mit Tag-Werten für die Sortierung zu verwenden. "%{track.3}" kann beispielsweise benutzt werden, um nach der Tracknummer zu sortieren (".3" wird verwendet, um 3 Stellen mit führenden Nullen zu kriegen, und somit korrekt nach der Nummer zu sortieren). Es ist auch möglich, mehrere Codes zu verwenden, z.B. "%{genre}%{year}", um nach einer Zeichenkette aus Stil und Jahr zu sortieren. Die Einträge in der Stückliste sind relative oder absolute Pfadangaben, je nachdem ob Benutze relativen Pfad für Dateien in der Wiedergabeliste oder Benutze vollen Pfad für Dateien in der Wiedergabeliste gesetzt ist. Nur Liste der Dateien schreiben führt dazu, dass die Stückliste nur die Pfade zu den Dateien enthält. Wünscht man zusätzliche Informationen, so steht Schreibe Informationen mit einer Format-Zeichenfolge zur Verfügung, und es wird eine erweiterte Stückliste generiert. CtrlQ Datei Beenden Beendet die Anwendung. Das Menü Bearbeiten AltA Bearbeiten Alles auswählen Selektiert alle Dateien. CtrlShiftA Bearbeiten Abwählen Deselektiert alle Dateien. Bearbeiten Alles im Verzeichnis auswählen Selektiert alle Dateien im aktuellen Verzeichnis. AltUp Bearbeiten Vorangehende Datei Selektiert die vorangehende Datei. AltDown Bearbeiten Nächste Datei Selektiert die nächste Datei. Das Menü Extras Extras Dateinamenformat anwenden Wenn Während des Editierens formatieren im Konfigurationsdialog beim Dateinamenformat deaktiviert ist, kann man mit diesem Menüpunkt die Formatierung auf die Namen der ausgewählten Dateien anwenden. Diese Funktion kann auch angewendet werden, um zu testen, ob die Dateinamen dem eingestellten Format entsprechen. Hierzu sollten alle Dateien gespeichert werden; nach Anwendung des Formates sind dann nur diejenigen Dateien verändert (in der Dateiliste mit einem Diskettensymbol versehen), welche nicht dem eingestellten Format entsprechen. Ungewollte Änderungen können mit Zuletzt gespeicherte Fassung rückgängig gemacht werden. Extras Tag-Format anwenden Wenn Während des Editierens formatieren im Konfigurationsdialog beim Tag Format deaktiviert ist, kann man mit diesem Menüpunkt die Formatierung auf die Tags der ausgewählten Dateien anwenden. Diese Funktion kann auch angewendet werden, um zu testen, ob die Tags dem eingestellten Format entsprechen. Hierzu sollten alle Dateien gespeichert werden; nach Anwendung des Formates sind dann nur diejenigen Dateien verändert (in der Dateiliste mit einem Diskettensymbol versehen), welche nicht dem eingestellten Format entsprechen. Ungewollte Änderungen können mit Zuletzt gespeicherte Fassung rückgängig gemacht werden. Extras Textkodierung anwenden Setzt die Textkodierung, welche in Einstellungen/Kid3 einrichten.../Tags/Tag 2 eingestellt ist, für alle ausgewählten Dateien. Wenn UTF8 eingestellt ist, so wird für ID3v2.3.0 Tags UTF16 verwendet, denn dieses Format unterstützt UTF8 nicht. Extras Verzeichnis umbenennen... Dieser Dialog bietet die Möglichkeit, automatisch das aktuelle Verzeichnis gemäß den Tags umzubenennen. Verschiedene Formate sind vordefiniert und Informationen über Interpret, Album und Jahr in den Verzeichnisnamen zu übernehmen. Es ist auch möglich, ein eigenes Format zu definieren, die folgenden Codes können verwendet werden, um Werte aus den Tags im Verzeichnisnamen einzusetzen: %s %{title} Titel (Song) %a %{artist} Interpret (Artist) %l %{album} Album %c %{comment} Kommentar (Comment) %y %{year} Jahr (Year) %t %{track} Nummer (Track)) %t %{track.n} Nummer mit Feldlänge n (z.B. 001 für %{track.3}) %T %{tracknumber} Nummer (Track), ohne führende Null %g %{genre} Stil (Genre) Wenn ein Verzeichnis-Trennzeichen "/" im Format gefunden wird, werden mehrere Verzeichnisse erstellt. Will man ein neues Verzeichnis erstellen statt das aktuelle Verzeichnis umbenennen, so wählt man Verzeichnis erstellen statt Verzeichnis umbenennen. Die Quelle der Tag-Information kann zwischen Von Tag 1 und Tag 2, Von Tag 1 und Von Tag 2 ausgewählt werden. Die voraussichtliche Verzeichnisnamensänderung für die erste Datei kann in den Abschnitten Von und Nach des Dialogs gesehen werden. Es können auch mehrere Verzeichnisse umbenannt werden, indem sie selektiert werden. Extras Tracks nummerieren... Falls die Track-Nummern in den Tags nicht vorhanden oder falsch sind, so lassen sich mit dieser Funktion die Tracks automatisch aufsteigend nummerieren. Im Dialog kann die Startnummer angegeben werden. Falls nur ein Teil der Tracks nummeriert werden soll, so sind diese im Voraus auszuwählen. Wenn Anzahl Stücke markiert ist, wird auch die Anzahl Tracks in den Tags gesetzt. Es können auch die Tracks über mehrere Ordner hinweg nummeriert werden. Dazu müssen die Verzeichnisse aufgeklappt und selektiert werden. Extras Filter... Der Filter kann dazu verwendet werden, nur diejenigen Dateien anzeigen, welche gewisse Eigenschaften erfüllen. Dies kann dann nützlich sein, wenn man die Tags oder Dateinamen in einer größeren Sammlung bereinigen möchte, jedoch nur diejenigen Dateien bearbeiten möchte, welche nicht dem gewünschten Schema entsprechen. Der Ausdruck, der bestimmt, welche Dateien angezeigt werden, benutzt dieselben Formatcodes, welche auch beim Dateinamenformat, Importieren und Exportieren verwendet werden. %s %{title} Titel (Song) %a %{artist} Interpret (Artist) %l %{album} Album %c %{comment} Kommentar (Comment) %y %{year} Jahr (Year) %t %{track} Nummer (Track)) %t %{track.n} Nummer mit Feldlänge n (z.B. 001 für %{track.3}) %T %{tracknumber} Nummer (Track), ohne führende Null %g %{genre} Stil (Genre) %f %{file} Dateiname (File name) %p %{filepath} Absoluter Pfadname %e %{extension} Dateiendung %O %{tag1} Das Format von Tag 1 (ID3v1.1 oder leer falls nicht vorhanden) %o %{tag2} Das Format von Tag 2 (ID3v2.3.0, ID3v2.4.0, ID3v2.2.0, ID3v2.2.1, Vorbis, APE, MP4, ASF oder leer falls nicht vorhanden) %b %{bitrate} Bitrate in kbit/s %v %{vbr} VBR oder leer (nur für ID3v2.3 mit id3lib) %r %{samplerate} Samplerate in Hz %m %{mode} Kanalmodus (Stereo oder Joint Stereo) %h %{channels} Anzahl Kanäle (1 oder 2) %k %{codec} Codec (z.B. MPEG 1 Layer 3, MP4, Ogg Vorbis, FLAC, MPC, APE, ASF, AIFF, WAV) %1a %1{artist}, ... Der Präfix 1 wird benutzt, um Werte von Tag 1 zu erhalten %2a %2{artist}, ... Der Präfix 2 wird benutzt, um Werte von Tag 2 zu erhalten Diese Codes werden mit dem Wert für die Datei ersetzt und die resultierenden Zeichenketten können mit folgenden Operatoren verglichen werden: s1 equals s2: wahr wenn s1 und s2 gleich sind. s1 contains s2: wahr wenn s2 in s1 enthalten ist. s matches re: wahr wenn s den regulären Ausdruck re erfüllt. Wahre Ausdrücke werden durch 1 ersetzt, falsche durch 0. Wahre Werte repräsentieren nebst 1 auch true, on und yes, falsche Werte entsprechend 0, false, off und no. Diese Wahrheitwerte können durch die logischen Operatoren not, and und or verknüpft werden (die Priorität ist in dieser Reihenfolge), und mit Klammern gruppiert werden. Einige Filterregeln sind bereits vordefiniert und können als Beispiele für eigene Ausdrücke dienen: All Wenn die Dateiliste gefiltert ist - dies wird mit "[Gefiltert]" in der Titelzeile angezeigt - und man möchte wieder alle Dateien anzeigen, kann das Filtern mit diesem Filter rückgängig gemacht werden. Es wird ein leerer Ausdruck verwendet, ein Wahrheitswert wie true hätte aber denselben Effekt. Filename Tag Mismatch not (%{filepath} contains "%{artist} - %{album}/%{track} %{title}") Testet, ob ein Dateipfad dem Dateinamenformat entspricht. Diese Regel wird automatisch angepasst, wenn sich das Dateinamenformat ändert. No Tag 1 %{tag1} equals "" Es werden nur Dateien ohne Tag 1 angezeigt. No Tag 2 %{tag2} equals "" Es werden nur Dateien ohne Tag 2 angezeigt. ID3v2.3.0 Tag %{tag2} equals "ID3v2.3.0" Es werden nur Dateien mit ID3v2.3.0 Tag angezeigt. ID3v2.4.0 Tag %{tag2} equals "ID3v2.4.0" Es werden nur Dateien mit ID3v2.4.0 Tag angezeigt. Tag 1 != Tag 2 not (%1{title} equals %2{title} and %1{album} equals %2{album} and %1{artist} equals %2{artist} and %1{comment} equals %2{comment} and %1{year} equals %2{year} and %1{track} equals %2{track} and %1{genre} equals %2{genre}) Es werden Dateien mit Unterschieden zwischen Tag 1 und Tag 2 angezeigt. Tag 1 == Tag 2 %1{title} equals %2{title} and %1{album} equals %2{album} and %1{artist} equals %2{artist} and %1{comment} equals %2{comment} and %1{year} equals %2{year} and %1{track} equals %2{track} and %1{genre} equals %2{genre} Es werden Dateien mit identischen Tag 1 und Tag 2 angezeigt. Incomplete %{title} equals "" or %{artist} equals "" or %{album} equals "" or %{year} equals "" or %{tracknumber} equals "" or %{genre} equals "" Es werden nur Dateien angezeigt, welche leere Standard-Tag-Werte haben (Titel, Interpret, Album, Datum, Tracknummer, Stil). No Picture %{picture} equals "" Es werden nur Dateien ohne Bild angezeigt. Custom Filter Dieser Eintrag kann verwendet werden, um einen eigenen Filter hinzuzufügen. Um beispielsweise nur Dateien anzuzeigen, deren Interpret mit "The" beginnt, ersetzt man "Custom Filter" durch einen Namen wie "The Bands" und drückt die Enter Taste. Dann fügt man folgenden Ausdruck in das Eingabefeld ein: %{artist} matches "The.*" Gespeichert wird mit Einstellungen speichern. Die Dateien werden mit Anwenden gefiltert. Alle bearbeiteten Dateien werden im Textfeld angezeigt, mit einem "+" vor solchen, die den Filterausdruck erfüllen, und einem "-" vor den anderen. Wenn das Filtern fertig ist, werden nur noch diejenigen Dateien angezeigt, deren Interpret mit "The" beginnt, und in der Titelzeile wird "[Gefiltert]" angezeigt. Extras ID3v2.3 nach ID3v2.4 konvertieren Wenn in den ausgewählten Dateien ID3v2.3 Tags vorhanden sind, so werden sie zu ID3v2.4 Tags konvertiert. Frames, welche von TagLib nicht unterstützt sind, werden verworfen. Nur Dateien ohne ungesicherte Änderungen werden konvertiert. Extras ID3v2.4 nach ID3v2.3 konvertieren Wenn in den ausgewählten Dateien ID3v2.4 Tags vorhanden sind, so werden sie zu ID3v2.3 Tags konvertiert. Nur Dateien ohne ungesicherte Änderungen werden konvertiert. Extras Wiedergabe Damit öffnet sich eine einfache Werkzeugleiste zum Abspielen von Audio-Dateien. Sie enthält Knöpfe für die grundlegenden Operationen (Wiedergabe/Pause, Wiedergabe stoppen, Zurück, Vorwärts, Schließen), Regler für Position und Lautstärke und eine Anzeige der aktuellen Position. Abgespielt werden die selektierten Stücke bei Mehrfachselektion bzw. alle Stücke wenn eine oder keine Datei selektiert ist. Das Menü Einstellungen Einstellungen Werkzeugleiste anzeigen Schaltet die Anzeige der Werkzeugleiste ein und aus. Einstellungen Statusleiste anzeigen Schaltet die Anzeige der Statusleiste, welche längere Aktionen wie Öffnen und Speichern eines Verzeichnisses anzeigt, ein und aus. Einstellungen Bild anzeigen Schaltet die Anzeige des Cover-Bildes ein und aus. Einstellungen Tags automatisch ausblenden Wenn diese Option aktiv ist, werden leere Tags automatisch ausgeblendet. Manuell lassen sich die Datei, Tag 1 und Tag 2 Sektionen durch Druck auf die entsprechenden - bzw. + Schaltflächen zu- und aufklappen. Einstellungen Kid3 einrichten... Öffnet den Konfigurationsdialog, der aus verschiedenen Seiten besteht, mit Optionen für Tags, Dateien, Benutzerbefehle und Netzwerk. Optionen für Tags sind auf der Seite Tags zu finden, welche ihrerseits in drei Reiter unterteilt ist für Tag 1, Tag 2 und Tag 1 und Tag 2. Wenn Markiere abgeschnittene Felder aktiv ist, werden abgeschnittene ID3v1.1 Felder rot markiert. Die Textfelder in ID3v1.1 Tags haben bloß 30 Zeichen, der Kommentar nur 28 Zeichen. Auch Stil und Nummer sind Beschränkungen unterworfen, so dass Felder gekürzt werden können, wenn sie importiert oder von ID3v2 konvertiert werden. Abgeschnittene Felder und ihre Datei werden rot markiert; die Markierung wird entfernt, nachdem das Feld editiert worden ist. Mit Text Codierung für ID3v1 kann die Codierung, welche für ID3v1 Tags verwendet wird, eingestellt werden. Standardmäßig wird hier ISO-8859-1 erwartet, daher wird empfohlen, diese Einstellung so zu lassen. Da jedoch Tags mit anderer Codierung anzutreffen sind, kann hier eine andere Codierung gewählt werden, so dass dann die ID3v1 Tags nach ID3v2 transferiert werden können, welches Unicode Zeichen unterstützt. Das Markierungsfeld Benutze Format Nummer/Anzahl Stücke bewirkt, dass im Tracknummer-Feld bei den ID3v2 Tags nicht bloß die Tracknummer gespeichert wird, sondern auch die Gesamtzahl der Stücke im Verzeichnis. Wenn Stil als Text statt als Nummer aktiviert ist, werden alle Stile als Text gespeichert, auch wenn ein ID3v1-Code für den Stil existiert. Ohne diese Einstellungen werden solche Stile als die Nummer des entsprechenden ID3v1-Codes gespeichert (in Klammern für ID3v2.3). Der Stil Metal wird also abhängig von dieser Option entweder als "Metal" oder als "(9)" gespeichert. Stile, für welche kein ID3v1-Code existiert, werden immer als Text gespeichert. Der Sinn dieser Option ist es, die Kompatibilität mit Geräten zu verbessern, welche die Codes nicht korrekt interpretieren. Text Codierung bestimmt die Codierung von ID3v2 Elementen und kann auf ISO-8859-1, UTF16 oder UTF8 gesetzt werden. UTF8 ist für ID3v2.3.0 nicht gültig; wenn dieser Wert gesetzt ist, wird stattdessen UTF16 verwendet. Für ID3v2.4.0 sind alle drei Codierungen möglich. Version für neue Tags legt fest, ob neue ID3v2 Tags mit der Version 2.3.0 oder 2.4.0 erzeugt werden. Im ersteren Fall wird id3lib verwendet, TagLib kommt nur dann zum Einsatz, wenn ein ID3v2.4.0 Tag bearbeitet werden muss. Mit der Einstellung ID3v2.4.0 (TagLib) wird für MP3-Dateien immer die TagLib verwendet; veränderte oder neu erstellte Tags werden dann als ID3v2.4.0 gespeichert. Mit ID3v2.3.0 (TagLib) wird TagLib auch für ID3v2.3.0 Tags verwendet. Anzahl der Stellen in Tracknummer bestimmt die Zahl der Dezimalstellen in den Tracknummer-Feldern. Die Nummern werden mit führenden Nullen auf die eingestellte Anzahl Stellen erweitert. Beispielsweise wird mit dem Wert 2 die Tracknummer 5 als "05" gesetzt. Das Auswahlfeld Name des Kommentar-Feldes ist für Ogg/Vorbis und FLAC-Dateien relevant und bestimmt den Namen des Feldes, welches für Kommentare verwendet wird. Hier scheinen verschiedene Anwendungen unterschiedliche Namen zu verwenden, "COMMENT" wird beispielsweise von xmms benutzt, während amaroK "DESCRIPTION" verwendet. Das Format von Bildern in Ogg/Vorbis-Dateien wird bestimmt durch Name des Bild-Feldes und kann METADATA_BLOCK_PICTURE oder COVERART sein. Das erstere ist der offizielle Standard und benutzt dasselbe Format wie Bilder in FLAC-Dateien. Bei COVERART handelt es sich um eine frühere, inoffizielle Methode, um Bilder in Vorbis-Tags zu codieren. Manche Player benutzen dieses Feld. Eigene Stile kann zur Definition von Stilen benutzt werden, welche nicht in der Standard-Liste enthalten sind, so z.B. "Gothic Metal". Solche Stile sind dann im Stil Auswahlfeld von Tag 2 verfügbar. Für ID3v1.1 Tags können nur die vordefinierten Stile verwendet werden. Die Liste mit den eigenen Stilen kann auch dazu verwendet werden, die Anzahl der im Stil Auswahlfeld angebotenen Einträge auf diejenigen zu reduzieren, welche man braucht. Falls die Musiksammlung hauptsächlich aus Metal, Gothic Metal, Ancient und Hard Rock Nummern besteht, so kann man diese Stile eingeben und Nur eigene Stile anzeigen markieren. Das Tag 2 Stil Auswahlfeld enthält dann nur noch diese vier Stile, und man braucht nicht mehr die ganze lange Liste nach ihnen zu durchsuchen. Bei dieser Beispielkonfiguration sind im Tag 1 Stil Auswahlfeld nur noch Metal und Hard Rock verfügbar, denn diese zwei Einträge sind Standard Stile. Wenn Nur eigene Stile anzeigen nicht markiert ist, so werden die eigenen Stile am Ende der Auswahlliste aufgeführt. Schnellzugriff-Tags bestimmt, welche Frametypen immer bei Tag 2 angezeigt werden. Solche Frames können dann hinzugefügt werden, ohne zuerst den Hinzufügen Knopf zu drücken. Tag Format enthält Einstellungen für das Format der Tags. Wenn Während dem Editieren formatieren aktiviert ist, wird die Eingabe während dem Editieren automatisch formatiert. Groß/Kleinschreibung kann auf Keine Änderungen, Alles Kleinbuchstaben, Alles Großbuchstaben, Den ersten Buchstaben groß oder Alle ersten Buchstaben groß gesetzt werden. Um die Eigenheiten einer Sprache bei der Konversion zwischen Klein- und Großbuchstaben zu berücksichtigen, kann eine Sprache in der darunterliegenden Auswahlliste gewählt werden. Die Zeichen ersetzen Liste kann mit beliebigen Zuordnungen von Zeichenketten gefüllt werden. Eine neue Zuordnung kann hinzugefügt werden, indem in der Von Zelle einer Zeile der zu ersetzende Text eingegeben wird und dann in der Nach Spalte der Ersetzungstext. Zum Löschen einer Zuordnung wird die Von Zelle auf einen leeren Wert gesetzt (z.B. indem man zuerst einen Leerschlag eingibt und diesen dann wieder löscht). Einfügen und Löschen von Zeilen ist auch über das Kontext-Menü möglich, welches erscheint, wenn man die rechte Maustaste drückt. Ersetzungen werden nur durchgeführt, wenn das Zeichen ersetzen Markierungsfeld aktiviert ist. Auf der Seite Dateien bestimmt das Markierungsfeld Zuletzt geöffnete Dateien laden, ob &kid3; beim nächsten Start wieder die zuletzt gewählte Datei öffnet und selektiert. Mit Datei-Zeitstempel nicht verändern kann eingestellt werden, dass die Modifikationszeit der Datei nicht verändert wird. Wenn Änderungen markieren aktiviert ist, werden veränderte Felder markiert, indem der Hintergrund des Namens grau gesetzt wird. Dateiname für Cover setzt den Namen, welcher vorgeschlagen wird, wenn ein eingebettetes Bild als Datei exportiert wird. Dateinamen Format enthält Einstellungen für das Format der Dateinamen. Dabei sind die selben Optionen wie bei Tag Format möglich. Die Seite Benutzerbefehle enthält eine Tabelle mit Befehlen, welche über das Kontext Menü der Dateiliste ausgewählt werden können. Bei kritischen Operationen, beispielsweise dem Löschen von Dateien, empfiehlt es sich, Nachfragen zu markieren, damit vor dem Ausführen des Befehls eine Bestätigung verlangt wird. Ausgabe kann markiert werden, um die Ausgabe von Konsolen-Applikationen zu sehen (Standard und Fehler-Ausgabe). Name ist der Text, welcher im Kontext Menü dargestellt wird. Befehl schließlich ist die auszuführende Kommandozeile. Argumente können mit den folgenden Codes übergeben werden: %F %{files} Dateipfad (eine Liste wenn mehrere Dateien selektiert sind) %f %{file} Pfad einer einzelnen Datei %uF %{urls} URLs (eine Liste wenn mehrere Dateien selektiert sind) %uf %{url} URL zu einer einzelnen Datei %d %{directory} Verzeichnis %s %{title} Titel (Song) %a %{artist} Interpret (Artist) %l %{album} Album %c %{comment} Kommentar (Comment) %y %{year} Jahr (Year) %t %{track} Nummer (Track) %t %{track.n} Nummer mit Feldlänge n (z.B. 001 für %{track.3}) %T %{tracknumber} Nummer (Track), ohne führende Null %g %{genre} Stil (Genre) %b %{browser} Kommando zum Starten des Web Browsers Das Kommando, welches mit %{browser} eingefügt werden kann, wird oberhalb der Tabelle im Eingabefeld Web Browser definiert. Damit lassen sich leicht Webseiten mit Informationen zu den Audiodateien aufrufen. Zum Beispiel startet %{browser} http://lyricwiki.org/%u{artist}:%u{title} eine Anfrage in LyricWiki mit Interpret und Titel. Das "u" in %u{artist} und %u{title} bewirkt, dass Interpret %{artist} und Titel %{title} URL-codiert werden. In gleicher Weise lassen sich auch leicht andere Abfragen definieren, z.B. eine Bildsuche bei Google: %{browser} http://images.google.com/images?q=%u{artist}%20%u{album} Um dem Tag 2 Album Cover Bilder hinzuzufügen, kann man mit den oben beschriebenen Benutzerbefehlen über Google Images oder Amazon nach Bildern suchen. Die Bilder können mit "Drag and Drop" hinzugefügt werden. Bilder können auch mit Hinzufügen eines Bild Elementes und Importieren einer Datei oder Einfügen aus der Zwischenablage in ein Tag gebracht werden. Unterstützt sind Bilder für ID3v2, MP4, FLAC, Ogg und ASF Tags. Zum Löschen und Hinzufügen von Einträgen steht in der Tabelle ein Kontext Menü zur Verfügung. Die Netzwerk Seite enthält bloß ein Textfeld für die Adresse des Netzwerk Proxys und optional der Portnummer, abgetrennt durch einen Doppelpunkt. Dieser Proxy wird für den Import von einem Internet Server verwendet, wenn das Kästchen davor markiert ist. Die verfügbaren Plugins können in der Seite Erweiterungen ein- bzw. ausgeschaltet werden. Die Plugins sind in zwei Listen unterteilt. Metadaten Plugins & Priorität enthält Plugins, welche Audio-Datei-Formate unterstützen. Die Reihenfolge der Einträge ist entscheidend, denn sie werden der Reihe nach durchprobiert. Gewisse Formate werden von mehreren Plugins unterstützt, daher werden Dateien durch das erste Plugin, welches sie unterstützt, geöffnet. TaglibMetadata unterstützt die meisten Formate; wenn es zuoberst in der Liste ist, wird es die meisten Dateien öffnen. Um ein anderes Plugin zu verwenden, muss es vor TaglibMetadata in der Liste stehen. Details über die Metadaten Plugins und Gründe, warum man sie statt TagLib verwenden möchte, folgen. Id3libMetadata: Benutzt id3lib für ID3v1.1 und ID3v2.3 Tags in MP3, MP2 und AAC Dateien; unterstützt einige Frame-Typen mehr als TagLib. Ein weiterer Grund für die Verwendung ist, dass TagLib ID3v2.3 erst seit Version 1.8 unterstützt. OggFlacMetadata: Benutzt libogg, libvorbis und libvorbisfile für Ogg Dateien und zusätzlich libFLAC++ und libFLAC für FLAC Dateien. Gründe für die Benutzung sind Bilder in Ogg Dateien - solche werden von TagLib nicht unterstützt - und dass es die offiziellen Bibliotheken dieser Formate sind. TaglibMetadata: Benutzt TagLib, welche viele Datei-Formate unterstützt, seit Version 1.8 auch ID3v2.3, so dass sie für alle Audio-Dateien verwendet werden kann, welche &kid3; unterstützt. Mp4v2Metadata: mp4v2 wurde ursprünglich von &kid3; für M4A-Dateien verwendet. Mittlerweile ist die Unterstützung von TagLib für dieses Format so gut, dass dieses Plugin nur erstellt wird, wenn dies explizit gefordert wird. Verfügbare Erweiterungen listet die verbleibenden Plugins auf. Ihre Reihenfolge ist unwichtig, sie können über die Kästchen links ein- oder ausgeschaltet werden. AmazonImport: Benutzt für die Importieren von Amazon... Funktion. DiscogsImport: Benutzt für die Importieren von Discogs... Funktion. FreedbImport: Benutzt für die Importieren von gnudb.org... und Importieren von TrackType.org... Funktionen. MusicBrainzImport: Benutzt für die Importieren von MusicBrainz-Album... Funktion. AcoustidImport: Benutzt für die Importieren von MusicBrainz-Fingerprint... Funktion, welche von den Bibliotheken Chromaprint und libav abhängig ist. Deaktivierte Erweiterungen werden nicht geladen, damit lässt sich also der Speicherverbrauch und die Ladezeit optimieren. Die Einstellungen auf dieser Seite werden aber erst nach einem Neustart von &kid3; aktiv. Einstellungen Kurzbefehle festlegen... Öffnet einen Dialog, um den meisten Programm-Funktionen ein Tastatur-Kürzel zuzuordnen. Es gibt sogar einige Funktionen ohne entsprechenden Menüpunkt oder Knopf, z.B. nächste Datei, vorangehende Datei, alles auswählen. Das Menü Hilfe Hilfe Handbuch zu Kid3 Öffnet dieses Handbuch. Hilfe Über Kid3 Zeigt eine kurze Information über Kid3 an.
kid3-cli Befehle kid3-cli bietet eine Kommandozeile für &kid3;. Wenn ein Verzeichnispfad als Parameter verwendet wird, so wird das Verzeichnis geöffnet. Bei einem oder mehreren Dateipfaden wird das übergeordnete Verzeichnis geöffnet, und die Dateien werden ausgewählt. Befehle werden dann auf diese Dateien angewendet. Befehle werden mit Optionen angegeben, wenn mehrere vorhanden sind, so werden sie der Reihe nach ausgeführt. Falls Dateien durch die Befehle verändert werden, so werden sie am Ende gespeichert. Wenn keine Befehlsoptionen vorhanden sind, so startet kid3-cli im interaktiven Modus. Dort können Befehle eingegeben werden, welche auf die ausgewählten Dateien angewendet werden. Die folgenden Abschnitte beschreiben alle verfügbaren Befehle. Hilfe help BEFEHLSNAME Zeigt Hilfe über die Parameter von BEFEHLSNAME oder über alle Befehle falls kein Befehlsname angegeben wird. Anwendung beenden exit force Beendet die Anwendung. Falls veränderte Dateien existieren, so muss der Parameter force mitgegeben werden. Verzeichnis wechseln cd VERZEICHNIS Wenn kein VERZEICHNIS angegeben wird, so wird ins Benutzerverzeichnis gewechselt, sonst in das angegebene Verzeichnis. Bei einem oder mehreren Dateipfaden wird das übergeordnete Verzeichnis geöffnet, und die Dateien werden ausgewählt. Aktuelles Verzeichnis ausgeben pwd Gibt den Pfad des aktuellen Verzeichnisses aus. Verzeichnisliste ls Zeigt den Inhalt des aktuellen Verzeichnisses an. Dies entspricht der Dateiliste im &kid3; GUI. Vier Zeichen auf der linken Seite des Dateinamens zeigen Zustandsinformationen an. > Datei ist ausgewählt. * Datei ist verändert. 1 Datei hat ein Tag 1, sonst wird '-' angezeigt. 2 Datei hat ein Tag 2, sonst wird '-' angezeigt. kid3-cli> ls 1- 01 Intro.mp3 > 12 02 We Only Got This One.mp3 *1- 03 Outro.mp3 In diesem Beispiel haben alle Dateien ein Tag 1, die zweite Datei hat auch ein Tag 2 und ist ausgewählt. Die dritte Datei ist verändert. Veränderte Dateien speichern save Datei wählen select all none first previous next DATEI Alle Dateien werden mit select all ausgewählt, mit select none wird die Selektion aufgehoben. Um die Dateien des aktuellen Verzeichnisses zu traversieren, wird mit select first gestartet, vorwärts geht es mit select next und rückwärts mit select previous. Dateien können über ihren Namen zur Auswahl hinzugefügt werden. Wildcards sind möglich, so werden mit select *.mp3 alle MP3-Dateien im aktuellen Verzeichnis ausgewählt. kid3-cli> select first kid3-cli> ls > 1- 01 Intro.mp3 12 02 We Only Got This One.mp3 *1- 03 Outro.mp3 kid3-cli> select next kid3-cli> ls 1- 01 Intro.mp3 > 12 02 We Only Got This One.mp3 *1- 03 Outro.mp3 kid3-cli> select *.mp3 kid3-cli> ls > 1- 01 Intro.mp3 > 12 02 We Only Got This One.mp3 >*1- 03 Outro.mp3 Tag auswählen tag TAGNUMMERN Viele Befehle haben einen optionalen TAGNUMMERN Parameter, welcher festlegt, ob der Befehl auf Tag 1 oder 2 wirkt. Wenn dieser Parameter weggelassen wird, so werden die standardmäßigen Tagnummern verwendet, welche mit dem Befehl tag festgelegt werden können. Voreingestellt ist ein Wert von 12, was bedeutet, dass Informationen vom Tag 2 geholt werden, falls es vorhanden ist, sonst vom Tag 1. Änderungen werden am Tag 2 durchgeführt. Die können auf 1 oder 2 gesetzt werden, damit nur das entsprechende Tag verwendet wird. Wird kein Parameter angegeben, so wird die momentane Einstellung angezeigt. Tag-Element holen get all ELEMENTNAME TAGNUMMERN Dieser Befehl kann dazu benutzt werden, den Wert eines bestimmten Tag-Elements zu lesen oder um Informationen über alle Tag-Elemente zu holen (wenn kein Parameter vorhanden ist oder verwendet wird). Veränderte Elemente werden mit einem '*' markiert. kid3-cli> get Datei: MPEG 1 Layer 3 192 kbps 44100 Hz Joint Stereo Name: 01 Intro.mp3 Tag 1: ID3v1.1 Titel Intro Interpret One Hit Wonder Album Let's Tag Datum 2013 Tracknummer 1 Stil Pop kid3-cli> get title Intro Tag-Element setzen set ELEMENTNAME ELEMENTWERT TAGNUMMERN Dieser Befehl setzt den Wert eines bestimmten Tag-Elements. kid3-cli> set remixer "O.H. Wonder" Um den Inhalt eines Bild-Elements aus einer Datei zu befüllen, kann set picture:"/pfad/zu/folder.jpg" "Bildbeschreibung" verwendet werden. Änderungen rückgängig machen revert Macht alle Änderungen an den ausgewählten Dateien rückgängig (oder an allen Dateien, falls keine selektiert sind). Von Datei oder Zwischenablage importieren import DATEI FORMATNAME TAGNUMMERN Die Tags werden aus DATEI importiert (oder von der Zwischenablage falls clipboard für DATEI angegeben wird) im Format FORMATNAME (z.B. "CSV unquoted", siehe Importieren). Automatisch importieren autoimport PROFILNAME TAGNUMMERN Es wird automatisch importiert unter Verwendung des Profils PROFILNAME (siehe Automatisch importieren, "All" wird benutzt, falls kein Profil angegeben wird). Cover-Bilder herunterladen albumart URL all Setzt das Albumcover mit einem von URL heruntergeladenen Bild. Die Regeln zur Umwandlung einer allgemeinen URL (z.B. von Amazon) zu einer Bild-URL werden verwendet, diese können im Nach Cover-Bildern suchen Dialog definiert werden. Um das Albumcover mit einer lokalen Datei zu setzen kann der set Befehl verwendet werden. kid3-cli> albumart http://www.amazon.com/Versus-World-Amon-Amarth/dp/B000078DOC In Datei oder Zwischenablage exportieren export DATEI FORMATNAME TAGNUMMERN Tags werden in die Datei DATEI exportiert (oder in die Zwischenablage, falls clipboard für DATEI angegeben wird) im Format FORMATNAME (z.B. "CSV unquoted", siehe Exportieren). Stückliste erstellen playlist Erstellt eine Stückliste in dem in der Konfiguration gesetzten Format, siehe Stückliste erstellen. Dateinamenformat anwenden filenameformat Wendet das Dateinamenformat an, welches in der Konfiguration gesetzt ist, siehe Dateinamenformat anwenden. Tag-Format anwenden tagformat Wendet das Tag-Format an, welches in der Konfiguration gesetzt ist, siehe Tag-Format anwenden. Textkodierung anwenden textencoding Wendet die Textkodierung an, welche in der Konfiguration gesetzt ist, siehe Textkodierung anwenden. Verzeichnis umbenennen renamedir FORMAT create rename dryrun TAGNUMMERN Mit diesem Befehl werden Verzeichnisse umbenannt oder neu erstellt anhand der Werte in den Tags nach einem bestimmten FORMAT (z.B. %{artist} - %{album}, siehe Verzeichnis umbenennen), wenn kein Format angegeben wird, so wird dasjenige verwendet, welches im Verzeichnis umbenennen Dialog gesetzt ist. Normalerweise wird der Modus verwendet; um neue Verzeichnisse zu erstellen, muss explizit angegeben werden. Die zum Umbenennen nötigen Aktionen werden sofort ausgeführt, um bloß eine Vorschau über diese Aktionen zu sehen, kann die Option verwendet werden. Tracks nummerieren numbertracks TRACKNUMMER TAGNUMMERN Nummeriert die ausgewählten Dateien beginnend mit TRACKNUMMER (1 wenn nichts angegeben wird). Filter filter FILTERNAME FILTERFORMAT Filtert die Dateien, so dass nur Dateien angezeigt werden, welche dem FILTERFORMAT entsprechen. Der Name eines vordefinierten Filterausdrucks (z.B. "Filename Tag Mismatch") kann anstelle eines Filterausdrucks verwendet werden, siehe Filter. kid3-cli> filter '%{title} contains "tro"' Begonnen /home/urs/One Hit Wonder - Let's Tag + 01 Intro.mp3 - 02 We Only Got This One.mp3 + 03 Outro.mp3 Fertig kid3-cli> ls 1- 01 Intro.mp3 1- 03 Outro.mp3 kid3-cli> filter All Begonnen /home/urs/One Hit Wonder - Let's Tag + 01 Intro.mp3 + 02 We Only Got This One.mp3 + 03 Outro.mp3 Fertig kid3-cli> ls 1- 01 Intro.mp3 12 02 We Only Got This One.mp3 1- 03 Outro.mp3 ID3v2.3 nach ID3v2.4 konvertieren to24 ID3v2.4 nach ID3v2.3 konvertieren to23 Dateiname von Tag fromtag FORMAT TAGNUMMERN Setzt die Dateinamen der ausgewählten Dateien anhand der Werte in den Tags, z.B. fromtag "%{track} - %{title}" 1. Wenn kein Format angegeben wird, wird dasjenige verwendet, welches im GUI gesetzt ist. Tag von Dateiname totag FORMAT TAGNUMMERN Setzt die Tag-Elemente anhand der Dateinamen, z.B. totag "%{albumartist} - %{album}/%{track} %{title}" 2. Wenn kein Format angegeben wird, wird dasjenige verwendet, welches im GUI gesetzt ist. Falls der Dateiname nicht diesem Format entspricht, werden noch einige andere, gängige Formate ausprobiert. Tag zu anderem Tag syncto TAGNUMMER Kopiert die Tag-Elemente von einem Tag zum anderen Tag, um beispielsweise das ID3v2 Tag anhand der Werte im ID3v1 Tag zu setzen, wird syncto 2 verwendet. Kopieren copy TAGNUMMER Kopiert die Tag-Elemente der selektierten Datei in die interne Ablage. Sie können dann in einer anderen Datei mit dem paste Befehl gesetzt werden. Einfügen paste TAGNUMMER Setzt die Tag-Elemente gemäß dem Inhalt der copy Ablage in den ausgewählten Dateien. Entfernen remove TAGNUMMER Entfernt ein Tag. Wiedergabe play pause stop previous next Startet die Wiedergabe, danach kann sie mit den Optionen (um zu pausieren und weiterzufahren), , und kontrolliert werden. Beispiele Setze das Albumcover in allen Dateien eines Verzeichnisses unter Verwendung der automatischen Import-Funktion: kid3-cli -c 'autoimport "Cover Art"' /pfad/zum/verzeichnis Entferne Kommentar und wende Tag-Format an für beide Tags in allen MP3-Dateien eines Verzeichnisses: kid3-cli -c 'set comment "" 1' -c 'set comment "" 2' \ -c 'tagformat 1' -c 'tagformat 2' /pfad/zum/verzeichnis/*.mp3 Automatischer Import in Tag 2, auch ins Tag 1 kopieren, die Dateinamen aus Tag 2 setzen und schließlich eine Stückliste erstellen: kid3-cli -c autoimport -c 'syncto 1' -c fromtag -c playlist \ /pfad/zum/verzeichnis/*.mp3 Für alle Dateien mit einem ID3v2.4.0 Tag, dieses nach ID3v2.3.0 konvertieren und das Arrangierung Element entfernen: kid3-cli -c "filter 'ID3v2.4.0 Tag'" -c "select all" -c to23 \ -c "set arranger ''" /pfad/zum/verzeichnis Danksagungen und Lizenz &kid3; Programmiert von Urs Fleisch ufleisch@users.sourceforge.net &underFDL; &underGPL; Installation Bezugsquellen &kid3; ist zu finden unter http://kid3.sourceforge.net. Anforderungen &kid3; braucht Qt. KDE ist empfohlen aber nicht notwendig, denn &kid3; kann auch als reine Qt Applikation kompiliert werden. &kid3; läuft auf Systemen, wo diese Programmbibliotheken vorhanden sind, z.B. auf GNU/Linux, Windows und Mac OS X. Um Ogg/Vorbis Dateien zu taggen werden libogg, libvorbis und libvorbisfile benötigt, für FLAC Dateien libFLAC++ und libFLAC. Für MP3 Dateien wird die id3lib verwendet. Für diese vier Formate kann auch die TagLib verwendet werden, welche zudem auch MPC, APE, MP2, Speex, TrueAudio, WavPack, WMA, WAV, AIFF Dateien und Tracker Module unterstützt. Für das Importieren über akustische Fingerabdrücke werden Chromaprint und libav verwendet. &kid3; ist für die meisten Linux-Distributionen, Windows und Mac OS X erhältlich. Links finden sich auf http://kid3.sourceforge.net. Kompilieren und Installation Man kann &kid3; mit oder ohne KDE kompilieren. Ohne KDE ist &kid3; eine reine Qt Applikation und es fehlen einige Konfigurations- und Session-Funktionen. Für die KDE 4 Version wird im obersten Verzeichnis getippt: % cmake . % make % make install Falls nicht alle Programmbibliotheken vorhanden sind, so wird &kid3; mit reduzierter Funktionalität kompiliert. Es sollte also darauf geachtet werden, dass die Entwicklungspakete aller gewünschten Bibliotheken auch installiert sind. Andererseits kann mit cmake-Optionen bestimmt werden, was verwendet werden soll. Die Voreinstellung ist -D WITH_TAGLIB:BOOL=ON -D WITH_MP4V2:BOOL=OFF -D WITH_ID3LIB:BOOL=ON -D WITH_CHROMAPRINT:BOOL=ON -D WITH_VORBIS:BOOL=ON -D WITH_FLAC:BOOL=ON . Entsprechend kann auch OFF gesetzt werden. Um &kid3; als Qt Applikation ohne KDE zu kompilieren, wird die cmake Option -DWITH_APPS=Qt benutzt. Um sowohl eine KDE als auch eine Qt Applikation zu kompilieren, wird -DWITH_APPS="Qt;KDE" gesetzt. Qt 4 oder Qt 5 können verwendet werden. Um Qt 4 zu erzwingen, kann -DWITH_QT4=ON gesetzt werden, entsprechend -DWITH_QT5=ON für Qt 5, wenn beide Optionen ausgeschaltet sind (Standardeinstellung), so wird Qt 4 bevorzugt, wenn beide Versionen installiert sind. Um eine bestimmte Qt-Installation zu nutzen, kann -DQT_QMAKE_EXECUTABLE=/Pfad/zu/qmake gesetzt werden. Für die Erzeugung von RPM-Paketen ist die Datei kid3.spec vorhanden, für Debian-Pakete steht das Skript build-deb.sh zur Verfügung. Die Qt Anwendung kann auch unter Windows und Mac OS X kompiliert werden. Die Verzeichnisse win32 und macosx beinhalten Skripte zum Kompilieren und Erzeugen von Paketen. Das Skript buildlibs.sh erstellt die notwendigen Programmbibliotheken. Konfiguration Mit KDE werden der Dateinamenfilter, das Dateinamenformat, die Importformate, die Dateinamen- und ID3-Formate, die Einstellungen für die Werkzeug- und Statusleiste sowie die Fenstergröße im Standardverzeichnis in der Datei kid3rc gespeichert. Als Qt-Anwendung liegt diese Datei in .config/Kid3/Kid3.conf. Unter Windows wird die Konfiguration in der Registry gespeichert. D-Bus Schnittstelle D-Bus Beispiele Die Qt 4 und KDE 4 Versionen unter Linux bieten eine D-Bus Schnittstelle, mit welcher Kid3 über Skripte gesteuert werden kann. Skripte können in jeder Sprache, für die D-Bus-Anbindungen existieren, geschrieben werden (z.B. in Python) und können über die Benutzerbefehle die Funktionalität von &kid3; erweitern. Mit folgenden Befehlen kann der Interpret im Tag 2 auf den Wert "One Hit Wonder" gesetzt werden: Shell dbus-send --dest=net.sourceforge.kid3 --print-reply=literal \ /Kid3 net.sourceforge.Kid3.setFrame int32:2 string:'Artist' \ string:'One Hit Wonder' oder einfacher mit qdbus von Qt (qdbusviewer kann benutzt werden, um die D-Bus Schnittstelle über eine grafische Benutzeroberfläche zu erkunden): qdbus net.sourceforge.kid3 /Kid3 setFrame 2 Artist \ 'One Hit Wonder' Python import dbus kid3 = dbus.SessionBus().get_object( 'net.sourceforge.kid3', '/Kid3') kid3.setFrame(2, 'Artist', 'One Hit Wonder') Perl use Net::DBus; $kid3 = Net::DBus->session->get_service( "net.sourceforge.kid3")->get_object( "/Kid3", "net.sourceforge.Kid3"); $kid3->setFrame(2, "Artist", "One Hit Wonder"); D-Bus API Das D-Bus API ist in net.sourceforge.Kid3.xml spezifiziert. Die Kid3 Schnittstelle hat die folgenden Methoden: Öffne Datei oder Verzeichnis boolean openDirectory string path path Pfad zu Datei oder Verzeichnis Wenn OK wird true zurückgegeben. Speichere alle veränderten Dateien boolean save Wenn OK wird true zurückgegeben. Detaillierte Fehlermeldung für einige Methoden string getErrorMessage Gibt eine detaillierte Fehlermeldung zurück. Änderungen an Dateien rückgängig machen revert Automatischen Import starten boolean batchImport int32 tagMask string profileName tagMask Tag Maske (Bit 0 für Tag 1, Bit 1 für Tag 2) profileName Name des zu verwendenden Profils Tags von Datei importieren boolean importFromFile int32 tagMask string path int32 fmtIdx tagMask Tag Bit (1 für Tag 1, 2 für Tag 2) path Pfad zu Datei fmtIdx Format Index Wenn OK wird true zurückgegeben. Cover-Bild herunterladen downloadAlbumArt string url boolean allFilesInDir url URL des Bildes oder einer dazugehörigen Website allFilesInDir falls true, wird das Bild in allen Dateien des Verzeichnisses hinzugefügt Tags in Datei exportieren boolean exportToFile int32 tagMask string path int32 fmtIdx tagMask Tag Bit (1 für Tag 1, 2 für Tag 2) path Pfad zu Datei fmtIdx Format Index Wenn OK wird true zurückgegeben. Stückliste erstellen boolean createPlaylist Wenn OK wird true zurückgegeben. Anwendung beenden quit Alles auswählen selectAll Gesamte Auswahl aufheben deselectAll Gehe zur ersten Datei boolean firstFile Gibt true zurück, wenn eine Datei existiert. Gehe zur vorangehende Datei boolean previousFile Gibt true zurück, wenn eine vorangehende Datei existiert. Gehe zur nächstes Datei boolean nextFile Gibt true zurück, wenn eine nächste Datei existiert. Erste Datei auswählen boolean selectFirstFile Gibt true zurück, wenn eine Datei existiert. Vorangehende Datei auswählen boolean selectPreviousFile Gibt true zurück, wenn eine vorangehende Datei existiert. Nächste Datei auswählen boolean selectNextFile Gibt true zurück, wenn eine nächste Datei existiert. Aktuelle Datei auswählen boolean selectCurrentFile Gibt true zurück, wenn eine aktuelle Datei existiert. Aktuelles Verzeichnis auf- oder zuklappen boolean expandDirectory Ein Dateilistenelement ist ein Verzeichnis, wenn getFileName() einen Namen mit '/' als letztem Zeichen zurückgibt. Gibt true zurück, wenn das aktuelle Dateilistenelement ein Verzeichnis ist. Dateinamenformat anwenden applyFilenameFormat Tag Format anwenden applyTagFormat Textkodierung anwenden applyTextEncoding Verzeichnisname von Tags setzen boolean setDirNameFromTag int32 tagMask string format boolean create tagMask Tag Maske (Bit 0 für Tag 1, Bit 1 für Tag 2) format Verzeichnisformat create true zum Erstellen, false zum Umbenennen Wenn OK wird true zurückgegeben, sonst ist eine Fehlermeldung mit getErrorMessage() abrufbar. Nummeriere Tracks in ausgewählten Dateien numberTracks int32 tagMask int32 firstTrackNr tagMask Tag Maske (Bit 0 für Tag 1, Bit 1 für Tag 2) firstTrackNr Nummer für erste Datei Dateien filtern filter string expression expression Filter Ausdruck ID3v2.3 nach ID3v2.4 konvertieren convertToId3v24 ID3v2.4 nach ID3v2.3 konvertieren convertToId3v23 Wenn OK wird true zurückgegeben. Verzeichnispfad string getDirectoryName Gibt den absoluten Verzeichnispfad zurück. Dateiname string getFileName Gibt den absoluten Dateinamen zurück, wenn es ein Verzeichnis ist, endet er mit "/". Setze Dateinamen setFileName string name name Dateiname Die Datei wird umbenannt, wenn das Verzeichnis gespeichert wird. Setze Format für Dateiname aus Tags setFileNameFormat string format format Dateinamenformat Generiere den Dateinamen aus den Tags setFileNameFromTag int32 tagMask tagMask Tag Bit (1 für Tag 1, 2 für Tag 2) Wert eines Elementes string getFrame int32 tagMask string name tagMask Tag Bit (1 für Tag 1, 2 für Tag 2) name Elementname (z.B. "artist") Um binäre Daten wie ein Bild von einem Element in einer Datei zu speichern, kann der Dateiname dem Elementnamen hinzugefügt werden, z.B. "Picture:/Pfad/zu/Datei". Gibt Wert des Elementes zurück. Setze Wert eines Elementes boolean setFrame int32 tagMask string name string value tagMask Tag Bit (1 für Tag 1, 2 für Tag 2) name Elementname (z.B. "artist") value Wert des Elementes Für Tag 2 (tagMask 2) gilt: Wenn kein Element mit name existiert, so wird ein neues Element hinzugefügt, wenn value leer ist, so wird das Element gelöscht. Um binäre Daten wie ein Bild einem Element hinzuzufügen, kann der Dateiname dem Elementnamen hinzugefügt werden, z.B. "Picture:/Pfad/zu/Datei". Wenn OK wird true zurückgegeben. Alle Elemente eines Tags array of string getTag int32 tagMask tagMask Tag Bit (1 für Tag 1, 2 für Tag 2) Gibt eine Liste mit abwechselnd Elementnamen und -werten zurück. Technische Informationen über Datei array of string getInformation Eigenschaften sind Format, Bitrate, Samplerate, Channels, Duration, Channel Mode, VBR, Tag 1, Tag 2. Eigenschaften, welche nicht vorhanden sind, werden ausgelassen. Gibt eine Liste mit abwechselnd Eigenschaftsnamen und -werten zurück. Setze Tag aus Dateinamen setTagFromFileName int32 tagMask tagMask Tag Bit (1 für Tag 1, 2 für Tag 2) Setze Tag aus anderem Tag setTagFromOtherTag int32 tagMask tagMask Tag Bit (1 für Tag 1, 2 für Tag 2) Kopiere Tag copyTag int32 tagMask tagMask Tag Bit (1 für Tag 1, 2 für Tag 2) Füge Tag ein pasteTag int32 tagMask tagMask Tag Bit (1 für Tag 1, 2 für Tag 2) Entferne Tag removeTag int32 tagMask tagMask Tag Bit (1 für Tag 1, 2 für Tag 2) Lese die Konfiguration neu ein reparseConfiguration Automatisierte Konfigurationsänderungen sind durch direktes Modifizieren der Konfigurationsdatei und anschließendes Einlesen möglich. Spielt die ausgewählten Dateien ab playAudio &documentation.index;
kid3-3.0.2/doc/en/000077500000000000000000000000001224603511300135125ustar00rootroot00000000000000kid3-3.0.2/doc/en/CMakeLists.txt000066400000000000000000000004571224603511300162600ustar00rootroot00000000000000if (BUILD_KDE_APP) find_package(KDE4 REQUIRED) kde4_create_handbook(index.docbook INSTALL_DESTINATION ${HTML_INSTALL_DIR}/en SUBDIR kid3) endif (BUILD_KDE_APP) if (BUILD_QT_APP) kid3_create_handbook(index.docbook) endif (BUILD_QT_APP) if (UNIX) kid3_create_manpage(index.docbook) endif (UNIX) kid3-3.0.2/doc/en/index.docbook000066400000000000000000004106731224603511300161760ustar00rootroot00000000000000 Kid3'> ]> The Kid3 Handbook Urs Fleisch
ufleisch@users.sourceforge.net
Software development
2013 Urs Fleisch &FDLNotice; 2013-11-27 3.0.2 &kid3; is an application to edit the ID3v1 and ID3v2 tags in MP3 files in an efficient way. Also tags in Ogg/Vorbis, FLAC, MPC, APE, MP4/AAC, MP2, Speex, TrueAudio, WavPack, WMA, WAV, AIFF files and tracker modules (MOD, S3M, IT, XM) are supported. It is easy to set tags of multiple files to the same values (e.g. album, artist, year and genre in all files of the same album) and generate the tags from the file name or vice versa. KDE kdemultimedia MP3 ID3 ID3v1 ID3v2 Ogg Vorbis FLAC MPC APE Musepack MP4 M4A MP2 Speex TrueAudio WavPack WMA WAV AIFF MOD S3M IT XM
Synopsis kid3 FILE kid3-qt FILE kid3-cli FILE Options FILE If FILE is the path to a directory, it will be opened. If one or more file paths are given, their common directory is opened and the files are selected. kid3 Show help about options. Show Qt specific options, see qtoptions7 . Show KDE specific options, see kdeoptions7 . Show all options. Show author information. Show version information. Show license information. kid3-qt See qtoptions 7, however, the options have "-" as a prefix, not "&doublehyphen;". kid3-cli Execute a command. Multiple options are possible, they are executed in sequence. See the section about kid3-cli for a description of the available commands. | Show help about options and commands. Introduction &kid3; is an application to edit the ID3v1 and ID3v2 tags in MP3 files in an efficient way. These tags can be edited by most MP3 players, but not in a very comfortable and efficient way. Moreover the tags in Ogg/Vorbis, FLAC, MPC, APE, MP4/AAC, MP2, Speex, TrueAudio, WavPack, WMA, WAV, AIFF files and tracker modules (MOD, S3M, IT, XM) are supported too. &kid3; does not grab nor encode MP3 files, but it is targeted to edit the ID3 tags of all files of an album in an efficient way, i.e. with as few mouse clicks and key strokes as possible. Where most other programs can edit either ID3v1 or ID3v2 tags, &kid3; has full control over both versions, can convert tags between the two formats and has access to all ID3v2 tags. Tags of multiple files can be set to the same value, e.g. the artist, album, year and genre of all files of an album typically have the same values and can be set together. If the information for the tags is contained in the file name, the tags can be automatically set from the file name. It is also possible to set the file name according to the tags found in the file in arbitrary formats. The editing task is further supported by automatic replacement of characters or substrings, for instance to remove illegal characters from filenames. Automatic control of upper and lower case characters makes it easy to use a consistent naming scheme in all tags. The tag information for full albums can be taken from gnudb.org, TrackType.org, MusicBrainz, Discogs, Amazon or other sources of track lists. The import format is freely configurable by regular expressions. Please report any problems or feature requests to the author. Using Kid3 Kid3 features Edit ID3v1.1 tags Edit all ID3v2.3 and ID3v2.4 frames Edit tags of multiple files Convert between ID3v1 and ID3v2 tags Edit MP3, Ogg/Vorbis, FLAC, MPC, APE, MP4/AAC, MP2, Speex, TrueAudio, WavPack, WMA, WAV and AIFF tags Generate tags from filename Generate tags from the contents of tag fields Generate filename from tags Generate and change directory names from tags Generate playlist file Automatic case conversion and string translation Import from gnudb.org, TrackType.org, MusicBrainz, Discogs, Amazon and other data sources Export as CSV, HTML, playlist, Kover XML and other formats. Exported CSV files can be imported again. Example Usage This section describes a typical session with &kid3;. Let's assume we have a directory containing MP3 files with the tracks from the album "Let's Tag" from the band "One Hit Wonder". The directory is named in the "artist - album" format, in our case One Hit Wonder - Let's Tag. The directory contains the tracks in the "track title.mp3" format, which I think is useful because the filenames are short (important when using mobile MP3 players with small displays) and in the correct order when sorted alphabetically (important when using hardware MP3 players which play the tracks in alphabetical order or in the order in which they are burnt on CD and that order is alphabetical when using mkisofs). Besides this, the artist and album information is already in the directory name and does not have to be repeated in the filename. But back to our example, the directory listing looks like this: 01 Intro.mp3 02 We Only Got This One.mp3 03 Outro.mp3 These files have no tags yet and we want to generate them using &kid3;. We use Open (File menu or toolbar) and select one of the files in this directory. All files will be displayed in the file listbox. Lazy as we are, we want to use the information in the directory and file names to generate tags. Therefore we select all files, then click the To: Tag 1 button in the File section. This will set the title, artist, album and track values in all files. To set the year and genre values of all files, we keep all files selected and type in "2002" for the Year and select "Pop" from the Genre combobox. To set only these two values, their checkboxes are automatically checked and all other checkboxes are left unchecked. Now we change the selection by only selecting the first file and we see that all tags contain the correct values. The tags of the other files can be verified too by selecting them one by one. When we are satisfied with the tags, we use Save (File menu or toolbar). Selecting Create Playlist from the File menu will generate a file One Hit Wonder - Let's Tag.m3u in the directory. Command Reference The GUI Elements The &kid3; GUI is separated in five sections: At the left are the file and directory listboxes, the right side contains the File, Tag 1 and Tag 2 sections. File List The file list contains the names of all the files in the opened directory which match the selected file name filter (typically *.mp3 *.ogg *.flac *.mpc *.aac *.m4a *.m4b *.m4p *.mp4 *.mp2 *.spx *.tta *.wv *.wma *.wav *.aiff *.ape). A single or multiple files can be selected. To select no file, click into the empty area after the listbox entries. The selection determines the files which are affected by the operations which are available by using the buttons described below. At the left of the names an icon can be displayed: a disc to show that the file has been modified or information about which tags are present (V1, V2, V1V2 or NO TAG, no icon is displayed if the file has not been read in yet). Directories are displayed with a folder icon. If a directory is opened, its files are displayed in a hierarchical tree. By selecting files from subdirectories, operations can be executed on files in different directories, which is useful if the music collection is organized with a folder for each artist containing folders for albums of this artist. Clicking the right mouse button inside the file list opens a context menu with the following commands: Expand all: Expands all folder trees Collapse all: Collapses all folder trees Rename: Changes the name of a file Move to Trash: Moves a file to the trash Play: Plays a file, see Play The subsequent entries are user commands, which can be defined in the User Actions tab of Configure Kid3. The playback on double click can also be activated there. Directory List The directory list contains the names of the directories in the opened directory, as well as the current (.) and the parent (..) directory. It allows to quickly change the directory without using the Open... command or drag and drop. File Shows information about the encoding (MP3, Ogg, FLAC, MPC, APE, MP2, MP4, AAC, Speex, TrueAudio, WavPack, WMA, WAV, AIFF), bit rate, sample rate, channels and the length of the file. The Name line edit contains the name of the file (if only a single file is selected). If this name is changed, the file will be renamed when the Save command is used. The Format combo box and line edit contains the format to be used when the filename is generated from the first or the second tag. The filename can contain arbitrary characters, even a directory part separated by a slash from the file name, but that directory must already exist for the renaming to succeed. The following special codes are used to insert tag values into the filename: %s %{title} Title (Song) %a %{artist} Artist %l %{album} Album %c %{comment} Comment %y %{year} Year %t %{track} Track (e.g. 01) %t %{track.n} Track with field width n (e.g. 001 for %{track.3}) %T %{tracknumber} Track (without leading zeros, e.g. 1) %g %{genre} Genre %{ignore} Ignored when generating tags from the file name The format codes are not restricted to the examples given above. Any frame name can be used, for instance unified frame names like %{bpm} or format specific names like %{popm}. A second Format combo box (with arrow down) is used to generate the tags from the filename. If the format of the filename does not match this pattern, a few other commonly used formats are tried. Some commonly used filename formats are already available in the combo box, but it is also possible to type in some special format into the line edit. From: Tag 1, Tag 2: Sets the filename using the selected format and the first tag or the second tag, respectively. To: Tag 1, Tag 2: The tags are set from the filename. First, the format specified in Format is used. If the existing filename does not match this format, the following formats are tried: Artist - Album/Track Song Album/Track - Artist - Song /Artist - Album - Track - Song Album/Artist - Track - Song Album/Artist - Song Artist/Album/Track Song If a single file is selected, the GUI controls are filled with the values extracted from the filename. If multiple files are selected, the tags of the files are directly set according to the filenames. Tag 1 The line edit widgets for Title, Artist, Album, Comment, Year, Track and Genre are used to edit the corresponding value in the first tag of the selected files. The value will be changed when the file selection is altered or before operations like Save and Quit and when the corresponding check box at the left of the field name is checked. This is useful to change only some values and leave the other values unchanged. If a single file is selected, all check boxes are checked and the line edit widgets contain the values found in the tags of this file. If a tag is not found in the file, the corresponding empty value is displayed, which is an empty string for the Title, Artist, Album and Comment line edits, 0 for the numerical Year and Track edits and an empty selected value for the Genre combo box. The values can be changed and if the corresponding check box is checked, they will be set for the selected file after the selection is changed. The file is then marked as modified by a disk symbol in the file listbox but remains unchanged until the Save command is used. If multiple files are selected, only the values which are identical in all selected files are displayed. In all other controls, the empty values as described above are displayed. All check boxes are unchecked to avoid unwanted changes. If a value has to be set for all selected files, it can be edited and the checkbox has to be set. The values will be set for all selected files when the selection is changed and can be saved using the Save command. The check boxes also control the operation of most commands affecting the tags, such as copy, paste and transfer between tags 1 and 2. To make it easier to use with multiple files where all check boxes are unchecked, these commands behave in the same way when all check boxes are checked and when all check boxes are unchecked. From Tag 2: The tag 1 fields are set from the corresponding values in tag 2. If a single file is selected, the GUI controls are filled with the values from tag 2. If multiple files are selected, the tags of the files are directly set. Copy: The copy buffer is filled with the Tag 1 values. Only values with checked checkbox will be used in subsequent Paste commands. Paste: Pastes the values from the copy buffer into the GUI controls. Remove: This will set all GUI controls to their empty values which results in removing all values. The saved file will then contain no tag 1. Tag 2 The GUI controls function in the same way as described for the Tag 1 section, but the size of the strings is not limited. For the tag 2 Genre you can also use your own names besides the genres listed in the combo box, just type the name into the line edit. The tag 2 can not only contain the same values as the tag 1, the format is built in a flexible way from several frames which are themselves composed of several fields. The tag 2 table shows all the frames which are available in the selected file. Edit: This will open a window which allows to edit all fields of the selected frame. If multiple files are selected, the edited fields are applied to all selected files which contain such a frame. Add: A requester to select the frame type will appear and a frame of the selected type can be edited and added to the file. This works also to add a frame to multiple selected files. Delete: Deletes the selected frame in the selected files. Drag album artwork here is shown if the file does not contain embedded cover art. A picture can be added using drag and drop from a browser or file manager and will be displayed here. Picture frames can be edited or added by double clicking on this control. Frame List &kid3; can edit most of the frames for all of the supported file types. Some frames are accessed using unified names, so that they can be exchanged between files with different formats. Frames which are not unified can be accessed as format specific frames. Mapping of Unified Frame Types to Various Formats Unified ID3v2.3 ID3v2.4 MP4 ASF VorbisTitle TIT2 TIT2 ©nam Title TITLEArtist TPE1 TPE1 ©ART Author ARTISTAlbum TALB TALB ©alb WM/AlbumTitle ALBUMComment COMM COMM ©cmt Description COMMENTDate TYER TDRC ©day WM/Year DATETrack Number TRCK TRCK trkn WM/TrackNumber TRACKNUMBERGenre TCON TCON ©gen WM/Genre GENREAlbum Artist TPE2 TPE2 aART WM/AlbumArtist ALBUMARTISTArranger IPLS TIPL ARRANGER WM/Producer ARRANGERAuthor TOLY TOLY AUTHOR AUTHORBPM TBPM TBPM tmpo WM/BeatsPerMinute BPMCatalog Number TXXX:CATALOGNUMBER TXXX:CATALOGNUMBER CATALOGNUMBERCompilation TCMP TCMP cpil COMPILATIONComposer TCOM TCOM ©wrt WM/Composer COMPOSERConductor TPE3 TPE3 CONDUCTOR WM/Conductor CONDUCTORCopyright TCOP TCOP cprt Copyright COPYRIGHTDisc Number TPOS TPOS disk WM/PartOfSet DISCNUMBEREncoded-by TENC TENC ©enc WM/EncodedBy ENCODED-BYEncoder Settings TSSE TSSE ©too WM/EncodingSettings ENCODERSETTINGSEncoding Time TDEN WM/EncodingTime ENCODINGTIMEGrouping TIT1 TIT1 ©grp WM/ContentGroupDescription GROUPINGInitial Key TKEY TKEY WM/InitialKey INITIALKEYISRC TSRC TSRC ISRC WM/ISRC ISRCLanguage TLAN TLAN LANGUAGE WM/Language LANGUAGELyricist TEXT TEXT LYRICIST WM/Writer LYRICISTLyrics USLT USLT ©lyr WM/Lyrics LYRICSMedia TMED TMED SOURCEMEDIA SOURCEMEDIAMood TMOO WM/Mood MOODOriginal Album TOAL TOAL ORIGINALALBUM WM/OriginalAlbumTitle ORIGINALALBUMOriginal Artist TOPE TOPE ORIGINALARTIST WM/OriginalArtist ORIGINALARTISTOriginal Date TORY TDOR ORIGINALDATE WM/OriginalReleaseYear ORIGINALDATEPart TSST PART WM/SubTitleDescription PARTPerformer IPLS TMCL PERFORMER PERFORMERPicture APIC APIC covr WM/Picture METADATA_BLOCK_PICTUREPublisher TPUB TPUB PUBLISHER WM/Publisher PUBLISHERRelease Country TXXX:RELEASECOUNTRY TXXX:RELEASECOUNTRY RELEASECOUNTRYRemixer TPE4 TPE4 REMIXER WM/ModifiedBy REMIXERSort Album TSOA TSOA soal WM/AlbumSortOrder ALBUMSORTSort Album Artist TSO2 TSO2 soaa ALBUMARTISTSORTSort Artist TSOP TSOP soar WM/ArtistSortOrder ARTISTSORTSort Composer TSOC TSOC soco COMPOSERSORTSort Name TSOT TSOT sonm WM/TitleSortOrder TITLESORTSubtitle TIT3 TIT3 SUBTITLE WM/SubTitle SUBTITLEWebsite WOAR WOAR WEBSITE WM/AuthorURL WEBSITEWWW Audio File WOAF WOAF WM/AudioFileURL WWWAUDIOFILEWWW Audio Source WOAS WOAS WM/AudioSourceURL WWWAUDIOSOURCE
Remarks concerning the mappings to unified frame names: The number of unified frame names is limited by the fact that a sensible mapping shall be possible for all supported file formats. Most tags support frames with arbitrary names; these will be used if no specific frame is available (e.g. the names in uppercase in the column MP4). If no such possibility exists, some frame types may not be supported for the format, e.g. Author and Performer for ASF (WMA). The mappings are not chosen arbitrarily, they are geared to the usage of the frames in other applications and devices. Thus the ID3v2 frame "TPE2 - Band/orchestra/accompaniment" does not suggest its usage as Album Artist, but this is commonly used. The actual meaning for ID3v2 on the other hand is the reason why this frame is used for the orchestra when importing (e.g. from Discogs), although this may seem a bit strange for other tag formats. The mappings are not always bijective. So ID3v2.3 uses an IPLS frame for both Arranger and Performer. When reading back, both frames are displayed as "Arranger". The frames Arranger and Performer use a particular format for their contents: "involvement 1|involvee 1|involvement 2|involvee 2|...", for instance "Chorus Master|Ernst Dunshirn|Soprano Vocals|Anna Netrebko". This will create IPLS (ID3v2.3) or TIPL/TMCL (ID3v2.4) frames with a string list in the specified format (the "|" is used as a separator between the strings). Values in this format are also set when importing data from servers which offer this information.
The File Menu CtrlO File Open... Opens a directory. All files matching the selected file name filter will be displayed in the file listbox and the chosen file is selected. File Open Recent Opens a recently opened directory. CtrlD File Open Directory... Opens a directory. All files matching the selected file name filter will be displayed in the file listbox. CtrlS File Save Saves all changed files in the directory. The changed files are marked with a disk symbol in the file listbox. If any file names have been changed, those files will be renamed. File Revert Reverts the changes of one or multiple files. If no files are selected in the file listbox, the changes of all files will be reverted, else only the changes of the selected files are reverted. File Import... The Import dialog can be used to import data directly from a freedb.org server, from a MusicBrainz server, from Discogs, Amazon or other sources of album track lists in textual format. Import from a freedb.org server is possible using a dialog which appears when From Server: gnudb.org or TrackType.org is selected. The artist and album name to search for can be entered in the two topmost fields, the albums which match the query will be displayed when Find is clicked and the results from www.gnudb.org are received. Importing the track data for an album is done by double-clicking the album in the list. The freedb.org server to import from can be selected as well as the CGI path. The imported data is displayed in the preview table of the import dialog. When satisfied with the displayed tracks, they can be imported by terminating the import dialog with OK. A search on the Discogs server can be performed using Discogs. As in the gnudb.org dialog, you can enter artist and album and then choose from a list of releases. If Standard Tags is marked, the standard information is imported, e.g. artist, album, and title. If Additional Tags is marked, more information is imported if available, e.g. performers, arrangers, or the publisher. If Cover Art is marked, cover art will be downloaded if available. A search on Amazon can be performed using Amazon. As in the gnudb.org dialog, you can enter artist and album and then choose from a list of releases. If Additional Tags is marked, more information is imported if available, e.g. performers, arrangers, or the publisher. If Cover Art is marked, cover art will be downloaded if available. You can search in the same way in the release database of MusicBrainz using From MusicBrainz Release. The workflow is the same as described for From gnudb.org. Import from a MusicBrainz server is possible using the dialog which appears when From MusicBrainz Fingerprint is selected. The Server can be selected as in the freedb import dialog. Below is a table displaying the imported track data. The right column shows the state of the MusicBrainz query, which starts with "Pending" when the dialog is opened. Then the fingerprint is looked up and if it does not yield a result, another lookup using the tags in the file is tried. Thus it can be helpful for a successful MusicBrainz query to store known information (e.g. artist and album) in the tags before the import. If a result was found, the search ends in the state "Recognized", otherwise nothing was found or multiple ambiguous results and one of them has to be selected by the user. OK and Apply use the imported data, Cancel closes the dialog. The closing can take a while since the whole MusicBrainz machinery has to be shut down. For the import of textual data, From File/Clipboard opens a subdialog, where several preconfigured import formats are available. The first two, "CSV unquoted" and "CSV quoted" can be used to import data which was exported by the Export dialog. The CSV data can be edited with a spreadsheet, and shall be written using tabs as delimiters. Import should then be possible using "CSV quoted", which is more flexible than "CSV unquoted". However, its fields cannot contain any double quotes. If you only export from &kid3; and import later, "CSV unquoted" can be used as a simple format for this purpose. The next format, "freedb HTML text", can be used to copy information from an HTML page of freedb.org. Search an album in freedb and if the desired information is displayed in the web browser, copy the contents to the clipboard. Then click the From Clipboard button and the imported tracks will be displayed in the preview table at the top of the dialog. If you are satisfied with the imported data, terminate the dialog with OK, which will insert the data into the tags of the current directory. The destination (Tag 1, Tag 2 or Tag 1 and Tag 2) can be selected with a combo box. The files in the current directory should be in the correct track order to get their tags assigned. This is the case if they are numbered. The next preconfigured import format, "freedb HTML source", can be used, if the data is available as an HTML document. Import is possible using the From File button, which opens a file selector, or copying its contents from an editor and then importing from clipboard. This format can be useful for offline import, although the HTML document could also be opened in a browser and then be imported in the first format via the clipboard. More preconfigured formats, e.g. "Track Title Time", are available. An empty custom format can be created with Add to be set by the user. Two lines below the format name can be set with a regular expression to capture the fields from the import text. The first regular expression will be parsed once per document to gather per-album data such as artist, album, year and genre. The second line is tried to match from the start of the document to the end to get track data, usually number and title. The regular expressions include all the features offered by Qt, which is most of the what Perl offers. Bracketing constructs "(..)" create capture buffers for the fields to import and are preceded by &kid3; specific codes to specify which field to capture. The codes are the same as used for the filename format, besides the codes listed below, any frame name is possible: %s %{title} Title (Song) %a %{artist} Artist %l %{album} Album %c %{comment} Comment %y %{year} Year %t %{track} Track %g %{genre} Genre %d %{duration} Duration For example, a track regular expression (second line) to import from an .m3u playlist could be "%{track}(\d+)\s+%{title}(\S[^\r\n]*)\.mp3[\r\n]". All formats can be changed by editing the regular expressions and the name and then clicking Save Settings. They will be stored in the kid3rc file in the configuration directory. This file can be directly edited to have more import formats or it can be deleted to revert to the default formats. Formats can be deleted using Remove. Accuracy shows an estimation of how good the imported information matches the given tracks. It uses track durations or file names to calculate the level of similarity in percent. Cover Art shows the URL of the album cover image which will be downloaded. To check whether the imported tracks match the current set of files, the duration of the imported tracks can be compared with the duration of the files. This option can be enabled with the checkbox Check maximum allowable time difference and the maximum tolerated difference in time can be set in seconds. If a mismatch in a length is detected, the length is displayed with a red background in the preview table. It the files are ordered differently than the imported tracks, their assigned tracks have to be changed. This task can be facilitated using the Match with buttons Length, Track, and Title, which will reorder the tracks according to the corresponding field. To correct the assignments manually, a track can be dragged with the left mouse button and the Ctrl key hold down, and then dropped at the new location. When the import dialog is opened, it contains the actual contents of the tags. The tag type (Tag 1, Tag 2, Tag 1 and Tag 2) can be selected using the Destination combo box. The button on the right of this combo box can be used to revert the table to the current contents of the tags. The checkboxes in the first table column can be used to select the tracks which are imported. This can be useful if a folder contains the tracks of both CDs of a double CD and only the tracks of the second CD have to be imported. To identify the tracks which are imported, it is possible to display the file names or the full paths to the files using the context menu of the table header. The values in the import table can be edited. The revert-button to the right of the Destination combo box can be used to restore the contents of the tags, which can also be useful after changing the Destination. Almost all dialogs feature a Save Settings button, which can be used to store the dialog specific settings and the window size persistently. From Tags leads to a subdialog to set tag frames from the contents of other tag frames. This can be used to simply copy information between tags or extract a part from one frame and insert it in another. As in the import from file/clipboard, there are freely configurable formats to perform different operations. Already preconfigured are formats to copy the Album value to Album Artist, Composer or Conductor, and to extract the Track Number from Title fields which contain a number. There is also a format to extract a Subtitle from a Title field. The following example explains how to add a custom format, which sets the information from the Subtitle field also in the Comment field. Create a new format using Add and set a new name, e.g. "Subtitle to Comment". Then enter "%{subtitle}" in Source and "%{comment}(.*)" for Extraction and click Save Settings. The expression in Source can contain format codes for arbitrary tag frames, multiple codes can be used to combine the contents from different frames. For each track, a text is generated from its tags using the Source format, and the regular expression from Extraction is applied to this text to set new values for the tags. Format codes are used before the capturing parentheses to specify the tag frame where the captured text shall be stored. It works in the same way as for the import from file/clipboard. File Import from gnudb.org... Import from a freedb.org server using gnudb.org album search. This menu item opens the same import dialog as Import..., but opens directly the gnudb.org dialog. File Import from TrackType.org... Import from the TrackType.org server. This menu item opens the same import dialog as Import..., but opens directly the TrackType.org dialog. File Import from Discogs... Import from the Discogs server. This menu item opens the same import dialog as Import..., but opens directly the From Discogs dialog. File Import from Amazon... Import from Amazon. This menu item opens the same import dialog as Import..., but opens directly the From Amazon dialog. File Import from MusicBrainz Release... Import from the MusicBrainz release database. This menu item opens the same import dialog as Import..., but opens directly the From MusicBrainz Release dialog. File Import from MusicBrainz Fingerprint... Import from a MusicBrainz server. This menu item opens the same import dialog as Import..., but opens directly the From MusicBrainz Fingerprint dialog. File Automatic Import... Automatic Import allows to import information for multiple albums from various web services. If directories are selected in the file list, track data for the selected directories will be imported. If no directory is selected, all directories in the file list will be imported. The tag type (Tag 1, Tag 2, Tag 1 and Tag 2) can be selected using the Destination combo box. Profiles determine which servers will be contacted to fetch album information. Some profiles are predefined (All, MusicBrainz, Discogs, Cover Art), custom profiles can be added using the Add button at the right of the Profile combo box. The table below shows the servers which will be used when importing album information using the selected profile. The import process for an album is finished if all required information has been found, so the order of the rows in the table is important. It can be changed using the Move Up and Move Down buttons. Edit can be used to change an existing entry. The Server selection offers the same servers as can be used in the import functions. Standard Tags, Additional Tags, Cover Art determine the information which shall be fetched from the server. Finally, Accuracy is the minimum accuracy which must be achieved to accept the imported data. If the accuracy is insufficient, the next server in the list will be tried. The same dialog containing the server properties appears when Add is clicked to add a new server entry. Existing entries can be deleted using Remove. To launch an automatic batch import with the selected profile, click Start. Details about the running import are displayed at the top of the dialog. The process can be aborted with the Abort button. File Browse Cover Art... The Browse Cover Art dialog helps to find album cover art. Artist/Album is filled from the tags if possible. Source offers a variety of websites with album cover art. The URL with artist and album as parameters can be found beneath the name. URL-encoded values for artist and album can be inserted using %u{artist} and %u{album}, other values from the tags are possible too, as described in Configure Kid3, User Actions. More sources can be entered after the entry "Custom Source" by replacing "Custom Source" with the source's name, pressing Enter, then inserting the URL and finally pressing Save Settings. The resulting browser command is displayed at the top of the dialog and can be started by clicking Browse. The browser, which can be configured in the settings, is started with the selected source. A cover image can then be dragged from the browser into the &kid3; window and will be set in the picture frame of the selected files. Because not all browsers support drag'n'drop of images and the pictures on websites often have a URL, in such cases &kid3; will receive the URL and not the picture. If the URL points to a picture, it will be downloaded. However, if the URL refers to some other web resource, it has to be translated to the corresponding picture. Such mappings are defined in the table URL extraction. The left column Match contains a regular expression which is compared with the URL. If it matches, the captured expressions in parentheses are inserted into the pattern of the right Picture URL column (at the positions marked with \1 etc.). The replaced regular expression contains the URL of the picture. By this means cover art can be imported from Amazon, Google Images, etc. using drag'n'drop. It is also possible to define your own mappings. File Export... The Export Dialog is used to store data from the tags in a file or the clipboard. The editor at the top shows a preview of the data to export. If the export data contain tabulator characters, the export is displayed in a table. The data will be generated from the tags in the current directory according to the configured format. The format settings are similar as in the Import dialog: The topmost field contains the title (e.g. "CSV unquoted"), followed by the header, which will be generated at the begin of the file. The track data follows; it is used for every track. Finally, the trailer can be used to generate some finishing text. The format fields do not contain regular expressions as in the Import dialog, but only output format expressions with special %-expressions, which will be replaced by values from the tags. The whole thing works like the file name format, and the same codes are used plus some additional codes. Not only the codes listed below but all tag frame names can be used. %s %{title} Title (Song) %a %{artist} Artist %l %{album} Album %c %{comment} Comment %y %{year} Year %t %{track} Track (e.g. 01) %t %{track.n} Track with field width n (e.g. 001 for %{track.3}) %T %{tracknumber} Track (without leading zeros, e.g. 1) %g %{genre} Genre %f %{file} File name %p %{filepath} Path %u %{url} URL %d %{duration} Duration in minutes:seconds %D %{seconds} Duration in seconds %n %{tracks} Number of tracks of the album %e %{extension} File extension %O %{tag1} The format of tag 1 (ID3v1.1 or empty if not existing) %o %{tag2} The format of tag 2 (ID3v2.3.0, ID3v2.4.0, ID3v2.2.0, ID3v2.2.1, Vorbis, APE, MP4, ASF, or empty if not existing) %b %{bitrate} Bit rate in kbit/s %v %{vbr} VBR or empty (only for ID3v2.3 with id3lib) %r %{samplerate} Sample rate in Hz %m %{mode} Channel mode (Stereo or Joint Stereo) %h %{channels} Number of channels (1 or 2) %k %{codec} Codec (e.g. MPEG 1 Layer 3, MP4, Ogg Vorbis, FLAC, MPC, APE, ASF, AIFF, WAV) A few formats are predefined. "CSV unquoted" separates the fields by tabs. Data in this format can be imported again into &kid3; using the import format with the same name. "CSV quoted" additionally encloses the fields by double quotes, which eases the import into spreadsheet applications. However, the fields shall not contain any double quotes when this format is used. "Extended M3U" and "Extended PLS" generate playlists with extended attributes and absolute path names. "HTML" can be used to generate an HTML page with hyperlinks to the tracks. "Kover XML" creates a file which can be imported by the cover printing program Kover. "Technical Details" provides information about bit rate, sample rate, channels, etc. Finally, "Custom Format" is left empty for definition of a custom format. You can define more formats of your own by adding lines in the file kid3rc in the configuration directory. The other formats can be adapted to your needs. The source of the tags to generate the export data (Tag 1 or Tag 2) can be selected with a combo box. Pushing To File or To Clipboard stores the data in a file or on the clipboard. OK and Cancel close the dialog, whereas OK accepts the current dialog settings. File Create Playlist Creates a playlist. The format and contents of the playlist can be set by various options. The name of the playlist can be the Same as directory name or use a Format with values from the tags, e.g. "%{artist} - %{album}" to have the artist and album name in the playlist file name. The format codes are the same as for Export. The extension depends on the playlist format. The location of the generated playlist is determined by the selection of the Create in combo box. Current directory The playlist is created in the current directory and contains only files of the current directory. Every directory A playlist is created in every directory which contains listed files, and each playlist contains the files of that directory. Top-level directory Only one playlist is created in the top-level directory (i.e. the directory of the file list) and it contains the listed files of the top-level directory and all of its sub-directories. The Format of the playlist can be M3U, PLS or XSPF. If Include only the selected files is checked, only the selected files will be included in the playlist. If a directory is selected, all of its files are selected. If this check box is not activated, all audio files are included in the playlist. Sort by file name selects the usual case where the files are ordered by file name. With Sort by tag field, it is possible to sort by a format string with values from tag fields. For instance, "%{track.3}" can be used to sort by track number (the ".3" is used to get three digits with leading zeros because strings are used for sorting). It is also possible to use multiple fields, e.g. "%{genre}%{year}" to sort using a string composed of genre and year. The playlist entries will have relative or absolute file paths depending on whether Use relative path for files in playlist or Use full path for files in playlist is set. When Write only list of files is set, the playlist will only contain the paths to the files. To generate an extended playlist with additional information, a format string can be set using the Write info using control. CtrlQ File Quit Quits the application. The Edit Menu AltA Edit Select All Selects all files. CtrlShiftA Edit Deselect Deselects all files. Edit Select All in Directory Selects all files of the current directory. AltUp Edit Previous File Selects the previous file. AltDown Edit Next File Selects the next file. The Tools Menu Tools Apply Filename Format When Format while editing is switched off for the filename format in the configuration dialog, this menu item can be used to apply the configured format to the names of the selected files. This can also be used to check whether the file names conform with the configured format by applying the format to all saved files and then checking if any files were changed (and therefore marked with a disk symbol in the file listbox). Tools Apply Tag Format When Format while editing is switched off for the tag format in the configuration dialog, this menu item can be used to apply the configured format to the tags of the selected files. This can also be used to check whether the tags conform with the configured format by applying the format to all saved files and then checking if any files were changed (and therefore marked with a disk symbol in the file listbox). Tools Apply Text Encoding Sets the Text encoding selected in Settings/Configure Kid3.../Tags/Tag 2 for all selected files. If UTF8 is selected, UTF16 will be used for ID3v2.3.0 tags because UTF8 is not supported for this format. Tools Rename Directory... This dialog offers the possibility to automatically rename the currently open directory according to the tags in the files. Several formats are preconfigured to include information about artist, album and year in the directory name. It is also possible to set a custom format, the following special codes are used to insert tag values into the directory name: %s %{title} Title (Song) %a %{artist} Artist %l %{album} Album %c %{comment} Comment %y %{year} Year %t %{track} Track (e.g. 01) %t %{track.n} Track with field width n (e.g. 001 for %{track.3}) %T %{tracknumber} Track (without leading zeros, e.g. 1) %g %{genre} Genre If a directory separator "/" is found in the format, multiple directories are created. If you want to create a new directory instead of renaming the current directory, select Create Directory instead of Rename Directory. The source of the tag information can be chosen between From Tag 1 and Tag 2, From Tag 1 and From Tag 2. A preview for the rename operation performed on the first file can be seen in the From and To sections of the dialog. Multiple directories can be renamed by selecting them. Tools Number Tracks... If the track numbers in the tags are not set or have the wrong values, this function can number the tracks automatically in ascending order. The start number can be set in the dialog. If only part of the tracks have to be numbered, they must be selected. When Total number of tracks is checked, the number of tracks will also be set in the tags. It is possible to number the tracks over multiple directories. The folders have to be expanded and selected. Tools Filter... The filter can be used to display only those files which match certain criteria. This is helpful if you want to organize a large collection and only edit those files which are not in the desired scheme. The expression defining which files to display uses the same format codes which are used in the file name format, import and export. %s %{title} Title (Song) %a %{artist} Artist %l %{album} Album %c %{comment} Comment %y %{year} Year %t %{track} Track (e.g. 01) %t %{track.n} Track with field width n (e.g. 001 for %{track.3}) %T %{tracknumber} Track (without leading zeros, e.g. 1) %g %{genre} Genre %f %{file} File name %p %{filepath} Absolute path to file %e %{extension} File extension %O %{tag1} The format of tag 1 (ID3v1.1 or empty if not existing) %o %{tag2} The format of tag 2 (ID3v2.3.0, ID3v2.4.0, ID3v2.2.0, ID3v2.2.1, Vorbis, APE, MP4, ASF, or empty if not existing) %b %{bitrate} Bit rate in kbit/s %v %{vbr} VBR or empty (only for ID3v2.3 with id3lib) %r %{samplerate} Sample rate in Hz %m %{mode} Channel mode (Stereo or Joint Stereo) %h %{channels} Number of channels (1 or 2) %k %{codec} Codec (e.g. MPEG 1 Layer 3, MP4, Ogg Vorbis, FLAC, MPC, APE, ASF, AIFF, WAV) %1a %1{artist}, ... Use the prefix 1 to get values of tag 1 %2a %2{artist}, ... Use the prefix 2 to get values of tag 2 These codes are replaced with the values for the file, and the resulting strings can be compared with the following operations: s1 equals s2: true if s1 and s2 are equal. s1 contains s2: true if s1 contains s2, i.e. s2 is a substring of s1. s matches re: true if s matches the regular expression re. True expressions are replaced by 1, false by 0. True values are represented by 1, true, on and yes, false values by 0, false, off and no. Boolean operations are not, and, or (in this order of precedence) and can be grouped by parentheses. Some filter rules are predefined and can serve as examples for your own expressions: All When the file list is filtered - this is shown by "[filtered]" in the window title - and all files shall be displayed again, the filtering can be reverted using this filter. It uses an empty expression, but a true value would have the same effect. Filename Tag Mismatch not (%{filepath} contains "%{artist} - %{album}/%{track} %{title}") Tests if the file path conforms with the file name format. This rule is automatically adapted if the file name format changes. No Tag 1 %{tag1} equals "" Displays only files which do not have a tag 1. No Tag 2 %{tag2} equals "" Displays only files which do not have a tag 2. ID3v2.3.0 Tag %{tag2} equals "ID3v2.3.0" Displays only files which have an ID3v2.3.0 tag. ID3v2.4.0 Tag %{tag2} equals "ID3v2.4.0" Displays only files which have an ID3v2.4.0 tag. Tag 1 != Tag 2 not (%1{title} equals %2{title} and %1{album} equals %2{album} and %1{artist} equals %2{artist} and %1{comment} equals %2{comment} and %1{year} equals %2{year} and %1{track} equals %2{track} and %1{genre} equals %2{genre}) Displays files with differences between tag 1 and tag2. Tag 1 == Tag 2 %1{title} equals %2{title} and %1{album} equals %2{album} and %1{artist} equals %2{artist} and %1{comment} equals %2{comment} and %1{year} equals %2{year} and %1{track} equals %2{track} and %1{genre} equals %2{genre} Displays files with identical tag 1 and tag 2. Incomplete %{title} equals "" or %{artist} equals "" or %{album} equals "" or %{year} equals "" or %{tracknumber} equals "" or %{genre} equals "" Displays files with empty values in the standard tags (title, artist, album, date, track number, genre). No Picture %{picture} equals "" Displays only files which do not have a picture. Custom Filter To add your own filter, select this entry. For instance, if you want to have a filter for artists starting with "The", replace "Custom Filter" with the name "The Bands" and press Enter. Then insert the following expression into the line edit: %{artist} matches "The.*" Then click Save Settings. Click Apply to filter the files. All files processed are displayed in the text view, with a "+" for those who match the filter and a "-" for the others. When finished, only the files with an artist starting with "The" are displayed, and the window title is marked with "[filtered]". Tools Convert ID3v2.3 to ID3v2.4 If there are any ID3v2.3 tags in the selected files, they will be converted to ID3v2.4 tags. Frames which are not supported by TagLib will be discarded. Only files without unsaved changes will be converted. Tools Convert ID3v2.4 to ID3v2.3 If there are any ID3v2.4 tags in the selected files, they will be converted to ID3v2.3 tags. Only files without unsaved changes will be converted. Tools Play This opens a simple toolbar to play audio files. It contains buttons for the basic operations (Play/Pause, Stop playback, Previous Track, Next Track, Close), sliders for position and volume and a display of the current position. If multiple files are selected, the selected tracks are played, else all files will be played. The Settings Menu Settings Show Toolbar Toggles displaying of the toolbar. Settings Show Statusbar Toggles displaying of the statusbar, which displays longer actions such as opening or saving a directory. Settings Show Picture Toggles displaying of the album cover art preview picture. Settings Auto Hide Tags Empty tags are automatically hidden if this option is active. The File, Tag 1 and Tag 2 sections can be manually collapsed and expanded by clicking on the corresponding -/+ buttons. Settings Configure Kid3... Opens the configuration dialog, which consists of pages for tags, files, user actions, and network settings. Tag specific options can be found on the Tags page, which is itself separated into three tabs for Tag 1, Tag 2, and Tag 1 and Tag 2. If Mark truncated fields is checked, truncated ID3v1.1 fields will be marked red. The text fields of ID3v1.1 tags can only have 30 characters, the comment only 28 characters. Also the genre and track numbers are restricted, so that fields can be truncated when imported or transferred from ID3v2. Truncated fields and the file will be marked red, and the mark will be removed after the field has been edited. With Text encoding for ID3v1 it is possible to set the character set used in ID3v1 tags. This encoding is supposed to be ISO-8859-1, so it is recommended to keep this default value. However, there are tags around with different encoding, so it can be set here and the ID3v1 tags can then be copied to ID3v2 which supports Unicode. The check box Use track/total number of tracks format controls whether the track number field of ID3v2 tags contains simply the track number or additionally the total number of tracks in the directory. When Genre as text instead of numeric string is checked, all ID3v2 genres will be stored as a text string even if there is a corresponding code for ID3v1 genres. If this option is not set, genres for which an ID3v1 code exists are stored as the number of the genre code (in parentheses for ID3v2.3). Thus the genre Metal is stored as "Metal" or "(9)" depending on this option. Genres which are not in the list of ID3v1 genres are always stored as a text string. The purpose of this option is improved compatibility with devices which do not correctly interpret genre codes. Text encoding defines the default encoding used for ID3v2 frames and can be set to ISO-8859-1, UTF16, or UTF8. UTF8 is not valid for ID3v2.3.0 frames; if it is set, UTF16 will be used instead. For ID3v2.4.0 frames, all three encodings are possible. Version used for new tags determines whether new ID3v2 tags are created as version 2.3.0 or 2.4.0. In the first case, id3lib is used as it was in earlier versions of &kid3; and TagLib is only applied when an ID3v2.4.0 tag is encountered. In the second case, ID3v2.4.0 (TagLib), TagLib is generally used for MP3 files; changed or newly created tags will then be saved in version 2.4.0. When ID3v2.3.0 (TagLib) is selected, TagLib is also used for ID3v2.3.0 tags. Track number digits is the number of digits in Track Number fields. Leading zeros are used to pad. For instance, with a value of 2 the track number 5 is set as "05". The combo box Comment field name is only relevant for Ogg/Vorbis and FLAC files and sets the name of the field used for comments. Different applications seem to use different names, "COMMENT" for instance is used by xmms, whereas amaroK uses "DESCRIPTION". The format of pictures in Ogg/Vorbis files is determined by Picture field name, which can be METADATA_BLOCK_PICTURE or COVERART. The first is the official standard and uses the same format as pictures in FLAC tags. COVERART is an earlier unofficial way to include pictures in Vorbis comments. It can be used for compatibility with legacy players. Custom Genres can be used to define genres which are not available in the standard genre list, e.g. "Gothic Metal". Such custom genres will appear in the Genre combo box of Tag 2. For ID3v1.1 tags, only the predefined genres can be used. The list of custom genres can also be used to reduce the number of genres available in the Genre combo box to those typically used. If your collection mostly contains music in the genres Metal, Gothic Metal, Ancient and Hard Rock, you can enter those genres and mark Show only custom genres. The Tag 2 Genre combo box will then only contain those four genres and you will not have to search through the complete genres list for them. In this example, only Metal and Hard Rock will be listed in the tag 1 genres list, because those two custom genres entries are standard genres. If Show only custom genres is not active, the custom genres can be found at the end of the genres list. Quick Access Tags defines which frame types are always shown in the Tag 2 section. Such frames can then be added without first using the Add button. Tag Format contains options for the format of the tags. When Format while editing is checked, the format configuration is automatically used while editing text in the line edits. The Case conversion can be set to No changes, All lowercase, All uppercase, First letter uppercase or All first letters uppercase. To use locale-aware conversion between lowercase and uppercase characters, a locale can be selected in the combobox below. The string replacement list can be set to arbitrary string mappings. To add a new mapping, select the From cell of a row and insert the text to replace, then go to the To column and enter the replacement text. To remove a mapping set the From cell to an empty value (e.g. by first typing space and then backspace). Inserting and deleting rows is also possible using a context menu which appears when the right mouse button is clicked. Replacement is only active, if the String replacement checkbox is checked. On the page Files the check box Load last-opened files can be marked so that &kid3; will open and select the last selected file when it is started the next time. Preserve file timestamp can be checked to preserve the file modification time stamp. Filename for cover sets the name which is suggested when an embedded image is exported to a file. If Mark changes is active, changed fields are marked with a light gray label background. Filename Format contains options for the format of the filenames. The same options as in Tag Format are available. The User Actions page contains a table with the commands which are available in the context menu of the file list. For critical operations such as deleting files, it is advisable to mark Confirm to pop up a confirmation dialog before executing the command. Output can be marked to see the output written by console commands (standard output and standard error). Name is the name displayed in the context menu. Command is the command line to be executed. Arguments can be passed using the following codes: %F %{files} File paths (a list if multiple files selected) %f %{file} File path to single file %uF %{urls} URLs (a list if multiple files selected) %uf %{url} URL to single file %d %{directory} Directory %s %{title} Title (Song) %a %{artist} Artist %l %{album} Album %c %{comment} Comment %y %{year} Year %t %{track} Track (e.g. 01) %t %{track.n} Track with field width n (e.g. 001 for %{track.3}) %T %{tracknumber} Track (without leading zeros, e.g. 1) %g %{genre} Genre %b %{browser} Command to start the web browser The command which will be inserted with %{browser} can be defined in the Web browser line edit above. Commands starting with %{browser} can be used to fetch information about the audio files from the web, for instance %{browser} http://lyricwiki.org/%u{artist}:%u{title} will query the lyrics for the current song in LyricWiki. The "u" in %u{artist} and %u{title} is used to URL-encode the artist %{artist} and song %{title} information. It is easy to define your own queries in the same way, e.g. an image search with Google: %{browser} http://images.google.com/images?q=%u{artist}%20%u{album} To add album cover art to tag 2, you can search for images with Google or Amazon using the commands described above. The picture can be added to the tag with drag and drop. You can also add an image with Add, then select the Picture frame and import an image file or paste from the clipboard. Picture frames are supported for ID3v2, MP4, FLAC, Ogg and ASF tags. To add and delete entries in the table, a context menu can be used. The Network page contains only a field to insert the proxy address and optionally the port, separated by a colon. The proxy will be used when importing from an Internet server when the checkbox is checked. In the Plugins page, available plugins can be enabled or disabled. The plugins are separated into two sections. The Metadata Plugins & Priority list contains plugins which support audio file formats. The order of the plugins is important because they are tried from top to bottom. Some formats are supported by multiple plugins, so files will be opened with the first plugin supporting them. The TaglibMetadata supports most formats, if it is at the top of the list, it will open most of the files. If you want to use a different plugin for a file format, make sure that it is listed before the TaglibMetadata plugin. Details about the metadata plugin and why you may want to use them instead of TagLib are listed below. Id3libMetadata: Uses id3lib for ID3v1.1 and ID3v2.3 tags in MP3, MP2, AAC files. Supports a few more frame types than TagLib. Another reason to use it is that TagLib only supports ID3v2.3 since version 1.8. OggFlacMetadata: Uses libogg, libvorbis, libvorbisfile for Ogg files, and additionally libFLAC++ and libFLAC for FLAC files. Reasons to use them are pictures in Ogg files, which are not supported by TagLib and that these are the official libraries for these formats. TaglibMetadata: Uses TagLib which supports a lot of audio file formats. Since version 1.8, it also supports ID3v2.3, so it can be used for all audio files supported by &kid3;. Mp4v2Metadata: mp4v2 was originally used by &kid3; to support M4A files. However, newer versions of TagLib provide good M4A support, so this plugin is not built by default. The Available Plugins section lists the remaining plugins. Their order is not important, but they can be enabled or disabled using the check boxes. AmazonImport: Used for the Import from Amazon... function. DiscogsImport: Used for the Import from Discogs... function. FreedbImport: Used for the Import from gnudb.org... and Import from TrackType.org... functions. MusicBrainzImport: Used for the Import from MusicBrainz Release... function. AcoustidImport: Used for the Import from MusicBrainz Fingerprint... function, which depends on the Chromaprint and libav libraries. Plugins which are disabled will not be loaded. This can be used to optimize resource usage and startup time. The settings on this page take only effect after a restart of &kid3;. Settings Configure Shortcuts... Opens a dialog to assign keyboard shortcuts for most of the program functions. There are even functions without corresponding menu or button available, e.g. next file, previous file, select all. The Help Menu Help Kid3 Handbook Opens this handbook. Help About Kid3 Displays a short information about Kid3;.
kid3-cli Commands kid3-cli offers a command-line-interface for &kid3;. If a directory path is used, the directory is opened. If one or more file paths are given, the common directory is opened and the files are selected. Subsequent commands will then work on these files. Commands are specified using options. If multiple commands are passed, they are executed in the given order. If files are modified by the commands, they will be saved at the end. If no command options are passed, kid3-cli starts in interactive mode. Commands can be entered and will operate on the current selection. The following sections list all available commands. Help help COMMAND-NAME Displays help about the parameters of COMMAND-NAME or about all commands if no command name is given. Quit application exit force Exit application. If there are modified unsaved files, the force parameter is required. Change directory cd DIRECTORY If no DIRECTORY is given, change to the home directory. If a directory is given, change into the directory. If one or more file paths are given, change to their common directory and select the files. Print the current working directory pwd Print the filename of the current working directory. Directory list ls List the contents of the current directory. This corresponds to the file list in the &kid3; GUI. Four characters before the file names show the state of the file. > File is selected. * File is modified. 1 File has a tag 1, otherwise '-' is displayed. 2 File has a tag 2, otherwise '-' is displayed. kid3-cli> ls 1- 01 Intro.mp3 > 12 02 We Only Got This One.mp3 *1- 03 Outro.mp3 In this example, all files have a tag 1, the second file also has a tag 2 and it is selected. The third file is modified. Save the changed files save Select file select all none first previous next FILE To select all files, enter select all, to deselect all files, enter select none. To traverse the files in the current directory start with select first, then go forward using select next or backward using select previous. Specific files can be added to the current selection by giving their file names. Wildcards are possible, so select *.mp3 will select all MP3 files in the current directory. kid3-cli> select first kid3-cli> ls > 1- 01 Intro.mp3 12 02 We Only Got This One.mp3 *1- 03 Outro.mp3 kid3-cli> select next kid3-cli> ls 1- 01 Intro.mp3 > 12 02 We Only Got This One.mp3 *1- 03 Outro.mp3 kid3-cli> select *.mp3 kid3-cli> ls > 1- 01 Intro.mp3 > 12 02 We Only Got This One.mp3 >*1- 03 Outro.mp3 Select tag tag TAG-NUMBERS Many commands have an optional TAG-NUMBERS parameter, which specifies whether the command operates on tag 1 or tag 2. If this parameter is omitted, the default tag numbers are used, which can be set by this command. At startup, is is set to 12 which means that information is read from tag 2 if available, else from tag 1; modifications are done on tag 2. The can be set to 1 or 2 to operate only on the corresponding tag. If the parameter is omitted, the current setting is displayed. Get tag frame get all FRAME-NAME TAG-NUMBERS This command can be used to read the value of a specific tag frame or get information about all tag frames (if the argument is omitted or is used). Modified frames are marked with a '*'. kid3-cli> get File: MPEG 1 Layer 3 192 kbps 44100 Hz Joint Stereo Name: 01 Intro.mp3 Tag 1: ID3v1.1 Title Intro Artist One Hit Wonder Album Let's Tag Date 2013 Track Number 1 Genre Pop kid3-cli> get title Intro Set tag frame set FRAME-NAME FRAME-VALUE TAG-NUMBERS This command sets the value of a specific tag frame. kid3-cli> set remixer "O.H. Wonder" To set the contents of a picture frame from a file, use set picture:"/path/to/folder.jpg" "Picture Description" Revert revert Revert all modifications in the selected files (or all files if no files are selected). Import from file or clipboard import FILE FORMAT-NAME TAG-NUMBERS Tags are imported from the file FILE (or from the clipboard if clipboard is used for FILE) in the format with the name FORMAT-NAME (e.g. "CSV unquoted", see Import). Automatic import autoimport PROFILE-NAME TAG-NUMBERS Batch import using profile PROFILE-NAME (see Automatic Import, "All" is used if omitted). Download album cover artwork albumart URL all Set the album artwork by downloading a picture from URL. The rules defined in the Browse Cover Art dialog are used to transform general URLs (e.g. from Amazon) to a picture URL. To set the album cover from a local picture file, use the set command. kid3-cli> albumart http://www.amazon.com/Versus-World-Amon-Amarth/dp/B000078DOC Export to file or clipboard export FILE FORMAT-NAME TAG-NUMBERS Tags are exported to file FILE (or to the clipboard if clipboard is used for FILE) in the format with the name FORMAT-NAME (e.g. "CSV unquoted", see Export). Create playlist playlist Create playlist in the format set in the configuration, see Create Playlist. Apply filename format filenameformat Apply file name format set in the configuration, see Apply Filename Format. Apply tag format tagformat Apply tag name format set in the configuration, see Apply Tag Format. Apply text encoding textencoding Apply text encoding set in the configuration, see Apply Text Encoding. Rename directory renamedir FORMAT create rename dryrun TAG-NUMBERS Rename or create directories from the values in the tags according to a given FORMAT (e.g. %{artist} - %{album}, see Rename Directory), if no format is given, the format defined in the Rename directory dialog is used. The default mode is ; to create directories, must be given explicitly. The rename actions will be performed immediately, to just see what would be done, use the option. Number tracks numbertracks TRACK-NUMBER TAG-NUMBERS Number the selected tracks starting with TRACK-NUMBER (1 if omitted). Filter filter FILTER-NAME FILTER-FORMAT Filter the files so that only the files matching the FILTER-FORMAT are visible. The name of a predefined filter expression (e.g. "Filename Tag Mismatch") can be used instead of a filter expression, see Filter. kid3-cli> filter '%{title} contains "tro"' Started /home/urs/One Hit Wonder - Let's Tag + 01 Intro.mp3 - 02 We Only Got This One.mp3 + 03 Outro.mp3 Finished kid3-cli> ls 1- 01 Intro.mp3 1- 03 Outro.mp3 kid3-cli> filter All Started /home/urs/One Hit Wonder - Let's Tag + 01 Intro.mp3 + 02 We Only Got This One.mp3 + 03 Outro.mp3 Finished kid3-cli> ls 1- 01 Intro.mp3 12 02 We Only Got This One.mp3 1- 03 Outro.mp3 Convert ID3v2.3 to ID3v2.4 to24 Convert ID3v2.4 to ID3v2.3 to23 Filename from tag fromtag FORMAT TAG-NUMBERS Set the file names of the selected files from values in the tags, for example fromtag "%{track} - %{title}" 1. If no format is specified, the format set in the GUI is used. Tag from filename totag FORMAT TAG-NUMBERS Set the tag frames from the file names, for example totag "%{albumartist} - %{album}/%{track} %{title}" 2. If no format is specified, the format set in the GUI is used. If the format of the filename does not match this pattern, a few other commonly used formats are tried. Tag to other tag syncto TAG-NUMBER Copy the tag frames from one tag to the other tag, e.g. to set the ID3v2 tag from the ID3v1 tag, use syncto 2. Copy copy TAG-NUMBER Copy the tag frames of the selected file to the internal copy buffer. They can then be set on another file using the paste command. Paste paste TAG-NUMBER Set tag frames from the contents of the copy buffer in the selected files. Remove remove TAG-NUMBER Remove a tag. Play play pause stop previous next Start audio playback. Once it has been started, it can be controlled with the (to pause and resume), , and options. Examples Set album cover in all files of a directory using the batch import function: kid3-cli -c 'autoimport "Cover Art"' /path/to/dir Remove comment frames and apply the tag format in both tags of all MP3 files of a directory: kid3-cli -c 'set comment "" 1' -c 'set comment "" 2' \ -c 'tagformat 1' -c 'tagformat 2' /path/to/dir/*.mp3 Automatically import tag 2, synchronize to tag 1, set file names from tag 2 and finally create a playlist: kid3-cli -c autoimport -c 'syncto 1' -c fromtag -c playlist \ /path/to/dir/*.mp3 For all files with an ID3v2.4.0 tag, convert to ID3v2.3.0 and remove the arranger frame: kid3-cli -c "filter 'ID3v2.4.0 Tag'" -c "select all" -c to23 \ -c "set arranger ''" /path/to/dir Credits and License &kid3; Program written by Urs Fleisch ufleisch@users.sourceforge.net &underFDL; &underGPL; Installation How to obtain Kid3 &kid3; can be found at http://kid3.sourceforge.net. Requirements &kid3; needs Qt. KDE is recommended but not necessary, as &kid3; can also be compiled as a Qt application. &kid3; can be compiled for systems where these libraries are available, e.g. for GNU/Linux, Windows and Mac OS X. To tag Ogg/Vorbis files, libogg, libvorbis and libvorbisfile are required, for FLAC files libFLAC++ and libFLAC. id3lib is used for MP3 files. These four formats are also supported by TagLib, which can also handle MPC, APE, MP2, Speex, TrueAudio, WavPack, WMA, WAV, AIFF files and tracker modules. To import from acoustic fingerprints, Chromaprint and libav are used. &kid3; is available for most Linux distributions, Windows and Mac OS X. Links can be found on http://kid3.sourceforge.net. Compilation and Installation You can compile &kid3; with or without KDE. Without KDE, &kid3; is a simple Qt application and lacks some configuration and session features. For a KDE 4 version, go into the top directory and type % cmake . % make % make install To compile for different versions of Qt or KDE, set the corresponding cmake options. If not all libraries are present, &kid3; is built with reduced functionality. So you should take care to have all desired development packages installed. On the other side, cmake-options control which libraries are compiled in. The default is -D WITH_TAGLIB:BOOL=ON -D WITH_MP4V2:BOOL=OFF -D WITH_ID3LIB:BOOL=ON -D WITH_CHROMAPRINT:BOOL=ON -D WITH_VORBIS:BOOL=ON -D WITH_FLAC:BOOL=ON . These options can be disabled using OFF. To build &kid3; as a Qt application without KDE, use the cmake option -DWITH_APPS=Qt. To build both a KDE and a Qt application, set -DWITH_APPS="Qt;KDE". Qt 4 or Qt 5 can be used. To force the use of Qt 4, set -DWITH_QT4=ON, to force the use of Qt 5, set -DWITH_QT5=ON, if both these options are OFF (default), Qt 4 is preferred if both versions are installed. To use a specific Qt installation, set -DQT_QMAKE_EXECUTABLE=/path/to/qmake. Generation of RPM-Packages is supported by the file kid3.spec, for Debian-Packages, the script build-deb.shis available. The Qt application can also be compiled for Windows and Mac OS X. The scripts in the folders win32 and macosx can be used to build and create packages. The libraries can be installed using buildlibs.sh. Configuration With KDE, the file name filter and format, the import formats, the filename and ID3 formats, the toolbar and statusbar settings as well as the window size will be be saved in the standard location in file kid3rc. As a Qt application, this file is in .config/Kid3/Kid3.conf. On Windows, the configuration is stored in the registry. D-Bus Interface D-Bus Examples The Qt 4 and KDE 4 versions on Linux offer a D-Bus-Interface to control &kid3; by scripts. Scripts can be written in any language with D-Bus-bindings (e.g. in Python) and can be added to the User Actions to extend the functionality of &kid3;. The artist in tag 2 of the current file can be set to the value "One Hit Wonder" with the following code: Shell dbus-send --dest=net.sourceforge.kid3 --print-reply=literal \ /Kid3 net.sourceforge.Kid3.setFrame int32:2 string:'Artist' \ string:'One Hit Wonder' or easier with Qt's qdbus (qdbusviewer can be used to explore the interface in a GUI): qdbus net.sourceforge.kid3 /Kid3 setFrame 2 Artist \ 'One Hit Wonder' Python import dbus kid3 = dbus.SessionBus().get_object( 'net.sourceforge.kid3', '/Kid3') kid3.setFrame(2, 'Artist', 'One Hit Wonder') Perl use Net::DBus; $kid3 = Net::DBus->session->get_service( "net.sourceforge.kid3")->get_object( "/Kid3", "net.sourceforge.Kid3"); $kid3->setFrame(2, "Artist", "One Hit Wonder"); D-Bus API The D-Bus API is specified in net.sourceforge.Kid3.xml. The Kid3 interface has the following methods: Open file or directory boolean openDirectory string path path path to file or directory Returns true if OK. Save all modified files boolean save Returns true if OK. Get a detailed error message provided by some methods string getErrorMessage Returns detailed error message. Revert changes in the selected files revert Start an automatic batch import boolean batchImport int32 tagMask string profileName tagMask tag mask (bit 0 for tag 1, bit 1 for tag 2) profileName name of batch import profile to use Import tags from a file boolean importFromFile int32 tagMask string path int32 fmtIdx tagMask tag bit (1 for tag 1, 2 for tag 2) path path of file fmtIdx index of format Returns true if OK. Download album cover art downloadAlbumArt string url boolean allFilesInDir url URL of picture file or album art resource allFilesInDir true to add the image to all files in the directory Export tags to a file boolean exportToFile int32 tagMask string path int32 fmtIdx tagMask tag bit (1 for tag 1, 2 for tag 2) path path of file fmtIdx index of format Returns true if OK. Create a playlist boolean createPlaylist Returns true if OK. Quit the application quit Select all files selectAll Deselect all files deselectAll Set the first file as the current file boolean firstFile Returns true if there is a first file. Set the previous file as the current file boolean previousFile Returns true if there is a previous file. Set the next file as the current file boolean nextFile Returns true if there is a next file. Select the first file boolean selectFirstFile Returns true if there is a first file. Select the previous file boolean selectPreviousFile Returns true if there is a previous file. Select the next file boolean selectNextFile Returns true if there is a next file. Select the current file boolean selectCurrentFile Returns true if there is a current file. Expand or collapse the current file item if it is a directory boolean expandDirectory A file list item is a directory if getFileName() returns a name with '/' as the last character. Returns true if current file item is a directory. Apply the file name format applyFilenameFormat Apply the tag format applyTagFormat Apply text encoding applyTextEncoding Set the directory name from the tags boolean setDirNameFromTag int32 tagMask string format boolean create tagMask tag mask (bit 0 for tag 1, bit 1 for tag 2) format directory name format create true to create, false to rename Returns true if OK, else the error message is available using getErrorMessage(). Set subsequent track numbers in the selected files numberTracks int32 tagMask int32 firstTrackNr tagMask tag mask (bit 0 for tag 1, bit 1 for tag 2) firstTrackNr number to use for first file Filter the files filter string expression expression filter expression Convert ID3v2.3 tags to ID3v2.4 convertToId3v24 Convert ID3v2.4 tags to ID3v2.3 convertToId3v23 Returns true if OK. Get path of directory string getDirectoryName Returns absolute path of directory. Get name of current file string getFileName Returns true absolute file name, ends with "/" if it is a directory. Set name of selected file setFileName string name name file name The file will be renamed when the directory is saved. Set format to use when setting the filename from the tags setFileNameFormat string format format file name format Set the file names of the selected files from the tags setFileNameFromTag int32 tagMask tagMask tag bit (1 for tag 1, 2 for tag 2) Get value of frame string getFrame int32 tagMask string name tagMask tag bit (1 for tag 1, 2 for tag 2) name name of frame (e.g. "artist") To get binary data like a picture, the name of a file to write can be added after the name, e.g. "Picture:/path/to/file". Returns value of frame. Set value of frame boolean setFrame int32 tagMask string name string value tagMask tag bit (1 for tag 1, 2 for tag 2) name name of frame (e.g. "artist") value value of frame For tag 2 (tagMask 2), if no frame with name exists, a new frame is added, if value is empty, the frame is deleted. To add binary data like a picture, a file can be added after the name, e.g. "Picture:/path/to/file". Returns true if OK. Get all frames of a tag array of string getTag int32 tagMask tagMask tag bit (1 for tag 1, 2 for tag 2) Returns list with alternating frame names and values. Get technical information about file array of string getInformation Properties are Format, Bitrate, Samplerate, Channels, Duration, Channel Mode, VBR, Tag 1, Tag 2. Properties which are not available are omitted. Returns list with alternating property names and values. Set tag from file name setTagFromFileName int32 tagMask tagMask tag bit (1 for tag 1, 2 for tag 2) Set tag from other tag setTagFromOtherTag int32 tagMask tagMask tag bit (1 for tag 1, 2 for tag 2) Copy tag copyTag int32 tagMask tagMask tag bit (1 for tag 1, 2 for tag 2) Paste tag pasteTag int32 tagMask tagMask tag bit (1 for tag 1, 2 for tag 2) Remove tag removeTag int32 tagMask tagMask tag bit (1 for tag 1, 2 for tag 2) Reparse the configuration reparseConfiguration Automated configuration changes are possible by modifying the configuration file and then reparsing the configuration. Plays the selected files playAudio &documentation.index;
kid3-3.0.2/doc/fixdocbook.pl000077500000000000000000000012761224603511300156050ustar00rootroot00000000000000#!/usr/bin/perl -n s/"-\/\/KDE\/\/DTD DocBook XML V4.2-Based Variant V1.1\/\/EN" "dtd\/kdex.dtd"/"-\/\/OASIS\/\/DTD DocBook XML V4.2\/\/EN" "http:\/\/www.oasis-open.org\/docbook\/xml\/4.2\/docbookx.dtd"/; s///; s///; s/ufleisch@/ufleisch at /g; s/&FDLNotice;/FDL<\/ulink><\/para>/g; s/&underFDL;/FDL<\/ulink><\/para>/g; s/&underGPL;/GPL<\/ulink><\/para>/g; s/&documentation.index;//g; print; kid3-3.0.2/doc/fixhtml.pl000077500000000000000000000016031224603511300151230ustar00rootroot00000000000000#!/usr/bin/perl -n s/^>\ <style type="text\/css">\ body { font-family: Arial, Helvetica, sans-serif; color: #000000; background: #ffffff; }\ h1, h2, h3, h4 { text-align: left; font-weight: bold; color: #f7800a; background: transparent; }\ a:link { color: #0057ae; }\ pre { display: block; color: #000000; background: #f9f9f9; border: #2f6fab dashed; border-width: 1px; overflow: auto; line-height: 1.1em; }\ dt { font-weight: bold; color: #0057ae; }\ p { text-align: justify; }\ li { text-align: left; }\ .guibutton, .guilabel, .guimenu, .guimenuitem { font-family: Arial, Helvetica, sans-serif; color: #000000; background: #dcdcdc; }\ .application { font-weight: bold; }\ .command { font-family: "Courier New", Courier, monospace; }\ .filename { font-style: italic; }\ <\/style/i; print; �����������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/doc/fixmandocbook.pl���������������������������������������������������������������������0000775�0000000�0000000�00000002521�12246035113�0016273�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -n s/"-\/\/KDE\/\/DTD DocBook XML V4.2-Based Variant V1.1\/\/EN" "dtd\/kdex.dtd"/"-\/\/OASIS\/\/DTD DocBook XML V4.2\/\/EN" "http:\/\/www.oasis-open.org\/docbook\/xml\/4.2\/docbookx.dtd"/; s/<!ENTITY % German "INCLUDE">/<!ENTITY language "de">/; s/<!ENTITY % English "INCLUDE">/<!ENTITY language "en">/; s/ufleisch@/ufleisch at /g; s/&FDLNotice;/<para><ulink url="http:\/\/www.gnu.org\/licenses\/licenses.html#FDL">FDL<\/ulink><\/para>/g; s/&underFDL;/<para><ulink url="http:\/\/www.gnu.org\/licenses\/licenses.html#FDL">FDL<\/ulink><\/para>/g; s/&underGPL;/<para><ulink url="http:\/\/www.gnu.org\/licenses\/licenses.html#GPL">GPL<\/ulink><\/para>/g; s/&documentation.index;//g; s/<book /<article /; s/<\/book>/<\/refentry>\n<\/article>/; s/<bookinfo/<articleinfo/; s/<\/bookinfo>/<\/articleinfo>\n\n<refentry id="kid3">\n\n/; s/^<!--change manpage(.*)-->.*$/\1/; s/^<!--begin manpage include$/<!--begin manpage include-->/; s/^end manpage include-->$/<!--end manpage include-->/; s/^<!--begin manpage ignore-->$/<!--begin manpage ignore/; s/^<!--end manpage ignore-->$/end manpage ignore-->/; s/<preface/<refsect1/; s/<\/preface/<\/refsect1/; s/<chapter/<refsect1/; s/<\/chapter/<\/refsect1/; s/<sect1/<refsect2/; s/<\/sect1/<\/refsect2/; s/<sect2/<refsect3/; s/<\/sect2/<\/refsect3/; s/<appendix/<refsect1/; s/<\/appendix/<\/refsect1/; print; �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/kid3.lsm���������������������������������������������������������������������������������0000664�0000000�0000000�00000001723�12246035113�0013715�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Begin4 Title: Kid3 -- Efficient Audio Tagger Version: 3.0.2 Entered-date: 2013-11-27 Description: Audio tagger to edit ID3v1.1, ID3v2.3, ID3v2.4, Ogg/Vorbis, FLAC, MPC, APE, MP4/AAC, MP2, Speex, TrueAudio, WavPack, WMA, WAV and AIFF tags, multiple files, generate tags from file names and vice versa, import and export album data in various formats. Keywords: KDE Qt kdemultimedia MP3 ID3 ID3v1 ID3v2 Ogg FLAC MPC APE MP4 M4A AAC MP2 Speex TrueAudio WavPack WMA WAV AIFF Author: Urs Fleisch <ufleisch@users.sourceforge.net> Maintained-by: Urs Fleisch <ufleisch@users.sourceforge.net> Home-page: http://kid3.sourceforge.net Alternate-site: http://www.kde-apps.org/content/show.php?content=10415 Primary-site: http://www.sourceforge.net/projects/kid3 http://prdownloads.sourceforge.net/kid3/kid3-3.0.2.tar.gz?download Platform: Linux, Windows. Copying-policy: GPL End ���������������������������������������������kid3-3.0.2/kid3.spec��������������������������������������������������������������������������������0000664�0000000�0000000�00000012373�12246035113�0014057�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# # spec file for package kid3 # Name: kid3 License: GPL Group: Applications/Multimedia Summary: Efficient ID3 tag editor Version: 3.0.2 Release: 1%{?dist} URL: http://kid3.sourceforge.net/ Source0: http://downloads.sourceforge.net/kid3/%{name}-%{version}.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) BuildRequires: kdelibs4-devel # OpenSUSE: BuildRequires: libkde4-devel BuildRequires: cmake BuildRequires: id3lib-devel # Mandriva: BuildRequires: libid3lib3.8-devel BuildRequires: taglib-devel >= 1.4 BuildRequires: flac-devel # Mandriva: BuildRequires: libflac++-devel BuildRequires: libchromaprint-devel BuildRequires: libvorbis-devel BuildRequires: readline-devel BuildRequires: gettext Requires: xdg-utils %description With Kid3 you can: - Edit ID3v1.1 tags - Edit all ID3v2.3 and ID3v2.4 frames - Convert between ID3v1.1, ID3v2.3 and ID3v2.4 tags - Edit tags in MP3, Ogg/Vorbis, FLAC, MPC, APE, MP4/AAC, MP2, Speex, TrueAudio, WavPack, WMA, WAV, AIFF files and tracker modules (MOD, S3M, IT, XM). - Edit tags of multiple files, e.g. the artist, album, year and genre of all files of an album typically have the same values and can be set together. - Generate tags from filenames - Generate tags from the contents of tag fields - Generate filenames from tags - Generate playlist files - Automatic case conversion and string translation - Import and export album data - Import from gnudb.org, TrackType.org, MusicBrainz, Discogs, Amazon This package uses KDE libraries, if you do not use KDE you should use kid3-qt. Authors: Urs Fleisch %prep [ ${RPM_BUILD_ROOT} != "/" ] && rm -rf ${RPM_BUILD_ROOT} %setup -q %build mkdir kid3-build cd kid3-build; \ cmake -DCMAKE_INSTALL_PREFIX=/usr -DLIB_SUFFIX= -DCMAKE_BUILD_TYPE=Release ..; \ make %{?_smp_mflags}; \ cd .. %install mkdir -p ${RPM_BUILD_ROOT}/%{_defaultdocdir} make -C kid3-build install DESTDIR=${RPM_BUILD_ROOT} test -d $RPM_BUILD_ROOT/usr/bin && strip $RPM_BUILD_ROOT/usr/bin/* find $RPM_BUILD_ROOT -type f -o -name "*.so" -exec strip "{}" \; %clean [ -d ${RPM_BUILD_ROOT} -a "${RPM_BUILD_ROOT}" != "/" ] && rm -rf ${RPM_BUILD_ROOT} %files %defattr(-,root,root,-) %doc AUTHORS ChangeLog COPYING LICENSE README %{_bindir}/kid3 %{_datadir}/applications/kde4/*kid3.desktop %{_datadir}/icons/hicolor/*x*/apps/kid3.png %{_datadir}/icons/hicolor/scalable/apps/kid3.svgz %{_datadir}/kde4/apps/kid3/ %{_datadir}/doc/kde/HTML/en/kid3/ %{_datadir}/doc/kde/HTML/de/kid3/ %package qt Group: Applications/Multimedia Summary: Efficient ID3 tag editor %description qt With Kid3 you can: - Edit ID3v1.1 tags - Edit all ID3v2.3 and ID3v2.4 frames - Convert between ID3v1.1, ID3v2.3 and ID3v2.4 tags - Edit tags in MP3, Ogg/Vorbis, FLAC, MPC, MP4/AAC, MP2, Speex, TrueAudio, WavPack, WMA, WAV, AIFF files and tracker modules (MOD, S3M, IT, XM). - Edit tags of multiple files, e.g. the artist, album, year and genre of all files of an album typically have the same values and can be set together. - Generate tags from filenames - Generate tags from the contents of tag fields - Generate filenames from tags - Generate playlist files - Automatic case conversion and string translation - Import and export album data - Import from gnudb.org, TrackType.org, MusicBrainz, Discogs, Amazon This package does not use KDE libraries, if you use KDE you should use kid3. Authors: Urs Fleisch %files qt %defattr(-,root,root,-) %doc AUTHORS ChangeLog COPYING LICENSE README %{_bindir}/kid3-qt %{_datadir}/applications/*kid3-qt.desktop %{_datadir}/icons/hicolor/*x*/apps/kid3-qt.png %{_datadir}/icons/hicolor/scalable/apps/kid3-qt.svg %{_datadir}/doc/kid3-qt/ %{_mandir}/man1/kid3-qt.1.gz %{_mandir}/de/man1/kid3-qt.1.gz %package core Group: Applications/Multimedia Summary: Audio tag editor core libraries and data %description core This package contains common libraries and data used by both kid3 and kid3-qt. Authors: Urs Fleisch %files core %defattr(-,root,root,-) %doc AUTHORS ChangeLog COPYING LICENSE README %{_libdir}/kid3/* %{_datadir}/dbus-1/interfaces/*.xml %{_datadir}/kid3/translations/ %{_mandir}/man1/kid3.1.gz %{_mandir}/de/man1/kid3.1.gz %package cli Group: Applications/Multimedia Summary: Efficient ID3 tag editor %description cli With Kid3 you can: - Edit ID3v1.1 tags - Edit all ID3v2.3 and ID3v2.4 frames - Convert between ID3v1.1, ID3v2.3 and ID3v2.4 tags - Edit tags in MP3, Ogg/Vorbis, FLAC, MPC, MP4/AAC, MP2, Speex, TrueAudio, WavPack, WMA, WAV, AIFF files and tracker modules (MOD, S3M, IT, XM). - Edit tags of multiple files, e.g. the artist, album, year and genre of all files of an album typically have the same values and can be set together. - Generate tags from filenames - Generate tags from the contents of tag fields - Generate filenames from tags - Generate playlist files - Automatic case conversion and string translation - Import and export album data - Import from gnudb.org, TrackType.org, MusicBrainz, Discogs, Amazon This package contains a command line interface for Kid3, for a GUI you can use kid3-qt or kid3. Authors: Urs Fleisch %files cli %defattr(-,root,root,-) %doc AUTHORS ChangeLog COPYING LICENSE README %{_bindir}/kid3-cli %{_mandir}/man1/kid3-cli.1.gz %{_mandir}/de/man1/kid3-cli.1.gz ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/macosx/����������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�12246035113�0013635�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/macosx/Info.plist.in���������������������������������������������������������������������0000664�0000000�0000000�00000002530�12246035113�0016212�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>CFBundleDevelopmentRegion</key> <string>English</string> <key>CFBundleExecutable</key> <string>${MACOSX_BUNDLE_BUNDLE_NAME}</string> <key>CFBundleGetInfoString</key> <string>${MACOSX_BUNDLE_INFO_STRING}</string> <key>CFBundleIconFile</key> <string>${MACOSX_BUNDLE_ICON_FILE}</string> <key>CFBundleIdentifier</key> <string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string> <key>CFBundleInfoDictionaryVersion</key> <string>6.0</string> <key>CFBundleLongVersionString</key> <string>${MACOSX_BUNDLE_LONG_VERSION_STRING}</string> <key>CFBundleName</key> <string>${MACOSX_BUNDLE_BUNDLE_NAME}</string> <key>CFBundlePackageType</key> <string>APPL</string> <key>CFBundleShortVersionString</key> <string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string> <key>CFBundleSignature</key> <string>????</string> <key>CFBundleVersion</key> <string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string> <key>CSResourcesFileMapped</key> <true/> <key>LSRequiresCarbon</key> <true/> <key>NSHumanReadableCopyright</key> <string>${MACOSX_BUNDLE_COPYRIGHT}</string> <key>NSPrincipalClass</key> <string>NSApplication</string> <key>NSHighResolutionCapable</key> <string>True</string> </dict> </plist> ������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/macosx/buildkid3.sh����������������������������������������������������������������������0000775�0000000�0000000�00000000166�12246035113�0016051�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX= -DWITH_FFMPEG=ON .. make cpack ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/po/��������������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�12246035113�0012761�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/po/CMakeLists.txt������������������������������������������������������������������������0000664�0000000�0000000�00000003125�12246035113�0015522�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Generate .ts files from the .po files and then convert them to .qm files. file(GLOB _poFiles *.po) set(_tsFiles) foreach (_currentPoFile ${_poFiles}) get_filename_component(_lang ${_currentPoFile} NAME_WE) set(_tsFiles ${_tsFiles} ${CMAKE_CURRENT_BINARY_DIR}/kid3_${_lang}.ts) endforeach (_currentPoFile) if (APPLE OR WIN32) # Also copy or convert the Qt language files from the Qt installation. set(_qtQmFiles) set(_qtTsFiles) foreach (_currentPoFile ${_poFiles}) get_filename_component(_lang ${_currentPoFile} NAME_WE) if (EXISTS ${QT_TRANSLATIONS_DIR}/qt_${_lang}.qm) set(_qtQmFiles ${_qtQmFiles} ${QT_TRANSLATIONS_DIR}/qt_${_lang}.qm) elseif (EXISTS ${QT_TRANSLATIONS_DIR}/qt_${_lang}.ts) set(_qtTsFiles ${_qtTsFiles} ${QT_TRANSLATIONS_DIR}/qt_${_lang}.ts) endif (EXISTS ${QT_TRANSLATIONS_DIR}/qt_${_lang}.qm) endforeach (_currentPoFile) endif (APPLE OR WIN32) if (_tsFiles) add_custom_command( OUTPUT ${_tsFiles} COMMAND ${PERL_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/po2ts.pl ${QT_LUPDATE_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../src DEPENDS ${_poFiles} ) endif (_tsFiles) if (APPLE OR WIN32) qt4_add_translation(_qmFiles ${_tsFiles} ${_qtTsFiles}) add_custom_target(translations "ALL" DEPENDS ${_qmFiles}) install(FILES ${_qmFiles} ${_qtQmFiles} DESTINATION ${WITH_TRANSLATIONSDIR}) else (APPLE OR WIN32) qt4_add_translation(_qmFiles ${_tsFiles}) add_custom_target(translations "ALL" DEPENDS ${_qmFiles}) install(FILES ${_qmFiles} DESTINATION ${WITH_TRANSLATIONSDIR}) endif (APPLE OR WIN32) �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/po/cs.po���������������������������������������������������������������������������������0000664�0000000�0000000�00000106751�12246035113�0013740�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Czech translation of Kid3 # This file is put in the public domain. # # Pavel Fric <pavelfric@seznam.cz>, 2009, 2011. # Urs Fleisch <ufleisch@users.sourceforge.net>, 2010. # Pavel Fric <fripohled@blogspot.com>, 2010, 2011, 2012, 2013. msgid "" msgstr "" "Project-Id-Version: Kid3 3.0.2\n" "Report-Msgid-Bugs-To: http://sourceforge.net/p/kid3/bugs/\n" "POT-Creation-Date: 2013-09-11 21:22+0200\n" "PO-Revision-Date: 2013-06-26 21:43+0200\n" "Last-Translator: Pavel Fric <pavelfric@seznam.cz>\n" "Language-Team: Czech <kde-i18n-doc@kde.org>\n" "Language: cs\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Lokalize 1.5\n" "Plural-Forms: nplurals=3; plural=((n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2);\n" "X-Language: cs\n" "X-Source-Language: en\n" msgid "Timeout" msgstr "ÄŒasový limit" msgid "Usage:" msgstr "Využití:" msgid "Help" msgstr "NápovÄ›da" msgid "Command name" msgstr "Název příkazu" msgid "Quit application" msgstr "UkonÄí program" msgid "The current directory has been modified." msgstr "NynÄ›jší adresář byl zmÄ›nÄ›n." msgid "Type 'exit force' to quit." msgstr "NapiÅ¡te 'exit force' pro skonÄení." msgid "Change directory" msgstr "ZmÄ›nit adresář" msgid "%1 does not exist" msgstr "%1 neexistuje" msgid "Print the filename of the current working directory" msgstr "Vypíše celý název souÄasného pracovního adresáře" msgid "Directory list" msgstr "Výpis adresáře" msgid "Saves the changed files" msgstr "Uloží zmÄ›nÄ›né soubory" msgid "Error while writing file:\n" msgstr "PÅ™i zápisu do souboru nastala chyba:\n" msgid "Select file" msgstr "Vyberte soubor" msgid "%1 not found" msgstr "%1 nenalezen" msgid "Select tag" msgstr "Vybrat Å¡títek" msgid "Get tag frame" msgstr "Získat zbrusu prvek" msgid "Set tag frame" msgstr "Nastavit zbrusu prvek" msgid "Could not set \"%1\" for %2" msgstr "Souboru %2 nelze nastavit atribut \"%1\"" msgid "Revert" msgstr "Naposledy uložené znÄ›ní" msgid "Import from file or clipboard" msgstr "Zavést ze souboru nebo ze schránky" msgid "Format name" msgstr "Formát" msgid "%1 not found." msgstr "%1 nenalezena." msgid "Available" msgstr "Dostupné" msgid "Error" msgstr "Chyba" msgid "Automatic import" msgstr "Automaticky importovat" msgid "Profile name" msgstr "Název profilu" msgid "Reading Directory" msgstr "ÄŒte se adresář" msgid "Started" msgstr "SpuÅ¡tÄ›n" msgid "Source" msgstr "Zdroj" msgid "Querying" msgstr "Dotazování se" msgid "Fetching" msgstr "Získává se" msgid "Data received" msgstr "PÅ™ijatá data" msgid "Cover" msgstr "Obal" msgid "Finished" msgstr "Hotovo" msgid "Aborted" msgstr "PÅ™eruÅ¡eno" msgid "Download album cover artwork" msgstr "Procházet kvůli obrázkům obalů" msgid "Export to file or clipboard" msgstr "Vyvést do souboru nebo do schránky" msgid "Create playlist" msgstr "VytvoÅ™it seznam skladeb" msgid "Apply filename format" msgstr "Použít formát pro název souboru" msgid "Apply tag format" msgstr "Použít formát pro znaÄku" msgid "Apply text encoding" msgstr "Použít kódování textu" msgid "Rename directory" msgstr "PÅ™ejmenovat adresář" msgid "Number tracks" msgstr "Číslovat skladby" msgid "Track number" msgstr "Číslo skladby" msgid "Filter" msgstr "Filtrovat" msgid "Filter name" msgstr "Název filtru" msgid "Convert ID3v2.3 to ID3v2.4" msgstr "PÅ™evést ID3v2.3 na ID3v2.4" msgid "Convert ID3v2.4 to ID3v2.3" msgstr "PÅ™evést ID3v2.4 na ID3v2.3" msgid "Filename from tag" msgstr "Název souboru ze znaÄky" msgid "Tag from filename" msgstr "ZnaÄka z názvu souboru" msgid "Tag to other tag" msgstr "" msgid "Copy" msgstr "Kopírovat" msgid "Paste" msgstr "Vložit" msgid "Remove" msgstr "Odstranit" msgid "Play" msgstr "PÅ™ehrát" msgid "Parameter" msgstr "Parametr" msgid "File path" msgstr "Cesta k souboru" msgid "URL" msgstr "URL" msgid "Tag numbers" msgstr "Číslo znaÄky" msgid "Frame name" msgstr "Z názvu souboru" msgid "Frame value" msgstr "" msgid "Format" msgstr "Formát" msgid "Command specific" msgstr "Příkaz" msgid "Available Commands" msgstr "Dostupné příkazy" msgid "File" msgstr "Soubor" msgid "Name" msgstr "Název" msgid "Tag 2" msgstr "ZnaÄka 2" msgid "Tag 1" msgstr "ZnaÄka 1" msgid "Tags" msgstr "ZnaÄky" msgid "Unknown command '%1'. Type 'help' for help." msgstr "Neznámý příkaz '%1'. Pro zobrazení nápovÄ›dy napiÅ¡te 'help'." msgid "Unknown command '%1', -h for help." msgstr "Neznámý příkaz '%1', -h pro pomoc." msgid "Files" msgstr "Soubory" msgid "User Actions" msgstr "Uživatelské příkazy" msgid "Network" msgstr "Síť" msgid "Plugins" msgstr "Zásuvné moduly" msgid "Main Toolbar" msgstr "Hlavní nástrojový panel" msgid "Opens a directory" msgstr "OtevÅ™e adresář" msgid "Opens a recently used directory" msgstr "OtevÅ™e nedávno použitý adresář" msgid "Reverts the changes of all or the selected files" msgstr "Vrátí zmÄ›ny vÅ¡ech nebo vybraných souborů" msgid "Quits the application" msgstr "UkonÄí program" msgid "Select all files" msgstr "Vybrat vÅ¡echny soubory" msgid "Deselect all files" msgstr "ZruÅ¡it výbÄ›r vÅ¡ech souborů" msgid "Configure Shortcuts" msgstr "Nastavit zkratky" msgid "Configure Toolbars" msgstr "Nastavit nástrojové panely" msgid "Preferences dialog" msgstr "Nastavení" msgid "O&pen Directory..." msgstr "Otevřít &adresář..." msgid "&Import..." msgstr "&Zavést..." msgid "Import from %1..." msgstr "Zavést z %1..." msgid "Automatic I&mport..." msgstr "Automaticky &zavést..." msgid "&Browse Cover Art..." msgstr "&Procházet kvůli obrázkům obalů..." msgid "&Export..." msgstr "&Vyvést..." msgid "&Create Playlist..." msgstr "VytvoÅ™it &seznam skladeb..." msgid "Apply &Filename Format" msgstr "Použít formát pro &název souboru" msgid "Apply &Tag Format" msgstr "Použít formát pro &znaÄku" msgid "Apply Text &Encoding" msgstr "Použít kódování t&extu" msgid "&Rename Directory..." msgstr "PÅ™e&jmenovat adresář..." msgid "&Number Tracks..." msgstr "Čís&lovat skladby..." msgid "F&ilter..." msgstr "&Filtrovat..." msgid "Convert ID3v2.3 to ID3v2.&4" msgstr "PÅ™evést ID3v2.3 na ID3v2.&4" msgid "Convert ID3v2.4 to ID3v2.&3" msgstr "PÅ™evést ID3v2.4 na ID3v2.&3" msgid "&Play" msgstr "&PÅ™ehrát" msgid "Show &Picture" msgstr "Ukázat &obrázek" msgid "Auto &Hide Tags" msgstr "Automaticky skrýt &znaÄky" msgid "Select All in &Directory" msgstr "Vybrat vÅ¡echny soubory v &adresáři" msgid "&Previous File" msgstr "&PÅ™edchozí soubor" msgid "&Next File" msgstr "&Další soubor" msgid "From Filename" msgstr "Z názvu souboru" msgid "From Tag 2" msgstr "Ze znaÄky 2" msgid "From Tag 1" msgstr "Ze znaÄky 1" msgid "Frames:" msgstr "Prvky:" msgid "Edit" msgstr "Upravit" msgid "Add" msgstr "PÅ™idat" msgid "Delete" msgstr "Smazat" msgid "Filename" msgstr "Název souboru" msgid "Focus" msgstr "Zaměření" msgid "File List" msgstr "Seznam souborů" msgid "Directory List" msgstr "Výpis adresáře" msgid "Configure - Kid3" msgstr "Nastavit Kid3" msgid "Open" msgstr "Otevřít" msgid "Save As" msgstr "Uložit jako" msgid "directory to open" msgstr "Adresář k otevÅ™ení" msgid "Kid3 ID3 Tagger" msgstr "ZnaÄkovaÄ ID3 s názvem Kid3" #. i18n NAME OF TRANSLATORS msgid "Your names" msgstr "Pavel Fric" #. i18n EMAIL OF TRANSLATORS msgid "Your emails" msgstr "fripohled.blogspot.com" msgid "&Back" msgstr "&ZpÄ›t" msgid "&Forward" msgstr "&VpÅ™ed" msgid "&Find:" msgstr "&Hledat:" msgid "Find Previous" msgstr "Najít pÅ™edchozí" msgid "Find Next" msgstr "Najít následující" msgid "&Close" msgstr "&Zavřít" msgid "&Tags" msgstr "&ZnaÄky" msgid "&Files" msgstr "&Soubory" msgid "&User Actions" msgstr "&Uživatelské příkazy" msgid "&Network" msgstr "&Síť" msgid "&Plugins" msgstr "&Zásuvné moduly" msgid "&Keyboard Shortcuts" msgstr "&Klávesové zkratky" msgid "Use custom app&lication font" msgstr "V programu použít vlastní &písmo" msgid "A&pplication Font..." msgstr "Pís&mo..." msgid "Use custom application &style" msgstr "V programu použít vlastní &styl" msgid "Unknown" msgstr "Neznámý" msgid "Use native system file &dialogs" msgstr "Použít systému vlastní souborové &dialogy" msgid "&Appearance" msgstr "Vz&hled" msgid "&Help" msgstr "Nápo&vÄ›da" msgid "&OK" msgstr "&OK" msgid "&Cancel" msgstr "&ZruÅ¡it" msgid "The keyboard shortcut '%1' is already assigned to '%2'." msgstr "Klávesová zkratka '%1' už je použita pro '%2'." msgid "&File" msgstr "&Soubor" msgid "&Open..." msgstr "&Otevřít..." msgid "Open &Recent" msgstr "Otevřít &nedávný" msgid "&Save" msgstr "&Uložit" msgid "Re&vert" msgstr "V&rátit" msgid "Import from %1" msgstr "Zavést z %1" msgid "Browse album cover artwork" msgstr "Procházet kvůli obrázkům obalů" msgid "Create M3U Playlist" msgstr "VytvoÅ™it seznam skladeb M3U" msgid "&Quit" msgstr "&UkonÄit" msgid "&Edit" msgstr "Úp&ravy" msgid "Select &All" msgstr "Vybrat &vÅ¡e" msgid "Dese&lect" msgstr "&ZruÅ¡it výbÄ›r" msgid "Select all files in the current directory" msgstr "Vybrat vÅ¡echny soubory v aktuálním adresáři" msgid "Select previous file" msgstr "Vybrat pÅ™edchozí soubor" msgid "Select next file" msgstr "Vybrat další soubor" msgid "&Tools" msgstr "&Nástroje" msgid "Apply Filename Format" msgstr "Použít formát pro název souboru" msgid "Apply Tag Format" msgstr "Použít formát pro znaÄku" msgid "Apply Text Encoding" msgstr "Použít kódování textu" msgid "Rename Directory" msgstr "PÅ™ejmenovat adresář" msgid "Number Tracks" msgstr "Číslovat skladby" msgid "&Settings" msgstr "Na&stavení" msgid "Enables/disables the toolbar" msgstr "Povolí/Zakáže nástrojový panel" msgid "Show &Toolbar" msgstr "Ukázat nástrojový &panel" msgid "Enables/disables the statusbar" msgstr "Povolí/Zakáže stavový řádek" msgid "Show St&atusbar" msgstr "Ukázat &stavový řádek" msgid "Show Picture" msgstr "Ukázat obrázek" msgid "Auto Hide Tags" msgstr "Automaticky skrýt" msgid "Configure Kid3" msgstr "Nastavit Kid3" msgid "&Configure Kid3..." msgstr "&Nastavit Kid3..." msgid "Kid3 Handbook" msgstr "PříruÄka k programu Kid3" msgid "Kid3 &Handbook" msgstr "&PříruÄka k programu Kid3" msgid "About Kid3" msgstr "O programu Kid3" msgid "&About Kid3" msgstr "&O programu Kid3" msgid "About Qt" msgstr "O Qt" msgid "About &Qt" msgstr "O &Qt" msgid " [modified]" msgstr "[zmÄ›nÄ›ný]" msgid "Toggle the statusbar..." msgstr "PÅ™epíná se stavový řádek..." msgid "Ready." msgstr "PÅ™ipraven." msgid "&Clear List" msgstr "&Smazat seznam" msgid "Clear" msgstr "VyÄistit" msgid "Reset" msgstr "Obnovit" msgid "Press F2 or double click to edit cell contents." msgstr "Obsah buňky editujte stisknutím F2 nebo dvojklikem." msgid "Action" msgstr "ÄŒinnost" msgid "Shortcut" msgstr "Zkratka" msgid "Server" msgstr "Server" msgid "Accuracy" msgstr "PÅ™esnost" msgid "Standard Tags" msgstr "Standardní znaÄky" msgid "Additional Tags" msgstr "DodateÄné znaÄky" msgid "Cover Art" msgstr "Obrázky obalů" msgid "Invalid File" msgstr "Neplatný soubor" msgid "Data received: %1" msgstr "PÅ™ijatá data: %1" msgid "Request sent..." msgstr "Požadavek poslán..." msgid "Year" msgstr "Rok" msgid "Length" msgstr "Délka" msgid "Filenames" msgstr "Názvy souborů" msgid "URLs" msgstr "URL" msgid "Directory name" msgstr "Název adresáře" msgid "Encode as URL" msgstr "Zakódovat jako URL" msgid "Confirm" msgstr "Potvrzení" msgid "Output" msgstr "Výstup" msgid "Command" msgstr "Příkaz" msgid "Create directory %1 failed\n" msgstr "VytvoÅ™ení adresáře %1 se nepodaÅ™ilo\n" msgid "File %1 already exists\n" msgstr "Soubor %1 již existuje\n" msgid "%1 is not a directory\n" msgstr "%1 není adresářem\n" msgid "Rename %1 to %2 failed\n" msgstr "PÅ™ejmenování %1 na %2 se nepodaÅ™ilo\n" msgid "%1 already exists\n" msgstr "" "%1 již existuje\n" "\n" msgid "%1 is not a file\n" msgstr "%1 není souborem\n" msgid "New directory name is too different\n" msgstr "Nový název adresáře se příliÅ¡ liší\n" msgid "Create directory" msgstr "VytvoÅ™it adresář" msgid "Rename file" msgstr "PÅ™ejmenovat soubor" msgid "C&lear" msgstr "&Smazat" msgid "Execute " msgstr "Provést " msgid "Could not execute " msgstr "NepodaÅ™ilo se provést" msgid "True if strings are equal" msgstr "Správné, jestliže jsou Å™etÄ›zce znaků stejné" msgid "True if string contains substring" msgstr "Správné, jestliže Å™etÄ›zec znaků obsahuje podÅ™etÄ›zec" msgid "True if string matches regexp" msgstr "Správné, jestliže Å™etÄ›zec znaků odpovídá Regexp" msgid "Logical AND" msgstr "Logické A" msgid "Logical OR" msgstr "Logické NEBO" msgid "Logical negation" msgstr "Logické popÅ™ení" msgid "Data" msgstr "Data" msgid "%1 is not writable" msgstr "%1 není zapisovatelný" msgid "All Supported Files" msgstr "VÅ¡echny podporované soubory" msgid "All Files" msgstr "VÅ¡echny soubory" msgid "Track" msgstr "Stopa" msgid "Absolute path to file" msgstr "Nepochybná cesta k souboru" msgid "Duration" msgstr "Délka" msgid "Title" msgstr "Název" msgid "Artist" msgstr "UmÄ›lec" msgid "Album" msgstr "Album" msgid "Comment" msgstr "Poznámka" msgid "Date" msgstr "Datum" msgid "Track Number" msgstr "Číslo skladby" msgid "Genre" msgstr "Žánr" msgid "Album Artist" msgstr "UmÄ›lec alba" msgid "Arranger" msgstr "Hudební aranžér" msgid "Author" msgstr "Autor" msgid "BPM" msgstr "BPM" msgid "Catalog Number" msgstr "Katalogové Äíslo" msgid "Compilation" msgstr "Kompilace" msgid "Composer" msgstr "Skladatel" msgid "Conductor" msgstr "Vedoucí" msgid "Copyright" msgstr "Copyright" msgid "Disc Number" msgstr "Číslo disku" msgid "Encoded-by" msgstr "Zakódováno" msgid "Encoder Settings" msgstr "Nastavení kódování" msgid "Encoding Time" msgstr "ÄŒas zakódování" msgid "Grouping" msgstr "ZaÅ™azení do skupin" msgid "Initial Key" msgstr "Tónina na zaÄátku" msgid "ISRC" msgstr "ISRC" msgid "Language" msgstr "Jazyk" msgid "Lyricist" msgstr "TextaÅ™" msgid "Lyrics" msgstr "Text písnÄ›" msgid "Media" msgstr "Medium" msgid "Mood" msgstr "Nálada" msgid "Original Album" msgstr "Původní album" msgid "Original Artist" msgstr "Původní umÄ›lec" msgid "Original Date" msgstr "Původní datum" msgid "Part" msgstr "Část" msgid "Performer" msgstr "ÚÄinkující" msgid "Picture" msgstr "Obrázek" msgid "Publisher" msgstr "Vydavatel" msgid "Release Country" msgstr "ZemÄ› vydání" msgid "Remixer" msgstr "Autor remixů" msgid "Sort Album" msgstr "SetřídÄ›ní alb" msgid "Sort Album Artist" msgstr "SeÅ™azování podle umÄ›lce alba" msgid "Sort Artist" msgstr "SetřídÄ›ní úÄinkujících" msgid "Sort Composer" msgstr "SetřídÄ›ní skladatel" msgid "Sort Name" msgstr "SetřídÄ›ní názvů" msgid "Subtitle" msgstr "Podnázev" msgid "Website" msgstr "Internetové stránky" msgid "WWW Audio File" msgstr "WWW zvukového souboru" msgid "WWW Audio Source" msgstr "WWW zvukového zdroje" msgid "Number of tracks" msgstr "PoÄet skladeb" msgid "Extension" msgstr "Přípona" msgid "Bitrate" msgstr "Datový tok" msgid "VBR" msgstr "VBR" msgid "Samplerate" msgstr "Vzorkovací kmitoÄet" msgid "Channels" msgstr "Kanály" msgid "Codec" msgstr "Kodek" msgid "Escape for HTML" msgstr "Escape HTML" msgid "Automatic Import" msgstr "Automaticky importovat" msgid "D&estination:" msgstr "Cí&l:" msgid "Tag 1 and Tag 2" msgstr "ZnaÄka 1 a znaÄka 2" msgid "&Profile:" msgstr "&Profil:" msgid "&Save Settings" msgstr "&Uložit nastavení" msgid "New" msgstr "Nový" msgid "A&bort" msgstr "&PÅ™eruÅ¡it" msgid "S&tart" msgstr "ZaÄát&ek" msgid "Import Source" msgstr "Zdroj zavedení" msgid "&Server:" msgstr "&Server:" msgid "&Accuracy:" msgstr "&PÅ™esnost:" msgid "&Standard Tags" msgstr "&Standardní znaÄky" msgid "&Additional Tags" msgstr "&DodateÄné znaÄky" msgid "C&over Art" msgstr "&Obrázky obalů" msgid "Browse Cover Art" msgstr "Procházet kvůli obrázkům obalů" msgid "&Artist/Album" msgstr "UmÄ›lec/&Album" msgid "&Source" msgstr "&Zdroj" msgid "Source:" msgstr "Zdroj:" msgid "URL:" msgstr "URL:" msgid "&URL extraction" msgstr "Získání &URL" msgid "Match" msgstr "Porovnání" msgid "Picture URL" msgstr "URL obrázku" msgid "&Browse" msgstr "&Procházet" msgid "Click Browse to start" msgstr "KlepnÄ›te na Procházet pro zapoÄetí" msgid "Then drag the picture from the browser to Kid3." msgstr "Potom pÅ™etáhnÄ›te obrázek z prohlížeÄe do okna Kid3." msgid "ID3v1" msgstr "ID3v1" msgid "&Mark truncated fields" msgstr "&OznaÄit zkrácená pole" msgid "Text &encoding:" msgstr "Kódování t&extu:" msgid "ID3v2" msgstr "ID3v2" msgid "Use &track/total number of tracks format" msgstr "Použít formát Äíslo/celkový poÄet &stop" msgid "Track number &digits:" msgstr "&PoÄet Äíslic v Äísle skladby:" msgid "&Genre as text instead of numeric string" msgstr "Žá&nr jako text místo jako Äíslo" msgid "ISO-8859-1" msgstr "ISO-8859-1" msgid "UTF16" msgstr "UTF16" msgid "UTF8" msgstr "UTF8" msgid "&Version used for new tags:" msgstr "&Verze používaná pro nové znaÄky:" msgid "ID3v2.3.0" msgstr "ID3v2.3.0" msgid "ID3v2.4.0" msgstr "ID3v2.4.0" msgid "Ogg/Vorbis" msgstr "Ogg/Vorbis" msgid "Co&mment field name:" msgstr "Název pole s pozná&mkou:" msgid "&Picture field name:" msgstr "Název pole s &obrázkem:" msgid "Custom &Genres" msgstr "Vlas&tní žánry" msgid "&Show only custom genres" msgstr "&Ukázat pouze vlastní žánry" msgid "&Quick Access Tags" msgstr "ZnaÄky s &rychlým přístupem" msgid "&Tag Format" msgstr "&Formát znaÄky" msgid "Tag &1" msgstr "ZnaÄka &1" msgid "Tag &2" msgstr "ZnaÄka &2" msgid "Tag 1 a&nd Tag 2" msgstr "Z&naÄka 1 a znaÄka 2" msgid "Startup" msgstr "SpuÅ¡tÄ›ní" msgid "&Load last-opened files" msgstr "&Nahrát naposledy otevÅ™ené soubory" msgid "Save" msgstr "Uložit" msgid "&Preserve file timestamp" msgstr "&NemÄ›nit Äasové razítko" msgid "&Mark changes" msgstr "&OznaÄit zmÄ›ny" msgid "F&ilename for cover:" msgstr "&Jméno souboru s obalem:" msgid "&Filename Format" msgstr "Formát &souborového názvu" msgid "Browser" msgstr "ProhlížeÄ" msgid "Web &browser:" msgstr "&ProhlížeÄ internetu:" msgid "Context &Menu Commands" msgstr "Příkazy souvisejících &nabídek" msgid "&Play on double click" msgstr "&PÅ™ehrát po dvojitém poklepání" msgid "Proxy" msgstr "Proxy" msgid "&Proxy:" msgstr "&Proxy:" msgid "&Use authentication with proxy" msgstr "Po&užít ověření pomocí Proxy" msgid "Proxy user &name:" msgstr "Uživatelské &jméno uživatele Proxy:" msgid "Proxy pass&word:" msgstr "&Heslo pro Proxy:" msgid "&Metadata Plugins && Priority" msgstr "&Metadata moduly && Priorita" msgid "A&vailable Plugins" msgstr "Dostupné zásu&vné moduly" msgid "Changes take only effect after a restart!" msgstr "ZmÄ›na se projeví až po restartu!" msgid "From Clip&board" msgstr "Ze s&chránky" msgid "&Import" msgstr "&Zavést" msgid "&Export" msgstr "&Vyvést" msgid "&View" msgstr "&Pohled" msgid "Text Encoding" msgstr "Kódování textu" msgid "Text" msgstr "Text" msgid "Description" msgstr "Popis" msgid "Owner" msgstr "Vlastník" msgid "Email" msgstr "E-mail" msgid "Rating" msgstr "Hodnocení" msgid "Picture Type" msgstr "Druh obrázku" msgid "Image format" msgstr "Formát obrázku" msgid "Mimetype" msgstr "MIME typ" msgid "Counter" msgstr "PoÄítadlo" msgid "Identifier" msgstr "UrÄení" msgid "Volume Adjustment" msgstr "Úprava hlasitosti" msgid "Number of Bits" msgstr "PoÄet bitů" msgid "Volume Change Right" msgstr "ZmÄ›na hlasitosti vpravo" msgid "Volume Change Left" msgstr "ZmÄ›na hlasitosti vlevo" msgid "Peak Volume Right" msgstr "Nejvyšší stupeň hlasitosti vpravo" msgid "Peak Volume Left" msgstr "Nejvyšší stupeň hlasitosti vlevo" msgid "Timestamp Format" msgstr "Formát Äasového razítka" msgid "Content Type" msgstr "Druh obsahu" msgid "Price" msgstr "Cena" msgid "Seller" msgstr "ProdavaÄ" msgid "Images" msgstr "Obrázky" msgid "UTF16BE" msgstr "UTF16BE" msgid "Other" msgstr "Jiný" msgid "32x32 pixels PNG file icon" msgstr "Ikona o velikosti 32x32 pixelů pro soubor PNG" msgid "Other file icon" msgstr "Ikona jiného souboru" msgid "Cover (front)" msgstr "Obal (pÅ™ední strana)" msgid "Cover (back)" msgstr "Obal (zadní strana)" msgid "Leaflet page" msgstr "Strana letáku" msgid "Lead artist/lead performer/soloist" msgstr "Hlavní umÄ›lec/ÚÄinkující/Sólista" msgid "Artist/performer" msgstr "UmÄ›lec/ÚÄinkující" msgid "Band/Orchestra" msgstr "Hudební skupina/Orchestr" msgid "Lyricist/text writer" msgstr "TextaÅ™/Skladatel textu" msgid "Recording Location" msgstr "Místo nahrávky" msgid "During recording" msgstr "BÄ›hem nahrávání" msgid "During performance" msgstr "BÄ›hem vystoupení" msgid "Movie/video screen capture" msgstr "Obrázek z filmu/videa" msgid "A bright coloured fish" msgstr "JasnÄ› svítící ryba" msgid "Illustration" msgstr "Ukázka" msgid "Band/artist logotype" msgstr "Logotyp hudební skupiny/umÄ›lce" msgid "Publisher/Studio logotype" msgstr "Logotyp vydavatele/nahrávacího studia" msgid "MPEG frames as unit" msgstr "Snímky MPEG jako jednotka" msgid "Milliseconds as unit" msgstr "Milisekundy jako jednotka" msgid "Text transcription" msgstr "PÅ™epis textu" msgid "Movement/part name" msgstr "Název vÄ›ty/Äásti" msgid "Events" msgstr "Událost" msgid "Chord" msgstr "Akord" msgid "Trivia/pop up" msgstr "Drobnosti/Vyskakovací" msgid "Export" msgstr "Vyvést" msgid "Format:" msgstr "Formát:" msgid "Header:" msgstr "Záhlaví:" msgid "Tracks:" msgstr "Stopy:" msgid "Footer:" msgstr "Zápatí:" msgid "To F&ile" msgstr "Do sou&boru" msgid "To Clip&board" msgstr "Do sch&ránky" msgid "&Source:" msgstr "&Zdroj:" msgid "File Error" msgstr "Chyba v souboru" msgid "&Filter:" msgstr "&Filtr:" msgid "&Expression:" msgstr "&Výraz:" msgid "&Apply" msgstr "&Použít" msgid "Accuracy:" msgstr "PÅ™esnost:" msgid "Cover Art:" msgstr "Obrázky obalů:" msgid "From F&ile/Clipboard..." msgstr "Zavést ze souboru/s&chránky..." msgid "From T&ags..." msgstr "Ze zn&aÄky..." msgid "&From Server:" msgstr "&Ze serveru:" msgid "Check maximum allowable time &difference (sec):" msgstr "VyzkouÅ¡et nejvÄ›tší možný přípustný Äasový &rozdíl (s):" msgid "Match with:" msgstr "Porovnat s:" msgid "&Length" msgstr "&Délka" msgid "T&rack" msgstr "&Stopa" msgid "&Title" msgstr "&Název" msgid "&Start number:" msgstr "&ZaÄáteÄní Äíslo:" msgid "&Destination:" msgstr "&Cíl:" msgid "&Total number of tracks:" msgstr "Celkový poÄet s&top:" msgid "Create Playlist" msgstr "VytvoÅ™it seznam skladeb" msgid "Playlist File Name" msgstr "Název souboru se seznamem skladeb" msgid "Same as &directory name" msgstr "Stejný jako &název adresáře" msgid "&Format:" msgstr "&Formát:" msgid "Cr&eate in:" msgstr "V&ytvoÅ™it v:" msgid "Current directory" msgstr "SouÄasný adresář" msgid "Every directory" msgstr "Každý adresář" msgid "Top-level directory" msgstr "Adresář na nejvyšší úrovni" msgid "Playlist Content" msgstr "Obsah seznamu skladeb" msgid "For&mat:" msgstr "For&mát:" msgid "Incl&ude only the selected files" msgstr "ZaÅ™&adit pouze vybrané soubory" msgid "Sort by file &name" msgstr "SeÅ™adit podle &názvu souboru" msgid "Sort by &tag field" msgstr "SeÅ™adit podle pole se &znaÄkou" msgid "Use &relative path for files in playlist" msgstr "Pro soubory v seznamu skladeb použít k nim se &vztahující cestu" msgid "Use full p&ath for files in playlist" msgstr "Pro soubory v seznamu skladeb použít úplnou &cestu" msgid "Write only &list of files" msgstr "Zapsat pouze &seznamy souborů" msgid "Write &info using" msgstr "Zapsat ú&daje pomocí" msgid "Preview" msgstr "Náhled" msgid "Create Directory" msgstr "VytvoÅ™it adresář" msgid "From Tag 2 and Tag 1" msgstr "Ze znaÄky 2 a znaÄky 1" msgid "From:" msgstr "Z:" msgid "To:" msgstr "Do:" msgid "&Find" msgstr "&Hledat" msgid "C&GI Path:" msgstr "Cesta C&GI:" msgid "Track Title/Artist - Album" msgstr "Název skladby/UmÄ›lec - Album" msgid "State" msgstr "Stav" msgid "No result" msgstr "Žádný výsledek" msgid "No result selected" msgstr "Nevybrán žádný výsledek" msgid "Import from Tags" msgstr "Zavést ze znaÄek" msgid "Extraction:" msgstr "Získání:" msgid "Import from File/Clipboard" msgstr "Zavést ze souboru/schránky" msgid "From F&ile" msgstr "Ze soubor&u" msgid "Download" msgstr "Stáhnout" msgid "Opening directory..." msgstr "Otevírá se adresář..." msgid "Saving directory..." msgstr "Ukládá se adresář..." msgid "" "The current directory has been modified.\n" "Do you want to save it?" msgstr "" "NynÄ›jší adresář byl zmÄ›nÄ›n.\n" "Chcete jej uložit?" msgid "Warning" msgstr "Varování" msgid "Exiting..." msgstr "UkonÄuje se..." msgid "Creating playlist..." msgstr "Vytváří se seznam skladeb..." msgid "Import" msgstr "Zavést" msgid "Error while renaming:\n" msgstr "BÄ›hem pÅ™ejmenovávání se vyskytla chyba:\n" msgid " [filtered]" msgstr " [filtrován]" msgid "Add Frame" msgstr "PÅ™idat prvek" msgid "Select the frame ID" msgstr "Vybrat ID prvku" msgid "Rename File" msgstr "PÅ™ejmenovat soubor" msgid "Enter new file name:" msgstr "Zadejte nový název souboru:" msgid "Do you really want to move these %1 items to the trash?" msgstr "Opravdu si pÅ™ejete pÅ™esunout tÄ›chto %1 položek do koÅ¡e?" msgid "Do you really want to move this item to the trash?" msgstr "Opravdu si pÅ™ejete pÅ™esunout tuto položku do koÅ¡e?" msgid "Move to Trash" msgstr "PÅ™esunout do koÅ¡e" msgid "Directory must be empty.\n" msgstr "Adresář musí být prázdný.\n" msgid "Could not move these files to the Trash" msgstr "Nelze pÅ™esunout tyto soubory do koÅ¡e" msgid "Expand All" msgstr "Rozbalit vÅ¡e" msgid "&Expand all" msgstr "&Rozbalit vÅ¡e" msgid "&Collapse all" msgstr "&Složit vÅ¡e" msgid "&Rename" msgstr "PÅ™e&jmenovat" msgid "&Move to Trash" msgstr "&PÅ™esunout do koÅ¡e" msgid "Kid3" msgstr "Kid3" msgid "F&ile" msgstr "S&oubor" msgid "Name:" msgstr "Název:" msgid "Filename from Tag 1" msgstr "Název souboru ze znaÄky 1" msgid "Filename from Tag 2" msgstr "Název souboru ze znaÄky 2" msgid "Tag 1 from Filename" msgstr "ZnaÄka 1 z názvu souboru" msgid "Tag 2 from Filename" msgstr "ZnaÄka 2 z názvu souboru" msgid "&Add..." msgstr "&PÅ™idat..." msgid "Move &Up" msgstr "Posunout &nahoru" msgid "Move &Down" msgstr "Posunout &dolů" msgid "&Edit..." msgstr "&Upravit..." msgid "&Remove" msgstr "&Odstranit" msgid "&Insert row" msgstr "&Vložit řádek" msgid "&Delete row" msgstr "&Smazat řádek" msgid "&Clear row" msgstr "Vy&prázdnit řádek" msgid "Format while editing" msgstr "Formátovat bÄ›hem úprav" msgid "Case conversion:" msgstr "Psaní velkých/malých písmen:" msgid "No changes" msgstr "Žádné zmÄ›ny" msgid "All lowercase" msgstr "VÅ¡e malými písmeny" msgid "All uppercase" msgstr "VÅ¡e velkými písmeny" msgid "First letter uppercase" msgstr "První písmeno velké" msgid "All first letters uppercase" msgstr "VÅ¡echna první písmena velká" msgid "Locale:" msgstr "Jazyk:" msgid "None" msgstr "Žádný" msgid "String replacement:" msgstr "Nahrazení Å™etÄ›zce:" msgid "From" msgstr "Z" msgid "To" msgstr "Do" msgid "&Add" msgstr "&PÅ™idat" msgid "&Select all" msgstr "&Vybrat vÅ¡e" msgid "&Deselect all" msgstr "Z&ruÅ¡it výbÄ›r" msgid "View Picture" msgstr "Zobrazit obrázek" msgid "" "Drag album\n" "artwork\n" "here" msgstr "" "PÅ™etáhnout\n" "obal alba\n" "sem" msgid "Play/Pause" msgstr "PÅ™ehrát/Pozastavit" msgid "Stop playback" msgstr "Zastavit pÅ™ehrávání" msgid "Previous Track" msgstr "PÅ™edchozí skladba" msgid "Next Track" msgstr "Další skladba" msgid "Close" msgstr "Zavřít" msgid "Mute" msgstr "Ztlumit" msgid "Volume: %1%" msgstr "Hlasitost: %1%" msgid "Add Item" msgstr "PÅ™idat položku" msgid "Edit Item" msgstr "Upravit položku" msgid "MusicBrainz Fingerprint" msgstr "MusicBrainz Fingerprint" msgid "Unrecognized" msgstr "Nerozpoznáno" msgid "Recognized" msgstr "Rozpoznáno" msgid "User Selection" msgstr "VýbÄ›r" msgid "ID Lookup" msgstr "Hledání ID" msgid "Fingerprint" msgstr "Otisk" msgid "Metadata Lookup" msgstr "Vyhledat Metadata" msgid "Amazon" msgstr "Amazon" msgid "Discogs" msgstr "Discogs" msgid "gnudb.org" msgstr "gnudb.org" msgid "TrackType.org" msgstr "TrackType.org" msgid "AENC - Audio encryption" msgstr "AENC - Kódování zvuku" msgid "APIC - Attached picture" msgstr "APIC - PÅ™ipojený obrázek" msgid "COMM - Comments" msgstr "COMM - Poznámky" msgid "COMR - Commercial" msgstr "COMR - Obchodní informace" msgid "ENCR - Encryption method registration" msgstr "ENCR - Zápis způsobu kódování" msgid "EQUA - Equalization" msgstr "EQUA - Vyrovnání" msgid "ETCO - Event timing codes" msgstr "ETCO - ÄŒasové kódy událostí" msgid "GEOB - General encapsulated object" msgstr "GEOB - VÅ¡eobecný uzavÅ™ený pÅ™edmÄ›t" msgid "GRID - Group identification registration" msgstr "GRID - Zápis urÄení zaÅ™azení do skupin" msgid "IPLS - Involved people list" msgstr "IPLS - Seznam zúÄastnÄ›ných osob" msgid "LINK - Linked information" msgstr "LINK - Spojená informace" msgid "MCDI - Music CD identifier" msgstr "MCDI - UrÄení hudebního CD" msgid "MLLT - MPEG location lookup table" msgstr "MLLT - Vyhledávací tabulka poloh MPEG" msgid "OWNE - Ownership frame" msgstr "OWNE - Vlastník" msgid "PRIV - Private frame" msgstr "PRIV - Soukromé" msgid "PCNT - Play counter" msgstr "PCNT - PoÄítadlo pÅ™ehrání" msgid "POPM - Popularimeter" msgstr "POPM - Měřidlo oblíbenosti" msgid "POSS - Position synchronisation frame" msgstr "POSS - Seřízení polohy" msgid "RBUF - Recommended buffer size" msgstr "RBUF - DoporuÄená velikost vyrovnávací pamÄ›ti" msgid "RVAD - Relative volume adjustment" msgstr "RVAD - PomÄ›rné pÅ™izpůsobení hlasitosti" msgid "RVRB - Reverb" msgstr "RVRB - OzvÄ›na" msgid "SYLT - Synchronized lyric/text" msgstr "SYLT - Souběžný text písnÄ›/text" msgid "SYTC - Synchronized tempo codes" msgstr "SYTC - Souběžné Äasové kódy" msgid "TALB - Album/Movie/Show title" msgstr "TALB - Album/Film/Ukázat název" msgid "TBPM - BPM (beats per minute)" msgstr "TBPM - BPM (dob za minutu)" msgid "TCOM - Composer" msgstr "TCOM - Skladatel" msgid "TCON - Content type" msgstr "TCON - Druh obsahu" msgid "TCOP - Copyright message" msgstr "TCOP - SdÄ›lení o autorském právu" msgid "TDAT - Date" msgstr "TDAT - Datum" msgid "TDLY - Playlist delay" msgstr "TDLY - Zdržení seznamu skladeb" msgid "TENC - Encoded by" msgstr "TENC - Zakódováno" msgid "TEXT - Lyricist/Text writer" msgstr "TEXT - TextaÅ™/Skladatel textu" msgid "TFLT - File type" msgstr "TFLT - Typ souboru" msgid "TIME - Time" msgstr "TIME - ÄŒas" msgid "TIT1 - Content group description" msgstr "TIT1 - Popis skupiny obsahu" msgid "TIT2 - Title/songname/content description" msgstr "TIT2 - Název/Název písnÄ›/Popis obsahu" msgid "TIT3 - Subtitle/Description refinement" msgstr "TIT3 - Jemný rozdíl Podnázev/PÅ™esný popis" msgid "TKEY - Initial key" msgstr "TKEY - Tónina na zaÄátku" msgid "TLAN - Language(s)" msgstr "TLAN - Jazyk(y)" msgid "TLEN - Length" msgstr "TLEN - Délka" msgid "TMED - Media type" msgstr "TMED - Typ média" msgid "TOAL - Original album/movie/show title" msgstr "TOAL - Původní album/Film/Ukázat název" msgid "TOFN - Original filename" msgstr "TOFN - Původní název souboru" msgid "TOLY - Original lyricist(s)/text writer(s)" msgstr "TOLY - Původní textaÅ™(i)/Skladatel(é) textu" msgid "TOPE - Original artist(s)/performer(s)" msgstr "TOPE - Původní umÄ›lec(i)/ÚÄinkující" msgid "TORY - Original release year" msgstr "TORY - Původní rok vydání" msgid "TOWN - File owner/licensee" msgstr "TOWN - Vlastník souboru/Držitel povolení" msgid "TPE1 - Lead performer(s)/Soloist(s)" msgstr "TPE1 - Hlavní umÄ›lec(i)/Sólista(é)" msgid "TPE2 - Band/orchestra/accompaniment" msgstr "TPE2 - Hudební skupina/Orchestr/Doprovod" msgid "TPE3 - Conductor/performer refinement" msgstr "TPE3 - Jemný rozdíl Dirigent/ÚÄinkující" msgid "TPE4 - Interpreted, remixed, or otherwise modified by" msgstr "TPE4 - UmÄ›lecky ztvárnÄ›no, znovu zamícháno nebo jinak zmÄ›nÄ›no od" msgid "TPOS - Part of a set" msgstr "TPOS - Část souboru skladeb" msgid "TPUB - Publisher" msgstr "TPUB - Vydavatel" msgid "TRCK - Track number/Position in set" msgstr "TRCK - Číslo skladby/Poloha v souboru skladeb" msgid "TRDA - Recording dates" msgstr "TRDA - Datum nahrávky" msgid "TRSN - Internet radio station name" msgstr "TRSN - Název internetové rozhlasové stanice" msgid "TRSO - Internet radio station owner" msgstr "TRSO - Vlastník internetové rozhlasové stanice" msgid "TSIZ - Size" msgstr "TSIZ - Velikost" msgid "TSRC - ISRC (international standard recording code)" msgstr "TSRC - ISRC (Obvyklý mezinárodní nahrávací kód)" msgid "TSSE - Software/Hardware and settings used for encoding" msgstr "TSSE - Software/Hardware a nastavení kódování" msgid "TXXX - User defined text information" msgstr "TXXX - Uživatelsky stanovená textová informace" msgid "TYER - Year" msgstr "TYER - Rok" msgid "UFID - Unique file identifier" msgstr "UFID - JedineÄné urÄení souboru" msgid "USER - Terms of use" msgstr "USER - Podmínky užití" msgid "USLT - Unsynchronized lyric/text transcription" msgstr "USLT - Nesouběžný pÅ™epis textu písnÄ›/textu" msgid "WCOM - Commercial information" msgstr "WCOM - Obchodní sdÄ›lení" msgid "WCOP - Copyright/Legal information" msgstr "WCOP - Autorské právo/Zákonná sdÄ›lení" msgid "WOAF - Official audio file webpage" msgstr "WOAF - VeÅ™ejné internetové stránky zvukového souboru" msgid "WOAR - Official artist/performer webpage" msgstr "WOAR - VeÅ™ejné internetové stránky umÄ›lce/úÄinkujícího" msgid "WOAS - Official audio source webpage" msgstr "WOAS - VeÅ™ejné internetové stránky zvukového zdroje" msgid "WORS - Official internet radio station homepage" msgstr "WORS - VeÅ™ejné internetové stránky internetové rozhlasové stanice" msgid "WPAY - Payment" msgstr "WPAY - Platba" msgid "WPUB - Official publisher webpage" msgstr "WPUB - VeÅ™ejné internetové stránky vydavatele" msgid "WXXX - User defined URL link" msgstr "WXXX - Uživatelsky stanovený odkaz na URL" msgid "MusicBrainz Release" msgstr "MusicBrainz Album" msgid "ASPI - Audio seek point index" msgstr "ASPI - Rejstřík s body hledání zvuku" msgid "EQU2 - Equalisation (2)" msgstr "EQU2 - Vyrovnání (2)" msgid "RVA2 - Relative volume adjustment (2)" msgstr "RVA2 - PomÄ›rné pÅ™izpůsobení hlasitosti (2)" msgid "SEEK - Seek frame" msgstr "SEEK - Hledání" msgid "SIGN - Signature frame" msgstr "SIGN - Podpis" msgid "TCMP - iTunes compilation flag" msgstr "TCMP - iTunes kompilací" msgid "TDEN - Encoding time" msgstr "TDEN - ÄŒas zakódování" msgid "TDOR - Original release time" msgstr "TDOR - Původní rok vydání" msgid "TDRC - Recording time" msgstr "TDRC - ÄŒas nahrání" msgid "TDRL - Release time" msgstr "TDRL - ÄŒas vydání" msgid "TDTG - Tagging time" msgstr "TDTG - ÄŒas opatÅ™ení znaÄkami" msgid "TIPL - Involved people list" msgstr "TIPL - Seznam zúÄastnÄ›ných osob" msgid "TMCL - Musician credits list" msgstr "TMCL - Seznam hudebníků" msgid "TMOO - Mood" msgstr "TMOO - Nálada" msgid "TPRO - Produced notice" msgstr "TPRO - Autorské právo výroby" msgid "TSO2 - Album artist sort order" msgstr "TSO2 - SeÅ™azování podle umÄ›lce alba" msgid "TSOA - Album sort order" msgstr "TSOA - SetřídÄ›ní alb" msgid "TSOC - Composer sort order" msgstr "TSOC - SetřídÄ›ní skladatel" msgid "TSOP - Performer sort order" msgstr "TSOP - SetřídÄ›ní úÄinkujících" msgid "TSOT - Title sort order" msgstr "TSOT - SetřídÄ›ní názvů" msgid "TSST - Set subtitle" msgstr "TSST - Podnázev skladby" �����������������������kid3-3.0.2/po/de.po���������������������������������������������������������������������������������0000664�0000000�0000000�00000105330�12246035113�0013713�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# translation of de.po to English # translation of de.po to # Kid3 German Translation. # Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. # # Urs Fleisch <ufleisch@users.sourceforge.net>, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011. # Frank Steinmetzger <Warp_7@gmx.de>, 2011. msgid "" msgstr "" "Project-Id-Version: Kid3 3.0.2\n" "Report-Msgid-Bugs-To: http://sourceforge.net/p/kid3/bugs/\n" "POT-Creation-Date: 2013-09-11 21:22+0200\n" "PO-Revision-Date: 2011-04-26 23:41+0200\n" "Last-Translator: Frank Steinmetzger <Warp_7@gmx.de>\n" "Language-Team: German <kde-i18n-doc@kde.org>\n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Lokalize 1.2\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Language: de\n" "X-Source-Language: en\n" msgid "Timeout" msgstr "Zeitüberschreitung" msgid "Usage:" msgstr "Benutzung:" msgid "Help" msgstr "Hilfe" msgid "Command name" msgstr "Befehlsbezeichnung" msgid "Quit application" msgstr "Beendet die Anwendung" msgid "The current directory has been modified." msgstr "Das aktuelle Verzeichnis wurde verändert." msgid "Type 'exit force' to quit." msgstr "Geben Sie 'exit force' ein, um das Programm zu beenden." msgid "Change directory" msgstr "Verzeichnis wechseln" msgid "%1 does not exist" msgstr "%1 existiert nicht" msgid "Print the filename of the current working directory" msgstr "Den Dateinamen des aktuellen Verzeichnisses ausgeben" msgid "Directory list" msgstr "Verzeichnisliste" msgid "Saves the changed files" msgstr "Speichert die veränderten Dateien" msgid "Error while writing file:\n" msgstr "Fehler während des Schreibens in Datei:\n" msgid "Select file" msgstr "Datei wählen" msgid "%1 not found" msgstr "%1 nicht gefunden" msgid "Select tag" msgstr "Tag auswählen" msgid "Get tag frame" msgstr "Hole Tag-Element" msgid "Set tag frame" msgstr "Setze Tag-Element" msgid "Could not set \"%1\" for %2" msgstr "Konnte nicht \"%1\" setzen für %2" msgid "Revert" msgstr "Zuletzt gespeicherte Fassung" msgid "Import from file or clipboard" msgstr "Von Datei oder Zwischenablage importieren" msgid "Format name" msgstr "Formatname" msgid "%1 not found." msgstr "%1 nicht gefunden." msgid "Available" msgstr "Verfügbar" msgid "Error" msgstr "Fehler" msgid "Automatic import" msgstr "Automatisch importieren" msgid "Profile name" msgstr "Profilname" msgid "Reading Directory" msgstr "Verzeichnis wird gelesen" msgid "Started" msgstr "Begonnen" msgid "Source" msgstr "Quelle" msgid "Querying" msgstr "Abfrage läuft" msgid "Fetching" msgstr "Hole" msgid "Data received" msgstr "Daten empfangen" msgid "Cover" msgstr "Cover" msgid "Finished" msgstr "Fertig" msgid "Aborted" msgstr "Abgebrochen" msgid "Download album cover artwork" msgstr "Cover-Bilder herunterladen" msgid "Export to file or clipboard" msgstr "In Datei oder Zwischenablage exportieren" msgid "Create playlist" msgstr "Stückliste erstellen" msgid "Apply filename format" msgstr "Dateinamenformat anwenden" msgid "Apply tag format" msgstr "Tag-Format anwenden" msgid "Apply text encoding" msgstr "Textkodierung anwenden" msgid "Rename directory" msgstr "Verzeichnis umbenennen" msgid "Number tracks" msgstr "Tracks nummerieren" msgid "Track number" msgstr "Tracknummer" msgid "Filter" msgstr "Filter" msgid "Filter name" msgstr "Filtername" msgid "Convert ID3v2.3 to ID3v2.4" msgstr "ID3v2.3 nach ID3v2.4 konvertieren" msgid "Convert ID3v2.4 to ID3v2.3" msgstr "ID3v2.4 nach ID3v2.3 konvertieren" msgid "Filename from tag" msgstr "Dateiname von Tag" msgid "Tag from filename" msgstr "Tag von Dateiname" msgid "Tag to other tag" msgstr "Tag zu anderem Tag" msgid "Copy" msgstr "Kopieren" msgid "Paste" msgstr "Einfügen" msgid "Remove" msgstr "Entfernen" msgid "Play" msgstr "Wiedergabe" msgid "Parameter" msgstr "Parameter" msgid "File path" msgstr "Dateipfad" msgid "URL" msgstr "URL" msgid "Tag numbers" msgstr "Tagnummern" msgid "Frame name" msgstr "Elementname" msgid "Frame value" msgstr "Elementwert" msgid "Format" msgstr "Format" msgid "Command specific" msgstr "Befehlsspezifisch" msgid "Available Commands" msgstr "Verfügbare Befehle" msgid "File" msgstr "Datei" msgid "Name" msgstr "Name" msgid "Tag 2" msgstr "Tag 2" msgid "Tag 1" msgstr "Tag 1" msgid "Tags" msgstr "Tags" msgid "Unknown command '%1'. Type 'help' for help." msgstr "Unbekannter Befehl '%1'. Geben Sie 'help' ein für Hilfe." msgid "Unknown command '%1', -h for help." msgstr "Unbekannter Befehl '%1', -h für Hilfe." msgid "Files" msgstr "Dateien" msgid "User Actions" msgstr "Benutzerbefehle" msgid "Network" msgstr "Netzwerk" msgid "Plugins" msgstr "Erweiterungen" msgid "Main Toolbar" msgstr "Hauptwerkzeugleiste" msgid "Opens a directory" msgstr "Öffnet ein Verzeichnis" msgid "Opens a recently used directory" msgstr "Öffnet ein kürzlich benutztes Verzeichnis" msgid "Reverts the changes of all or the selected files" msgstr "Macht die Änderungen an allen oder den ausgewählten Dateien rückgängig" msgid "Quits the application" msgstr "Beendet die Anwendung" msgid "Select all files" msgstr "Alles auswählen" msgid "Deselect all files" msgstr "Gesamte Auswahl aufheben" msgid "Configure Shortcuts" msgstr "Kurzbefehle festlegen" msgid "Configure Toolbars" msgstr "Werkzeugleisten einrichten" msgid "Preferences dialog" msgstr "Einstellungen" msgid "O&pen Directory..." msgstr "&Verzeichnis öffnen..." msgid "&Import..." msgstr "&Importieren..." msgid "Import from %1..." msgstr "Importieren von %1..." msgid "Automatic I&mport..." msgstr "Automatisch i&mportieren..." msgid "&Browse Cover Art..." msgstr "Nach Cover-&Bildern suchen..." msgid "&Export..." msgstr "&Exportieren..." msgid "&Create Playlist..." msgstr "Stü&ckliste erstellen..." msgid "Apply &Filename Format" msgstr "Dateinamen&format anwenden" msgid "Apply &Tag Format" msgstr "&Tag-Format anwenden" msgid "Apply Text &Encoding" msgstr "T&extkodierung anwenden" msgid "&Rename Directory..." msgstr "Verzeichnis &umbenennen..." msgid "&Number Tracks..." msgstr "Tracks &nummerieren..." msgid "F&ilter..." msgstr "F&ilter..." msgid "Convert ID3v2.3 to ID3v2.&4" msgstr "ID3v2.3 nach ID3v2.&4 konvertieren" msgid "Convert ID3v2.4 to ID3v2.&3" msgstr "ID3v2.4 nach ID3v2.&3 konvertieren" msgid "&Play" msgstr "&Wiedergabe" msgid "Show &Picture" msgstr "&Bild anzeigen" msgid "Auto &Hide Tags" msgstr "Tags &automatisch ausblenden" msgid "Select All in &Directory" msgstr "Alles im &Verzeichnis auswählen" msgid "&Previous File" msgstr "&Vorangehende Datei" msgid "&Next File" msgstr "&Nächste Datei" msgid "From Filename" msgstr "Von Dateiname" msgid "From Tag 2" msgstr "Von Tag 2" msgid "From Tag 1" msgstr "Von Tag 1" msgid "Frames:" msgstr "Elemente:" msgid "Edit" msgstr "Bearbeiten" msgid "Add" msgstr "Hinzufügen" msgid "Delete" msgstr "Löschen" msgid "Filename" msgstr "Dateiname" msgid "Focus" msgstr "Fokus" msgid "File List" msgstr "Dateiliste" msgid "Directory List" msgstr "Verzeichnisliste" msgid "Configure - Kid3" msgstr "Kid3 einrichten" msgid "Open" msgstr "Öffnen" msgid "Save As" msgstr "Speichern unter" msgid "directory to open" msgstr "Zu öffnendes Verzeichnis" msgid "Kid3 ID3 Tagger" msgstr "Kid3 ID3-Tagger" #. i18n NAME OF TRANSLATORS msgid "Your names" msgstr "Urs Fleisch" #. i18n EMAIL OF TRANSLATORS msgid "Your emails" msgstr "ufleisch@users.sourceforge.net" msgid "&Back" msgstr "&Zurück" msgid "&Forward" msgstr "Nach &vorne" msgid "&Find:" msgstr "&Suchen:" msgid "Find Previous" msgstr "Rückwärts suchen" msgid "Find Next" msgstr "Weitersuchen" msgid "&Close" msgstr "&Schließen" msgid "&Tags" msgstr "&Tags" msgid "&Files" msgstr "&Dateien" msgid "&User Actions" msgstr "&Benutzerbefehle" msgid "&Network" msgstr "&Netzwerk" msgid "&Plugins" msgstr "E&rweiterungen" msgid "&Keyboard Shortcuts" msgstr "Tasten&kürzel" msgid "Use custom app&lication font" msgstr "Benutzerdefinierte &Schrift verwenden" msgid "A&pplication Font..." msgstr "Schr&ift..." msgid "Use custom application &style" msgstr "Benutzerdefinierten &Stil verwenden" msgid "Unknown" msgstr "Unbekannt" msgid "Use native system file &dialogs" msgstr "Native Dateisystem-&Dialoge verwenden" msgid "&Appearance" msgstr "&Erscheinungsbild" msgid "&Help" msgstr "&Hilfe" msgid "&OK" msgstr "&OK" msgid "&Cancel" msgstr "&Abbrechen" msgid "The keyboard shortcut '%1' is already assigned to '%2'." msgstr "Die Tastaturbelegung '%1' ist bereits vergeben für '%2'." msgid "&File" msgstr "&Datei" msgid "&Open..." msgstr "&Öffnen..." msgid "Open &Recent" msgstr "Zu&letzt geöffnete Dateien" msgid "&Save" msgstr "S&peichern" msgid "Re&vert" msgstr "&Zuletzt gespeicherte Fassung" msgid "Import from %1" msgstr "Import von %1" msgid "Browse album cover artwork" msgstr "Nach Cover-Bildern suchen" msgid "Create M3U Playlist" msgstr "M3U-Stückliste erstellen" msgid "&Quit" msgstr "B&eenden" msgid "&Edit" msgstr "&Bearbeiten" msgid "Select &All" msgstr "A&lles auswählen" msgid "Dese&lect" msgstr "Abwäh&len" msgid "Select all files in the current directory" msgstr "Alles im aktuellen Verzeichnis auswählen" msgid "Select previous file" msgstr "Vorangehende Datei" msgid "Select next file" msgstr "Nächste Datei" msgid "&Tools" msgstr "E&xtras" msgid "Apply Filename Format" msgstr "Dateinamenformat anwenden" msgid "Apply Tag Format" msgstr "Tag-Format anwenden" msgid "Apply Text Encoding" msgstr "Textkodierung anwenden" msgid "Rename Directory" msgstr "Verzeichnis umbenennen" msgid "Number Tracks" msgstr "Tracks nummerieren" msgid "&Settings" msgstr "&Einstellungen" msgid "Enables/disables the toolbar" msgstr "Werkzeugleiste anzeigen" msgid "Show &Toolbar" msgstr "&Werkzeugleiste anzeigen" msgid "Enables/disables the statusbar" msgstr "Statusleiste anzeigen" msgid "Show St&atusbar" msgstr "&Statusleiste anzeigen" msgid "Show Picture" msgstr "Bild anzeigen" msgid "Auto Hide Tags" msgstr "Tags automatisch ausblenden" msgid "Configure Kid3" msgstr "Kid3 einrichten" msgid "&Configure Kid3..." msgstr "Kid3 einr&ichten..." msgid "Kid3 Handbook" msgstr "Handbuch zu Kid3" msgid "Kid3 &Handbook" msgstr "&Handbuch zu Kid3" msgid "About Kid3" msgstr "Über Kid3" msgid "&About Kid3" msgstr "&Über Kid3" msgid "About Qt" msgstr "Über Qt" msgid "About &Qt" msgstr "Über &Qt" msgid " [modified]" msgstr " [Geändert]" msgid "Toggle the statusbar..." msgstr "Schalte Statusleiste um..." msgid "Ready." msgstr "Bereit." msgid "&Clear List" msgstr "Liste &leeren" msgid "Clear" msgstr "Leeren" msgid "Reset" msgstr "Zurücksetzen" msgid "Press F2 or double click to edit cell contents." msgstr "F2 drücken oder Doppelklick um Zelleninhalt zu bearbeiten." msgid "Action" msgstr "Aktion" msgid "Shortcut" msgstr "Kurzbefehl" msgid "Server" msgstr "Server" msgid "Accuracy" msgstr "Genauigkeit" msgid "Standard Tags" msgstr "Standard Tags" msgid "Additional Tags" msgstr "Zusätzliche Tags" msgid "Cover Art" msgstr "Cover-Bilder" msgid "Invalid File" msgstr "Ungültige Datei" msgid "Data received: %1" msgstr "Daten empfangen: %1" msgid "Request sent..." msgstr "Anfrage gesendet..." msgid "Year" msgstr "Jahr" msgid "Length" msgstr "Länge" msgid "Filenames" msgstr "Dateinamen" msgid "URLs" msgstr "URLs" msgid "Directory name" msgstr "Verzeichnisname" msgid "Encode as URL" msgstr "Als URL kodieren" msgid "Confirm" msgstr "Nachfragen" msgid "Output" msgstr "Ausgabe" msgid "Command" msgstr "Befehl" msgid "Create directory %1 failed\n" msgstr "Fehler beim Erstellen des Verzeichnisses %1\n" msgid "File %1 already exists\n" msgstr "Datei %1 existiert bereits\n" msgid "%1 is not a directory\n" msgstr "%1 ist kein Verzeichnis\n" msgid "Rename %1 to %2 failed\n" msgstr "Fehler beim Umbenennen von %1 nach %2\n" msgid "%1 already exists\n" msgstr "%1 existiert bereits\n" msgid "%1 is not a file\n" msgstr "%1 ist keine Datei\n" msgid "New directory name is too different\n" msgstr "Der neue Verzeichnisname ist zu verschieden\n" msgid "Create directory" msgstr "Verzeichnis erstellen" msgid "Rename file" msgstr "Datei umbenennen" msgid "C&lear" msgstr "&Löschen" msgid "Execute " msgstr "Ausführen von " msgid "Could not execute " msgstr "Konnte nicht ausführen " msgid "True if strings are equal" msgstr "Wahr wenn die Zeichenketten gleich sind" msgid "True if string contains substring" msgstr "Wahr wenn in Zeichenkette enthalten" msgid "True if string matches regexp" msgstr "Wahr wenn Zeichenkette Regexp erfüllt" msgid "Logical AND" msgstr "Logisches UND" msgid "Logical OR" msgstr "Logisches ODER" msgid "Logical negation" msgstr "Logische Verneinung" msgid "Data" msgstr "Daten" msgid "%1 is not writable" msgstr "%1 ist nicht schreibbar" msgid "All Supported Files" msgstr "Alle unterstützten Dateitypen" msgid "All Files" msgstr "Alle Dateien" msgid "Track" msgstr "Stück" msgid "Absolute path to file" msgstr "Absoluter Dateiname" msgid "Duration" msgstr "Dauer" msgid "Title" msgstr "Titel" msgid "Artist" msgstr "Interpret" msgid "Album" msgstr "Album" msgid "Comment" msgstr "Kommentar" msgid "Date" msgstr "Datum" msgid "Track Number" msgstr "Tracknummer" msgid "Genre" msgstr "Stil" msgid "Album Artist" msgstr "Albuminterpret" msgid "Arranger" msgstr "Arrangierung" msgid "Author" msgstr "Autor" msgid "BPM" msgstr "BPM" msgid "Catalog Number" msgstr "Katalognummer" msgid "Compilation" msgstr "Kompilation" msgid "Composer" msgstr "Komponist" msgid "Conductor" msgstr "Leiter/Dirigent" msgid "Copyright" msgstr "Copyright" msgid "Disc Number" msgstr "CD-Nummer" msgid "Encoded-by" msgstr "Kodiert von" msgid "Encoder Settings" msgstr "Encoder-Einstellungen" msgid "Encoding Time" msgstr "Aufnahmedatum" msgid "Grouping" msgstr "Gruppierung" msgid "Initial Key" msgstr "Anfangstonart" msgid "ISRC" msgstr "ISRC" msgid "Language" msgstr "Sprache" msgid "Lyricist" msgstr "Texter" msgid "Lyrics" msgstr "Text" msgid "Media" msgstr "Medium" msgid "Mood" msgstr "Stimmung" msgid "Original Album" msgstr "Originalalbum" msgid "Original Artist" msgstr "Originalkünstler" msgid "Original Date" msgstr "Originaldatum" msgid "Part" msgstr "Teil" msgid "Performer" msgstr "Interpret" msgid "Picture" msgstr "Bild" msgid "Publisher" msgstr "Herausgeber" msgid "Release Country" msgstr "Veröffentlichungsland" msgid "Remixer" msgstr "Remixer" msgid "Sort Album" msgstr "Album für Sortierung" msgid "Sort Album Artist" msgstr "Albuminterpret für Sortierung" msgid "Sort Artist" msgstr "Interpret für Sortierung" msgid "Sort Composer" msgstr "Komponist für Sortierung" msgid "Sort Name" msgstr "Titel für Sortierung" msgid "Subtitle" msgstr "Untertitel" msgid "Website" msgstr "Webseite" msgid "WWW Audio File" msgstr "WWW der Audio-Datei" msgid "WWW Audio Source" msgstr "WWW der Audio-Quelle" msgid "Number of tracks" msgstr "Anzahl Tracks" msgid "Extension" msgstr "Dateiendung" msgid "Bitrate" msgstr "Bitrate" msgid "VBR" msgstr "VBR" msgid "Samplerate" msgstr "Samplerate" msgid "Channels" msgstr "Kanäle" msgid "Codec" msgstr "Codec" msgid "Escape for HTML" msgstr "Escapes für HTML" msgid "Automatic Import" msgstr "Automatisch importieren" msgid "D&estination:" msgstr "Zi&el:" msgid "Tag 1 and Tag 2" msgstr "Tag 1 und Tag 2" msgid "&Profile:" msgstr "&Profil:" msgid "&Save Settings" msgstr "Einstellungen &speichern" msgid "New" msgstr "Neu" msgid "A&bort" msgstr "A&bbrechen" msgid "S&tart" msgstr "St&arten" msgid "Import Source" msgstr "Import Quelle" msgid "&Server:" msgstr "&Server:" msgid "&Accuracy:" msgstr "Gen&auigkeit:" msgid "&Standard Tags" msgstr "&Standard Tags" msgid "&Additional Tags" msgstr "Zusätzliche T&ags" msgid "C&over Art" msgstr "C&over-Bilder" msgid "Browse Cover Art" msgstr "Nach Cover-Bildern suchen" msgid "&Artist/Album" msgstr "Interpret/&Album" msgid "&Source" msgstr "&Quelle" msgid "Source:" msgstr "Quelle:" msgid "URL:" msgstr "URL:" msgid "&URL extraction" msgstr "&URL-Ermittlung" msgid "Match" msgstr "Übereinstimmung" msgid "Picture URL" msgstr "URL des Bildes" msgid "&Browse" msgstr "&Browser" msgid "Click Browse to start" msgstr "Drücken Sie Browser zum Starten von" msgid "Then drag the picture from the browser to Kid3." msgstr "Dann ziehen Sie das Bild vom Browser ins Kid3-Fenster." msgid "ID3v1" msgstr "ID3v1" msgid "&Mark truncated fields" msgstr "Abgeschnittene Felder &markieren" msgid "Text &encoding:" msgstr "T&extkodierung:" msgid "ID3v2" msgstr "ID3v2" msgid "Use &track/total number of tracks format" msgstr "Format „Nummer/Anzahl Stücke“ benu&tzen" msgid "Track number &digits:" msgstr "&Anzahl der Stellen in Tracknummer:" msgid "&Genre as text instead of numeric string" msgstr "&Stil als Text statt als Nummer" msgid "ISO-8859-1" msgstr "ISO-8859-1" msgid "UTF16" msgstr "UTF16" msgid "UTF8" msgstr "UTF8" msgid "&Version used for new tags:" msgstr "&Version für neue Tags:" msgid "ID3v2.3.0" msgstr "ID3v2.3.0" msgid "ID3v2.4.0" msgstr "ID3v2.4.0" msgid "Ogg/Vorbis" msgstr "Ogg/Vorbis" msgid "Co&mment field name:" msgstr "Na&me des Kommentar-Feldes:" msgid "&Picture field name:" msgstr "Name des &Bild-Feldes:" msgid "Custom &Genres" msgstr "Ei&gene Stile" msgid "&Show only custom genres" msgstr "Nur eigene &Stile anzeigen" msgid "&Quick Access Tags" msgstr "Schnellzug&riff-Tags" msgid "&Tag Format" msgstr "&Tag-Format" msgid "Tag &1" msgstr "Tag &1" msgid "Tag &2" msgstr "Tag &2" msgid "Tag 1 a&nd Tag 2" msgstr "Tag 1 u&nd Tag 2" msgid "Startup" msgstr "Programmstart" msgid "&Load last-opened files" msgstr "Zuletzt geöffnete Dateien &laden" msgid "Save" msgstr "Speichern" msgid "&Preserve file timestamp" msgstr "Datei-Zeitstem&pel nicht verändern" msgid "&Mark changes" msgstr "Änderungen &markieren" msgid "F&ilename for cover:" msgstr "Date&iname für Cover:" msgid "&Filename Format" msgstr "&Dateinamenformat" msgid "Browser" msgstr "Browser" msgid "Web &browser:" msgstr "Web&browser:" msgid "Context &Menu Commands" msgstr "Kontext&menübefehle" msgid "&Play on double click" msgstr "&Wiedergabe per Doppelklick" msgid "Proxy" msgstr "Proxy" msgid "&Proxy:" msgstr "&Proxy:" msgid "&Use authentication with proxy" msgstr "Ben&utze Authentisierung mit Proxy" msgid "Proxy user &name:" msgstr "Benutzer&name für Proxy:" msgid "Proxy pass&word:" msgstr "Pass&wort für Proxy:" msgid "&Metadata Plugins && Priority" msgstr "&Metadaten Plugins && Priorität" msgid "A&vailable Plugins" msgstr "&Verfügbare Erweiterungen" msgid "Changes take only effect after a restart!" msgstr "Änderungen werden erst nach einem Neustart der Anwendung wirksam!" msgid "From Clip&board" msgstr "Von Zwischena&blage" msgid "&Import" msgstr "&Importieren" msgid "&Export" msgstr "&Exportieren" msgid "&View" msgstr "&Ansicht" msgid "Text Encoding" msgstr "Textkodierung" msgid "Text" msgstr "Text" msgid "Description" msgstr "Beschreibung" msgid "Owner" msgstr "Besitzer" msgid "Email" msgstr "E-Mail" msgid "Rating" msgstr "Bewertung" msgid "Picture Type" msgstr "Bildtyp" msgid "Image format" msgstr "Bildformat" msgid "Mimetype" msgstr "MIME-Typ" msgid "Counter" msgstr "Zähler" msgid "Identifier" msgstr "Identifikation" msgid "Volume Adjustment" msgstr "Lautstärkenanpassung" msgid "Number of Bits" msgstr "Anzahl Bits" msgid "Volume Change Right" msgstr "Lautstärkenänderung rechts" msgid "Volume Change Left" msgstr "Lautstärkenänderung links" msgid "Peak Volume Right" msgstr "Höchstlautstärke rechts" msgid "Peak Volume Left" msgstr "Höchstlautstärke links" msgid "Timestamp Format" msgstr "Zeitstempelformat" msgid "Content Type" msgstr "Stil" msgid "Price" msgstr "Preis" msgid "Seller" msgstr "Verkäufer" msgid "Images" msgstr "Bilder" msgid "UTF16BE" msgstr "UTF16BE" msgid "Other" msgstr "Anderes" msgid "32x32 pixels PNG file icon" msgstr "32x32 Pixel PNG-Dateiicon" msgid "Other file icon" msgstr "Anderes Dateiicon" msgid "Cover (front)" msgstr "Cover (Vorderseite)" msgid "Cover (back)" msgstr "Cover (Rückseite)" msgid "Leaflet page" msgstr "Bookletseite" msgid "Lead artist/lead performer/soloist" msgstr "Hauptkünstler/-interpret/-solist" msgid "Artist/performer" msgstr "Künster/Interpret" msgid "Band/Orchestra" msgstr "Band/Orchester" msgid "Lyricist/text writer" msgstr "Texter" msgid "Recording Location" msgstr "Aufnahmeort" msgid "During recording" msgstr "Während Aufnahme" msgid "During performance" msgstr "Während Aufführung" msgid "Movie/video screen capture" msgstr "Bild aus Video" msgid "A bright coloured fish" msgstr "Ein hell leuchtender Fisch" msgid "Illustration" msgstr "Illustration" msgid "Band/artist logotype" msgstr "Band-/Künstlerlogo" msgid "Publisher/Studio logotype" msgstr "Herausgeber-/Studiologo" msgid "MPEG frames as unit" msgstr "MPEG-Frames als Einheit" msgid "Milliseconds as unit" msgstr "Millisekunden als Einheit" msgid "Text transcription" msgstr "Textabschrift" msgid "Movement/part name" msgstr "Name des Satzes/Teils" msgid "Events" msgstr "Ereignis" msgid "Chord" msgstr "Saite" msgid "Trivia/pop up" msgstr "Trivia/Popup" msgid "Export" msgstr "Exportieren" msgid "Format:" msgstr "Format:" msgid "Header:" msgstr "Kopfzeile:" msgid "Tracks:" msgstr "Stücke:" msgid "Footer:" msgstr "Fußzeile:" msgid "To F&ile" msgstr "In Date&i" msgid "To Clip&board" msgstr "In Zwischena&blage" msgid "&Source:" msgstr "&Quelle:" msgid "File Error" msgstr "Dateifehler" msgid "&Filter:" msgstr "&Filter:" msgid "&Expression:" msgstr "&Ausdruck:" msgid "&Apply" msgstr "An&wenden" msgid "Accuracy:" msgstr "Genauigkeit:" msgid "Cover Art:" msgstr "Cover-Bild:" msgid "From F&ile/Clipboard..." msgstr "Von Date&i/Zwischenablage..." msgid "From T&ags..." msgstr "Von T&ags..." msgid "&From Server:" msgstr "&Von Server:" msgid "Check maximum allowable time &difference (sec):" msgstr "Maximal erlaubten Zeitunterschie&d (s) prüfen:" msgid "Match with:" msgstr "Zuordnen nach:" msgid "&Length" msgstr "&Länge" msgid "T&rack" msgstr "Numme&r" msgid "&Title" msgstr "&Titel" msgid "&Start number:" msgstr "&Startnummer:" msgid "&Destination:" msgstr "Zi&el:" msgid "&Total number of tracks:" msgstr "Anzahl S&tücke:" msgid "Create Playlist" msgstr "Stückliste erstellen" msgid "Playlist File Name" msgstr "Dateiname der Stückliste" msgid "Same as &directory name" msgstr "Wie &Verzeichnisname" msgid "&Format:" msgstr "&Format:" msgid "Cr&eate in:" msgstr "&Erstellen in:" msgid "Current directory" msgstr "Aktuellem Verzeichnis" msgid "Every directory" msgstr "Jedem Ordner" msgid "Top-level directory" msgstr "Basisordner" msgid "Playlist Content" msgstr "Inhalt der Wiedergabeliste" msgid "For&mat:" msgstr "For&mat:" msgid "Incl&ude only the selected files" msgstr "N&ur ausgewählte Dateien einfügen" msgid "Sort by file &name" msgstr "Nach Datei&namen sortieren" msgid "Sort by &tag field" msgstr "Nach &Tag-Feld sortieren" msgid "Use &relative path for files in playlist" msgstr "&Relativen Pfad für Dateien in der Wiedergabeliste benutzen" msgid "Use full p&ath for files in playlist" msgstr "Vollen Pf&ad für Dateien in der Wiedergabeliste benutzen" msgid "Write only &list of files" msgstr "Nur &Liste der Dateien schreiben" msgid "Write &info using" msgstr "&Information schreiben" msgid "Preview" msgstr "Vorschau" msgid "Create Directory" msgstr "Verzeichnis erstellen" msgid "From Tag 2 and Tag 1" msgstr "Von Tag 2 und Tag 1" msgid "From:" msgstr "Von:" msgid "To:" msgstr "Nach:" msgid "&Find" msgstr "&Suchen" msgid "C&GI Path:" msgstr "C&GI-Pfad:" msgid "Track Title/Artist - Album" msgstr "Nummer Titel/Interpret - Album" msgid "State" msgstr "Zustand" msgid "No result" msgstr "Kein Resultat" msgid "No result selected" msgstr "Kein Resultat ausgewählt" msgid "Import from Tags" msgstr "Import von Tags" msgid "Extraction:" msgstr "Extraktion:" msgid "Import from File/Clipboard" msgstr "Von Datei/Zwischenablage importieren" msgid "From F&ile" msgstr "Von Date&i" msgid "Download" msgstr "Download" msgid "Opening directory..." msgstr "Öffne Verzeichnis..." msgid "Saving directory..." msgstr "Speichere Verzeichnis..." msgid "" "The current directory has been modified.\n" "Do you want to save it?" msgstr "" "Das aktuelle Verzeichnis wurde verändert.\n" "Soll es gespeichert werden?" msgid "Warning" msgstr "Warnung" msgid "Exiting..." msgstr "Beende..." msgid "Creating playlist..." msgstr "Erstelle Stückliste..." msgid "Import" msgstr "Importieren" msgid "Error while renaming:\n" msgstr "Fehler beim Umbenennen:\n" msgid " [filtered]" msgstr " [Gefiltert]" msgid "Add Frame" msgstr "Element hinzufügen" msgid "Select the frame ID" msgstr "Element-ID auswählen" msgid "Rename File" msgstr "Datei umbenennen" msgid "Enter new file name:" msgstr "Neuer Dateiname:" msgid "Do you really want to move these %1 items to the trash?" msgstr "Möchten Sie diese %1 Einträge wirklich in den Mülleimer werfen?" msgid "Do you really want to move this item to the trash?" msgstr "Möchten Sie diesen Eintrag wirklich in den Mülleimer werfen?" msgid "Move to Trash" msgstr "In den Mülleimer werfen" msgid "Directory must be empty.\n" msgstr "Das Verzeichnis muss leer sein.\n" msgid "Could not move these files to the Trash" msgstr "Die angegebenen Dateien lassen sich nicht in den Mülleimer werfen" msgid "Expand All" msgstr "Alle ausklappen" msgid "&Expand all" msgstr "Alle &ausklappen" msgid "&Collapse all" msgstr "Alle &einklappen" msgid "&Rename" msgstr "&Umbenennen" msgid "&Move to Trash" msgstr "In den &Mülleimer werfen" msgid "Kid3" msgstr "Kid3" msgid "F&ile" msgstr "Date&i" msgid "Name:" msgstr "Name:" msgid "Filename from Tag 1" msgstr "Dateiname von Tag 1" msgid "Filename from Tag 2" msgstr "Dateiname von Tag 2" msgid "Tag 1 from Filename" msgstr "Tag 1 von Dateiname" msgid "Tag 2 from Filename" msgstr "Tag 2 von Dateiname" msgid "&Add..." msgstr "&Hinzufügen..." msgid "Move &Up" msgstr "Nach &oben" msgid "Move &Down" msgstr "Nach &unten" msgid "&Edit..." msgstr "B&earbeiten..." msgid "&Remove" msgstr "&Entfernen" msgid "&Insert row" msgstr "Zeile &einfügen" msgid "&Delete row" msgstr "Zeile ent&fernen" msgid "&Clear row" msgstr "Zeile lös&chen" msgid "Format while editing" msgstr "Während des Editierens formatieren" msgid "Case conversion:" msgstr "Groß-/Kleinschreibung:" msgid "No changes" msgstr "Keine Änderungen" msgid "All lowercase" msgstr "Alles Kleinbuchstaben" msgid "All uppercase" msgstr "Alles Großbuchstaben" msgid "First letter uppercase" msgstr "Den ersten Buchstaben groß" msgid "All first letters uppercase" msgstr "Alle ersten Buchstaben groß" msgid "Locale:" msgstr "Sprache:" msgid "None" msgstr "Keine" msgid "String replacement:" msgstr "Zeichen ersetzen:" msgid "From" msgstr "Von" msgid "To" msgstr "Nach" msgid "&Add" msgstr "&Hinzufügen" msgid "&Select all" msgstr "Alles &auswählen" msgid "&Deselect all" msgstr "Alles abwäh&len" msgid "View Picture" msgstr "Bild anzeigen" msgid "" "Drag album\n" "artwork\n" "here" msgstr "" "Albumcover\n" "hierhin\n" "ziehen" msgid "Play/Pause" msgstr "Wiedergabe/Pause" msgid "Stop playback" msgstr "Wiedergabe stoppen" msgid "Previous Track" msgstr "Zurück" msgid "Next Track" msgstr "Vorwärts" msgid "Close" msgstr "Schließen" msgid "Mute" msgstr "Stumm" msgid "Volume: %1%" msgstr "Lautstärke: %1%" msgid "Add Item" msgstr "Element hinzufügen" msgid "Edit Item" msgstr "Element bearbeiten" msgid "MusicBrainz Fingerprint" msgstr "MusicBrainz-Fingerprint" msgid "Unrecognized" msgstr "Nicht erkannt" msgid "Recognized" msgstr "Erkannt" msgid "User Selection" msgstr "Auswahl" msgid "ID Lookup" msgstr "ID-Suche" msgid "Fingerprint" msgstr "Fingerprint" msgid "Metadata Lookup" msgstr "Lese Metadaten" msgid "Amazon" msgstr "Amazon" msgid "Discogs" msgstr "Discogs" msgid "gnudb.org" msgstr "gnudb.org" msgid "TrackType.org" msgstr "TrackType.org" msgid "AENC - Audio encryption" msgstr "AENC - Audioverschlüsselung" msgid "APIC - Attached picture" msgstr "APIC - Dazugehöriges Bild" msgid "COMM - Comments" msgstr "COMM - Kommentar" msgid "COMR - Commercial" msgstr "COMR - Kommerzielle Information" msgid "ENCR - Encryption method registration" msgstr "ENCR - Verschlüsselungsmethode" msgid "EQUA - Equalization" msgstr "EQUA - Equalization" msgid "ETCO - Event timing codes" msgstr "ETCO - Ereignis-Zeitcodes" msgid "GEOB - General encapsulated object" msgstr "GEOB - Allgemeines gekapseltes Objekt" msgid "GRID - Group identification registration" msgstr "GRID - Gruppierungsidentifikation" msgid "IPLS - Involved people list" msgstr "IPLS - Liste beteiligter Personen" msgid "LINK - Linked information" msgstr "LINK - Verknüpfungsinformation" msgid "MCDI - Music CD identifier" msgstr "MCDI - Musik-CD-Indentifikation" msgid "MLLT - MPEG location lookup table" msgstr "MLLT - MPEG-Positions-Nachschlagetabelle" msgid "OWNE - Ownership frame" msgstr "OWNE - Besitzer" msgid "PRIV - Private frame" msgstr "PRIV - Privat" msgid "PCNT - Play counter" msgstr "PCNT - Abspielzähler" msgid "POPM - Popularimeter" msgstr "POPM - Beliebtheitsmessung" msgid "POSS - Position synchronisation frame" msgstr "POSS - Positionssynchronisierung" msgid "RBUF - Recommended buffer size" msgstr "RBUF - Empfohlene Puffergröße" msgid "RVAD - Relative volume adjustment" msgstr "RVAD - Relative Lautstärkenanpassung" msgid "RVRB - Reverb" msgstr "RVRB - Echo" msgid "SYLT - Synchronized lyric/text" msgstr "SYLT - Synchronisierter Text" msgid "SYTC - Synchronized tempo codes" msgstr "SYTC - Codes für synchronisiertes Tempo" msgid "TALB - Album/Movie/Show title" msgstr "TALB - Album-/Film-/Showtitel" msgid "TBPM - BPM (beats per minute)" msgstr "TBPM - BPM (Schläge pro Minute)" msgid "TCOM - Composer" msgstr "TCOM - Komponist" msgid "TCON - Content type" msgstr "TCON - Stil" msgid "TCOP - Copyright message" msgstr "TCOP - Copyright-Information" msgid "TDAT - Date" msgstr "TDAT - Datum" msgid "TDLY - Playlist delay" msgstr "TDLY - Stücklistenverzögerung" msgid "TENC - Encoded by" msgstr "TENC - Kodiert von" msgid "TEXT - Lyricist/Text writer" msgstr "TEXT - Texter" msgid "TFLT - File type" msgstr "TFLT - Dateityp" msgid "TIME - Time" msgstr "TIME - Zeit" msgid "TIT1 - Content group description" msgstr "TIT1 - Beschreibung der Stilkategorie" msgid "TIT2 - Title/songname/content description" msgstr "TIT2 - Titel/Name/Inhaltsbeschreibung" msgid "TIT3 - Subtitle/Description refinement" msgstr "TIT3 - Untergeordneter Titel/Genauere Beschreibung" msgid "TKEY - Initial key" msgstr "TKEY - Anfangstonart" msgid "TLAN - Language(s)" msgstr "TLAN - Sprache(n)" msgid "TLEN - Length" msgstr "TLEN - Länge" msgid "TMED - Media type" msgstr "TMED - Mediumtyp" msgid "TOAL - Original album/movie/show title" msgstr "TOAL - Originaltitel des Albums/Films/der Show" msgid "TOFN - Original filename" msgstr "TOFN - Originaler Dateiname" msgid "TOLY - Original lyricist(s)/text writer(s)" msgstr "TOLY - Originaler Texter" msgid "TOPE - Original artist(s)/performer(s)" msgstr "TOPE - Originaler Künstler/Interpret" msgid "TORY - Original release year" msgstr "TORY - Originales Erscheinungsjahr" msgid "TOWN - File owner/licensee" msgstr "TOWN - Originaler Dateibesitzer/Lizenznehmer" msgid "TPE1 - Lead performer(s)/Soloist(s)" msgstr "TPE1 - Hauptinterpret(en)/-solist(en)" msgid "TPE2 - Band/orchestra/accompaniment" msgstr "TPE2 - Band/Orchester/Begleitung" msgid "TPE3 - Conductor/performer refinement" msgstr "TPE3 - Leiter/Interpret (genauere Angaben)" msgid "TPE4 - Interpreted, remixed, or otherwise modified by" msgstr "TPE4 - Interpretiert, remixed oder auf andere Weise modifiziert durch" msgid "TPOS - Part of a set" msgstr "TPOS - Teil einer Menge" msgid "TPUB - Publisher" msgstr "TPUB - Herausgeber" msgid "TRCK - Track number/Position in set" msgstr "TRCK - Stücknummer/Position in einer Menge" msgid "TRDA - Recording dates" msgstr "TRDA - Aufnahmedaten" msgid "TRSN - Internet radio station name" msgstr "TRSN - Internetradiosender" msgid "TRSO - Internet radio station owner" msgstr "TRSO - Internetradiosender-Besitzer" msgid "TSIZ - Size" msgstr "TSIZ - Größe" msgid "TSRC - ISRC (international standard recording code)" msgstr "TSRC - ISRC (Internationaler Standardaufnahmecode)" msgid "TSSE - Software/Hardware and settings used for encoding" msgstr "TSSE - Software/Hardware und Einstellungen der Kodierung" msgid "TXXX - User defined text information" msgstr "TXXX - Benutzerdefinierte Textinformation" msgid "TYER - Year" msgstr "TYER - Jahr" msgid "UFID - Unique file identifier" msgstr "UFID - Eindeutige Dateiidentifikation" msgid "USER - Terms of use" msgstr "USER - Benutzungsbedingungen" msgid "USLT - Unsynchronized lyric/text transcription" msgstr "USLT - Unsynchronisierte Textabschrift" msgid "WCOM - Commercial information" msgstr "WCOM - Kommerzielle Information" msgid "WCOP - Copyright/Legal information" msgstr "WCOP - Copyright/Gesetzliche Information" msgid "WOAF - Official audio file webpage" msgstr "WOAF - Offizielle Webseite der Audio-Datei" msgid "WOAR - Official artist/performer webpage" msgstr "WOAR - Offizielle Webseite des Künstlers/Interpreten" msgid "WOAS - Official audio source webpage" msgstr "WOAS - Offizielle Webseite der Audio-Quelle" msgid "WORS - Official internet radio station homepage" msgstr "WORS - Offizielle Webseite der Internetradiostation" msgid "WPAY - Payment" msgstr "WPAY - Bezahlung" msgid "WPUB - Official publisher webpage" msgstr "WPUB - Offizielle Webseite des Herausgebers" msgid "WXXX - User defined URL link" msgstr "WXXX - Benutzerdefinierte URL" msgid "MusicBrainz Release" msgstr "MusicBrainz-Album" msgid "ASPI - Audio seek point index" msgstr "ASPI - Audio-Suchpunkt-Index" msgid "EQU2 - Equalisation (2)" msgstr "EQU2 - Equalisation (2)" msgid "RVA2 - Relative volume adjustment (2)" msgstr "RVA2 - Relative Lautstärkenanpassung (2)" msgid "SEEK - Seek frame" msgstr "SEEK - Suchframe" msgid "SIGN - Signature frame" msgstr "SIGN - Signaturframe" msgid "TCMP - iTunes compilation flag" msgstr "TCMP - iTunes Zusammenstellung" msgid "TDEN - Encoding time" msgstr "TDEN - Kodierdatum" msgid "TDOR - Original release time" msgstr "TDOR - Originales Erscheinungsdatum" msgid "TDRC - Recording time" msgstr "TDRC - Aufnahmedatum" msgid "TDRL - Release time" msgstr "TDRL - Erscheinungsdatum" msgid "TDTG - Tagging time" msgstr "TDTG - Tag-Datum" msgid "TIPL - Involved people list" msgstr "TIPL - Liste beteiligter Personen" msgid "TMCL - Musician credits list" msgstr "TMCL - Musikerzuordnung" msgid "TMOO - Mood" msgstr "TMOO - Stimmung" msgid "TPRO - Produced notice" msgstr "TPRO - Produktions-Urheberrecht" msgid "TSO2 - Album artist sort order" msgstr "TSO2 - Albuminterpret für Sortierung" msgid "TSOA - Album sort order" msgstr "TSOA - Album für Sortierung" msgid "TSOC - Composer sort order" msgstr "TSOC - Komponist für Sortierung" msgid "TSOP - Performer sort order" msgstr "TSOP - Interpret für Sortierung" msgid "TSOT - Title sort order" msgstr "TSOT - Titel für Sortierung" msgid "TSST - Set subtitle" msgstr "TSST - Teil-Untertitel" ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/po/es.po���������������������������������������������������������������������������������0000664�0000000�0000000�00000107404�12246035113�0013736�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# translation of es.po to English # translation of es.po to # Kid3 Spanish Translation. # Copyright (C) 2003, 2004, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. # # Sergio Cambra García <sergio.cambra@hispalinux.es>, 2004. # Urs Fleisch <ufleisch@users.sourceforge.net>, 2005, 2006, 2007, 2008, 2009, 2010, 2011. msgid "" msgstr "" "Project-Id-Version: Kid3 3.0.2\n" "Report-Msgid-Bugs-To: http://sourceforge.net/p/kid3/bugs/\n" "POT-Creation-Date: 2013-09-11 21:22+0200\n" "PO-Revision-Date: 2011-01-15 17:29+0100\n" "Last-Translator: \n" "Language-Team: Spanish <kde-i18n-doc@kde.org>\n" "Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Lokalize 1.1\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Language: es\n" "X-Source-Language: en\n" msgid "Timeout" msgstr "Tiempo de espera" msgid "Usage:" msgstr "Uso:" msgid "Help" msgstr "Ayuda" msgid "Command name" msgstr "Nombre de comando" msgid "Quit application" msgstr "Sale del programa" msgid "The current directory has been modified." msgstr "El directorio actual ha sido modificado." msgid "Type 'exit force' to quit." msgstr "Pulsa 'exit force' para salir." msgid "Change directory" msgstr "Cambiar de directorio" msgid "%1 does not exist" msgstr "%1 no existe" msgid "Print the filename of the current working directory" msgstr "Muestra el nombre del directorio de trabajo actual" msgid "Directory list" msgstr "Lista de directorios" msgid "Saves the changed files" msgstr "Guarda los archivos cambiados" msgid "Error while writing file:\n" msgstr "Error al escribir en el archivo:\n" msgid "Select file" msgstr "Seleccionar archivo" msgid "%1 not found" msgstr "no se encontró %1" msgid "Select tag" msgstr "Seleccionar etiqueta" msgid "Get tag frame" msgstr "Obtener campo d'etiqueta" msgid "Set tag frame" msgstr "Establecer campo d'etiqueta" msgid "Could not set \"%1\" for %2" msgstr "No se pudo establecer \"%1\" para %2" msgid "Revert" msgstr "Deshacer" msgid "Import from file or clipboard" msgstr "Importar de archivo o portapapeles" msgid "Format name" msgstr "Nombre de formato" msgid "%1 not found." msgstr "%1 no encontrado." msgid "Available" msgstr "Disponible" msgid "Error" msgstr "Error" msgid "Automatic import" msgstr "Importar automáticamente" msgid "Profile name" msgstr "Nombre del perfil" msgid "Reading Directory" msgstr "Leyendo directorio" msgid "Started" msgstr "Iniciado" msgid "Source" msgstr "Origen" msgid "Querying" msgstr "Consultando" msgid "Fetching" msgstr "Obteniendo" msgid "Data received" msgstr "Datos recibidos" msgid "Cover" msgstr "Portada" msgid "Finished" msgstr "Finalizado" msgid "Aborted" msgstr "Abortado" msgid "Download album cover artwork" msgstr "Descargar las imágenes de las portadas" msgid "Export to file or clipboard" msgstr "Exportar en archivo o al portapapeles" msgid "Create playlist" msgstr "Crear lista de reproducción" msgid "Apply filename format" msgstr "Aplicar formato de nombre de archivo" msgid "Apply tag format" msgstr "Aplicar formato de etiqueta" msgid "Apply text encoding" msgstr "Aplicar codificación del texto" msgid "Rename directory" msgstr "Renombrar directorio" msgid "Number tracks" msgstr "Numerar pistas" msgid "Track number" msgstr "Número de pista" msgid "Filter" msgstr "Filtro" msgid "Filter name" msgstr "Nombre del filtro" msgid "Convert ID3v2.3 to ID3v2.4" msgstr "Convertir ID3v2.3 en ID3v2.4" msgid "Convert ID3v2.4 to ID3v2.3" msgstr "Convertir ID3v2.4 en ID3v2.3" msgid "Filename from tag" msgstr "Nombre de archivo de etiqueta" msgid "Tag from filename" msgstr "Etiqueta desde nombre de archivo" msgid "Tag to other tag" msgstr "Etiqueta desde otra etiqueta" msgid "Copy" msgstr "Copiar" msgid "Paste" msgstr "Pegar" msgid "Remove" msgstr "Eliminar" msgid "Play" msgstr "Reproducir" msgid "Parameter" msgstr "Parámetro" msgid "File path" msgstr "Ruta del archivo" msgid "URL" msgstr "URL" msgid "Tag numbers" msgstr "Número de etiqueta" msgid "Frame name" msgstr "Nombre de campo" msgid "Frame value" msgstr "Valor de campo" msgid "Format" msgstr "Formato" msgid "Command specific" msgstr "Específicos de la orden" msgid "Available Commands" msgstr "Comandos disponibles" msgid "File" msgstr "Archivo" msgid "Name" msgstr "Nombre" msgid "Tag 2" msgstr "Etiqueta 2" msgid "Tag 1" msgstr "Etiqueta 1" msgid "Tags" msgstr "Etiquetas" msgid "Unknown command '%1'. Type 'help' for help." msgstr "Comando desconocido '%1'. Escribe `help' para ayuda." msgid "Unknown command '%1', -h for help." msgstr "Comando desconocido '%1', -h para ayuda." msgid "Files" msgstr "Archivos" msgid "User Actions" msgstr "Comandos de usuario" msgid "Network" msgstr "Red" msgid "Plugins" msgstr "Extensiones" msgid "Main Toolbar" msgstr "Barra de herramientas Principal" msgid "Opens a directory" msgstr "Abre un directorio" msgid "Opens a recently used directory" msgstr "Abre un directorio usado recientemente" msgid "Reverts the changes of all or the selected files" msgstr "Deshace los cambios de los archivos seleccionados o de todos" msgid "Quits the application" msgstr "Sale del programa" msgid "Select all files" msgstr "Seleccionar todo" msgid "Deselect all files" msgstr "Deseleccionar todo" msgid "Configure Shortcuts" msgstr "Configura los atajos de teclado" msgid "Configure Toolbars" msgstr "Configurar barras de herramientas" msgid "Preferences dialog" msgstr "Diálogo de preferencias" msgid "O&pen Directory..." msgstr "A&brir el directorio..." msgid "&Import..." msgstr "&Importar..." msgid "Import from %1..." msgstr "Importar de %1..." msgid "Automatic I&mport..." msgstr "I&mportar automáticamente..." msgid "&Browse Cover Art..." msgstr "&Descargar las imágenes de las portadas..." msgid "&Export..." msgstr "&Exportar..." msgid "&Create Playlist..." msgstr "&Crear lista de reproducción..." msgid "Apply &Filename Format" msgstr "Aplicar formato de &nombre de archivo" msgid "Apply &Tag Format" msgstr "Aplicar formato de &etiqueta" msgid "Apply Text &Encoding" msgstr "Aplicar codificación del t&exto" msgid "&Rename Directory..." msgstr "&Renombrar un directorio..." msgid "&Number Tracks..." msgstr "&Asignar números de pista por orden..." msgid "F&ilter..." msgstr "F&iltro..." msgid "Convert ID3v2.3 to ID3v2.&4" msgstr "Convertir ID3v2.3 en ID3v2.&4" msgid "Convert ID3v2.4 to ID3v2.&3" msgstr "Convertir ID3v2.4 en ID3v2.&3" msgid "&Play" msgstr "&Reproducir" msgid "Show &Picture" msgstr "Mostrar &imagen" msgid "Auto &Hide Tags" msgstr "&Ocultar automáticamente" msgid "Select All in &Directory" msgstr "Seleccionar todos los archivos en el &directorio" msgid "&Previous File" msgstr "&Anterior archivo" msgid "&Next File" msgstr "&Siguiente archivo" msgid "From Filename" msgstr "Desde nombre de archivo" msgid "From Tag 2" msgstr "De etiqueta 2" msgid "From Tag 1" msgstr "De etiqueta 1" msgid "Frames:" msgstr "Campos:" msgid "Edit" msgstr "Editar" msgid "Add" msgstr "Añadir" msgid "Delete" msgstr "Eliminar" msgid "Filename" msgstr "Nombre de archivo" msgid "Focus" msgstr "Foco" msgid "File List" msgstr "Lista de Archivos" msgid "Directory List" msgstr "Lista de directorios" msgid "Configure - Kid3" msgstr "Configurar - Kid3" msgid "Open" msgstr "Abrir" msgid "Save As" msgstr "Guardar como" msgid "directory to open" msgstr "Directorio a abrir" msgid "Kid3 ID3 Tagger" msgstr "Editor de etiquetas ID3 Kid3" #. i18n NAME OF TRANSLATORS msgid "Your names" msgstr "Sergio Cambra" #. i18n EMAIL OF TRANSLATORS msgid "Your emails" msgstr "runico@users.berlios.de" msgid "&Back" msgstr "&Atrás" msgid "&Forward" msgstr "A&delante" msgid "&Find:" msgstr "&Buscar:" msgid "Find Previous" msgstr "Buscar anterior" msgid "Find Next" msgstr "Buscar siguiente" msgid "&Close" msgstr "&Cerrar" msgid "&Tags" msgstr "E&tiquetas" msgid "&Files" msgstr "&Archivos" msgid "&User Actions" msgstr "Comandos de &usuario" msgid "&Network" msgstr "&Red" msgid "&Plugins" msgstr "&Extensiones" msgid "&Keyboard Shortcuts" msgstr "&Combinaciones de teclas" msgid "Use custom app&lication font" msgstr "Usar tipografía persona&lizada" msgid "A&pplication Font..." msgstr "Ti&pografía personalizada..." msgid "Use custom application &style" msgstr "Usar e&stilo personalizado" msgid "Unknown" msgstr "Desconocido" msgid "Use native system file &dialogs" msgstr "Usar &diálogos de archivo nativos del sistema" msgid "&Appearance" msgstr "&Aspecto" msgid "&Help" msgstr "A&yuda" msgid "&OK" msgstr "Acep&tar" msgid "&Cancel" msgstr "&Cancelar" msgid "The keyboard shortcut '%1' is already assigned to '%2'." msgstr "El acceso directo de teclado '%1' ya está asignado a '%2'." msgid "&File" msgstr "&Archivo" msgid "&Open..." msgstr "&Abrir..." msgid "Open &Recent" msgstr "Abrir &reciente" msgid "&Save" msgstr "&Guardar" msgid "Re&vert" msgstr "&Deshacer" msgid "Import from %1" msgstr "Importar de %1" msgid "Browse album cover artwork" msgstr "Descargar las imágenes de las portadas" msgid "Create M3U Playlist" msgstr "Crear lista m3u" msgid "&Quit" msgstr "&Salir" msgid "&Edit" msgstr "&Editar" msgid "Select &All" msgstr "Seleccionar &todo" msgid "Dese&lect" msgstr "Dese&leccionar" msgid "Select all files in the current directory" msgstr "Seleccionar todos los archivos en el directorio actual" msgid "Select previous file" msgstr "Anterior archivo" msgid "Select next file" msgstr "Siguiente archivo" msgid "&Tools" msgstr "&Herramientas" msgid "Apply Filename Format" msgstr "Aplicar formato de nombre de archivo" msgid "Apply Tag Format" msgstr "Aplicar formato de etiqueta" msgid "Apply Text Encoding" msgstr "Aplicar codificación del texto" msgid "Rename Directory" msgstr "Renombrar directorio" msgid "Number Tracks" msgstr "Numerar pistas" msgid "&Settings" msgstr "&Preferencias" msgid "Enables/disables the toolbar" msgstr "Activa/desactiva la barra de herramientas" msgid "Show &Toolbar" msgstr "Mostrar barra de &herramientas" msgid "Enables/disables the statusbar" msgstr "Activa/desactiva la barra de estado" msgid "Show St&atusbar" msgstr "Mostrar barra de &estado" msgid "Show Picture" msgstr "Mostrar imagen" msgid "Auto Hide Tags" msgstr "Ocultar automáticamente" msgid "Configure Kid3" msgstr "Configurar Kid3" msgid "&Configure Kid3..." msgstr "&Configurar Kid3..." msgid "Kid3 Handbook" msgstr "Manual de Kid3" msgid "Kid3 &Handbook" msgstr "&Manual de Kid3" msgid "About Kid3" msgstr "Acerca de Kid3" msgid "&About Kid3" msgstr "&Acerca de Kid3" msgid "About Qt" msgstr "Acerca de Qt" msgid "About &Qt" msgstr "Acerca de &Qt" msgid " [modified]" msgstr " [modificado]" msgid "Toggle the statusbar..." msgstr "Conmutando la barra de estado..." msgid "Ready." msgstr "Listo." msgid "&Clear List" msgstr "&Limpiar lista" msgid "Clear" msgstr "Limpiar" msgid "Reset" msgstr "Reiniciar" msgid "Press F2 or double click to edit cell contents." msgstr "Pulse F2 o haga doble clic para editar los contenidos de la celda." msgid "Action" msgstr "Acción" msgid "Shortcut" msgstr "Acceso rápido" msgid "Server" msgstr "Servidor" msgid "Accuracy" msgstr "Precisión" msgid "Standard Tags" msgstr "Etiquetas estándar" msgid "Additional Tags" msgstr "Etiquetas adicionales" msgid "Cover Art" msgstr "Imágenes de las portadas" msgid "Invalid File" msgstr "Archivo no válido" msgid "Data received: %1" msgstr "Datos recibidos: %1" msgid "Request sent..." msgstr "Petición enviada..." msgid "Year" msgstr "Año" msgid "Length" msgstr "Longitud" msgid "Filenames" msgstr "Nombres de archivos" msgid "URLs" msgstr "URLs" msgid "Directory name" msgstr "Nombre de directorio" msgid "Encode as URL" msgstr "Codificación URL" msgid "Confirm" msgstr "Confirmar" msgid "Output" msgstr "Salida" msgid "Command" msgstr "Comando" msgid "Create directory %1 failed\n" msgstr "Fallo al crear el directorio %1\n" msgid "File %1 already exists\n" msgstr "El archivo %1 ya existe\n" msgid "%1 is not a directory\n" msgstr "%1 no es un directorio\n" msgid "Rename %1 to %2 failed\n" msgstr "Fallo al renombrar %1 a %2\n" msgid "%1 already exists\n" msgstr "%1 ya existe\n" msgid "%1 is not a file\n" msgstr "%1 no es un archivo\n" msgid "New directory name is too different\n" msgstr "El nuevo nombre del directorio es demasiado diferente\n" msgid "Create directory" msgstr "Crear directorio" msgid "Rename file" msgstr "Renombrar archivo" msgid "C&lear" msgstr "&Limpiar" msgid "Execute " msgstr "Ejecutar " msgid "Could not execute " msgstr "No se pudo ejecutar " msgid "True if strings are equal" msgstr "Las cadenas son iguales" msgid "True if string contains substring" msgstr "La cadena contiene subcadena" msgid "True if string matches regexp" msgstr "La cadena coincida con EXPREG" msgid "Logical AND" msgstr "La función lógica AND" msgid "Logical OR" msgstr "La función lógica OR" msgid "Logical negation" msgstr "La función lógica NOT" msgid "Data" msgstr "Datos" msgid "%1 is not writable" msgstr "El archivo %1 no es escribible" msgid "All Supported Files" msgstr "Todos los archivos soportados" msgid "All Files" msgstr "Todos los archivos" msgid "Track" msgstr "Pista" msgid "Absolute path to file" msgstr "Nombre de fichero absoluto" msgid "Duration" msgstr "Duración" msgid "Title" msgstr "Título" msgid "Artist" msgstr "Artista" msgid "Album" msgstr "Ãlbum" msgid "Comment" msgstr "Comentario" msgid "Date" msgstr "Fecha" msgid "Track Number" msgstr "Número de pista" msgid "Genre" msgstr "Género" msgid "Album Artist" msgstr "Artista del álbum" msgid "Arranger" msgstr "Refundidor" msgid "Author" msgstr "Autor" msgid "BPM" msgstr "BPM" msgid "Catalog Number" msgstr "Número de catálogo" msgid "Compilation" msgstr "Compilación" msgid "Composer" msgstr "Compositor" msgid "Conductor" msgstr "Director" msgid "Copyright" msgstr "Copyright" msgid "Disc Number" msgstr "Número de CD" msgid "Encoded-by" msgstr "Codificado por" msgid "Encoder Settings" msgstr "Opciones de codificador" msgid "Encoding Time" msgstr "Fechas de codificación" msgid "Grouping" msgstr "Agrupación" msgid "Initial Key" msgstr "Clave inicial" msgid "ISRC" msgstr "ISRC" msgid "Language" msgstr "Idioma" msgid "Lyricist" msgstr "Autor" msgid "Lyrics" msgstr "Letras" msgid "Media" msgstr "Medio" msgid "Mood" msgstr "Ambiente" msgid "Original Album" msgstr "Título original de álbum" msgid "Original Artist" msgstr "Artista original" msgid "Original Date" msgstr "Fecha original" msgid "Part" msgstr "Parte" msgid "Performer" msgstr "Intérprete" msgid "Picture" msgstr "Imagen" msgid "Publisher" msgstr "Editorial" msgid "Release Country" msgstr "País de lanzamiento" msgid "Remixer" msgstr "Remixer" msgid "Sort Album" msgstr "Clasificación de álbumes" msgid "Sort Album Artist" msgstr "Clasificación de artistas del álbum" msgid "Sort Artist" msgstr "Clasificación de interprétes" msgid "Sort Composer" msgstr "Clasificación de compositores" msgid "Sort Name" msgstr "Clasificación de títulos" msgid "Subtitle" msgstr "Subtítulo" msgid "Website" msgstr "Página web" msgid "WWW Audio File" msgstr "WWW del archivo de audio" msgid "WWW Audio Source" msgstr "WWW del fuente del audio" msgid "Number of tracks" msgstr "Número de pistas" msgid "Extension" msgstr "Extensión del archivo" msgid "Bitrate" msgstr "Tasa de bits" msgid "VBR" msgstr "VBR" msgid "Samplerate" msgstr "Muestreo" msgid "Channels" msgstr "Canales" msgid "Codec" msgstr "Codec" msgid "Escape for HTML" msgstr "Escape HTML" msgid "Automatic Import" msgstr "Importar automáticamente" msgid "D&estination:" msgstr "D&estino:" msgid "Tag 1 and Tag 2" msgstr "Etiqueta 1 y etiqueta 2" msgid "&Profile:" msgstr "&Perfil:" msgid "&Save Settings" msgstr "&Guardar configuración" msgid "New" msgstr "Nuevo" msgid "A&bort" msgstr "A&bortar" msgid "S&tart" msgstr "&Iniciar" msgid "Import Source" msgstr "Origen de la importación" msgid "&Server:" msgstr "&Servidor:" msgid "&Accuracy:" msgstr "&Precisión:" msgid "&Standard Tags" msgstr "Etiquetas e&stándar" msgid "&Additional Tags" msgstr "Etiquetas &adicionales" msgid "C&over Art" msgstr "&Imágenes de las portadas" msgid "Browse Cover Art" msgstr "Descargar las imágenes de las portadas" msgid "&Artist/Album" msgstr "&Artista/Ãlbum" msgid "&Source" msgstr "&Origen" msgid "Source:" msgstr "Origen:" msgid "URL:" msgstr "URL:" msgid "&URL extraction" msgstr "Reemplazar &URL" msgid "Match" msgstr "Coincidencia" msgid "Picture URL" msgstr "URL de imagen" msgid "&Browse" msgstr "&Navegador" msgid "Click Browse to start" msgstr "Clic Navegador para iniciar" msgid "Then drag the picture from the browser to Kid3." msgstr "" "Entonces arrastre el imagen desde el navedagor dentro de la ventana Kid3." msgid "ID3v1" msgstr "ID3v1" msgid "&Mark truncated fields" msgstr "&Marcar campos truncados" msgid "Text &encoding:" msgstr "Codificación del t&exto:" msgid "ID3v2" msgstr "ID3v2" msgid "Use &track/total number of tracks format" msgstr "Usar el formato pis&ta/número de pistas" msgid "Track number &digits:" msgstr "Número de &dígitos en pista:" msgid "&Genre as text instead of numeric string" msgstr "&Género como texto en vez de número" msgid "ISO-8859-1" msgstr "ISO-8859-1" msgid "UTF16" msgstr "UTF16" msgid "UTF8" msgstr "UTF8" msgid "&Version used for new tags:" msgstr "&Versión utilizada para nuevas etiquetas:" msgid "ID3v2.3.0" msgstr "ID3v2.3.0" msgid "ID3v2.4.0" msgstr "ID3v2.4.0" msgid "Ogg/Vorbis" msgstr "Ogg/Vorbis" msgid "Co&mment field name:" msgstr "No&mbre de campo comentario:" msgid "&Picture field name:" msgstr "Nombre de campo &imagen:" msgid "Custom &Genres" msgstr "&Géneros personalizados" msgid "&Show only custom genres" msgstr "&Mostrar sólo géneros personalizados" msgid "&Quick Access Tags" msgstr "Etiquetas de acceso &rápido" msgid "&Tag Format" msgstr "&Formato de etiqueta" msgid "Tag &1" msgstr "Etiqueta &1" msgid "Tag &2" msgstr "Etiqueta &2" msgid "Tag 1 a&nd Tag 2" msgstr "Etiqueta 1 &y etiqueta 2" msgid "Startup" msgstr "Inicio" msgid "&Load last-opened files" msgstr "Cargar &los últimos archivos abiertos" msgid "Save" msgstr "Guardar" msgid "&Preserve file timestamp" msgstr "Co&piar las marcas de tiempo de modificación" msgid "&Mark changes" msgstr "&Marcar cambios" msgid "F&ilename for cover:" msgstr "Nombre de archivo de la &imagen:" msgid "&Filename Format" msgstr "Formato de &nombre de archivo" msgid "Browser" msgstr "Navegador" msgid "Web &browser:" msgstr "&Navegador web:" msgid "Context &Menu Commands" msgstr "Comandos en el &menú contextual" msgid "&Play on double click" msgstr "&Reproducir del doble clic" msgid "Proxy" msgstr "Proxy" msgid "&Proxy:" msgstr "&Proxy:" msgid "&Use authentication with proxy" msgstr "&Usar autenticación con el proxy" msgid "Proxy user &name:" msgstr "&Nombre de usuario del proxy:" msgid "Proxy pass&word:" msgstr "Contra&seña del proxy:" msgid "&Metadata Plugins && Priority" msgstr "Extensiones &Metadatos && Prioridad" msgid "A&vailable Plugins" msgstr "Extensiones &disponibles" msgid "Changes take only effect after a restart!" msgstr "¡Los cambios tendrán efecto en la siguiente ejecución!" msgid "From Clip&board" msgstr "Del &portapapeles" msgid "&Import" msgstr "&Importar" msgid "&Export" msgstr "&Exportar" msgid "&View" msgstr "&Ver" msgid "Text Encoding" msgstr "Codificación del texto" msgid "Text" msgstr "Texto" msgid "Description" msgstr "Descripción" msgid "Owner" msgstr "Propietario" msgid "Email" msgstr "Correo electrónico" msgid "Rating" msgstr "Puntuación" msgid "Picture Type" msgstr "Tipo de dibujo" msgid "Image format" msgstr "Formato de imagen" msgid "Mimetype" msgstr "Tipo MIME" msgid "Counter" msgstr "Contador" msgid "Identifier" msgstr "Identificador" msgid "Volume Adjustment" msgstr "Ajuste de volumen" msgid "Number of Bits" msgstr "Número de bits" msgid "Volume Change Right" msgstr "Cambio de volumen del canal derecho" msgid "Volume Change Left" msgstr "Cambio de volumen del canal izquierdo" msgid "Peak Volume Right" msgstr "Pico de volumen del canal derecho" msgid "Peak Volume Left" msgstr "Pico de volumen del canal izquierdo" msgid "Timestamp Format" msgstr "Formato de fecha y hora" msgid "Content Type" msgstr "Tipo de contenido" msgid "Price" msgstr "Precio" msgid "Seller" msgstr "Vendedor" msgid "Images" msgstr "Imágenes" msgid "UTF16BE" msgstr "UTF16BE" msgid "Other" msgstr "Otro" msgid "32x32 pixels PNG file icon" msgstr "Archivo de icono PNG de 32x32 pixels" msgid "Other file icon" msgstr "Otro archivo de icono" msgid "Cover (front)" msgstr "Portada (frontal)" msgid "Cover (back)" msgstr "Portada (trasera)" msgid "Leaflet page" msgstr "Página de folleto" msgid "Lead artist/lead performer/soloist" msgstr "Líder/solista" msgid "Artist/performer" msgstr "Artista/intérprete" msgid "Band/Orchestra" msgstr "Banda/Orquesta" msgid "Lyricist/text writer" msgstr "Autor" msgid "Recording Location" msgstr "Localización de grabación" msgid "During recording" msgstr "Duración de la grabación" msgid "During performance" msgstr "Duración de la interpretación" msgid "Movie/video screen capture" msgstr "Captura de video/película" msgid "A bright coloured fish" msgstr "Un pez de colores brillante" msgid "Illustration" msgstr "Ilustración" msgid "Band/artist logotype" msgstr "Logotipo del artista/banda" msgid "Publisher/Studio logotype" msgstr "Logotipo del estudio/editorial" msgid "MPEG frames as unit" msgstr "Muestras MPEG como unidad" msgid "Milliseconds as unit" msgstr "Milisegundos como unidad" msgid "Text transcription" msgstr "Transcripción del texto" msgid "Movement/part name" msgstr "Nombre de parte/movimiento" msgid "Events" msgstr "Actos" msgid "Chord" msgstr "Acorde" msgid "Trivia/pop up" msgstr "Trivia/pop up" msgid "Export" msgstr "Exportar" msgid "Format:" msgstr "Formato:" msgid "Header:" msgstr "Cabecera:" msgid "Tracks:" msgstr "Pistas:" msgid "Footer:" msgstr "Pie de página:" msgid "To F&ile" msgstr "En Arch&ivo" msgid "To Clip&board" msgstr "Al &portapapeles" msgid "&Source:" msgstr "&Origen:" msgid "File Error" msgstr "Error en archivo" msgid "&Filter:" msgstr "&Filtro:" msgid "&Expression:" msgstr "&Expresión:" msgid "&Apply" msgstr "&Aplicar" msgid "Accuracy:" msgstr "Precisión:" msgid "Cover Art:" msgstr "Imágenes de las portadas:" msgid "From F&ile/Clipboard..." msgstr "De archivo/&portapapeles..." msgid "From T&ags..." msgstr "De etiquet&a..." msgid "&From Server:" msgstr "&De servidor:" msgid "Check maximum allowable time &difference (sec):" msgstr "Comprobar máxima &diferencia de tiempo permitida (seg):" msgid "Match with:" msgstr "Concuerda con:" msgid "&Length" msgstr "&Longitud" msgid "T&rack" msgstr "&Pista" msgid "&Title" msgstr "&Título" msgid "&Start number:" msgstr "&Número primero:" msgid "&Destination:" msgstr "D&estino:" msgid "&Total number of tracks:" msgstr "Número de pis&tas:" msgid "Create Playlist" msgstr "Crear lista de reproducción" msgid "Playlist File Name" msgstr "Nombre de la lista de reproducción" msgid "Same as &directory name" msgstr "Igual que el &directorio" msgid "&Format:" msgstr "&Formato:" msgid "Cr&eate in:" msgstr "Cr&ear en:" msgid "Current directory" msgstr "Directorio actual" msgid "Every directory" msgstr "Cada directorio" msgid "Top-level directory" msgstr "Directorio superior" msgid "Playlist Content" msgstr "Contenido de la lista de reproducción" msgid "For&mat:" msgstr "For&mato:" msgid "Incl&ude only the selected files" msgstr "Incl&uir sólo los archivos seleccionados" msgid "Sort by file &name" msgstr "Ordenar por &nombre" msgid "Sort by &tag field" msgstr "Ordenar por &tag" msgid "Use &relative path for files in playlist" msgstr "Usar ruta &relativa para los archivos en la lista" msgid "Use full p&ath for files in playlist" msgstr "Usar ruta complet&a para los archivos en la lista" msgid "Write only &list of files" msgstr "Escribir sólo la &lista de archivos" msgid "Write &info using" msgstr "Escribir &información usando" msgid "Preview" msgstr "Vista previa" msgid "Create Directory" msgstr "Crear directorio" msgid "From Tag 2 and Tag 1" msgstr "De etiqueta 2 y etiqueta 1" msgid "From:" msgstr "De:" msgid "To:" msgstr "A:" msgid "&Find" msgstr "&Buscar" msgid "C&GI Path:" msgstr "Ruta de C&GI:" msgid "Track Title/Artist - Album" msgstr "Número Título/Artista - Ãlbum" msgid "State" msgstr "Estado" msgid "No result" msgstr "Ningún resultado" msgid "No result selected" msgstr "Ningún resultado seleccionado" msgid "Import from Tags" msgstr "Importar de etiquetas" msgid "Extraction:" msgstr "Extracción:" msgid "Import from File/Clipboard" msgstr "Importar de archivo/portapapeles" msgid "From F&ile" msgstr "De Arch&ivo" msgid "Download" msgstr "Descarga" msgid "Opening directory..." msgstr "Abrir directorio..." msgid "Saving directory..." msgstr "Guardando directorio..." msgid "" "The current directory has been modified.\n" "Do you want to save it?" msgstr "" "El directorio actual ha sido modificado.\n" "¿Desea guardarlo?" msgid "Warning" msgstr "Advertencia" msgid "Exiting..." msgstr "Saliendo..." msgid "Creating playlist..." msgstr "Creando lista de reproducción..." msgid "Import" msgstr "Importar" msgid "Error while renaming:\n" msgstr "Error mientras se renombra:\n" msgid " [filtered]" msgstr " [filtrado]" msgid "Add Frame" msgstr "Añadir campo" msgid "Select the frame ID" msgstr "Seleccionar el identificador de campo" msgid "Rename File" msgstr "Renombrar archivo" msgid "Enter new file name:" msgstr "Da un nuevo nombre de archivo:" msgid "Do you really want to move these %1 items to the trash?" msgstr "¿Esta seguro que quiere mover estos %1 objetos a la basura?" msgid "Do you really want to move this item to the trash?" msgstr "¿Esta seguro que quiere mover este objeto a la basura?" msgid "Move to Trash" msgstr "Mover a la papelera" msgid "Directory must be empty.\n" msgstr "El directorio debe estar vacío.\n" msgid "Could not move these files to the Trash" msgstr "No puede mover algunos de los elementos a la papelera" msgid "Expand All" msgstr "Expandir todo" msgid "&Expand all" msgstr "&Expandir todo" msgid "&Collapse all" msgstr "&Plegar todo" msgid "&Rename" msgstr "&Renombrar" msgid "&Move to Trash" msgstr "&Mover a la papelera" msgid "Kid3" msgstr "Kid3" msgid "F&ile" msgstr "Arch&ivo" msgid "Name:" msgstr "Nombre:" msgid "Filename from Tag 1" msgstr "Nombre de archivo de etiqueta 1" msgid "Filename from Tag 2" msgstr "Nombre de archivo de etiqueta 2" msgid "Tag 1 from Filename" msgstr "Etiqueta 1 desde nombre de archivo" msgid "Tag 2 from Filename" msgstr "Etiqueta 2 desde nombre de archivo" msgid "&Add..." msgstr "&Añadir..." msgid "Move &Up" msgstr "S&ubir" msgid "Move &Down" msgstr "&Bajar" msgid "&Edit..." msgstr "&Editar..." msgid "&Remove" msgstr "&Eliminar" msgid "&Insert row" msgstr "&Insertar fila" msgid "&Delete row" msgstr "&Eliminar fila" msgid "&Clear row" msgstr "&Limpiar fila" msgid "Format while editing" msgstr "Formatear mientras se edita" msgid "Case conversion:" msgstr "Mayúsculas / minúsculas:" msgid "No changes" msgstr "Sin cambios" msgid "All lowercase" msgstr "Todas minúsculas" msgid "All uppercase" msgstr "Todas mayúsculas" msgid "First letter uppercase" msgstr "Primera letra mayúscula" msgid "All first letters uppercase" msgstr "Todas las primeras letras mayúsculas" msgid "Locale:" msgstr "Locale:" msgid "None" msgstr "Ninguno" msgid "String replacement:" msgstr "Reemplazar cadenas:" msgid "From" msgstr "De" msgid "To" msgstr "A" msgid "&Add" msgstr "&Añadir" msgid "&Select all" msgstr "&Seleccionar todo" msgid "&Deselect all" msgstr "&Deseleccionar todo" msgid "View Picture" msgstr "Ver Imagen" msgid "" "Drag album\n" "artwork\n" "here" msgstr "" "Arrastre la\n" "carátula del\n" "álbum aquí" msgid "Play/Pause" msgstr "Reproducir/Pausa" msgid "Stop playback" msgstr "Parar reproducción" msgid "Previous Track" msgstr "Pista anterior" msgid "Next Track" msgstr "Pista siguiente" msgid "Close" msgstr "Cerrar" msgid "Mute" msgstr "Silencio" msgid "Volume: %1%" msgstr "Volumen: %1%" msgid "Add Item" msgstr "Añadir elemento" msgid "Edit Item" msgstr "Editar elemento" msgid "MusicBrainz Fingerprint" msgstr "MusicBrainz huella digital" msgid "Unrecognized" msgstr "Desconocido" msgid "Recognized" msgstr "Reconocido" msgid "User Selection" msgstr "Selección del usuario" msgid "ID Lookup" msgstr "Buscar ID" msgid "Fingerprint" msgstr "Huella digital" msgid "Metadata Lookup" msgstr "Leyendo los metadatos" msgid "Amazon" msgstr "Amazon" msgid "Discogs" msgstr "Discogs" msgid "gnudb.org" msgstr "gnudb.org" msgid "TrackType.org" msgstr "TrackType.org" msgid "AENC - Audio encryption" msgstr "AENC - Cifrado de audio" msgid "APIC - Attached picture" msgstr "APIC - Imagen adjunta" msgid "COMM - Comments" msgstr "COMM - Comentario" msgid "COMR - Commercial" msgstr "COMR - Comercial" msgid "ENCR - Encryption method registration" msgstr "ENCR - Registro del método de cifrado" msgid "EQUA - Equalization" msgstr "EQUA - Ecualización" msgid "ETCO - Event timing codes" msgstr "ETCO - Códigos de tiempos de eventos" msgid "GEOB - General encapsulated object" msgstr "GEOB - Objeto encapsulado general" msgid "GRID - Group identification registration" msgstr "GRID - Registro de identificación de grupo" msgid "IPLS - Involved people list" msgstr "IPLS - Lista de personas involucradas" msgid "LINK - Linked information" msgstr "LINK - Información enlazada" msgid "MCDI - Music CD identifier" msgstr "MCDI - Identificador de CD de música" msgid "MLLT - MPEG location lookup table" msgstr "MLLT - Tabla de búsqueda de localización MPEG" msgid "OWNE - Ownership frame" msgstr "OWNE - Campo de propiedad" msgid "PRIV - Private frame" msgstr "PRIV - Campo privado" msgid "PCNT - Play counter" msgstr "PCNT - Contador de reproducción" msgid "POPM - Popularimeter" msgstr "POPM - Medidor de popularidad" msgid "POSS - Position synchronisation frame" msgstr "POSS - Campo de sincronización de posición" msgid "RBUF - Recommended buffer size" msgstr "RBUF - Tamaño de búfer recomendado" msgid "RVAD - Relative volume adjustment" msgstr "RVAD - Ajsute de volumen relativo" msgid "RVRB - Reverb" msgstr "RVRB - Eco" msgid "SYLT - Synchronized lyric/text" msgstr "SYLT - Texto/letras sincronizadas" msgid "SYTC - Synchronized tempo codes" msgstr "SYTC - Códigos de ritmo sincronizado" msgid "TALB - Album/Movie/Show title" msgstr "TALB - Título de álbum/película/espectáculo" msgid "TBPM - BPM (beats per minute)" msgstr "TBPM - Ritmo" msgid "TCOM - Composer" msgstr "TCOM - Compositor" msgid "TCON - Content type" msgstr "TCON - Tipo de contenido" msgid "TCOP - Copyright message" msgstr "TCOP - Mensaje de copyright" msgid "TDAT - Date" msgstr "TDAT - Fecha" msgid "TDLY - Playlist delay" msgstr "TDLY - Retraso de lista de reproducción" msgid "TENC - Encoded by" msgstr "TENC - Codificado por" msgid "TEXT - Lyricist/Text writer" msgstr "TEXT - Autor" msgid "TFLT - File type" msgstr "TFLT - Tipo de archivo" msgid "TIME - Time" msgstr "TIME - Tiempo" msgid "TIT1 - Content group description" msgstr "TIT1 - Descripción del grupo de contenido" msgid "TIT2 - Title/songname/content description" msgstr "TIT2 - Descripción del contenido/título" msgid "TIT3 - Subtitle/Description refinement" msgstr "TIT3 - Mejora de la descripción/subtítulo" msgid "TKEY - Initial key" msgstr "TKEY - Clave inicial" msgid "TLAN - Language(s)" msgstr "TLAN - Idioma(s)" msgid "TLEN - Length" msgstr "TLEN - Longitud" msgid "TMED - Media type" msgstr "TMED - Tipo de medio" msgid "TOAL - Original album/movie/show title" msgstr "TOAL - Título original de álbum/película/espectáculo" msgid "TOFN - Original filename" msgstr "TOFN - Título original del nombre de archivo" msgid "TOLY - Original lyricist(s)/text writer(s)" msgstr "TOLY - Autor original" msgid "TOPE - Original artist(s)/performer(s)" msgstr "TOPE - Artista/intérprete original" msgid "TORY - Original release year" msgstr "TORY - Año de lanzamiento original" msgid "TOWN - File owner/licensee" msgstr "TOWN - Propietario del archivo" msgid "TPE1 - Lead performer(s)/Soloist(s)" msgstr "TPE1 - Líder(es)/Solista(s)" msgid "TPE2 - Band/orchestra/accompaniment" msgstr "TPE2 - Banda/Orquesta/Acompañamiento" msgid "TPE3 - Conductor/performer refinement" msgstr "TPE3 - Director" msgid "TPE4 - Interpreted, remixed, or otherwise modified by" msgstr "TPE4 - Interpretado, remezclado o modificado de otra forma por" msgid "TPOS - Part of a set" msgstr "TPOS - Parte de un conjunto" msgid "TPUB - Publisher" msgstr "TPUB - Editorial" msgid "TRCK - Track number/Position in set" msgstr "TRCK - Número de pista/Posición en el conjunto" msgid "TRDA - Recording dates" msgstr "TRDA - Fechas de grabación" msgid "TRSN - Internet radio station name" msgstr "TRSN - Nombre de emisora de radio de Internet" msgid "TRSO - Internet radio station owner" msgstr "TRSO - Propietario de emisora de radio de internet" msgid "TSIZ - Size" msgstr "TSIZ - Tamaño" msgid "TSRC - ISRC (international standard recording code)" msgstr "TSRC - ISRC (Código de grabación estándar internacional)" msgid "TSSE - Software/Hardware and settings used for encoding" msgstr "TSSE - Software/Hardware y configuraciones usadas para codificar" msgid "TXXX - User defined text information" msgstr "TXXX - Información definida por el usuario" msgid "TYER - Year" msgstr "TYER - Año" msgid "UFID - Unique file identifier" msgstr "UFID - Identificador de archivo único" msgid "USER - Terms of use" msgstr "USER - Términos de uso" msgid "USLT - Unsynchronized lyric/text transcription" msgstr "USLT - Transcripción de texto/letras sin sincronización" msgid "WCOM - Commercial information" msgstr "WCOM - Información comercial" msgid "WCOP - Copyright/Legal information" msgstr "WCOP - Información legal/copyright" msgid "WOAF - Official audio file webpage" msgstr "WOAF - Página web oficial del archivo de audio" msgid "WOAR - Official artist/performer webpage" msgstr "WOAR - Página web oficial del artista/interpréte" msgid "WOAS - Official audio source webpage" msgstr "WOAS - Página web original del fuente del audio" msgid "WORS - Official internet radio station homepage" msgstr "WORS - Página web oficial de la emisora de radio de internet" msgid "WPAY - Payment" msgstr "WPAY - Pago" msgid "WPUB - Official publisher webpage" msgstr "WPUB - Página web de la editorial" msgid "WXXX - User defined URL link" msgstr "WXXX - URL definida por el usuario" msgid "MusicBrainz Release" msgstr "MusicBrainz álbum" msgid "ASPI - Audio seek point index" msgstr "ASPI - Ãndice de puntos de búsqueda" msgid "EQU2 - Equalisation (2)" msgstr "EQU2 - Ecualización (2)" msgid "RVA2 - Relative volume adjustment (2)" msgstr "RVA2 - Ajsute de volumen relativo (2)" msgid "SEEK - Seek frame" msgstr "SEEK - Campo de búsqueda" msgid "SIGN - Signature frame" msgstr "SIGN - Campo de firma" msgid "TCMP - iTunes compilation flag" msgstr "TCMP - iTunes compilación" msgid "TDEN - Encoding time" msgstr "TDEN - Fechas de codificación" msgid "TDOR - Original release time" msgstr "TDOR - Año de lanzamiento original" msgid "TDRC - Recording time" msgstr "TDRC - Fechas de grabación" msgid "TDRL - Release time" msgstr "TDRL - Fechas de lanzamiento" msgid "TDTG - Tagging time" msgstr "TDTG - Fecha de etiqueta" msgid "TIPL - Involved people list" msgstr "TIPL - Lista de personas involucradas" msgid "TMCL - Musician credits list" msgstr "TMCL - Crédito de músicos" msgid "TMOO - Mood" msgstr "TMOO - Ambiente" msgid "TPRO - Produced notice" msgstr "TPRO - Copyright de producción" msgid "TSO2 - Album artist sort order" msgstr "TSO2 - Clasificación de artistas del álbum" msgid "TSOA - Album sort order" msgstr "TSOA - Clasificación de álbumes" msgid "TSOC - Composer sort order" msgstr "TSOC - Clasificación de compositores" msgid "TSOP - Performer sort order" msgstr "TSOP - Clasificación de interprétes" msgid "TSOT - Title sort order" msgstr "TSOT - Clasificación de títulos" msgid "TSST - Set subtitle" msgstr "TSST - Subtítulo de movimiento" ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/po/et.po���������������������������������������������������������������������������������0000664�0000000�0000000�00000104330�12246035113�0013732�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Estonian translation of Kid3 # This file is put in the public domain. # # Mihkel Tõnnov <mihhkel@gmail.com>, 2007–2009. # Urs Fleisch <ufleisch@users.sourceforge.net>, 2010, 2011. msgid "" msgstr "" "Project-Id-Version: Kid3 3.0.2\n" "Report-Msgid-Bugs-To: http://sourceforge.net/p/kid3/bugs/\n" "POT-Creation-Date: 2013-09-11 21:22+0200\n" "PO-Revision-Date: 2011-01-15 17:30+0100\n" "Last-Translator: \n" "Language-Team: Estonian <kde-i18n-doc@kde.org>\n" "Language: et\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Poedit-Language: Estonian\n" "X-Generator: Lokalize 1.1\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Language: et\n" "X-Source-Language: en\n" msgid "Timeout" msgstr "Aegumine" msgid "Usage:" msgstr "Kasutamine:" msgid "Help" msgstr "Abi" msgid "Command name" msgstr "Käsu nimi" msgid "Quit application" msgstr "Sulgeb rakenduse" msgid "The current directory has been modified." msgstr "Kataloogi sisu on muudetud." msgid "Type 'exit force' to quit." msgstr "Kui valmis saad, sisesta 'exit force'." msgid "Change directory" msgstr "" msgid "%1 does not exist" msgstr "%1 ei ole olemas" msgid "Print the filename of the current working directory" msgstr "Väljasta jooksva töökataloogi täielik nimi" msgid "Directory list" msgstr "Kataloogide loetelu" msgid "Saves the changed files" msgstr "Salvestab muudetud failid" msgid "Error while writing file:\n" msgstr "Viga kirjutamisel:\n" msgid "Select file" msgstr "Vali fail" msgid "%1 not found" msgstr "%1 ei leitud" msgid "Select tag" msgstr "Vali märksõna" msgid "Get tag frame" msgstr "" msgid "Set tag frame" msgstr "" msgid "Could not set \"%1\" for %2" msgstr "" msgid "Revert" msgstr "Ennista" msgid "Import from file or clipboard" msgstr "Avab failist või lõikepuhvrist importimise dialoogi" msgid "Format name" msgstr "Vormingu nimi" msgid "%1 not found." msgstr "%1 ei leitud." msgid "Available" msgstr "Saadaval" msgid "Error" msgstr "Viga" msgid "Automatic import" msgstr "Imporditakse automaatselt" msgid "Profile name" msgstr "Profiili nimi" msgid "Reading Directory" msgstr "Kataloogi lugemine" msgid "Started" msgstr "Käivitatud" msgid "Source" msgstr "Allikas" msgid "Querying" msgstr "Päring" msgid "Fetching" msgstr "Allalaadimine" msgid "Data received" msgstr "Vastuvõetud andmed" msgid "Cover" msgstr "Kaanepilt" msgid "Finished" msgstr "Lõpetatud" msgid "Aborted" msgstr "Katkestatud" msgid "Download album cover artwork" msgstr "Avab albumi kaanepildi otsimise dialoogi" msgid "Export to file or clipboard" msgstr "Avab lõikepuhvrisse või faili eksportimise dialoogi" msgid "Create playlist" msgstr "Esitusloendi loomine" msgid "Apply filename format" msgstr "Rakendab failinime vormingu" msgid "Apply tag format" msgstr "Rakendab sildivormingu" msgid "Apply text encoding" msgstr "Rakenda teksti kodeering" msgid "Rename directory" msgstr "Kataloogi ümbernimetamine" msgid "Number tracks" msgstr "Lugude nummerdamine" msgid "Track number" msgstr "Loo number" msgid "Filter" msgstr "Filter" msgid "Filter name" msgstr "Filtri nimi" msgid "Convert ID3v2.3 to ID3v2.4" msgstr "Teisendab ID3v2.3 sildid ID3v2.4-ks" msgid "Convert ID3v2.4 to ID3v2.3" msgstr "Teisendab ID3v2.4 sildid ID3v2.3-ks" msgid "Filename from tag" msgstr "Failinimi silt põhjal" msgid "Tag from filename" msgstr "Silt failinime põhjal" msgid "Tag to other tag" msgstr "" msgid "Copy" msgstr "Kopeeri" msgid "Paste" msgstr "Aseta" msgid "Remove" msgstr "Eemalda" msgid "Play" msgstr "Mängib valitud lugu" msgid "Parameter" msgstr "Parameeter" msgid "File path" msgstr "Faili asukoht" msgid "URL" msgstr "URL" msgid "Tag numbers" msgstr "Silt number" msgid "Frame name" msgstr "Failinime põhjal" msgid "Frame value" msgstr "" msgid "Format" msgstr "Vorming" msgid "Command specific" msgstr "Käsk" msgid "Available Commands" msgstr "Saadaolevad käsud" msgid "File" msgstr "Fail" msgid "Name" msgstr "Nimi" msgid "Tag 2" msgstr "Silt 2" msgid "Tag 1" msgstr "Silt 1" msgid "Tags" msgstr "Sildid" msgid "Unknown command '%1'. Type 'help' for help." msgstr "Tundmatu käsk '%1'. Abiteabe jaoks käivita 'help'." msgid "Unknown command '%1', -h for help." msgstr "Tundmatu käsk '%1', -h näitab abiinfot." msgid "Files" msgstr "Failid" msgid "User Actions" msgstr "Kasutaja toimingud" msgid "Network" msgstr "Võrk" msgid "Plugins" msgstr "Pluginad" msgid "Main Toolbar" msgstr "Peamine tööriistariba" msgid "Opens a directory" msgstr "Avab kataloogi valimise dialoogi" msgid "Opens a recently used directory" msgstr "Avab hiljuti kasutatud kataloogi" msgid "Reverts the changes of all or the selected files" msgstr "Ennistab kõik muutused valitud elementidel" msgid "Quits the application" msgstr "Sulgeb rakenduse" msgid "Select all files" msgstr "Valib kõik failid" msgid "Deselect all files" msgstr "Tühistab valiku kõikidel failidel" msgid "Configure Shortcuts" msgstr "Kiirklahvide seadistamine" msgid "Configure Toolbars" msgstr "Tööriistaribade seadistamine" msgid "Preferences dialog" msgstr "Avab seadistusdialoogi" msgid "O&pen Directory..." msgstr "Ava &kataloog..." msgid "&Import..." msgstr "&Impordi..." msgid "Import from %1..." msgstr "Impordi %1-ist..." msgid "Automatic I&mport..." msgstr "I&mporditakse automaatselt..." msgid "&Browse Cover Art..." msgstr "Kaane&pildi otsimine..." msgid "&Export..." msgstr "&Ekspordi..." msgid "&Create Playlist..." msgstr "&Loo esitusloend..." msgid "Apply &Filename Format" msgstr "Rakenda &failinime vorming" msgid "Apply &Tag Format" msgstr "Rakenda &sildivorming" msgid "Apply Text &Encoding" msgstr "Rakenda t&eksti kodeering" msgid "&Rename Directory..." msgstr "&Kataloogi ümbernimetamine..." msgid "&Number Tracks..." msgstr "&Lugude nummerdamine..." msgid "F&ilter..." msgstr "F&iltreeri..." msgid "Convert ID3v2.3 to ID3v2.&4" msgstr "Teisenda ID3v2.3 ID3v2.&4-ks" msgid "Convert ID3v2.4 to ID3v2.&3" msgstr "Teisenda ID3v2.4 ID3v2.&3-ks" msgid "&Play" msgstr "&Esita" msgid "Show &Picture" msgstr "&Kaanepildi näitamine" msgid "Auto &Hide Tags" msgstr "Siltide &automaatpeitmine" msgid "Select All in &Directory" msgstr "" msgid "&Previous File" msgstr "&Eelmine fail" msgid "&Next File" msgstr "&Järgmine fail" msgid "From Filename" msgstr "Failinime põhjal" msgid "From Tag 2" msgstr "Silt 2 põhjal" msgid "From Tag 1" msgstr "Silt 1 põhjal" msgid "Frames:" msgstr "Raamid:" msgid "Edit" msgstr "Redigeeri" msgid "Add" msgstr "Lisa" msgid "Delete" msgstr "Kustuta" msgid "Filename" msgstr "Failinimi" msgid "Focus" msgstr "Fookusesse" msgid "File List" msgstr "Failide nimekiri" msgid "Directory List" msgstr "Kataloogide loetelu" msgid "Configure - Kid3" msgstr "Seadistamine - Kid3" msgid "Open" msgstr "Avamine" msgid "Save As" msgstr "Salvestamine" msgid "directory to open" msgstr "avatav kataloog" msgid "Kid3 ID3 Tagger" msgstr "Kid3, ID3-sildistaja" #. i18n NAME OF TRANSLATORS msgid "Your names" msgstr "Mihkel Tõnnov" #. i18n EMAIL OF TRANSLATORS msgid "Your emails" msgstr "mihhkel@gmail.com" msgid "&Back" msgstr "&Tagasi" msgid "&Forward" msgstr "&Edasi" msgid "&Find:" msgstr "&Otsi:" msgid "Find Previous" msgstr "Leia eelmine" msgid "Find Next" msgstr "Leia järgmine" msgid "&Close" msgstr "&Sulge" msgid "&Tags" msgstr "&Sildid" msgid "&Files" msgstr "&Failid" msgid "&User Actions" msgstr "&Kasutaja toimingud" msgid "&Network" msgstr "&Võrk" msgid "&Plugins" msgstr "&Pluginad" msgid "&Keyboard Shortcuts" msgstr "&Klaviatuuri kiirklahvid" msgid "Use custom app&lication font" msgstr "Kohandatud &fondi kasutamine" msgid "A&pplication Font..." msgstr "&Rakenduse font..." msgid "Use custom application &style" msgstr "Kohandatud &stiili kasutamine" msgid "Unknown" msgstr "Teadmata" msgid "Use native system file &dialogs" msgstr "Natiivsete süsteemi faili&dialoogide kasutamine" msgid "&Appearance" msgstr "Väl&imus" msgid "&Help" msgstr "&Abi" msgid "&OK" msgstr "&OK" msgid "&Cancel" msgstr "&Loobu" msgid "The keyboard shortcut '%1' is already assigned to '%2'." msgstr "Kiirklahvi '%1' juba kasutatakse järgneva tegevuse jaoks '%2'." msgid "&File" msgstr "&Fail" msgid "&Open..." msgstr "&Ava..." msgid "Open &Recent" msgstr "Viimati avat&ud" msgid "&Save" msgstr "&Salvesta" msgid "Re&vert" msgstr "E&nnista" msgid "Import from %1" msgstr "Impordib %1-ist" msgid "Browse album cover artwork" msgstr "Avab albumi kaanepildi otsimise dialoogi" msgid "Create M3U Playlist" msgstr "Loob M3U-esitusnimekirja" msgid "&Quit" msgstr "&Välju" msgid "&Edit" msgstr "&Redigeerimine" msgid "Select &All" msgstr "&Vali kõik" msgid "Dese&lect" msgstr "&Tühista valik" msgid "Select all files in the current directory" msgstr "" msgid "Select previous file" msgstr "Valib eelmise faili" msgid "Select next file" msgstr "Valib järgmise faili" msgid "&Tools" msgstr "&Tööriistad" msgid "Apply Filename Format" msgstr "Rakendab failinime vormingu" msgid "Apply Tag Format" msgstr "Rakendab sildivormingu" msgid "Apply Text Encoding" msgstr "Rakenda teksti kodeering" msgid "Rename Directory" msgstr "Kataloogi ümbernimetamine" msgid "Number Tracks" msgstr "Lugude nummerdamine" msgid "&Settings" msgstr "&Seadistused" msgid "Enables/disables the toolbar" msgstr "Lülitab tööriistariba näitamist" msgid "Show &Toolbar" msgstr "&Tööriistariba näitamine" msgid "Enables/disables the statusbar" msgstr "Lülitab olekuriba näitamist" msgid "Show St&atusbar" msgstr "&Olekuriba näitamine" msgid "Show Picture" msgstr "Lülitab kaanepildi näitamist" msgid "Auto Hide Tags" msgstr "Lülitab siltide automaatpeitmist" msgid "Configure Kid3" msgstr "Avab Kid3 seadistusakna" msgid "&Configure Kid3..." msgstr "Kid3 &seadistamine..." msgid "Kid3 Handbook" msgstr "Kid3 käsiraamat" msgid "Kid3 &Handbook" msgstr "Kid&3 käsiraamat" msgid "About Kid3" msgstr "Kid3 info" msgid "&About Kid3" msgstr "Kid3 &info" msgid "About Qt" msgstr "Qt info" msgid "About &Qt" msgstr "&Qt info" msgid " [modified]" msgstr " [muudetud]" msgid "Toggle the statusbar..." msgstr "Lülita olekuriba..." msgid "Ready." msgstr "Valmis." msgid "&Clear List" msgstr "&Puhasta loend" msgid "Clear" msgstr "Puhasta" msgid "Reset" msgstr "Lähtesta" msgid "Press F2 or double click to edit cell contents." msgstr "" msgid "Action" msgstr "Tegevus" msgid "Shortcut" msgstr "Kiirklahv" msgid "Server" msgstr "Server" msgid "Accuracy" msgstr "Täpsus" msgid "Standard Tags" msgstr "Standardsed sildid" msgid "Additional Tags" msgstr "Lisasildid" msgid "Cover Art" msgstr "Kaanepilt" msgid "Invalid File" msgstr "Vigane fail" msgid "Data received: %1" msgstr "Vastuvõetud andmed: %1" msgid "Request sent..." msgstr "Taotlus saadetud..." msgid "Year" msgstr "Aasta" msgid "Length" msgstr "Pikkus" msgid "Filenames" msgstr "Failinimed" msgid "URLs" msgstr "URL-id" msgid "Directory name" msgstr "Kataloogi nimi" msgid "Encode as URL" msgstr "URL-i jaoks sobivas kodeeringus esitaja jm" msgid "Confirm" msgstr "Kinnita" msgid "Output" msgstr "Väljund" msgid "Command" msgstr "Käsk" msgid "Create directory %1 failed\n" msgstr "Kataloogi \"%1\" loomine ebaõnnestus\n" msgid "File %1 already exists\n" msgstr "Fail \"%1\" on juba olemas\n" msgid "%1 is not a directory\n" msgstr "\"%1\" ei ole kataloog\n" msgid "Rename %1 to %2 failed\n" msgstr "Elemendi \"%1\" ümbernimetamine \"%2-ks\" ebaõnnestus\n" msgid "%1 already exists\n" msgstr "\"%1\" on juba olemas\n" msgid "%1 is not a file\n" msgstr "\"%1\" ei ole fail\n" msgid "New directory name is too different\n" msgstr "Uue kataloogi nimi on liiga erinev\n" msgid "Create directory" msgstr "Kataloogi loomine" msgid "Rename file" msgstr "Faili ümbernimetamine" msgid "C&lear" msgstr "P&uhasta" msgid "Execute " msgstr "Käivita" msgid "Could not execute " msgstr "Käivitamine pole võimalik" msgid "True if strings are equal" msgstr "Tõene, kui stringid on võrdsed" msgid "True if string contains substring" msgstr "Tõene, kui string sisaldab alamstringi" msgid "True if string matches regexp" msgstr "Tõene, kui string vastab regulaaravaldisele" msgid "Logical AND" msgstr "Loogiline NING" msgid "Logical OR" msgstr "Loogiline VÕI" msgid "Logical negation" msgstr "Loogiline EI" msgid "Data" msgstr "Andmed" msgid "%1 is not writable" msgstr "%1 ei ole kirjutatav" msgid "All Supported Files" msgstr "Kõik toetatud failid" msgid "All Files" msgstr "Kõik failid" msgid "Track" msgstr "Loo nr" msgid "Absolute path to file" msgstr "Täielik tee faili juurde" msgid "Duration" msgstr "Kestus" msgid "Title" msgstr "Pealkiri" msgid "Artist" msgstr "Esitaja" msgid "Album" msgstr "Album" msgid "Comment" msgstr "Kommentaar" msgid "Date" msgstr "Aasta" msgid "Track Number" msgstr "Loo number" msgid "Genre" msgstr "Stiil" msgid "Album Artist" msgstr "Albumi esitaja" msgid "Arranger" msgstr "Arranžeerija" msgid "Author" msgstr "Autor" msgid "BPM" msgstr "Lööki/min" msgid "Catalog Number" msgstr "Katalooginumber" msgid "Compilation" msgstr "Kogumik" msgid "Composer" msgstr "Helilooja" msgid "Conductor" msgstr "Dirigent" msgid "Copyright" msgstr "Autoriõigus" msgid "Disc Number" msgstr "Plaadi nr" msgid "Encoded-by" msgstr "Kodeerija" msgid "Encoder Settings" msgstr "Kodeerija seadistused" msgid "Encoding Time" msgstr "Kodeerimise aeg" msgid "Grouping" msgstr "Grupeerimine" msgid "Initial Key" msgstr "Esialgne võti" msgid "ISRC" msgstr "ISRC" msgid "Language" msgstr "Keel" msgid "Lyricist" msgstr "Sõnade autor" msgid "Lyrics" msgstr "Sõnad" msgid "Media" msgstr "Andmekandja" msgid "Mood" msgstr "Meeleolu" msgid "Original Album" msgstr "Originaalalbum" msgid "Original Artist" msgstr "Originaalesitaja" msgid "Original Date" msgstr "Originaali aasta" msgid "Part" msgstr "Osa" msgid "Performer" msgstr "Ettekandja" msgid "Picture" msgstr "Pilt" msgid "Publisher" msgstr "Väljaandja" msgid "Release Country" msgstr "Väljalaskeriik" msgid "Remixer" msgstr "Remiksija" msgid "Sort Album" msgstr "Albumi sorteerimisjärjestus" msgid "Sort Album Artist" msgstr "Albumi esitaja nimekuju sortimisel" msgid "Sort Artist" msgstr "Esitaja sorteerimisjärjestus" msgid "Sort Composer" msgstr "Helilooja sorteerimisjärjestus" msgid "Sort Name" msgstr "Pealkirja sorteerimisjärjestus" msgid "Subtitle" msgstr "Alapealkiri" msgid "Website" msgstr "Veebilehekülg" msgid "WWW Audio File" msgstr "Audiofaili WWW" msgid "WWW Audio Source" msgstr "Audioallika WWW" msgid "Number of tracks" msgstr "Lugude arv" msgid "Extension" msgstr "Laiend" msgid "Bitrate" msgstr "Bitikiirus" msgid "VBR" msgstr "VBR" msgid "Samplerate" msgstr "Diskreetimissagedus" msgid "Channels" msgstr "Kanaleid" msgid "Codec" msgstr "Koodek" msgid "Escape for HTML" msgstr "Paoklahv HTML" msgid "Automatic Import" msgstr "Imporditakse automaatselt" msgid "D&estination:" msgstr "&Sihtkoht:" msgid "Tag 1 and Tag 2" msgstr "Silt 1 ja silt 2" msgid "&Profile:" msgstr "&Profiil:" msgid "&Save Settings" msgstr "&Salvesta seaded" msgid "New" msgstr "Uus" msgid "A&bort" msgstr "&Katkesta" msgid "S&tart" msgstr "&Alusta" msgid "Import Source" msgstr "Importimise allikas" msgid "&Server:" msgstr "&Server:" msgid "&Accuracy:" msgstr "T&äpsus:" msgid "&Standard Tags" msgstr "&Standardsed sildid" msgid "&Additional Tags" msgstr "&Lisasildid" msgid "C&over Art" msgstr "&Kaanepilt" msgid "Browse Cover Art" msgstr "Kaanepildi otsimine" msgid "&Artist/Album" msgstr "&Esitaja / Album" msgid "&Source" msgstr "&Allikas" msgid "Source:" msgstr "Allikas:" msgid "URL:" msgstr "URL:" msgid "&URL extraction" msgstr "&URL-i tuvastamine" msgid "Match" msgstr "Tulemus" msgid "Picture URL" msgstr "Pildi URL" msgid "&Browse" msgstr "&Otsi" msgid "Click Browse to start" msgstr "Alustamiseks klõpsa \"Otsi\", see käivitab järgneva käsu" msgid "Then drag the picture from the browser to Kid3." msgstr "Seejärel lohista pilt veebilehitsejast Kid3 aknasse." msgid "ID3v1" msgstr "ID3v1" msgid "&Mark truncated fields" msgstr "Lühikeseks &jäävad väljad tõstetakse esile" msgid "Text &encoding:" msgstr "Teksti ko&deering:" msgid "ID3v2" msgstr "ID3v2" msgid "Use &track/total number of tracks format" msgstr "Kasutatakse vormingut \"loo n&r/lugude koguarv\"" msgid "Track number &digits:" msgstr "Num&brite vähim arv loo numbris:" msgid "&Genre as text instead of numeric string" msgstr "&Stiil salvestatakse numberkoodi asemel tekstina" msgid "ISO-8859-1" msgstr "ISO-8859-1" msgid "UTF16" msgstr "UTF-16" msgid "UTF8" msgstr "UTF-8" msgid "&Version used for new tags:" msgstr "Uutes siltides kasutatav &versioon:" msgid "ID3v2.3.0" msgstr "ID3v2.3.0" msgid "ID3v2.4.0" msgstr "ID3v2.4.0" msgid "Ogg/Vorbis" msgstr "Ogg/Vorbis" msgid "Co&mment field name:" msgstr "&Kommentaarivälja nimi:" msgid "&Picture field name:" msgstr "&Pildivälja nimi:" msgid "Custom &Genres" msgstr "Ko&handatud stiilid" msgid "&Show only custom genres" msgstr "Näidatakse ainult ko&handatud stiile" msgid "&Quick Access Tags" msgstr "" msgid "&Tag Format" msgstr "&Sildivorming" msgid "Tag &1" msgstr "Silt &1" msgid "Tag &2" msgstr "Silt &2" msgid "Tag 1 a&nd Tag 2" msgstr "Silt 1 &ja silt 2" msgid "Startup" msgstr "Käivitamine" msgid "&Load last-opened files" msgstr "" msgid "Save" msgstr "Salvestamine" msgid "&Preserve file timestamp" msgstr "Failide aja&templid säilitatakse" msgid "&Mark changes" msgstr "&Muudetud väljad tõstetakse esile" msgid "F&ilename for cover:" msgstr "Fa&ilinimi kaanepilt:" msgid "&Filename Format" msgstr "&Failinime vorming" msgid "Browser" msgstr "Veebilehitseja" msgid "Web &browser:" msgstr "&Veebilehitseja:" msgid "Context &Menu Commands" msgstr "Kontekst&menüü käsud" #, fuzzy msgid "&Play on double click" msgstr "&Esitus topeltklõpsu" msgid "Proxy" msgstr "Puhverserver" msgid "&Proxy:" msgstr "&Puhverserver:" msgid "&Use authentication with proxy" msgstr "Puhverserveris &autentimise kasutamine" msgid "Proxy user &name:" msgstr "Puhverserveri &kasutajanimi:" msgid "Proxy pass&word:" msgstr "Puhverserveri &parool:" msgid "&Metadata Plugins && Priority" msgstr "&Metaandmed pluginad && Prioriteet" msgid "A&vailable Plugins" msgstr "S&aadaolevad pluginad" msgid "Changes take only effect after a restart!" msgstr "Rakendub alles pärast taaskäivitust!" msgid "From Clip&board" msgstr "&Lõikepuhvrist" msgid "&Import" msgstr "&Impordi" msgid "&Export" msgstr "&Ekspordi" msgid "&View" msgstr "&Näita" msgid "Text Encoding" msgstr "Teksti kodeering" msgid "Text" msgstr "Tekst" msgid "Description" msgstr "Kirjeldus" msgid "Owner" msgstr "Omanik" msgid "Email" msgstr "E-post" msgid "Rating" msgstr "Hinnang" msgid "Picture Type" msgstr "Pildi tüüp" msgid "Image format" msgstr "Pildivorming" msgid "Mimetype" msgstr "MIME-tüüp" msgid "Counter" msgstr "Loendur" msgid "Identifier" msgstr "Identifikaator" msgid "Volume Adjustment" msgstr "Helitugevuse korrigeerimine" msgid "Number of Bits" msgstr "Bittide arv" msgid "Volume Change Right" msgstr "Helitugevuse muutus paremal" msgid "Volume Change Left" msgstr "Helitugevuse muutus vasakul" msgid "Peak Volume Right" msgstr "Helitugevuse tipp paremal" msgid "Peak Volume Left" msgstr "Helitugevuse tipp vasakul" msgid "Timestamp Format" msgstr "Ajatempli vorming" msgid "Content Type" msgstr "Sisu tüüp" msgid "Price" msgstr "Hind" msgid "Seller" msgstr "Müüja" msgid "Images" msgstr "Pildid" msgid "UTF16BE" msgstr "UTF-16-BE" msgid "Other" msgstr "Muu" msgid "32x32 pixels PNG file icon" msgstr "32x32 px PNG-vormingus faili ikoon" msgid "Other file icon" msgstr "Muus vormingus faili ikoon" msgid "Cover (front)" msgstr "Karbi esikülg" msgid "Cover (back)" msgstr "Karbi tagakülg" msgid "Leaflet page" msgstr "Voldiku lehekülg" msgid "Lead artist/lead performer/soloist" msgstr "Juhtiv esitaja/ettekandja / solist" msgid "Artist/performer" msgstr "Esitaja" msgid "Band/Orchestra" msgstr "Bänd/orkester" msgid "Lyricist/text writer" msgstr "Sõnade autor" msgid "Recording Location" msgstr "Lindistuskoht" msgid "During recording" msgstr "Lindistamise ajal" msgid "During performance" msgstr "Esituse ajal" msgid "Movie/video screen capture" msgstr "Kaader filmist/videost" msgid "A bright coloured fish" msgstr "Värvikirev kala" msgid "Illustration" msgstr "Illustratsioon" msgid "Band/artist logotype" msgstr "Bändi/esitaja logo" msgid "Publisher/Studio logotype" msgstr "Väljaandja/stuudio logo" msgid "MPEG frames as unit" msgstr "Ühikutena MPEG-kaadrid" msgid "Milliseconds as unit" msgstr "Ühikutena millisekundid" msgid "Text transcription" msgstr "Teksti transkriptsioon" msgid "Movement/part name" msgstr "Liikumise/osa nimi" msgid "Events" msgstr "Sündmused" msgid "Chord" msgstr "Akord" msgid "Trivia/pop up" msgstr "Trivia/hüpik" msgid "Export" msgstr "Eksport" msgid "Format:" msgstr "Vorming:" msgid "Header:" msgstr "Päis:" msgid "Tracks:" msgstr "Palad:" msgid "Footer:" msgstr "Jalus:" msgid "To F&ile" msgstr "&Faili" msgid "To Clip&board" msgstr "&Lõikepuhvrisse" msgid "&Source:" msgstr "&Allikas:" msgid "File Error" msgstr "Failiviga" msgid "&Filter:" msgstr "&Filter:" msgid "&Expression:" msgstr "&Avaldis:" msgid "&Apply" msgstr "&Rakenda" msgid "Accuracy:" msgstr "Täpsus:" msgid "Cover Art:" msgstr "Kaanepilt:" msgid "From F&ile/Clipboard..." msgstr "Failist/&lõikepuhvrist..." msgid "From T&ags..." msgstr "Silt põhj&al..." msgid "&From Server:" msgstr "&Serverist:" msgid "Check maximum allowable time &difference (sec):" msgstr "S&uurim lubatav ajaline erinevus (s):" msgid "Match with:" msgstr "Sobita:" msgid "&Length" msgstr "&Pikkus" msgid "T&rack" msgstr "&Loo nr" msgid "&Title" msgstr "&Pealkiri" msgid "&Start number:" msgstr "&Algusnumber:" msgid "&Destination:" msgstr "&Sihtkoht:" msgid "&Total number of tracks:" msgstr "&Lugude arv:" msgid "Create Playlist" msgstr "Esitusloendi loomine" msgid "Playlist File Name" msgstr "Esitusloendi failinimi" msgid "Same as &directory name" msgstr "Sama mis &kataloogil" msgid "&Format:" msgstr "&Vorming:" msgid "Cr&eate in:" msgstr "Loomis&koht:" msgid "Current directory" msgstr "Jooksev kataloog" msgid "Every directory" msgstr "Iga kataloog" msgid "Top-level directory" msgstr "Tipptaseme kataloog" msgid "Playlist Content" msgstr "Esitusloendi sisu" msgid "For&mat:" msgstr "Vor&ming:" msgid "Incl&ude only the selected files" msgstr "Kaasatakse &ainult valitud failid" msgid "Sort by file &name" msgstr "Sorditakse &failinime järgi" msgid "Sort by &tag field" msgstr "Sorditakse sildi &välja järgi" msgid "Use &relative path for files in playlist" msgstr "Failinimedes kasutatakse &suhtelisi teid" msgid "Use full p&ath for files in playlist" msgstr "Failinimedes kasutatakse &täielikke teid" msgid "Write only &list of files" msgstr "Kirjutatakse ainult failide &loend" msgid "Write &info using" msgstr "Kirjutatakse &info" msgid "Preview" msgstr "Eelvaade" msgid "Create Directory" msgstr "Kataloogi loomine" msgid "From Tag 2 and Tag 1" msgstr "Siltide 1 ja 2 põhjal" msgid "From:" msgstr "Siit:" msgid "To:" msgstr "Siia:" msgid "&Find" msgstr "&Otsi" msgid "C&GI Path:" msgstr "C&GI tee:" msgid "Track Title/Artist - Album" msgstr "Loo nr Pealkiri/Esitaja - Album" msgid "State" msgstr "Olek" msgid "No result" msgstr "Tulemuseta" msgid "No result selected" msgstr "Tulemust pole valitud" msgid "Import from Tags" msgstr "Importid sildid" msgid "Extraction:" msgstr "Kaevandamine:" msgid "Import from File/Clipboard" msgstr "Impordi failist/lõikepuhvrist" msgid "From F&ile" msgstr "&Failist" msgid "Download" msgstr "Allalaadimine" msgid "Opening directory..." msgstr "Kataloogi avamine..." msgid "Saving directory..." msgstr "Kataloogi salvestamine..." msgid "" "The current directory has been modified.\n" "Do you want to save it?" msgstr "" "Kataloogi sisu on muudetud.\n" "Kas salvestada see?" msgid "Warning" msgstr "Hoiatus" msgid "Exiting..." msgstr "Väljumine..." msgid "Creating playlist..." msgstr "Esitusnimekirja loomine..." msgid "Import" msgstr "Import" msgid "Error while renaming:\n" msgstr "Viga ümbernimetamisel:\n" msgid " [filtered]" msgstr " [filtreeritud]" msgid "Add Frame" msgstr "Raami lisamine" msgid "Select the frame ID" msgstr "Raami ID" msgid "Rename File" msgstr "Faili ümbernimetamine" msgid "Enter new file name:" msgstr "Sisesta faili uus nimi:" msgid "Do you really want to move these %1 items to the trash?" msgstr "Kas tõesti visata need %1 elementi prügikasti?" msgid "Do you really want to move this item to the trash?" msgstr "Kas tõesti visata see element prügikati?" msgid "Move to Trash" msgstr "Viska prügikasti" msgid "Directory must be empty.\n" msgstr "Kataloog peab olema tühi.\n" msgid "Could not move these files to the Trash" msgstr "Nende failide viskamine prügikasti nurjus" msgid "Expand All" msgstr "Laienda kõik" msgid "&Expand all" msgstr "&Laienda kõik" msgid "&Collapse all" msgstr "&Ahenda kõik" msgid "&Rename" msgstr "&Nimeta ümber" msgid "&Move to Trash" msgstr "&Viska prügikasti" msgid "Kid3" msgstr "Kid3" msgid "F&ile" msgstr "Fa&il" msgid "Name:" msgstr "Nimi:" msgid "Filename from Tag 1" msgstr "Failinimi silt 1 põhjal" msgid "Filename from Tag 2" msgstr "Failinimi silt 2 põhjal" msgid "Tag 1 from Filename" msgstr "Silt 1 failinime põhjal" msgid "Tag 2 from Filename" msgstr "Silt 2 failinime põhjal" msgid "&Add..." msgstr "L&isa..." msgid "Move &Up" msgstr "Liiguta &üles" msgid "Move &Down" msgstr "Lii&guta alla" msgid "&Edit..." msgstr "&Muuda..." msgid "&Remove" msgstr "&Eemalda" msgid "&Insert row" msgstr "&Sisesta rida" msgid "&Delete row" msgstr "&Kustuta rida" msgid "&Clear row" msgstr "&Puhasta rida" msgid "Format while editing" msgstr "Automaatasendus" msgid "Case conversion:" msgstr "Tõstu muutmine:" msgid "No changes" msgstr "Jäetakse samaks" msgid "All lowercase" msgstr "kõik tähed alatõstus" msgid "All uppercase" msgstr "KÕIK TÄHED ÜLATÕSTUS" msgid "First letter uppercase" msgstr "Esimene täht ülatõstus" msgid "All first letters uppercase" msgstr "Iga Sõna Esimene Täht Ülatõstus" msgid "Locale:" msgstr "Lokaat:" msgid "None" msgstr "Puudub" msgid "String replacement:" msgstr "Stringiasendus:" msgid "From" msgstr "Asendatav" msgid "To" msgstr "Asendaja" msgid "&Add" msgstr "L&isa" msgid "&Select all" msgstr "&Vali kõik" msgid "&Deselect all" msgstr "&Tühista kõik valikud" msgid "View Picture" msgstr "Pilt" msgid "" "Drag album\n" "artwork\n" "here" msgstr "" "Lohista albumi\n" "kaanepilt\n" "siia" msgid "Play/Pause" msgstr "Esitus/paus" msgid "Stop playback" msgstr "Peata esitus" msgid "Previous Track" msgstr "Eelmine lugu" msgid "Next Track" msgstr "Järgmine lugu" msgid "Close" msgstr "Sulge" msgid "Mute" msgstr "Tumm" msgid "Volume: %1%" msgstr "Helitugevus: %1%" msgid "Add Item" msgstr "Lisamine" msgid "Edit Item" msgstr "Redigeerimine" msgid "MusicBrainz Fingerprint" msgstr "MusicBrainz: sõrmejälg" msgid "Unrecognized" msgstr "Tundmatu" msgid "Recognized" msgstr "Tuvastatud" msgid "User Selection" msgstr "Kasutaja valik" msgid "ID Lookup" msgstr "ID otsimine" msgid "Fingerprint" msgstr "Sõrmejälg" msgid "Metadata Lookup" msgstr "Metaandmed loetud" msgid "Amazon" msgstr "Amazon" msgid "Discogs" msgstr "Discogs" msgid "gnudb.org" msgstr "gnudb.org" msgid "TrackType.org" msgstr "TrackType.org" msgid "AENC - Audio encryption" msgstr "AENC - audiokrüpteering" msgid "APIC - Attached picture" msgstr "APIC - lisatud pilt" msgid "COMM - Comments" msgstr "COMM - kommentaarid" msgid "COMR - Commercial" msgstr "COMR - kommertsteave" msgid "ENCR - Encryption method registration" msgstr "ENCR - krüpteerimismeetodi registreerimine" msgid "EQUA - Equalization" msgstr "EQUA - ekvalaisimine" msgid "ETCO - Event timing codes" msgstr "ETCO - sündmuse ajastamise koodid" msgid "GEOB - General encapsulated object" msgstr "GEOB - üldine kapseldatud objekt" msgid "GRID - Group identification registration" msgstr "GRID - grupi identifitseerimise registreerimine" msgid "IPLS - Involved people list" msgstr "IPLS - seotud inimeste nimekiri" msgid "LINK - Linked information" msgstr "LINK - lingitud teave" msgid "MCDI - Music CD identifier" msgstr "MCDI - muusika-CD identifikaator" msgid "MLLT - MPEG location lookup table" msgstr "MLLT - MPEG asukohaotsingutabel" msgid "OWNE - Ownership frame" msgstr "OWNE - omandus" msgid "PRIV - Private frame" msgstr "PRIV - privaatne" msgid "PCNT - Play counter" msgstr "PCNT - esitusloendur" msgid "POPM - Popularimeter" msgstr "POPM - popularimeeter" msgid "POSS - Position synchronisation frame" msgstr "POSS - asukoha sünkroniseerimine" msgid "RBUF - Recommended buffer size" msgstr "RBUF - soovituslik puhvrisuurus" msgid "RVAD - Relative volume adjustment" msgstr "RVAD - suhteline helitugevuse reguleerimine" msgid "RVRB - Reverb" msgstr "RVRB - reverb" msgid "SYLT - Synchronized lyric/text" msgstr "SYLT - sünkroniseeritud laulusõnad/tekst" msgid "SYTC - Synchronized tempo codes" msgstr "SYTC - sünkroniseeritud tempokoodid" msgid "TALB - Album/Movie/Show title" msgstr "TALB - albumi/filmi/etenduse pealkiri" msgid "TBPM - BPM (beats per minute)" msgstr "TBPM - BPM (lööki minutis)" msgid "TCOM - Composer" msgstr "TCOM - helilooja" msgid "TCON - Content type" msgstr "TCON - sisu tüüp" msgid "TCOP - Copyright message" msgstr "TCOP - autoriõiguse sõnum" msgid "TDAT - Date" msgstr "TDAT - kuupäev" msgid "TDLY - Playlist delay" msgstr "TDLY - esitusnimekirja viivitus" msgid "TENC - Encoded by" msgstr "TENC - kodeerija" msgid "TEXT - Lyricist/Text writer" msgstr "TEXT - sõnade/teksti autor" msgid "TFLT - File type" msgstr "TFLT - failitüüp" msgid "TIME - Time" msgstr "TIME - aeg" msgid "TIT1 - Content group description" msgstr "TIT1 - sisu grupi kirjeldus" msgid "TIT2 - Title/songname/content description" msgstr "TIT2 - pealkiri / laulu nimi / sisu kirjeldus" msgid "TIT3 - Subtitle/Description refinement" msgstr "TIT3 - alapealkiri / kirjelduse täpsustus" msgid "TKEY - Initial key" msgstr "TKEY - esialgne võti" msgid "TLAN - Language(s)" msgstr "TLAN - keel(ed)" msgid "TLEN - Length" msgstr "TLEN - pikkus" msgid "TMED - Media type" msgstr "TMED - andmekandja tüüp" msgid "TOAL - Original album/movie/show title" msgstr "TOAL - originaalalbumi/-filmi/-etenduse pealkiri" msgid "TOFN - Original filename" msgstr "TOFN - originaalfailinimi" msgid "TOLY - Original lyricist(s)/text writer(s)" msgstr "TOLY - originaalsõnade/-teksti autor" msgid "TOPE - Original artist(s)/performer(s)" msgstr "TOPE - originaalesitaja(d)/-ettekandja(d)" msgid "TORY - Original release year" msgstr "TORY - originaali väljalaske aasta" msgid "TOWN - File owner/licensee" msgstr "TOWN - faili omanik/litsenseeritu" msgid "TPE1 - Lead performer(s)/Soloist(s)" msgstr "TPE1 - juhtiv(ad) ettekandja(d) / solist(id)" msgid "TPE2 - Band/orchestra/accompaniment" msgstr "TPE2 - bänd/orkester/saateansambel" msgid "TPE3 - Conductor/performer refinement" msgstr "TPE3 - dirigent / esineja täpsustus" msgid "TPE4 - Interpreted, remixed, or otherwise modified by" msgstr "TPE4 - tõlgendaja, remiksija või muul moel muutja" msgid "TPOS - Part of a set" msgstr "TPOS - osa komplektist" msgid "TPUB - Publisher" msgstr "TPUB - väljaandja" msgid "TRCK - Track number/Position in set" msgstr "TRCK - loo nr / asukoht komplektis" msgid "TRDA - Recording dates" msgstr "TRDA - lindistuskuupäevad" msgid "TRSN - Internet radio station name" msgstr "TRSN - Interneti-raadiojaama nimi" msgid "TRSO - Internet radio station owner" msgstr "TRSO - Interneti-raadiojaama omanik" msgid "TSIZ - Size" msgstr "TSIZ - suurus" msgid "TSRC - ISRC (international standard recording code)" msgstr "TSRC - ISRC (rahvusvaheline standardsalvestuskood)" msgid "TSSE - Software/Hardware and settings used for encoding" msgstr "TSSE - kodeerimiseks kasutatud tarkvara/riistvara ja sätted" msgid "TXXX - User defined text information" msgstr "TXXX - kasutaja määratud tekstiline info" msgid "TYER - Year" msgstr "TYER - aasta" msgid "UFID - Unique file identifier" msgstr "UFID - unikaalne faili-identifikaator" msgid "USER - Terms of use" msgstr "USER - kasutustingimused" msgid "USLT - Unsynchronized lyric/text transcription" msgstr "USLT - sünkroniseerimata laulusõnad/teksti transkriptsioon" msgid "WCOM - Commercial information" msgstr "WCOM - kommertsinfo URL" msgid "WCOP - Copyright/Legal information" msgstr "WCOP - autoriõiguse / juriidilise teabe URL" msgid "WOAF - Official audio file webpage" msgstr "WOAF - audiofaili ametlik veebileht" msgid "WOAR - Official artist/performer webpage" msgstr "WOAR - esitaja/ettekandja ametlik veebileht" msgid "WOAS - Official audio source webpage" msgstr "WOAS - audioallika ametlik veebileht" msgid "WORS - Official internet radio station homepage" msgstr "WORS - Interneti-raadiojaama ametlik koduleht" msgid "WPAY - Payment" msgstr "WPAY - makse URL" msgid "WPUB - Official publisher webpage" msgstr "WPUB - väljaandja ametlik veebileht" msgid "WXXX - User defined URL link" msgstr "WXXX - kasutaja määratud URL" msgid "MusicBrainz Release" msgstr "MusicBrainz: album" msgid "ASPI - Audio seek point index" msgstr "ASPI - audio otsingupunktiindeks" msgid "EQU2 - Equalisation (2)" msgstr "EQU2 - ekvalaisimine (2)" msgid "RVA2 - Relative volume adjustment (2)" msgstr "RVA2 - suhteline helitugevuse reguleerimine (2)" msgid "SEEK - Seek frame" msgstr "SEEK - otsimine" msgid "SIGN - Signature frame" msgstr "SIGN - signatuur" msgid "TCMP - iTunes compilation flag" msgstr "TCMP - iTunes kompilatsioon" msgid "TDEN - Encoding time" msgstr "TDEN - kodeerimise aeg" msgid "TDOR - Original release time" msgstr "TDOR - originaali väljalaske aeg" msgid "TDRC - Recording time" msgstr "TDRC - lindistamise aeg" msgid "TDRL - Release time" msgstr "TDRL - väljalaske aeg" msgid "TDTG - Tagging time" msgstr "TDTG - sildistamise aeg" msgid "TIPL - Involved people list" msgstr "TIPL - seotud inimeste nimekiri" msgid "TMCL - Musician credits list" msgstr "TMCL - muusikute tänunimekiri" msgid "TMOO - Mood" msgstr "TMOO - meeleolu" msgid "TPRO - Produced notice" msgstr "TPRO - tootmismärkus" msgid "TSO2 - Album artist sort order" msgstr "TSO2 - albumi esitaja nimekuju sortimisel" msgid "TSOA - Album sort order" msgstr "TSOA - albumi sorteerimisjärjestus" msgid "TSOC - Composer sort order" msgstr "TSOC - helilooja sorteerimisjärjestus" msgid "TSOP - Performer sort order" msgstr "TSOP - esitaja sorteerimisjärjestus" msgid "TSOT - Title sort order" msgstr "TSOT - pealkirja sorteerimisjärjestus" msgid "TSST - Set subtitle" msgstr "TSST - komplekti alapealkiri" ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/po/extract-merge.sh����������������������������������������������������������������������0000775�0000000�0000000�00000003000�12246035113�0016060�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/bash BASEDIR="../src" # root of translatable sources PROJECT="kid3" # project name PROJECTVERSION="3.0.2" # project version BUGADDR="http://sourceforge.net/p/kid3/bugs/" # MSGID-Bugs WDIR=`pwd` # working dir echo "Preparing rc files" cd ${BASEDIR} # we use simple sorting to make sure the lines do not jump around too much from system to system find . -name '*.rc' -o -name '*.ui' -o -name '*.kcfg' | sort > ${WDIR}/rcfiles.list xargs --arg-file=${WDIR}/rcfiles.list extractrc > ${WDIR}/rc.cpp cd ${WDIR} echo "Done preparing rc files" echo "Extracting messages" cd ${BASEDIR} # see above on sorting find . -name '*.cpp' -o -name '*.h' -o -name '*.c' -o -name '*.hpp' | sort > ${WDIR}/infiles.list echo "rc.cpp" >> ${WDIR}/infiles.list cd ${WDIR} xgettext --from-code=UTF-8 -C --qt --no-location -ci18n -ktr:1,1t -ktr:1,2c,2t -ktr:1,1,2c,3t \ -kQT_TRANSLATE_NOOP:1,1t -kQT_TRANSLATE_NOOP:2,2t \ --msgid-bugs-address="${BUGADDR}" \ --package-name="${PROJECT^}" \ --package-version="${PROJECTVERSION}" \ --files-from=infiles.list -D ${BASEDIR} -D ${WDIR} -o ${PROJECT}.pot || { echo "error while calling xgettext. aborting."; exit 1; } echo "Done extracting messages" echo "Merging translations" catalogs=`find . -name '*.po'` for cat in $catalogs; do echo $cat msgmerge --quiet --update --backup=none $cat ${PROJECT}.pot sed -i "/#, qt-format/ d; /#, kde-format/ d; /^#~ msg/ d" $cat done echo "Done merging translations" echo "Cleaning up" cd ${WDIR} rm rcfiles.list rm infiles.list rm rc.cpp echo "Done" kid3-3.0.2/po/fi.po���������������������������������������������������������������������������������0000664�0000000�0000000�00000105503�12246035113�0013723�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Finnish translation of Kid3 # This file is put in the public domain. # # Tommi Nieminen <translator@legisign.org>, 2009. # Urs Fleisch <ufleisch@users.sourceforge.net>, 2010. # Ville Skyttä <ville.skytta@iki.fi>, 2010, 2011. msgid "" msgstr "" "Project-Id-Version: Kid3 3.0.2\n" "Report-Msgid-Bugs-To: http://sourceforge.net/p/kid3/bugs/\n" "POT-Creation-Date: 2013-09-11 21:22+0200\n" "PO-Revision-Date: 2013-03-14 23:39+0200\n" "Last-Translator: Ville Skyttä <ville.skytta@iki.fi>\n" "Language-Team: Finnish <kde-i18n-doc@kde.org>\n" "Language: fi\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 1.5.4\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Language: fi\n" "X-Source-Language: en\n" msgid "Timeout" msgstr "Aikakatkaisu" msgid "Usage:" msgstr "Käyttö:" msgid "Help" msgstr "Ohje" msgid "Command name" msgstr "Komentonimi" msgid "Quit application" msgstr "Sulkee sovelluksen" msgid "The current directory has been modified." msgstr "Nykyistä kansiota on muokattu." msgid "Type 'exit force' to quit." msgstr "Kirjoita 'exit force' poistuaksesi." msgid "Change directory" msgstr "Vaihda hakemistoa" msgid "%1 does not exist" msgstr "%1 ei ole olemassa" msgid "Print the filename of the current working directory" msgstr "Tulosta työhakemiston täydellinen nimi" msgid "Directory list" msgstr "Hakemistolista" msgid "Saves the changed files" msgstr "Tallentaa muuttuneet tiedostot" msgid "Error while writing file:\n" msgstr "Virhe kirjoitettaessa tiedostoa:\n" msgid "Select file" msgstr "Valitse tiedosto" msgid "%1 not found" msgstr "kohdetta %1 ei löytynyt" msgid "Select tag" msgstr "Valitse luokitus" msgid "Get tag frame" msgstr "" msgid "Set tag frame" msgstr "" msgid "Could not set \"%1\" for %2" msgstr "Ei voitu asettaa \"%1\" %2" msgid "Revert" msgstr "Kumoa" msgid "Import from file or clipboard" msgstr "Tuo tiedostosta tai leikepöydältä" msgid "Format name" msgstr "Muodon nimi" msgid "%1 not found." msgstr "%1 ei löytynyt." msgid "Available" msgstr "Käytettävissä" msgid "Error" msgstr "Virhe" msgid "Automatic import" msgstr "Tuo automaattisesti" msgid "Profile name" msgstr "Profiilin nimi" msgid "Reading Directory" msgstr "Luetaan hakemistoa" msgid "Started" msgstr "Aloitettu" msgid "Source" msgstr "Lähde" msgid "Querying" msgstr "Kysytään" msgid "Fetching" msgstr "Noudetaan" msgid "Data received" msgstr "Vastaanotettu data" msgid "Cover" msgstr "Kansikuvat" msgid "Finished" msgstr "Valmis" msgid "Aborted" msgstr "Keskeytetty" msgid "Download album cover artwork" msgstr "Selaa kansikuvia" msgid "Export to file or clipboard" msgstr "Vie tiedostoon tai leikepöydälle" msgid "Create playlist" msgstr "Luo soittolista" msgid "Apply filename format" msgstr "Ota muutokset käyttöön" msgid "Apply tag format" msgstr "Ota muutokset käyttöön" msgid "Apply text encoding" msgstr "Ota merkistökoodaus" msgid "Rename directory" msgstr "Muuta kansion nimeä" msgid "Number tracks" msgstr "Kappaleiden numerointi" msgid "Track number" msgstr "Kappaleen raita" msgid "Filter" msgstr "Suodin" msgid "Filter name" msgstr "Suodattimen nimi" msgid "Convert ID3v2.3 to ID3v2.4" msgstr "Muunna ID3v2.3:sta ID3v2.4:ksi" msgid "Convert ID3v2.4 to ID3v2.3" msgstr "Muunna ID3v2.4:sta ID3v2.3:ksi" msgid "Filename from tag" msgstr "Tiedostonimi tagista" msgid "Tag from filename" msgstr "Tagi tiedostonimestä" msgid "Tag to other tag" msgstr "" msgid "Copy" msgstr "Kopioi" msgid "Paste" msgstr "Liitä" msgid "Remove" msgstr "Poista" msgid "Play" msgstr "Soita" msgid "Parameter" msgstr "Parametri" msgid "File path" msgstr "Tiedostopolku" msgid "URL" msgstr "Osoite" msgid "Tag numbers" msgstr "Kappaleen raita" msgid "Frame name" msgstr "Tiedostonimestä" msgid "Frame value" msgstr "" msgid "Format" msgstr "Muoto" msgid "Command specific" msgstr "Komento" msgid "Available Commands" msgstr "Käytössäolevat komennot" msgid "File" msgstr "Tiedosto" msgid "Name" msgstr "Nimi" msgid "Tag 2" msgstr "2-tagi" msgid "Tag 1" msgstr "1-tagi" msgid "Tags" msgstr "Tagit" msgid "Unknown command '%1'. Type 'help' for help." msgstr "Tuntematon komento '%1'. Kirjoita 'help' saadaksesi apua." msgid "Unknown command '%1', -h for help." msgstr "Tuntematon komento '%1', -h näyttää ohjeen." msgid "Files" msgstr "Tiedostot" msgid "User Actions" msgstr "Käyttäjän toimet" msgid "Network" msgstr "Verkko" msgid "Plugins" msgstr "Liitännäiset" msgid "Main Toolbar" msgstr "Päätyökalupalkki" msgid "Opens a directory" msgstr "Avaa kansion" msgid "Opens a recently used directory" msgstr "Avaa viimeaikaisen kansion" msgid "Reverts the changes of all or the selected files" msgstr "Kumoaa muutokset kaikkiin tai valittuihin tiedostoihin" msgid "Quits the application" msgstr "Sulkee sovelluksen" msgid "Select all files" msgstr "Valitse kaikki tiedostot" msgid "Deselect all files" msgstr "Poista kaikki valinnat" msgid "Configure Shortcuts" msgstr "Aseta pikanäppäimet" msgid "Configure Toolbars" msgstr "Aseta työkalupalkit" msgid "Preferences dialog" msgstr "Asetusikkuna" msgid "O&pen Directory..." msgstr "Avaa &kansio..." msgid "&Import..." msgstr "T&uo..." msgid "Import from %1..." msgstr "Tuo %1ista..." msgid "Automatic I&mport..." msgstr "Tuo auto&maattisesti..." msgid "&Browse Cover Art..." msgstr "Selaa &kansikuvia..." msgid "&Export..." msgstr "&Vie..." msgid "&Create Playlist..." msgstr "&Luo soittolista..." msgid "Apply &Filename Format" msgstr "Ota tiedosto&nimimuutokset käyttöön" msgid "Apply &Tag Format" msgstr "Ota ta&gimuutokset käyttöön" msgid "Apply Text &Encoding" msgstr "Ota m&erkistökoodaus" msgid "&Rename Directory..." msgstr "Muuta &kansion nimeä..." msgid "&Number Tracks..." msgstr "&Numeroi kappaleet..." msgid "F&ilter..." msgstr "&Suodata..." msgid "Convert ID3v2.3 to ID3v2.&4" msgstr "Muunna ID3v2.3:sta ID3v2.&4:ksi" msgid "Convert ID3v2.4 to ID3v2.&3" msgstr "Muunna ID3v2.4:sta ID3v2.&3:ksi" msgid "&Play" msgstr "&Soita" msgid "Show &Picture" msgstr "Näytä k&uva" msgid "Auto &Hide Tags" msgstr "&Piilota automaattisesti" msgid "Select All in &Directory" msgstr "Valitse kaikki &hakemistossa" msgid "&Previous File" msgstr "&Edellinen tiedosto" msgid "&Next File" msgstr "&Seuraava tiedosto" msgid "From Filename" msgstr "Tiedostonimestä" msgid "From Tag 2" msgstr "2-tagista" msgid "From Tag 1" msgstr "1-tagista" msgid "Frames:" msgstr "Kehykset:" msgid "Edit" msgstr "Muokkaa" msgid "Add" msgstr "Lisää" msgid "Delete" msgstr "Poista" msgid "Filename" msgstr "Tiedostonimi" msgid "Focus" msgstr "Kohdistus" msgid "File List" msgstr "Tiedostoluettelo" msgid "Directory List" msgstr "Hakemistolista" msgid "Configure - Kid3" msgstr "Asetukset – Kid3" msgid "Open" msgstr "Avaa" msgid "Save As" msgstr "Tallenna nimellä" msgid "directory to open" msgstr "avattava kansio" msgid "Kid3 ID3 Tagger" msgstr "Kid3, ID3-tagimuokkaaja" #. i18n NAME OF TRANSLATORS msgid "Your names" msgstr "Tommi Nieminen, Ville Skyttä" #. i18n EMAIL OF TRANSLATORS msgid "Your emails" msgstr "translator@legisign.org, ville.skytta@iki.fi" msgid "&Back" msgstr "&Takaisin" msgid "&Forward" msgstr "&Seuraava" msgid "&Find:" msgstr "&Etsi:" msgid "Find Previous" msgstr "Etsi edellinen" msgid "Find Next" msgstr "Etsi seuraava" msgid "&Close" msgstr "&Sulje" msgid "&Tags" msgstr "&Tagit" msgid "&Files" msgstr "&Tiedostot" msgid "&User Actions" msgstr "&Käyttäjän toimet" msgid "&Network" msgstr "&Verkko" msgid "&Plugins" msgstr "&Liitännäiset" msgid "&Keyboard Shortcuts" msgstr "Näppäimistön pi&kavalinnat" msgid "Use custom app&lication font" msgstr "Käytä &omaa kirjasinta sovelluksessa" msgid "A&pplication Font..." msgstr "&Käytettävä kirjasin..." msgid "Use custom application &style" msgstr "Käytä sovelluksessa omaa t&yyliä" msgid "Unknown" msgstr "Tuntematon" msgid "Use native system file &dialogs" msgstr "Käytä kotoperäisiä järjestelmän tie&dostovalintaikkunoita" msgid "&Appearance" msgstr "&Ulkonäkö" msgid "&Help" msgstr "O&hje" msgid "&OK" msgstr "&OK" msgid "&Cancel" msgstr "&Peruuta" msgid "The keyboard shortcut '%1' is already assigned to '%2'." msgstr "Pikanäppäin '%1' on jo kytketty toimintoon '%2'." msgid "&File" msgstr "&Tiedosto" msgid "&Open..." msgstr "&Avaa..." msgid "Open &Recent" msgstr "Avaa &viimeaikainen" msgid "&Save" msgstr "&Tallenna" msgid "Re&vert" msgstr "K&umoa" msgid "Import from %1" msgstr "Tuo %1ista" msgid "Browse album cover artwork" msgstr "Selaa kansikuvia" msgid "Create M3U Playlist" msgstr "Luo soittolista" msgid "&Quit" msgstr "&Lopeta" msgid "&Edit" msgstr "&Muokkaa" msgid "Select &All" msgstr "Valitse &kaikki" msgid "Dese&lect" msgstr "&Poista valinta" msgid "Select all files in the current directory" msgstr "Valitse kaikki tiedostot nykyisessä hakemistossa" msgid "Select previous file" msgstr "Edellinen tiedosto" msgid "Select next file" msgstr "Seuraava tiedosto" msgid "&Tools" msgstr "T&yökalut" msgid "Apply Filename Format" msgstr "Ota muutokset käyttöön" msgid "Apply Tag Format" msgstr "Ota muutokset käyttöön" msgid "Apply Text Encoding" msgstr "Ota merkistökoodaus" msgid "Rename Directory" msgstr "Muuta kansion nimeä" msgid "Number Tracks" msgstr "Kappaleiden numerointi" msgid "&Settings" msgstr "&Asetukset" msgid "Enables/disables the toolbar" msgstr "Ottaa käyttöön tai poistaa käytöstä työkalupalkin" msgid "Show &Toolbar" msgstr "Näytä &työkalurivi" msgid "Enables/disables the statusbar" msgstr "Ottaa käyttöön tai poistaa käytöstä tilarivin" msgid "Show St&atusbar" msgstr "Näytä &tilarivi" msgid "Show Picture" msgstr "Näytä kuva" msgid "Auto Hide Tags" msgstr "Piilota automaattisesti" msgid "Configure Kid3" msgstr "Kid3:n asetukset" msgid "&Configure Kid3..." msgstr "Kid3:n &asetukset..." msgid "Kid3 Handbook" msgstr "Kid3 Käsikirja" msgid "Kid3 &Handbook" msgstr "Kid3 &Käsikirja" msgid "About Kid3" msgstr "Tietoja Kid3:sta" msgid "&About Kid3" msgstr "&Tietoja Kid3:sta" msgid "About Qt" msgstr "Tietoja Qt:stä" msgid "About &Qt" msgstr "Tietoja &Qt:stä" msgid " [modified]" msgstr " [muutettu]" msgid "Toggle the statusbar..." msgstr "Näytä/piilota tilarivi..." msgid "Ready." msgstr "Valmis." msgid "&Clear List" msgstr "&Tyhjennä luettelo" msgid "Clear" msgstr "Tyhjennä" msgid "Reset" msgstr "Nollaa" msgid "Press F2 or double click to edit cell contents." msgstr "" msgid "Action" msgstr "Toiminto" msgid "Shortcut" msgstr "Näppäinyhdistelmä" msgid "Server" msgstr "Palvelin" msgid "Accuracy" msgstr "Tarkkuus" msgid "Standard Tags" msgstr "Standardit tagit" msgid "Additional Tags" msgstr "Lisätagit" msgid "Cover Art" msgstr "Kansikuvat" msgid "Invalid File" msgstr "Virheellinen tiedosto" msgid "Data received: %1" msgstr "Vastaanotettu data: %1" msgid "Request sent..." msgstr "Pyyntö lähetetty..." msgid "Year" msgstr "Vuosi" msgid "Length" msgstr "Kesto" msgid "Filenames" msgstr "Tiedostot" msgid "URLs" msgstr "Osoitteet" msgid "Directory name" msgstr "Kansion nimi" msgid "Encode as URL" msgstr "Koodaa URLiksi" msgid "Confirm" msgstr "Vahvista" msgid "Output" msgstr "Tulos" msgid "Command" msgstr "Komento" msgid "Create directory %1 failed\n" msgstr "Kansion %1 luonti epäonnistui\n" msgid "File %1 already exists\n" msgstr "Tiedosto %1 on jo olemassa\n" msgid "%1 is not a directory\n" msgstr "%1 ei ole kansio\n" msgid "Rename %1 to %2 failed\n" msgstr "Nimen muutos %1 -> %2 epäonnistui\n" msgid "%1 already exists\n" msgstr "%1 on jo olemassa\n" msgid "%1 is not a file\n" msgstr "%1 ei ole tiedosto\n" msgid "New directory name is too different\n" msgstr "Hakemiston uusi nimi on liian erilainen\n" msgid "Create directory" msgstr "Luo kansio" msgid "Rename file" msgstr "Muuta tiedoston nimeä" msgid "C&lear" msgstr "&Tyhjennä" msgid "Execute " msgstr "Suorita" msgid "Could not execute " msgstr "Ei voitu suorittaa " msgid "True if strings are equal" msgstr "Tosi, jos merkkijonot ovat yhtäläiset" msgid "True if string contains substring" msgstr "Tosi, jos merkkijono sisältää alijonon" msgid "True if string matches regexp" msgstr "Tosi, jos merkkijono vastaa sääntölauseketta" msgid "Logical AND" msgstr "Looginen JA" msgid "Logical OR" msgstr "Looginen TAI" msgid "Logical negation" msgstr "Looginen kielto" msgid "Data" msgstr "Data" msgid "%1 is not writable" msgstr "%1 ei voi kirjoittaa" msgid "All Supported Files" msgstr "Kaikki tuetut tiedostot" msgid "All Files" msgstr "Kaikki tiedostot" msgid "Track" msgstr "Raita" msgid "Absolute path to file" msgstr "Tiedoston absoluuttinen polku" msgid "Duration" msgstr "Kesto" msgid "Title" msgstr "Nimi" msgid "Artist" msgstr "Artisti" msgid "Album" msgstr "Levyn nimi" msgid "Comment" msgstr "Kommentti" msgid "Date" msgstr "Päiväys" msgid "Track Number" msgstr "Kappaleen raita" msgid "Genre" msgstr "Tyylilaji" msgid "Album Artist" msgstr "Levyn artisti" msgid "Arranger" msgstr "Sovittaja" msgid "Author" msgstr "Tekijä" msgid "BPM" msgstr "BPM" msgid "Catalog Number" msgstr "Kataloginumero" msgid "Compilation" msgstr "Kokoelmalevy" msgid "Composer" msgstr "Säveltäjä" msgid "Conductor" msgstr "Kapellimestari" msgid "Copyright" msgstr "Tekijänoikeudet" msgid "Disc Number" msgstr "Levynumero" msgid "Encoded-by" msgstr "Pakannut" msgid "Encoder Settings" msgstr "Pakkausasetukset" msgid "Encoding Time" msgstr "Koodausajankohta" msgid "Grouping" msgstr "Ryhmittely" msgid "Initial Key" msgstr "Alkusävellaji" msgid "ISRC" msgstr "ISRC" msgid "Language" msgstr "Kieli" msgid "Lyricist" msgstr "Sanoittaja" msgid "Lyrics" msgstr "Sanoitus" msgid "Media" msgstr "Media" msgid "Mood" msgstr "Tunnelma" msgid "Original Album" msgstr "Alkuperäinen levy" msgid "Original Artist" msgstr "Alkuperäinen artisti" msgid "Original Date" msgstr "Alkuperäinen päiväys" msgid "Part" msgstr "Osa" msgid "Performer" msgstr "Esiintyjä" msgid "Picture" msgstr "Kuva" msgid "Publisher" msgstr "Julkaisija" msgid "Release Country" msgstr "Julkaisumaa" msgid "Remixer" msgstr "Uudelleen miksannut" msgid "Sort Album" msgstr "Levyjen lajittelujärjestys" msgid "Sort Album Artist" msgstr "Albumiartistien lajittelujärjestys" msgid "Sort Artist" msgstr "Esittäjien lajittelujärjestys" msgid "Sort Composer" msgstr "Säveltäjien lajittelujärjestys" msgid "Sort Name" msgstr "Nimikkeiden lajittelujärjestys" msgid "Subtitle" msgstr "Tekstitys" msgid "Website" msgstr "Sivusto" msgid "WWW Audio File" msgstr "Äänitiedoston WWW" msgid "WWW Audio Source" msgstr "Äänilähteen WWW" msgid "Number of tracks" msgstr "Kappaleiden lukumäärä" msgid "Extension" msgstr "Tiedostopääte" msgid "Bitrate" msgstr "Bittinopeus" msgid "VBR" msgstr "Vaihteleva bittinopeus" msgid "Samplerate" msgstr "Näytteenottotaajuus" msgid "Channels" msgstr "Kanavat" msgid "Codec" msgstr "Koodekki" msgid "Escape for HTML" msgstr "" msgid "Automatic Import" msgstr "Tuo automaattisesti" msgid "D&estination:" msgstr "Koh&de:" msgid "Tag 1 and Tag 2" msgstr "1- ja 2-tagit" msgid "&Profile:" msgstr "&Profiili:" msgid "&Save Settings" msgstr "&Tallenna asetukset" msgid "New" msgstr "Uusi" msgid "A&bort" msgstr "&Keskeytä" msgid "S&tart" msgstr "&Alku" msgid "Import Source" msgstr "Tuonnin lähde" msgid "&Server:" msgstr "&Palvelin:" msgid "&Accuracy:" msgstr "T&arkkuus:" msgid "&Standard Tags" msgstr "&Standardit tagit" msgid "&Additional Tags" msgstr "&Lisätagit" msgid "C&over Art" msgstr "&Kansikuvat" msgid "Browse Cover Art" msgstr "Selaa kansikuvia" msgid "&Artist/Album" msgstr "&Artisti/Levy" msgid "&Source" msgstr "&Lähde" msgid "Source:" msgstr "Lähde:" msgid "URL:" msgstr "Osoite:" msgid "&URL extraction" msgstr "&Osoitteen tunnistus" msgid "Match" msgstr "Sovita" msgid "Picture URL" msgstr "Kuvan osoite" msgid "&Browse" msgstr "&Selaa" msgid "Click Browse to start" msgstr "Aloita napsauttamalla Selaa" msgid "Then drag the picture from the browser to Kid3." msgstr "Vedä sitten kuva selaimesta Kid3:een." msgid "ID3v1" msgstr "ID3v1" msgid "&Mark truncated fields" msgstr "&Merkitse lyhennetyt kentät" msgid "Text &encoding:" msgstr "Merkist&ökoodaus:" msgid "ID3v2" msgstr "ID3v2" msgid "Use &track/total number of tracks format" msgstr "Numeroi &raidat muodossa raita/kaikkiaan" msgid "Track number &digits:" msgstr "Numeroiden &määrä:" msgid "&Genre as text instead of numeric string" msgstr "&Tyylilaji tekstinä numerojonon sijaan" msgid "ISO-8859-1" msgstr "ISO-8859-1" msgid "UTF16" msgstr "UTF-16" msgid "UTF8" msgstr "UTF-8" msgid "&Version used for new tags:" msgstr "&Uusien tagien versio:" msgid "ID3v2.3.0" msgstr "ID3v2.3.0" msgid "ID3v2.4.0" msgstr "ID3v2.4.0" msgid "Ogg/Vorbis" msgstr "Ogg/Vorbis" msgid "Co&mment field name:" msgstr "K&ommenttikentän nimi:" msgid "&Picture field name:" msgstr "&Kuvakentän nimi:" msgid "Custom &Genres" msgstr "Omat t&yylilajit" msgid "&Show only custom genres" msgstr "Näytä &vain omat tyylilajit" msgid "&Quick Access Tags" msgstr "" msgid "&Tag Format" msgstr "Ta&gin muoto" msgid "Tag &1" msgstr "&1-tagi" msgid "Tag &2" msgstr "&2-tagi" msgid "Tag 1 a&nd Tag 2" msgstr "1- &ja 2-tagit" msgid "Startup" msgstr "Käynnistys" msgid "&Load last-opened files" msgstr "&Lataa viimeksi avatut tiedostot" msgid "Save" msgstr "Tallenna" msgid "&Preserve file timestamp" msgstr "&Säilytä tiedoston päiväys" msgid "&Mark changes" msgstr "&Merkitse muutokset" msgid "F&ilename for cover:" msgstr "Kans&ikuvan tiedostonimi:" msgid "&Filename Format" msgstr "&Tiedostonimen muoto" msgid "Browser" msgstr "Selain" msgid "Web &browser:" msgstr "&Web-selain:" msgid "Context &Menu Commands" msgstr "&Kontekstivalikon komennot" msgid "&Play on double click" msgstr "&Soita tuplaklikkaamalla" msgid "Proxy" msgstr "Välityspalvelin" msgid "&Proxy:" msgstr "&Välityspalvelin:" msgid "&Use authentication with proxy" msgstr "T&unnistaudu välityspalvelimeen" msgid "Proxy user &name:" msgstr "K&äyttäjätunnus:" msgid "Proxy pass&word:" msgstr "&Salasana:" msgid "&Metadata Plugins && Priority" msgstr "&Metatiedot liitännäiset && Prioriteetti" msgid "A&vailable Plugins" msgstr "&Käytettävissä olevat liitännäiset" msgid "Changes take only effect after a restart!" msgstr "Muutos astuu voimaan uudelleenkäynnistettäessä!" msgid "From Clip&board" msgstr "&Leikepöydältä" msgid "&Import" msgstr "&Tuo" msgid "&Export" msgstr "&Vie" msgid "&View" msgstr "&Näytä" msgid "Text Encoding" msgstr "Merkistökoodaus" msgid "Text" msgstr "Teksti" msgid "Description" msgstr "Kuvaus" msgid "Owner" msgstr "Omistaja" msgid "Email" msgstr "Sähköposti" msgid "Rating" msgstr "Arvostelu" msgid "Picture Type" msgstr "Kuvan tyyppi" msgid "Image format" msgstr "Kuvatiedoston muoto" msgid "Mimetype" msgstr "MIME-tyyppi" msgid "Counter" msgstr "Laskuri" msgid "Identifier" msgstr "Tunniste" msgid "Volume Adjustment" msgstr "Äänenvoimakkuuden säätö" msgid "Number of Bits" msgstr "Bittien määrä" msgid "Volume Change Right" msgstr "Äänenvoimakkuuden säätö (oikea)" msgid "Volume Change Left" msgstr "Äänenvoimakkuuden säätö (vasen)" msgid "Peak Volume Right" msgstr "Enimmäisäänenvoimakkuus (oikea)" msgid "Peak Volume Left" msgstr "Enimmäisäänenvoimakkuus (vasen)" msgid "Timestamp Format" msgstr "Aikaleiman muoto" msgid "Content Type" msgstr "Sisällön tyyppi" msgid "Price" msgstr "Hinta" msgid "Seller" msgstr "Myyjä" msgid "Images" msgstr "Kuvat" msgid "UTF16BE" msgstr "UTF-16BE" msgid "Other" msgstr "Muut" msgid "32x32 pixels PNG file icon" msgstr "32×32 pisteen PNG-kuvake" msgid "Other file icon" msgstr "Muu kuvake" msgid "Cover (front)" msgstr "Etukansi" msgid "Cover (back)" msgstr "Takakansi" msgid "Leaflet page" msgstr "Lehtiön sivu" msgid "Lead artist/lead performer/soloist" msgstr "Pääartisti/-esiintyjä/-sooloartisti" msgid "Artist/performer" msgstr "Artisti/esiintyjä" msgid "Band/Orchestra" msgstr "Yhtye/orkesteri" msgid "Lyricist/text writer" msgstr "Sanoittaja/tekstin tuottaja" msgid "Recording Location" msgstr "Tallennuspaikka" msgid "During recording" msgstr "Tallennuksen aikana" msgid "During performance" msgstr "Esityksen aikana" msgid "Movie/video screen capture" msgstr "Videokuvakaappaus" msgid "A bright coloured fish" msgstr "Kirkasvärinen kala" msgid "Illustration" msgstr "Kuvitus" msgid "Band/artist logotype" msgstr "Yhtyeen/artistin logotyyppi" msgid "Publisher/Studio logotype" msgstr "Julkaisijan/studion logotyyppi" msgid "MPEG frames as unit" msgstr "Yksiköinä MPEG-kuvakehykset" msgid "Milliseconds as unit" msgstr "Yksiköinä millisekunnit" msgid "Text transcription" msgstr "Tekstin transkriptio" msgid "Movement/part name" msgstr "Osan nimi" msgid "Events" msgstr "Tapahtumat" msgid "Chord" msgstr "Sointu" msgid "Trivia/pop up" msgstr "Nippelitietoa" msgid "Export" msgstr "Vie" msgid "Format:" msgstr "Muoto:" msgid "Header:" msgstr "Ylätunniste" msgid "Tracks:" msgstr "Kappale:" msgid "Footer:" msgstr "Alatunniste:" msgid "To F&ile" msgstr "T&iedostoon" msgid "To Clip&board" msgstr "&Leikepöydälle" msgid "&Source:" msgstr "&Lähde:" msgid "File Error" msgstr "Tiedostovirhe" msgid "&Filter:" msgstr "&Suodin:" msgid "&Expression:" msgstr "&Lauseke:" msgid "&Apply" msgstr "&Käytä" msgid "Accuracy:" msgstr "Tarkkuus:" msgid "Cover Art:" msgstr "Kansikuvat:" msgid "From F&ile/Clipboard..." msgstr "Tiedostosta/&leikepöydältä..." msgid "From T&ags..." msgstr "T&agista..." msgid "&From Server:" msgstr "&Palvelimelta:" msgid "Check maximum allowable time &difference (sec):" msgstr "Aseta &enimmäisaikaero (s):" msgid "Match with:" msgstr "Sovita:" msgid "&Length" msgstr "&Kestoon" msgid "T&rack" msgstr "&Raitaan" msgid "&Title" msgstr "&Nimeen" msgid "&Start number:" msgstr "&Aloittava numero:" msgid "&Destination:" msgstr "&Kohde:" msgid "&Total number of tracks:" msgstr "Kappaleiden &lukumäärä:" msgid "Create Playlist" msgstr "Luo soittolista" msgid "Playlist File Name" msgstr "Soittolistatiedoston nimi" msgid "Same as &directory name" msgstr "Käytä &kansion nimeä" msgid "&Format:" msgstr "&Muoto:" msgid "Cr&eate in:" msgstr "&Luo kansioon:" msgid "Current directory" msgstr "Nykyinen kansio" msgid "Every directory" msgstr "Jokainen kansio" msgid "Top-level directory" msgstr "Ylimmän tason kansio" msgid "Playlist Content" msgstr "Soittolistan sisältö" msgid "For&mat:" msgstr "&Muoto:" msgid "Incl&ude only the selected files" msgstr "S&isällytä vain valitut tiedostot" msgid "Sort by file &name" msgstr "Lajittele tiedosto&nimen mukaan" msgid "Sort by &tag field" msgstr "Lajittele ta&gin mukaan" msgid "Use &relative path for files in playlist" msgstr "Käytä soittolistan tiedostoille s&uhteellista polkua" msgid "Use full p&ath for files in playlist" msgstr "Käytä soittolistan tiedostoille &täyttä polkua" msgid "Write only &list of files" msgstr "Kirjoita &vain tiedostoluettelo" msgid "Write &info using" msgstr "Kirjoita tieto k&äyttäen" msgid "Preview" msgstr "Esikatselu" msgid "Create Directory" msgstr "Luo kansio" msgid "From Tag 2 and Tag 1" msgstr "2- ja 1-tageista" msgid "From:" msgstr "Alkuperäinen:" msgid "To:" msgstr "Uusi:" msgid "&Find" msgstr "&Etsi" msgid "C&GI Path:" msgstr "&CGI-polku:" msgid "Track Title/Artist - Album" msgstr "Kappaleen nimi/artisti - levy" msgid "State" msgstr "Tila" msgid "No result" msgstr "Ei tuloksia" msgid "No result selected" msgstr "Ei valittuja tuloksia" msgid "Import from Tags" msgstr "Tuo tagista" msgid "Extraction:" msgstr "Uuttaminen:" msgid "Import from File/Clipboard" msgstr "Tuo tiedostosta/leikepöydältä" msgid "From F&ile" msgstr "&Tiedostosta" msgid "Download" msgstr "Lataa" msgid "Opening directory..." msgstr "Avaa kansio..." msgid "Saving directory..." msgstr "Tallennetaan kansio..." msgid "" "The current directory has been modified.\n" "Do you want to save it?" msgstr "" "Nykyistä kansiota on muokattu.\n" "Haluatko tallentaa sen?" msgid "Warning" msgstr "Varoitus" msgid "Exiting..." msgstr "Poistutaan..." msgid "Creating playlist..." msgstr "Luo soittolista..." msgid "Import" msgstr "Tuo" msgid "Error while renaming:\n" msgstr "Virhe muutettaessa nimeä:\n" msgid " [filtered]" msgstr " [suodatettu]" msgid "Add Frame" msgstr "Lisää kehys" msgid "Select the frame ID" msgstr "Valitse kehyksen tunniste" msgid "Rename File" msgstr "Muuta tiedoston nimeä" msgid "Enter new file name:" msgstr "Anna tiedoston uusi nimi:" msgid "Do you really want to move these %1 items to the trash?" msgstr "Haluatko varmasti siirtää seuraavat %1 tietuetta roskakoriin?" msgid "Do you really want to move this item to the trash?" msgstr "Haluatko varmasti siirtää tämän tietueen roskakoriin?" msgid "Move to Trash" msgstr "Siirrä roskakoriin" msgid "Directory must be empty.\n" msgstr "Kansion on oltava tyhjä.\n" msgid "Could not move these files to the Trash" msgstr "Tiedostoja ei voitu siirtää Roskakoriin" msgid "Expand All" msgstr "Levitä kaikki" msgid "&Expand all" msgstr "&Levitä kaikki" msgid "&Collapse all" msgstr "&Kokoa kaikki" msgid "&Rename" msgstr "M&uuta nimeä" msgid "&Move to Trash" msgstr "&Siirrä roskakoriin" msgid "Kid3" msgstr "Kid3" msgid "F&ile" msgstr "T&iedosto" msgid "Name:" msgstr "Nimi:" msgid "Filename from Tag 1" msgstr "Tiedostonimi 1-tagista" msgid "Filename from Tag 2" msgstr "Tiedostonimi 2-tagista" msgid "Tag 1 from Filename" msgstr "1-tagi tiedostonimestä" msgid "Tag 2 from Filename" msgstr "2-tagi tiedostonimestä" msgid "&Add..." msgstr "L&isää..." msgid "Move &Up" msgstr "Siirrä &ylemmäs" msgid "Move &Down" msgstr "Siirrä &alemmas" msgid "&Edit..." msgstr "&Muokkaa..." msgid "&Remove" msgstr "&Poista" msgid "&Insert row" msgstr "&Lisää rivi" msgid "&Delete row" msgstr "&Poista rivi" msgid "&Clear row" msgstr "&Tyhjennä rivi" msgid "Format while editing" msgstr "Muotoile muokattaessa" msgid "Case conversion:" msgstr "Kirjaintason muunnos:" msgid "No changes" msgstr "Ei mitään" msgid "All lowercase" msgstr "Kokonaan pienaakkosiin" msgid "All uppercase" msgstr "Kokonaan suuraakkosiin" msgid "First letter uppercase" msgstr "Alkukirjain suuraakkosiin" msgid "All first letters uppercase" msgstr "Kaikki alkukirjaimet suuraakkosiin" msgid "Locale:" msgstr "Maa-asetukset:" msgid "None" msgstr "Ei mitään" msgid "String replacement:" msgstr "Merkkijonojen korvaus:" msgid "From" msgstr "Korvattava" msgid "To" msgstr "Korvaava" msgid "&Add" msgstr "L&isää" msgid "&Select all" msgstr "&Valitse kaikki" msgid "&Deselect all" msgstr "&Poista valinnat" msgid "View Picture" msgstr "Katso kuvaa" msgid "" "Drag album\n" "artwork\n" "here" msgstr "" "Vedä albumin\n" "kansikuva\n" "tähän" msgid "Play/Pause" msgstr "Soita/tauko" msgid "Stop playback" msgstr "Pysäytä soitto" msgid "Previous Track" msgstr "Edellinen kappale" msgid "Next Track" msgstr "Seuraava kappale" msgid "Close" msgstr "Sulje" msgid "Mute" msgstr "Vaimenna" msgid "Volume: %1%" msgstr "Äänenvoimakkuus: %1%" msgid "Add Item" msgstr "Lisää osa" msgid "Edit Item" msgstr "Muokkaa osaa" msgid "MusicBrainz Fingerprint" msgstr "MusicBrainzin sormenjälki" msgid "Unrecognized" msgstr "Tunnistamaton" msgid "Recognized" msgstr "Tunnistettu" msgid "User Selection" msgstr "Käyttäjän valinta" msgid "ID Lookup" msgstr "ID-haku" msgid "Fingerprint" msgstr "Sormenjälki" msgid "Metadata Lookup" msgstr "Metadata luettu" msgid "Amazon" msgstr "Amazon" msgid "Discogs" msgstr "Discogs" msgid "gnudb.org" msgstr "gnudb.org" msgid "TrackType.org" msgstr "TrackType.org" msgid "AENC - Audio encryption" msgstr "AENC – Äänen koodaus" msgid "APIC - Attached picture" msgstr "APIC – Liitetty kuva" msgid "COMM - Comments" msgstr "COMM – Kommentit" msgid "COMR - Commercial" msgstr "COMR – Kaupallinen" msgid "ENCR - Encryption method registration" msgstr "ENCR – Salausmenetelmän rekisteröinti" msgid "EQUA - Equalization" msgstr "EQUA – Ekvalisointi" msgid "ETCO - Event timing codes" msgstr "ETCO – Tapahtumien aikakoodit" msgid "GEOB - General encapsulated object" msgstr "GEOB – Yleinen sisällytetty objekti" msgid "GRID - Group identification registration" msgstr "GRID – Ryhmätunnisteen rekisteröinti" msgid "IPLS - Involved people list" msgstr "IPLS – Mukana olleiden henkilöiden luettelo" msgid "LINK - Linked information" msgstr "LINK – Linkattu tieto" msgid "MCDI - Music CD identifier" msgstr "MCDI – Musiikki-CD:n tunniste" msgid "MLLT - MPEG location lookup table" msgstr "MLLT – MPEGin sijainninhakutaulukko" msgid "OWNE - Ownership frame" msgstr "OWNE – Omistajakehys" msgid "PRIV - Private frame" msgstr "PRIV – Yksityiskehys" msgid "PCNT - Play counter" msgstr "PCNT – Soittokertalaskuri" msgid "POPM - Popularimeter" msgstr "POPM – Suosiomittari" msgid "POSS - Position synchronisation frame" msgstr "POSS – Sijainnin tahdistuskehys" msgid "RBUF - Recommended buffer size" msgstr "RBUF – Suositeltu puskurikoko" msgid "RVAD - Relative volume adjustment" msgstr "RVAD – Äänenvoimakkuuden suhteellinen säätö" msgid "RVRB - Reverb" msgstr "RVRB – Kaikuefekti" msgid "SYLT - Synchronized lyric/text" msgstr "SYLT – Tahdistettu sanoitus/teksti" msgid "SYTC - Synchronized tempo codes" msgstr "SYTC – Tahdistetut tempokoodit" msgid "TALB - Album/Movie/Show title" msgstr "TALB – Levyn/elokuvan/sarjan otsikko" msgid "TBPM - BPM (beats per minute)" msgstr "TBPM – BPM (iskua minuutissa)" msgid "TCOM - Composer" msgstr "TCOM – Säveltäjä" msgid "TCON - Content type" msgstr "TCON – Sisältötyyppi" msgid "TCOP - Copyright message" msgstr "TCOP – Tekijänoikeustiedote" msgid "TDAT - Date" msgstr "TDAT – Päiväys" msgid "TDLY - Playlist delay" msgstr "TDLY – Soittolistan viive" msgid "TENC - Encoded by" msgstr "TENC – Koodaaja" msgid "TEXT - Lyricist/Text writer" msgstr "TEXT – Sanoittaja/tekstin kirjoittaja" msgid "TFLT - File type" msgstr "TFLT – Tiedoston tyyppi" msgid "TIME - Time" msgstr "TIME – Aika" msgid "TIT1 - Content group description" msgstr "TIT1 – Sisältöryhmän kuvaus" msgid "TIT2 - Title/songname/content description" msgstr "TIT2 – Otsikon/kappaleen nimen/sisällön kuvaus" msgid "TIT3 - Subtitle/Description refinement" msgstr "TIT3 – Tekstitys/kuvauksen täsmennys" msgid "TKEY - Initial key" msgstr "TKEY – Alkusävellaji" msgid "TLAN - Language(s)" msgstr "TLAN – Kieli tai kielet" msgid "TLEN - Length" msgstr "TLEN – Kesto" msgid "TMED - Media type" msgstr "TMED – Mediatyyppi" msgid "TOAL - Original album/movie/show title" msgstr "TOAL – Alkuperäinen levyn/elokuvan/sarjan otsikko" msgid "TOFN - Original filename" msgstr "TOFN – Alkuperäinen tiedostonimi" msgid "TOLY - Original lyricist(s)/text writer(s)" msgstr "TOLY – Alkuperäinen sanoittaja/tekstin kirjoittaja" msgid "TOPE - Original artist(s)/performer(s)" msgstr "TOPE – Alkuperäinen artisti/esiintyjä" msgid "TORY - Original release year" msgstr "TORY – Alkuperäinen julkaisuvuosi" msgid "TOWN - File owner/licensee" msgstr "TOWN – Tiedoston omistaja/lisenssi" msgid "TPE1 - Lead performer(s)/Soloist(s)" msgstr "TPE1 – Pääesiintyjä(t)/sooloartisti(t)" msgid "TPE2 - Band/orchestra/accompaniment" msgstr "TPE2 – Yhtye/orkesteri/säestys" msgid "TPE3 - Conductor/performer refinement" msgstr "TPE3 – Kapellimestari tai esityksen parantelija" msgid "TPE4 - Interpreted, remixed, or otherwise modified by" msgstr "TPE4 – Tulkinnut, uudelleen miksannut tai muuten muokannut" msgid "TPOS - Part of a set" msgstr "TPOS – Osa joukkoa" msgid "TPUB - Publisher" msgstr "TPUB – Julkaisija" msgid "TRCK - Track number/Position in set" msgstr "TRCK – Kappaleen raita/sijainti joukossa" msgid "TRDA - Recording dates" msgstr "TRDA – Tallennuspäivät" msgid "TRSN - Internet radio station name" msgstr "TRSN – Internet-radioaseman nimi" msgid "TRSO - Internet radio station owner" msgstr "TRSO – Internetin radioaseman omistaja" msgid "TSIZ - Size" msgstr "TSIZ – Koko" msgid "TSRC - ISRC (international standard recording code)" msgstr "TSRC – ISRC (kansainvälinen standardi nauhoituskoodi)" msgid "TSSE - Software/Hardware and settings used for encoding" msgstr "TSSE – Koodauksessa käytetty ohjelmisto tai laitteisto sekä asetukset" msgid "TXXX - User defined text information" msgstr "TXXX – Käyttäjän määrittämä tekstitieto" msgid "TYER - Year" msgstr "TYER – Vuosi" msgid "UFID - Unique file identifier" msgstr "UFID – Tiedoston ainutkertainen tunniste" msgid "USER - Terms of use" msgstr "USER – Käyttöehdot" msgid "USLT - Unsynchronized lyric/text transcription" msgstr "USLT – Tahdistamaton sanoitus tai tekstitranskriptio" msgid "WCOM - Commercial information" msgstr "WCOM – Kaupallinen tieto" msgid "WCOP - Copyright/Legal information" msgstr "WCOP – Tekijänoikeustiedot" msgid "WOAF - Official audio file webpage" msgstr "WOAF – Äänitiedoston virallinen verkkosivu" msgid "WOAR - Official artist/performer webpage" msgstr "WOAR – Artistin/esiintyjän virallinen verkkosivu" msgid "WOAS - Official audio source webpage" msgstr "WOAS – Äänilähteen virallinen verkkosivu" msgid "WORS - Official internet radio station homepage" msgstr "WORS – Internetin radioaseman virallinen verkkosivu" msgid "WPAY - Payment" msgstr "WPAY – Maksaminen" msgid "WPUB - Official publisher webpage" msgstr "WPUB – Julkaisijan virallinen verkkosivu" msgid "WXXX - User defined URL link" msgstr "WXXX – Käyttäjän määrittämä verkko-osoite" msgid "MusicBrainz Release" msgstr "MusicBrainzin julkaisu" msgid "ASPI - Audio seek point index" msgstr "ASPI – Äänihaun sijainti-indeksi" msgid "EQU2 - Equalisation (2)" msgstr "EQU2 – Ekvalisaatio (2)" msgid "RVA2 - Relative volume adjustment (2)" msgstr "RVA2 – Suhteellinen äänenvoimakkuuden säätö (2)" msgid "SEEK - Seek frame" msgstr "SEEK – Hakukehys" msgid "SIGN - Signature frame" msgstr "SIGN – Allekirjoituskehys" msgid "TCMP - iTunes compilation flag" msgstr "TCMP - iTunes kokoelma" msgid "TDEN - Encoding time" msgstr "TDEN – Koodausajankohta" msgid "TDOR - Original release time" msgstr "TDOR – Alkuperäinen julkaisuajankohta" msgid "TDRC - Recording time" msgstr "TDRC – Äänitysajankohta" msgid "TDRL - Release time" msgstr "TRDL – Julkaisuajankohta" msgid "TDTG - Tagging time" msgstr "TDTG – Luokitusajankohta" msgid "TIPL - Involved people list" msgstr "TIPL – Mukana olleiden henkilöiden luettelo" msgid "TMCL - Musician credits list" msgstr "TMCL – Ansioituneiden muusikkojen luettelo" msgid "TMOO - Mood" msgstr "TMOO – Tunnelma" msgid "TPRO - Produced notice" msgstr "TPRO – Tuotantoilmoitus" msgid "TSO2 - Album artist sort order" msgstr "TSO2 – Albumiartistien lajittelujärjestys" msgid "TSOA - Album sort order" msgstr "TSOA – Levyjen lajittelujärjestys" msgid "TSOC - Composer sort order" msgstr "TSOC – Säveltäjien lajittelujärjestys" msgid "TSOP - Performer sort order" msgstr "TSOP – Esittäjien lajittelujärjestys" msgid "TSOT - Title sort order" msgstr "TSOT – Nimikkeiden lajittelujärjestys" msgid "TSST - Set subtitle" msgstr "TSST – Aseta tekstitys" ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/po/fr.po���������������������������������������������������������������������������������0000664�0000000�0000000�00000107231�12246035113�0013734�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# translation of fr.po to English # translation of fr.po to # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # pierre <pierresaliou@yahoo.fr>, 2006. # Urs Fleisch <ufleisch@users.sourceforge.net>, 2006, 2007, 2008, 2009, 2010, 2011. msgid "" msgstr "" "Project-Id-Version: Kid3 3.0.2\n" "Report-Msgid-Bugs-To: http://sourceforge.net/p/kid3/bugs/\n" "POT-Creation-Date: 2013-09-11 21:22+0200\n" "PO-Revision-Date: 2011-01-15 17:32+0100\n" "Last-Translator: \n" "Language-Team: French <kde-i18n-doc@kde.org>\n" "Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Lokalize 1.1\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" "X-Language: fr\n" "X-Source-Language: en\n" msgid "Timeout" msgstr "Délai dépassé" msgid "Usage:" msgstr "Utilisation:" msgid "Help" msgstr "Aide" msgid "Command name" msgstr "Nom de la commande" msgid "Quit application" msgstr "Quitter l'application" msgid "The current directory has been modified." msgstr "Le répertoire courant a été modifié." msgid "Type 'exit force' to quit." msgstr "Tapez 'exit force' pour quitter." msgid "Change directory" msgstr "Changer de répertoire" msgid "%1 does not exist" msgstr "%1 n'existe pas" msgid "Print the filename of the current working directory" msgstr "Afficher le nom du répertoire courant" msgid "Directory list" msgstr "Liste des répertoires" msgid "Saves the changed files" msgstr "Sauvegarder les fichiers modifiés" msgid "Error while writing file:\n" msgstr "Erreur pendant l'écriture du fichier:\n" msgid "Select file" msgstr "Sélectionnez un fichier" msgid "%1 not found" msgstr "%1 introuvable" msgid "Select tag" msgstr "Sélection du marqueur" msgid "Get tag frame" msgstr "Obtenir cadre de balise" msgid "Set tag frame" msgstr "Définir cadre de balise" msgid "Could not set \"%1\" for %2" msgstr "Impossible d'affecter \"%1\" à %2" msgid "Revert" msgstr "Revenir" msgid "Import from file or clipboard" msgstr "Importer depuis un fichier ou le presse-papier" msgid "Format name" msgstr "Nom du format" msgid "%1 not found." msgstr "%1 introuvable." msgid "Available" msgstr "Disponible" msgid "Error" msgstr "Erreur" msgid "Automatic import" msgstr "Importer automatiquement" msgid "Profile name" msgstr "Nom du profil" msgid "Reading Directory" msgstr "Lecture du répertoire" msgid "Started" msgstr "Démarré" msgid "Source" msgstr "Source" msgid "Querying" msgstr "Interrogation" msgid "Fetching" msgstr "Réception" msgid "Data received" msgstr "Donnée reçues" msgid "Cover" msgstr "Jaquettes" msgid "Finished" msgstr "Terminé" msgid "Aborted" msgstr "Abandonné" msgid "Download album cover artwork" msgstr "Télécharger la jaquette" msgid "Export to file or clipboard" msgstr "Exporter dans un fichier ou le presse-papier" msgid "Create playlist" msgstr "Créer liste de lecture" msgid "Apply filename format" msgstr "Appliquer le format du nom de fichier" msgid "Apply tag format" msgstr "Appliquer le format des balises" msgid "Apply text encoding" msgstr "Appliquer l'encodage du texte" msgid "Rename directory" msgstr "Renommer répertoire" msgid "Number tracks" msgstr "Numéroter les pistes" msgid "Track number" msgstr "Numéro de piste" msgid "Filter" msgstr "Filtre" msgid "Filter name" msgstr "Nom du filtre" msgid "Convert ID3v2.3 to ID3v2.4" msgstr "Convertir de ID3v2.3 vers ID3v2.4" msgid "Convert ID3v2.4 to ID3v2.3" msgstr "Convertir de ID3v2.4 vers ID3v2.3" msgid "Filename from tag" msgstr "Nom de fichier depuis balise" msgid "Tag from filename" msgstr "Balise depuis le nom de fichier" msgid "Tag to other tag" msgstr "Balise depuis autre balise" msgid "Copy" msgstr "Copier" msgid "Paste" msgstr "Coller" msgid "Remove" msgstr "Supprimer" msgid "Play" msgstr "Lecture" msgid "Parameter" msgstr "Paramètre" msgid "File path" msgstr "Chemin du fichier" msgid "URL" msgstr "URL" msgid "Tag numbers" msgstr "Nombres des balises" msgid "Frame name" msgstr "Nom du cadre" msgid "Frame value" msgstr "Valeur du cadre" msgid "Format" msgstr "Format" msgid "Command specific" msgstr "Spécifique de commande" msgid "Available Commands" msgstr "Commandes disponibles" msgid "File" msgstr "Fichier" msgid "Name" msgstr "Nom" msgid "Tag 2" msgstr "Balise 2" msgid "Tag 1" msgstr "Balise 1" msgid "Tags" msgstr "Balises" msgid "Unknown command '%1'. Type 'help' for help." msgstr "Commande inconnue '%1', tapez 'help' pour obtenir de l’aide." msgid "Unknown command '%1', -h for help." msgstr "Commande inconnue '%1', -h pour l'aide" msgid "Files" msgstr "Fichiers" msgid "User Actions" msgstr "Commandes utilisateur" msgid "Network" msgstr "Réseau" msgid "Plugins" msgstr "Modules externes" msgid "Main Toolbar" msgstr "Barre d'outils principale" msgid "Opens a directory" msgstr "Ouvrir un répertoire" msgid "Opens a recently used directory" msgstr "Ouvrir un fichier récemment ouvert" msgid "Reverts the changes of all or the selected files" msgstr "Annuler toutes les modifications" msgid "Quits the application" msgstr "Quitter l'application" msgid "Select all files" msgstr "Sélectionner tout" msgid "Deselect all files" msgstr "Déselectionner tout" msgid "Configure Shortcuts" msgstr "Configurer les raccourcis" msgid "Configure Toolbars" msgstr "Configurer les barres d'outils" msgid "Preferences dialog" msgstr "Boite de dialogue des préférences" msgid "O&pen Directory..." msgstr "Ouvrir ré&pertoire..." msgid "&Import..." msgstr "&Importer..." msgid "Import from %1..." msgstr "Importer depuis %1..." msgid "Automatic I&mport..." msgstr "I&mporter automatiquement..." msgid "&Browse Cover Art..." msgstr "&Télécharger la jaquette..." msgid "&Export..." msgstr "&Exporter..." msgid "&Create Playlist..." msgstr "&Créer liste de lecture..." msgid "Apply &Filename Format" msgstr "Appliquer le format du nom de &fichier" msgid "Apply &Tag Format" msgstr "Appliquer le forma&t des balises" msgid "Apply Text &Encoding" msgstr "Appliquer l'encodage du t&exte" msgid "&Rename Directory..." msgstr "&Renommer le répertoire..." msgid "&Number Tracks..." msgstr "&Numéroter les pistes..." msgid "F&ilter..." msgstr "F&iltre..." msgid "Convert ID3v2.3 to ID3v2.&4" msgstr "Convertir de ID3v2.3 vers ID3v2.&4" msgid "Convert ID3v2.4 to ID3v2.&3" msgstr "Convertir de ID3v2.4 vers ID3v2.&3" msgid "&Play" msgstr "&Lire" msgid "Show &Picture" msgstr "Afficher l'&image" msgid "Auto &Hide Tags" msgstr "&Cacher automatiquement balises" msgid "Select All in &Directory" msgstr "Sélectionner tous les fichiers du &répertoire" msgid "&Previous File" msgstr "&Fichier précédant" msgid "&Next File" msgstr "&Fichier suivant" msgid "From Filename" msgstr "Depuis le nom de fichier" msgid "From Tag 2" msgstr "Depuis balise 2" msgid "From Tag 1" msgstr "Depuis balise 1" msgid "Frames:" msgstr "Encadrement:" msgid "Edit" msgstr "Édition" msgid "Add" msgstr "Ajouter" msgid "Delete" msgstr "Supprimer" msgid "Filename" msgstr "Nom de fichier" msgid "Focus" msgstr "Focus" msgid "File List" msgstr "Liste des fichiers" msgid "Directory List" msgstr "Liste des répertoires" msgid "Configure - Kid3" msgstr "Configurer Kid3" msgid "Open" msgstr "Ouvrir" msgid "Save As" msgstr "Enregistrer sous" msgid "directory to open" msgstr "dossier à ouvrir" msgid "Kid3 ID3 Tagger" msgstr "Kid3 ID3 Tagger" #. i18n NAME OF TRANSLATORS msgid "Your names" msgstr "Pierre Saliou" #. i18n EMAIL OF TRANSLATORS msgid "Your emails" msgstr "pierresaliou@yahoo.fr" msgid "&Back" msgstr "&Précédent" msgid "&Forward" msgstr "&Suivant" msgid "&Find:" msgstr "&Trouver:" msgid "Find Previous" msgstr "Cherche le précédent" msgid "Find Next" msgstr "Cherche le suivant" msgid "&Close" msgstr "&Fermer" msgid "&Tags" msgstr "&Balises" msgid "&Files" msgstr "&Fichiers" msgid "&User Actions" msgstr "Commandes &utilisateur" msgid "&Network" msgstr "&Réseau" msgid "&Plugins" msgstr "Modules &externes" msgid "&Keyboard Shortcuts" msgstr "&Raccourcis clavier" msgid "Use custom app&lication font" msgstr "Choisir une po&lice personnalisée" msgid "A&pplication Font..." msgstr "&Police..." msgid "Use custom application &style" msgstr "Choisir un &style personnalisé" msgid "Unknown" msgstr "Inconnu" msgid "Use native system file &dialogs" msgstr "Utiliser les boîtes de &dialogue de fichiers système natives" msgid "&Appearance" msgstr "&Apparence" msgid "&Help" msgstr "&Aide" msgid "&OK" msgstr "&OK" msgid "&Cancel" msgstr "Annu&ler" msgid "The keyboard shortcut '%1' is already assigned to '%2'." msgstr "Le raccourcis clavier '%1' est déjà assigné à '%2'." msgid "&File" msgstr "&Fichier" msgid "&Open..." msgstr "&Ouvrir..." msgid "Open &Recent" msgstr "&Récemment ouvert(s)" msgid "&Save" msgstr "&Enregistrer" msgid "Re&vert" msgstr "Re&venir" msgid "Import from %1" msgstr "Importer depuis %1" msgid "Browse album cover artwork" msgstr "Télécharger la jaquette" msgid "Create M3U Playlist" msgstr "Créer liste de lecture M3U" msgid "&Quit" msgstr "&Quitter" msgid "&Edit" msgstr "&Édition" msgid "Select &All" msgstr "Sélectionner &tout" msgid "Dese&lect" msgstr "&Désélectionner" msgid "Select all files in the current directory" msgstr "Sélectionner tous les fichiers du dossier courant" msgid "Select previous file" msgstr "Fichier précédant" msgid "Select next file" msgstr "Fichier suivant" msgid "&Tools" msgstr "O&utils" msgid "Apply Filename Format" msgstr "Appliquer le format du nom de fichier" msgid "Apply Tag Format" msgstr "Appliquer le format des balises" msgid "Apply Text Encoding" msgstr "Appliquer l'encodage du texte" msgid "Rename Directory" msgstr "Renommer le répertoire" msgid "Number Tracks" msgstr "Numéroter les pistes" msgid "&Settings" msgstr "&Configuration" msgid "Enables/disables the toolbar" msgstr "Activer/désactiver la barre d'outils" msgid "Show &Toolbar" msgstr "Affic&her la barre d'outils" msgid "Enables/disables the statusbar" msgstr "Activer/désactiver la barre d'état" msgid "Show St&atusbar" msgstr "Afficher la barre d'ét&at" msgid "Show Picture" msgstr "Afficher l'image" msgid "Auto Hide Tags" msgstr "Cacher automatiquement balises" msgid "Configure Kid3" msgstr "Configurer Kid3" msgid "&Configure Kid3..." msgstr "&Configurer Kid3..." msgid "Kid3 Handbook" msgstr "Manuel de Kid3" msgid "Kid3 &Handbook" msgstr "&Manuel de Kid3" msgid "About Kid3" msgstr "A propos de Kid3" msgid "&About Kid3" msgstr "&A propos de Kid3" msgid "About Qt" msgstr "A propos de Qt" msgid "About &Qt" msgstr "A propos de &Qt" msgid " [modified]" msgstr " [modifié]" msgid "Toggle the statusbar..." msgstr "Basculer vers la barre d'état..." msgid "Ready." msgstr "Prêt." msgid "&Clear List" msgstr "E&ffacer la liste" msgid "Clear" msgstr "Effacer" msgid "Reset" msgstr "Réinitialiser" msgid "Press F2 or double click to edit cell contents." msgstr "Appuyer sur F2 ou double-cliquer pour éditer le contenu des cellules." msgid "Action" msgstr "Action" msgid "Shortcut" msgstr "Raccourci" msgid "Server" msgstr "Serveur" msgid "Accuracy" msgstr "Précision" msgid "Standard Tags" msgstr "Balises standard" msgid "Additional Tags" msgstr "Balises supplémentaires" msgid "Cover Art" msgstr "Jaquettes" msgid "Invalid File" msgstr "Fichier invalide" msgid "Data received: %1" msgstr "Donnée reçues : %1" msgid "Request sent..." msgstr "Requête envoyée..." msgid "Year" msgstr "Année" msgid "Length" msgstr "Longueur" msgid "Filenames" msgstr "Noms des fichiers" msgid "URLs" msgstr "URLs" msgid "Directory name" msgstr "Nom de répertoire" msgid "Encode as URL" msgstr "Encodée en URL" msgid "Confirm" msgstr "Confirmer" msgid "Output" msgstr "Sortie" msgid "Command" msgstr "Commande" msgid "Create directory %1 failed\n" msgstr "Échec de création du répertoire %1\n" msgid "File %1 already exists\n" msgstr "Le fichier %1 existe déjà\n" msgid "%1 is not a directory\n" msgstr "%1 n'est pas un répertoire\n" msgid "Rename %1 to %2 failed\n" msgstr "Impossible de renommer %1 en %2\n" msgid "%1 already exists\n" msgstr "%1 existe déjà\n" msgid "%1 is not a file\n" msgstr "%1 n'est pas un fichier\n" msgid "New directory name is too different\n" msgstr "Nom noveau du répertoire est trop different\n" msgid "Create directory" msgstr "Créer répertoire" msgid "Rename file" msgstr "Renommer fichier" msgid "C&lear" msgstr "E&ffacer" msgid "Execute " msgstr "Exécuter " msgid "Could not execute " msgstr "Impossible d'exécuter " msgid "True if strings are equal" msgstr "Les chaînes sont identiques" msgid "True if string contains substring" msgstr "Sous-chaîne de chaîne" msgid "True if string matches regexp" msgstr "Concordance de l'EXPREG dans la chaîne" msgid "Logical AND" msgstr "Logique et" msgid "Logical OR" msgstr "Logique ou" msgid "Logical negation" msgstr "Logique non" msgid "Data" msgstr "Donnée" msgid "%1 is not writable" msgstr "%1 n'est pas accessible en écriture" msgid "All Supported Files" msgstr "Tous les fichiers pris en charge" msgid "All Files" msgstr "Tous les fichiers" msgid "Track" msgstr "Piste" msgid "Absolute path to file" msgstr "Nom de fichier absolu" msgid "Duration" msgstr "Durée" msgid "Title" msgstr "Titre" msgid "Artist" msgstr "Artiste" msgid "Album" msgstr "Album" msgid "Comment" msgstr "Commentaire" msgid "Date" msgstr "Date" msgid "Track Number" msgstr "Numéro de piste" msgid "Genre" msgstr "Genre" msgid "Album Artist" msgstr "Artiste de l'album" msgid "Arranger" msgstr "Arrangeur" msgid "Author" msgstr "Auteur" msgid "BPM" msgstr "BPM" msgid "Catalog Number" msgstr "Numéro du catalogue" msgid "Compilation" msgstr "Compilation" msgid "Composer" msgstr "Compositeur" msgid "Conductor" msgstr "Conducteur" msgid "Copyright" msgstr "Copyright" msgid "Disc Number" msgstr "Numéro de disque" msgid "Encoded-by" msgstr "Encodé par" msgid "Encoder Settings" msgstr "Paramètres d'encodeur" msgid "Encoding Time" msgstr "Date d'encodage" msgid "Grouping" msgstr "Regroupement" msgid "Initial Key" msgstr "Clé initiale" msgid "ISRC" msgstr "ISRC" msgid "Language" msgstr "Langue" msgid "Lyricist" msgstr "Auteur" msgid "Lyrics" msgstr "Paroles" msgid "Media" msgstr "Média" msgid "Mood" msgstr "Humeur" msgid "Original Album" msgstr "Titre d'album original" msgid "Original Artist" msgstr "Artiste original" msgid "Original Date" msgstr "Date original" msgid "Part" msgstr "Partie" msgid "Performer" msgstr "Interprète" msgid "Picture" msgstr "Image" msgid "Publisher" msgstr "Éditeur" msgid "Release Country" msgstr "Pays de publication" msgid "Remixer" msgstr "Remixer" msgid "Sort Album" msgstr "Ordre de tri d'album" msgid "Sort Album Artist" msgstr "Ordre de tri d'artiste de l'album" msgid "Sort Artist" msgstr "Ordre de tri d'interprète" msgid "Sort Composer" msgstr "Ordre de tri de compositeur" msgid "Sort Name" msgstr "Ordre de tri du titre" msgid "Subtitle" msgstr "Sous-titre" msgid "Website" msgstr "Site web" msgid "WWW Audio File" msgstr "URL du fichier audio" msgid "WWW Audio Source" msgstr "URL de la source audio" msgid "Number of tracks" msgstr "Nombre de pistes" msgid "Extension" msgstr "Extension du fichier" msgid "Bitrate" msgstr "Débit" msgid "VBR" msgstr "VBR" msgid "Samplerate" msgstr "Échantillonnage" msgid "Channels" msgstr "Canaux" msgid "Codec" msgstr "Codec" msgid "Escape for HTML" msgstr "Échappement HTML" msgid "Automatic Import" msgstr "Importer automatiquement" msgid "D&estination:" msgstr "D&estination:" msgid "Tag 1 and Tag 2" msgstr "Balise 1 et balise 2" msgid "&Profile:" msgstr "&Profil:" msgid "&Save Settings" msgstr "Enregistrer la confi&guration" msgid "New" msgstr "Nouveau" msgid "A&bort" msgstr "A&bandonner" msgid "S&tart" msgstr "&Démarrer" msgid "Import Source" msgstr "Source d'importation" msgid "&Server:" msgstr "&Serveur:" msgid "&Accuracy:" msgstr "&Précision:" msgid "&Standard Tags" msgstr "Balises &standard" msgid "&Additional Tags" msgstr "B&alises supplémentaires" msgid "C&over Art" msgstr "&Jaquettes" msgid "Browse Cover Art" msgstr "Télécharger la jaquette" msgid "&Artist/Album" msgstr "&Artiste/Album" msgid "&Source" msgstr "&Source" msgid "Source:" msgstr "Source:" msgid "URL:" msgstr "URL:" msgid "&URL extraction" msgstr "Remplacement de l'&URL" msgid "Match" msgstr "Correspondance" msgid "Picture URL" msgstr "URL de l'image" msgid "&Browse" msgstr "&Navigateur" msgid "Click Browse to start" msgstr "Cliquez sur Navigateur pour démarrer" msgid "Then drag the picture from the browser to Kid3." msgstr "Ensuite glissez l'image depuis le navigateur sur la fenêtre de Kid3." msgid "ID3v1" msgstr "ID3v1" msgid "&Mark truncated fields" msgstr "&Marquer les champs tronqués" msgid "Text &encoding:" msgstr "&Encodage du texte:" msgid "ID3v2" msgstr "ID3v2" msgid "Use &track/total number of tracks format" msgstr "Utiliser le format piste/nombre &total de pistes" msgid "Track number &digits:" msgstr "Nombre de &chiffres dans le numéro de piste:" msgid "&Genre as text instead of numeric string" msgstr "&Genre comme du texte au lieu du nombre" msgid "ISO-8859-1" msgstr "ISO-8859-1" msgid "UTF16" msgstr "UTF16" msgid "UTF8" msgstr "UTF8" msgid "&Version used for new tags:" msgstr "&Version utilisée pour balises novelles:" msgid "ID3v2.3.0" msgstr "ID3v2.3.0" msgid "ID3v2.4.0" msgstr "ID3v2.4.0" msgid "Ogg/Vorbis" msgstr "Ogg/Vorbis" msgid "Co&mment field name:" msgstr "No&m du champ commentaire:" msgid "&Picture field name:" msgstr "Nom du champ d'&image:" msgid "Custom &Genres" msgstr "&Genres personnalisés" msgid "&Show only custom genres" msgstr "&Montrer seulement les genres pernonnalisés" msgid "&Quick Access Tags" msgstr "Balises d'accès &rapide" msgid "&Tag Format" msgstr "Forma&t des balises" msgid "Tag &1" msgstr "Balise &1" msgid "Tag &2" msgstr "Balise &2" msgid "Tag 1 a&nd Tag 2" msgstr "Bali&se 1 et balise 2" msgid "Startup" msgstr "Démarrage" msgid "&Load last-opened files" msgstr "Ouvrir &les derniers fichiers" msgid "Save" msgstr "Enregistrer" msgid "&Preserve file timestamp" msgstr "&Préserver l'horodatage des fichiers" msgid "&Mark changes" msgstr "&Marquer les changements" msgid "F&ilename for cover:" msgstr "Nom de f&ichier pour la jaquette:" msgid "&Filename Format" msgstr "Format du nom de &fichier" msgid "Browser" msgstr "Navigateur" msgid "Web &browser:" msgstr "&Navigateur web:" msgid "Context &Menu Commands" msgstr "Commandes du &menu contextuel" msgid "&Play on double click" msgstr "Double-cliquer pour &lire" msgid "Proxy" msgstr "Proxy" msgid "&Proxy:" msgstr "&Proxy:" msgid "&Use authentication with proxy" msgstr "&Utiliser l'authentification avec le serveur mandataire" msgid "Proxy user &name:" msgstr "&Nom d'utilisateur:" msgid "Proxy pass&word:" msgstr "&Mot de passe:" msgid "&Metadata Plugins && Priority" msgstr "Modules &métadonnées && Priorité" msgid "A&vailable Plugins" msgstr "Modules externes &disponibles" msgid "Changes take only effect after a restart!" msgstr "Les modifications ne prendront effet qu'après un redémarrage!" msgid "From Clip&board" msgstr "Depuis &presse-papier" msgid "&Import" msgstr "&Importer" msgid "&Export" msgstr "&Exporter" msgid "&View" msgstr "&Affichage" msgid "Text Encoding" msgstr "Encodage du texte" msgid "Text" msgstr "Texte" msgid "Description" msgstr "Description" msgid "Owner" msgstr "Propriétaire" msgid "Email" msgstr "Adresse email" msgid "Rating" msgstr "Classement" msgid "Picture Type" msgstr "Type d'image" msgid "Image format" msgstr "Format de l'image" msgid "Mimetype" msgstr "Mimetype" msgid "Counter" msgstr "Conteur" msgid "Identifier" msgstr "Identifiant" msgid "Volume Adjustment" msgstr "Ajustement du volume" msgid "Number of Bits" msgstr "Nombres de bits" msgid "Volume Change Right" msgstr "Changement de volume droit" msgid "Volume Change Left" msgstr "Changement de volume gauche" msgid "Peak Volume Right" msgstr "Crête droite" msgid "Peak Volume Left" msgstr "Crête gauche" msgid "Timestamp Format" msgstr "Format de l'horodatage" msgid "Content Type" msgstr "Type de contenu" msgid "Price" msgstr "Prix" msgid "Seller" msgstr "Vendeur" msgid "Images" msgstr "Images" msgid "UTF16BE" msgstr "UTF16BE" msgid "Other" msgstr "Autre" msgid "32x32 pixels PNG file icon" msgstr "Fichier icône de 32x32 pixels" msgid "Other file icon" msgstr "Autre fichier d'icône" msgid "Cover (front)" msgstr "Pochette (avant)" msgid "Cover (back)" msgstr "Pochette (arrière)" msgid "Leaflet page" msgstr "Leaflet page" msgid "Lead artist/lead performer/soloist" msgstr "Artiste principal/interprète principal/soliste" msgid "Artist/performer" msgstr "Artiste/Interprète" msgid "Band/Orchestra" msgstr "Groupe/Orchestre" msgid "Lyricist/text writer" msgstr "Auteur" msgid "Recording Location" msgstr "Emplacement d'enregistrement" msgid "During recording" msgstr "Pendant l'enregistrement" msgid "During performance" msgstr "Pendant la représentation" msgid "Movie/video screen capture" msgstr "Capture d'écran de film/vidéo" msgid "A bright coloured fish" msgstr "Un poisson brillant de couleur" msgid "Illustration" msgstr "Illustration" msgid "Band/artist logotype" msgstr "Logo du groupe/artiste" msgid "Publisher/Studio logotype" msgstr "Logo du label/studio" msgid "MPEG frames as unit" msgstr "Cadre MPEG comme l'unité" msgid "Milliseconds as unit" msgstr "Millisecondes comme unité" msgid "Text transcription" msgstr "Transcription du texte" msgid "Movement/part name" msgstr "Nom de mouvement/partie" msgid "Events" msgstr "Évènements" msgid "Chord" msgstr "Accord" msgid "Trivia/pop up" msgstr "Trivia/pop up" msgid "Export" msgstr "Exporter" msgid "Format:" msgstr "Format:" msgid "Header:" msgstr "En-tête:" msgid "Tracks:" msgstr "Morceaux" msgid "Footer:" msgstr "Bas de page:" msgid "To F&ile" msgstr "Dans un f&ichier" msgid "To Clip&board" msgstr "Dans le &presse-papiers" msgid "&Source:" msgstr "&Source:" msgid "File Error" msgstr "Erreur de fichier" msgid "&Filter:" msgstr "&Filtre:" msgid "&Expression:" msgstr "&Expression:" msgid "&Apply" msgstr "&Appliquer" msgid "Accuracy:" msgstr "Précision:" msgid "Cover Art:" msgstr "Jaquettes:" msgid "From F&ile/Clipboard..." msgstr "Depuis f&ichier/presse-papier..." msgid "From T&ags..." msgstr "Depuis b&alises..." msgid "&From Server:" msgstr "&Depuis serveur:" msgid "Check maximum allowable time &difference (sec):" msgstr "Vérifier le temps de &différence maximum permis (sec):" msgid "Match with:" msgstr "Correspondre à:" msgid "&Length" msgstr "&Longueur" msgid "T&rack" msgstr "&Piste" msgid "&Title" msgstr "&Titre" msgid "&Start number:" msgstr "&Nombre de début:" msgid "&Destination:" msgstr "&Destination:" msgid "&Total number of tracks:" msgstr "&Nombre de pistes:" msgid "Create Playlist" msgstr "Créer liste de lecture" msgid "Playlist File Name" msgstr "Nom de la liste de lecture" msgid "Same as &directory name" msgstr "I&dentique au nom du répertoire" msgid "&Format:" msgstr "&Format:" msgid "Cr&eate in:" msgstr "Cré&er dans:" msgid "Current directory" msgstr "Répertoire courant" msgid "Every directory" msgstr "Chaque répertoire" msgid "Top-level directory" msgstr "Répertoire supérieur" msgid "Playlist Content" msgstr "Contenu de la Liste de Lecture" msgid "For&mat:" msgstr "For&mat:" msgid "Incl&ude only the selected files" msgstr "Incl&ure seulement les fichiers sélectionnés" msgid "Sort by file &name" msgstr "Trier par &nom de fichier" msgid "Sort by &tag field" msgstr "Trier par champ du &tag" msgid "Use &relative path for files in playlist" msgstr "Utiliser le chemin &relatif pour les fichiers" msgid "Use full p&ath for files in playlist" msgstr "Utiliser le chemin &complet pour les fichiers" msgid "Write only &list of files" msgstr "Ecrire seulement la &liste des fichiers" msgid "Write &info using" msgstr "Ecrire les &infos en utilisant" msgid "Preview" msgstr "Aperçu" msgid "Create Directory" msgstr "Créer répertoire" msgid "From Tag 2 and Tag 1" msgstr "Depuis balise 2 et balise 1" msgid "From:" msgstr "De:" msgid "To:" msgstr "À:" msgid "&Find" msgstr "&Trouver" msgid "C&GI Path:" msgstr "Dossier du C&GI:" msgid "Track Title/Artist - Album" msgstr "Piste Titre/Artiste - Album" msgid "State" msgstr "État" msgid "No result" msgstr "Pas de résultat" msgid "No result selected" msgstr "Pas de résultat sélectionné" msgid "Import from Tags" msgstr "Importer depuis balises" msgid "Extraction:" msgstr "Extraction:" msgid "Import from File/Clipboard" msgstr "Importer depuis fichier/presse-papier" msgid "From F&ile" msgstr "Depuis f&ichier" msgid "Download" msgstr "Télécharger" msgid "Opening directory..." msgstr "Ouvrir répertoire..." msgid "Saving directory..." msgstr "Sauvegarder dans le répertoire..." msgid "" "The current directory has been modified.\n" "Do you want to save it?" msgstr "" "Le répertoire courant a été modifié.\n" "Voulez vous l'enregistrer?" msgid "Warning" msgstr "Avertissement" msgid "Exiting..." msgstr "Quitter..." msgid "Creating playlist..." msgstr "Création d'une liste de lecture..." msgid "Import" msgstr "Importer" msgid "Error while renaming:\n" msgstr "Erreur en renommant:\n" msgid " [filtered]" msgstr " [filtré]" msgid "Add Frame" msgstr "Ajouter cadre" msgid "Select the frame ID" msgstr "Sélectionnez l'ID du cadre" msgid "Rename File" msgstr "Renommer le fichier" msgid "Enter new file name:" msgstr "Saisissez un nouveau nom pour le fichier:" msgid "Do you really want to move these %1 items to the trash?" msgstr "Voulez-vous vraiment déplacer ces %1 éléments vers la corbeille?" msgid "Do you really want to move this item to the trash?" msgstr "Voulez-vous vraiment déplacer cet élément vers la corbeille?" msgid "Move to Trash" msgstr "Mettre à la corbeille" msgid "Directory must be empty.\n" msgstr "Le répertoire doit être vide.\n" msgid "Could not move these files to the Trash" msgstr "Impossible de déplacer ces fichiers dans la corbeille" msgid "Expand All" msgstr "Tout développer" msgid "&Expand all" msgstr "Tout &développer" msgid "&Collapse all" msgstr "Tout &réduire" msgid "&Rename" msgstr "&Renommer" msgid "&Move to Trash" msgstr "Me&ttre à la corbeille" msgid "Kid3" msgstr "Kid3" msgid "F&ile" msgstr "F&ichier" msgid "Name:" msgstr "Nom:" msgid "Filename from Tag 1" msgstr "Nom de fichier depuis balise 1" msgid "Filename from Tag 2" msgstr "Nom de fichier depuis balise 2" msgid "Tag 1 from Filename" msgstr "Balise 1 depuis le nom de fichier" msgid "Tag 2 from Filename" msgstr "Balise 2 depuis le nom de fichier" msgid "&Add..." msgstr "&Ajouter..." msgid "Move &Up" msgstr "&Monter" msgid "Move &Down" msgstr "&Descendre" msgid "&Edit..." msgstr "M&odifier..." msgid "&Remove" msgstr "Su&pprimer" msgid "&Insert row" msgstr "&Insérer ligne" msgid "&Delete row" msgstr "&Supprimer ligne" msgid "&Clear row" msgstr "&Effacer ligne" msgid "Format while editing" msgstr "Formater pendant l'édition" msgid "Case conversion:" msgstr "Conversion de la casse:" msgid "No changes" msgstr "Pas de changements" msgid "All lowercase" msgstr "Tout en minuscule" msgid "All uppercase" msgstr "Tout en majuscule" msgid "First letter uppercase" msgstr "Première lettre en majuscule" msgid "All first letters uppercase" msgstr "Toutes les premières lettres en majuscule" msgid "Locale:" msgstr "Localisation:" msgid "None" msgstr "Aucune" msgid "String replacement:" msgstr "Remplacement de chaînes:" msgid "From" msgstr "De" msgid "To" msgstr "À" msgid "&Add" msgstr "&Ajouter" msgid "&Select all" msgstr "Sé&lectionner tout" msgid "&Deselect all" msgstr "Désele&ctionner tout" msgid "View Picture" msgstr "Voir l'image" msgid "" "Drag album\n" "artwork\n" "here" msgstr "" "Déposer la\n" "pochette de\n" "l'album ici" msgid "Play/Pause" msgstr "Lecture / pause" msgid "Stop playback" msgstr "Arrêter la lecture" msgid "Previous Track" msgstr "Morceau précédent" msgid "Next Track" msgstr "Morceau suivant" msgid "Close" msgstr "Fermer" msgid "Mute" msgstr "Muet" msgid "Volume: %1%" msgstr "Volume: %1%" msgid "Add Item" msgstr "Ajouter un élément" msgid "Edit Item" msgstr "Modifier l'élément" msgid "MusicBrainz Fingerprint" msgstr "MusicBrainz empreinte" msgid "Unrecognized" msgstr "Non reconnu" msgid "Recognized" msgstr "Reconnu" msgid "User Selection" msgstr "Sélection de l'utilisateur" msgid "ID Lookup" msgstr "Recherche ID" msgid "Fingerprint" msgstr "Empreinte" msgid "Metadata Lookup" msgstr "Donnée meta lue" msgid "Amazon" msgstr "Amazon" msgid "Discogs" msgstr "Discogs" msgid "gnudb.org" msgstr "gnudb.org" msgid "TrackType.org" msgstr "TrackType.org" msgid "AENC - Audio encryption" msgstr "AENC - Encodage audio" msgid "APIC - Attached picture" msgstr "APIC - Image attachée" msgid "COMM - Comments" msgstr "COMM - Commentaires" msgid "COMR - Commercial" msgstr "COMR - Commercial" msgid "ENCR - Encryption method registration" msgstr "ENCR - Enregistrement de la méthode de chiffrement" msgid "EQUA - Equalization" msgstr "EQUA - Égalisation" msgid "ETCO - Event timing codes" msgstr "ETCO - Codes d'événement" msgid "GEOB - General encapsulated object" msgstr "GEOB - Objet géneral encapsulé" msgid "GRID - Group identification registration" msgstr "GRID - Enregistrement de la groupe d'identification" msgid "IPLS - Involved people list" msgstr "IPLS - Liste des personnes impliquées" msgid "LINK - Linked information" msgstr "LINK - Information liée" msgid "MCDI - Music CD identifier" msgstr "MCDI - Identifiant de disque" msgid "MLLT - MPEG location lookup table" msgstr "MLLT - Table de recherche de l'emplacement MPEG" msgid "OWNE - Ownership frame" msgstr "OWNE - Cadre de propriété" msgid "PRIV - Private frame" msgstr "PRIV - Cadre privé" msgid "PCNT - Play counter" msgstr "PCNT - Compteur d'écoutes" msgid "POPM - Popularimeter" msgstr "POPM - Popularimètre" msgid "POSS - Position synchronisation frame" msgstr "POSS - Cadre de synchronisation" msgid "RBUF - Recommended buffer size" msgstr "RBUF - Taille recommandée du tampon" msgid "RVAD - Relative volume adjustment" msgstr "RVAD - Ajustement relative de volume" msgid "RVRB - Reverb" msgstr "RVRB - Reverbe" msgid "SYLT - Synchronized lyric/text" msgstr "SYLT - Paroles/textes synchronisés" msgid "SYTC - Synchronized tempo codes" msgstr "SYTC - Codes de vitesse synchronisés" msgid "TALB - Album/Movie/Show title" msgstr "TALB - Titre d'album/film/spectacle" msgid "TBPM - BPM (beats per minute)" msgstr "TBPM - BPM (battements par minute)" msgid "TCOM - Composer" msgstr "TCOM - Composeur" msgid "TCON - Content type" msgstr "TCON - Type de contenu" msgid "TCOP - Copyright message" msgstr "TCOP - Message de copyright" msgid "TDAT - Date" msgstr "TDAT - Date" msgid "TDLY - Playlist delay" msgstr "TDLY - Delay de la liste de lecture" msgid "TENC - Encoded by" msgstr "TENC - Encodé par" msgid "TEXT - Lyricist/Text writer" msgstr "TEXT - Auteur de texte" msgid "TFLT - File type" msgstr "TFLT - Type de fichier" msgid "TIME - Time" msgstr "TIME - Temps" msgid "TIT1 - Content group description" msgstr "TIT1 - Description du groupe" msgid "TIT2 - Title/songname/content description" msgstr "TIT2 - Description du titre/nom de la chanson/contenu" msgid "TIT3 - Subtitle/Description refinement" msgstr "TIT3 - Affinage des sous-titres/description" msgid "TKEY - Initial key" msgstr "TKEY - Clé initiale" msgid "TLAN - Language(s)" msgstr "TLAN - Langages" msgid "TLEN - Length" msgstr "TLEN - Longueur" msgid "TMED - Media type" msgstr "TMED - Type de média" msgid "TOAL - Original album/movie/show title" msgstr "TOAL - Titre d'album/film/spectacle original" msgid "TOFN - Original filename" msgstr "TOFN - Nom de fichier original" msgid "TOLY - Original lyricist(s)/text writer(s)" msgstr "TOLY - Auteur de texte original" msgid "TOPE - Original artist(s)/performer(s)" msgstr "TOPE - Artiste/interprète original" msgid "TORY - Original release year" msgstr "TORY - Année de sortie originale" msgid "TOWN - File owner/licensee" msgstr "TOWN - Propriétaire/licencié du fichier" msgid "TPE1 - Lead performer(s)/Soloist(s)" msgstr "TPE1 - Interprète/soliste principal" msgid "TPE2 - Band/orchestra/accompaniment" msgstr "TPE2 - Groupe/orchestre/accompagnement" msgid "TPE3 - Conductor/performer refinement" msgstr "TPE3 - Affinage de conducteur/interprète" msgid "TPE4 - Interpreted, remixed, or otherwise modified by" msgstr "TPE4 - Interprété, remélangé ou modifié par" msgid "TPOS - Part of a set" msgstr "TPOS - Partie d'un ensemble" msgid "TPUB - Publisher" msgstr "TPUB - Éditeur" msgid "TRCK - Track number/Position in set" msgstr "TRCK - Nombre de piste/position" msgid "TRDA - Recording dates" msgstr "TRDA - Dates d'enregistrement" msgid "TRSN - Internet radio station name" msgstr "TRSN - Nom de station de radio internet" msgid "TRSO - Internet radio station owner" msgstr "TRSO - Propriétaire de la station de radio internet" msgid "TSIZ - Size" msgstr "TSIZ - Taille" msgid "TSRC - ISRC (international standard recording code)" msgstr "TSRC - ISRC (Code international standard d'enregistrement)" msgid "TSSE - Software/Hardware and settings used for encoding" msgstr "TSSE - Matériel/configuration utilisé pour l'encodage" msgid "TXXX - User defined text information" msgstr "TXXX - Texte défini par l'utilisateur" msgid "TYER - Year" msgstr "TYER - Année" msgid "UFID - Unique file identifier" msgstr "UFID - Identificateur unique de fichier" msgid "USER - Terms of use" msgstr "USER - Conditions d'utilisation" msgid "USLT - Unsynchronized lyric/text transcription" msgstr "USLT - Paroles/textes non synchronisés" msgid "WCOM - Commercial information" msgstr "WCOM - Information commerciale" msgid "WCOP - Copyright/Legal information" msgstr "WCOP - Information juridique/copyright" msgid "WOAF - Official audio file webpage" msgstr "WOAF - URL officiel du fichier audio" msgid "WOAR - Official artist/performer webpage" msgstr "WOAR - URL officiel d'artiste/interprète" msgid "WOAS - Official audio source webpage" msgstr "WOAS - URL officiel de la source audio" msgid "WORS - Official internet radio station homepage" msgstr "WORS - URL officiel de la station de radio internet" msgid "WPAY - Payment" msgstr "WPAY - Paiement" msgid "WPUB - Official publisher webpage" msgstr "WPUB - Page officielle de l'éditeur" msgid "WXXX - User defined URL link" msgstr "WXXX - URL défini par l'utilisateur" msgid "MusicBrainz Release" msgstr "MusicBrainz album" msgid "ASPI - Audio seek point index" msgstr "ASPI - Index de recherche" msgid "EQU2 - Equalisation (2)" msgstr "EQU2 - Égalisation (2)" msgid "RVA2 - Relative volume adjustment (2)" msgstr "RVA2 - Ajustement relative de volume (2)" msgid "SEEK - Seek frame" msgstr "SEEK - Cadre de recherche" msgid "SIGN - Signature frame" msgstr "SIGN - Signature" msgid "TCMP - iTunes compilation flag" msgstr "TCMP - iTunes compilation" msgid "TDEN - Encoding time" msgstr "TDEN - Date d'encodage" msgid "TDOR - Original release time" msgstr "TDOR - Date de sortie originale" msgid "TDRC - Recording time" msgstr "TDRC - Date d'enregistrement" msgid "TDRL - Release time" msgstr "TDRL - Date de sortie" msgid "TDTG - Tagging time" msgstr "TDTG - Date de balisage" msgid "TIPL - Involved people list" msgstr "TIPL - Liste des personnes impliquées" msgid "TMCL - Musician credits list" msgstr "TMCL - Liste de crédits des musiciens" msgid "TMOO - Mood" msgstr "TMOO - Humeur" msgid "TPRO - Produced notice" msgstr "TPRO - Préavis produit" msgid "TSO2 - Album artist sort order" msgstr "TSO2 - Ordre de tri d'artiste de l'album" msgid "TSOA - Album sort order" msgstr "TSOA - Ordre de tri d'album" msgid "TSOC - Composer sort order" msgstr "TSOC - Ordre de tri de compositeur" msgid "TSOP - Performer sort order" msgstr "TSOP - Ordre de tri d'interprète" msgid "TSOT - Title sort order" msgstr "TSOT - Ordre de tri du titre" msgid "TSST - Set subtitle" msgstr "TSST - Sous-titre" �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/po/it.po���������������������������������������������������������������������������������0000664�0000000�0000000�00000105164�12246035113�0013744�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# translation of it.po to English # This file is put in the public domain. # # Valter Mura <valtermura@yahoo.it>, 2007. # Urs Fleisch <ufleisch@users.sourceforge.net>, 2008, 2009, 2010, 2011. msgid "" msgstr "" "Project-Id-Version: Kid3 3.0.2\n" "Report-Msgid-Bugs-To: http://sourceforge.net/p/kid3/bugs/\n" "POT-Creation-Date: 2013-09-11 21:22+0200\n" "PO-Revision-Date: 2011-01-15 17:33+0100\n" "Last-Translator: \n" "Language-Team: Italian <kde-i18n-doc@kde.org>\n" "Language: it\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Lokalize 1.1\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Language: it\n" "X-Source-Language: en\n" msgid "Timeout" msgstr "Tempo massimo" msgid "Usage:" msgstr "Utilizzo:" msgid "Help" msgstr "Aiuto" msgid "Command name" msgstr "Nome comando" msgid "Quit application" msgstr "Esce dall'applicazione" msgid "The current directory has been modified." msgstr "La cartella in uso è stata modificata." msgid "Type 'exit force' to quit." msgstr "Digitate 'exit force' per uscire." msgid "Change directory" msgstr "Cambia cartella" msgid "%1 does not exist" msgstr "%1 non esiste" msgid "Print the filename of the current working directory" msgstr "Stampa il nome dell'attuale directory di lavoro" msgid "Directory list" msgstr "Lista delle cartelle" msgid "Saves the changed files" msgstr "Salva i file modificati" msgid "Error while writing file:\n" msgstr "Errore durante la scrittura del file:\n" msgid "Select file" msgstr "Seleziona file" msgid "%1 not found" msgstr "%1 non trovato" msgid "Select tag" msgstr "Seleziona tag" msgid "Get tag frame" msgstr "Ottienei tag frame" msgid "Set tag frame" msgstr "Imposta tag frame" msgid "Could not set \"%1\" for %2" msgstr "Impossibile impostare %2 a \"%1\"" msgid "Revert" msgstr "Ripristina" msgid "Import from file or clipboard" msgstr "Importa da file o dagli appunti" msgid "Format name" msgstr "Nome del formato" msgid "%1 not found." msgstr "%1 non trovata." msgid "Available" msgstr "Disponibile" msgid "Error" msgstr "Errore" msgid "Automatic import" msgstr "Importa automaticamente" msgid "Profile name" msgstr "Nome profilo" msgid "Reading Directory" msgstr "Lettura cartella" msgid "Started" msgstr "Cominciato" msgid "Source" msgstr "Origine" msgid "Querying" msgstr "Interrogazione" msgid "Fetching" msgstr "Recupero" msgid "Data received" msgstr "Dati ricevuti" msgid "Cover" msgstr "Copertina" msgid "Finished" msgstr "Finito" msgid "Aborted" msgstr "Interrotto" msgid "Download album cover artwork" msgstr "Preleva copertina" msgid "Export to file or clipboard" msgstr "Esporta in un file o negli appunti" msgid "Create playlist" msgstr "Crea Playlist" msgid "Apply filename format" msgstr "Applica formato Nome file" msgid "Apply tag format" msgstr "Applica formato Tag" msgid "Apply text encoding" msgstr "Applica codifica del testo" msgid "Rename directory" msgstr "Rinomina cartella" msgid "Number tracks" msgstr "Numero tracce" msgid "Track number" msgstr "Numero traccia" msgid "Filter" msgstr "Filtro" msgid "Filter name" msgstr "Nome del filtro" msgid "Convert ID3v2.3 to ID3v2.4" msgstr "Converti ID3v2.3 in ID3v2.4" msgid "Convert ID3v2.4 to ID3v2.3" msgstr "Converti ID3v2.4 in ID3v2.3" msgid "Filename from tag" msgstr "Nome file da Tag" msgid "Tag from filename" msgstr "Tag dal Nome file" msgid "Tag to other tag" msgstr "Tag dal altro tag" msgid "Copy" msgstr "Copia" msgid "Paste" msgstr "Incolla" msgid "Remove" msgstr "Rimuovi" msgid "Play" msgstr "Riproduci" msgid "Parameter" msgstr "Parametro" msgid "File path" msgstr "Percorso file" msgid "URL" msgstr "URL" msgid "Tag numbers" msgstr "Numero tag" msgid "Frame name" msgstr "Nome frame" msgid "Frame value" msgstr "Valore frame" msgid "Format" msgstr "Formato" msgid "Command specific" msgstr "Specifico di un comando" msgid "Available Commands" msgstr "Comandi disponibili" msgid "File" msgstr "File" msgid "Name" msgstr "Nome" msgid "Tag 2" msgstr "Tag 2" msgid "Tag 1" msgstr "Tag 1" msgid "Tags" msgstr "Tag" msgid "Unknown command '%1'. Type 'help' for help." msgstr "Comando sconosciuto '%1'. Esegui 'help' per un aiuto." msgid "Unknown command '%1', -h for help." msgstr "Comando sconosciuto '%1', -h per l'aiuto." msgid "Files" msgstr "File" msgid "User Actions" msgstr "Azioni utente" msgid "Network" msgstr "Rete" msgid "Plugins" msgstr "Plugin" msgid "Main Toolbar" msgstr "Barra degli strumenti principale" msgid "Opens a directory" msgstr "Apre una cartella" msgid "Opens a recently used directory" msgstr "Apre una cartella usata di recente" msgid "Reverts the changes of all or the selected files" msgstr "Annulla le modifiche eseguite su tutti i file o quelli selezionati" msgid "Quits the application" msgstr "Esce dall'applicazione" msgid "Select all files" msgstr "Seleziona tutti i file" msgid "Deselect all files" msgstr "Deseleziona tutti i file" msgid "Configure Shortcuts" msgstr "Configura le scorciatoie" msgid "Configure Toolbars" msgstr "Configura la barra degli strumenti" msgid "Preferences dialog" msgstr "Preferenze" msgid "O&pen Directory..." msgstr "A&pri cartella..." msgid "&Import..." msgstr "&Importa..." msgid "Import from %1..." msgstr "Importa da %1..." msgid "Automatic I&mport..." msgstr "I&mporta automaticamente..." msgid "&Browse Cover Art..." msgstr "&Preleva copertina..." msgid "&Export..." msgstr "&Esporta..." msgid "&Create Playlist..." msgstr "&Crea Playlist..." msgid "Apply &Filename Format" msgstr "Applica formato Nome &file" msgid "Apply &Tag Format" msgstr "Applica formato &Tag" msgid "Apply Text &Encoding" msgstr "Applica codifica del t&esto" msgid "&Rename Directory..." msgstr "&Rinomina cartella..." msgid "&Number Tracks..." msgstr "&Numero tracce..." msgid "F&ilter..." msgstr "F&iltro..." msgid "Convert ID3v2.3 to ID3v2.&4" msgstr "Converti ID3v2.3 in ID3v2.&4" msgid "Convert ID3v2.4 to ID3v2.&3" msgstr "Converti ID3v2.4 in ID3v2.&3" msgid "&Play" msgstr "&Riproduci" msgid "Show &Picture" msgstr "Mostra &immagine" msgid "Auto &Hide Tags" msgstr "&Nascondi automaticamente" msgid "Select All in &Directory" msgstr "Seleziona tutti i file nella &cartella" msgid "&Previous File" msgstr "File &precedente" msgid "&Next File" msgstr "File &successivo" msgid "From Filename" msgstr "Dal Nome file" msgid "From Tag 2" msgstr "Da Tag 2" msgid "From Tag 1" msgstr "Da Tag 1" msgid "Frames:" msgstr "Frame:" msgid "Edit" msgstr "Modifica" msgid "Add" msgstr "Aggiungi" msgid "Delete" msgstr "Elimina" msgid "Filename" msgstr "Nome file" msgid "Focus" msgstr "Punto focale" msgid "File List" msgstr "Lista File" msgid "Directory List" msgstr "Lista delle cartelle" msgid "Configure - Kid3" msgstr "Configura - Kid3" msgid "Open" msgstr "Apri" msgid "Save As" msgstr "Salva con nome" msgid "directory to open" msgstr "cartella da aprire" msgid "Kid3 ID3 Tagger" msgstr "Kid3 ID3 Tagger" #. i18n NAME OF TRANSLATORS msgid "Your names" msgstr "Valter Mura" #. i18n EMAIL OF TRANSLATORS msgid "Your emails" msgstr "valtermura@yahoo.it, giacomomargarito@gmail.com" msgid "&Back" msgstr "&Indietro" msgid "&Forward" msgstr "&Avanti" msgid "&Find:" msgstr "&Trova:" msgid "Find Previous" msgstr "Trova precedente" msgid "Find Next" msgstr "Trova successivo" msgid "&Close" msgstr "&Chiudi" msgid "&Tags" msgstr "&Tag" msgid "&Files" msgstr "&File" msgid "&User Actions" msgstr "Azioni &utente" msgid "&Network" msgstr "&Rete" msgid "&Plugins" msgstr "&Plugin" msgid "&Keyboard Shortcuts" msgstr "&Scorciatoie da tastiera" msgid "Use custom app&lication font" msgstr "Usare un carattere persona&lizzato" msgid "A&pplication Font..." msgstr "&Carattere..." msgid "Use custom application &style" msgstr "Usare uno &stile personalizzato" msgid "Unknown" msgstr "Sconosciuto" msgid "Use native system file &dialogs" msgstr "Usa finestre di &dialogo file native" msgid "&Appearance" msgstr "&Aspetto" msgid "&Help" msgstr "Ai&uto" msgid "&OK" msgstr "&OK" msgid "&Cancel" msgstr "&Annulla" msgid "The keyboard shortcut '%1' is already assigned to '%2'." msgstr "La scorciatoia '%1' è già usata per il comando '%2'." msgid "&File" msgstr "&File" msgid "&Open..." msgstr "&Apri..." msgid "Open &Recent" msgstr "Apri &recenti" msgid "&Save" msgstr "&Salva" msgid "Re&vert" msgstr "Ri&pristina" msgid "Import from %1" msgstr "Importa da %1" msgid "Browse album cover artwork" msgstr "Preleva copertina" msgid "Create M3U Playlist" msgstr "Crea Playlist M3U" msgid "&Quit" msgstr "&Esci" msgid "&Edit" msgstr "&Modifica" msgid "Select &All" msgstr "Seleziona &tutto" msgid "Dese&lect" msgstr "Dese&leziona" msgid "Select all files in the current directory" msgstr "Seleziona tutti i file nella directory attuale" msgid "Select previous file" msgstr "Seleziona file precedente" msgid "Select next file" msgstr "Seleziona file successivo" msgid "&Tools" msgstr "S&trumenti" msgid "Apply Filename Format" msgstr "Applica formato Nome file" msgid "Apply Tag Format" msgstr "Applica formato Tag" msgid "Apply Text Encoding" msgstr "Applica codifica del testo" msgid "Rename Directory" msgstr "Rinomina cartella" msgid "Number Tracks" msgstr "Numero tracce" msgid "&Settings" msgstr "Imp&ostazioni" msgid "Enables/disables the toolbar" msgstr "Attiva/disattiva la barra degli strumenti" msgid "Show &Toolbar" msgstr "Mostra la barra degli &strumenti" msgid "Enables/disables the statusbar" msgstr "Attiva/disattiva la barra di stato" msgid "Show St&atusbar" msgstr "Mostra la barra di st&ato" msgid "Show Picture" msgstr "Mostra immagine" msgid "Auto Hide Tags" msgstr "Nascondi automaticamente" msgid "Configure Kid3" msgstr "Configura Kid3" msgid "&Configure Kid3..." msgstr "&Configura Kid3..." msgid "Kid3 Handbook" msgstr "Manuale di Kid3" msgid "Kid3 &Handbook" msgstr "&Manuale di Kid3" msgid "About Kid3" msgstr "Informazioni su Kid3" msgid "&About Kid3" msgstr "&Informazioni su Kid3" msgid "About Qt" msgstr "Informazioni su Qt" msgid "About &Qt" msgstr "Informazioni su &Qt" msgid " [modified]" msgstr " [modificato]" msgid "Toggle the statusbar..." msgstr "Cambiamento barra di stato in corso..." msgid "Ready." msgstr "Pronto." msgid "&Clear List" msgstr "&Pulisci la lista" msgid "Clear" msgstr "Pulisci" msgid "Reset" msgstr "Azzera" msgid "Press F2 or double click to edit cell contents." msgstr "Premi F2 o fai doppio clic per modificare il contenuto della cella." msgid "Action" msgstr "Azione" msgid "Shortcut" msgstr "Scorciatoia" msgid "Server" msgstr "Server" msgid "Accuracy" msgstr "Accuratezza" msgid "Standard Tags" msgstr "Tag standard" msgid "Additional Tags" msgstr "Tag aggiuntivi" msgid "Cover Art" msgstr "Copertina" msgid "Invalid File" msgstr "File non valido" msgid "Data received: %1" msgstr "Dati ricevuti: %1" msgid "Request sent..." msgstr "Richiesta inviata..." msgid "Year" msgstr "Anno" msgid "Length" msgstr "Lunghezza" msgid "Filenames" msgstr "nomi dei file" msgid "URLs" msgstr "URL" msgid "Directory name" msgstr "Nome cartella" msgid "Encode as URL" msgstr "Codifica come URL" msgid "Confirm" msgstr "Conferma" msgid "Output" msgstr "Output" msgid "Command" msgstr "Comando" msgid "Create directory %1 failed\n" msgstr "Creazione cartella %1 fallita\n" msgid "File %1 already exists\n" msgstr "Il file %1 esiste già\n" msgid "%1 is not a directory\n" msgstr "%1 non è una cartella\n" msgid "Rename %1 to %2 failed\n" msgstr "Impossibile rinominare %1 in %2 \n" msgid "%1 already exists\n" msgstr "%1 esiste già\n" msgid "%1 is not a file\n" msgstr "%1 non un file\n" msgid "New directory name is too different\n" msgstr "Il nuovo nome della cartella è troppo diverso\n" msgid "Create directory" msgstr "Crea cartella" msgid "Rename file" msgstr "Rinomina file" msgid "C&lear" msgstr "A&zzera" msgid "Execute " msgstr "Esegui " msgid "Could not execute " msgstr "Non eseguire " msgid "True if strings are equal" msgstr "True se le stringhe sono uguali" msgid "True if string contains substring" msgstr "True se la stringa contiene una sottostringa" msgid "True if string matches regexp" msgstr "True se la stringa corrisponde ad una regexp" msgid "Logical AND" msgstr "E logico" msgid "Logical OR" msgstr "O logico" msgid "Logical negation" msgstr "Negazione logica" msgid "Data" msgstr "Dati" msgid "%1 is not writable" msgstr "%1 non è scrivibile" msgid "All Supported Files" msgstr "Tutti i file supportati" msgid "All Files" msgstr "Tutti i file" msgid "Track" msgstr "Traccia" msgid "Absolute path to file" msgstr "Nome di file assoluto" msgid "Duration" msgstr "Durata" msgid "Title" msgstr "Titolo" msgid "Artist" msgstr "Artista" msgid "Album" msgstr "Album" msgid "Comment" msgstr "Commento" msgid "Date" msgstr "Data" msgid "Track Number" msgstr "Numero traccia" msgid "Genre" msgstr "Genere" msgid "Album Artist" msgstr "Artista album" msgid "Arranger" msgstr "Arrangiatore" msgid "Author" msgstr "Autore" msgid "BPM" msgstr "BPM" msgid "Catalog Number" msgstr "Numero di catalogo" msgid "Compilation" msgstr "Compilation" msgid "Composer" msgstr "Compositore" msgid "Conductor" msgstr "Direttore d'orchestra" msgid "Copyright" msgstr "Copyright" msgid "Disc Number" msgstr "Numero disco" msgid "Encoded-by" msgstr "Codificato da" msgid "Encoder Settings" msgstr "Impostazioni del codificatore" msgid "Encoding Time" msgstr "Tempo di codifica" msgid "Grouping" msgstr "Aggregazione" msgid "Initial Key" msgstr "Chiave iniziale" msgid "ISRC" msgstr "ISRC" msgid "Language" msgstr "Lingua" msgid "Lyricist" msgstr "Paroliere" msgid "Lyrics" msgstr "Testi" msgid "Media" msgstr "Media" msgid "Mood" msgstr "Mood" msgid "Original Album" msgstr "Album originale" msgid "Original Artist" msgstr "Artista originale" msgid "Original Date" msgstr "Data originale" msgid "Part" msgstr "Parte" msgid "Performer" msgstr "Interprete" msgid "Picture" msgstr "Immagine" msgid "Publisher" msgstr "Editore" msgid "Release Country" msgstr "Paese della release" msgid "Remixer" msgstr "Remixer" msgid "Sort Album" msgstr "Ordinamento per album" msgid "Sort Album Artist" msgstr "Ordina per artista album" msgid "Sort Artist" msgstr "Ordinamento per interprete" msgid "Sort Composer" msgstr "Ordinamento per compositore" msgid "Sort Name" msgstr "Ordinamento per titolo" msgid "Subtitle" msgstr "Sottotitolo" msgid "Website" msgstr "Sito web" msgid "WWW Audio File" msgstr "WWW del file audio" msgid "WWW Audio Source" msgstr "WWW della sorgente audio" msgid "Number of tracks" msgstr "Numero delle tracce" msgid "Extension" msgstr "Estensione file" msgid "Bitrate" msgstr "Bitrate" msgid "VBR" msgstr "VBR" msgid "Samplerate" msgstr "Campionamento" msgid "Channels" msgstr "Canali" msgid "Codec" msgstr "Codec" msgid "Escape for HTML" msgstr "Escape HTML" msgid "Automatic Import" msgstr "Importa automaticamente" msgid "D&estination:" msgstr "D&estinazione:" msgid "Tag 1 and Tag 2" msgstr "Tag 1 e Tag 2" msgid "&Profile:" msgstr "&Profilo:" msgid "&Save Settings" msgstr "&Salva impostazioni" msgid "New" msgstr "Nuovi" msgid "A&bort" msgstr "Inte&rrompi" msgid "S&tart" msgstr "&Avvio" msgid "Import Source" msgstr "Sorgente di importazione" msgid "&Server:" msgstr "&Server:" msgid "&Accuracy:" msgstr "&Accuratezza:" msgid "&Standard Tags" msgstr "Tag &standard" msgid "&Additional Tags" msgstr "Tag &aggiuntivi" msgid "C&over Art" msgstr "C&opertina" msgid "Browse Cover Art" msgstr "Cerca la copertina" msgid "&Artist/Album" msgstr "&Artista/Album" msgid "&Source" msgstr "&Origine" msgid "Source:" msgstr "Origine:" msgid "URL:" msgstr "URL:" msgid "&URL extraction" msgstr "&Estrazione dell'URL" msgid "Match" msgstr "Corrispondenza" msgid "Picture URL" msgstr "URL dell'immagine" msgid "&Browse" msgstr "&Sfoglia" msgid "Click Browse to start" msgstr "Fai click sul pulsante Sfoglia per avviare" msgid "Then drag the picture from the browser to Kid3." msgstr "Poi trascina l'immagine dal browser su Kid3." msgid "ID3v1" msgstr "ID3v1" msgid "&Mark truncated fields" msgstr "E&videnzia i campi troncati" msgid "Text &encoding:" msgstr "&Codifica del testo:" msgid "ID3v2" msgstr "ID3v2" msgid "Use &track/total number of tracks format" msgstr "Usa formato &traccia/numero totale delle tracce" msgid "Track number &digits:" msgstr "&Numero di cifre in campo della traccia:" msgid "&Genre as text instead of numeric string" msgstr "&Genere come testo invece dei numeri" msgid "ISO-8859-1" msgstr "ISO-8859-1" msgid "UTF16" msgstr "UTF16" msgid "UTF8" msgstr "UTF8" msgid "&Version used for new tags:" msgstr "&Versione usata per i nuovi tag:" msgid "ID3v2.3.0" msgstr "ID3v2.3.0" msgid "ID3v2.4.0" msgstr "ID3v2.4.0" msgid "Ogg/Vorbis" msgstr "Ogg/Vorbis" msgid "Co&mment field name:" msgstr "Co&mmento nome campo:" msgid "&Picture field name:" msgstr "&Immagine nome campo:" msgid "Custom &Genres" msgstr "&Generi personalizzati" msgid "&Show only custom genres" msgstr "&Mostra solo generi personalizzati" msgid "&Quick Access Tags" msgstr "Tag di accesso &rapido" msgid "&Tag Format" msgstr "Formato &tag" msgid "Tag &1" msgstr "Tag &1" msgid "Tag &2" msgstr "Tag &2" msgid "Tag 1 a&nd Tag 2" msgstr "Tag 1 &e Tag 2" msgid "Startup" msgstr "Avvio" msgid "&Load last-opened files" msgstr "Carica gli &ultimi file aperti" msgid "Save" msgstr "Salva" msgid "&Preserve file timestamp" msgstr "M&antieni file data/ora" msgid "&Mark changes" msgstr "E&videnzia le modifiche" msgid "F&ilename for cover:" msgstr "Nome f&ile per copertina:" msgid "&Filename Format" msgstr "Formato nome&file" msgid "Browser" msgstr "Browser" msgid "Web &browser:" msgstr "&Browser web:" msgid "Context &Menu Commands" msgstr "Comandi &menu contestuale" msgid "&Play on double click" msgstr "&Riproduci con un doppio clic" msgid "Proxy" msgstr "Proxy" msgid "&Proxy:" msgstr "&Proxy:" msgid "&Use authentication with proxy" msgstr "&Usa autenticazione con il proxy" msgid "Proxy user &name:" msgstr "Ute&nte proxy:" msgid "Proxy pass&word:" msgstr "Pass&word proxy:" msgid "&Metadata Plugins && Priority" msgstr "Plugin &metadati && Priorità" msgid "A&vailable Plugins" msgstr "Plugin &disponibili" msgid "Changes take only effect after a restart!" msgstr "I cambiamenti avranno luogo dopo il riavvio!" msgid "From Clip&board" msgstr "Dagli a&ppunti" msgid "&Import" msgstr "&Importa" msgid "&Export" msgstr "&Esporta" msgid "&View" msgstr "Visuali&zza" msgid "Text Encoding" msgstr "Codifica del testo" msgid "Text" msgstr "Testo" msgid "Description" msgstr "Descrizione" msgid "Owner" msgstr "Proprietario" msgid "Email" msgstr "E-mail" msgid "Rating" msgstr "Giudizio" msgid "Picture Type" msgstr "Tipo immagine" msgid "Image format" msgstr "Formato immagine" msgid "Mimetype" msgstr "Tipo MIME" msgid "Counter" msgstr "Contatore" msgid "Identifier" msgstr "Identificatore" msgid "Volume Adjustment" msgstr "Regolazione volume" msgid "Number of Bits" msgstr "Numero di Bit" msgid "Volume Change Right" msgstr "Cambia volume destro" msgid "Volume Change Left" msgstr "Cambia volume sinistro" msgid "Peak Volume Right" msgstr "Picco volume destro" msgid "Peak Volume Left" msgstr "Picco volume sinistro" msgid "Timestamp Format" msgstr "Formato data/ora" msgid "Content Type" msgstr "Tipo di contenuto" msgid "Price" msgstr "Prezzo" msgid "Seller" msgstr "Venditore" msgid "Images" msgstr "Immagini" msgid "UTF16BE" msgstr "UTF16BE" msgid "Other" msgstr "Altro" msgid "32x32 pixels PNG file icon" msgstr "Icona file PNG da 32x32 pixel" msgid "Other file icon" msgstr "Altra icona file" msgid "Cover (front)" msgstr "Copertina (fronte)" msgid "Cover (back)" msgstr "Copertina (retro)" msgid "Leaflet page" msgstr "Pagina dépliant" msgid "Lead artist/lead performer/soloist" msgstr "Artista principale/interprete principale/solista" msgid "Artist/performer" msgstr "Artista/interprete" msgid "Band/Orchestra" msgstr "Band/Orchestra" msgid "Lyricist/text writer" msgstr "Paroliere/scrittore testi" msgid "Recording Location" msgstr "Luogo d'incisione" msgid "During recording" msgstr "Durante la registrazione" msgid "During performance" msgstr "Durante l'esecuzione" msgid "Movie/video screen capture" msgstr "Cattura schermo/film" msgid "A bright coloured fish" msgstr "Un pesce dai colori vivaci" msgid "Illustration" msgstr "Illustrazione" msgid "Band/artist logotype" msgstr "Logo Gruppo/artista" msgid "Publisher/Studio logotype" msgstr "Logo Editore/Studio" msgid "MPEG frames as unit" msgstr "Frame MPEG come unità" msgid "Milliseconds as unit" msgstr "Millisecondi come unità" msgid "Text transcription" msgstr "Trascrizione testo" msgid "Movement/part name" msgstr "Nome movimento/parte" msgid "Events" msgstr "Eventi" msgid "Chord" msgstr "Accordo" msgid "Trivia/pop up" msgstr "Trivia/pop up" msgid "Export" msgstr "Esporta" msgid "Format:" msgstr "Formato:" msgid "Header:" msgstr "Intestazione:" msgid "Tracks:" msgstr "Tracce:" msgid "Footer:" msgstr "Piè di pagina:" msgid "To F&ile" msgstr "In F&ile" msgid "To Clip&board" msgstr "Negli Ap&punti" msgid "&Source:" msgstr "Ori&gine:" msgid "File Error" msgstr "Errore del file" msgid "&Filter:" msgstr "&Filtro:" msgid "&Expression:" msgstr "&Espressione:" msgid "&Apply" msgstr "A&pplica" msgid "Accuracy:" msgstr "Accuratezza:" msgid "Cover Art:" msgstr "Copertina:" msgid "From F&ile/Clipboard..." msgstr "Da f&ile/appunti..." msgid "From T&ags..." msgstr "Da T&ag..." msgid "&From Server:" msgstr "Dal Ser&ver:" msgid "Check maximum allowable time &difference (sec):" msgstr "Controlla &differenza tempo max ammissibile (sec):" msgid "Match with:" msgstr "Trova corrispondenza con:" msgid "&Length" msgstr "&Lunghezza" msgid "T&rack" msgstr "T&raccia" msgid "&Title" msgstr "&Titolo" msgid "&Start number:" msgstr "Numero &inizio:" msgid "&Destination:" msgstr "&Destinazione:" msgid "&Total number of tracks:" msgstr "&Numero delle tracce:" msgid "Create Playlist" msgstr "Crea Playlist" msgid "Playlist File Name" msgstr "Nome del file della Playlist" msgid "Same as &directory name" msgstr "Stesso nome &della cartella" msgid "&Format:" msgstr "&Formato:" msgid "Cr&eate in:" msgstr "Cr&ea in:" msgid "Current directory" msgstr "Cartella corrente" msgid "Every directory" msgstr "Ogni cartella" msgid "Top-level directory" msgstr "Cartella di livello superiore" msgid "Playlist Content" msgstr "Contenuto Playlist" msgid "For&mat:" msgstr "For&mato:" msgid "Incl&ude only the selected files" msgstr "Incl&udi solo i file selezionati" msgid "Sort by file &name" msgstr "Ordina secondo il &nome del file" msgid "Sort by &tag field" msgstr "Ordina secondo il &tag" msgid "Use &relative path for files in playlist" msgstr "Usa un percorso &relativo per i file nella playlist" msgid "Use full p&ath for files in playlist" msgstr "Usa un percorso &completo per i file nella playlist" msgid "Write only &list of files" msgstr "Scrivi solo la &lista dei file" msgid "Write &info using" msgstr "Scrivi le &informazioni usando" msgid "Preview" msgstr "Anteprima" msgid "Create Directory" msgstr "Crea cartella" msgid "From Tag 2 and Tag 1" msgstr "Da Tag 2 e Tag 1" msgid "From:" msgstr "Da:" msgid "To:" msgstr "A:" msgid "&Find" msgstr "&Trova" msgid "C&GI Path:" msgstr "Percorso C&GI:" msgid "Track Title/Artist - Album" msgstr "Titolo traccia/artista - album" msgid "State" msgstr "Stato" msgid "No result" msgstr "Nessun risultato" msgid "No result selected" msgstr "Nessun risultato selezionato" msgid "Import from Tags" msgstr "Importa da Tag" msgid "Extraction:" msgstr "Estrazione:" msgid "Import from File/Clipboard" msgstr "Importa da file/appunti" msgid "From F&ile" msgstr "Da f&ile" msgid "Download" msgstr "Scarica" msgid "Opening directory..." msgstr "Apertura cartella in corso..." msgid "Saving directory..." msgstr "Salvataggio cartella in corso..." msgid "" "The current directory has been modified.\n" "Do you want to save it?" msgstr "" "La cartella in uso è stata modificata.\n" "Volete salvarla?" msgid "Warning" msgstr "Avvertimento" msgid "Exiting..." msgstr "Uscita in corso..." msgid "Creating playlist..." msgstr "Creazione playlist in corso..." msgid "Import" msgstr "Importa" msgid "Error while renaming:\n" msgstr "Errore durante la rinomina:\n" msgid " [filtered]" msgstr " [filtrato]" msgid "Add Frame" msgstr "Aggiungi frame" msgid "Select the frame ID" msgstr "Seleziona l'ID del frame" msgid "Rename File" msgstr "Rinomina file" msgid "Enter new file name:" msgstr "Digita nuovo nome file:" msgid "Do you really want to move these %1 items to the trash?" msgstr "Vuoi veramente spostare nel cestino questi %1 elementi?" msgid "Do you really want to move this item to the trash?" msgstr "Vuoi veramente spostare nel cestino questo elemento?" msgid "Move to Trash" msgstr "Cestina" msgid "Directory must be empty.\n" msgstr "La cartella deve essere vuota.\n" msgid "Could not move these files to the Trash" msgstr "Impossibile spostare questi file nel cestino" msgid "Expand All" msgstr "Espandi tutto" msgid "&Expand all" msgstr "&Espandi tutto" msgid "&Collapse all" msgstr "&Raccogli tutto" msgid "&Rename" msgstr "&Rinomina" msgid "&Move to Trash" msgstr "Cesti&na" msgid "Kid3" msgstr "Kid3" msgid "F&ile" msgstr "F&ile" msgid "Name:" msgstr "Nome:" msgid "Filename from Tag 1" msgstr "Nome file da Tag 1" msgid "Filename from Tag 2" msgstr "Nome file da Tag 2" msgid "Tag 1 from Filename" msgstr "Tag 1 dal Nome file" msgid "Tag 2 from Filename" msgstr "Tag 2 dal Nome file" msgid "&Add..." msgstr "&Aggiungi..." msgid "Move &Up" msgstr "Sposta in &alto" msgid "Move &Down" msgstr "Sposta in &basso" msgid "&Edit..." msgstr "&Modifica..." msgid "&Remove" msgstr "&Rimuovi" msgid "&Insert row" msgstr "&Inserisci riga" msgid "&Delete row" msgstr "&Elimina riga" msgid "&Clear row" msgstr "&Azzera riga" msgid "Format while editing" msgstr "Formattazione durante la modifica" msgid "Case conversion:" msgstr "Maiuscole/minuscole:" msgid "No changes" msgstr "Nessuna modifica" msgid "All lowercase" msgstr "Tutte minuscole" msgid "All uppercase" msgstr "Tutte maiuscole" msgid "First letter uppercase" msgstr "Prima lettera maiuscola" msgid "All first letters uppercase" msgstr "Tutte le prime lettere maiuscole" msgid "Locale:" msgstr "Locale:" msgid "None" msgstr "Nessuno" msgid "String replacement:" msgstr "Sostituzione stringa:" msgid "From" msgstr "Da" msgid "To" msgstr "A" msgid "&Add" msgstr "&Aggiungi" msgid "&Select all" msgstr "&Seleziona tutto" msgid "&Deselect all" msgstr "&Deseleziona tutto" msgid "View Picture" msgstr "Visualizza immagine" msgid "" "Drag album\n" "artwork\n" "here" msgstr "" "Trascina qui\n" "immagini\n" "dell'album" msgid "Play/Pause" msgstr "Riproduci/Pausa" msgid "Stop playback" msgstr "Interrompi la riproduzione" msgid "Previous Track" msgstr "Traccia precedente" msgid "Next Track" msgstr "Prossima traccia" msgid "Close" msgstr "Chiudi" msgid "Mute" msgstr "Muto" msgid "Volume: %1%" msgstr "Volume: %1%" msgid "Add Item" msgstr "Aggiungi elemento" msgid "Edit Item" msgstr "Modifica elemento" msgid "MusicBrainz Fingerprint" msgstr "MusicBrainz Fingerprint" msgid "Unrecognized" msgstr "Non riconosciuto" msgid "Recognized" msgstr "Riconosciuto" msgid "User Selection" msgstr "Selezione utente" msgid "ID Lookup" msgstr "Ricerca ID" msgid "Fingerprint" msgstr "Fingerprint" msgid "Metadata Lookup" msgstr "Informazioni aggiuntive lette" msgid "Amazon" msgstr "Amazon" msgid "Discogs" msgstr "Discogs" msgid "gnudb.org" msgstr "gnudb.org" msgid "TrackType.org" msgstr "TrackType.org" msgid "AENC - Audio encryption" msgstr "AENC - Cifratura audio" msgid "APIC - Attached picture" msgstr "APIC - Immagine allegata" msgid "COMM - Comments" msgstr "COMM - Commenti" msgid "COMR - Commercial" msgstr "COMR - Commerciale" msgid "ENCR - Encryption method registration" msgstr "ENCR - Registrazione metodo di cifratura" msgid "EQUA - Equalization" msgstr "EQUA - Equalizzazione" msgid "ETCO - Event timing codes" msgstr "ETCO - Codici temporali degli eventi" msgid "GEOB - General encapsulated object" msgstr "GEOB - Oggetto incapsulato di tipo generale" msgid "GRID - Group identification registration" msgstr "GRID - Registrazione identificazione gruppo" msgid "IPLS - Involved people list" msgstr "IPLS - Elenco persone coinvolte" msgid "LINK - Linked information" msgstr "LINK - Informazione collegata" msgid "MCDI - Music CD identifier" msgstr "MCDI - Identificatore CD musicale" msgid "MLLT - MPEG location lookup table" msgstr "MLLT - Tabella ricerca posizione MPEG" msgid "OWNE - Ownership frame" msgstr "OWNE - Frame appartenenza" msgid "PRIV - Private frame" msgstr "PRIV - Frame privato" msgid "PCNT - Play counter" msgstr "PCNT - Contatore esecuzione" msgid "POPM - Popularimeter" msgstr "POPM - Indicatore di popolarità" msgid "POSS - Position synchronisation frame" msgstr "POSS - Frame sincronizzazione posizione" msgid "RBUF - Recommended buffer size" msgstr "RBUF - Dimensione buffer raccomandata" msgid "RVAD - Relative volume adjustment" msgstr "RVAD - Regolazione volume relativo" msgid "RVRB - Reverb" msgstr "RVRB - Riverbero" msgid "SYLT - Synchronized lyric/text" msgstr "SYLT - Lirica/testo sincronizzato" msgid "SYTC - Synchronized tempo codes" msgstr "SYTC - Codici tempo sincronizzati" msgid "TALB - Album/Movie/Show title" msgstr "TALB - Titolo Album/Film/Show" msgid "TBPM - BPM (beats per minute)" msgstr "TBPM - BPM (battute al minuto)" msgid "TCOM - Composer" msgstr "TCOM - Compositore" msgid "TCON - Content type" msgstr "TCON - Tipo contenuto" msgid "TCOP - Copyright message" msgstr "TCOP - Messaggio di copyright" msgid "TDAT - Date" msgstr "TDAT - Data" msgid "TDLY - Playlist delay" msgstr "TDLY - Ritardo playlist" msgid "TENC - Encoded by" msgstr "TENC - Codificato da" msgid "TEXT - Lyricist/Text writer" msgstr "TEXT - Paroliere/Scrittore testi" msgid "TFLT - File type" msgstr "TFLT - Tipo di file" msgid "TIME - Time" msgstr "TIME - Ora" msgid "TIT1 - Content group description" msgstr "TIT1 - Descrizione contenuto gruppo" msgid "TIT2 - Title/songname/content description" msgstr "TIT2 - Descrizione titolo/nome brano/contenuto" msgid "TIT3 - Subtitle/Description refinement" msgstr "TIT3 - Miglioramento sottotitolo/descrizione" msgid "TKEY - Initial key" msgstr "TKEY - Chiave iniziale" msgid "TLAN - Language(s)" msgstr "TLAN - Lingua/e" msgid "TLEN - Length" msgstr "TLEN - Lunghezza" msgid "TMED - Media type" msgstr "TMED - Tipo di media" msgid "TOAL - Original album/movie/show title" msgstr "TOAL - Titolo album originale/film/show" msgid "TOFN - Original filename" msgstr "TOFN - Nome file originale" msgid "TOLY - Original lyricist(s)/text writer(s)" msgstr "TOLY - Paroliere/i originale/i - Scrittore/i testo" msgid "TOPE - Original artist(s)/performer(s)" msgstr "TOPE - Artista/i originale/i - interprete/i" msgid "TORY - Original release year" msgstr "TORY - Anno originale di pubblicazione" msgid "TOWN - File owner/licensee" msgstr "TOWN - Proprietario/licenziatario file" msgid "TPE1 - Lead performer(s)/Soloist(s)" msgstr "TPE1 - Interprete/i principale/i - Solista/i" msgid "TPE2 - Band/orchestra/accompaniment" msgstr "TPE2 - Band/orchestra/accompagnamento" msgid "TPE3 - Conductor/performer refinement" msgstr "TPE3 - Miglioramento direttore d'orchestra/interprete" msgid "TPE4 - Interpreted, remixed, or otherwise modified by" msgstr "TPE4 - Interpretato, re-mixato, o comunque modificato da" msgid "TPOS - Part of a set" msgstr "TPOS - Parte di una serie" msgid "TPUB - Publisher" msgstr "TPUB - Editore" msgid "TRCK - Track number/Position in set" msgstr "TRCK - Numero traccia/Posizione nella serie" msgid "TRDA - Recording dates" msgstr "TRDA - Date registrazione" msgid "TRSN - Internet radio station name" msgstr "TRSN - Nome stazione radio Internet" msgid "TRSO - Internet radio station owner" msgstr "TRSO - Proprietario stazione radio Internet" msgid "TSIZ - Size" msgstr "TSIZ - Dimensione" msgid "TSRC - ISRC (international standard recording code)" msgstr "TSRC - ISRC (Codice registrazione internazionale standard)" msgid "TSSE - Software/Hardware and settings used for encoding" msgstr "TSSE - Software/Hardware e impostazioni utilizzati per la codifica" msgid "TXXX - User defined text information" msgstr "TXXX - Informazione di testo definita dall'utente" msgid "TYER - Year" msgstr "TYER - Anno" msgid "UFID - Unique file identifier" msgstr "UFID - Identificatore unico di file" msgid "USER - Terms of use" msgstr "USER - Condizioni di utilizzo" msgid "USLT - Unsynchronized lyric/text transcription" msgstr "USLT - Lirica non sincronizzata/trascrizione testo" msgid "WCOM - Commercial information" msgstr "WCOM - Informazioni commerciali" msgid "WCOP - Copyright/Legal information" msgstr "WCOP - Informazioni di copyright/legali" msgid "WOAF - Official audio file webpage" msgstr "WOAF - Pagina web ufficiale del file audio" msgid "WOAR - Official artist/performer webpage" msgstr "WOAR - Pagina web ufficiale dell'artista/interprete" msgid "WOAS - Official audio source webpage" msgstr "WOAS - Pagina web ufficiale della sorgente audio" msgid "WORS - Official internet radio station homepage" msgstr "WORS - Home page ufficiale della stazione radio internet" msgid "WPAY - Payment" msgstr "WPAY - Pagamento" msgid "WPUB - Official publisher webpage" msgstr "WPUB - Pagina web ufficiale dell'editore" msgid "WXXX - User defined URL link" msgstr "WXXX - Collegamento URL definito dall'utente" msgid "MusicBrainz Release" msgstr "MusicBrainz Release" msgid "ASPI - Audio seek point index" msgstr "ASPI - Indice punto di ricerca audio" msgid "EQU2 - Equalisation (2)" msgstr "EQU2 - Equalizzazione (2)" msgid "RVA2 - Relative volume adjustment (2)" msgstr "RVA2 - Regolazione volume relativo (2)" msgid "SEEK - Seek frame" msgstr "SEEK - Frame ricerca" msgid "SIGN - Signature frame" msgstr "SIGN - Frame firma" msgid "TCMP - iTunes compilation flag" msgstr "TCMP - iTunes compilation" msgid "TDEN - Encoding time" msgstr "TDEN - Tempo di codifica" msgid "TDOR - Original release time" msgstr "TDOR - Data originale di pubblicazione" msgid "TDRC - Recording time" msgstr "TDRC - Tempo di registrazione" msgid "TDRL - Release time" msgstr "TDRL - Data di pubblicazione" msgid "TDTG - Tagging time" msgstr "TDTG - Tempo creazione tag" msgid "TIPL - Involved people list" msgstr "TIPL - Elenco persone coinvolte" msgid "TMCL - Musician credits list" msgstr "TMCL - Elenco ringraziamenti musicisti" msgid "TMOO - Mood" msgstr "TMOO - Mood" msgid "TPRO - Produced notice" msgstr "TPRO - Indicatore copyright produttore" msgid "TSO2 - Album artist sort order" msgstr "TSO2 - Ordina per artista album" msgid "TSOA - Album sort order" msgstr "TSOA - Ordinamento per album" msgid "TSOC - Composer sort order" msgstr "TSOC - Ordinamento per compositore" msgid "TSOP - Performer sort order" msgstr "TSOP - Ordinamento per interprete" msgid "TSOT - Title sort order" msgstr "TSOT - Ordinamento per titolo" msgid "TSST - Set subtitle" msgstr "TSST - Imposta sottotitolo" ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/po/kid3.pot������������������������������������������������������������������������������0000664�0000000�0000000�00000055601�12246035113�0014346�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: Kid3 3.0.2\n" "Report-Msgid-Bugs-To: http://sourceforge.net/p/kid3/bugs/\n" "POT-Creation-Date: 2013-09-11 21:22+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" msgid "Timeout" msgstr "" msgid "Usage:" msgstr "" msgid "Help" msgstr "" msgid "Command name" msgstr "" msgid "Quit application" msgstr "" msgid "The current directory has been modified." msgstr "" msgid "Type 'exit force' to quit." msgstr "" msgid "Change directory" msgstr "" msgid "%1 does not exist" msgstr "" msgid "Print the filename of the current working directory" msgstr "" msgid "Directory list" msgstr "" msgid "Saves the changed files" msgstr "" msgid "Error while writing file:\n" msgstr "" msgid "Select file" msgstr "" msgid "%1 not found" msgstr "" msgid "Select tag" msgstr "" msgid "Get tag frame" msgstr "" msgid "Set tag frame" msgstr "" msgid "Could not set \"%1\" for %2" msgstr "" msgid "Revert" msgstr "" msgid "Import from file or clipboard" msgstr "" msgid "Format name" msgstr "" msgid "%1 not found." msgstr "" msgid "Available" msgstr "" msgid "Error" msgstr "" msgid "Automatic import" msgstr "" msgid "Profile name" msgstr "" msgid "Reading Directory" msgstr "" msgid "Started" msgstr "" msgid "Source" msgstr "" msgid "Querying" msgstr "" msgid "Fetching" msgstr "" msgid "Data received" msgstr "" msgid "Cover" msgstr "" msgid "Finished" msgstr "" msgid "Aborted" msgstr "" msgid "Download album cover artwork" msgstr "" msgid "Export to file or clipboard" msgstr "" msgid "Create playlist" msgstr "" msgid "Apply filename format" msgstr "" msgid "Apply tag format" msgstr "" msgid "Apply text encoding" msgstr "" msgid "Rename directory" msgstr "" msgid "Number tracks" msgstr "" msgid "Track number" msgstr "" msgid "Filter" msgstr "" msgid "Filter name" msgstr "" msgid "Convert ID3v2.3 to ID3v2.4" msgstr "" msgid "Convert ID3v2.4 to ID3v2.3" msgstr "" msgid "Filename from tag" msgstr "" msgid "Tag from filename" msgstr "" msgid "Tag to other tag" msgstr "" msgid "Copy" msgstr "" msgid "Paste" msgstr "" msgid "Remove" msgstr "" msgid "Play" msgstr "" msgid "Parameter" msgstr "" msgid "File path" msgstr "" msgid "URL" msgstr "" msgid "Tag numbers" msgstr "" msgid "Frame name" msgstr "" msgid "Frame value" msgstr "" msgid "Format" msgstr "" msgid "Command specific" msgstr "" msgid "Available Commands" msgstr "" msgid "File" msgstr "" msgid "Name" msgstr "" msgid "Tag 2" msgstr "" msgid "Tag 1" msgstr "" msgid "Tags" msgstr "" msgid "Unknown command '%1'. Type 'help' for help." msgstr "" msgid "Unknown command '%1', -h for help." msgstr "" msgid "Files" msgstr "" msgid "User Actions" msgstr "" msgid "Network" msgstr "" msgid "Plugins" msgstr "" msgid "Main Toolbar" msgstr "" msgid "Opens a directory" msgstr "" msgid "Opens a recently used directory" msgstr "" msgid "Reverts the changes of all or the selected files" msgstr "" msgid "Quits the application" msgstr "" msgid "Select all files" msgstr "" msgid "Deselect all files" msgstr "" msgid "Configure Shortcuts" msgstr "" msgid "Configure Toolbars" msgstr "" msgid "Preferences dialog" msgstr "" msgid "O&pen Directory..." msgstr "" msgid "&Import..." msgstr "" msgid "Import from %1..." msgstr "" msgid "Automatic I&mport..." msgstr "" msgid "&Browse Cover Art..." msgstr "" msgid "&Export..." msgstr "" msgid "&Create Playlist..." msgstr "" msgid "Apply &Filename Format" msgstr "" msgid "Apply &Tag Format" msgstr "" msgid "Apply Text &Encoding" msgstr "" msgid "&Rename Directory..." msgstr "" msgid "&Number Tracks..." msgstr "" msgid "F&ilter..." msgstr "" msgid "Convert ID3v2.3 to ID3v2.&4" msgstr "" msgid "Convert ID3v2.4 to ID3v2.&3" msgstr "" msgid "&Play" msgstr "" msgid "Show &Picture" msgstr "" msgid "Auto &Hide Tags" msgstr "" msgid "Select All in &Directory" msgstr "" msgid "&Previous File" msgstr "" msgid "&Next File" msgstr "" msgid "From Filename" msgstr "" msgid "From Tag 2" msgstr "" msgid "From Tag 1" msgstr "" msgid "Frames:" msgstr "" msgid "Edit" msgstr "" msgid "Add" msgstr "" msgid "Delete" msgstr "" msgid "Filename" msgstr "" msgid "Focus" msgstr "" msgid "File List" msgstr "" msgid "Directory List" msgstr "" msgid "Configure - Kid3" msgstr "" msgid "Open" msgstr "" msgid "Save As" msgstr "" msgid "directory to open" msgstr "" msgid "Kid3 ID3 Tagger" msgstr "" #. i18n NAME OF TRANSLATORS msgid "Your names" msgstr "" #. i18n EMAIL OF TRANSLATORS msgid "Your emails" msgstr "" msgid "&Back" msgstr "" msgid "&Forward" msgstr "" msgid "&Find:" msgstr "" msgid "Find Previous" msgstr "" msgid "Find Next" msgstr "" msgid "&Close" msgstr "" msgid "&Tags" msgstr "" msgid "&Files" msgstr "" msgid "&User Actions" msgstr "" msgid "&Network" msgstr "" msgid "&Plugins" msgstr "" msgid "&Keyboard Shortcuts" msgstr "" msgid "Use custom app&lication font" msgstr "" msgid "A&pplication Font..." msgstr "" msgid "Use custom application &style" msgstr "" msgid "Unknown" msgstr "" msgid "Use native system file &dialogs" msgstr "" msgid "&Appearance" msgstr "" msgid "&Help" msgstr "" msgid "&OK" msgstr "" msgid "&Cancel" msgstr "" msgid "The keyboard shortcut '%1' is already assigned to '%2'." msgstr "" msgid "&File" msgstr "" msgid "&Open..." msgstr "" msgid "Open &Recent" msgstr "" msgid "&Save" msgstr "" msgid "Re&vert" msgstr "" msgid "Import from %1" msgstr "" msgid "Browse album cover artwork" msgstr "" msgid "Create M3U Playlist" msgstr "" msgid "&Quit" msgstr "" msgid "&Edit" msgstr "" msgid "Select &All" msgstr "" msgid "Dese&lect" msgstr "" msgid "Select all files in the current directory" msgstr "" msgid "Select previous file" msgstr "" msgid "Select next file" msgstr "" msgid "&Tools" msgstr "" msgid "Apply Filename Format" msgstr "" msgid "Apply Tag Format" msgstr "" msgid "Apply Text Encoding" msgstr "" msgid "Rename Directory" msgstr "" msgid "Number Tracks" msgstr "" msgid "&Settings" msgstr "" msgid "Enables/disables the toolbar" msgstr "" msgid "Show &Toolbar" msgstr "" msgid "Enables/disables the statusbar" msgstr "" msgid "Show St&atusbar" msgstr "" msgid "Show Picture" msgstr "" msgid "Auto Hide Tags" msgstr "" msgid "Configure Kid3" msgstr "" msgid "&Configure Kid3..." msgstr "" msgid "Kid3 Handbook" msgstr "" msgid "Kid3 &Handbook" msgstr "" msgid "About Kid3" msgstr "" msgid "&About Kid3" msgstr "" msgid "About Qt" msgstr "" msgid "About &Qt" msgstr "" msgid " [modified]" msgstr "" msgid "Toggle the statusbar..." msgstr "" msgid "Ready." msgstr "" msgid "&Clear List" msgstr "" msgid "Clear" msgstr "" msgid "Reset" msgstr "" msgid "Press F2 or double click to edit cell contents." msgstr "" msgid "Action" msgstr "" msgid "Shortcut" msgstr "" msgid "Server" msgstr "" msgid "Accuracy" msgstr "" msgid "Standard Tags" msgstr "" msgid "Additional Tags" msgstr "" msgid "Cover Art" msgstr "" msgid "Invalid File" msgstr "" msgid "Data received: %1" msgstr "" msgid "Request sent..." msgstr "" msgid "Year" msgstr "" msgid "Length" msgstr "" msgid "Filenames" msgstr "" msgid "URLs" msgstr "" msgid "Directory name" msgstr "" msgid "Encode as URL" msgstr "" msgid "Confirm" msgstr "" msgid "Output" msgstr "" msgid "Command" msgstr "" msgid "Create directory %1 failed\n" msgstr "" msgid "File %1 already exists\n" msgstr "" msgid "%1 is not a directory\n" msgstr "" msgid "Rename %1 to %2 failed\n" msgstr "" msgid "%1 already exists\n" msgstr "" msgid "%1 is not a file\n" msgstr "" msgid "New directory name is too different\n" msgstr "" msgid "Create directory" msgstr "" msgid "Rename file" msgstr "" msgid "C&lear" msgstr "" msgid "Execute " msgstr "" msgid "Could not execute " msgstr "" msgid "True if strings are equal" msgstr "" msgid "True if string contains substring" msgstr "" msgid "True if string matches regexp" msgstr "" msgid "Logical AND" msgstr "" msgid "Logical OR" msgstr "" msgid "Logical negation" msgstr "" msgid "Data" msgstr "" msgid "%1 is not writable" msgstr "" msgid "All Supported Files" msgstr "" msgid "All Files" msgstr "" msgid "Track" msgstr "" msgid "Absolute path to file" msgstr "" msgid "Duration" msgstr "" msgid "Title" msgstr "" msgid "Artist" msgstr "" msgid "Album" msgstr "" msgid "Comment" msgstr "" msgid "Date" msgstr "" msgid "Track Number" msgstr "" msgid "Genre" msgstr "" msgid "Album Artist" msgstr "" msgid "Arranger" msgstr "" msgid "Author" msgstr "" msgid "BPM" msgstr "" msgid "Catalog Number" msgstr "" msgid "Compilation" msgstr "" msgid "Composer" msgstr "" msgid "Conductor" msgstr "" msgid "Copyright" msgstr "" msgid "Disc Number" msgstr "" msgid "Encoded-by" msgstr "" msgid "Encoder Settings" msgstr "" msgid "Encoding Time" msgstr "" msgid "Grouping" msgstr "" msgid "Initial Key" msgstr "" msgid "ISRC" msgstr "" msgid "Language" msgstr "" msgid "Lyricist" msgstr "" msgid "Lyrics" msgstr "" msgid "Media" msgstr "" msgid "Mood" msgstr "" msgid "Original Album" msgstr "" msgid "Original Artist" msgstr "" msgid "Original Date" msgstr "" msgid "Part" msgstr "" msgid "Performer" msgstr "" msgid "Picture" msgstr "" msgid "Publisher" msgstr "" msgid "Release Country" msgstr "" msgid "Remixer" msgstr "" msgid "Sort Album" msgstr "" msgid "Sort Album Artist" msgstr "" msgid "Sort Artist" msgstr "" msgid "Sort Composer" msgstr "" msgid "Sort Name" msgstr "" msgid "Subtitle" msgstr "" msgid "Website" msgstr "" msgid "WWW Audio File" msgstr "" msgid "WWW Audio Source" msgstr "" msgid "Number of tracks" msgstr "" msgid "Extension" msgstr "" msgid "Bitrate" msgstr "" msgid "VBR" msgstr "" msgid "Samplerate" msgstr "" msgid "Channels" msgstr "" msgid "Codec" msgstr "" msgid "Escape for HTML" msgstr "" msgid "Automatic Import" msgstr "" msgid "D&estination:" msgstr "" msgid "Tag 1 and Tag 2" msgstr "" msgid "&Profile:" msgstr "" msgid "&Save Settings" msgstr "" msgid "New" msgstr "" msgid "A&bort" msgstr "" msgid "S&tart" msgstr "" msgid "Import Source" msgstr "" msgid "&Server:" msgstr "" msgid "&Accuracy:" msgstr "" msgid "&Standard Tags" msgstr "" msgid "&Additional Tags" msgstr "" msgid "C&over Art" msgstr "" msgid "Browse Cover Art" msgstr "" msgid "&Artist/Album" msgstr "" msgid "&Source" msgstr "" msgid "Source:" msgstr "" msgid "URL:" msgstr "" msgid "&URL extraction" msgstr "" msgid "Match" msgstr "" msgid "Picture URL" msgstr "" msgid "&Browse" msgstr "" msgid "Click Browse to start" msgstr "" msgid "Then drag the picture from the browser to Kid3." msgstr "" msgid "ID3v1" msgstr "" msgid "&Mark truncated fields" msgstr "" msgid "Text &encoding:" msgstr "" msgid "ID3v2" msgstr "" msgid "Use &track/total number of tracks format" msgstr "" msgid "Track number &digits:" msgstr "" msgid "&Genre as text instead of numeric string" msgstr "" msgid "ISO-8859-1" msgstr "" msgid "UTF16" msgstr "" msgid "UTF8" msgstr "" msgid "&Version used for new tags:" msgstr "" msgid "ID3v2.3.0" msgstr "" msgid "ID3v2.4.0" msgstr "" msgid "Ogg/Vorbis" msgstr "" msgid "Co&mment field name:" msgstr "" msgid "&Picture field name:" msgstr "" msgid "Custom &Genres" msgstr "" msgid "&Show only custom genres" msgstr "" msgid "&Quick Access Tags" msgstr "" msgid "&Tag Format" msgstr "" msgid "Tag &1" msgstr "" msgid "Tag &2" msgstr "" msgid "Tag 1 a&nd Tag 2" msgstr "" msgid "Startup" msgstr "" msgid "&Load last-opened files" msgstr "" msgid "Save" msgstr "" msgid "&Preserve file timestamp" msgstr "" msgid "&Mark changes" msgstr "" msgid "F&ilename for cover:" msgstr "" msgid "&Filename Format" msgstr "" msgid "Browser" msgstr "" msgid "Web &browser:" msgstr "" msgid "Context &Menu Commands" msgstr "" msgid "&Play on double click" msgstr "" msgid "Proxy" msgstr "" msgid "&Proxy:" msgstr "" msgid "&Use authentication with proxy" msgstr "" msgid "Proxy user &name:" msgstr "" msgid "Proxy pass&word:" msgstr "" msgid "&Metadata Plugins && Priority" msgstr "" msgid "A&vailable Plugins" msgstr "" msgid "Changes take only effect after a restart!" msgstr "" msgid "From Clip&board" msgstr "" msgid "&Import" msgstr "" msgid "&Export" msgstr "" msgid "&View" msgstr "" msgid "Text Encoding" msgstr "" msgid "Text" msgstr "" msgid "Description" msgstr "" msgid "Owner" msgstr "" msgid "Email" msgstr "" msgid "Rating" msgstr "" msgid "Picture Type" msgstr "" msgid "Image format" msgstr "" msgid "Mimetype" msgstr "" msgid "Counter" msgstr "" msgid "Identifier" msgstr "" msgid "Volume Adjustment" msgstr "" msgid "Number of Bits" msgstr "" msgid "Volume Change Right" msgstr "" msgid "Volume Change Left" msgstr "" msgid "Peak Volume Right" msgstr "" msgid "Peak Volume Left" msgstr "" msgid "Timestamp Format" msgstr "" msgid "Content Type" msgstr "" msgid "Price" msgstr "" msgid "Seller" msgstr "" msgid "Images" msgstr "" msgid "UTF16BE" msgstr "" msgid "Other" msgstr "" msgid "32x32 pixels PNG file icon" msgstr "" msgid "Other file icon" msgstr "" msgid "Cover (front)" msgstr "" msgid "Cover (back)" msgstr "" msgid "Leaflet page" msgstr "" msgid "Lead artist/lead performer/soloist" msgstr "" msgid "Artist/performer" msgstr "" msgid "Band/Orchestra" msgstr "" msgid "Lyricist/text writer" msgstr "" msgid "Recording Location" msgstr "" msgid "During recording" msgstr "" msgid "During performance" msgstr "" msgid "Movie/video screen capture" msgstr "" msgid "A bright coloured fish" msgstr "" msgid "Illustration" msgstr "" msgid "Band/artist logotype" msgstr "" msgid "Publisher/Studio logotype" msgstr "" msgid "MPEG frames as unit" msgstr "" msgid "Milliseconds as unit" msgstr "" msgid "Text transcription" msgstr "" msgid "Movement/part name" msgstr "" msgid "Events" msgstr "" msgid "Chord" msgstr "" msgid "Trivia/pop up" msgstr "" msgid "Export" msgstr "" msgid "Format:" msgstr "" msgid "Header:" msgstr "" msgid "Tracks:" msgstr "" msgid "Footer:" msgstr "" msgid "To F&ile" msgstr "" msgid "To Clip&board" msgstr "" msgid "&Source:" msgstr "" msgid "File Error" msgstr "" msgid "&Filter:" msgstr "" msgid "&Expression:" msgstr "" msgid "&Apply" msgstr "" msgid "Accuracy:" msgstr "" msgid "Cover Art:" msgstr "" msgid "From F&ile/Clipboard..." msgstr "" msgid "From T&ags..." msgstr "" msgid "&From Server:" msgstr "" msgid "Check maximum allowable time &difference (sec):" msgstr "" msgid "Match with:" msgstr "" msgid "&Length" msgstr "" msgid "T&rack" msgstr "" msgid "&Title" msgstr "" msgid "&Start number:" msgstr "" msgid "&Destination:" msgstr "" msgid "&Total number of tracks:" msgstr "" msgid "Create Playlist" msgstr "" msgid "Playlist File Name" msgstr "" msgid "Same as &directory name" msgstr "" msgid "&Format:" msgstr "" msgid "Cr&eate in:" msgstr "" msgid "Current directory" msgstr "" msgid "Every directory" msgstr "" msgid "Top-level directory" msgstr "" msgid "Playlist Content" msgstr "" msgid "For&mat:" msgstr "" msgid "Incl&ude only the selected files" msgstr "" msgid "Sort by file &name" msgstr "" msgid "Sort by &tag field" msgstr "" msgid "Use &relative path for files in playlist" msgstr "" msgid "Use full p&ath for files in playlist" msgstr "" msgid "Write only &list of files" msgstr "" msgid "Write &info using" msgstr "" msgid "Preview" msgstr "" msgid "Create Directory" msgstr "" msgid "From Tag 2 and Tag 1" msgstr "" msgid "From:" msgstr "" msgid "To:" msgstr "" msgid "&Find" msgstr "" msgid "C&GI Path:" msgstr "" msgid "Track Title/Artist - Album" msgstr "" msgid "State" msgstr "" msgid "No result" msgstr "" msgid "No result selected" msgstr "" msgid "Import from Tags" msgstr "" msgid "Extraction:" msgstr "" msgid "Import from File/Clipboard" msgstr "" msgid "From F&ile" msgstr "" msgid "Download" msgstr "" msgid "Opening directory..." msgstr "" msgid "Saving directory..." msgstr "" msgid "" "The current directory has been modified.\n" "Do you want to save it?" msgstr "" msgid "Warning" msgstr "" msgid "Exiting..." msgstr "" msgid "Creating playlist..." msgstr "" msgid "Import" msgstr "" msgid "Error while renaming:\n" msgstr "" msgid " [filtered]" msgstr "" msgid "Add Frame" msgstr "" msgid "Select the frame ID" msgstr "" msgid "Rename File" msgstr "" msgid "Enter new file name:" msgstr "" msgid "Do you really want to move these %1 items to the trash?" msgstr "" msgid "Do you really want to move this item to the trash?" msgstr "" msgid "Move to Trash" msgstr "" msgid "Directory must be empty.\n" msgstr "" msgid "Could not move these files to the Trash" msgstr "" msgid "Expand All" msgstr "" msgid "&Expand all" msgstr "" msgid "&Collapse all" msgstr "" msgid "&Rename" msgstr "" msgid "&Move to Trash" msgstr "" msgid "Kid3" msgstr "" msgid "F&ile" msgstr "" msgid "Name:" msgstr "" msgid "Filename from Tag 1" msgstr "" msgid "Filename from Tag 2" msgstr "" msgid "Tag 1 from Filename" msgstr "" msgid "Tag 2 from Filename" msgstr "" msgid "&Add..." msgstr "" msgid "Move &Up" msgstr "" msgid "Move &Down" msgstr "" msgid "&Edit..." msgstr "" msgid "&Remove" msgstr "" msgid "&Insert row" msgstr "" msgid "&Delete row" msgstr "" msgid "&Clear row" msgstr "" msgid "Format while editing" msgstr "" msgid "Case conversion:" msgstr "" msgid "No changes" msgstr "" msgid "All lowercase" msgstr "" msgid "All uppercase" msgstr "" msgid "First letter uppercase" msgstr "" msgid "All first letters uppercase" msgstr "" msgid "Locale:" msgstr "" msgid "None" msgstr "" msgid "String replacement:" msgstr "" msgid "From" msgstr "" msgid "To" msgstr "" msgid "&Add" msgstr "" msgid "&Select all" msgstr "" msgid "&Deselect all" msgstr "" msgid "View Picture" msgstr "" msgid "" "Drag album\n" "artwork\n" "here" msgstr "" msgid "Play/Pause" msgstr "" msgid "Stop playback" msgstr "" msgid "Previous Track" msgstr "" msgid "Next Track" msgstr "" msgid "Close" msgstr "" msgid "Mute" msgstr "" msgid "Volume: %1%" msgstr "" msgid "Add Item" msgstr "" msgid "Edit Item" msgstr "" msgid "MusicBrainz Fingerprint" msgstr "" msgid "Unrecognized" msgstr "" msgid "Recognized" msgstr "" msgid "User Selection" msgstr "" msgid "ID Lookup" msgstr "" msgid "Fingerprint" msgstr "" msgid "Metadata Lookup" msgstr "" msgid "Amazon" msgstr "" msgid "Discogs" msgstr "" msgid "gnudb.org" msgstr "" msgid "TrackType.org" msgstr "" msgid "AENC - Audio encryption" msgstr "" msgid "APIC - Attached picture" msgstr "" msgid "COMM - Comments" msgstr "" msgid "COMR - Commercial" msgstr "" msgid "ENCR - Encryption method registration" msgstr "" msgid "EQUA - Equalization" msgstr "" msgid "ETCO - Event timing codes" msgstr "" msgid "GEOB - General encapsulated object" msgstr "" msgid "GRID - Group identification registration" msgstr "" msgid "IPLS - Involved people list" msgstr "" msgid "LINK - Linked information" msgstr "" msgid "MCDI - Music CD identifier" msgstr "" msgid "MLLT - MPEG location lookup table" msgstr "" msgid "OWNE - Ownership frame" msgstr "" msgid "PRIV - Private frame" msgstr "" msgid "PCNT - Play counter" msgstr "" msgid "POPM - Popularimeter" msgstr "" msgid "POSS - Position synchronisation frame" msgstr "" msgid "RBUF - Recommended buffer size" msgstr "" msgid "RVAD - Relative volume adjustment" msgstr "" msgid "RVRB - Reverb" msgstr "" msgid "SYLT - Synchronized lyric/text" msgstr "" msgid "SYTC - Synchronized tempo codes" msgstr "" msgid "TALB - Album/Movie/Show title" msgstr "" msgid "TBPM - BPM (beats per minute)" msgstr "" msgid "TCOM - Composer" msgstr "" msgid "TCON - Content type" msgstr "" msgid "TCOP - Copyright message" msgstr "" msgid "TDAT - Date" msgstr "" msgid "TDLY - Playlist delay" msgstr "" msgid "TENC - Encoded by" msgstr "" msgid "TEXT - Lyricist/Text writer" msgstr "" msgid "TFLT - File type" msgstr "" msgid "TIME - Time" msgstr "" msgid "TIT1 - Content group description" msgstr "" msgid "TIT2 - Title/songname/content description" msgstr "" msgid "TIT3 - Subtitle/Description refinement" msgstr "" msgid "TKEY - Initial key" msgstr "" msgid "TLAN - Language(s)" msgstr "" msgid "TLEN - Length" msgstr "" msgid "TMED - Media type" msgstr "" msgid "TOAL - Original album/movie/show title" msgstr "" msgid "TOFN - Original filename" msgstr "" msgid "TOLY - Original lyricist(s)/text writer(s)" msgstr "" msgid "TOPE - Original artist(s)/performer(s)" msgstr "" msgid "TORY - Original release year" msgstr "" msgid "TOWN - File owner/licensee" msgstr "" msgid "TPE1 - Lead performer(s)/Soloist(s)" msgstr "" msgid "TPE2 - Band/orchestra/accompaniment" msgstr "" msgid "TPE3 - Conductor/performer refinement" msgstr "" msgid "TPE4 - Interpreted, remixed, or otherwise modified by" msgstr "" msgid "TPOS - Part of a set" msgstr "" msgid "TPUB - Publisher" msgstr "" msgid "TRCK - Track number/Position in set" msgstr "" msgid "TRDA - Recording dates" msgstr "" msgid "TRSN - Internet radio station name" msgstr "" msgid "TRSO - Internet radio station owner" msgstr "" msgid "TSIZ - Size" msgstr "" msgid "TSRC - ISRC (international standard recording code)" msgstr "" msgid "TSSE - Software/Hardware and settings used for encoding" msgstr "" msgid "TXXX - User defined text information" msgstr "" msgid "TYER - Year" msgstr "" msgid "UFID - Unique file identifier" msgstr "" msgid "USER - Terms of use" msgstr "" msgid "USLT - Unsynchronized lyric/text transcription" msgstr "" msgid "WCOM - Commercial information" msgstr "" msgid "WCOP - Copyright/Legal information" msgstr "" msgid "WOAF - Official audio file webpage" msgstr "" msgid "WOAR - Official artist/performer webpage" msgstr "" msgid "WOAS - Official audio source webpage" msgstr "" msgid "WORS - Official internet radio station homepage" msgstr "" msgid "WPAY - Payment" msgstr "" msgid "WPUB - Official publisher webpage" msgstr "" msgid "WXXX - User defined URL link" msgstr "" msgid "MusicBrainz Release" msgstr "" msgid "ASPI - Audio seek point index" msgstr "" msgid "EQU2 - Equalisation (2)" msgstr "" msgid "RVA2 - Relative volume adjustment (2)" msgstr "" msgid "SEEK - Seek frame" msgstr "" msgid "SIGN - Signature frame" msgstr "" msgid "TCMP - iTunes compilation flag" msgstr "" msgid "TDEN - Encoding time" msgstr "" msgid "TDOR - Original release time" msgstr "" msgid "TDRC - Recording time" msgstr "" msgid "TDRL - Release time" msgstr "" msgid "TDTG - Tagging time" msgstr "" msgid "TIPL - Involved people list" msgstr "" msgid "TMCL - Musician credits list" msgstr "" msgid "TMOO - Mood" msgstr "" msgid "TPRO - Produced notice" msgstr "" msgid "TSO2 - Album artist sort order" msgstr "" msgid "TSOA - Album sort order" msgstr "" msgid "TSOC - Composer sort order" msgstr "" msgid "TSOP - Performer sort order" msgstr "" msgid "TSOT - Title sort order" msgstr "" msgid "TSST - Set subtitle" msgstr "" �������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/po/nl.po���������������������������������������������������������������������������������0000664�0000000�0000000�00000105017�12246035113�0013736�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# translation of nl.po to English # Kid3 Dutch Translation. # Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. # # Urs Fleisch <ufleisch@users.sourceforge.net>, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010. # Kenny Verstraete <verstraete_kenny@skynet.be>, 2008, 2011. msgid "" msgstr "" "Project-Id-Version: Kid3 3.0.2\n" "Report-Msgid-Bugs-To: http://sourceforge.net/p/kid3/bugs/\n" "POT-Creation-Date: 2013-09-11 21:22+0200\n" "PO-Revision-Date: 2012-12-07 15:47+0100\n" "Last-Translator: toon <toon@vgerwen.nl>\n" "Language-Team: Dutch <kde-i18n-doc@kde.org>\n" "Language: nl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Lokalize 1.1\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Language: nl\n" "X-Source-Language: en\n" msgid "Timeout" msgstr "Tijdslimiet" msgid "Usage:" msgstr "Gebruik:" msgid "Help" msgstr "Help" msgid "Command name" msgstr "Commandonaam" msgid "Quit application" msgstr "Sluit de toepassing" msgid "The current directory has been modified." msgstr "De huidige map is gewijzigd." msgid "Type 'exit force' to quit." msgstr "Type 'exit force' als u klaar bent." msgid "Change directory" msgstr "Verander map" msgid "%1 does not exist" msgstr "%1 bestaat niet" msgid "Print the filename of the current working directory" msgstr "Toon de bestandsnaam van de huidige werkmap" msgid "Directory list" msgstr "Mappenlijst" msgid "Saves the changed files" msgstr "Slaat de gewijzigde bestanden op" msgid "Error while writing file:\n" msgstr "Er is een fout opgetreden tijdens het opslaan van het bestand:\n" msgid "Select file" msgstr "Bestand selecteren" msgid "%1 not found" msgstr "%1 niet gevonden" msgid "Select tag" msgstr "Tag selecteren" msgid "Get tag frame" msgstr "Frame ophalen" msgid "Set tag frame" msgstr "Frame instellen" msgid "Could not set \"%1\" for %2" msgstr "Kon \"%1\" niet instellen voor %2" msgid "Revert" msgstr "Ongedaan maken" msgid "Import from file or clipboard" msgstr "Importeer van bestand of klembord" msgid "Format name" msgstr "Naam van format" msgid "%1 not found." msgstr "%1 niet gevonden." msgid "Available" msgstr "Beschikbaar" msgid "Error" msgstr "Fout" msgid "Automatic import" msgstr "Automatisch importeren" msgid "Profile name" msgstr "Profielnaam" msgid "Reading Directory" msgstr "Directory lezen" msgid "Started" msgstr "Begonnen" msgid "Source" msgstr "Bron" msgid "Querying" msgstr "Aanvraag loopt" msgid "Fetching" msgstr "Ophalen" msgid "Data received" msgstr "Gegevens ontvangen" msgid "Cover" msgstr "Hoes" msgid "Finished" msgstr "Voltooid" msgid "Aborted" msgstr "Afgebroken" msgid "Download album cover artwork" msgstr "Hoes ophalen" msgid "Export to file or clipboard" msgstr "Exporteer naar bestand of klembord" msgid "Create playlist" msgstr "Multimedia afspeellijst maken" msgid "Apply filename format" msgstr "Bestandsnaamstructuur Toepassen" msgid "Apply tag format" msgstr "Tagstructuur toepassen" msgid "Apply text encoding" msgstr "Tekst-codering toepassen" msgid "Rename directory" msgstr "Map hernoemen" msgid "Number tracks" msgstr "Tracks nummeren" msgid "Track number" msgstr "Tracknummer" msgid "Filter" msgstr "Filter" msgid "Filter name" msgstr "Filternaam" msgid "Convert ID3v2.3 to ID3v2.4" msgstr "ID3v2.3 naar ID3v2.4 omzetten" msgid "Convert ID3v2.4 to ID3v2.3" msgstr "ID3v2.4 naar ID3v2.3 omzetten" msgid "Filename from tag" msgstr "Bestandsnaam van Tag" msgid "Tag from filename" msgstr "Tag van bestandsnaam" msgid "Tag to other tag" msgstr "Tag van ander tag" msgid "Copy" msgstr "Tag kopiëren" msgid "Paste" msgstr "Tag plakken" msgid "Remove" msgstr "Tag verwijderen" msgid "Play" msgstr "Afspelen" msgid "Parameter" msgstr "Parameter" msgid "File path" msgstr "Bestandspad" msgid "URL" msgstr "URL-adres" msgid "Tag numbers" msgstr "Tagnummer" msgid "Frame name" msgstr "Framenaam" msgid "Frame value" msgstr "Framewaarde" msgid "Format" msgstr "Opmaak" msgid "Command specific" msgstr "Commandospecifieke" msgid "Available Commands" msgstr "Beschikbare opdrachten" msgid "File" msgstr "Bestand" msgid "Name" msgstr "Naam" msgid "Tag 2" msgstr "Tag 2" msgid "Tag 1" msgstr "Tag 1" msgid "Tags" msgstr "Tags" msgid "Unknown command '%1'. Type 'help' for help." msgstr "Onbekend commando '%1'. Typ 'help' voor help." msgid "Unknown command '%1', -h for help." msgstr "Onbekend commando '%1', -h voor help." msgid "Files" msgstr "Bestanden" msgid "User Actions" msgstr "Gebruikersacties" msgid "Network" msgstr "Netwerk" msgid "Plugins" msgstr "Plugins" msgid "Main Toolbar" msgstr "Hoofdwerkbalk" msgid "Opens a directory" msgstr "Opent een map" msgid "Opens a recently used directory" msgstr "Opent een recent geopende map" msgid "Reverts the changes of all or the selected files" msgstr "Maakt de wijzigingen van alle of de geselcteerde bestanden ongedaan" msgid "Quits the application" msgstr "Sluit de toepassing" msgid "Select all files" msgstr "Selecteer alle bestanden" msgid "Deselect all files" msgstr "Deselecteer alle bestanden" msgid "Configure Shortcuts" msgstr "Sneltoetsen instellen" msgid "Configure Toolbars" msgstr "Werkbalken instellen" msgid "Preferences dialog" msgstr "Voorkeuren" msgid "O&pen Directory..." msgstr "&Map openen..." msgid "&Import..." msgstr "&Importeren..." msgid "Import from %1..." msgstr "Importeren van %1..." msgid "Automatic I&mport..." msgstr "Automatisch i&mporteren..." msgid "&Browse Cover Art..." msgstr "&Hoes ophalen..." msgid "&Export..." msgstr "&Exporteren..." msgid "&Create Playlist..." msgstr "&Speellijst aanmaken..." msgid "Apply &Filename Format" msgstr "&Bestandsnaamstructuur toepassen" msgid "Apply &Tag Format" msgstr "ID3 en &Letterstructuur toepassen" msgid "Apply Text &Encoding" msgstr "T&ekst-codering toepassen" msgid "&Rename Directory..." msgstr "&Map hernoemen..." msgid "&Number Tracks..." msgstr "Tracks &nummeren..." msgid "F&ilter..." msgstr "&Filter..." msgid "Convert ID3v2.3 to ID3v2.&4" msgstr "Converteer ID3v2.3 naar ID3v2.&4" msgid "Convert ID3v2.4 to ID3v2.&3" msgstr "Converteer ID3v2.4 naar ID3v2.&3" msgid "&Play" msgstr "&Afspelen" msgid "Show &Picture" msgstr "&Afbeelding tonen" msgid "Auto &Hide Tags" msgstr "Automatisch &verbergen" msgid "Select All in &Directory" msgstr "Selecteer alle bestanden in &de map" msgid "&Previous File" msgstr "&Vorig bestand" msgid "&Next File" msgstr "Volge&nd bestand" msgid "From Filename" msgstr "Van bestandsnaam" msgid "From Tag 2" msgstr "Van Tag 2" msgid "From Tag 1" msgstr "Van Tag 1" msgid "Frames:" msgstr "Frames:" msgid "Edit" msgstr "Frame bewerken" msgid "Add" msgstr "Frame toevoegen" msgid "Delete" msgstr "Frame verwijderen" msgid "Filename" msgstr "Bestandsnaam" msgid "Focus" msgstr "Focus" msgid "File List" msgstr "Bestandslijst" msgid "Directory List" msgstr "Mappenlijst" msgid "Configure - Kid3" msgstr "Kid3 instellen" msgid "Open" msgstr "Openen" msgid "Save As" msgstr "Opslaan als" msgid "directory to open" msgstr "Map te openen" msgid "Kid3 ID3 Tagger" msgstr "Kid3 ID3 Tagger" #. i18n NAME OF TRANSLATORS msgid "Your names" msgstr "Kenny Verstraete, Toon van Gerwen" #. i18n EMAIL OF TRANSLATORS msgid "Your emails" msgstr "verstraete_kenny@skynet.be, toon@vgerwen.nl" msgid "&Back" msgstr "&Vorige" msgid "&Forward" msgstr "&Volgende" msgid "&Find:" msgstr "&Zoeken:" msgid "Find Previous" msgstr "Vorige zoeken" msgid "Find Next" msgstr "Volgende zoeken" msgid "&Close" msgstr "&Sluiten" msgid "&Tags" msgstr "&Tags" msgid "&Files" msgstr "&Bestanden" msgid "&User Actions" msgstr "&Gebruikersacties" msgid "&Network" msgstr "&Netwerk" msgid "&Plugins" msgstr "&Plugins" msgid "&Keyboard Shortcuts" msgstr "&Sneltoetsen" msgid "Use custom app&lication font" msgstr "Gebruik &aangepast lettertype" msgid "A&pplication Font..." msgstr "&Lettertype..." msgid "Use custom application &style" msgstr "Aangepaste &stijl gebruiken" msgid "Unknown" msgstr "Onbekend" msgid "Use native system file &dialogs" msgstr "Bestands&dialogen van systeem gebruiken" msgid "&Appearance" msgstr "&Uiterlijk" msgid "&Help" msgstr "&Help" msgid "&OK" msgstr "&OK" msgid "&Cancel" msgstr "&Annuleren" msgid "The keyboard shortcut '%1' is already assigned to '%2'." msgstr "De sneltoets ‘%1’ is al toegekend aan '%2'." msgid "&File" msgstr "&Bestand" msgid "&Open..." msgstr "&Openen..." msgid "Open &Recent" msgstr "&Recent geopend" msgid "&Save" msgstr "Op&slaan" msgid "Re&vert" msgstr "On&gedaan maken" msgid "Import from %1" msgstr "Importeer van %1" msgid "Browse album cover artwork" msgstr "Hoes ophalen" msgid "Create M3U Playlist" msgstr "M3U speellijst aanmaken" msgid "&Quit" msgstr "&Afsluiten" msgid "&Edit" msgstr "Be&werken" msgid "Select &All" msgstr "&Alles selecteren" msgid "Dese&lect" msgstr "&Deselecteren" msgid "Select all files in the current directory" msgstr "Selecteer alle bestanden in de huidige map" msgid "Select previous file" msgstr "Vorig bestand selecteren" msgid "Select next file" msgstr "Volgend bestand selecteren" msgid "&Tools" msgstr "H&ulpmiddelen" msgid "Apply Filename Format" msgstr "Bestandsnaamstructuur Toepassen" msgid "Apply Tag Format" msgstr "Tagstructuur toepassen" msgid "Apply Text Encoding" msgstr "Tekst-codering toepassen" msgid "Rename Directory" msgstr "Map hernoemen" msgid "Number Tracks" msgstr "Tracks nummeren" msgid "&Settings" msgstr "&Instellingen" msgid "Enables/disables the toolbar" msgstr "Activeert/deactiveert de werkbalk" msgid "Show &Toolbar" msgstr "&Werkbalk tonen" msgid "Enables/disables the statusbar" msgstr "Activeert/deactiveert de statusbalk" msgid "Show St&atusbar" msgstr "&Statusbalk tonen" msgid "Show Picture" msgstr "Afbeelding tonen" msgid "Auto Hide Tags" msgstr "Automatisch verbergen" msgid "Configure Kid3" msgstr "Kid3 instellen" msgid "&Configure Kid3..." msgstr "&Kid3 instellen..." msgid "Kid3 Handbook" msgstr "Kid3 Handboek" msgid "Kid3 &Handbook" msgstr "Kid3 &Handboek" msgid "About Kid3" msgstr "Over Kid3" msgid "&About Kid3" msgstr "Over &Kid3" msgid "About Qt" msgstr "Over Qt" msgid "About &Qt" msgstr "Over &Qt" msgid " [modified]" msgstr " [gewijzigd]" msgid "Toggle the statusbar..." msgstr "De statusbalk omschakelen..." msgid "Ready." msgstr "Gereed." msgid "&Clear List" msgstr "&Lijst opschonen" msgid "Clear" msgstr "Wissen" msgid "Reset" msgstr "Herstellen" msgid "Press F2 or double click to edit cell contents." msgstr "Toets F2 of dubbelklik om de inhoud van de cel te bewerken." msgid "Action" msgstr "Actie" msgid "Shortcut" msgstr "Sneltoets" msgid "Server" msgstr "Server" msgid "Accuracy" msgstr "Nauwkeurigheid" msgid "Standard Tags" msgstr "Standaard tags" msgid "Additional Tags" msgstr "Additionele tags" msgid "Cover Art" msgstr "Hoesafbeelding" msgid "Invalid File" msgstr "Ongeldig bestand" msgid "Data received: %1" msgstr "Gegevens ontvangen: %1" msgid "Request sent..." msgstr "Verzoek verzonden..." msgid "Year" msgstr "Jaar" msgid "Length" msgstr "Lengte" msgid "Filenames" msgstr "Bestandsnamen" msgid "URLs" msgstr "URL-adressen" msgid "Directory name" msgstr "Mapnaam" msgid "Encode as URL" msgstr "Coderen als URL-adres" msgid "Confirm" msgstr "Bevestigen" msgid "Output" msgstr "Uitvoer" msgid "Command" msgstr "Commando" msgid "Create directory %1 failed\n" msgstr "Aanmaken map %1 is mislukt\n" msgid "File %1 already exists\n" msgstr "Bestand %1 bestaat reeds\n" msgid "%1 is not a directory\n" msgstr "%1 is geen map\n" msgid "Rename %1 to %2 failed\n" msgstr "Hernoemen %1 naar %2 mislukt \n" msgid "%1 already exists\n" msgstr "%1 bestaat reeds\n" msgid "%1 is not a file\n" msgstr "%1 is geen bestand\n" msgid "New directory name is too different\n" msgstr "De nieuwe mapnaam is te verschillend\n" msgid "Create directory" msgstr "Map aanmaken" msgid "Rename file" msgstr "Bestand hernoemen" msgid "C&lear" msgstr "&Wissen" msgid "Execute " msgstr "Uitvoeren " msgid "Could not execute " msgstr "Kon bewerking niet uitvoeren " msgid "True if strings are equal" msgstr "Waar als strings gelijk zijn" msgid "True if string contains substring" msgstr "Waar als string substring bevat" msgid "True if string matches regexp" msgstr "Waar als string overeenstemt met regexp" msgid "Logical AND" msgstr "Booleaanse EN" msgid "Logical OR" msgstr "Booleaanse OF" msgid "Logical negation" msgstr "Booleaanse NEE" msgid "Data" msgstr "Gegevens" msgid "%1 is not writable" msgstr "%1 is niet schrijfbaar" msgid "All Supported Files" msgstr "Alle ondersteunde bestanden" msgid "All Files" msgstr "Alle bestanden" msgid "Track" msgstr "Track" msgid "Absolute path to file" msgstr "Absoluut pad naar bestand" msgid "Duration" msgstr "Duur" msgid "Title" msgstr "Titel" msgid "Artist" msgstr "Artiest" msgid "Album" msgstr "Album" msgid "Comment" msgstr "Opmerkingen" msgid "Date" msgstr "Datum" msgid "Track Number" msgstr "Tracknummer" msgid "Genre" msgstr "Genre" msgid "Album Artist" msgstr "Albumartiest" msgid "Arranger" msgstr "Arrangeur" msgid "Author" msgstr "Auteur" msgid "BPM" msgstr "BPM" msgid "Catalog Number" msgstr "Catalogusnummer" msgid "Compilation" msgstr "Compilatie" msgid "Composer" msgstr "Componist" msgid "Conductor" msgstr "Dirigent" msgid "Copyright" msgstr "Auteursrecht" msgid "Disc Number" msgstr "Schijfnummer" msgid "Encoded-by" msgstr "Geëncodeerd door" msgid "Encoder Settings" msgstr "Instellingen voor codering" msgid "Encoding Time" msgstr "Tijd encodering" msgid "Grouping" msgstr "Groepering" msgid "Initial Key" msgstr "Initiële sleutel" msgid "ISRC" msgstr "ISRC" msgid "Language" msgstr "Taal" msgid "Lyricist" msgstr "Tekstschrijver" msgid "Lyrics" msgstr "Liedtekst" msgid "Media" msgstr "Medium" msgid "Mood" msgstr "Stemming" msgid "Original Album" msgstr "Originele Album" msgid "Original Artist" msgstr "Originele Artiest" msgid "Original Date" msgstr "Originele Datum" msgid "Part" msgstr "Deel" msgid "Performer" msgstr "Artiest/uitvoerende" msgid "Picture" msgstr "Afbeelding" msgid "Publisher" msgstr "Uitgever" msgid "Release Country" msgstr "Releaseland" msgid "Remixer" msgstr "Remixer" msgid "Sort Album" msgstr "Sorteervolgorde album" msgid "Sort Album Artist" msgstr "Sorteervolgorde albumartiest" msgid "Sort Artist" msgstr "Sorteervolgorde uitvoerende" msgid "Sort Composer" msgstr "Sorteervolgorde componist" msgid "Sort Name" msgstr "Sorteervolgorde titel" msgid "Subtitle" msgstr "Ondertitel" msgid "Website" msgstr "Website" msgid "WWW Audio File" msgstr "WWW van audio bestand" msgid "WWW Audio Source" msgstr "WWW van audio bron" msgid "Number of tracks" msgstr "Aantal tracks" msgid "Extension" msgstr "Extensie" msgid "Bitrate" msgstr "Bitrate" msgid "VBR" msgstr "VBR" msgid "Samplerate" msgstr "Samplerate" msgid "Channels" msgstr "Kanalen" msgid "Codec" msgstr "Codec" msgid "Escape for HTML" msgstr "Escape HTML" msgid "Automatic Import" msgstr "Automatisch importeren" msgid "D&estination:" msgstr "&Bestemming:" msgid "Tag 1 and Tag 2" msgstr "Tag 1 en Tag 2" msgid "&Profile:" msgstr "&Profiel:" msgid "&Save Settings" msgstr "&Instellingen opslaan" msgid "New" msgstr "Nieuwe" msgid "A&bort" msgstr "Af&breken" msgid "S&tart" msgstr "S&tarten" msgid "Import Source" msgstr "Importeer lokatie" msgid "&Server:" msgstr "&Server:" msgid "&Accuracy:" msgstr "N&auwkeurigheid:" msgid "&Standard Tags" msgstr "&Standaard tags" msgid "&Additional Tags" msgstr "&Additionele tags" msgid "C&over Art" msgstr "&Hoesafbeelding" msgid "Browse Cover Art" msgstr "Hoes ophalen" msgid "&Artist/Album" msgstr "A&rtiest / Album" msgid "&Source" msgstr "&Bron" msgid "Source:" msgstr "Bron:" msgid "URL:" msgstr "URL:" msgid "&URL extraction" msgstr "&URL vervanging" msgid "Match" msgstr "Komt overeen" msgid "Picture URL" msgstr "Afbeelding URL" msgid "&Browse" msgstr "&Ophalen" msgid "Click Browse to start" msgstr "Klik op de knop Browser om te starten" msgid "Then drag the picture from the browser to Kid3." msgstr "Sleep de afbeelding van de browser naar Kid3." msgid "ID3v1" msgstr "ID3v1" msgid "&Mark truncated fields" msgstr "&Markeer afgebroken velden" msgid "Text &encoding:" msgstr "Tekstco&dering:" msgid "ID3v2" msgstr "ID3v2" msgid "Use &track/total number of tracks format" msgstr "&Track/totaal aantal tracks structuur gebruiken" msgid "Track number &digits:" msgstr "T&racknummer aantal cijfers:" msgid "&Genre as text instead of numeric string" msgstr "Genre als te&kst in plaats van nummer" msgid "ISO-8859-1" msgstr "ISO-8859-1" msgid "UTF16" msgstr "UTF16" msgid "UTF8" msgstr "UTF8" msgid "&Version used for new tags:" msgstr "Gebruikte &versie voor nieuwe Tags:" msgid "ID3v2.3.0" msgstr "ID3v2.3.0" msgid "ID3v2.4.0" msgstr "ID3v2.4.0" msgid "Ogg/Vorbis" msgstr "Ogg/Vorbis" msgid "Co&mment field name:" msgstr "Naam &commentaarveld:" msgid "&Picture field name:" msgstr "Naam &afbeeldingsveld:" msgid "Custom &Genres" msgstr "&Aangepaste Genres" msgid "&Show only custom genres" msgstr "Toon a&lleen aangepaste genres" msgid "&Quick Access Tags" msgstr "S&nelle Toegang Tags" msgid "&Tag Format" msgstr "&Tagstructuur" msgid "Tag &1" msgstr "Tag &1" msgid "Tag &2" msgstr "Tag &2" msgid "Tag 1 a&nd Tag 2" msgstr "Tag 1 &en Tag 2" msgid "Startup" msgstr "Opstarten" msgid "&Load last-opened files" msgstr "&Laatstgeopende bestanden laden" msgid "Save" msgstr "Opslaan" msgid "&Preserve file timestamp" msgstr "Tij&dstempel van bestanden behouden" msgid "&Mark changes" msgstr "&Markeer wijzigingen" msgid "F&ilename for cover:" msgstr "Bestandsnaam voor de hoesafbeeld&ing:" msgid "&Filename Format" msgstr "B&estandsnaamstructuur" msgid "Browser" msgstr "Browser" msgid "Web &browser:" msgstr "&Internetbrowser commando:" msgid "Context &Menu Commands" msgstr "&Contextmenu commando's voor albumboom" msgid "&Play on double click" msgstr "Afspelen bij &dubbelklik" msgid "Proxy" msgstr "Proxy" msgid "&Proxy:" msgstr "&Proxy:" msgid "&Use authentication with proxy" msgstr "Gebruik &authenticatie voor proxy" msgid "Proxy user &name:" msgstr "P&roxygebruikersnaam:" msgid "Proxy pass&word:" msgstr "Proxy&wachtwoord:" msgid "&Metadata Plugins && Priority" msgstr "&Metadata plugins && Prioriteit" msgid "A&vailable Plugins" msgstr "&Beschikbare plugins" msgid "Changes take only effect after a restart!" msgstr "Wijzigingen worden pas doorgevoerd na een herstart!" msgid "From Clip&board" msgstr "Van Klem&bord" msgid "&Import" msgstr "&Importeren" msgid "&Export" msgstr "&Exporteren" msgid "&View" msgstr "&Beeld" msgid "Text Encoding" msgstr "Tekst-Codering" msgid "Text" msgstr "Tekst" msgid "Description" msgstr "Beschrijving" msgid "Owner" msgstr "Eigenaar" msgid "Email" msgstr "E-mail" msgid "Rating" msgstr "Waardering" msgid "Picture Type" msgstr "Afbeeldingstype" msgid "Image format" msgstr "Afbeeldingsformaat" msgid "Mimetype" msgstr "Mime bestandstype" msgid "Counter" msgstr "Teller" msgid "Identifier" msgstr "Identifier" msgid "Volume Adjustment" msgstr "Volume aanpassing" msgid "Number of Bits" msgstr "Aantal Bits" msgid "Volume Change Right" msgstr "Volume Wijzigen Rechts" msgid "Volume Change Left" msgstr "Volume Wijzigen Links" msgid "Peak Volume Right" msgstr "Piekvolume Rechts" msgid "Peak Volume Left" msgstr "Piekvolume Links" msgid "Timestamp Format" msgstr "Opmaak Tijdsaanduiding" msgid "Content Type" msgstr "Soort inhoud" msgid "Price" msgstr "" msgid "Seller" msgstr "" msgid "Images" msgstr "Afbeeldingen" msgid "UTF16BE" msgstr "UTF16BE" msgid "Other" msgstr "Ander" msgid "32x32 pixels PNG file icon" msgstr "32x32 pixels PNG bestandsicoon" msgid "Other file icon" msgstr "Ander bestandsicoon" msgid "Cover (front)" msgstr "Hoes voor" msgid "Cover (back)" msgstr "Hoes achter" msgid "Leaflet page" msgstr "Booklet pagina" msgid "Lead artist/lead performer/soloist" msgstr "Hoofd artiest/hoofd uitvoerende" msgid "Artist/performer" msgstr "Artiest/uitvoerende" msgid "Band/Orchestra" msgstr "Band/Orchest" msgid "Lyricist/text writer" msgstr "Tekstschrijver" msgid "Recording Location" msgstr "Opnamelocatie" msgid "During recording" msgstr "Tijdens opname" msgid "During performance" msgstr "Tijdens uitvoering" msgid "Movie/video screen capture" msgstr "Film/schermafdruk" msgid "A bright coloured fish" msgstr "A bright coloured fish" msgid "Illustration" msgstr "Illustratie" msgid "Band/artist logotype" msgstr "Artiest logotype" msgid "Publisher/Studio logotype" msgstr "Uitgever logotype" msgid "MPEG frames as unit" msgstr "MPEG frame als eenheid" msgid "Milliseconds as unit" msgstr "Milliseconden als eenheid" msgid "Text transcription" msgstr "Tekst" msgid "Movement/part name" msgstr "Deel" msgid "Events" msgstr "Gebeurtenissen" msgid "Chord" msgstr "Koorde" msgid "Trivia/pop up" msgstr "Trivia/pop up" msgid "Export" msgstr "Exporteren" msgid "Format:" msgstr "Structuur:" msgid "Header:" msgstr "Koptekst:" msgid "Tracks:" msgstr "Tracks:" msgid "Footer:" msgstr "Voettekst:" msgid "To F&ile" msgstr "Naar &Bestand" msgid "To Clip&board" msgstr "Naar &Klembord" msgid "&Source:" msgstr "T&agbron:" msgid "File Error" msgstr "Bestandsfout" msgid "&Filter:" msgstr "&Filter:" msgid "&Expression:" msgstr "&Uitdrukking:" msgid "&Apply" msgstr "Toe&passen" msgid "Accuracy:" msgstr "Nauwkeurigheid:" msgid "Cover Art:" msgstr "Hoesafbeelding:" msgid "From F&ile/Clipboard..." msgstr "Van bestand/&klembord..." msgid "From T&ags..." msgstr "Van &tags..." msgid "&From Server:" msgstr "Van &server:" msgid "Check maximum allowable time &difference (sec):" msgstr "&Maximum tijdverschil (sec):" msgid "Match with:" msgstr "Overeenkomt met:" msgid "&Length" msgstr "&Lengte" msgid "T&rack" msgstr "&Nummer" msgid "&Title" msgstr "&Titel" msgid "&Start number:" msgstr "&Start nummer:" msgid "&Destination:" msgstr "&Bestemming:" msgid "&Total number of tracks:" msgstr "&Totaal aantal tracks:" msgid "Create Playlist" msgstr "Multimedia afspeellijst maken" msgid "Playlist File Name" msgstr "Bestandsnaam van multimedia afspeellijst" msgid "Same as &directory name" msgstr "Bestandsnaam volgens &mapnaam" msgid "&Format:" msgstr "Bestandsnaam volgens &tructuur:" msgid "Cr&eate in:" msgstr "&Bestand aanmaken in:" msgid "Current directory" msgstr "Huidige map" msgid "Every directory" msgstr "Elke map" msgid "Top-level directory" msgstr "Hoogste nivo map" msgid "Playlist Content" msgstr "Inhoud van multimedia afspeellijst" msgid "For&mat:" msgstr "Multimedia &bestandsformaat:" msgid "Incl&ude only the selected files" msgstr "Alleen &geselecteerde bestanden gebruiken" msgid "Sort by file &name" msgstr "Sorteren op bestands&naam" msgid "Sort by &tag field" msgstr "Sorteren op &tagframe" msgid "Use &relative path for files in playlist" msgstr "Gebruik &relatief pad voor bestanden in lijst" msgid "Use full p&ath for files in playlist" msgstr "Gebruik &volledig pad voor bestanden in lijst" msgid "Write only &list of files" msgstr "Bewaar enkel &lijst met bestanden" msgid "Write &info using" msgstr "Bewaar &info gebruik makend van" msgid "Preview" msgstr "Voorbeeld" msgid "Create Directory" msgstr "Map aanmaken" msgid "From Tag 2 and Tag 1" msgstr "Van Tag 2 en Tag 1" msgid "From:" msgstr "Van:" msgid "To:" msgstr "Aan:" msgid "&Find" msgstr "&Zoeken" msgid "C&GI Path:" msgstr "C&GI Locatie:" msgid "Track Title/Artist - Album" msgstr "Nummer Titel / Artiest - Album" msgid "State" msgstr "Status" msgid "No result" msgstr "Geen resultaat" msgid "No result selected" msgstr "Geen resultaat geselecteerd" msgid "Import from Tags" msgstr "Importeer van Tags" msgid "Extraction:" msgstr "Extractie:" msgid "Import from File/Clipboard" msgstr "Importeer van bestand/klembord" msgid "From F&ile" msgstr "Van &bestand" msgid "Download" msgstr "Download" msgid "Opening directory..." msgstr "Map openen..." msgid "Saving directory..." msgstr "Map opslaan..." msgid "" "The current directory has been modified.\n" "Do you want to save it?" msgstr "" "De huidige map is gewijzigd.\n" "Wenst u de wijzigingen op te slaan?" msgid "Warning" msgstr "Waarschuwing" msgid "Exiting..." msgstr "Bezig met afsluiten..." msgid "Creating playlist..." msgstr "Speellijst Aanmaken..." msgid "Import" msgstr "Importeren" msgid "Error while renaming:\n" msgstr "Fout tijdens hernoemen:\n" msgid " [filtered]" msgstr " [gefiltert]" msgid "Add Frame" msgstr "Frame toevoegen" msgid "Select the frame ID" msgstr "Selecteer de frame identiteit" msgid "Rename File" msgstr "Bestand hernoemen" msgid "Enter new file name:" msgstr "Geef nieuwe bestand naam op:" msgid "Do you really want to move these %1 items to the trash?" msgstr "" "Bent u er zeker van dat u %1 items naar de prullenbak wilt verplaatsen?" msgid "Do you really want to move this item to the trash?" msgstr "" "Bent u er zeker van dat u dit item naar de prullenbak wilt verplaatsen?" msgid "Move to Trash" msgstr "Naar prullenbak verplaatsen" msgid "Directory must be empty.\n" msgstr "De map moet leeg zijn.\n" msgid "Could not move these files to the Trash" msgstr "Deze bestanden konden niet naar de prullenbak worden verplaatst" msgid "Expand All" msgstr "Mappen uitvouwen" msgid "&Expand all" msgstr "Mappen &uitvouwen" msgid "&Collapse all" msgstr "Mappen &invouwen" msgid "&Rename" msgstr "&Hernoemen" msgid "&Move to Trash" msgstr "&Verwijder map/bestand" msgid "Kid3" msgstr "Kid3" msgid "F&ile" msgstr "B&estand" msgid "Name:" msgstr "Naam:" msgid "Filename from Tag 1" msgstr "Bestandsnaam van Tag 1" msgid "Filename from Tag 2" msgstr "Bestandsnaam van Tag 2" msgid "Tag 1 from Filename" msgstr "Tag 1 van bestandsnaam" msgid "Tag 2 from Filename" msgstr "Tag 2 van bestandsnaam" msgid "&Add..." msgstr "T&oevoegen..." msgid "Move &Up" msgstr "Om&hoog" msgid "Move &Down" msgstr "Om&laag" msgid "&Edit..." msgstr "Be&werken..." msgid "&Remove" msgstr "&Verwijderen" msgid "&Insert row" msgstr "Rij &Invoegen" msgid "&Delete row" msgstr "Rij &Verwijderen" msgid "&Clear row" msgstr "Rij &Wissen" msgid "Format while editing" msgstr "Opmaak gebruiken tijdens het opslaan van bestanden" msgid "Case conversion:" msgstr "Hoofd-/kleine letter:" msgid "No changes" msgstr "Geen wijzigingen" msgid "All lowercase" msgstr "Alles in kleine letters" msgid "All uppercase" msgstr "Alles in hoofdletters" msgid "First letter uppercase" msgstr "Eerste teken met hoofdletter" msgid "All first letters uppercase" msgstr "Alle eerste tekens met hoofdletter" msgid "Locale:" msgstr "Taal:" msgid "None" msgstr "Geen" msgid "String replacement:" msgstr "Tekenreeks vervangen:" msgid "From" msgstr "Van" msgid "To" msgstr "Naar" msgid "&Add" msgstr "&Toevoegen" msgid "&Select all" msgstr "&Alles selecteren" msgid "&Deselect all" msgstr "Alles &deselecteren" msgid "View Picture" msgstr "Afbeelding bekijken" msgid "" "Drag album\n" "artwork\n" "here" msgstr "" "Sleep\n" "albumhoes\n" "hier" msgid "Play/Pause" msgstr "Afspelen / Pauze" msgid "Stop playback" msgstr "Afspelen stoppen" msgid "Previous Track" msgstr "Vorige track" msgid "Next Track" msgstr "Volgende track" msgid "Close" msgstr "Sluiten" msgid "Mute" msgstr "Dempen" msgid "Volume: %1%" msgstr "Volume: %1%" msgid "Add Item" msgstr "Item toevoegen" msgid "Edit Item" msgstr "Item bewerken" msgid "MusicBrainz Fingerprint" msgstr "MusicBrainz Fingerprint" msgid "Unrecognized" msgstr "Niet herkend" msgid "Recognized" msgstr "Herkend" msgid "User Selection" msgstr "Gebruikerselectie" msgid "ID Lookup" msgstr "ID-zoekactie" msgid "Fingerprint" msgstr "Vingerafdruk" msgid "Metadata Lookup" msgstr "Metadata zoekactie" msgid "Amazon" msgstr "Amazon" msgid "Discogs" msgstr "Discogs" msgid "gnudb.org" msgstr "gnudb.org" msgid "TrackType.org" msgstr "TrackType.org" msgid "AENC - Audio encryption" msgstr "AENC - Audio versleuteling" msgid "APIC - Attached picture" msgstr "APIC - Bijgevoegde afbeelding" msgid "COMM - Comments" msgstr "COMM - Opmerkingen" msgid "COMR - Commercial" msgstr "COMR - Commercieel" msgid "ENCR - Encryption method registration" msgstr "ENCR - Registratie van encryptie methode" msgid "EQUA - Equalization" msgstr "EQUA - Egalisatie" msgid "ETCO - Event timing codes" msgstr "ETCO - Gebeurtenis timing codes" msgid "GEOB - General encapsulated object" msgstr "GEOB - Algemeen ingekapselde object" msgid "GRID - Group identification registration" msgstr "GRID - Registratie van groep identificatie" msgid "IPLS - Involved people list" msgstr "IPLS - Betrokken personenlijst" msgid "LINK - Linked information" msgstr "LINK - Gelinkte informatie" msgid "MCDI - Music CD identifier" msgstr "MCDI - Muziek CD identificatie" msgid "MLLT - MPEG location lookup table" msgstr "MLLT - Opzoektabel van MPEG locatie" msgid "OWNE - Ownership frame" msgstr "OWNE - Eigendom frame" msgid "PRIV - Private frame" msgstr "PRIV - Privé frame" msgid "PCNT - Play counter" msgstr "PCNT - Afspeelteller" msgid "POPM - Popularimeter" msgstr "POPM - Popularimeter" msgid "POSS - Position synchronisation frame" msgstr "POSS - Positie synchronisatie frame" msgid "RBUF - Recommended buffer size" msgstr "RBUF - Aanbevolen buffer grootte" msgid "RVAD - Relative volume adjustment" msgstr "RVAD - Relatief volume aanpassing" msgid "RVRB - Reverb" msgstr "RVRB - Galm" msgid "SYLT - Synchronized lyric/text" msgstr "SYLT - Gesynchroniseerde liedtekst" msgid "SYTC - Synchronized tempo codes" msgstr "SYTC - Gesynchroniseerde tempo codes" msgid "TALB - Album/Movie/Show title" msgstr "TALB - Album/film/show titel" msgid "TBPM - BPM (beats per minute)" msgstr "TBPM - BPM (slagen per minuut)" msgid "TCOM - Composer" msgstr "TCOM - Componist" msgid "TCON - Content type" msgstr "TCON - Soort inhoud" msgid "TCOP - Copyright message" msgstr "TCOP - Auteursrecht boodschap" msgid "TDAT - Date" msgstr "TDAT - Datum" msgid "TDLY - Playlist delay" msgstr "TDLY - Speellijst vertraging" msgid "TENC - Encoded by" msgstr "TENC - Geëncodeerd door" msgid "TEXT - Lyricist/Text writer" msgstr "TEXT - Tekstschrijver" msgid "TFLT - File type" msgstr "TFLT - Soort bestand" msgid "TIME - Time" msgstr "TIME - Tijd" msgid "TIT1 - Content group description" msgstr "TIT1 - Inhoud van groep omschrijving" msgid "TIT2 - Title/songname/content description" msgstr "TIT2 - Titel/inhoudsbeschrijving" msgid "TIT3 - Subtitle/Description refinement" msgstr "TIT3 - Verfijning van ondertitel/omschrijving" msgid "TKEY - Initial key" msgstr "TKEY - Initiële sleutel" msgid "TLAN - Language(s)" msgstr "TLAN - Talen" msgid "TLEN - Length" msgstr "TLEN - Lengte" msgid "TMED - Media type" msgstr "TMED - Soort media" msgid "TOAL - Original album/movie/show title" msgstr "TOAL - Titel van originele album/film/show" msgid "TOFN - Original filename" msgstr "TOFN - Originele bestandsnaam" msgid "TOLY - Original lyricist(s)/text writer(s)" msgstr "TOLY - Originele tekstschrijver(s)" msgid "TOPE - Original artist(s)/performer(s)" msgstr "TOPE - Originele artiest(s)/uitvoerende" msgid "TORY - Original release year" msgstr "TORY - Originele vrijgavejaar" msgid "TOWN - File owner/licensee" msgstr "TOWN - Eigenaar/licentiehouder van bestand" msgid "TPE1 - Lead performer(s)/Soloist(s)" msgstr "TPE1 - Hoofd uitvoerende" msgid "TPE2 - Band/orchestra/accompaniment" msgstr "TPE2 - Begeleidende band/orkest" msgid "TPE3 - Conductor/performer refinement" msgstr "TPE3 - Dirigent/uitvoerende verfijning" msgid "TPE4 - Interpreted, remixed, or otherwise modified by" msgstr "TPE4 - Geïnterpreteerd, remixen, of op andere wijze gewijzigd" msgid "TPOS - Part of a set" msgstr "TPOS - Deel van een set" msgid "TPUB - Publisher" msgstr "TPUB - Uitgever" msgid "TRCK - Track number/Position in set" msgstr "TRCK - Tracknummer/positie in een set" msgid "TRDA - Recording dates" msgstr "TRDA - Opnamedata" msgid "TRSN - Internet radio station name" msgstr "TRSN - Naam van internet radiostation" msgid "TRSO - Internet radio station owner" msgstr "TRSO - Eigenaar van internet radiostation" msgid "TSIZ - Size" msgstr "TSIZ - Grootte" msgid "TSRC - ISRC (international standard recording code)" msgstr "TSRC - ISRC (internationale standaard opnamecode)" msgid "TSSE - Software/Hardware and settings used for encoding" msgstr "TSSE - Instellingen die worden gebruikt voor het coderen" msgid "TXXX - User defined text information" msgstr "TXXX - Gedefinieerde gebruikersinformatie" msgid "TYER - Year" msgstr "TYER - Jaar" msgid "UFID - Unique file identifier" msgstr "UFID - Unieke bestand herkenner" msgid "USER - Terms of use" msgstr "USER - Voorwaarden voor het gebruik" msgid "USLT - Unsynchronized lyric/text transcription" msgstr "USLT - ongesynchroniseerde liedtekst" msgid "WCOM - Commercial information" msgstr "WCOM - Commerciële informatie" msgid "WCOP - Copyright/Legal information" msgstr "WCOP - Auteursrecht informatie" msgid "WOAF - Official audio file webpage" msgstr "WOAF - Officiële website van audio bestand" msgid "WOAR - Official artist/performer webpage" msgstr "WOAR - Officiële website van artiest/uitvoerende" msgid "WOAS - Official audio source webpage" msgstr "WOAS - Officiële website van audio bron" msgid "WORS - Official internet radio station homepage" msgstr "WORS - Officiële website van internet radiostation" msgid "WPAY - Payment" msgstr "WPAY - Betaling" msgid "WPUB - Official publisher webpage" msgstr "WPUB - Officiële website van uitgever" msgid "WXXX - User defined URL link" msgstr "WXXX - Gedefinieerde link van gebruiker" msgid "MusicBrainz Release" msgstr "MusicBrainz vrijgave" msgid "ASPI - Audio seek point index" msgstr "ASPI - Audio zoeken punt-index" msgid "EQU2 - Equalisation (2)" msgstr "EQU2 - Egalisatie (2)" msgid "RVA2 - Relative volume adjustment (2)" msgstr "RVAD - Aanpassing van relatief volume (2)" msgid "SEEK - Seek frame" msgstr "SEEK - zoek frame" msgid "SIGN - Signature frame" msgstr "SIGN - Handtekening frame" msgid "TCMP - iTunes compilation flag" msgstr "TCMP - iTunes compilatie flag" msgid "TDEN - Encoding time" msgstr "TDEN - Tijd encodering" msgid "TDOR - Original release time" msgstr "TDOR - Originele vrijgavetijd" msgid "TDRC - Recording time" msgstr "TDRC - Opnametijd" msgid "TDRL - Release time" msgstr "TDRL - Vrijgavetijd" msgid "TDTG - Tagging time" msgstr "TDTG - Tag tijd" msgid "TIPL - Involved people list" msgstr "TIPL - Personenlijst van betrokkenen" msgid "TMCL - Musician credits list" msgstr "TMCL - Credits van muzikant" msgid "TMOO - Mood" msgstr "TMOO - Stemming" msgid "TPRO - Produced notice" msgstr "TPRO - Bericht producent" msgid "TSO2 - Album artist sort order" msgstr "TSO2 - Sorteervolgorde albumartiest" msgid "TSOA - Album sort order" msgstr "TSOA - Sorteervolgorde album" msgid "TSOC - Composer sort order" msgstr "TSOC - Sorteervolgorde componist" msgid "TSOP - Performer sort order" msgstr "TSOP - Sorteervolgorde uitvoerende" msgid "TSOT - Title sort order" msgstr "TSOT - Sorteervolgorde titel" msgid "TSST - Set subtitle" msgstr "TSST - ingestelde ondertitel" �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/po/pl.po���������������������������������������������������������������������������������0000664�0000000�0000000�00000104430�12246035113�0013736�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# translation of pl.po to English # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # Tomasz ArgasiÅ„ski <targasinski@o2.pl>, 2007. # MichaÅ‚ Smoczyk <msmoczyk@wp.pl>, 2007. # Urs Fleisch <ufleisch@users.sourceforge.net>, 2008, 2009, 2010, 2011. msgid "" msgstr "" "Project-Id-Version: Kid3 3.0.2\n" "Report-Msgid-Bugs-To: http://sourceforge.net/p/kid3/bugs/\n" "POT-Creation-Date: 2013-09-11 21:22+0200\n" "PO-Revision-Date: 2011-01-15 17:37+0100\n" "Last-Translator: \n" "Language-Team: Polish <kde-i18n-doc@kde.org>\n" "Language: pl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" "X-Generator: Lokalize 1.1\n" "X-Language: pl\n" "X-Source-Language: en\n" msgid "Timeout" msgstr "Limit czasu" msgid "Usage:" msgstr "Użycie:" msgid "Help" msgstr "Pomoc" msgid "Command name" msgstr "Nazwa polecenia" msgid "Quit application" msgstr "KoÅ„czy program" msgid "The current directory has been modified." msgstr "Bieżący katalog zostaÅ‚ zmodyfikowany." msgid "Type 'exit force' to quit." msgstr "Typ 'exit force' aby zakoÅ„czyć." msgid "Change directory" msgstr "ZmieÅ„ katalog" msgid "%1 does not exist" msgstr "%1 nie istnieje" msgid "Print the filename of the current working directory" msgstr "Wypisanie peÅ‚nej nazwy bieżącego katalogu" msgid "Directory list" msgstr "Lista katalogów" msgid "Saves the changed files" msgstr "Zapisuje zmienione pliki" msgid "Error while writing file:\n" msgstr "Błąd podczas zapisu pliku:\n" msgid "Select file" msgstr "Wybierz plik" msgid "%1 not found" msgstr "%1 nie odnaleziony" msgid "Select tag" msgstr "Wybór znacznika" msgid "Get tag frame" msgstr "" msgid "Set tag frame" msgstr "" msgid "Could not set \"%1\" for %2" msgstr "Nie można ustawić \"%1\" dla %2" msgid "Revert" msgstr "Odwróć" msgid "Import from file or clipboard" msgstr "Importuj plik ze schowka" msgid "Format name" msgstr "Nazwa formatu" msgid "%1 not found." msgstr "Nie znaleziono %1." msgid "Available" msgstr "DostÄ™pne" msgid "Error" msgstr "Błąd" msgid "Automatic import" msgstr "Automatycznie importuj" msgid "Profile name" msgstr "Nazwa profilu" msgid "Reading Directory" msgstr "Czytanie katalogu" msgid "Started" msgstr "Uruchomiono" msgid "Source" msgstr "ŹródÅ‚o" msgid "Querying" msgstr "Odpytywanie" msgid "Fetching" msgstr "Pobieranie" msgid "Data received" msgstr "Otrzymano dane" msgid "Cover" msgstr "OkÅ‚adka" msgid "Finished" msgstr "ZakoÅ„czono" msgid "Aborted" msgstr "Przerwane" msgid "Download album cover artwork" msgstr "Pobierz okÅ‚adkÄ™" msgid "Export to file or clipboard" msgstr "Eksportuj do pliku lub schowka" msgid "Create playlist" msgstr "Stwórz listÄ™ odtwarzania" msgid "Apply filename format" msgstr "Zastosuj formatowanie nazwy pliku" msgid "Apply tag format" msgstr "Zastosuj formatowanie znacznika" msgid "Apply text encoding" msgstr "Zastosuj kodowanie tekstu" msgid "Rename directory" msgstr "ZmieÅ„ nazwÄ™ katalogu" msgid "Number tracks" msgstr "Numeruj Å›cieżki" msgid "Track number" msgstr "Numer Å›cieżki" msgid "Filter" msgstr "Filtr" msgid "Filter name" msgstr "Nazwa filtru" msgid "Convert ID3v2.3 to ID3v2.4" msgstr "Konwertuj ID3v2.3 do ID3v2.4" msgid "Convert ID3v2.4 to ID3v2.3" msgstr "Konwertuj ID3v2.4 do ID3v2.3" msgid "Filename from tag" msgstr "Nazwa pliku ze znacznika" msgid "Tag from filename" msgstr "Znacznik wczytaj z nazwy pliku" msgid "Tag to other tag" msgstr "" msgid "Copy" msgstr "Kopiuj" msgid "Paste" msgstr "Wklej" msgid "Remove" msgstr "UsuÅ„" msgid "Play" msgstr "Odtwarzaj" msgid "Parameter" msgstr "Parametr" msgid "File path" msgstr "Åšcieżka pliku" msgid "URL" msgstr "URL" msgid "Tag numbers" msgstr "Numer Å›cieżki" msgid "Frame name" msgstr "Nazwa ramki" msgid "Frame value" msgstr "Wartość ramki" msgid "Format" msgstr "Format" msgid "Command specific" msgstr "Polecenie" msgid "Available Commands" msgstr "DostÄ™pne polecenia" msgid "File" msgstr "Plik" msgid "Name" msgstr "Nazwa" msgid "Tag 2" msgstr "Znacznik 2" msgid "Tag 1" msgstr "Znacznik 1" msgid "Tags" msgstr "Znaczniki" msgid "Unknown command '%1'. Type 'help' for help." msgstr "Nieznane polecenie '%1'. Wpisz 'help' dla pomocy." msgid "Unknown command '%1', -h for help." msgstr "Nieznane polecenie '%1', -h podaje pomoc." msgid "Files" msgstr "Pliki" msgid "User Actions" msgstr "DziaÅ‚ania użytkownika" msgid "Network" msgstr "Sieć" msgid "Plugins" msgstr "Wtyczki" msgid "Main Toolbar" msgstr "Główny pasek narzÄ™dzi" msgid "Opens a directory" msgstr "Otwiera katalog" msgid "Opens a recently used directory" msgstr "Otwiera poprzednio używany katalog" msgid "Reverts the changes of all or the selected files" msgstr "Przywraca do poprzedniego stanu zmiany we wszystkich wybranych plikach" msgid "Quits the application" msgstr "KoÅ„czy program" msgid "Select all files" msgstr "Wybierz wszystkie pliki" msgid "Deselect all files" msgstr "Odznacz wszystkie pliki" msgid "Configure Shortcuts" msgstr "Konfiguracja skrótów" msgid "Configure Toolbars" msgstr "Konfiguracja pasków narzÄ™dzi" msgid "Preferences dialog" msgstr "Okno opcji" msgid "O&pen Directory..." msgstr "&Otwórz katalog..." msgid "&Import..." msgstr "&Importuj..." msgid "Import from %1..." msgstr "Importuj z %1..." msgid "Automatic I&mport..." msgstr "Automatycznie i&mportuj..." msgid "&Browse Cover Art..." msgstr "&Pobierz okÅ‚adkÄ™..." msgid "&Export..." msgstr "&Eksportuj..." msgid "&Create Playlist..." msgstr "Stwórz &listÄ™ odtwarzania..." msgid "Apply &Filename Format" msgstr "Zastosuj formatowanie &nazwy pliku" msgid "Apply &Tag Format" msgstr "Zastosuj formatowanie &znacznika" msgid "Apply Text &Encoding" msgstr "Zastosuj kodowanie t&ekstu" msgid "&Rename Directory..." msgstr "ZmieÅ„ &nazwÄ™ katalogu..." msgid "&Number Tracks..." msgstr "&Numeruj Å›cieżki..." msgid "F&ilter..." msgstr "F&iltr..." msgid "Convert ID3v2.3 to ID3v2.&4" msgstr "Konwertuj ID3v2.3 do ID3v2.&4" msgid "Convert ID3v2.4 to ID3v2.&3" msgstr "Konwertuj ID3v2.4 do ID3v2.&3" msgid "&Play" msgstr "&Odtwarzaj" msgid "Show &Picture" msgstr "Pokaż &obrazek" msgid "Auto &Hide Tags" msgstr "Automatyczne &ukrywanie" msgid "Select All in &Directory" msgstr "Zaznacz wszystkie pliki w &katalogu" msgid "&Previous File" msgstr "&Poprzedni plik" msgid "&Next File" msgstr "&NastÄ™pny plik" msgid "From Filename" msgstr "Wczytaj z nazwy pliku" msgid "From Tag 2" msgstr "Ze znacznika 2" msgid "From Tag 1" msgstr "Ze znacznika 1" msgid "Frames:" msgstr "Ramki:" msgid "Edit" msgstr "Edycja" msgid "Add" msgstr "Dodaj" msgid "Delete" msgstr "UsuÅ„" msgid "Filename" msgstr "Nazwa pliku" msgid "Focus" msgstr "Ognisko" msgid "File List" msgstr "Lista plików" msgid "Directory List" msgstr "Lista katalogów" msgid "Configure - Kid3" msgstr "Konfiguracja - Kid3" msgid "Open" msgstr "Otwórz" msgid "Save As" msgstr "Zapisz jako" msgid "directory to open" msgstr "katalog do otwarcia" msgid "Kid3 ID3 Tagger" msgstr "Kid3 - Edytor znaczników ID3" #. i18n NAME OF TRANSLATORS msgid "Your names" msgstr "Tomasz ArgasiÅ„ski, MichaÅ‚ Smoczyk" #. i18n EMAIL OF TRANSLATORS msgid "Your emails" msgstr "targasinski@o2.pl, msmoczyk@wp.pl" msgid "&Back" msgstr "&Wstecz" msgid "&Forward" msgstr "&Do przodu" msgid "&Find:" msgstr "&Znajdź:" msgid "Find Previous" msgstr "Znajdź poprzednie" msgid "Find Next" msgstr "Znajdź nastÄ™pne" msgid "&Close" msgstr "Z&amknij" msgid "&Tags" msgstr "&Znaczniki" msgid "&Files" msgstr "&Pliki" msgid "&User Actions" msgstr "&DziaÅ‚ania użytkownika" msgid "&Network" msgstr "&Sieć" msgid "&Plugins" msgstr "&Wtyczki" msgid "&Keyboard Shortcuts" msgstr "Skróty &klawiszowe" msgid "Use custom app&lication font" msgstr "Użyj wÅ‚asnej &czcionki" msgid "A&pplication Font..." msgstr "Czcionka &programu..." msgid "Use custom application &style" msgstr "Używaj wÅ‚asnego programu i &stylu" msgid "Unknown" msgstr "Nieznany" msgid "Use native system file &dialogs" msgstr "&Użyj natywnych okien systemu" msgid "&Appearance" msgstr "&WyglÄ…d" msgid "&Help" msgstr "Pomo&c" msgid "&OK" msgstr "&OK" msgid "&Cancel" msgstr "&Anuluj" msgid "The keyboard shortcut '%1' is already assigned to '%2'." msgstr "Skrót klawiaturowy '%1' jest już przypisany do '%2'." msgid "&File" msgstr "&Plik" msgid "&Open..." msgstr "&Otwórz..." msgid "Open &Recent" msgstr "Otwórz poprz&edni" msgid "&Save" msgstr "&Zapisz" msgid "Re&vert" msgstr "Od&wróć" msgid "Import from %1" msgstr "Importuj z %1" msgid "Browse album cover artwork" msgstr "Pobierz okÅ‚adkÄ™" msgid "Create M3U Playlist" msgstr "Utwórz listÄ™ odtwarzania M3U" msgid "&Quit" msgstr "Za&koÅ„cz" msgid "&Edit" msgstr "&Edycja" msgid "Select &All" msgstr "Zaznacz &wszystko" msgid "Dese&lect" msgstr "O&dznacz" msgid "Select all files in the current directory" msgstr "Zaznacz wszystkie pliki w bieżącym katalogu" msgid "Select previous file" msgstr "Zaznacz poprzedni plik" msgid "Select next file" msgstr "Zaznacz nastÄ™pny plik" msgid "&Tools" msgstr "&NarzÄ™dzia" msgid "Apply Filename Format" msgstr "Zastosuj formatowanie nazwy pliku" msgid "Apply Tag Format" msgstr "Zastosuj formatowanie znacznika" msgid "Apply Text Encoding" msgstr "Zastosuj kodowanie tekstu" msgid "Rename Directory" msgstr "ZmieÅ„ nazwÄ™ katalogu" msgid "Number Tracks" msgstr "Numeruj Å›cieżki" msgid "&Settings" msgstr "&Ustawienia" msgid "Enables/disables the toolbar" msgstr "Włącza/wyłącza pasek narzÄ™dzi" msgid "Show &Toolbar" msgstr "WyÅ›wietlaj pasek &narzÄ™dzi" msgid "Enables/disables the statusbar" msgstr "Włącza/wyłącza pasek stanu" msgid "Show St&atusbar" msgstr "WyÅ›wietlaj pasek s&tanu" msgid "Show Picture" msgstr "Pokaż obrazek" msgid "Auto Hide Tags" msgstr "Automatyczne ukrywanie" msgid "Configure Kid3" msgstr "Konfiguracja Kid3" msgid "&Configure Kid3..." msgstr "&Konfiguracja Kid3..." msgid "Kid3 Handbook" msgstr "PodrÄ™cznik programu Kid3" msgid "Kid3 &Handbook" msgstr "&PodrÄ™cznik programu Kid3" msgid "About Kid3" msgstr "O programie Kid3" msgid "&About Kid3" msgstr "&O programie Kid3" msgid "About Qt" msgstr "Informacje o Qt" msgid "About &Qt" msgstr "Informacje o &Qt" msgid " [modified]" msgstr " [zmodyfikowane]" msgid "Toggle the statusbar..." msgstr "Przełączanie paska statusu..." msgid "Ready." msgstr "Gotowe." msgid "&Clear List" msgstr "Wy&czyść listÄ™" msgid "Clear" msgstr "Wyczyść" msgid "Reset" msgstr "Wróć" msgid "Press F2 or double click to edit cell contents." msgstr "NaciÅ›nij F2 lub kliknij podwójnie, aby edytować zawartość komórek." msgid "Action" msgstr "Zdarzenie" msgid "Shortcut" msgstr "Skrót" msgid "Server" msgstr "Serwer" msgid "Accuracy" msgstr "DokÅ‚adność" msgid "Standard Tags" msgstr "Standardowe znaczniki" msgid "Additional Tags" msgstr "Dodatkowe znaczniki" msgid "Cover Art" msgstr "OkÅ‚adek" msgid "Invalid File" msgstr "NiewÅ‚aÅ›ciwy plik" msgid "Data received: %1" msgstr "Otrzymano dane: %1" msgid "Request sent..." msgstr "WysÅ‚ano zapytanie..." msgid "Year" msgstr "Rok" msgid "Length" msgstr "DÅ‚ugość" msgid "Filenames" msgstr "Nazwy plików" msgid "URLs" msgstr "URL" msgid "Directory name" msgstr "Nazwa katalogu" msgid "Encode as URL" msgstr "WyÅ›wietlaj pasek stanu" msgid "Confirm" msgstr "Potwierdź" msgid "Output" msgstr "WyjÅ›cie" msgid "Command" msgstr "Polecenie" msgid "Create directory %1 failed\n" msgstr "Nie można utworzyć katalogu %1\n" msgid "File %1 already exists\n" msgstr "Plik %1 już istnieje\n" msgid "%1 is not a directory\n" msgstr "%1 nie jest katalogiem\n" msgid "Rename %1 to %2 failed\n" msgstr "Nie udaÅ‚o siÄ™ zmienić nazwy %1 na %2\n" msgid "%1 already exists\n" msgstr "%1 już istnieje\n" msgid "%1 is not a file\n" msgstr "%1 nie jest plikiem\n" msgid "New directory name is too different\n" msgstr "Nowe nazwa katalogu jest też różne\n" msgid "Create directory" msgstr "Utwórz katalog" msgid "Rename file" msgstr "ZmieÅ„ nazwÄ™ pliku" msgid "C&lear" msgstr "&Wyczyść" msgid "Execute " msgstr "Wykonaj" msgid "Could not execute " msgstr "Nie można wykonać" msgid "True if strings are equal" msgstr "Å‚aÅ„cuchy sÄ… równe" msgid "True if string contains substring" msgstr "Dopasowanie częściowe" msgid "True if string matches regexp" msgstr "Dopasowanie WYR_REG" msgid "Logical AND" msgstr "Logiczna AND" msgid "Logical OR" msgstr "Logiczna OR" msgid "Logical negation" msgstr "Logiczna NOT" msgid "Data" msgstr "Dane" msgid "%1 is not writable" msgstr "%1 nie można zapisywać" msgid "All Supported Files" msgstr "Wszystkie obsÅ‚ugiwane pliki" msgid "All Files" msgstr "Wszystkie pliki" msgid "Track" msgstr "Åšcieżka" msgid "Absolute path to file" msgstr "BezwzglÄ™dna Å›cieżka" msgid "Duration" msgstr "Czas trwania" msgid "Title" msgstr "TytuÅ‚" msgid "Artist" msgstr "Wykonawca" msgid "Album" msgstr "Album" msgid "Comment" msgstr "Komentarz" msgid "Date" msgstr "Data" msgid "Track Number" msgstr "Numer Å›cieżki" msgid "Genre" msgstr "Gatunek" msgid "Album Artist" msgstr "Wykonawca albumu" msgid "Arranger" msgstr "Aranżer" msgid "Author" msgstr "Autor" msgid "BPM" msgstr "Uderzenia na minutÄ™" msgid "Catalog Number" msgstr "Numer katalogowy" msgid "Compilation" msgstr "SkÅ‚adanka" msgid "Composer" msgstr "Kompozytor" msgid "Conductor" msgstr "Dyrygent" msgid "Copyright" msgstr "Prawa autorskie" msgid "Disc Number" msgstr "Numer pÅ‚yty" msgid "Encoded-by" msgstr "Koder" msgid "Encoder Settings" msgstr "Ustawienia kodera" msgid "Encoding Time" msgstr "Czas zakodowania" msgid "Grouping" msgstr "Grupowanie" msgid "Initial Key" msgstr "" msgid "ISRC" msgstr "ISRC" msgid "Language" msgstr "JÄ™zyk" msgid "Lyricist" msgstr "TekÅ›ciarz" msgid "Lyrics" msgstr "Tekst" msgid "Media" msgstr "Media (np. strona z nadrukiem na CD)" msgid "Mood" msgstr "Nastrój" msgid "Original Album" msgstr "Oryginalny album" msgid "Original Artist" msgstr "Oryg. artysta" msgid "Original Date" msgstr "Oryginalna data" msgid "Part" msgstr "Część" msgid "Performer" msgstr "Wykonawca" msgid "Picture" msgstr "Obrazek" msgid "Publisher" msgstr "Wydawca" msgid "Release Country" msgstr "Kraj wydania albumu" msgid "Remixer" msgstr "Remixer" msgid "Sort Album" msgstr "PorzÄ…dek sortowania albumów" msgid "Sort Album Artist" msgstr "Nazwa wykonawcy albumu" msgid "Sort Artist" msgstr "PorzÄ…dek sortowania wykonawców" msgid "Sort Composer" msgstr "PorzÄ…dek sortowania kompozytor" msgid "Sort Name" msgstr "PorzÄ…dek sortowania tytułów" msgid "Subtitle" msgstr "Napisy" msgid "Website" msgstr "Strona WWW" msgid "WWW Audio File" msgstr "WWW pliku audio" msgid "WWW Audio Source" msgstr "WWW źródÅ‚a audio" msgid "Number of tracks" msgstr "Liczba utworów" msgid "Extension" msgstr "Rozszerzenie pliku" msgid "Bitrate" msgstr "Szybkość transmisji" msgid "VBR" msgstr "VBR" msgid "Samplerate" msgstr "Próbkowanie" msgid "Channels" msgstr "KanaÅ‚y" msgid "Codec" msgstr "Kodek" msgid "Escape for HTML" msgstr "Wyjdź HTML" msgid "Automatic Import" msgstr "Automatycznie importuj" msgid "D&estination:" msgstr "C&el:" msgid "Tag 1 and Tag 2" msgstr "Znacznik 1 i znacznik 2" msgid "&Profile:" msgstr "&Profil:" msgid "&Save Settings" msgstr "&Zapisz ustawienia" msgid "New" msgstr "Nowy" msgid "A&bort" msgstr "&Przerwij" msgid "S&tart" msgstr "PoczÄ…&tek" msgid "Import Source" msgstr "ŹródÅ‚o importu" msgid "&Server:" msgstr "&Serwer:" msgid "&Accuracy:" msgstr "DokÅ‚&adność:" msgid "&Standard Tags" msgstr "&Standardowe znaczniki" msgid "&Additional Tags" msgstr "Dod&atkowe znaczniki" msgid "C&over Art" msgstr "&OkÅ‚adek" msgid "Browse Cover Art" msgstr "Pobierz okÅ‚adkÄ™" msgid "&Artist/Album" msgstr "Wykonawca/&Album" msgid "&Source" msgstr "&ŹródÅ‚o" msgid "Source:" msgstr "ŹródÅ‚o:" msgid "URL:" msgstr "URL:" msgid "&URL extraction" msgstr "&URL zamiania" msgid "Match" msgstr "Dopasowanie" msgid "Picture URL" msgstr "Obrazek URL" msgid "&Browse" msgstr "&PrzeglÄ…darka" msgid "Click Browse to start" msgstr "Kliknij przycisk PrzeglÄ…darka aby uruchomić" msgid "Then drag the picture from the browser to Kid3." msgstr "Obrazek można przeciÄ…gnąć do Kid3." msgid "ID3v1" msgstr "ID3v1" msgid "&Mark truncated fields" msgstr "&Oznacz obciÄ™te pola" msgid "Text &encoding:" msgstr "Kodowanie t&ekstu:" msgid "ID3v2" msgstr "ID3v2" msgid "Use &track/total number of tracks format" msgstr "Używaj &formatu Å›cieżka/caÅ‚kowita liczba Å›cieżek" msgid "Track number &digits:" msgstr "Numer Å›cieżki &liczba cyfr:" msgid "&Genre as text instead of numeric string" msgstr "&Gatunek jako tekst" msgid "ISO-8859-1" msgstr "ISO-8859-1" msgid "UTF16" msgstr "UTF16" msgid "UTF8" msgstr "UTF8" msgid "&Version used for new tags:" msgstr "Wersja używana dla &nowych znaczników:" msgid "ID3v2.3.0" msgstr "ID3v2.3.0" msgid "ID3v2.4.0" msgstr "ID3v2.4.0" msgid "Ogg/Vorbis" msgstr "Ogg/Vorbis" msgid "Co&mment field name:" msgstr "Nazwa pola ko&mentarza:" msgid "&Picture field name:" msgstr "Nazwa pola &obrazu:" msgid "Custom &Genres" msgstr "WÅ‚asne &gatunki" msgid "&Show only custom genres" msgstr "&Pokazuj tylko wÅ‚asne gatunki" msgid "&Quick Access Tags" msgstr "Znaczni&ki szybkiego dostÄ™pu" msgid "&Tag Format" msgstr "&Format znacznika" msgid "Tag &1" msgstr "Znacznik &1" msgid "Tag &2" msgstr "Znacznik &2" msgid "Tag 1 a&nd Tag 2" msgstr "Znacznik 1 &i znacznik 2" msgid "Startup" msgstr "Uruchamianie" msgid "&Load last-opened files" msgstr "Na &starcie wczytaj ostatnio otwarte pliki" msgid "Save" msgstr "Zapisz" msgid "&Preserve file timestamp" msgstr "&Zachowaj znacznik czasu pliku" msgid "&Mark changes" msgstr "&Oznacz zmiany" msgid "F&ilename for cover:" msgstr "Nazwa pl&iku obrazu:" msgid "&Filename Format" msgstr "&Format nazwy pliku" msgid "Browser" msgstr "PrzeglÄ…darka" msgid "Web &browser:" msgstr "&PrzeglÄ…darka WWW:" msgid "Context &Menu Commands" msgstr "Polecenia &menu kontekstowego" #, fuzzy msgid "&Play on double click" msgstr "&Odtwarzaj na podwójne klikniÄ™cie" msgid "Proxy" msgstr "PoÅ›rednik (proxy)" msgid "&Proxy:" msgstr "&PoÅ›rednik (proxy):" msgid "&Use authentication with proxy" msgstr "&Użyj uwierzytelniania dla proxy" msgid "Proxy user &name:" msgstr "&Użytkownik proxy:" msgid "Proxy pass&word:" msgstr "&HasÅ‚o dla proxy:" msgid "&Metadata Plugins && Priority" msgstr "Wtyczki &metadane && Priorytet" msgid "A&vailable Plugins" msgstr "&DostÄ™pne wtyczki" msgid "Changes take only effect after a restart!" msgstr "Zmiany bÄ™dÄ… widoczne dopiero po ponownym uruchomieniu!" msgid "From Clip&board" msgstr "Ze &schowka" msgid "&Import" msgstr "&Importuj" msgid "&Export" msgstr "&Eksportuj" msgid "&View" msgstr "&Widok" msgid "Text Encoding" msgstr "Kodowanie tekstu" msgid "Text" msgstr "Tekst" msgid "Description" msgstr "Opis" msgid "Owner" msgstr "WÅ‚aÅ›ciciel" msgid "Email" msgstr "E-mail" msgid "Rating" msgstr "Ocena" msgid "Picture Type" msgstr "Typ obrazu" msgid "Image format" msgstr "Format obrazu" msgid "Mimetype" msgstr "Typ MIME" msgid "Counter" msgstr "Licznik" msgid "Identifier" msgstr "Identyfikator" msgid "Volume Adjustment" msgstr "Zmiana gÅ‚oÅ›noÅ›ci" msgid "Number of Bits" msgstr "Liczba bitów" msgid "Volume Change Right" msgstr "" msgid "Volume Change Left" msgstr "" msgid "Peak Volume Right" msgstr "" msgid "Peak Volume Left" msgstr "" msgid "Timestamp Format" msgstr "Format znacznika czasu" msgid "Content Type" msgstr "Typ zawartoÅ›ci" msgid "Price" msgstr "Cena" msgid "Seller" msgstr "Sprzedawca" msgid "Images" msgstr "Obrazy" msgid "UTF16BE" msgstr "UTF16BE" msgid "Other" msgstr "Inny" msgid "32x32 pixels PNG file icon" msgstr "Ikona pliku PNG 32x32 pikseli" msgid "Other file icon" msgstr "Inna ikona pliku" msgid "Cover (front)" msgstr "OkÅ‚adka (przód)" msgid "Cover (back)" msgstr "OkÅ‚adka (tyÅ‚)" msgid "Leaflet page" msgstr "Strona ulotki" msgid "Lead artist/lead performer/soloist" msgstr "Główny artysta/wykonawca/solista" msgid "Artist/performer" msgstr "Artysta/wykonawca" msgid "Band/Orchestra" msgstr "Zespół/orkiestra" msgid "Lyricist/text writer" msgstr "TekÅ›ciarz/autor tekstu" msgid "Recording Location" msgstr "Miejsce nagrania" msgid "During recording" msgstr "Podczas nagrywania" msgid "During performance" msgstr "Podczas wystÄ™pu" msgid "Movie/video screen capture" msgstr "Zrzut ekranu z filmu/video" msgid "A bright coloured fish" msgstr "Jaskrawo pokolorowana ryba" msgid "Illustration" msgstr "Ilustracja" msgid "Band/artist logotype" msgstr "Logo zespoÅ‚u/artysty" msgid "Publisher/Studio logotype" msgstr "Logo wydawcy/studia" msgid "MPEG frames as unit" msgstr "Ramki MPEG jako jednostka" msgid "Milliseconds as unit" msgstr "Milisekundy jako jednostka" msgid "Text transcription" msgstr "Transkrypcja tekstu" msgid "Movement/part name" msgstr "Nazwa części/odcinka" msgid "Events" msgstr "Wydarzenia" msgid "Chord" msgstr "Akord" msgid "Trivia/pop up" msgstr "Trivia/podnoszÄ…" msgid "Export" msgstr "Eksportuj" msgid "Format:" msgstr "Format:" msgid "Header:" msgstr "Nagłówek:" msgid "Tracks:" msgstr "Utwory:" msgid "Footer:" msgstr "Nagłówek:" msgid "To F&ile" msgstr "Do p&liku" msgid "To Clip&board" msgstr "Do &schowka" msgid "&Source:" msgstr "&ŹródÅ‚o:" msgid "File Error" msgstr "Błąd otwarcia pliku" msgid "&Filter:" msgstr "&Filtr:" msgid "&Expression:" msgstr "&Wyrażenie:" msgid "&Apply" msgstr "&Zastosuj" msgid "Accuracy:" msgstr "DokÅ‚adność:" msgid "Cover Art:" msgstr "OkÅ‚adek:" msgid "From F&ile/Clipboard..." msgstr "Ze pl&ik/schowka..." msgid "From T&ags..." msgstr "Ze zn&acznika..." msgid "&From Server:" msgstr "Z se&rwera:" msgid "Check maximum allowable time &difference (sec):" msgstr "Sprawdź dopuszczalnÄ… &różnicÄ™ czasu (sek.):" msgid "Match with:" msgstr "Dopasuj wedÅ‚ug:" msgid "&Length" msgstr "&DÅ‚ugoÅ›ci" msgid "T&rack" msgstr "Åš&cieżki" msgid "&Title" msgstr "&TytuÅ‚u" msgid "&Start number:" msgstr "Liczba &poczÄ…tkowa:" msgid "&Destination:" msgstr "&Cel:" msgid "&Total number of tracks:" msgstr "&Liczba utworów:" msgid "Create Playlist" msgstr "Stwórz listÄ™ odtwarzania" msgid "Playlist File Name" msgstr "Nazwa pliku playlisty" msgid "Same as &directory name" msgstr "Taka jak nazwa &katalogu" msgid "&Format:" msgstr "&Format:" msgid "Cr&eate in:" msgstr "&Utwórz w:" msgid "Current directory" msgstr "Bieżący katalog" msgid "Every directory" msgstr "We wszystkich katalogach" msgid "Top-level directory" msgstr "W nadrzÄ™dnych katalogach" msgid "Playlist Content" msgstr "Zawartość listy odtwarzania" msgid "For&mat:" msgstr "For&mat:" msgid "Incl&ude only the selected files" msgstr "Dołącz tylko &zaznaczone pliki" msgid "Sort by file &name" msgstr "Sortuj elementy wedÅ‚ug &nazwy pliku" msgid "Sort by &tag field" msgstr "Sortuj elementy wedÅ‚ug &pola znacznika" msgid "Use &relative path for files in playlist" msgstr "Użycie &wzglÄ™dnych Å›cieżek do plików" msgid "Use full p&ath for files in playlist" msgstr "Użycie &bezwzglÄ™dnych Å›cieżek do plików" msgid "Write only &list of files" msgstr "Tylko &listy plików" msgid "Write &info using" msgstr "Dodatkowa &informacja z maski" msgid "Preview" msgstr "PodglÄ…d" msgid "Create Directory" msgstr "Utwórz katalog" msgid "From Tag 2 and Tag 1" msgstr "Ze znacznika 1 i znacznika 2" msgid "From:" msgstr "Z:" msgid "To:" msgstr "Do:" msgid "&Find" msgstr "&Znajdź" msgid "C&GI Path:" msgstr "Åšcieżka C&GI:" msgid "Track Title/Artist - Album" msgstr "TytuÅ‚ Å›cieżki/Artysta - Album" msgid "State" msgstr "Stan" msgid "No result" msgstr "Brak wyników" msgid "No result selected" msgstr "Nie wybrano rezultatu" msgid "Import from Tags" msgstr "Importuj ze znacznika" msgid "Extraction:" msgstr "Ekstrakcja:" msgid "Import from File/Clipboard" msgstr "Importuj plik/schowka" msgid "From F&ile" msgstr "Wczytaj z p&liku" msgid "Download" msgstr "Pobieranie" msgid "Opening directory..." msgstr "Otwieranie katalogu..." msgid "Saving directory..." msgstr "Zapisywanie katalogu..." msgid "" "The current directory has been modified.\n" "Do you want to save it?" msgstr "" "Bieżący katalog zostaÅ‚ zmodyfikowany.\n" "Czy chcesz go zapisać?" msgid "Warning" msgstr "Ostrzeżenie" msgid "Exiting..." msgstr "KoÅ„czenie pracy..." msgid "Creating playlist..." msgstr "Tworzenie listy odtwarzania..." msgid "Import" msgstr "Importuj" msgid "Error while renaming:\n" msgstr "Błąd podczas zmieniania nazwy:\n" msgid " [filtered]" msgstr " [filtrowane]" msgid "Add Frame" msgstr "Dodaj ramkÄ™" msgid "Select the frame ID" msgstr "Wybierz identyfikator ramki" msgid "Rename File" msgstr "ZmieÅ„ nazwÄ™ pliku" msgid "Enter new file name:" msgstr "Podaj nowÄ… nazwÄ™ pliku:" # Czy na pewno chcesz przenieść te %1 plików do kosza? msgid "Do you really want to move these %1 items to the trash?" msgstr "Czy na pewno chcesz usunąć te %1 pozycji?" msgid "Do you really want to move this item to the trash?" msgstr "Czy na pewno chcesz przenieść ten plik do kosza?" msgid "Move to Trash" msgstr "PrzenieÅ› do kosza" msgid "Directory must be empty.\n" msgstr "Katalog musi być pusty.\n" msgid "Could not move these files to the Trash" msgstr "Nie można przenieść tych plików do Kosza" msgid "Expand All" msgstr "RozwiÅ„ wszystko" msgid "&Expand all" msgstr "&RozwiÅ„ wszystko" msgid "&Collapse all" msgstr "&ZwiÅ„ wszystko" msgid "&Rename" msgstr "&ZmieÅ„ nazwÄ™" msgid "&Move to Trash" msgstr "PrzenieÅ› do &kosza" msgid "Kid3" msgstr "Kid3" msgid "F&ile" msgstr "Pl&ik" msgid "Name:" msgstr "Nazwa:" msgid "Filename from Tag 1" msgstr "Nazwa pliku ze znacznika 1" msgid "Filename from Tag 2" msgstr "Nazwa pliku ze znacznika 2" msgid "Tag 1 from Filename" msgstr "Znacznik 1 wczytaj z nazwy pliku" msgid "Tag 2 from Filename" msgstr "Znacznik 2 wczytaj z nazwy pliku" msgid "&Add..." msgstr "&Dodaj..." msgid "Move &Up" msgstr "&PrzesuÅ„ w górÄ™" msgid "Move &Down" msgstr "&PrzesuÅ„ niżej" msgid "&Edit..." msgstr "&Edycja..." msgid "&Remove" msgstr "&UsuÅ„" msgid "&Insert row" msgstr "&Wstaw wiersz" msgid "&Delete row" msgstr "&UsuÅ„ wiersz" msgid "&Clear row" msgstr "W&yczyść wiersz" msgid "Format while editing" msgstr "Formatuj podczas edycji" msgid "Case conversion:" msgstr "Konwersja przypadków:" msgid "No changes" msgstr "Bez zmian" msgid "All lowercase" msgstr "Wszystko maÅ‚ymi literami" msgid "All uppercase" msgstr "Wszystko dużymi literami" msgid "First letter uppercase" msgstr "Duża pierwsza litera" msgid "All first letters uppercase" msgstr "Duże wszystkie pierwsze litery" msgid "Locale:" msgstr "Lokalizacja:" msgid "None" msgstr "Brak" msgid "String replacement:" msgstr "ZastÄ™pczy ciÄ…g znaków:" msgid "From" msgstr "Z" msgid "To" msgstr "Do" msgid "&Add" msgstr "&Dodaj" msgid "&Select all" msgstr "&Zaznacz wszystko" msgid "&Deselect all" msgstr "&UsuÅ„ zaznaczenie" msgid "View Picture" msgstr "Pokaż obrazek" msgid "" "Drag album\n" "artwork\n" "here" msgstr "" "PrzeciÄ…gnij\n" "tutaj okÅ‚adkÄ™\n" "albumu" msgid "Play/Pause" msgstr "Odtwarzanie/Pauza" msgid "Stop playback" msgstr "ZakoÅ„cz odtwarzanie" msgid "Previous Track" msgstr "Poprzedni utwór" msgid "Next Track" msgstr "NastÄ™pny utwór" msgid "Close" msgstr "Zamknij" msgid "Mute" msgstr "Wycisz" msgid "Volume: %1%" msgstr "GÅ‚oÅ›ność: %1%" msgid "Add Item" msgstr "Dodaj element" msgid "Edit Item" msgstr "Edytuj element" msgid "MusicBrainz Fingerprint" msgstr "MusicBrainz Fingerprint" msgid "Unrecognized" msgstr "Nie rozpoznano" msgid "Recognized" msgstr "Rozpoznano" msgid "User Selection" msgstr "Wybór użytkownika" msgid "ID Lookup" msgstr "Poszukiwanie ID" msgid "Fingerprint" msgstr "Fingerprint" msgid "Metadata Lookup" msgstr "Odczytywanie metadanych" msgid "Amazon" msgstr "Amazon" msgid "Discogs" msgstr "Discogs" msgid "gnudb.org" msgstr "gnudb.org" msgid "TrackType.org" msgstr "TrackType.org" msgid "AENC - Audio encryption" msgstr "AENC - Szyfrowanie audio" msgid "APIC - Attached picture" msgstr "APIC - Załączony obrazek" msgid "COMM - Comments" msgstr "COMM - Komentarze" msgid "COMR - Commercial" msgstr "" msgid "ENCR - Encryption method registration" msgstr "" msgid "EQUA - Equalization" msgstr "" msgid "ETCO - Event timing codes" msgstr "" msgid "GEOB - General encapsulated object" msgstr "" msgid "GRID - Group identification registration" msgstr "" msgid "IPLS - Involved people list" msgstr "IPLS - Lista ludzi zaangażowanych" msgid "LINK - Linked information" msgstr "LINK - Połączona informacja" msgid "MCDI - Music CD identifier" msgstr "MCDI - Identyfikator muzycznego CD" msgid "MLLT - MPEG location lookup table" msgstr "" msgid "OWNE - Ownership frame" msgstr "" msgid "PRIV - Private frame" msgstr "PRIV - Pole prywatne" msgid "PCNT - Play counter" msgstr "PCNT - Licznik odtworzeÅ„" msgid "POPM - Popularimeter" msgstr "POPM - Miernik popularnoÅ›ci" msgid "POSS - Position synchronisation frame" msgstr "" msgid "RBUF - Recommended buffer size" msgstr "RBUF - Polecany rozmiar bufora" msgid "RVAD - Relative volume adjustment" msgstr "" msgid "RVRB - Reverb" msgstr "" msgid "SYLT - Synchronized lyric/text" msgstr "SYLT - Zsynchronizowane sÅ‚owa/tekst" msgid "SYTC - Synchronized tempo codes" msgstr "" msgid "TALB - Album/Movie/Show title" msgstr "TALB - TytuÅ‚ albumu/filmu/pokazu" msgid "TBPM - BPM (beats per minute)" msgstr "TBPM - BPM (uderzeÅ„ na minutÄ™)" msgid "TCOM - Composer" msgstr "TCOM - Kompozytor" msgid "TCON - Content type" msgstr "TCON - Typ zawartoÅ›ci" msgid "TCOP - Copyright message" msgstr "TCOP - Informacja o prawach autorskich" msgid "TDAT - Date" msgstr "TDAT - Data" msgid "TDLY - Playlist delay" msgstr "" msgid "TENC - Encoded by" msgstr "TENC - Zakodowane przez" msgid "TEXT - Lyricist/Text writer" msgstr "TEXT - TekÅ›ciarz/autor tekstu" msgid "TFLT - File type" msgstr "TFLT - Typ pliku" msgid "TIME - Time" msgstr "TIME - Czas" msgid "TIT1 - Content group description" msgstr "TIT1 - Opis zespoÅ‚u zawartoÅ›ci" msgid "TIT2 - Title/songname/content description" msgstr "TIT2 - TytuÅ‚/TytuÅ‚ utworu/Opis zawartoÅ›ci" msgid "TIT3 - Subtitle/Description refinement" msgstr "" msgid "TKEY - Initial key" msgstr "" msgid "TLAN - Language(s)" msgstr "TLAN - JÄ™zyk(i)" msgid "TLEN - Length" msgstr "TLEN - DÅ‚ugość" msgid "TMED - Media type" msgstr "TMED - Typ mediów" msgid "TOAL - Original album/movie/show title" msgstr "TOAL - TytuÅ‚ oryginalnego albumu/filmu/przedstawienia" msgid "TOFN - Original filename" msgstr "TOFN - Oryginalna nazwa pliku" msgid "TOLY - Original lyricist(s)/text writer(s)" msgstr "TOLY - Oryginalny tekÅ›ciarz(e)/autor(rzy) tekstu" msgid "TOPE - Original artist(s)/performer(s)" msgstr "TOPE - Oryginalny artysta/wykonawca" msgid "TORY - Original release year" msgstr "TORY - Oryginalny rok wypuszczenia" msgid "TOWN - File owner/licensee" msgstr "TOWN - WÅ‚aÅ›ciciel pliku/licencji" msgid "TPE1 - Lead performer(s)/Soloist(s)" msgstr "TPE1 - Główny wykonawca/Solista" msgid "TPE2 - Band/orchestra/accompaniment" msgstr "TPE2 - Zespół/orkiestra/akompaniament" msgid "TPE3 - Conductor/performer refinement" msgstr "" msgid "TPE4 - Interpreted, remixed, or otherwise modified by" msgstr "TPE4 - Interpretacja, remix lub jakakolwiek modyfikacja przez" msgid "TPOS - Part of a set" msgstr "TPOS - Część zestawu" msgid "TPUB - Publisher" msgstr "TPUB - Wydawca" msgid "TRCK - Track number/Position in set" msgstr "TRCK - Numer Å›cieżki/Pozycja w zestawie" msgid "TRDA - Recording dates" msgstr "TRDA - Daty nagrania" msgid "TRSN - Internet radio station name" msgstr "TRSN - Nazwa internetowej stacji radiowej" msgid "TRSO - Internet radio station owner" msgstr "TRSO - WÅ‚aÅ›ciciel internetowej stacji radiowej" msgid "TSIZ - Size" msgstr "TSIZ - Rozmiar" msgid "TSRC - ISRC (international standard recording code)" msgstr "" msgid "TSSE - Software/Hardware and settings used for encoding" msgstr "TSSE - Oprogramowanie/sprzÄ™t i ustawienia użyte do zakodowania" msgid "TXXX - User defined text information" msgstr "TXXX - Informacja tekstowa zdefiniowana przez użytkownika" msgid "TYER - Year" msgstr "TYER - Rok" msgid "UFID - Unique file identifier" msgstr "UFID - Unikalny identyfikator pliku" msgid "USER - Terms of use" msgstr "USER - Warunki użytkowania" msgid "USLT - Unsynchronized lyric/text transcription" msgstr "" msgid "WCOM - Commercial information" msgstr "" msgid "WCOP - Copyright/Legal information" msgstr "WCOP - Informacja o legalnoÅ›ci i prawach autorskich" msgid "WOAF - Official audio file webpage" msgstr "WOAF - Oficjalna strona internetowa pliku audio" msgid "WOAR - Official artist/performer webpage" msgstr "WOAR - Oficjalna strona internetowa artysty/wykonawcy" msgid "WOAS - Official audio source webpage" msgstr "WOAS - Oficjalna strona internetowa źródÅ‚a audio" msgid "WORS - Official internet radio station homepage" msgstr "WORS - Oficjalna strona internetowa radia internetowego" msgid "WPAY - Payment" msgstr "WPAY - PÅ‚atność" msgid "WPUB - Official publisher webpage" msgstr "WPUB - Oficjalna strona internetowa wydawcy" msgid "WXXX - User defined URL link" msgstr "WXXX - OdnoÅ›nik URL zdefiniowany przez użytkownika" msgid "MusicBrainz Release" msgstr "MusicBrainz Release" msgid "ASPI - Audio seek point index" msgstr "" msgid "EQU2 - Equalisation (2)" msgstr "" msgid "RVA2 - Relative volume adjustment (2)" msgstr "" msgid "SEEK - Seek frame" msgstr "" msgid "SIGN - Signature frame" msgstr "" msgid "TCMP - iTunes compilation flag" msgstr "TCMP - SkÅ‚adanka iTunes" msgid "TDEN - Encoding time" msgstr "TDEN - Czas zakodowania" msgid "TDOR - Original release time" msgstr "TDOR - Oryginalny czas wypuszczenia" msgid "TDRC - Recording time" msgstr "TDRC - Czas/data nagrania" msgid "TDRL - Release time" msgstr "TDRL - Czas wypuszczenia" msgid "TDTG - Tagging time" msgstr "TDTG - Czas zapisania znaczników" msgid "TIPL - Involved people list" msgstr "TIPL - Lista ludzi zaangażowanych" msgid "TMCL - Musician credits list" msgstr "" msgid "TMOO - Mood" msgstr "TMOO - Nastrój" msgid "TPRO - Produced notice" msgstr "" msgid "TSO2 - Album artist sort order" msgstr "TSO2 - Nazwa wykonawcy albumu (posortowane)" msgid "TSOA - Album sort order" msgstr "TSOA - PorzÄ…dek sortowania albumów" msgid "TSOC - Composer sort order" msgstr "TSOC - PorzÄ…dek sortowania kompozytor" msgid "TSOP - Performer sort order" msgstr "TSOP - PorzÄ…dek sortowania wykonawców" msgid "TSOT - Title sort order" msgstr "TSOT - PorzÄ…dek sortowania tytułów" msgid "TSST - Set subtitle" msgstr "" ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/po/po2ts.pl������������������������������������������������������������������������������0000775�0000000�0000000�00000006356�12246035113�0014402�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -W package po2ts; use strict; use Cwd; use File::Find; use vars qw(@ISA @EXPORT); require Exporter; @ISA = qw(Exporter); @EXPORT = qw(generateTs); # Read all translations from a .po file, fill them into an associative # array. sub getPoTranslations($) { my $fn = shift; my $msgid; my $msgstr; my $in_msgid = 0; my $in_msgstr = 0; my %trans; open IF, $fn or die "Could not open $fn: $!\n"; while (<IF>) { s/\r\n/\n/; if (/^msgid "(.*)"$/) { if ($msgid) { $trans{$msgid} = $msgstr; } $msgid = $1; $msgstr = ""; $in_msgid = 1; $in_msgstr = 0; } if (/^msgstr "(.*)"$/) { $msgstr = $1; $in_msgid = 0; $in_msgstr = 1; } if (/^"(.+)"$/) { if ($in_msgid) { $msgid .= $1; } elsif ($in_msgstr) { $msgstr .= $1; } } } close IF; if ($msgid) { $trans{$msgid} = $msgstr; } return %trans; } # Set the translations in a .ts file replacing & by &, < by <, # > by > and ' by '. sub setTsTranslations($$%) { my $infn = shift; my $outfn = shift; my %trans = @_; my $source; my $translation; my $in_source = 0; open IF, $infn or die "Could not open $infn: $!\n"; open OF, ">$outfn" or die "Could not create $outfn: $!\n"; while (<IF>) { s/\r\n/\n/; if (/<source>(.*)<\/source>/) { $source = $1; $in_source = 0; } elsif (/<source>(.*)$/) { $source = $1; $in_source = 1; } elsif ($in_source) { if (/^(.*)<\/source>/) { $source .= "\n$1"; $in_source = 0; } else { my $line = $_; chomp $line; $source .= "\n$line"; } } elsif (/<translation/) { $source =~ s/&/&/g; $source =~ s/</</g; $source =~ s/>/>/g; $source =~ s/'/'/g; $source =~ s/"/\\"/g; $source =~ s/\n/\\n/g; if (exists $trans{$source}) { $translation = $trans{$source}; $translation =~ s/&/&/g; $translation =~ s/</</g; $translation =~ s/>/>/g; $translation =~ s/'/'/g; $translation =~ s/\\"/"/g; $translation =~ s/\\n/\n/g; s/ type="unfinished"//; s/<\/translation>/$translation<\/translation>/; } else { print "Could not find translation for \"$source\"\n"; } } print OF $_; } close OF; close IF; } my @sources; # Push .cpp file names into @sources. sub wanted { /\.cpp$/ && push @sources, $File::Find::name; } # Generate .ts files from .po files. # parameters: path to lupdate command, directory with po-files, # directory with source files sub generateTs { my ($lupdate_cmd, $podir, $srcdir) = @_; my @pofiles = glob "$podir/*.po"; my @languages = map { /^.*\/([\w@]+)\.po$/ } @pofiles; my $curdir = cwd(); find(\&wanted, $srcdir); chdir $srcdir or die "Could not change to $srcdir: $!\n"; system "$lupdate_cmd -recursive . -ts " . join ' ', map { "$curdir/tmp_". $_ . ".ts" } @languages; chdir $curdir; foreach my $lang (@languages) { setTsTranslations("tmp_$lang.ts", "kid3_$lang.ts", getPoTranslations("$podir/$lang.po")); } unlink map { "tmp_". $_ . ".ts" } @languages; } if (!caller()) { generateTs(@ARGV); } 1; ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/po/ru.po���������������������������������������������������������������������������������0000664�0000000�0000000�00000122303�12246035113�0013750�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# translation of ru.po to English # translation of ru.po to # Kid3 Russian Translation. # Copyright (C) 2003, 2004, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. # # Urs Fleisch <ufleisch@users.sourceforge.net>, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010. # "Egor S. Orlov" <Egor.Orlov@avalon.ru>, 2003, 2011. msgid "" msgstr "" "Project-Id-Version: Kid3 3.0.2\n" "Report-Msgid-Bugs-To: http://sourceforge.net/p/kid3/bugs/\n" "POT-Creation-Date: 2013-09-11 21:22+0200\n" "PO-Revision-Date: 2011-01-15 17:41+0100\n" "Last-Translator: \n" "Language-Team: Russian <kde-i18n-doc@kde.org>\n" "Language: ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Lokalize 1.1\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" "X-Language: ru\n" "X-Source-Language: en\n" msgid "Timeout" msgstr "Ð’Ñ€ÐµÐ¼Ñ Ð¾Ð¶Ð¸Ð´Ð°Ð½Ð¸Ñ" msgid "Usage:" msgstr "ИÑпользовано:" msgid "Help" msgstr "Помощь" msgid "Command name" msgstr "Ð˜Ð¼Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ñ‹" msgid "Quit application" msgstr "Завершить приложение" msgid "The current directory has been modified." msgstr "Текущий каталог был изменен." msgid "Type 'exit force' to quit." msgstr "Ð”Ð»Ñ Ð²Ñ‹Ñ…Ð¾Ð´Ð° нажмите 'exit force'." msgid "Change directory" msgstr "" msgid "%1 does not exist" msgstr "%1 не ÑущеÑтвует" msgid "Print the filename of the current working directory" msgstr "Печатает полное Ð¸Ð¼Ñ Ñ‚ÐµÐºÑƒÑ‰ÐµÐ³Ð¾ рабочего каталога" msgid "Directory list" msgstr "Содержимое каталога" msgid "Saves the changed files" msgstr "Сохранить измененные файлы" msgid "Error while writing file:\n" msgstr "Ошибка при запиÑи файла:\n" msgid "Select file" msgstr "Выберите файл" msgid "%1 not found" msgstr "файл %1 не найден" msgid "Select tag" msgstr "Выбрать тег" msgid "Get tag frame" msgstr "" msgid "Set tag frame" msgstr "" msgid "Could not set \"%1\" for %2" msgstr "" msgid "Revert" msgstr "Отменить" msgid "Import from file or clipboard" msgstr "Импортировать файл или буфер обмена" msgid "Format name" msgstr "Ðазвание формата" msgid "%1 not found." msgstr "%1 не найден." msgid "Available" msgstr "ДоÑтупные" msgid "Error" msgstr "Ошибка" msgid "Automatic import" msgstr "ÐвтоматичеÑки импортировать" msgid "Profile name" msgstr "Ð˜Ð¼Ñ Ð¿Ñ€Ð¾Ñ„Ð¸Ð»Ñ" msgid "Reading Directory" msgstr "Чтение каталога" msgid "Started" msgstr "Запущено" msgid "Source" msgstr "ИÑточник" msgid "Querying" msgstr "ЗапроÑ" msgid "Fetching" msgstr "Получение" msgid "Data received" msgstr "Полученные ÑведениÑ" msgid "Cover" msgstr "Обложки" msgid "Finished" msgstr "Готово" msgid "Aborted" msgstr "Прервано" msgid "Download album cover artwork" msgstr "Ðайти обложки" msgid "Export to file or clipboard" msgstr "ЭкÑпорт в файл или Буфера Обмена" msgid "Create playlist" msgstr "Cоздать ÑпиÑок пеÑен" msgid "Apply filename format" msgstr "Применить Формат имени файла" msgid "Apply tag format" msgstr "Применить формат Ñ‚Ñга" msgid "Apply text encoding" msgstr "Применить Кодировка ТекÑта" msgid "Rename directory" msgstr "Переименовать Каталог" msgid "Number tracks" msgstr "Пронумеровать дорожки по порÑдку" msgid "Track number" msgstr "Ðомер дорожки" msgid "Filter" msgstr "Фильтр" msgid "Filter name" msgstr "Ð˜Ð¼Ñ Ñ„Ð¸Ð»ÑŒÑ‚Ñ€Ð°" msgid "Convert ID3v2.3 to ID3v2.4" msgstr "Преобразуйте ID3v2.3 в ID3v2.4" msgid "Convert ID3v2.4 to ID3v2.3" msgstr "Преобразуйте ID3v2.4 в ID3v2.3" msgid "Filename from tag" msgstr "Ð˜Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð° из тег" msgid "Tag from filename" msgstr "Тег из имени файла" msgid "Tag to other tag" msgstr "" msgid "Copy" msgstr "Копировать" msgid "Paste" msgstr "Ð’Ñтавить" msgid "Remove" msgstr "Удалить" msgid "Play" msgstr "Играть" msgid "Parameter" msgstr "Параметр" msgid "File path" msgstr "Путь к файлу" msgid "URL" msgstr "URL" msgid "Tag numbers" msgstr "Ðомер дорожки" msgid "Frame name" msgstr "Ð˜Ð¼Ñ Ñ„Ñ€ÐµÐ¹Ð¼Ð°" msgid "Frame value" msgstr "Значение фрейма" msgid "Format" msgstr "Формат" msgid "Command specific" msgstr "Команда" msgid "Available Commands" msgstr "ДоÑтупные команды" msgid "File" msgstr "Файл" msgid "Name" msgstr "ИмÑ" msgid "Tag 2" msgstr "Тег 2" msgid "Tag 1" msgstr "Тег 1" msgid "Tags" msgstr "Теги" msgid "Unknown command '%1'. Type 'help' for help." msgstr "ÐеизвеÑÑ‚Ð½Ð°Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð° '%1'. Введите 'help' Ð´Ð»Ñ Ñправки." msgid "Unknown command '%1', -h for help." msgstr "ÐеизвеÑÑ‚Ð½Ð°Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð° '%1', -h Ð´Ð»Ñ Ð¿Ð¾Ð´Ñказки." msgid "Files" msgstr "Файлы" msgid "User Actions" msgstr "Команды пользователÑ" msgid "Network" msgstr "Сеть" msgid "Plugins" msgstr "Модули" msgid "Main Toolbar" msgstr "ОÑÐ½Ð¾Ð²Ð½Ð°Ñ Ð¿Ð°Ð½ÐµÐ»ÑŒ инÑтрументов" msgid "Opens a directory" msgstr "Открыть каталог" msgid "Opens a recently used directory" msgstr "Открыть недавний каталог" msgid "Reverts the changes of all or the selected files" msgstr "Отменить Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð²Ñех выделенных файлов" msgid "Quits the application" msgstr "Завершить приложение" msgid "Select all files" msgstr "Выделить вÑе" msgid "Deselect all files" msgstr "Отменить выбор" msgid "Configure Shortcuts" msgstr "ÐаÑтройка быÑтрых клавиш" msgid "Configure Toolbars" msgstr "ÐаÑтроить инÑтрументы" msgid "Preferences dialog" msgstr "ÐаÑтройка параметров" msgid "O&pen Directory..." msgstr "Открыть &каталог..." msgid "&Import..." msgstr "&Импорт..." msgid "Import from %1..." msgstr "Импорт из %1..." msgid "Automatic I&mport..." msgstr "ÐвтоматичеÑки и&мпортировать..." msgid "&Browse Cover Art..." msgstr "&Ðайти обложки..." msgid "&Export..." msgstr "&ЭкÑпорт..." msgid "&Create Playlist..." msgstr "&Cоздать ÑпиÑок пеÑен..." msgid "Apply &Filename Format" msgstr "Применить Формат имени &файла" msgid "Apply &Tag Format" msgstr "Применить Формат &Ñ‚Ñга" msgid "Apply Text &Encoding" msgstr "Применить Кодировка Т&екÑта" msgid "&Rename Directory..." msgstr "&Переименовать Каталог..." msgid "&Number Tracks..." msgstr "&Пронумеровать дорожки по порÑдку..." msgid "F&ilter..." msgstr "Ф&ильтр..." msgid "Convert ID3v2.3 to ID3v2.&4" msgstr "Преобразуйте ID3v2.3 в ID3v2.&4" msgid "Convert ID3v2.4 to ID3v2.&3" msgstr "Преобразуйте ID3v2.4 в ID3v2.&3" msgid "&Play" msgstr "&Играть" msgid "Show &Picture" msgstr "Показывать &картину" msgid "Auto &Hide Tags" msgstr "Теги автоматичеÑкое &Ñкрытие" msgid "Select All in &Directory" msgstr "Выделить вÑе файлы в текущем &каталоге" msgid "&Previous File" msgstr "&Предыдущий файл" msgid "&Next File" msgstr "&Следующий файл" msgid "From Filename" msgstr "Из имени файла" msgid "From Tag 2" msgstr "Из тег 2" msgid "From Tag 1" msgstr "Из тег 1" msgid "Frames:" msgstr "Фреймы:" msgid "Edit" msgstr "Правка" msgid "Add" msgstr "Добавить" msgid "Delete" msgstr "Удалить" msgid "Filename" msgstr "Ð˜Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð°" msgid "Focus" msgstr "ФокуÑ" msgid "File List" msgstr "СпиÑок файлов" msgid "Directory List" msgstr "Содержимое каталога" msgid "Configure - Kid3" msgstr "ÐаÑтройка - Kid3" msgid "Open" msgstr "Открыть" msgid "Save As" msgstr "Сохранить как" msgid "directory to open" msgstr "каталог Ð´Ð»Ñ Ð¾Ñ‚ÐºÑ€Ñ‹Ñ‚Ð¸Ñ" msgid "Kid3 ID3 Tagger" msgstr "Kid3 Редактор Ñ‚Ñгов ID3" #. i18n NAME OF TRANSLATORS msgid "Your names" msgstr "Егор С. Орлов" #. i18n EMAIL OF TRANSLATORS msgid "Your emails" msgstr "Egor.Orlov@avalon.ru" msgid "&Back" msgstr "&Дорожка" msgid "&Forward" msgstr "&Вперёд" msgid "&Find:" msgstr "&Ðайти:" msgid "Find Previous" msgstr "Ðайти предыдущее" msgid "Find Next" msgstr "Ðайти Ñледующее" msgid "&Close" msgstr "&Закрыть" msgid "&Tags" msgstr "&Теги" msgid "&Files" msgstr "&Файлы" msgid "&User Actions" msgstr "&Команды пользователÑ" msgid "&Network" msgstr "&Сеть" msgid "&Plugins" msgstr "&Модули" msgid "&Keyboard Shortcuts" msgstr "&Клавиши быÑтрого доÑтупа" msgid "Use custom app&lication font" msgstr "ИÑпользовать выбранный &шрифт приложениÑ" msgid "A&pplication Font..." msgstr "Шрифт &приложениÑ..." msgid "Use custom application &style" msgstr "ИÑпользовать выбранный &Ñтиль приложениÑ" msgid "Unknown" msgstr "ÐеизвеÑтен" msgid "Use native system file &dialogs" msgstr "ИÑпользовать вÑтроенные файловые &диалоги" msgid "&Appearance" msgstr "&Внешний вид" msgid "&Help" msgstr "Помо&щь" msgid "&OK" msgstr "&ОК" msgid "&Cancel" msgstr "О&тмена" msgid "The keyboard shortcut '%1' is already assigned to '%2'." msgstr "ÐšÐ»Ð°Ð²Ð¸Ð°Ñ‚ÑƒÑ€Ð½Ð°Ñ ÐºÐ¾Ð¼Ð±Ð¸Ð½Ð°Ñ†Ð¸Ñ '%1' уже назначена Ð´Ð»Ñ '%2'." msgid "&File" msgstr "&Файл" msgid "&Open..." msgstr "&Открыть..." msgid "Open &Recent" msgstr "Открыть &недавние" msgid "&Save" msgstr "&Сохранить" msgid "Re&vert" msgstr "&Отменить" msgid "Import from %1" msgstr "Импорт из %1" msgid "Browse album cover artwork" msgstr "Ðайти обложки" msgid "Create M3U Playlist" msgstr "Создать ÑпиÑок M3U" msgid "&Quit" msgstr "Ð’&ыход" msgid "&Edit" msgstr "&Правка" msgid "Select &All" msgstr "Выделить &вÑе" msgid "Dese&lect" msgstr "Отме&нить выбор" msgid "Select all files in the current directory" msgstr "Выделить вÑе файлы в текущем каталоге" msgid "Select previous file" msgstr "Предыдущий файл" msgid "Select next file" msgstr "Следующий файл" msgid "&Tools" msgstr "&СервиÑ" msgid "Apply Filename Format" msgstr "Применить Формат имени файла" msgid "Apply Tag Format" msgstr "Применить формат Ñ‚Ñга" msgid "Apply Text Encoding" msgstr "Применить Кодировка ТекÑта" msgid "Rename Directory" msgstr "Переименовать Каталог" msgid "Number Tracks" msgstr "Пронумеровать дорожки по порÑдку" msgid "&Settings" msgstr "&ÐаÑтройка" msgid "Enables/disables the toolbar" msgstr "Включить/выключить панель инÑтрументов" msgid "Show &Toolbar" msgstr "Отобразить панель &инÑтрументов" msgid "Enables/disables the statusbar" msgstr "Включить/выключить Ñтроку ÑоÑтоÑниÑ" msgid "Show St&atusbar" msgstr "Показать Ñтроку &ÑоÑтоÑниÑ" msgid "Show Picture" msgstr "Показывать картину" msgid "Auto Hide Tags" msgstr "Теги автоматичеÑкое Ñкрытие" msgid "Configure Kid3" msgstr "ÐаÑтроить Kid3" msgid "&Configure Kid3..." msgstr "&ÐаÑтроить Kid3..." msgid "Kid3 Handbook" msgstr "РуководÑтво Kid3" msgid "Kid3 &Handbook" msgstr "&РуководÑтво \"Kid3\"" msgid "About Kid3" msgstr "О Kid3" msgid "&About Kid3" msgstr "&О Kid3" msgid "About Qt" msgstr "О Qt" msgid "About &Qt" msgstr "О &Qt" msgid " [modified]" msgstr " [изменено]" msgid "Toggle the statusbar..." msgstr "Переключение Ñтроки ÑоÑтоÑниÑ..." msgid "Ready." msgstr "Готово." msgid "&Clear List" msgstr "&ОчиÑтить ÑпиÑок" msgid "Clear" msgstr "ОчиÑтить" msgid "Reset" msgstr "СброÑ" msgid "Press F2 or double click to edit cell contents." msgstr "Ðажмите F2 или дважды щёлкните центр Ñчейки." msgid "Action" msgstr "ДейÑтвие" msgid "Shortcut" msgstr "ÐšÐ¾Ð¼Ð±Ð¸Ð½Ð°Ñ†Ð¸Ñ ÐºÐ»Ð°Ð²Ð¸Ñˆ" msgid "Server" msgstr "Сервер" msgid "Accuracy" msgstr "ТочноÑть" msgid "Standard Tags" msgstr "Стандартный теги" msgid "Additional Tags" msgstr "Дополнительные теги" msgid "Cover Art" msgstr "Обложки" msgid "Invalid File" msgstr "Ðеверный файл" msgid "Data received: %1" msgstr "Полученные ÑведениÑ: %1" msgid "Request sent..." msgstr "Передано..." msgid "Year" msgstr "Год" msgid "Length" msgstr "ДлительноÑть" msgid "Filenames" msgstr "Имена файлов" msgid "URLs" msgstr "URL" msgid "Directory name" msgstr "Ð˜Ð¼Ñ ÐºÐ°Ñ‚Ð°Ð»Ð¾Ð³Ð°" msgid "Encode as URL" msgstr "Кодировать URL" msgid "Confirm" msgstr "Подтверждение" msgid "Output" msgstr "Вывод" msgid "Command" msgstr "Команда" msgid "Create directory %1 failed\n" msgstr "Сбой попытки ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ñ‚Ñ€ÐµÐ±ÑƒÐµÐ¼Ð¾Ð³Ð¾ каталога %1\n" msgid "File %1 already exists\n" msgstr "Файл %1 уже ÑущеÑтвует\n" msgid "%1 is not a directory\n" msgstr "%1 - Ñто не каталог\n" msgid "Rename %1 to %2 failed\n" msgstr "Ошибка переименование из %1 в %2\n" msgid "%1 already exists\n" msgstr "%1 уже ÑущеÑтвует\n" msgid "%1 is not a file\n" msgstr "%1 не ÑвлÑетÑÑ Ñ„Ð°Ð¹Ð»Ð¾Ð¼\n" msgid "New directory name is too different\n" msgstr "Ðовое название каталога Ñлишком отлично\n" msgid "Create directory" msgstr "Создать Каталог" msgid "Rename file" msgstr "Переименовать файл" msgid "C&lear" msgstr "О&чиÑтить" msgid "Execute " msgstr "Выполнить " msgid "Could not execute " msgstr "Ðе удалоÑÑŒ запуÑтить " msgid "True if strings are equal" msgstr "Строки равны" msgid "True if string contains substring" msgstr "ПодÑтрока Ñтроки" msgid "True if string matches regexp" msgstr "Ð¡Ð¾Ð²Ð¿Ð°Ð´ÐµÐ½Ð¸Ñ REGEXP" msgid "Logical AND" msgstr "ЛогичеÑкое И" msgid "Logical OR" msgstr "ЛогичеÑкое ИЛИ" msgid "Logical negation" msgstr "ЛогичеÑкого отрицаниÑ" msgid "Data" msgstr "Дата" msgid "%1 is not writable" msgstr "Файл %1 только Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ" msgid "All Supported Files" msgstr "Ð’Ñе поддерживаемые файлы" msgid "All Files" msgstr "Ð’Ñе файлы" msgid "Track" msgstr "Дорожка" msgid "Absolute path to file" msgstr "ÐбÑолютный путь" msgid "Duration" msgstr "ДлительноÑть" msgid "Title" msgstr "Ðазвание" msgid "Artist" msgstr "ИÑполнитель" msgid "Album" msgstr "Ðльбом" msgid "Comment" msgstr "Коментарий" msgid "Date" msgstr "Дата" msgid "Track Number" msgstr "Ðомер дорожки" msgid "Genre" msgstr "Жанр" msgid "Album Artist" msgstr "ИÑполнитель" msgid "Arranger" msgstr "Ðранжировщик" msgid "Author" msgstr "Ðвтор" msgid "BPM" msgstr "BPM" msgid "Catalog Number" msgstr "Ðомер каталога" msgid "Compilation" msgstr "Сборник" msgid "Composer" msgstr "Композитор" msgid "Conductor" msgstr "Дирижер" msgid "Copyright" msgstr "ÐвторÑкое право" msgid "Disc Number" msgstr "ЧиÑло диÑка" msgid "Encoded-by" msgstr "Кодировщик" msgid "Encoder Settings" msgstr "ÐаÑтройки кодеров" msgid "Encoding Time" msgstr "" msgid "Grouping" msgstr "Группирование" msgid "Initial Key" msgstr "" msgid "ISRC" msgstr "ISRC" msgid "Language" msgstr "Язык" msgid "Lyricist" msgstr "Ðвтор" msgid "Lyrics" msgstr "Lyrics" msgid "Media" msgstr "Медиа" msgid "Mood" msgstr "ÐаÑтроение" msgid "Original Album" msgstr "Первый альбом" msgid "Original Artist" msgstr "Первый иÑполнитель" msgid "Original Date" msgstr "Первый дата" msgid "Part" msgstr "ЧаÑть" msgid "Performer" msgstr "ИÑполнитель" msgid "Picture" msgstr "Картинка" msgid "Publisher" msgstr "ИздательÑтво" msgid "Release Country" msgstr "Страна релиза" msgid "Remixer" msgstr "ПеремикÑер" msgid "Sort Album" msgstr "Ðльбома при упорÑдочивании" msgid "Sort Album Artist" msgstr "ПорÑдок Ñортировки иÑполнителей альбомов" msgid "Sort Artist" msgstr "ИÑполнитель дорожки при упорÑдочивании" msgid "Sort Composer" msgstr "ПорÑдок композитор рода" msgid "Sort Name" msgstr "Ðазвание дорожки при упорÑдочивании" msgid "Subtitle" msgstr "Подзаголовок" msgid "Website" msgstr "Веб-Ñайта" msgid "WWW Audio File" msgstr "" msgid "WWW Audio Source" msgstr "" msgid "Number of tracks" msgstr "КоличеÑтво дорожек" msgid "Extension" msgstr "РаÑширение" msgid "Bitrate" msgstr "Битовый поток" msgid "VBR" msgstr "VBR" msgid "Samplerate" msgstr "ЧаÑтота диÑкретизации" msgid "Channels" msgstr "Каналы" msgid "Codec" msgstr "Кодек" msgid "Escape for HTML" msgstr "Escape HTML" msgid "Automatic Import" msgstr "ÐвтоматичеÑки импортировать" msgid "D&estination:" msgstr "М&еÑто назначениÑ:" msgid "Tag 1 and Tag 2" msgstr "Тег 1 и тег 2" msgid "&Profile:" msgstr "&Профиль:" msgid "&Save Settings" msgstr "&Сохранить наÑтройки" msgid "New" msgstr "ÐоваÑ" msgid "A&bort" msgstr "Прер&вать" msgid "S&tart" msgstr "Ðа&чало" msgid "Import Source" msgstr "Откуда добавить" msgid "&Server:" msgstr "&Сервер:" msgid "&Accuracy:" msgstr "&ТочноÑть:" msgid "&Standard Tags" msgstr "&Стандартный теги" msgid "&Additional Tags" msgstr "&Дополнительные теги" msgid "C&over Art" msgstr "&Обложки" msgid "Browse Cover Art" msgstr "Ðайти обложки" msgid "&Artist/Album" msgstr "ИÑполнитель/&Ðльбом" msgid "&Source" msgstr "&ИÑточник" msgid "Source:" msgstr "ИÑточник:" msgid "URL:" msgstr "URL:" msgid "&URL extraction" msgstr "Замена &URL" msgid "Match" msgstr "СоÑÑ‚Ñзание" msgid "Picture URL" msgstr "Картинка URL" msgid "&Browse" msgstr "&Браузер" msgid "Click Browse to start" msgstr "" msgid "Then drag the picture from the browser to Kid3." msgstr "" msgid "ID3v1" msgstr "ID3v1" msgid "&Mark truncated fields" msgstr "&Отметить обрезанные Ñтруктуры" msgid "Text &encoding:" msgstr "Кодировка Ñ‚&екÑта:" msgid "ID3v2" msgstr "ID3v2" msgid "Use &track/total number of tracks format" msgstr "Формат иÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ &чиÑло/количеÑтво дорожек" msgid "Track number &digits:" msgstr "Ðомер дорожки &количеÑтво цифр:" msgid "&Genre as text instead of numeric string" msgstr "&Жанр как текÑÑ‚" msgid "ISO-8859-1" msgstr "ISO-8859-1" msgid "UTF16" msgstr "UTF16" msgid "UTF8" msgstr "UTF8" msgid "&Version used for new tags:" msgstr "&ВерÑиÑ, иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÐµÐ¼Ð°Ñ Ð´Ð»Ñ Ð½Ð¾Ð²Ð¾Ð³Ð¾ тегов:" msgid "ID3v2.3.0" msgstr "ID3v2.3.0" msgid "ID3v2.4.0" msgstr "ID3v2.4.0" msgid "Ogg/Vorbis" msgstr "Ogg/Vorbis" msgid "Co&mment field name:" msgstr "Ð˜Ð¼Ñ Ñтруктур ко&ментÑри:" msgid "&Picture field name:" msgstr "Ð˜Ð¼Ñ Ñтруктур &картинки:" msgid "Custom &Genres" msgstr "Ð’Ñ‹Ð±Ð¾Ñ€Ð¾Ñ‡Ð½Ð°Ñ &жанры" msgid "&Show only custom genres" msgstr "&Показывать только Ð²Ñ‹Ð±Ð¾Ñ€Ð¾Ñ‡Ð½Ð°Ñ Ð¶Ð°Ð½Ñ€Ñ‹" msgid "&Quick Access Tags" msgstr "Теги быÑтро&го доÑтупа" msgid "&Tag Format" msgstr "&Формат Ñ‚Ñга" msgid "Tag &1" msgstr "Тег &1" msgid "Tag &2" msgstr "Тег &2" msgid "Tag 1 a&nd Tag 2" msgstr "Тег 1 &и тег 2" msgid "Startup" msgstr "ЗапуÑк" msgid "&Load last-opened files" msgstr "&Загружать при запуÑке файлы, открытые при поÑледнем выходе" msgid "Save" msgstr "Сохранить" msgid "&Preserve file timestamp" msgstr "&СохранÑть Ð²Ñ€ÐµÐ¼Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñ„Ð°Ð¹Ð»Ð¾Ð²" msgid "&Mark changes" msgstr "&Отмечать изменениÑ" msgid "F&ilename for cover:" msgstr "Ð˜Ð¼Ñ Ñ„Ð°&йла Ð´Ð»Ñ Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ:" msgid "&Filename Format" msgstr "&Формат имени файла" msgid "Browser" msgstr "Браузер" msgid "Web &browser:" msgstr "Веб-&браузер:" msgid "Context &Menu Commands" msgstr "Элементы контекÑтного &меню" #, fuzzy msgid "&Play on double click" msgstr "&Играть на двойной клик" msgid "Proxy" msgstr "ПрокÑи" msgid "&Proxy:" msgstr "&ПрокÑи:" msgid "&Use authentication with proxy" msgstr "&ИÑпользовать авторизацию на прокÑи" msgid "Proxy user &name:" msgstr "&Ð˜Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð¿Ñ€Ð¾ÐºÑи:" msgid "Proxy pass&word:" msgstr "П&ароль прокÑи:" msgid "&Metadata Plugins && Priority" msgstr "&Метаданные раÑÑˆÐ¸Ñ€ÐµÐ½Ð¸Ñ && Приоритет" msgid "A&vailable Plugins" msgstr "&ДоÑтупные дополнениÑ" msgid "Changes take only effect after a restart!" msgstr "Ð˜Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð²ÑтупÑÑ‚ в Ñилу только поÑле Ñледующего запуÑка!" msgid "From Clip&board" msgstr "Из &Буфера Обмена" msgid "&Import" msgstr "&Импорт" msgid "&Export" msgstr "&ЭкÑпорт" msgid "&View" msgstr "&Вид" msgid "Text Encoding" msgstr "Кодировка ТекÑта" msgid "Text" msgstr "ТекÑÑ‚" msgid "Description" msgstr "ОпиÑание" msgid "Owner" msgstr "Владелец" msgid "Email" msgstr "Почта" msgid "Rating" msgstr "Рейтинг" msgid "Picture Type" msgstr "Тип Картинки" msgid "Image format" msgstr "Формат Картинки" msgid "Mimetype" msgstr "Тип MIME" msgid "Counter" msgstr "Счетчик" msgid "Identifier" msgstr "Идентификатор" msgid "Volume Adjustment" msgstr "Корректировка ГромкоÑти" msgid "Number of Bits" msgstr "КоличеÑтво Бит" msgid "Volume Change Right" msgstr "Изменить громкоÑть Справа" msgid "Volume Change Left" msgstr "Изменить громкоÑть Слева" msgid "Peak Volume Right" msgstr "МакÑÐ¸Ð¼Ð°Ð»ÑŒÐ½Ð°Ñ Ð³Ñ€Ð¾Ð¼ÐºÐ¾Ñть Справа" msgid "Peak Volume Left" msgstr "МакÑÐ¸Ð¼Ð°Ð»ÑŒÐ½Ð°Ñ Ð³Ñ€Ð¾Ð¼ÐºÐ¾Ñть Слева" msgid "Timestamp Format" msgstr "Формат отметки о времени" msgid "Content Type" msgstr "Тип Содержимого" msgid "Price" msgstr "Цена" msgid "Seller" msgstr "Продавец" msgid "Images" msgstr "ИзображениÑ" msgid "UTF16BE" msgstr "UTF16BE" msgid "Other" msgstr "Другое" msgid "32x32 pixels PNG file icon" msgstr "Иконка PNG-файла 32x32 точки" msgid "Other file icon" msgstr "Ð”Ñ€ÑƒÐ³Ð°Ñ Ð˜ÐºÐ¾Ð½ÐºÐ° файла" msgid "Cover (front)" msgstr "Оболочка (Спереди)" msgid "Cover (back)" msgstr "Оболочка (Сзади)" msgid "Leaflet page" msgstr "ЛиÑÑ‚ Брошюры" msgid "Lead artist/lead performer/soloist" msgstr "Ведущий ИÑполнитель/СолиÑÑ‚" msgid "Artist/performer" msgstr "ИÑполнитель" msgid "Band/Orchestra" msgstr "Группа/ОркеÑтр" msgid "Lyricist/text writer" msgstr "Ðвтор текÑта" msgid "Recording Location" msgstr "МеÑто запиÑи" msgid "During recording" msgstr "ДлительноÑть запиÑи" msgid "During performance" msgstr "ДлительноÑть иÑполнениÑ" msgid "Movie/video screen capture" msgstr "Movie/video screen capture" msgid "A bright coloured fish" msgstr "A bright coloured fish" msgid "Illustration" msgstr "ИллюÑтрациÑ" msgid "Band/artist logotype" msgstr "Логотип группы/иÑпольнителÑ" msgid "Publisher/Studio logotype" msgstr "Логотип Ñтудии запиÑи" msgid "MPEG frames as unit" msgstr "ИзмерÑть в MPEG-кадрах" msgid "Milliseconds as unit" msgstr "ИзмерÑть в милиÑекундах" msgid "Text transcription" msgstr "ЗапиÑÑŒ текÑта" msgid "Movement/part name" msgstr "Movement/part name" msgid "Events" msgstr "СобытиÑ" msgid "Chord" msgstr "Ðккорд" msgid "Trivia/pop up" msgstr "Trivia/pop up" msgid "Export" msgstr "ЭкÑпорт" msgid "Format:" msgstr "Формат:" msgid "Header:" msgstr "Заголовок:" msgid "Tracks:" msgstr "Дорожки:" msgid "Footer:" msgstr "СноÑка:" msgid "To F&ile" msgstr "Ð’ фа&йл" msgid "To Clip&board" msgstr "Ð’ &Буфера Обмена" msgid "&Source:" msgstr "&ИÑточник:" msgid "File Error" msgstr "Ошибка файла" msgid "&Filter:" msgstr "&Фильтр:" msgid "&Expression:" msgstr "&Выражение:" msgid "&Apply" msgstr "&Применить" msgid "Accuracy:" msgstr "ТочноÑть:" msgid "Cover Art:" msgstr "Обложки:" msgid "From F&ile/Clipboard..." msgstr "Из файл или &буфер обмена..." msgid "From T&ags..." msgstr "Из Ñ‚&ег..." msgid "&From Server:" msgstr "&Из Ñервер:" msgid "Check maximum allowable time &difference (sec):" msgstr "Проверить макÑимальную допуÑтимую &разницу во времени (Ñекунды):" msgid "Match with:" msgstr "СоÑÑ‚Ñзание Ñ:" msgid "&Length" msgstr "&ДлительноÑть" msgid "T&rack" msgstr "&Дорожка" msgid "&Title" msgstr "&Ðазвание" msgid "&Start number:" msgstr "&ЧиÑло начала:" msgid "&Destination:" msgstr "М&еÑто назначениÑ:" msgid "&Total number of tracks:" msgstr "&Общее КоличеÑтво дорожек:" msgid "Create Playlist" msgstr "Cоздать ÑпиÑок пеÑен" msgid "Playlist File Name" msgstr "Ð˜Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð° Ñо ÑпиÑком пеÑен" msgid "Same as &directory name" msgstr "Совпадает Ñ Ð¸Ð¼ÐµÐ½ÐµÐ¼ &каталога" msgid "&Format:" msgstr "&Формат:" msgid "Cr&eate in:" msgstr "Создать &в:" msgid "Current directory" msgstr "Текущий каталог" msgid "Every directory" msgstr "Каждом каталоге" msgid "Top-level directory" msgstr "Каталоге верхнего уровнÑ" msgid "Playlist Content" msgstr "Содержимое ÑпиÑка запиÑей" msgid "For&mat:" msgstr "Фо&рмат:" msgid "Incl&ude only the selected files" msgstr "Вклю&чать только выделенные файлы" msgid "Sort by file &name" msgstr "Сортировать Ñлементы по &имени файла" msgid "Sort by &tag field" msgstr "Сортировать Ñлементы по полю &тега" msgid "Use &relative path for files in playlist" msgstr "ИÑпользовать &отноÑительный путь Ð´Ð»Ñ Ñ„Ð°Ð¹Ð»Ð¾Ð² в ÑпиÑке запиÑей" msgid "Use full p&ath for files in playlist" msgstr "ИÑпользовать &абÑолютный путь Ð´Ð»Ñ Ñ„Ð°Ð¹Ð»Ð¾Ð² в ÑпиÑке запиÑей" msgid "Write only &list of files" msgstr "ЗапиÑать только &ÑпиÑок файлов" msgid "Write &info using" msgstr "ЗапиÑать &информацию иÑпользуÑ" msgid "Preview" msgstr "ПроÑмотр" msgid "Create Directory" msgstr "Создать Каталог" msgid "From Tag 2 and Tag 1" msgstr "Из тег 2 и тег 1" msgid "From:" msgstr "От:" msgid "To:" msgstr "Ðа:" msgid "&Find" msgstr "&Ðайти" msgid "C&GI Path:" msgstr "Путь к C&GI:" msgid "Track Title/Artist - Album" msgstr "ЧиÑло Ðазвание/ИÑполнитель - Ðльбом" msgid "State" msgstr "СоÑтоÑние" msgid "No result" msgstr "Ðе удалоÑÑŒ найти" msgid "No result selected" msgstr "Ðикакой отобранный результат" msgid "Import from Tags" msgstr "Импорт из тег" msgid "Extraction:" msgstr "Добыча:" msgid "Import from File/Clipboard" msgstr "Импортировать файл или буфер обмена" msgid "From F&ile" msgstr "Из имени фа&йла" msgid "Download" msgstr "Закачка" msgid "Opening directory..." msgstr "Открытие каталога..." msgid "Saving directory..." msgstr "Сохранение каталога..." msgid "" "The current directory has been modified.\n" "Do you want to save it?" msgstr "" "Текущий каталог был изменен.\n" "Хотите Ñохранить его?" msgid "Warning" msgstr "Предупреждение" msgid "Exiting..." msgstr "Выход..." msgid "Creating playlist..." msgstr "Создание ÑпиÑка пеÑен..." msgid "Import" msgstr "Импорт" msgid "Error while renaming:\n" msgstr "Ошибка переименование:\n" msgid " [filtered]" msgstr " [фильтровано]" msgid "Add Frame" msgstr "Добавить Фрейм" msgid "Select the frame ID" msgstr "Выбрать ID фрейма" msgid "Rename File" msgstr "Переименовать файл" msgid "Enter new file name:" msgstr "Введите новое Ð¸Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð°:" msgid "Do you really want to move these %1 items to the trash?" msgstr "Ð’Ñ‹ дейÑтвительно хотите перемеÑтить %1 Ñлементов в корзину?" msgid "Do you really want to move this item to the trash?" msgstr "ПеремеÑтить в корзину?" msgid "Move to Trash" msgstr "ВыброÑить в корзину" msgid "Directory must be empty.\n" msgstr "Каталог должен быть пуÑтым.\n" msgid "Could not move these files to the Trash" msgstr "Ðе удалоÑÑŒ перемеÑтить Ñлементы в корзину" msgid "Expand All" msgstr "Развернуть вÑÑ‘" msgid "&Expand all" msgstr "&Развернуть вÑÑ‘" msgid "&Collapse all" msgstr "&Свернуть вÑÑ‘" msgid "&Rename" msgstr "&Переименовать" msgid "&Move to Trash" msgstr "&ВыброÑить в корзину" msgid "Kid3" msgstr "Kid3" msgid "F&ile" msgstr "Ф&айлы" msgid "Name:" msgstr "ИмÑ:" msgid "Filename from Tag 1" msgstr "Ð˜Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð° из тег 1" msgid "Filename from Tag 2" msgstr "Ð˜Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð° из тег 2" msgid "Tag 1 from Filename" msgstr "Тег 1 из имени файла" msgid "Tag 2 from Filename" msgstr "Тег 1 из имени файла" msgid "&Add..." msgstr "&Добавить..." msgid "Move &Up" msgstr "ПеремеÑтить вв&ерх" msgid "Move &Down" msgstr "ПеремеÑтить &вниз" msgid "&Edit..." msgstr "&Изменить..." msgid "&Remove" msgstr "&Удалить" msgid "&Insert row" msgstr "&Ð’Ñтавить Ñтроку" msgid "&Delete row" msgstr "&Удалить Ñтроку" msgid "&Clear row" msgstr "&ОчиÑтить Ñтроку" msgid "Format while editing" msgstr "Форматировать при редактировании" msgid "Case conversion:" msgstr "Преобразование РегиÑтра:" msgid "No changes" msgstr "Ðет изменений" msgid "All lowercase" msgstr "Ð’Ñе в нижний региÑтр" msgid "All uppercase" msgstr "Ð’Ñе в верхний региÑтр" msgid "First letter uppercase" msgstr "ÐŸÐµÑ€Ð²Ð°Ñ Ð±ÑƒÐºÐ²Ð° в верхний региÑтр" msgid "All first letters uppercase" msgstr "Ð’Ñе первые буквы в верхний региÑтр" msgid "Locale:" msgstr "Локаль:" msgid "None" msgstr "Ðет" msgid "String replacement:" msgstr "Замена Ñтрок:" msgid "From" msgstr "От" msgid "To" msgstr "к" msgid "&Add" msgstr "&Добавить" msgid "&Select all" msgstr "Выбрать в&Ñе" msgid "&Deselect all" msgstr "Отменить веÑÑŒ вы&бор" msgid "View Picture" msgstr "ПроÑмотр Картинки" msgid "" "Drag album\n" "artwork\n" "here" msgstr "" "Перетащите\n" "Ñюда обложку\n" "альбома" msgid "Play/Pause" msgstr "Играть/Пауза" msgid "Stop playback" msgstr "ОÑтановить проигрывание" msgid "Previous Track" msgstr "ÐŸÑ€ÐµÐ´Ñ‹Ð´ÑƒÑ‰Ð°Ñ Ð´Ð¾Ñ€Ð¾Ð¶ÐºÐ°" msgid "Next Track" msgstr "Ð¡Ð»ÐµÐ´ÑƒÑŽÑ‰Ð°Ñ Ð´Ð¾Ñ€Ð¾Ð¶ÐºÐ°" msgid "Close" msgstr "Закрыть" msgid "Mute" msgstr "Отключить звук" msgid "Volume: %1%" msgstr "ГромкоÑть: %1%" msgid "Add Item" msgstr "Добавить Ñлемент" msgid "Edit Item" msgstr "Изменить Ñлемент" msgid "MusicBrainz Fingerprint" msgstr "MusicBrainz \"отпечатка пальцев\"" msgid "Unrecognized" msgstr "Ðепризнанный" msgid "Recognized" msgstr "Признанный" msgid "User Selection" msgstr "Выбор" msgid "ID Lookup" msgstr "ПоиÑк ID" msgid "Fingerprint" msgstr "Отпечатка пальцев" msgid "Metadata Lookup" msgstr "Чтение метаданных" msgid "Amazon" msgstr "Amazon" msgid "Discogs" msgstr "Discogs" msgid "gnudb.org" msgstr "gnudb.org" msgid "TrackType.org" msgstr "TrackType.org" msgid "AENC - Audio encryption" msgstr "AENC - Ðудио-кодирование" msgid "APIC - Attached picture" msgstr "APIC - ÐŸÑ€Ð¸ÐºÑ€ÐµÐ¿Ð»ÐµÐ½Ð½Ð°Ñ ÐºÐ°Ñ€Ñ‚Ð¸Ð½ÐºÐ°" msgid "COMM - Comments" msgstr "COMM - Комментарии" msgid "COMR - Commercial" msgstr "COMR - Реклама" msgid "ENCR - Encryption method registration" msgstr "ENCR - Encryption method registration" msgid "EQUA - Equalization" msgstr "EQUA - Equalization" msgid "ETCO - Event timing codes" msgstr "ETCO - Event timing codes" msgid "GEOB - General encapsulated object" msgstr "GEOB - General encapsulated object" msgid "GRID - Group identification registration" msgstr "GRID - Group identification registration" msgid "IPLS - Involved people list" msgstr "IPLS - Involved people list" msgid "LINK - Linked information" msgstr "LINK - Linked information" msgid "MCDI - Music CD identifier" msgstr "MCDI - Music CD identifier" msgid "MLLT - MPEG location lookup table" msgstr "MLLT - MPEG location lookup table" msgid "OWNE - Ownership frame" msgstr "OWNE - Ownership frame" msgid "PRIV - Private frame" msgstr "PRIV - Private frame" msgid "PCNT - Play counter" msgstr "PCNT - Play counter" msgid "POPM - Popularimeter" msgstr "POPM - Popularimeter" msgid "POSS - Position synchronisation frame" msgstr "POSS - Position synchronisation frame" msgid "RBUF - Recommended buffer size" msgstr "RBUF - Recommended buffer size" msgid "RVAD - Relative volume adjustment" msgstr "RVAD - Relative volume adjustment" msgid "RVRB - Reverb" msgstr "RVRB - Reverb" msgid "SYLT - Synchronized lyric/text" msgstr "SYLT - Synchronized lyric/text" msgid "SYTC - Synchronized tempo codes" msgstr "SYTC - Synchronized tempo codes" msgid "TALB - Album/Movie/Show title" msgstr "TALB - Album/Movie/Show title" msgid "TBPM - BPM (beats per minute)" msgstr "TBPM - BPM (beats per minute)" msgid "TCOM - Composer" msgstr "TCOM - Composer" msgid "TCON - Content type" msgstr "TCON - Content type" msgid "TCOP - Copyright message" msgstr "TCOP - Copyright message" msgid "TDAT - Date" msgstr "TDAT - Date" msgid "TDLY - Playlist delay" msgstr "TDLY - Playlist delay" msgid "TENC - Encoded by" msgstr "TENC - Encoded by" msgid "TEXT - Lyricist/Text writer" msgstr "TEXT - Lyricist/Text writer" msgid "TFLT - File type" msgstr "TFLT - File type" msgid "TIME - Time" msgstr "TIME - Time" msgid "TIT1 - Content group description" msgstr "TIT1 - Content group description" msgid "TIT2 - Title/songname/content description" msgstr "TIT2 - Title/songname/content description" msgid "TIT3 - Subtitle/Description refinement" msgstr "TIT3 - Subtitle/Description refinement" msgid "TKEY - Initial key" msgstr "TKEY - Initial key" msgid "TLAN - Language(s)" msgstr "TLAN - Язык(и)" msgid "TLEN - Length" msgstr "TLEN - Длина" msgid "TMED - Media type" msgstr "TMED - Тип ноÑителÑ" msgid "TOAL - Original album/movie/show title" msgstr "TOAL - Original album/movie/show title" msgid "TOFN - Original filename" msgstr "TOFN - Original filename" msgid "TOLY - Original lyricist(s)/text writer(s)" msgstr "TOLY - Original lyricist(s)/text writer(s)" msgid "TOPE - Original artist(s)/performer(s)" msgstr "TOPE - Original artist(s)/performer(s)" msgid "TORY - Original release year" msgstr "TORY - Original release year" msgid "TOWN - File owner/licensee" msgstr "TOWN - File owner/licensee" msgid "TPE1 - Lead performer(s)/Soloist(s)" msgstr "TPE1 - Lead performer(s)/Soloist(s)" msgid "TPE2 - Band/orchestra/accompaniment" msgstr "TPE2 - Band/orchestra/accompaniment" msgid "TPE3 - Conductor/performer refinement" msgstr "TPE3 - Conductor/performer refinement" msgid "TPE4 - Interpreted, remixed, or otherwise modified by" msgstr "TPE4 - Interpreted, remixed, or otherwise modified by" msgid "TPOS - Part of a set" msgstr "TPOS - Part of a set" msgid "TPUB - Publisher" msgstr "TPUB - Publisher" msgid "TRCK - Track number/Position in set" msgstr "TRCK - Track number/Position in set" msgid "TRDA - Recording dates" msgstr "TRDA - Recording dates" msgid "TRSN - Internet radio station name" msgstr "TRSN - Internet radio station name" msgid "TRSO - Internet radio station owner" msgstr "TRSO - Internet radio station owner" msgid "TSIZ - Size" msgstr "TSIZ - Size" msgid "TSRC - ISRC (international standard recording code)" msgstr "TSRC - ISRC (international standard recording code)" msgid "TSSE - Software/Hardware and settings used for encoding" msgstr "TSSE - Software/Hardware and settings used for encoding" msgid "TXXX - User defined text information" msgstr "TXXX - User defined text information" msgid "TYER - Year" msgstr "TYER - Year" msgid "UFID - Unique file identifier" msgstr "UFID - Unique file identifier" msgid "USER - Terms of use" msgstr "USER - Terms of use" msgid "USLT - Unsynchronized lyric/text transcription" msgstr "USLT - Unsynchronized lyric/text transcription" msgid "WCOM - Commercial information" msgstr "WCOM - Commercial information" msgid "WCOP - Copyright/Legal information" msgstr "WCOP - Copyright/Legal information" msgid "WOAF - Official audio file webpage" msgstr "WOAF - Official audio file webpage" msgid "WOAR - Official artist/performer webpage" msgstr "WOAR - Official artist/performer webpage" msgid "WOAS - Official audio source webpage" msgstr "WOAS - Official audio source webpage" msgid "WORS - Official internet radio station homepage" msgstr "WORS - Official internet radio station homepage" msgid "WPAY - Payment" msgstr "WPAY - Payment" msgid "WPUB - Official publisher webpage" msgstr "WPUB - Official publisher webpage" msgid "WXXX - User defined URL link" msgstr "WXXX - User defined URL link" msgid "MusicBrainz Release" msgstr "MusicBrainz альбом" msgid "ASPI - Audio seek point index" msgstr "ASPI - Audio seek point index" msgid "EQU2 - Equalisation (2)" msgstr "EQU2 - Equalisation (2)" msgid "RVA2 - Relative volume adjustment (2)" msgstr "RVA2 - Relative volume adjustment (2)" msgid "SEEK - Seek frame" msgstr "SEEK - Seek frame" msgid "SIGN - Signature frame" msgstr "SIGN - Signature frame" msgid "TCMP - iTunes compilation flag" msgstr "TCMP - iTunes Ñборник" msgid "TDEN - Encoding time" msgstr "TDEN - Encoding time" msgid "TDOR - Original release time" msgstr "TDOR - Original release time" msgid "TDRC - Recording time" msgstr "TDRC - Recording time" msgid "TDRL - Release time" msgstr "TDRL - Release time" msgid "TDTG - Tagging time" msgstr "TDTG - Tagging time" msgid "TIPL - Involved people list" msgstr "TIPL - Involved people list" msgid "TMCL - Musician credits list" msgstr "TMCL - Musician credits list" msgid "TMOO - Mood" msgstr "TMOO - Mood" msgid "TPRO - Produced notice" msgstr "TPRO - Produced notice" msgid "TSO2 - Album artist sort order" msgstr "TSO2 - ПорÑдок Ñортировки иÑполнителей альбомов" msgid "TSOA - Album sort order" msgstr "TSOA - Album sort order" msgid "TSOC - Composer sort order" msgstr "TSOC - порÑдок композитор рода" msgid "TSOP - Performer sort order" msgstr "TSOP - Performer sort order" msgid "TSOT - Title sort order" msgstr "TSOT - Title sort order" msgid "TSST - Set subtitle" msgstr "TSST - Set subtitle" �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/po/sr.po���������������������������������������������������������������������������������0000664�0000000�0000000�00000120515�12246035113�0013751�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Kid3 Serbian Translation. # Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012. Free Software Foundation, Inc. # # Urs Fleisch <ufleisch@users.sourceforge.net>, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012. # Mladen Pejaković <pejakm@gmail.com>, 2012. msgid "" msgstr "" "Project-Id-Version: Kid3 3.0.2\n" "Report-Msgid-Bugs-To: http://sourceforge.net/p/kid3/bugs/\n" "POT-Creation-Date: 2013-09-11 21:22+0200\n" "PO-Revision-Date: 2011-04-26 23:41+0200\n" "Last-Translator: Mladen Pejaković <pejakm@gmail.com>\n" "Language-Team: Serbian <kde-i18n-sr@lists.kde.org>\n" "Language: sr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Lokalize 0.3\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" "X-Language: sr\n" "X-Source-Language: C\n" msgid "Timeout" msgstr "Прековреме" msgid "Usage:" msgstr "Употреба:" msgid "Help" msgstr "Помоћ" msgid "Command name" msgstr "Ðаредба" msgid "Quit application" msgstr "Ðапушта апликацију" msgid "The current directory has been modified." msgstr "Тренутни директоријум је измењен." msgid "Type 'exit force' to quit." msgstr "УнеÑите 'exit force' за излаз." msgid "Change directory" msgstr "" msgid "%1 does not exist" msgstr "%1 не поÑтоји" msgid "Print the filename of the current working directory" msgstr "Штампај пуно име фајла текућег активног директоријума" msgid "Directory list" msgstr "СпиÑак фаÑцикли" msgid "Saves the changed files" msgstr "УпиÑује измене фајлова" msgid "Error while writing file:\n" msgstr "Грешка приликом упиÑа фајла:\n" msgid "Select file" msgstr "Изабери фајл" msgid "%1 not found" msgstr "%1 није пронађено" msgid "Select tag" msgstr "Избор ознаке" msgid "Get tag frame" msgstr "" msgid "Set tag frame" msgstr "" msgid "Could not set \"%1\" for %2" msgstr "Ðе могу да поÑтавим \"%1\" за %2" msgid "Revert" msgstr "Врати" msgid "Import from file or clipboard" msgstr "Увези из фајла или клипборда" msgid "Format name" msgstr "Формат имена" msgid "%1 not found." msgstr "%1 није нађен." msgid "Available" msgstr "ДоÑтупно" msgid "Error" msgstr "Грешка" msgid "Automatic import" msgstr "ÐутоматÑки увози" msgid "Profile name" msgstr "Име поÑтавке" msgid "Reading Directory" msgstr "Приликом читања директоријума" msgid "Started" msgstr "Започет" msgid "Source" msgstr "Извор" msgid "Querying" msgstr "Затражујем" msgid "Fetching" msgstr "Добављам" msgid "Data received" msgstr "Подаци примљени" msgid "Cover" msgstr "Омот" msgid "Finished" msgstr "Завршено" msgid "Aborted" msgstr "ОбуÑтављено" msgid "Download album cover artwork" msgstr "Потражи Ñлику омота албума" msgid "Export to file or clipboard" msgstr "Извези у фајл или клипборд" msgid "Create playlist" msgstr "Ðаправи лиÑту нумера" msgid "Apply filename format" msgstr "Примени формат назива фајла" msgid "Apply tag format" msgstr "Примени формат ознаке" msgid "Apply text encoding" msgstr "Примени кодирање текÑта" msgid "Rename directory" msgstr "Преименуј директоријум" msgid "Number tracks" msgstr "Ðумериши" msgid "Track number" msgstr "Број нумере" msgid "Filter" msgstr "Филтер" msgid "Filter name" msgstr "Име фајла" msgid "Convert ID3v2.3 to ID3v2.4" msgstr "Претвори ИД3в2.3 у ИД3в2.4" msgid "Convert ID3v2.4 to ID3v2.3" msgstr "Претвори ИД3в2.4 у ИД3в2.3" msgid "Filename from tag" msgstr "ознаке у име фајла" msgid "Tag from filename" msgstr "ознаку из имена фајла" msgid "Tag to other tag" msgstr "" msgid "Copy" msgstr "Копирај" msgid "Paste" msgstr "Ðалепи" msgid "Remove" msgstr "Уклони" msgid "Play" msgstr "Репродукција" msgid "Parameter" msgstr "Параметар" msgid "File path" msgstr "Путања фајла" msgid "URL" msgstr "УРЛ" msgid "Tag numbers" msgstr "Број нумере" msgid "Frame name" msgstr "Из имена фајла" msgid "Frame value" msgstr "" msgid "Format" msgstr "Формат" msgid "Command specific" msgstr "Ðаредба" msgid "Available Commands" msgstr "Ðаредбе на раÑполагању" msgid "File" msgstr "Фајл" msgid "Name" msgstr "Име" msgid "Tag 2" msgstr "ознака 2" msgid "Tag 1" msgstr "ознака 1" msgid "Tags" msgstr "Ознаке" msgid "Unknown command '%1'. Type 'help' for help." msgstr "Ðепозната наредба '%1'. КориÑтите 'help' да биÑте добили помоћ." msgid "Unknown command '%1', -h for help." msgstr "Ðепозната наредба '%1', -h за помоћ." msgid "Files" msgstr "Фајлови" msgid "User Actions" msgstr "Радње кориÑника" msgid "Network" msgstr "Мрежа" msgid "Plugins" msgstr "Прикључци" msgid "Main Toolbar" msgstr "Главна трака" msgid "Opens a directory" msgstr "Отвара директоријум" msgid "Opens a recently used directory" msgstr "Отвара недавно коришћен директоријум" msgid "Reverts the changes of all or the selected files" msgstr "Поништава измене Ñвих или изабраних фајлова" msgid "Quits the application" msgstr "Ðапушта апликацију" msgid "Select all files" msgstr "Изабери Ñве фајлове" msgid "Deselect all files" msgstr "Поништи избор Ñвих фајлова" msgid "Configure Shortcuts" msgstr "ПодеÑи пречице" msgid "Configure Toolbars" msgstr "ПодеÑи траке алата" msgid "Preferences dialog" msgstr "Дијалог подешавања" msgid "O&pen Directory..." msgstr "О&твори директоријум..." msgid "&Import..." msgstr "&Увези..." msgid "Import from %1..." msgstr "Увези Ñа %1..." msgid "Automatic I&mport..." msgstr "Ðуто&матÑки увози..." msgid "&Browse Cover Art..." msgstr "Потражи о&мот албума..." msgid "&Export..." msgstr "Изв&ези..." msgid "&Create Playlist..." msgstr "Ðаправи &лиÑту нумера..." msgid "Apply &Filename Format" msgstr "Примени формат имена фа&јла" msgid "Apply &Tag Format" msgstr "Примени формат &ознаке" msgid "Apply Text &Encoding" msgstr "Примени кодирање Ñ‚&екÑта" msgid "&Rename Directory..." msgstr "Преименуј &директоријум..." msgid "&Number Tracks..." msgstr "Ðу&мериши..." msgid "F&ilter..." msgstr "Ф&илтер..." msgid "Convert ID3v2.3 to ID3v2.&4" msgstr "Претвори ИД3в2.3 у ИД3в2.&4" msgid "Convert ID3v2.4 to ID3v2.&3" msgstr "Претвори ИД3в2.4 у ИД3в2.&3" msgid "&Play" msgstr "Ре&продукуј" msgid "Show &Picture" msgstr "Прикажи Ñ&лику" msgid "Auto &Hide Tags" msgstr "ÐутоматÑки &Ñакриј ознаке" msgid "Select All in &Directory" msgstr "Изабери Ñве фајлове у тренутном &директоријуму" msgid "&Previous File" msgstr "&Претходни фајл" msgid "&Next File" msgstr "&Следећи фајл" msgid "From Filename" msgstr "Из имена фајла" msgid "From Tag 2" msgstr "Из ознаке 2" msgid "From Tag 1" msgstr "Из ознаке 1" msgid "Frames:" msgstr "Кадрови:" msgid "Edit" msgstr "Уреди" msgid "Add" msgstr "Додај" msgid "Delete" msgstr "Обриши" msgid "Filename" msgstr "Име фајла" msgid "Focus" msgstr "ФокуÑ" msgid "File List" msgstr "СпиÑак фајлова" msgid "Directory List" msgstr "СпиÑак фаÑцикли" msgid "Configure - Kid3" msgstr "Подешавање - Кид3" msgid "Open" msgstr "Отвори" msgid "Save As" msgstr "Сачувај као" msgid "directory to open" msgstr "директоријум за отварање" msgid "Kid3 ID3 Tagger" msgstr "Кид3 уређивач ИД3 ознака" #. i18n NAME OF TRANSLATORS msgid "Your names" msgstr "Младен Пејаковић" #. i18n EMAIL OF TRANSLATORS msgid "Your emails" msgstr "pejakm@gmail.com" msgid "&Back" msgstr "&Ðазад" msgid "&Forward" msgstr "Ðа&пред" msgid "&Find:" msgstr "&Ðађи:" msgid "Find Previous" msgstr "Ðађи претходно" msgid "Find Next" msgstr "Ðађи Ñледеће" msgid "&Close" msgstr "&Затвори" msgid "&Tags" msgstr "&Ознаке" msgid "&Files" msgstr "&Фајлови" msgid "&User Actions" msgstr "Радње &кориÑника" msgid "&Network" msgstr "&Мрежа" msgid "&Plugins" msgstr "&Прикључци" msgid "&Keyboard Shortcuts" msgstr "Пречице &таÑтатуре" msgid "Use custom app&lication font" msgstr "КориÑти поÑе&бни фонт за апликацију" msgid "A&pplication Font..." msgstr "&Фонт апликације..." msgid "Use custom application &style" msgstr "КориÑти поÑебни &Ñтил за апликацију" msgid "Unknown" msgstr "Ðепознато" msgid "Use native system file &dialogs" msgstr "КориÑти ÑиÑтемÑке &дијалоге фајлова" msgid "&Appearance" msgstr "&Изглед" msgid "&Help" msgstr "По&моћ" msgid "&OK" msgstr "&У реду" msgid "&Cancel" msgstr "&ОдуÑтани" msgid "The keyboard shortcut '%1' is already assigned to '%2'." msgstr "Пречица таÑтатуре '%1' је већ додељена '%2'." msgid "&File" msgstr "&Фајл" msgid "&Open..." msgstr "&Отвори..." msgid "Open &Recent" msgstr "Отвори &недавно" msgid "&Save" msgstr "&Сачувај" msgid "Re&vert" msgstr "&Врати" msgid "Import from %1" msgstr "Увези Ñа %1" msgid "Browse album cover artwork" msgstr "Потражи Ñлику омота албума" msgid "Create M3U Playlist" msgstr "Ðаправи М3У лиÑту нумера" msgid "&Quit" msgstr "&ÐапуÑти" msgid "&Edit" msgstr "&Уреди" msgid "Select &All" msgstr "Изабери Ñ&ве" msgid "Dese&lect" msgstr "По&ништи избор" msgid "Select all files in the current directory" msgstr "Изабери Ñве фајлове у тренутном директоријуму" msgid "Select previous file" msgstr "Изабери претходни фајл" msgid "Select next file" msgstr "Изабери Ñледећи фајл" msgid "&Tools" msgstr "&Ðлатке" msgid "Apply Filename Format" msgstr "Примени формат назива фајла" msgid "Apply Tag Format" msgstr "Примени формат ознаке" msgid "Apply Text Encoding" msgstr "Примени кодирање текÑта" msgid "Rename Directory" msgstr "Преименуј директоријум" msgid "Number Tracks" msgstr "Ðумериши" msgid "&Settings" msgstr "&Подешавање" msgid "Enables/disables the toolbar" msgstr "Приказује/Ñакрива траку алата" msgid "Show &Toolbar" msgstr "Прикажи траку &алата" msgid "Enables/disables the statusbar" msgstr "Приказује/Ñакрива траку Ñтања" msgid "Show St&atusbar" msgstr "Прикажи траку &Ñтања" msgid "Show Picture" msgstr "Прикажи Ñлику" msgid "Auto Hide Tags" msgstr "ÐутоматÑки Ñакриј ознаке" msgid "Configure Kid3" msgstr "ПодеÑи Кид3" msgid "&Configure Kid3..." msgstr "&ПодеÑи Кид3..." msgid "Kid3 Handbook" msgstr "Кид3 приручник" msgid "Kid3 &Handbook" msgstr "Кид3 п&риручник" msgid "About Kid3" msgstr "О Кид3-у" msgid "&About Kid3" msgstr "&О Кид3-у" msgid "About Qt" msgstr "О КјуТ-у" msgid "About &Qt" msgstr "О &КјуТ-у" msgid " [modified]" msgstr " [измењен]" msgid "Toggle the statusbar..." msgstr "Прикажи/Ñакриј траку Ñтања..." msgid "Ready." msgstr "Спреман." msgid "&Clear List" msgstr "О&чиÑти ÑпиÑак" msgid "Clear" msgstr "ОчиÑти" msgid "Reset" msgstr "Поново поÑтави" msgid "Press F2 or double click to edit cell contents." msgstr "" msgid "Action" msgstr "Радња" msgid "Shortcut" msgstr "Пречица" msgid "Server" msgstr "Сервер" msgid "Accuracy" msgstr "ПрецизноÑÑ‚" msgid "Standard Tags" msgstr "Стандардне ознаке" msgid "Additional Tags" msgstr "Додатне ознаке" msgid "Cover Art" msgstr "Омот албума" msgid "Invalid File" msgstr "ÐеиÑправна датотека" msgid "Data received: %1" msgstr "Подаци примљени: %1" msgid "Request sent..." msgstr "Захтев поÑлат..." msgid "Year" msgstr "Година" msgid "Length" msgstr "Трајање" msgid "Filenames" msgstr "Имена фајлова" msgid "URLs" msgstr "УРЛ-ови" msgid "Directory name" msgstr "Име директоријума" msgid "Encode as URL" msgstr "Кодирај као УРЛ" msgid "Confirm" msgstr "Потврди" msgid "Output" msgstr "Излаз" msgid "Command" msgstr "Ðаредба" msgid "Create directory %1 failed\n" msgstr "Прављење директоријума %1 није уÑпело\n" msgid "File %1 already exists\n" msgstr "Фајл %1 већ поÑтоји\n" msgid "%1 is not a directory\n" msgstr "%1 није директоријум\n" msgid "Rename %1 to %2 failed\n" msgstr "Преименовање %1 у %2 није уÑпело\n" msgid "%1 already exists\n" msgstr "%1 већ поÑтоји\n" msgid "%1 is not a file\n" msgstr "%1 није фајл\n" msgid "New directory name is too different\n" msgstr "Ðово име директоријума је превише различито\n" msgid "Create directory" msgstr "Ðаправи директоријум" msgid "Rename file" msgstr "Преименуј фајл" msgid "C&lear" msgstr "О&чиÑти" msgid "Execute " msgstr "Изврши " msgid "Could not execute " msgstr "Ðе могу да извршим " msgid "True if strings are equal" msgstr "Тачно ако Ñу низови једнаки" msgid "True if string contains substring" msgstr "Тачно ако низ Ñадржи подниз" msgid "True if string matches regexp" msgstr "Тачно ако Ñе низ поклапа Ñа регизом" msgid "Logical AND" msgstr "Логичко И" msgid "Logical OR" msgstr "Логичко ИЛИ" msgid "Logical negation" msgstr "Логичка негација" msgid "Data" msgstr "Подаци" msgid "%1 is not writable" msgstr "%1 Ñе не може пиÑати" msgid "All Supported Files" msgstr "Сви подржани фајлови" msgid "All Files" msgstr "Сви фајлови" msgid "Track" msgstr "Ðумера" msgid "Absolute path to file" msgstr "ÐпÑолутна путања до фајла" msgid "Duration" msgstr "Трајање" msgid "Title" msgstr "ÐаÑлов" msgid "Artist" msgstr "Извођач" msgid "Album" msgstr "Ðлбум" msgid "Comment" msgstr "Коментар" msgid "Date" msgstr "Година" msgid "Track Number" msgstr "Број нумере" msgid "Genre" msgstr "Жанр" msgid "Album Artist" msgstr "Извођач албума" msgid "Arranger" msgstr "Ðранжер" msgid "Author" msgstr "Ðутор" msgid "BPM" msgstr "БПМ" msgid "Catalog Number" msgstr "" msgid "Compilation" msgstr "" msgid "Composer" msgstr "Композитор" msgid "Conductor" msgstr "Диригент" msgid "Copyright" msgstr "ÐуторÑко право" msgid "Disc Number" msgstr "Број диÑка" msgid "Encoded-by" msgstr "Кодер" msgid "Encoder Settings" msgstr "Кодирање подешавања" msgid "Encoding Time" msgstr "Време кодирања" msgid "Grouping" msgstr "ГрупиÑање" msgid "Initial Key" msgstr "Почетни кључ" msgid "ISRC" msgstr "ИСРЦ" msgid "Language" msgstr "Језик" msgid "Lyricist" msgstr "ТекÑтопиÑац" msgid "Lyrics" msgstr "ТекÑÑ‚" msgid "Media" msgstr "Медија" msgid "Mood" msgstr "РаÑположење" msgid "Original Album" msgstr "Изворни албум" msgid "Original Artist" msgstr "Изворни извођач" msgid "Original Date" msgstr "Изворни датум" msgid "Part" msgstr "Дио" msgid "Performer" msgstr "Извођач" msgid "Picture" msgstr "Слика" msgid "Publisher" msgstr "Издавач" msgid "Release Country" msgstr "" msgid "Remixer" msgstr "РемикÑер" msgid "Sort Album" msgstr "РедоÑлед Ñортирања албума" msgid "Sort Album Artist" msgstr "РедоÑлед Ñортирања аутора албума" msgid "Sort Artist" msgstr "РедоÑлед Ñортирања извођача" msgid "Sort Composer" msgstr "РедоÑлед Ñортирања композитора" msgid "Sort Name" msgstr "РедоÑлед Ñортирања наÑлова" msgid "Subtitle" msgstr "ПоднаÑлов" msgid "Website" msgstr "ВебÑајт" msgid "WWW Audio File" msgstr "" msgid "WWW Audio Source" msgstr "" msgid "Number of tracks" msgstr "Број нумера" msgid "Extension" msgstr "ЕкÑтензија" msgid "Bitrate" msgstr "БитÑки проток" msgid "VBR" msgstr "ВБР" msgid "Samplerate" msgstr "Узорковање" msgid "Channels" msgstr "Канала" msgid "Codec" msgstr "Кодек" msgid "Escape for HTML" msgstr "Escape HTML" msgid "Automatic Import" msgstr "ÐутоматÑки увози" msgid "D&estination:" msgstr "О&дредиште:" msgid "Tag 1 and Tag 2" msgstr "ознака 1 и ознака 2" msgid "&Profile:" msgstr "&Профил:" msgid "&Save Settings" msgstr "&Сачувај подешавања" msgid "New" msgstr "Ðово" msgid "A&bort" msgstr "ОбуÑта&ви" msgid "S&tart" msgstr "&Почетак" msgid "Import Source" msgstr "Извор увоза" msgid "&Server:" msgstr "&Сервер:" msgid "&Accuracy:" msgstr "&ПрецизноÑÑ‚:" msgid "&Standard Tags" msgstr "&Стандардне ознаке" msgid "&Additional Tags" msgstr "Дода&тне ознаке" msgid "C&over Art" msgstr "Омот а&лбума" msgid "Browse Cover Art" msgstr "Претрага омота албума" msgid "&Artist/Album" msgstr "И&звођач/албум" msgid "&Source" msgstr "&Извор" msgid "Source:" msgstr "Извор:" msgid "URL:" msgstr "УРЛ:" msgid "&URL extraction" msgstr "Извлачење &УРЛ-а" msgid "Match" msgstr "Поклопи" msgid "Picture URL" msgstr "УРЛ Ñлике" msgid "&Browse" msgstr "&Потражи" msgid "Click Browse to start" msgstr "Кликните на „Потражи“ да биÑте покренули" msgid "Then drag the picture from the browser to Kid3." msgstr "и онда превуците Ñлику Ñа прегледача у Кид3." msgid "ID3v1" msgstr "ИД3в1" msgid "&Mark truncated fields" msgstr "Означи одÑечена &поља" msgid "Text &encoding:" msgstr "Кодирање &текÑта:" msgid "ID3v2" msgstr "ИД3в2" msgid "Use &track/total number of tracks format" msgstr "КориÑти формат &нумера/укупно нумера" msgid "Track number &digits:" msgstr "Број &цифара нумере:" msgid "&Genre as text instead of numeric string" msgstr "&Жанр као текÑÑ‚ умеÑто бројног низа" msgid "ISO-8859-1" msgstr "ISO-8859-1" msgid "UTF16" msgstr "UTF-16" msgid "UTF8" msgstr "UTF-8" msgid "&Version used for new tags:" msgstr "И&здање за нове ознаке:" msgid "ID3v2.3.0" msgstr "ID3v2.3.0" msgid "ID3v2.4.0" msgstr "ID3v2.4.0" msgid "Ogg/Vorbis" msgstr "Огг/ВорбиÑ" msgid "Co&mment field name:" msgstr "Име поља ко&ментара:" msgid "&Picture field name:" msgstr "Име поља &Ñлике:" msgid "Custom &Genres" msgstr "ПоÑе&бни жанрови" msgid "&Show only custom genres" msgstr "Прик&ажи Ñамо поÑебне жанрове" msgid "&Quick Access Tags" msgstr "" msgid "&Tag Format" msgstr "&Формат ознаке" msgid "Tag &1" msgstr "Ознака &1" msgid "Tag &2" msgstr "Ознака &2" msgid "Tag 1 a&nd Tag 2" msgstr "ознака 1 &и ознака 2" msgid "Startup" msgstr "Покретање" msgid "&Load last-opened files" msgstr "" msgid "Save" msgstr "Сачувај" msgid "&Preserve file timestamp" msgstr "Очувај &временÑку ознаку фајла" msgid "&Mark changes" msgstr "О&значи измене" msgid "F&ilename for cover:" msgstr "&Име фајла за омот албума:" msgid "&Filename Format" msgstr "Формат имена &фајла" msgid "Browser" msgstr "Прегледач" msgid "Web &browser:" msgstr "Веб &прегледач:" msgid "Context &Menu Commands" msgstr "Ðаредбе контекÑтног ме&нија" msgid "&Play on double click" msgstr "&Репродукуј при двоклику" msgid "Proxy" msgstr "ПрокÑи" msgid "&Proxy:" msgstr "&ПрокÑи:" msgid "&Use authentication with proxy" msgstr "КориÑти &аутентификацију за прокÑи" msgid "Proxy user &name:" msgstr "КориÑничко &име за прокÑи:" msgid "Proxy pass&word:" msgstr "Л&озинка за прокÑи:" msgid "&Metadata Plugins && Priority" msgstr "&Метаподаци прикључци && Приоритет" msgid "A&vailable Plugins" msgstr "&ДоÑтупни прикључци" msgid "Changes take only effect after a restart!" msgstr "Ðеке измене неће бити укључене пре поновног покретања!" msgid "From Clip&board" msgstr "Из клип&борда" msgid "&Import" msgstr "У&вези" msgid "&Export" msgstr "Изв&ези" msgid "&View" msgstr "&Приказ" msgid "Text Encoding" msgstr "Кодирање текÑта" msgid "Text" msgstr "ТекÑÑ‚" msgid "Description" msgstr "ОпиÑ" msgid "Owner" msgstr "ВлаÑник" msgid "Email" msgstr "Е-адреÑа" msgid "Rating" msgstr "Рејтинг" msgid "Picture Type" msgstr "Тип Ñлике" msgid "Image format" msgstr "Формат Ñлике" msgid "Mimetype" msgstr "Миме" msgid "Counter" msgstr "Бројач" msgid "Identifier" msgstr "Идентификатор" msgid "Volume Adjustment" msgstr "Подешавање јачине" msgid "Number of Bits" msgstr "Број бита" msgid "Volume Change Right" msgstr "" msgid "Volume Change Left" msgstr "" msgid "Peak Volume Right" msgstr "" msgid "Peak Volume Left" msgstr "" msgid "Timestamp Format" msgstr "Формат временÑке ознаке" msgid "Content Type" msgstr "Тип Ñадржаја" msgid "Price" msgstr "Цена" msgid "Seller" msgstr "" msgid "Images" msgstr "Слике" msgid "UTF16BE" msgstr "UTF-16BE" msgid "Other" msgstr "ОÑтало" msgid "32x32 pixels PNG file icon" msgstr "ПÐГ икона фајла 32x32 пикÑела" msgid "Other file icon" msgstr "ОÑтале иконе фајла" msgid "Cover (front)" msgstr "Омот (предњи)" msgid "Cover (back)" msgstr "Омот (задњи)" msgid "Leaflet page" msgstr "" msgid "Lead artist/lead performer/soloist" msgstr "" msgid "Artist/performer" msgstr "Уметник/извођач" msgid "Band/Orchestra" msgstr "Бенд/оркеÑтар" msgid "Lyricist/text writer" msgstr "ПеÑник/текÑтопиÑац" msgid "Recording Location" msgstr "МеÑто Ñнимања" msgid "During recording" msgstr "Током Ñнимања" msgid "During performance" msgstr "Током извођења" msgid "Movie/video screen capture" msgstr "Снимак екрана филма/видеа" msgid "A bright coloured fish" msgstr "" msgid "Illustration" msgstr "ИлуÑтрација" msgid "Band/artist logotype" msgstr "Логотип бенда/извођача" msgid "Publisher/Studio logotype" msgstr "Логотип издавача/Ñтудија" msgid "MPEG frames as unit" msgstr "Број МПЕГ кадрова као јединица" msgid "Milliseconds as unit" msgstr "МилиÑекунде као јединица" msgid "Text transcription" msgstr "ТранÑкрипција текÑта" msgid "Movement/part name" msgstr "" msgid "Events" msgstr "" msgid "Chord" msgstr "Ðкорд" msgid "Trivia/pop up" msgstr "" msgid "Export" msgstr "Извези" msgid "Format:" msgstr "Формат:" msgid "Header:" msgstr "Заглавље:" msgid "Tracks:" msgstr "Ðумере:" msgid "Footer:" msgstr "Подножје:" msgid "To F&ile" msgstr "У Ñ„&ајл" msgid "To Clip&board" msgstr "У клип&борд" msgid "&Source:" msgstr "&Извор:" msgid "File Error" msgstr "Грешка фајла" msgid "&Filter:" msgstr "&Филтер:" msgid "&Expression:" msgstr "&Израз:" msgid "&Apply" msgstr "&Примени" msgid "Accuracy:" msgstr "ПрецизноÑÑ‚:" msgid "Cover Art:" msgstr "Омот албума:" msgid "From F&ile/Clipboard..." msgstr "Из &фајла/клипборда..." msgid "From T&ags..." msgstr "Из о&знака..." msgid "&From Server:" msgstr "С&а Ñервера:" msgid "Check maximum allowable time &difference (sec):" msgstr "Провери највећу дозвољену &временÑку разлику (Ñек):" msgid "Match with:" msgstr "Поклопи Ñа:" msgid "&Length" msgstr "тра&јањем" msgid "T&rack" msgstr "&нумером" msgid "&Title" msgstr "наÑ&ловом" msgid "&Start number:" msgstr "&Почетни број:" msgid "&Destination:" msgstr "&Одредиште:" msgid "&Total number of tracks:" msgstr "&Укупно нумера:" msgid "Create Playlist" msgstr "Ðаправи лиÑту нумера" msgid "Playlist File Name" msgstr "Име фајла лиÑте нумера" msgid "Same as &directory name" msgstr "ИÑто као име &директоријума" msgid "&Format:" msgstr "&Формат:" msgid "Cr&eate in:" msgstr "Ð&аправи у:" msgid "Current directory" msgstr "тренутном директоријуму" msgid "Every directory" msgstr "Ñвим директоријумима" msgid "Top-level directory" msgstr "највишем директоријуму" msgid "Playlist Content" msgstr "Садржај лиÑте нумера" msgid "For&mat:" msgstr "Фор&мат:" msgid "Incl&ude only the selected files" msgstr "Укључи Ñамо &означене фајлове" msgid "Sort by file &name" msgstr "Сортирај по &имену" msgid "Sort by &tag field" msgstr "Сортирај по &пољу ознаке" msgid "Use &relative path for files in playlist" msgstr "КориÑти &релативну путању за фајлове на лиÑти нумера" msgid "Use full p&ath for files in playlist" msgstr "КориÑти пуну пут&ању за фајлове на лиÑти нумера" msgid "Write only &list of files" msgstr "Упиши Ñамо &ÑпиÑак фајлова" msgid "Write &info using" msgstr "Упиши &информације кориÑтећи" msgid "Preview" msgstr "Преглед" msgid "Create Directory" msgstr "Ðаправи директоријум" msgid "From Tag 2 and Tag 1" msgstr "Из ознаке 2 и ознаке 1" msgid "From:" msgstr "Из:" msgid "To:" msgstr "У:" msgid "&Find" msgstr "&Ðађи" msgid "C&GI Path:" msgstr "Ц&ГИ путања:" msgid "Track Title/Artist - Album" msgstr "Ðумера/извођач - албум" msgid "State" msgstr "Стање" msgid "No result" msgstr "Ðема резултата" msgid "No result selected" msgstr "Ðишта није изабрано" msgid "Import from Tags" msgstr "Увези из ознака" msgid "Extraction:" msgstr "Извлачење:" msgid "Import from File/Clipboard" msgstr "Увези из фајла/клипборда" msgid "From F&ile" msgstr "Из &фајла" msgid "Download" msgstr "Преузми" msgid "Opening directory..." msgstr "Отварам директоријум..." msgid "Saving directory..." msgstr "УпиÑујем директоријум..." msgid "" "The current directory has been modified.\n" "Do you want to save it?" msgstr "" "Тренутни директоријум је измењен.\n" "Желите ли да Ñачувате измене?" msgid "Warning" msgstr "Упозорење" msgid "Exiting..." msgstr "Излазим..." msgid "Creating playlist..." msgstr "Правим лиÑту нумера..." msgid "Import" msgstr "Увези" msgid "Error while renaming:\n" msgstr "Грешка приликом преименовања:\n" msgid " [filtered]" msgstr " [филтриран]" msgid "Add Frame" msgstr "Додај оквир" msgid "Select the frame ID" msgstr "Изаберите ИД оквира" msgid "Rename File" msgstr "Преименуј фајл" msgid "Enter new file name:" msgstr "УнеÑите ново име фајла:" msgid "Do you really want to move these %1 items to the trash?" msgstr "Желите ли заиÑта да премеÑтите ових %1 Ñтавки у Ñмеће?" msgid "Do you really want to move this item to the trash?" msgstr "Желите ли заиÑта да премеÑтите ову %1 Ñтавку у Ñмеће?" msgid "Move to Trash" msgstr "ПремеÑти у Ñмеће" msgid "Directory must be empty.\n" msgstr "Директориујм мора бити празан.\n" msgid "Could not move these files to the Trash" msgstr "ÐиÑам могао да премеÑтим ове фајлове у Ñмеће" msgid "Expand All" msgstr "Разгранај Ñве" msgid "&Expand all" msgstr "Раз&гранај Ñве" msgid "&Collapse all" msgstr "&Сакупи Ñве" msgid "&Rename" msgstr "П&реименуј" msgid "&Move to Trash" msgstr "ПремеÑти у &Ñмеће" msgid "Kid3" msgstr "Кид3" msgid "F&ile" msgstr "Ф&ајл" msgid "Name:" msgstr "Име:" msgid "Filename from Tag 1" msgstr "ознаке 1 у име фајла" msgid "Filename from Tag 2" msgstr "ознаке 2 у име фајла" msgid "Tag 1 from Filename" msgstr "ознаку 1 из имена фајла" msgid "Tag 2 from Filename" msgstr "ознаку 2 из имена фајла" msgid "&Add..." msgstr "&Додај..." msgid "Move &Up" msgstr "Помери &горе" msgid "Move &Down" msgstr "Помери до&ле" msgid "&Edit..." msgstr "У&реди..." msgid "&Remove" msgstr "У&клони" msgid "&Insert row" msgstr "&Убаци ред" msgid "&Delete row" msgstr "&Обриши ред" msgid "&Clear row" msgstr "О&чиÑти ред" msgid "Format while editing" msgstr "Форматирај при уређивању" msgid "Case conversion:" msgstr "Величина Ñлова:" msgid "No changes" msgstr "без измена" msgid "All lowercase" msgstr "Ñве малим" msgid "All uppercase" msgstr "Ñве великим" msgid "First letter uppercase" msgstr "прво Ñлово великим" msgid "All first letters uppercase" msgstr "Ñва прва Ñлова великим" msgid "Locale:" msgstr "Локалитет:" msgid "None" msgstr "Ðишта" msgid "String replacement:" msgstr "Замена текÑта:" msgid "From" msgstr "из" msgid "To" msgstr "у" msgid "&Add" msgstr "&Додај" msgid "&Select all" msgstr "И&забери Ñве" msgid "&Deselect all" msgstr "По&ништи избор" msgid "View Picture" msgstr "Погледај Ñлику" msgid "" "Drag album\n" "artwork\n" "here" msgstr "" "Превуци\n" "Ñлику албума\n" "овде" msgid "Play/Pause" msgstr "Репродукуј/паузирај" msgid "Stop playback" msgstr "ЗауÑтави репродукцију" msgid "Previous Track" msgstr "Претходна нумера" msgid "Next Track" msgstr "Следећа нумера" msgid "Close" msgstr "Затвори" msgid "Mute" msgstr "Утишај" msgid "Volume: %1%" msgstr "Јачина: %1%" msgid "Add Item" msgstr "Додај Ñтавку" msgid "Edit Item" msgstr "Уреди Ñтавку" msgid "MusicBrainz Fingerprint" msgstr "МјузикБрејнз ОтиÑак прÑта" msgid "Unrecognized" msgstr "Ðије препознато" msgid "Recognized" msgstr "Препознато" msgid "User Selection" msgstr "КориÑников одабир" msgid "ID Lookup" msgstr "ИД претрага" msgid "Fingerprint" msgstr "ОтиÑак прÑта" msgid "Metadata Lookup" msgstr "Претрага метаподатака" msgid "Amazon" msgstr "Ðмазон" msgid "Discogs" msgstr "ДиÑкогÑ" msgid "gnudb.org" msgstr "gnudb.org" msgid "TrackType.org" msgstr "ТрекТајп.орг" msgid "AENC - Audio encryption" msgstr "AENC - аудио шифровање" msgid "APIC - Attached picture" msgstr "APIC - приложена Ñлика" msgid "COMM - Comments" msgstr "COMM - коментари" msgid "COMR - Commercial" msgstr "COMR - комерцијално" msgid "ENCR - Encryption method registration" msgstr "ENCR - региÑтрација методе шифровања" msgid "EQUA - Equalization" msgstr "EQUA - једначење" msgid "ETCO - Event timing codes" msgstr "" msgid "GEOB - General encapsulated object" msgstr "GEOB - општи угнежђени објекат" msgid "GRID - Group identification registration" msgstr "GRID - региÑтрација идентификације групе" msgid "IPLS - Involved people list" msgstr "IPLS - ÑпиÑак укључених људи" msgid "LINK - Linked information" msgstr "LINK - повезана информација" msgid "MCDI - Music CD identifier" msgstr "MCDI - идентификатор музичког ЦД-а" msgid "MLLT - MPEG location lookup table" msgstr "" msgid "OWNE - Ownership frame" msgstr "OWNE - влаÑнички оквир" msgid "PRIV - Private frame" msgstr "PRIV - приватни оквир" msgid "PCNT - Play counter" msgstr "PCNT - бројач репродукције" msgid "POPM - Popularimeter" msgstr "POPM - мерач популарноÑти" msgid "POSS - Position synchronisation frame" msgstr "POSS - кадар Ñинхронизације позиције" msgid "RBUF - Recommended buffer size" msgstr "RBUF - препоручена величина бафера" msgid "RVAD - Relative volume adjustment" msgstr "RVAD - подешавање релативне јачине звука" msgid "RVRB - Reverb" msgstr "RVRB - реверберација" msgid "SYLT - Synchronized lyric/text" msgstr "" msgid "SYTC - Synchronized tempo codes" msgstr "" msgid "TALB - Album/Movie/Show title" msgstr "TALB - наÑлов албума/филма/емиÑије" msgid "TBPM - BPM (beats per minute)" msgstr "TBPM - БПМ (откуцаја у минути)" msgid "TCOM - Composer" msgstr "TCOM - композитор" msgid "TCON - Content type" msgstr "TCON - тип Ñадржаја" msgid "TCOP - Copyright message" msgstr "TCOP - порука ауторÑког права" msgid "TDAT - Date" msgstr "TDAT - датум" msgid "TDLY - Playlist delay" msgstr "TDLY - кашњење лиÑте нумера" msgid "TENC - Encoded by" msgstr "TENC - кодер" msgid "TEXT - Lyricist/Text writer" msgstr "TEXT - пеÑник/текÑтопиÑац" msgid "TFLT - File type" msgstr "TFLT - тип фајла" msgid "TIME - Time" msgstr "TIME - време" msgid "TIT1 - Content group description" msgstr "TIT1 - Ð¾Ð¿Ð¸Ñ Ð³Ñ€ÑƒÐ¿Ðµ Ñадржаја" msgid "TIT2 - Title/songname/content description" msgstr "TIT2 - наÑлов/име пеÑме/Ð¾Ð¿Ð¸Ñ Ñадржаја" msgid "TIT3 - Subtitle/Description refinement" msgstr "" msgid "TKEY - Initial key" msgstr "TKEY - почетни кључ" msgid "TLAN - Language(s)" msgstr "TLAN - језик (језици)" msgid "TLEN - Length" msgstr "TLEN - трајање" msgid "TMED - Media type" msgstr "TMED - тип медија" msgid "TOAL - Original album/movie/show title" msgstr "TOAL - изворни наÑлов албума/филма/емиÑије" msgid "TOFN - Original filename" msgstr "TOFN - изворно име" msgid "TOLY - Original lyricist(s)/text writer(s)" msgstr "" msgid "TOPE - Original artist(s)/performer(s)" msgstr "" msgid "TORY - Original release year" msgstr "TORY - изворна година издавања" msgid "TOWN - File owner/licensee" msgstr "" msgid "TPE1 - Lead performer(s)/Soloist(s)" msgstr "" msgid "TPE2 - Band/orchestra/accompaniment" msgstr "" msgid "TPE3 - Conductor/performer refinement" msgstr "" msgid "TPE4 - Interpreted, remixed, or otherwise modified by" msgstr "" msgid "TPOS - Part of a set" msgstr "" msgid "TPUB - Publisher" msgstr "TPUB - издавач" msgid "TRCK - Track number/Position in set" msgstr "" msgid "TRDA - Recording dates" msgstr "TRDA - датуми Ñнимања" msgid "TRSN - Internet radio station name" msgstr "TRSN - име интернет радио Ñтанице" msgid "TRSO - Internet radio station owner" msgstr "TRSO - влаÑник интернет радио Ñтанице" msgid "TSIZ - Size" msgstr "TSIZ - величина" msgid "TSRC - ISRC (international standard recording code)" msgstr "TSRC - ИСРЦ (међународни Ñтандардни кôд Ñнимања)" msgid "TSSE - Software/Hardware and settings used for encoding" msgstr "" msgid "TXXX - User defined text information" msgstr "TXXX - кориÑнички дефиниÑана текÑтуална информација" msgid "TYER - Year" msgstr "TYER - година" msgid "UFID - Unique file identifier" msgstr "UFID - јединÑтвени идентификатор фајла" msgid "USER - Terms of use" msgstr "USER - уÑлови коришћења" msgid "USLT - Unsynchronized lyric/text transcription" msgstr "" msgid "WCOM - Commercial information" msgstr "WCOM - подаци о комерцијализовању" msgid "WCOP - Copyright/Legal information" msgstr "WCOP - ауторÑко право/правне информације" msgid "WOAF - Official audio file webpage" msgstr "" msgid "WOAR - Official artist/performer webpage" msgstr "" msgid "WOAS - Official audio source webpage" msgstr "" msgid "WORS - Official internet radio station homepage" msgstr "" msgid "WPAY - Payment" msgstr "WPAY - плаћање" msgid "WPUB - Official publisher webpage" msgstr "" msgid "WXXX - User defined URL link" msgstr "WXXX - кориÑнички дефиниÑана УРЛ веза" msgid "MusicBrainz Release" msgstr "МјузикБрејнз издање" msgid "ASPI - Audio seek point index" msgstr "" msgid "EQU2 - Equalisation (2)" msgstr "EQU2 - једначење (2)" msgid "RVA2 - Relative volume adjustment (2)" msgstr "RVA2 - подешавање релативне јачине звука (2)" msgid "SEEK - Seek frame" msgstr "SEEK - кадар тражења" msgid "SIGN - Signature frame" msgstr "SIGN - кадар потпиÑа" msgid "TCMP - iTunes compilation flag" msgstr "" msgid "TDEN - Encoding time" msgstr "TDEN - време кодирања" msgid "TDOR - Original release time" msgstr "TDOR - време издавања оригинала" msgid "TDRC - Recording time" msgstr "TDRC - време Ñнимања" msgid "TDRL - Release time" msgstr "TDRL - време издавања" msgid "TDTG - Tagging time" msgstr "TDTG - време означавања" msgid "TIPL - Involved people list" msgstr "TIPL - ÑпиÑак укључених људи" msgid "TMCL - Musician credits list" msgstr "" msgid "TMOO - Mood" msgstr "TMOO - раÑположење" msgid "TPRO - Produced notice" msgstr "" msgid "TSO2 - Album artist sort order" msgstr "TSO2 - редоÑлед Ñортирања аутора албума" msgid "TSOA - Album sort order" msgstr "TSOA - редоÑлед Ñортирања албума" msgid "TSOC - Composer sort order" msgstr "TSOC - редоÑлед Ñортирања композитора" msgid "TSOP - Performer sort order" msgstr "TSOP - редоÑлед Ñортирања извођача" msgid "TSOT - Title sort order" msgstr "TSOT - редоÑлед Ñортирања наÑлова" msgid "TSST - Set subtitle" msgstr "TSST - поÑтави поднаÑлов" �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/po/sr@ijekavian.po�����������������������������������������������������������������������0000664�0000000�0000000�00000121031�12246035113�0015725�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Kid3 Serbian Ijekavian Translation. # Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012. Free Software Foundation, Inc. # # Urs Fleisch <ufleisch@users.sourceforge.net>, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012. # Mladen Pejaković <pejakm@gmail.com>, 2012. msgid "" msgstr "" "Project-Id-Version: Kid3 3.0.2\n" "Report-Msgid-Bugs-To: http://sourceforge.net/p/kid3/bugs/\n" "POT-Creation-Date: 2013-09-11 21:22+0200\n" "PO-Revision-Date: 2011-04-26 23:41+0200\n" "Last-Translator: Mladen Pejaković <pejakm@gmail.com>\n" "Language-Team: Serbian <kde-i18n-sr@lists.kde.org>\n" "Language: sr@ijekavian\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Lokalize 0.3\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" "X-Language: sr\n" "X-Source-Language: C\n" msgid "Timeout" msgstr "Прековријеме" msgid "Usage:" msgstr "Употреба:" msgid "Help" msgstr "Помоћ" msgid "Command name" msgstr "Ðаредба" msgid "Quit application" msgstr "Ðапушта апликацију" msgid "The current directory has been modified." msgstr "Тренутни директоријум је измијењен." msgid "Type 'exit force' to quit." msgstr "УнеÑите 'exit force' за излаз." msgid "Change directory" msgstr "" msgid "%1 does not exist" msgstr "%1 не поÑтоји" msgid "Print the filename of the current working directory" msgstr "Штампај пуно име фајла текућег активног директоријума" msgid "Directory list" msgstr "СпиÑак фаÑцикли" msgid "Saves the changed files" msgstr "УпиÑује измјене фајлова" msgid "Error while writing file:\n" msgstr "Грешка приликом упиÑа фајла:\n" msgid "Select file" msgstr "Изабери фајл" msgid "%1 not found" msgstr "%1 није пронађено" msgid "Select tag" msgstr "Избор ознаке" msgid "Get tag frame" msgstr "" msgid "Set tag frame" msgstr "" msgid "Could not set \"%1\" for %2" msgstr "Ðе могу да поÑтавим \"%1\" за %2" msgid "Revert" msgstr "Врати" msgid "Import from file or clipboard" msgstr "Увези из фајла или клипборда" msgid "Format name" msgstr "Формат имена" msgid "%1 not found." msgstr "%1 није нађен." msgid "Available" msgstr "ДоÑтупно" msgid "Error" msgstr "Грешка" msgid "Automatic import" msgstr "ÐутоматÑки увози" msgid "Profile name" msgstr "Име поÑтавке" msgid "Reading Directory" msgstr "Приликом читања директоријума" msgid "Started" msgstr "Започет" msgid "Source" msgstr "Извор" msgid "Querying" msgstr "Затражујем" msgid "Fetching" msgstr "Добављам" msgid "Data received" msgstr "Подаци примљени" msgid "Cover" msgstr "Омот" msgid "Finished" msgstr "Завршено" msgid "Aborted" msgstr "ОбуÑтављен" msgid "Download album cover artwork" msgstr "Потражи Ñлику омота албума" msgid "Export to file or clipboard" msgstr "Извези у фајл или клипборд" msgid "Create playlist" msgstr "Ðаправи лиÑту нумера" msgid "Apply filename format" msgstr "Примијени формат назива фајла" msgid "Apply tag format" msgstr "Примијени формат ознаке" msgid "Apply text encoding" msgstr "Примени кодирање текÑта" msgid "Rename directory" msgstr "Преименуј директоријум" msgid "Number tracks" msgstr "Ðумериши" msgid "Track number" msgstr "Број нумере" msgid "Filter" msgstr "Филтер" msgid "Filter name" msgstr "Име фајла" msgid "Convert ID3v2.3 to ID3v2.4" msgstr "Претвори ИД3в2.3 у ИД3в2.4" msgid "Convert ID3v2.4 to ID3v2.3" msgstr "Претвори ИД3в2.4 у ИД3в2.3" msgid "Filename from tag" msgstr "ознаке у име фајла" msgid "Tag from filename" msgstr "ознаку из имена фајла" msgid "Tag to other tag" msgstr "" msgid "Copy" msgstr "Копирај" msgid "Paste" msgstr "Ðалијепи" msgid "Remove" msgstr "Уклони" msgid "Play" msgstr "Репродукција" msgid "Parameter" msgstr "Параметар" msgid "File path" msgstr "Путања фајла" msgid "URL" msgstr "УРЛ" msgid "Tag numbers" msgstr "Број нумере" msgid "Frame name" msgstr "Из имена фајла" msgid "Frame value" msgstr "" msgid "Format" msgstr "Формат" msgid "Command specific" msgstr "Ðаредба" msgid "Available Commands" msgstr "Ðаредбе на раÑполагању" msgid "File" msgstr "Фајл" msgid "Name" msgstr "Име" msgid "Tag 2" msgstr "ознака 2" msgid "Tag 1" msgstr "ознака 1" msgid "Tags" msgstr "Ознаке" msgid "Unknown command '%1'. Type 'help' for help." msgstr "Ðепозната наредба '%1'. КориÑтите 'help' да биÑте добили помоћ." msgid "Unknown command '%1', -h for help." msgstr "Ðепозната наредба '%1', -h за помоћ." msgid "Files" msgstr "Фајлови" msgid "User Actions" msgstr "Радње кориÑника" msgid "Network" msgstr "Мрежа" msgid "Plugins" msgstr "Прикључци" msgid "Main Toolbar" msgstr "Главна трака" msgid "Opens a directory" msgstr "Отвара директоријум" msgid "Opens a recently used directory" msgstr "Отвара недавно коришћен директоријум" msgid "Reverts the changes of all or the selected files" msgstr "Поништава измјене Ñвих или изабраних фајлова" msgid "Quits the application" msgstr "Ðапушта апликацију" msgid "Select all files" msgstr "Изабери Ñве фајлове" msgid "Deselect all files" msgstr "Поништи избор Ñвих фајлова" msgid "Configure Shortcuts" msgstr "ПодеÑи пречице" msgid "Configure Toolbars" msgstr "ПодеÑи траке алата" msgid "Preferences dialog" msgstr "Дијалог подешавања" msgid "O&pen Directory..." msgstr "О&твори директоријум..." msgid "&Import..." msgstr "&Увези..." msgid "Import from %1..." msgstr "Увези Ñа %1..." msgid "Automatic I&mport..." msgstr "Ðуто&матÑки увози..." msgid "&Browse Cover Art..." msgstr "Потражи о&мот албума..." msgid "&Export..." msgstr "Изв&ези..." msgid "&Create Playlist..." msgstr "Ðаправи &лиÑту нумера..." msgid "Apply &Filename Format" msgstr "Примијени формат имена фа&јла" msgid "Apply &Tag Format" msgstr "Примијени формат &ознаке" msgid "Apply Text &Encoding" msgstr "Примени кодирање Ñ‚&екÑта" msgid "&Rename Directory..." msgstr "Преименуј &директоријум..." msgid "&Number Tracks..." msgstr "Ðу&мериши..." msgid "F&ilter..." msgstr "Ф&илтер..." msgid "Convert ID3v2.3 to ID3v2.&4" msgstr "Претвори ИД3в2.3 у ИД3в2.&4" msgid "Convert ID3v2.4 to ID3v2.&3" msgstr "Претвори ИД3в2.4 у ИД3в2.&3" msgid "&Play" msgstr "Ре&продукуј" msgid "Show &Picture" msgstr "Прикажи Ñ&лику" msgid "Auto &Hide Tags" msgstr "ÐутоматÑки &Ñакриј ознаке" msgid "Select All in &Directory" msgstr "Изабери Ñве фајлове у тренутном &директоријуму" msgid "&Previous File" msgstr "&Претходни фајл" msgid "&Next File" msgstr "&Сљедећи фајл" msgid "From Filename" msgstr "Из имена фајла" msgid "From Tag 2" msgstr "Из ознаке 2" msgid "From Tag 1" msgstr "Из ознаке 1" msgid "Frames:" msgstr "Кадрови:" msgid "Edit" msgstr "Уреди" msgid "Add" msgstr "Додај" msgid "Delete" msgstr "Обриши" msgid "Filename" msgstr "Име фајла" msgid "Focus" msgstr "ФокуÑ" msgid "File List" msgstr "СпиÑак фајлова" msgid "Directory List" msgstr "СпиÑак фаÑцикли" msgid "Configure - Kid3" msgstr "Подешавање - Кид3" msgid "Open" msgstr "Отвори" msgid "Save As" msgstr "Сачувај као" msgid "directory to open" msgstr "директоријум за отварање" msgid "Kid3 ID3 Tagger" msgstr "Кид3 уређивач ИД3 ознака" #. i18n NAME OF TRANSLATORS msgid "Your names" msgstr "Младен Пејаковић" #. i18n EMAIL OF TRANSLATORS msgid "Your emails" msgstr "pejakm@gmail.com" msgid "&Back" msgstr "&Ðазад" msgid "&Forward" msgstr "Ðа&пријед" msgid "&Find:" msgstr "&Ðађи:" msgid "Find Previous" msgstr "Ðађи претходно" msgid "Find Next" msgstr "Ðађи Ñљедеће" msgid "&Close" msgstr "&Затвори" msgid "&Tags" msgstr "&Ознаке" msgid "&Files" msgstr "&Фајлови" msgid "&User Actions" msgstr "Радње &кориÑника" msgid "&Network" msgstr "&Мрежа" msgid "&Plugins" msgstr "&Прикључци" msgid "&Keyboard Shortcuts" msgstr "Пречице &таÑтатуре" msgid "Use custom app&lication font" msgstr "КориÑти поÑе&бни фонт за апликацију" msgid "A&pplication Font..." msgstr "&Фонт апликације..." msgid "Use custom application &style" msgstr "КориÑти поÑебни &Ñтил за апликацију" msgid "Unknown" msgstr "Ðепознато" msgid "Use native system file &dialogs" msgstr "КориÑти ÑиÑтемÑке &дијалоге фајлова" msgid "&Appearance" msgstr "&Изглед" msgid "&Help" msgstr "По&моћ" msgid "&OK" msgstr "&У реду" msgid "&Cancel" msgstr "&ОдуÑтани" msgid "The keyboard shortcut '%1' is already assigned to '%2'." msgstr "Пречица таÑтатуре '%1' је већ додијељена '%2'." msgid "&File" msgstr "&Фајл" msgid "&Open..." msgstr "&Отвори..." msgid "Open &Recent" msgstr "Отвори &недавно" msgid "&Save" msgstr "&Сачувај" msgid "Re&vert" msgstr "&Врати" msgid "Import from %1" msgstr "Увези Ñа %1" msgid "Browse album cover artwork" msgstr "Потражи Ñлику омота албума" msgid "Create M3U Playlist" msgstr "Ðаправи М3У лиÑту нумера" msgid "&Quit" msgstr "&ÐапуÑти" msgid "&Edit" msgstr "&Уреди" msgid "Select &All" msgstr "Изабери Ñ&ве" msgid "Dese&lect" msgstr "По&ништи избор" msgid "Select all files in the current directory" msgstr "Изабери Ñве фајлове у тренутном директоријуму" msgid "Select previous file" msgstr "Изабери претходни фајл" msgid "Select next file" msgstr "Изабери Ñљедећи фајл" msgid "&Tools" msgstr "&Ðлати" msgid "Apply Filename Format" msgstr "Примијени формат назива фајла" msgid "Apply Tag Format" msgstr "Примијени формат ознаке" msgid "Apply Text Encoding" msgstr "Примени кодирање текÑта" msgid "Rename Directory" msgstr "Преименуј директоријум" msgid "Number Tracks" msgstr "Ðумериши" msgid "&Settings" msgstr "&Подешавање" msgid "Enables/disables the toolbar" msgstr "Приказује/Ñакрива траку алата" msgid "Show &Toolbar" msgstr "Прикажи траку &алата" msgid "Enables/disables the statusbar" msgstr "Приказује/Ñакрива траку Ñтања" msgid "Show St&atusbar" msgstr "Прикажи траку &Ñтања" msgid "Show Picture" msgstr "Прикажи Ñлику" msgid "Auto Hide Tags" msgstr "ÐутоматÑки Ñакриј ознаке" msgid "Configure Kid3" msgstr "ПодеÑи Кид3" msgid "&Configure Kid3..." msgstr "&ПодеÑи Кид3..." msgid "Kid3 Handbook" msgstr "Кид3 приручник" msgid "Kid3 &Handbook" msgstr "Кид3 п&риручник" msgid "About Kid3" msgstr "О Кид3-у" msgid "&About Kid3" msgstr "&О Кид3-у" msgid "About Qt" msgstr "О КјуТ-у" msgid "About &Qt" msgstr "О &КјуТ-у" msgid " [modified]" msgstr " [измијењен]" msgid "Toggle the statusbar..." msgstr "Прикажи/Ñакриј траку Ñтања..." msgid "Ready." msgstr "Спреман." msgid "&Clear List" msgstr "О&чиÑти ÑпиÑак" msgid "Clear" msgstr "ОчиÑти" msgid "Reset" msgstr "Поново поÑтави" msgid "Press F2 or double click to edit cell contents." msgstr "" msgid "Action" msgstr "Радња" msgid "Shortcut" msgstr "Пречица" msgid "Server" msgstr "Сервер" msgid "Accuracy" msgstr "ПрецизноÑÑ‚" msgid "Standard Tags" msgstr "Стандардне ознаке" msgid "Additional Tags" msgstr "Додатне ознаке" msgid "Cover Art" msgstr "Омот албума" msgid "Invalid File" msgstr "ÐеиÑправна датотека" msgid "Data received: %1" msgstr "Подаци примљени: %1" msgid "Request sent..." msgstr "Захтјев поÑлат..." msgid "Year" msgstr "Година" msgid "Length" msgstr "Трајање" msgid "Filenames" msgstr "Имена фајлова" msgid "URLs" msgstr "УРЛ-ови" msgid "Directory name" msgstr "Име директоријума" msgid "Encode as URL" msgstr "Кодирај као УРЛ" msgid "Confirm" msgstr "Потврди" msgid "Output" msgstr "Излаз" msgid "Command" msgstr "Ðаредба" msgid "Create directory %1 failed\n" msgstr "Прављење директоријума %1 није уÑпјело\n" msgid "File %1 already exists\n" msgstr "Фајл %1 већ поÑтоји\n" msgid "%1 is not a directory\n" msgstr "%1 није директоријум\n" msgid "Rename %1 to %2 failed\n" msgstr "Преименовање %1 у %2 није уÑпјело\n" msgid "%1 already exists\n" msgstr "%1 већ поÑтоји\n" msgid "%1 is not a file\n" msgstr "%1 није фајл\n" msgid "New directory name is too different\n" msgstr "Ðово име директоријума је превише различито\n" msgid "Create directory" msgstr "Ðаправи директоријум" msgid "Rename file" msgstr "Преименуј фајл" msgid "C&lear" msgstr "О&чиÑти" msgid "Execute " msgstr "Изврши " msgid "Could not execute " msgstr "Ðе могу да извршим " msgid "True if strings are equal" msgstr "Тачно ако Ñу низови једнаки" msgid "True if string contains substring" msgstr "Тачно ако низ Ñадржи подниз" msgid "True if string matches regexp" msgstr "Тачно ако Ñе низ поклапа Ñа регизом" msgid "Logical AND" msgstr "Логичко И" msgid "Logical OR" msgstr "Логичко ИЛИ" msgid "Logical negation" msgstr "Логичка негација" msgid "Data" msgstr "Подаци" msgid "%1 is not writable" msgstr "%1 Ñе не може пиÑати" msgid "All Supported Files" msgstr "Сви подржани фајлови" msgid "All Files" msgstr "Сви фајлови" msgid "Track" msgstr "Ðумера" msgid "Absolute path to file" msgstr "ÐпÑолутна путања до фајла" msgid "Duration" msgstr "Трајање" msgid "Title" msgstr "ÐаÑлов" msgid "Artist" msgstr "Извођач" msgid "Album" msgstr "Ðлбум" msgid "Comment" msgstr "Коментар" msgid "Date" msgstr "Година" msgid "Track Number" msgstr "Број нумере" msgid "Genre" msgstr "Жанр" msgid "Album Artist" msgstr "Извођач албума" msgid "Arranger" msgstr "Ðранжер" msgid "Author" msgstr "Ðутор" msgid "BPM" msgstr "БПМ" msgid "Catalog Number" msgstr "" msgid "Compilation" msgstr "" msgid "Composer" msgstr "Композитор" msgid "Conductor" msgstr "Диригент" msgid "Copyright" msgstr "ÐуторÑко право" msgid "Disc Number" msgstr "Број диÑка" msgid "Encoded-by" msgstr "Кодер" msgid "Encoder Settings" msgstr "Кодирање подешавања" msgid "Encoding Time" msgstr "Време кодирања" msgid "Grouping" msgstr "ГрупиÑање" msgid "Initial Key" msgstr "Почетни кључ" msgid "ISRC" msgstr "ИСРЦ" msgid "Language" msgstr "Језик" msgid "Lyricist" msgstr "ТекÑтопиÑац" msgid "Lyrics" msgstr "ТекÑÑ‚" msgid "Media" msgstr "Медија" msgid "Mood" msgstr "РаÑположење" msgid "Original Album" msgstr "Изворни албум" msgid "Original Artist" msgstr "Изворни извођач" msgid "Original Date" msgstr "Изворни датум" msgid "Part" msgstr "Дио" msgid "Performer" msgstr "Извођач" msgid "Picture" msgstr "Слика" msgid "Publisher" msgstr "Издавач" msgid "Release Country" msgstr "" msgid "Remixer" msgstr "РемикÑер" msgid "Sort Album" msgstr "РедоÑлед Ñортирања албума" msgid "Sort Album Artist" msgstr "РедоÑлед Ñортирања аутора албума" msgid "Sort Artist" msgstr "РедоÑлијед Ñортирања извођача" msgid "Sort Composer" msgstr "РедоÑлед Ñортирања композитора" msgid "Sort Name" msgstr "РедоÑлијед Ñортирања наÑлова" msgid "Subtitle" msgstr "ПоднаÑлов" msgid "Website" msgstr "ВебÑајт" msgid "WWW Audio File" msgstr "" msgid "WWW Audio Source" msgstr "" msgid "Number of tracks" msgstr "Број нумера" msgid "Extension" msgstr "ЕкÑтензија" msgid "Bitrate" msgstr "БитÑки проток" msgid "VBR" msgstr "ВБР" msgid "Samplerate" msgstr "Узорковање" msgid "Channels" msgstr "Канала" msgid "Codec" msgstr "Кодек" msgid "Escape for HTML" msgstr "Escape HTML" msgid "Automatic Import" msgstr "ÐутоматÑки увози" msgid "D&estination:" msgstr "О&дредиште:" msgid "Tag 1 and Tag 2" msgstr "ознака 1 и ознака 2" msgid "&Profile:" msgstr "&Профил:" msgid "&Save Settings" msgstr "&Сачувај подешавања" msgid "New" msgstr "Ðово" msgid "A&bort" msgstr "ОбуÑта&ви" msgid "S&tart" msgstr "&Почетак" msgid "Import Source" msgstr "Извор увоза" msgid "&Server:" msgstr "&Сервер:" msgid "&Accuracy:" msgstr "&ПрецизноÑÑ‚:" msgid "&Standard Tags" msgstr "&Стандардне ознаке" msgid "&Additional Tags" msgstr "Дода&тне ознаке" msgid "C&over Art" msgstr "Омот а&лбума" msgid "Browse Cover Art" msgstr "Претрага омота албума" msgid "&Artist/Album" msgstr "И&звођач/албум" msgid "&Source" msgstr "&Извор" msgid "Source:" msgstr "Извор:" msgid "URL:" msgstr "УРЛ:" msgid "&URL extraction" msgstr "Извлачење &УРЛ-а" msgid "Match" msgstr "Поклопи" msgid "Picture URL" msgstr "УРЛ Ñлике" msgid "&Browse" msgstr "&Потражи" msgid "Click Browse to start" msgstr "Кликните на „Потражи“ да биÑте покренули" msgid "Then drag the picture from the browser to Kid3." msgstr "и онда превуците Ñлику Ñа прегледача у Кид3." msgid "ID3v1" msgstr "ИД3в1" msgid "&Mark truncated fields" msgstr "Означи одÑјечена &поља" msgid "Text &encoding:" msgstr "Кодирање &текÑта:" msgid "ID3v2" msgstr "ИД3в2" msgid "Use &track/total number of tracks format" msgstr "КориÑти формат &нумера/укупно нумера" msgid "Track number &digits:" msgstr "Број &цифара нумере:" msgid "&Genre as text instead of numeric string" msgstr "&Жанр као текÑÑ‚ умјеÑто бројног низа" msgid "ISO-8859-1" msgstr "ISO-8859-1" msgid "UTF16" msgstr "UTF-16" msgid "UTF8" msgstr "UTF-8" msgid "&Version used for new tags:" msgstr "И&здање за нове ознаке:" msgid "ID3v2.3.0" msgstr "ID3v2.3.0" msgid "ID3v2.4.0" msgstr "ID3v2.4.0" msgid "Ogg/Vorbis" msgstr "Огг/ВорбиÑ" msgid "Co&mment field name:" msgstr "Име поља ко&ментара:" msgid "&Picture field name:" msgstr "Име поља &Ñлике:" msgid "Custom &Genres" msgstr "ПоÑе&бни жанрови" msgid "&Show only custom genres" msgstr "Прик&ажи Ñамо поÑебне жанрове" msgid "&Quick Access Tags" msgstr "" msgid "&Tag Format" msgstr "&Формат ознаке" msgid "Tag &1" msgstr "Ознака &1" msgid "Tag &2" msgstr "Ознака &2" msgid "Tag 1 a&nd Tag 2" msgstr "ознака 1 &и ознака 2" msgid "Startup" msgstr "Покретање" msgid "&Load last-opened files" msgstr "" msgid "Save" msgstr "Сачувај" msgid "&Preserve file timestamp" msgstr "Очувај &временÑку ознаку фајла" msgid "&Mark changes" msgstr "О&значи измјене" msgid "F&ilename for cover:" msgstr "&Име фајла за омот албума:" msgid "&Filename Format" msgstr "Формат имена &фајла" msgid "Browser" msgstr "Прегледач" msgid "Web &browser:" msgstr "Веб &прегледач:" msgid "Context &Menu Commands" msgstr "Ðаредбе контекÑтног ме&нија" msgid "&Play on double click" msgstr "&Репродукуј при двоклику" msgid "Proxy" msgstr "ПрокÑи" msgid "&Proxy:" msgstr "&ПрокÑи:" msgid "&Use authentication with proxy" msgstr "КориÑти &аутентификацију за прокÑи" msgid "Proxy user &name:" msgstr "КориÑничко &име за прокÑи:" msgid "Proxy pass&word:" msgstr "Л&озинка за прокÑи:" msgid "&Metadata Plugins && Priority" msgstr "&Метаподаци прикључци && Приоритет" msgid "A&vailable Plugins" msgstr "&ДоÑтупни прикључци" msgid "Changes take only effect after a restart!" msgstr "Ðеке измене неће бити укључене пре поновног покретања!" msgid "From Clip&board" msgstr "Из клип&борда" msgid "&Import" msgstr "У&вези" msgid "&Export" msgstr "Изв&ези" msgid "&View" msgstr "&Приказ" msgid "Text Encoding" msgstr "Кодирање текÑта" msgid "Text" msgstr "ТекÑÑ‚" msgid "Description" msgstr "ОпиÑ" msgid "Owner" msgstr "ВлаÑник" msgid "Email" msgstr "Е-адреÑа" msgid "Rating" msgstr "Рејтинг" msgid "Picture Type" msgstr "Тип Ñлике" msgid "Image format" msgstr "Формат Ñлике" msgid "Mimetype" msgstr "Миме" msgid "Counter" msgstr "Бројач" msgid "Identifier" msgstr "Идентификатор" msgid "Volume Adjustment" msgstr "Подешавање јачине" msgid "Number of Bits" msgstr "Број бита" msgid "Volume Change Right" msgstr "" msgid "Volume Change Left" msgstr "" msgid "Peak Volume Right" msgstr "" msgid "Peak Volume Left" msgstr "" msgid "Timestamp Format" msgstr "Формат временÑке ознаке" msgid "Content Type" msgstr "Тип Ñадржаја" msgid "Price" msgstr "Цијена" msgid "Seller" msgstr "Продавац" msgid "Images" msgstr "Слике" msgid "UTF16BE" msgstr "UTF-16BE" msgid "Other" msgstr "ОÑтало" msgid "32x32 pixels PNG file icon" msgstr "ПÐГ икона фајла 32x32 пикÑела" msgid "Other file icon" msgstr "ОÑтале иконе фајла" msgid "Cover (front)" msgstr "Омот (предњи)" msgid "Cover (back)" msgstr "Омот (задњи)" msgid "Leaflet page" msgstr "" msgid "Lead artist/lead performer/soloist" msgstr "" msgid "Artist/performer" msgstr "Умјетник/извођач" msgid "Band/Orchestra" msgstr "Бенд/оркеÑтар" msgid "Lyricist/text writer" msgstr "ПјеÑник/текÑтопиÑац" msgid "Recording Location" msgstr "МјеÑто Ñнимања" msgid "During recording" msgstr "Током Ñнимања" msgid "During performance" msgstr "Током извођења" msgid "Movie/video screen capture" msgstr "Снимак екрана филма/видеа" msgid "A bright coloured fish" msgstr "" msgid "Illustration" msgstr "ИлуÑтрација" msgid "Band/artist logotype" msgstr "Логотип бенда/извођача" msgid "Publisher/Studio logotype" msgstr "Логотип издавача/Ñтудија" msgid "MPEG frames as unit" msgstr "Број МПЕГ кадрова као јединица" msgid "Milliseconds as unit" msgstr "МилиÑекунде као јединица" msgid "Text transcription" msgstr "ТранÑкрипција текÑта" msgid "Movement/part name" msgstr "" msgid "Events" msgstr "" msgid "Chord" msgstr "Ðкорд" msgid "Trivia/pop up" msgstr "" msgid "Export" msgstr "Извези" msgid "Format:" msgstr "Формат:" msgid "Header:" msgstr "Заглавље:" msgid "Tracks:" msgstr "Ðумере:" msgid "Footer:" msgstr "Подножје:" msgid "To F&ile" msgstr "У Ñ„&ајл" msgid "To Clip&board" msgstr "У клип&борд" msgid "&Source:" msgstr "&Извор:" msgid "File Error" msgstr "Грешка фајла" msgid "&Filter:" msgstr "&Филтер:" msgid "&Expression:" msgstr "&Израз:" msgid "&Apply" msgstr "&Примијени" msgid "Accuracy:" msgstr "ПрецизноÑÑ‚:" msgid "Cover Art:" msgstr "Омот албума:" msgid "From F&ile/Clipboard..." msgstr "Из &фајла/клипборда..." msgid "From T&ags..." msgstr "Из о&знака..." msgid "&From Server:" msgstr "С&а Ñервера:" msgid "Check maximum allowable time &difference (sec):" msgstr "Провјери највећу дозвољену &временÑку разлику (Ñек):" msgid "Match with:" msgstr "Поклопи Ñа:" msgid "&Length" msgstr "тра&јањем" msgid "T&rack" msgstr "&нумером" msgid "&Title" msgstr "наÑ&ловом" msgid "&Start number:" msgstr "&Почетни број:" msgid "&Destination:" msgstr "&Одредиште:" msgid "&Total number of tracks:" msgstr "&Укупно нумера:" msgid "Create Playlist" msgstr "Ðаправи лиÑту нумера" msgid "Playlist File Name" msgstr "Име фајла лиÑте нумера" msgid "Same as &directory name" msgstr "ИÑто као име &директоријума" msgid "&Format:" msgstr "&Формат:" msgid "Cr&eate in:" msgstr "Ð&аправи у:" msgid "Current directory" msgstr "тренутном директоријуму" msgid "Every directory" msgstr "Ñвим директоријумима" msgid "Top-level directory" msgstr "највишем директоријуму" msgid "Playlist Content" msgstr "Садржај лиÑте нумера" msgid "For&mat:" msgstr "Фор&мат:" msgid "Incl&ude only the selected files" msgstr "Укључи Ñамо &означене фајлове" msgid "Sort by file &name" msgstr "Сортирај по &имену" msgid "Sort by &tag field" msgstr "Сортирај по &пољу ознаке" msgid "Use &relative path for files in playlist" msgstr "КориÑти &релативну путању за фајлове на лиÑти нумера" msgid "Use full p&ath for files in playlist" msgstr "КориÑти пуну пут&ању за фајлове на лиÑти нумера" msgid "Write only &list of files" msgstr "Упиши Ñамо &ÑпиÑак фајлова" msgid "Write &info using" msgstr "Упиши &информације кориÑтећи" msgid "Preview" msgstr "Преглед" msgid "Create Directory" msgstr "Ðаправи директоријум" msgid "From Tag 2 and Tag 1" msgstr "Из ознаке 2 и ознаке 1" msgid "From:" msgstr "Из:" msgid "To:" msgstr "У:" msgid "&Find" msgstr "&Ðађи" msgid "C&GI Path:" msgstr "Ц&ГИ путања:" msgid "Track Title/Artist - Album" msgstr "Ðумера/извођач - албум" msgid "State" msgstr "Стање" msgid "No result" msgstr "Ðема резултата" msgid "No result selected" msgstr "Ðишта није изабрано" msgid "Import from Tags" msgstr "Увези из ознака" msgid "Extraction:" msgstr "Извлачење:" msgid "Import from File/Clipboard" msgstr "Увези из фајла/клипборда" msgid "From F&ile" msgstr "Из &фајла" msgid "Download" msgstr "Преузми" msgid "Opening directory..." msgstr "Отварам директоријум..." msgid "Saving directory..." msgstr "УпиÑујем директоријум..." msgid "" "The current directory has been modified.\n" "Do you want to save it?" msgstr "" "Тренутни директоријум је измијењен.\n" "Желите ли да Ñачувате измјене?" msgid "Warning" msgstr "Упозорење" msgid "Exiting..." msgstr "Излазим..." msgid "Creating playlist..." msgstr "Правим лиÑту нумера..." msgid "Import" msgstr "Увези" msgid "Error while renaming:\n" msgstr "Грешка приликом преименовања:\n" msgid " [filtered]" msgstr " [филтриран]" msgid "Add Frame" msgstr "Додај оквир" msgid "Select the frame ID" msgstr "Изаберите ИД оквира" msgid "Rename File" msgstr "Преименуј фајл" msgid "Enter new file name:" msgstr "УнеÑите ново име фајла:" msgid "Do you really want to move these %1 items to the trash?" msgstr "Желите ли заиÑта да премјеÑтите ових %1 Ñтавки у Ñмеће?" msgid "Do you really want to move this item to the trash?" msgstr "Желите ли заиÑта да премјеÑтите ову %1 Ñтавку у Ñмеће?" msgid "Move to Trash" msgstr "ПремјеÑти у Ñмеће" msgid "Directory must be empty.\n" msgstr "Директориујм мора бити празан.\n" msgid "Could not move these files to the Trash" msgstr "ÐиÑам могао да премјеÑтим ове фајлове у Ñмеће" msgid "Expand All" msgstr "Разгранај Ñве" msgid "&Expand all" msgstr "Раз&гранај Ñве" msgid "&Collapse all" msgstr "&Сакупи Ñве" msgid "&Rename" msgstr "П&реименуј" msgid "&Move to Trash" msgstr "ПремјеÑти у &Ñмеће" msgid "Kid3" msgstr "Кид3" msgid "F&ile" msgstr "Ф&ајл" msgid "Name:" msgstr "Име:" msgid "Filename from Tag 1" msgstr "ознаке 1 у име фајла" msgid "Filename from Tag 2" msgstr "ознаке 2 у име фајла" msgid "Tag 1 from Filename" msgstr "ознаку 1 из имена фајла" msgid "Tag 2 from Filename" msgstr "ознаку 2 из имена фајла" msgid "&Add..." msgstr "&Додај..." msgid "Move &Up" msgstr "Помјери &горе" msgid "Move &Down" msgstr "Помјери до&ље" msgid "&Edit..." msgstr "У&реди..." msgid "&Remove" msgstr "У&клони" msgid "&Insert row" msgstr "&Убаци ред" msgid "&Delete row" msgstr "&Обриши ред" msgid "&Clear row" msgstr "О&чиÑти ред" msgid "Format while editing" msgstr "Форматирај при уређивању" msgid "Case conversion:" msgstr "Величина Ñлова:" msgid "No changes" msgstr "без измјена" msgid "All lowercase" msgstr "Ñве малим" msgid "All uppercase" msgstr "Ñве великим" msgid "First letter uppercase" msgstr "прво Ñлово великим" msgid "All first letters uppercase" msgstr "Ñва прва Ñлова великим" msgid "Locale:" msgstr "Локалитет:" msgid "None" msgstr "Ðикаква" msgid "String replacement:" msgstr "Замјена текÑта:" msgid "From" msgstr "из" msgid "To" msgstr "у" msgid "&Add" msgstr "&Додај" msgid "&Select all" msgstr "И&забери Ñве" msgid "&Deselect all" msgstr "По&ништи избор" msgid "View Picture" msgstr "Погледај Ñлику" msgid "" "Drag album\n" "artwork\n" "here" msgstr "" "Превуци\n" "Ñлику албума\n" "овдје" msgid "Play/Pause" msgstr "Репродукуј/паузирај" msgid "Stop playback" msgstr "ЗауÑтави репродукцију" msgid "Previous Track" msgstr "Претходна нумера" msgid "Next Track" msgstr "Сљедећа нумера" msgid "Close" msgstr "Затвори" msgid "Mute" msgstr "Утишај" msgid "Volume: %1%" msgstr "Јачина: %1%" msgid "Add Item" msgstr "Додај Ñтавку" msgid "Edit Item" msgstr "Уреди Ñтавку" msgid "MusicBrainz Fingerprint" msgstr "МјузикБрејнз ОтиÑак прÑта" msgid "Unrecognized" msgstr "Ðије препознато" msgid "Recognized" msgstr "Препознато" msgid "User Selection" msgstr "КориÑников одабир" msgid "ID Lookup" msgstr "ИД претрага" msgid "Fingerprint" msgstr "ОтиÑак прÑта" msgid "Metadata Lookup" msgstr "Претрага метаподатака" msgid "Amazon" msgstr "Ðмазон" msgid "Discogs" msgstr "ДиÑкогÑ" msgid "gnudb.org" msgstr "gnudb.org" msgid "TrackType.org" msgstr "ТрекТајп.орг" msgid "AENC - Audio encryption" msgstr "AENC - аудио шифровање" msgid "APIC - Attached picture" msgstr "APIC - приложена Ñлика" msgid "COMM - Comments" msgstr "COMM - коментари" msgid "COMR - Commercial" msgstr "COMR - комерцијално" msgid "ENCR - Encryption method registration" msgstr "ENCR - региÑтрација методе шифровања" msgid "EQUA - Equalization" msgstr "EQUA - једначење" msgid "ETCO - Event timing codes" msgstr "" msgid "GEOB - General encapsulated object" msgstr "GEOB - општи угнијежђени објекат" msgid "GRID - Group identification registration" msgstr "GRID - региÑтрација идентификације групе" msgid "IPLS - Involved people list" msgstr "IPLS - ÑпиÑак укључених људи" msgid "LINK - Linked information" msgstr "LINK - повезана информација" msgid "MCDI - Music CD identifier" msgstr "MCDI - идентификатор музичког ЦД-а" msgid "MLLT - MPEG location lookup table" msgstr "" msgid "OWNE - Ownership frame" msgstr "OWNE - влаÑнички оквир" msgid "PRIV - Private frame" msgstr "PRIV - приватни оквир" msgid "PCNT - Play counter" msgstr "PCNT - бројач репродукције" msgid "POPM - Popularimeter" msgstr "POPM - мјерач популарноÑти" msgid "POSS - Position synchronisation frame" msgstr "POSS - кадар Ñинхронизације позиције" msgid "RBUF - Recommended buffer size" msgstr "RBUF - препоручена величина бафера" msgid "RVAD - Relative volume adjustment" msgstr "RVAD - подешавање релативне јачине звука" msgid "RVRB - Reverb" msgstr "RVRB - реверберација" msgid "SYLT - Synchronized lyric/text" msgstr "" msgid "SYTC - Synchronized tempo codes" msgstr "" msgid "TALB - Album/Movie/Show title" msgstr "TALB - наÑлов албума/филма/емиÑије" msgid "TBPM - BPM (beats per minute)" msgstr "TBPM - БПМ (откуцаја у минути)" msgid "TCOM - Composer" msgstr "TCOM - композитор" msgid "TCON - Content type" msgstr "TCON - тип Ñадржаја" msgid "TCOP - Copyright message" msgstr "TCOP - порука ауторÑког права" msgid "TDAT - Date" msgstr "TDAT - датум" msgid "TDLY - Playlist delay" msgstr "TDLY - кашњење лиÑте нумера" msgid "TENC - Encoded by" msgstr "TENC - кодер" msgid "TEXT - Lyricist/Text writer" msgstr "TEXT - пјеÑник/текÑтопиÑац" msgid "TFLT - File type" msgstr "TFLT - тип фајла" msgid "TIME - Time" msgstr "TIME - вријеме" msgid "TIT1 - Content group description" msgstr "TIT1 - Ð¾Ð¿Ð¸Ñ Ð³Ñ€ÑƒÐ¿Ðµ Ñадржаја" msgid "TIT2 - Title/songname/content description" msgstr "TIT2 - наÑлов/име пјеÑме/Ð¾Ð¿Ð¸Ñ Ñадржаја" msgid "TIT3 - Subtitle/Description refinement" msgstr "" msgid "TKEY - Initial key" msgstr "TKEY - почетни кључ" msgid "TLAN - Language(s)" msgstr "TLAN - језик (језици)" msgid "TLEN - Length" msgstr "TLEN - трајање" msgid "TMED - Media type" msgstr "TMED - тип медија" msgid "TOAL - Original album/movie/show title" msgstr "TOAL - изворни наÑлов албума/филма/емиÑије" msgid "TOFN - Original filename" msgstr "TOFN - изворно име" msgid "TOLY - Original lyricist(s)/text writer(s)" msgstr "" msgid "TOPE - Original artist(s)/performer(s)" msgstr "" msgid "TORY - Original release year" msgstr "TORY - изворна година издавања" msgid "TOWN - File owner/licensee" msgstr "" msgid "TPE1 - Lead performer(s)/Soloist(s)" msgstr "" msgid "TPE2 - Band/orchestra/accompaniment" msgstr "" msgid "TPE3 - Conductor/performer refinement" msgstr "" msgid "TPE4 - Interpreted, remixed, or otherwise modified by" msgstr "" msgid "TPOS - Part of a set" msgstr "" msgid "TPUB - Publisher" msgstr "TPUB - издавач" msgid "TRCK - Track number/Position in set" msgstr "" msgid "TRDA - Recording dates" msgstr "TRDA - датуми Ñнимања" msgid "TRSN - Internet radio station name" msgstr "TRSN - име интернет радио Ñтанице" msgid "TRSO - Internet radio station owner" msgstr "TRSO - влаÑник интернет радио Ñтанице" msgid "TSIZ - Size" msgstr "TSIZ - величина" msgid "TSRC - ISRC (international standard recording code)" msgstr "TSRC - ИСРЦ (међународни Ñтандардни кôд Ñнимања)" msgid "TSSE - Software/Hardware and settings used for encoding" msgstr "" msgid "TXXX - User defined text information" msgstr "TXXX - кориÑнички дефиниÑана текÑтуална информација" msgid "TYER - Year" msgstr "TYER - година" msgid "UFID - Unique file identifier" msgstr "UFID - јединÑтвени идентификатор фајла" msgid "USER - Terms of use" msgstr "USER - уÑлови коришћења" msgid "USLT - Unsynchronized lyric/text transcription" msgstr "" msgid "WCOM - Commercial information" msgstr "WCOM - подаци о комерцијализовању" msgid "WCOP - Copyright/Legal information" msgstr "WCOP - ауторÑко право/правне информације" msgid "WOAF - Official audio file webpage" msgstr "" msgid "WOAR - Official artist/performer webpage" msgstr "" msgid "WOAS - Official audio source webpage" msgstr "" msgid "WORS - Official internet radio station homepage" msgstr "" msgid "WPAY - Payment" msgstr "WPAY - плаћање" msgid "WPUB - Official publisher webpage" msgstr "" msgid "WXXX - User defined URL link" msgstr "WXXX - кориÑнички дефиниÑана УРЛ веза" msgid "MusicBrainz Release" msgstr "МјузикБрејнз издање" msgid "ASPI - Audio seek point index" msgstr "" msgid "EQU2 - Equalisation (2)" msgstr "EQU2 - једначење (2)" msgid "RVA2 - Relative volume adjustment (2)" msgstr "RVA2 - подешавање релативне јачине звука (2)" msgid "SEEK - Seek frame" msgstr "SEEK - кадар тражења" msgid "SIGN - Signature frame" msgstr "SIGN - кадар потпиÑа" msgid "TCMP - iTunes compilation flag" msgstr "" msgid "TDEN - Encoding time" msgstr "TDEN - вријеме кодирања" msgid "TDOR - Original release time" msgstr "TDOR - вријеме издавања оригинала" msgid "TDRC - Recording time" msgstr "TDRC - вријеме Ñнимања" msgid "TDRL - Release time" msgstr "TDRL - вријеме издавања" msgid "TDTG - Tagging time" msgstr "TDTG - вријеме означавања" msgid "TIPL - Involved people list" msgstr "TIPL - ÑпиÑак укључених људи" msgid "TMCL - Musician credits list" msgstr "" msgid "TMOO - Mood" msgstr "TMOO - раÑположење" msgid "TPRO - Produced notice" msgstr "" msgid "TSO2 - Album artist sort order" msgstr "TSO2 - редоÑлијед Ñортирања аутора албума" msgid "TSOA - Album sort order" msgstr "TSOA - редоÑлијед Ñортирања албума" msgid "TSOC - Composer sort order" msgstr "TSOC - редоÑлијед Ñортирања композитора" msgid "TSOP - Performer sort order" msgstr "TSOP - редоÑлијед Ñортирања извођача" msgid "TSOT - Title sort order" msgstr "TSOT - редоÑлијед Ñортирања наÑлова" msgid "TSST - Set subtitle" msgstr "TSST - поÑтави поднаÑлов" �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/po/sr@ijekavianlatin.po������������������������������������������������������������������0000664�0000000�0000000�00000101547�12246035113�0016767�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Kid3 Serbian Ijekavian Latin Translation. # Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012. Free Software Foundation, Inc. # # Urs Fleisch <ufleisch@users.sourceforge.net>, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012. # Mladen Pejaković <pejakm@gmail.com>, 2012. msgid "" msgstr "" "Project-Id-Version: Kid3 3.0.2\n" "Report-Msgid-Bugs-To: http://sourceforge.net/p/kid3/bugs/\n" "POT-Creation-Date: 2013-09-11 21:22+0200\n" "PO-Revision-Date: 2011-04-26 23:41+0200\n" "Last-Translator: Mladen Pejaković <pejakm@gmail.com>\n" "Language-Team: Serbian <kde-i18n-sr@lists.kde.org>\n" "Language: sr@ijekavianlatin\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Lokalize 0.3\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" "X-Language: sr\n" "X-Source-Language: C\n" msgid "Timeout" msgstr "Prekovrijeme" msgid "Usage:" msgstr "Upotreba:" msgid "Help" msgstr "Pomoć" msgid "Command name" msgstr "Naredba" msgid "Quit application" msgstr "NapuÅ¡ta aplikaciju" msgid "The current directory has been modified." msgstr "Trenutni direktorijum je izmijenjen." msgid "Type 'exit force' to quit." msgstr "Unesite 'exit force' za izlaz." msgid "Change directory" msgstr "" msgid "%1 does not exist" msgstr "%1 ne postoji" msgid "Print the filename of the current working directory" msgstr "" msgid "Directory list" msgstr "Spisak fascikli" msgid "Saves the changed files" msgstr "Upisuje izmjene fajlova" msgid "Error while writing file:\n" msgstr "GreÅ¡ka prilikom upisa fajla:\n" msgid "Select file" msgstr "Izaberi fajl" msgid "%1 not found" msgstr "%1 nije naÄ‘ena" msgid "Select tag" msgstr "Izbor oznake" msgid "Get tag frame" msgstr "" msgid "Set tag frame" msgstr "" msgid "Could not set \"%1\" for %2" msgstr "" msgid "Revert" msgstr "Vrati" msgid "Import from file or clipboard" msgstr "Uvezi iz fajla ili klipborda" msgid "Format name" msgstr "Format imena" msgid "%1 not found." msgstr "%1 nije naÄ‘en." msgid "Available" msgstr "Dostupno" msgid "Error" msgstr "GreÅ¡ka" msgid "Automatic import" msgstr "Automatski uvozi" msgid "Profile name" msgstr "Ime profila" msgid "Reading Directory" msgstr "" msgid "Started" msgstr "ZapoÄet" msgid "Source" msgstr "Izvor" msgid "Querying" msgstr "Zatražujem" msgid "Fetching" msgstr "Dobavljam" msgid "Data received" msgstr "Podaci primljeni" msgid "Cover" msgstr "Omot" msgid "Finished" msgstr "ZavrÅ¡eno" msgid "Aborted" msgstr "Obustavljen" msgid "Download album cover artwork" msgstr "Potraži sliku omota albuma" msgid "Export to file or clipboard" msgstr "Izvezi u fajl ili klipbord" msgid "Create playlist" msgstr "Napravi listu numera" msgid "Apply filename format" msgstr "Primijeni format naziva fajla" msgid "Apply tag format" msgstr "Primijeni format oznake" msgid "Apply text encoding" msgstr "Primijeni kodiranje teksta" msgid "Rename directory" msgstr "Preimenuj direktorijum" msgid "Number tracks" msgstr "NumeriÅ¡i" msgid "Track number" msgstr "Broj numere" msgid "Filter" msgstr "Filter" msgid "Filter name" msgstr "Ime fajla" msgid "Convert ID3v2.3 to ID3v2.4" msgstr "Pretvori ID3v2.3 u ID3v2.4" msgid "Convert ID3v2.4 to ID3v2.3" msgstr "Pretvori ID3v2.4 u ID3v2.3" msgid "Filename from tag" msgstr "oznake u ime fajla" msgid "Tag from filename" msgstr "oznaku iz imena fajla" msgid "Tag to other tag" msgstr "" msgid "Copy" msgstr "Kopiraj" msgid "Paste" msgstr "Nalijepi" msgid "Remove" msgstr "Ukloni" msgid "Play" msgstr "Reprodukcija" msgid "Parameter" msgstr "Parametar" msgid "File path" msgstr "Putanja fajla" msgid "URL" msgstr "URL" msgid "Tag numbers" msgstr "Broj numere" msgid "Frame name" msgstr "Iz imena fajla" msgid "Frame value" msgstr "" msgid "Format" msgstr "Format" msgid "Command specific" msgstr "Naredba" msgid "Available Commands" msgstr "Naredbe na raspolaganju" msgid "File" msgstr "Fajl" msgid "Name" msgstr "Ime" msgid "Tag 2" msgstr "oznaka 2" msgid "Tag 1" msgstr "oznaka 1" msgid "Tags" msgstr "Oznake" msgid "Unknown command '%1'. Type 'help' for help." msgstr "Nepoznata naredba '%1'. Ukucajte 'help'." msgid "Unknown command '%1', -h for help." msgstr "Nepoznata naredba '%1', -h za pomoć." msgid "Files" msgstr "Fajlovi" msgid "User Actions" msgstr "Radnje korisnika" msgid "Network" msgstr "Mreža" msgid "Plugins" msgstr "PrikljuÄci" msgid "Main Toolbar" msgstr "Glavna traka" msgid "Opens a directory" msgstr "Otvara direktorijum" msgid "Opens a recently used directory" msgstr "Otvara nedavno korišćen direktorijum" msgid "Reverts the changes of all or the selected files" msgstr "PoniÅ¡tava izmjene svih ili izabranih fajlova" msgid "Quits the application" msgstr "NapuÅ¡ta aplikaciju" msgid "Select all files" msgstr "Izaberi sve fajlove" msgid "Deselect all files" msgstr "PoniÅ¡ti izbor svih fajlova" msgid "Configure Shortcuts" msgstr "Podesi preÄice" msgid "Configure Toolbars" msgstr "Podesi trake alata" msgid "Preferences dialog" msgstr "Dijalog podeÅ¡avanja" msgid "O&pen Directory..." msgstr "O&tvori direktorijum..." msgid "&Import..." msgstr "&Uvezi..." msgid "Import from %1..." msgstr "Uvezi sa %1..." msgid "Automatic I&mport..." msgstr "Auto&matski uvozi..." msgid "&Browse Cover Art..." msgstr "Potraži o&mot albuma..." msgid "&Export..." msgstr "Izv&ezi..." msgid "&Create Playlist..." msgstr "Napravi &listu numera..." msgid "Apply &Filename Format" msgstr "Primijeni format imena fa&jla" msgid "Apply &Tag Format" msgstr "Primijeni format &oznake" msgid "Apply Text &Encoding" msgstr "Primijeni kodiranje t&eksta" msgid "&Rename Directory..." msgstr "Preimenuj &direktorijum..." msgid "&Number Tracks..." msgstr "Nu&meriÅ¡i..." msgid "F&ilter..." msgstr "F&ilter..." msgid "Convert ID3v2.3 to ID3v2.&4" msgstr "Pretvori ID3v2.3 u ID3v2.&4" msgid "Convert ID3v2.4 to ID3v2.&3" msgstr "Pretvori ID3v2.4 u ID3v2.&3" msgid "&Play" msgstr "Re&produkuj" msgid "Show &Picture" msgstr "Prikaži s&liku" msgid "Auto &Hide Tags" msgstr "Automatski &sakrij oznake" msgid "Select All in &Directory" msgstr "" msgid "&Previous File" msgstr "&Prethodni fajl" msgid "&Next File" msgstr "&Sljedeći fajl" msgid "From Filename" msgstr "Iz imena fajla" msgid "From Tag 2" msgstr "Iz oznake 2" msgid "From Tag 1" msgstr "Iz oznake 1" msgid "Frames:" msgstr "Kadrovi:" msgid "Edit" msgstr "Uredi" msgid "Add" msgstr "Dodaj" msgid "Delete" msgstr "ObriÅ¡i" msgid "Filename" msgstr "Ime fajla" msgid "Focus" msgstr "Fokus" msgid "File List" msgstr "Spisak fajlova" msgid "Directory List" msgstr "Spisak fascikli" msgid "Configure - Kid3" msgstr "PodeÅ¡avanje - Kid3" msgid "Open" msgstr "Otvori" msgid "Save As" msgstr "SaÄuvaj kao" msgid "directory to open" msgstr "direktorijum za otvaranje" msgid "Kid3 ID3 Tagger" msgstr "Kid3 ureÄ‘ivaÄ ID3 oznaka" #. i18n NAME OF TRANSLATORS msgid "Your names" msgstr "Mladen Pejaković" #. i18n EMAIL OF TRANSLATORS msgid "Your emails" msgstr "pejakm@gmail.com" msgid "&Back" msgstr "&Nazad" msgid "&Forward" msgstr "Na&prijed" msgid "&Find:" msgstr "&NaÄ‘i:" msgid "Find Previous" msgstr "NaÄ‘i prethodno" msgid "Find Next" msgstr "NaÄ‘i sljedeće" msgid "&Close" msgstr "&Zatvori" msgid "&Tags" msgstr "&Oznake" msgid "&Files" msgstr "&Fajlovi" msgid "&User Actions" msgstr "Radnje &korisnika" msgid "&Network" msgstr "&Mreža" msgid "&Plugins" msgstr "&PrikljuÄci" msgid "&Keyboard Shortcuts" msgstr "PreÄice &tastature" msgid "Use custom app&lication font" msgstr "Koristi pose&bni font za aplikaciju" msgid "A&pplication Font..." msgstr "&Font aplikacije..." msgid "Use custom application &style" msgstr "Koristi posebni &stil za aplikaciju" msgid "Unknown" msgstr "Nepoznato" msgid "Use native system file &dialogs" msgstr "Koristi sistemske &dijaloge fajlova" msgid "&Appearance" msgstr "&Izgled" msgid "&Help" msgstr "Po&moć" msgid "&OK" msgstr "&U redu" msgid "&Cancel" msgstr "&Odustani" msgid "The keyboard shortcut '%1' is already assigned to '%2'." msgstr "PreÄica tastature '%1' je već dodijeljena '%2'." msgid "&File" msgstr "&Fajl" msgid "&Open..." msgstr "&Otvori..." msgid "Open &Recent" msgstr "Otvori &nedavno" msgid "&Save" msgstr "&SaÄuvaj" msgid "Re&vert" msgstr "&Vrati" msgid "Import from %1" msgstr "Uvezi sa %1" msgid "Browse album cover artwork" msgstr "Potraži sliku omota albuma" msgid "Create M3U Playlist" msgstr "Napravi M3U listu numera" msgid "&Quit" msgstr "&Napusti" msgid "&Edit" msgstr "&Uredi" msgid "Select &All" msgstr "Izaberi s&ve" msgid "Dese&lect" msgstr "Po&niÅ¡ti izbor" msgid "Select all files in the current directory" msgstr "" msgid "Select previous file" msgstr "Izaberi prethodni fajl" msgid "Select next file" msgstr "Izaberi sljedeći fajl" msgid "&Tools" msgstr "&Alati" msgid "Apply Filename Format" msgstr "Primijeni format naziva fajla" msgid "Apply Tag Format" msgstr "Primijeni format oznake" msgid "Apply Text Encoding" msgstr "Primijeni kodiranje teksta" msgid "Rename Directory" msgstr "Preimenuj direktorijum" msgid "Number Tracks" msgstr "NumeriÅ¡i" msgid "&Settings" msgstr "&PodeÅ¡avanje" msgid "Enables/disables the toolbar" msgstr "Prikazuje/sakriva traku alata" msgid "Show &Toolbar" msgstr "Prikaži traku &alata" msgid "Enables/disables the statusbar" msgstr "Prikazuje/sakriva traku stanja" msgid "Show St&atusbar" msgstr "Prikaži traku &stanja" msgid "Show Picture" msgstr "Prikaži sliku" msgid "Auto Hide Tags" msgstr "Automatski sakrij oznake" msgid "Configure Kid3" msgstr "Podesi Kid3" msgid "&Configure Kid3..." msgstr "&Podesi Kid3..." msgid "Kid3 Handbook" msgstr "Kid3 priruÄnik" msgid "Kid3 &Handbook" msgstr "Kid3 p&riruÄnik" msgid "About Kid3" msgstr "O Kid3-u" msgid "&About Kid3" msgstr "&O Kid3-u" msgid "About Qt" msgstr "O KjuT-u" msgid "About &Qt" msgstr "O &KjuT-u" msgid " [modified]" msgstr " [izmijenjen]" msgid "Toggle the statusbar..." msgstr "Prikaži/sakrij traku stanja..." msgid "Ready." msgstr "Spreman." msgid "&Clear List" msgstr "O&Äisti spisak" msgid "Clear" msgstr "OÄisti" msgid "Reset" msgstr "Ponovo postavi" msgid "Press F2 or double click to edit cell contents." msgstr "" msgid "Action" msgstr "Radnja" msgid "Shortcut" msgstr "PreÄica" msgid "Server" msgstr "Server" msgid "Accuracy" msgstr "Preciznost" msgid "Standard Tags" msgstr "Standardne oznake" msgid "Additional Tags" msgstr "Dodatne oznake" msgid "Cover Art" msgstr "Omot albuma" msgid "Invalid File" msgstr "Neispravna datoteka" msgid "Data received: %1" msgstr "Podaci primljeni: %1" msgid "Request sent..." msgstr "Zahtjev poslat..." msgid "Year" msgstr "Godina" msgid "Length" msgstr "Trajanje" msgid "Filenames" msgstr "Imena fajlova" msgid "URLs" msgstr "URL-ovi" msgid "Directory name" msgstr "Ime direktorijuma" msgid "Encode as URL" msgstr "Kodiraj kao URL" msgid "Confirm" msgstr "Potvrdi" msgid "Output" msgstr "Izlaz" msgid "Command" msgstr "Naredba" msgid "Create directory %1 failed\n" msgstr "Pravljenje direktorijuma %1 nije uspjelo\n" msgid "File %1 already exists\n" msgstr "Fajl %1 već postoji\n" msgid "%1 is not a directory\n" msgstr "%1 nije direktorijum\n" msgid "Rename %1 to %2 failed\n" msgstr "Preimenovanje %1 u %2 nije uspjelo\n" msgid "%1 already exists\n" msgstr "%1 već postoji\n" msgid "%1 is not a file\n" msgstr "%1 nije fajl\n" msgid "New directory name is too different\n" msgstr "Novo ime direktorijuma je previÅ¡e razliÄito\n" msgid "Create directory" msgstr "Napravi direktorijum" msgid "Rename file" msgstr "Preimenuj fajl" msgid "C&lear" msgstr "O&Äisti" msgid "Execute " msgstr "IzvrÅ¡i " msgid "Could not execute " msgstr "Ne mogu da izvrÅ¡im " msgid "True if strings are equal" msgstr "TaÄno ako su nizovi jednaki" msgid "True if string contains substring" msgstr "TaÄno ako niz sadrži podniz" msgid "True if string matches regexp" msgstr "TaÄno ako se niz poklapa sa regizom" msgid "Logical AND" msgstr "LogiÄko I" msgid "Logical OR" msgstr "LogiÄko ILI" msgid "Logical negation" msgstr "LogiÄka negacija" msgid "Data" msgstr "Podaci" msgid "%1 is not writable" msgstr "%1 se ne može pisati" msgid "All Supported Files" msgstr "Svi podržani fajlovi" msgid "All Files" msgstr "Svi fajlovi" msgid "Track" msgstr "Numera" msgid "Absolute path to file" msgstr "Apsolutna putanja do fajla" msgid "Duration" msgstr "Trajanje" msgid "Title" msgstr "Naslov" msgid "Artist" msgstr "IzvoÄ‘aÄ" msgid "Album" msgstr "Album" msgid "Comment" msgstr "Komentar" msgid "Date" msgstr "Godina" msgid "Track Number" msgstr "Broj numere" msgid "Genre" msgstr "Žanr" msgid "Album Artist" msgstr "IzvoÄ‘aÄ albuma" msgid "Arranger" msgstr "Aranžer" msgid "Author" msgstr "Autor" msgid "BPM" msgstr "BPM" msgid "Catalog Number" msgstr "" msgid "Compilation" msgstr "" msgid "Composer" msgstr "Kompozitor" msgid "Conductor" msgstr "Dirigent" msgid "Copyright" msgstr "Autorsko pravo" msgid "Disc Number" msgstr "Broj diska" msgid "Encoded-by" msgstr "Koder" msgid "Encoder Settings" msgstr "Kodiranje podeÅ¡avanja" msgid "Encoding Time" msgstr "Vrijeme kodiranja" msgid "Grouping" msgstr "Grupisanje" msgid "Initial Key" msgstr "PoÄetni kljuÄ" msgid "ISRC" msgstr "ISRC" msgid "Language" msgstr "Jezik" msgid "Lyricist" msgstr "Tekstopisac" msgid "Lyrics" msgstr "Tekst" msgid "Media" msgstr "Medija" msgid "Mood" msgstr "Raspoloženje" msgid "Original Album" msgstr "Izvorni album" msgid "Original Artist" msgstr "Izvorni izvoÄ‘aÄ" msgid "Original Date" msgstr "Izvorni datum" msgid "Part" msgstr "Dio" msgid "Performer" msgstr "IzvoÄ‘aÄ" msgid "Picture" msgstr "Slika" msgid "Publisher" msgstr "IzdavaÄ" msgid "Release Country" msgstr "" msgid "Remixer" msgstr "Remikser" msgid "Sort Album" msgstr "Redoslijed sortiranja albuma" msgid "Sort Album Artist" msgstr "Redoslijed sortiranja autora albuma" msgid "Sort Artist" msgstr "Redoslijed sortiranja izvoÄ‘aÄa" msgid "Sort Composer" msgstr "Redoslijed sortiranja kompozitora" msgid "Sort Name" msgstr "Redoslijed sortiranja naslova" msgid "Subtitle" msgstr "Podnaslov" msgid "Website" msgstr "Vebsajt" msgid "WWW Audio File" msgstr "" msgid "WWW Audio Source" msgstr "" msgid "Number of tracks" msgstr "Broj numera" msgid "Extension" msgstr "Ekstenzija" msgid "Bitrate" msgstr "Bitski protok" msgid "VBR" msgstr "VBR" msgid "Samplerate" msgstr "Uzorkovanje" msgid "Channels" msgstr "Kanala" msgid "Codec" msgstr "Kodek" msgid "Escape for HTML" msgstr "Escape HTML" msgid "Automatic Import" msgstr "Automatski uvozi" msgid "D&estination:" msgstr "O&drediÅ¡te:" msgid "Tag 1 and Tag 2" msgstr "oznaka 1 i oznaka 2" msgid "&Profile:" msgstr "&Profil:" msgid "&Save Settings" msgstr "&SaÄuvaj podeÅ¡avanja" msgid "New" msgstr "Novo" msgid "A&bort" msgstr "O&bustavi" msgid "S&tart" msgstr "&Pokreni" msgid "Import Source" msgstr "Izvor uvoza" msgid "&Server:" msgstr "&Server:" msgid "&Accuracy:" msgstr "&Preciznost:" msgid "&Standard Tags" msgstr "&Standardne oznake" msgid "&Additional Tags" msgstr "Doda&tne oznake" msgid "C&over Art" msgstr "Omot a&lbuma" msgid "Browse Cover Art" msgstr "Pretraga omota albuma" msgid "&Artist/Album" msgstr "I&zvoÄ‘aÄ/album" msgid "&Source" msgstr "&Izvor" msgid "Source:" msgstr "Izvor:" msgid "URL:" msgstr "URL:" msgid "&URL extraction" msgstr "IzvlaÄenje &URL-a" msgid "Match" msgstr "Poklopi" msgid "Picture URL" msgstr "URL slike" msgid "&Browse" msgstr "&Potraži" msgid "Click Browse to start" msgstr "Kliknite na „Potraži“ da biste pokrenuli" msgid "Then drag the picture from the browser to Kid3." msgstr "i onda prevucite sliku sa pregledaÄa u Kid3." msgid "ID3v1" msgstr "ID3v1" msgid "&Mark truncated fields" msgstr "OznaÄi odsjeÄena &polja" msgid "Text &encoding:" msgstr "Kodiranje &teksta:" msgid "ID3v2" msgstr "ID3v2" msgid "Use &track/total number of tracks format" msgstr "Koristi format &numera/ukupno numera" msgid "Track number &digits:" msgstr "Broj &cifara numere:" msgid "&Genre as text instead of numeric string" msgstr "&Žanr kao tekst umjesto brojnog niza" msgid "ISO-8859-1" msgstr "ISO-8859-1" msgid "UTF16" msgstr "UTF-16" msgid "UTF8" msgstr "UTF-8" msgid "&Version used for new tags:" msgstr "I&zdanje za nove oznake:" msgid "ID3v2.3.0" msgstr "ID3v2.3.0" msgid "ID3v2.4.0" msgstr "ID3v2.4.0" msgid "Ogg/Vorbis" msgstr "Ogg/Vorbis" msgid "Co&mment field name:" msgstr "Ime polja ko&mentara:" msgid "&Picture field name:" msgstr "Ime polja &slike:" msgid "Custom &Genres" msgstr "Pose&bni žanrovi" msgid "&Show only custom genres" msgstr "Prik&aži samo posebne žanrove" msgid "&Quick Access Tags" msgstr "" msgid "&Tag Format" msgstr "&Format oznake" msgid "Tag &1" msgstr "Oznaka &1" msgid "Tag &2" msgstr "Oznaka &2" msgid "Tag 1 a&nd Tag 2" msgstr "oznaka 1 &i oznaka 2" msgid "Startup" msgstr "Pokretanje" msgid "&Load last-opened files" msgstr "" msgid "Save" msgstr "SaÄuvaj" msgid "&Preserve file timestamp" msgstr "OÄuvaj &vremensku oznaku fajla" msgid "&Mark changes" msgstr "O&znaÄi izmjene" msgid "F&ilename for cover:" msgstr "&Ime fajla za omot albuma:" msgid "&Filename Format" msgstr "Format imena &fajla" msgid "Browser" msgstr "PregledaÄ" msgid "Web &browser:" msgstr "Veb &pregledaÄ:" msgid "Context &Menu Commands" msgstr "Naredbe kontekstnog me&nija" msgid "&Play on double click" msgstr "&Reprodukuj pri dvokliku" msgid "Proxy" msgstr "Proksi" msgid "&Proxy:" msgstr "&Proksi:" msgid "&Use authentication with proxy" msgstr "Koristi &autentifikaciju za proksi" msgid "Proxy user &name:" msgstr "KorisniÄko &ime za proksi:" msgid "Proxy pass&word:" msgstr "L&ozinka za proksi:" msgid "&Metadata Plugins && Priority" msgstr "&Metapodaci prikljuÄci && Prioritet" msgid "A&vailable Plugins" msgstr "&Dostupni dodaci" msgid "Changes take only effect after a restart!" msgstr "Neke izmene neće biti ukljuÄene pre ponovnog pokretanja!" msgid "From Clip&board" msgstr "Iz klip&borda" msgid "&Import" msgstr "U&vezi" msgid "&Export" msgstr "Izv&ezi" msgid "&View" msgstr "&Prikaz" msgid "Text Encoding" msgstr "Kodiranje teksta" msgid "Text" msgstr "Tekst" msgid "Description" msgstr "Opis" msgid "Owner" msgstr "Vlasnik" msgid "Email" msgstr "E-adresa" msgid "Rating" msgstr "Rejting" msgid "Picture Type" msgstr "Tip slike" msgid "Image format" msgstr "Format slike" msgid "Mimetype" msgstr "Mime" msgid "Counter" msgstr "BrojaÄ" msgid "Identifier" msgstr "Identifikator" msgid "Volume Adjustment" msgstr "PodeÅ¡avanje jaÄine" msgid "Number of Bits" msgstr "Broj bita" msgid "Volume Change Right" msgstr "" msgid "Volume Change Left" msgstr "" msgid "Peak Volume Right" msgstr "" msgid "Peak Volume Left" msgstr "" msgid "Timestamp Format" msgstr "Format vremenske oznake" msgid "Content Type" msgstr "Tip sadržaja" msgid "Price" msgstr "Cijena" msgid "Seller" msgstr "" msgid "Images" msgstr "Slike" msgid "UTF16BE" msgstr "UTF-16BE" msgid "Other" msgstr "Ostalo" msgid "32x32 pixels PNG file icon" msgstr "PNG ikona fajla 32x32 piksela" msgid "Other file icon" msgstr "Ostale ikone fajla" msgid "Cover (front)" msgstr "Omot (prednji)" msgid "Cover (back)" msgstr "Omot (zadnji)" msgid "Leaflet page" msgstr "" msgid "Lead artist/lead performer/soloist" msgstr "" msgid "Artist/performer" msgstr "Umjetnik/izvoÄ‘aÄ" msgid "Band/Orchestra" msgstr "Bend/orkestar" msgid "Lyricist/text writer" msgstr "Pjesnik/tekstopisac" msgid "Recording Location" msgstr "Mjesto snimanja" msgid "During recording" msgstr "Tokom snimanja" msgid "During performance" msgstr "Tokom izvoÄ‘enja" msgid "Movie/video screen capture" msgstr "Snimak ekrana filma/videa" msgid "A bright coloured fish" msgstr "" msgid "Illustration" msgstr "Ilustracija" msgid "Band/artist logotype" msgstr "Logotip benda/izvoÄ‘aÄa" msgid "Publisher/Studio logotype" msgstr "Logotip izdavaÄa/studija" msgid "MPEG frames as unit" msgstr "Broj MPEG kadrova kao jedinica" msgid "Milliseconds as unit" msgstr "Milisekunde kao jedinica" msgid "Text transcription" msgstr "Transkripcija teksta" msgid "Movement/part name" msgstr "" msgid "Events" msgstr "" msgid "Chord" msgstr "Akord" msgid "Trivia/pop up" msgstr "" msgid "Export" msgstr "Izvezi" msgid "Format:" msgstr "Format:" msgid "Header:" msgstr "Zaglavlje:" msgid "Tracks:" msgstr "Numere:" msgid "Footer:" msgstr "Podnožje:" msgid "To F&ile" msgstr "U f&ajl" msgid "To Clip&board" msgstr "U klip&bord" msgid "&Source:" msgstr "&Izvor:" msgid "File Error" msgstr "GreÅ¡ka fajla" msgid "&Filter:" msgstr "&Filter:" msgid "&Expression:" msgstr "&Izraz:" msgid "&Apply" msgstr "&Primijeni" msgid "Accuracy:" msgstr "Preciznost:" msgid "Cover Art:" msgstr "Omot albuma:" msgid "From F&ile/Clipboard..." msgstr "Iz &fajla/klipborda..." msgid "From T&ags..." msgstr "Iz o&znaka..." msgid "&From Server:" msgstr "S&a servera:" msgid "Check maximum allowable time &difference (sec):" msgstr "Provjeri najveću dozvoljenu &vremensku razliku (sek):" msgid "Match with:" msgstr "Poklopi sa:" msgid "&Length" msgstr "tra&janjem" msgid "T&rack" msgstr "&numerom" msgid "&Title" msgstr "nas&lovom" msgid "&Start number:" msgstr "&PoÄetni broj:" msgid "&Destination:" msgstr "&OdrediÅ¡te:" msgid "&Total number of tracks:" msgstr "&Ukupno numera:" msgid "Create Playlist" msgstr "Napravi listu numera" msgid "Playlist File Name" msgstr "Ime fajla liste numera" msgid "Same as &directory name" msgstr "Isto kao ime &direktorijuma" msgid "&Format:" msgstr "&Format:" msgid "Cr&eate in:" msgstr "N&apravi u:" msgid "Current directory" msgstr "trenutnom direktorijumu" msgid "Every directory" msgstr "svim direktorijumima" msgid "Top-level directory" msgstr "najviÅ¡em direktorijumu" msgid "Playlist Content" msgstr "Sadržaj liste numera" msgid "For&mat:" msgstr "For&mat:" msgid "Incl&ude only the selected files" msgstr "UkljuÄi samo &oznaÄene fajlove" msgid "Sort by file &name" msgstr "Sortiraj po &imenu" msgid "Sort by &tag field" msgstr "Sortiraj po &polju oznake" msgid "Use &relative path for files in playlist" msgstr "Koristi &relativnu putanju za fajlove na listi numera" msgid "Use full p&ath for files in playlist" msgstr "Koristi punu put&anju za fajlove na listi numera" msgid "Write only &list of files" msgstr "UpiÅ¡i samo &spisak fajlova" msgid "Write &info using" msgstr "UpiÅ¡i &informacije koristeći" msgid "Preview" msgstr "Pregled" msgid "Create Directory" msgstr "Napravi direktorijum" msgid "From Tag 2 and Tag 1" msgstr "Iz oznake 2 i oznake 1" msgid "From:" msgstr "Iz:" msgid "To:" msgstr "U:" msgid "&Find" msgstr "&NaÄ‘i" msgid "C&GI Path:" msgstr "C&GI putanja:" msgid "Track Title/Artist - Album" msgstr "Numera/izvoÄ‘aÄ - album" msgid "State" msgstr "Stanje" msgid "No result" msgstr "Nema rezultata" msgid "No result selected" msgstr "NiÅ¡ta nije izabrano" msgid "Import from Tags" msgstr "Uvezi iz oznaka" msgid "Extraction:" msgstr "IzvlaÄenje:" msgid "Import from File/Clipboard" msgstr "Uvezi iz fajla/klipborda" msgid "From F&ile" msgstr "Iz &fajla" msgid "Download" msgstr "Preuzmi" msgid "Opening directory..." msgstr "Otvaram direktorijum..." msgid "Saving directory..." msgstr "Upisujem direktorijum..." msgid "" "The current directory has been modified.\n" "Do you want to save it?" msgstr "" "Trenutni direktorijum je izmijenjen.\n" "Želite li da saÄuvate izmjene?" msgid "Warning" msgstr "Upozorenje" msgid "Exiting..." msgstr "Izlazim..." msgid "Creating playlist..." msgstr "Pravim listu numera..." msgid "Import" msgstr "Uvezi" msgid "Error while renaming:\n" msgstr "GreÅ¡ka prilikom preimenovanja:\n" msgid " [filtered]" msgstr " [filtriran]" msgid "Add Frame" msgstr "Dodaj okvir" msgid "Select the frame ID" msgstr "Izaberite ID okvira" msgid "Rename File" msgstr "Preimenuj fajl" msgid "Enter new file name:" msgstr "Unesite novo ime fajla:" msgid "Do you really want to move these %1 items to the trash?" msgstr "Želite li zaista da premjestite ovih %1 stavki u smeće?" msgid "Do you really want to move this item to the trash?" msgstr "Želite li zaista da premjestite ovu %1 stavku u smeće?" msgid "Move to Trash" msgstr "Premjesti u smeće" msgid "Directory must be empty.\n" msgstr "Direktoriujm mora biti prazan.\n" msgid "Could not move these files to the Trash" msgstr "Nisam mogao da premjestim ove fajlove u smeće" msgid "Expand All" msgstr "Razgranaj sve" msgid "&Expand all" msgstr "Raz&granaj sve" msgid "&Collapse all" msgstr "&Sakupi sve" msgid "&Rename" msgstr "P&reimenuj" msgid "&Move to Trash" msgstr "Premjesti u &smeće" msgid "Kid3" msgstr "Kid3" msgid "F&ile" msgstr "F&ajl" msgid "Name:" msgstr "Ime:" msgid "Filename from Tag 1" msgstr "oznake 1 u ime fajla" msgid "Filename from Tag 2" msgstr "oznake 2 u ime fajla" msgid "Tag 1 from Filename" msgstr "oznaku 1 iz imena fajla" msgid "Tag 2 from Filename" msgstr "oznaku 2 iz imena fajla" msgid "&Add..." msgstr "&Dodaj..." msgid "Move &Up" msgstr "Pomjeri &gore" msgid "Move &Down" msgstr "Pomjeri do&lje" msgid "&Edit..." msgstr "U&redi..." msgid "&Remove" msgstr "U&kloni" msgid "&Insert row" msgstr "&Ubaci red" msgid "&Delete row" msgstr "&ObriÅ¡i red" msgid "&Clear row" msgstr "O&Äisti red" msgid "Format while editing" msgstr "Formatiraj pri ureÄ‘ivanju" msgid "Case conversion:" msgstr "VeliÄina slova:" msgid "No changes" msgstr "bez izmjena" msgid "All lowercase" msgstr "sve malim" msgid "All uppercase" msgstr "sve velikim" msgid "First letter uppercase" msgstr "prvo slovo velikim" msgid "All first letters uppercase" msgstr "sva prva slova velikim" msgid "Locale:" msgstr "Lokalitet:" msgid "None" msgstr "nikakav" msgid "String replacement:" msgstr "Zamjena teksta:" msgid "From" msgstr "iz" msgid "To" msgstr "u" msgid "&Add" msgstr "&Dodaj" msgid "&Select all" msgstr "I&zaberi sve" msgid "&Deselect all" msgstr "Po&niÅ¡ti izbor" msgid "View Picture" msgstr "Pogledaj sliku" msgid "" "Drag album\n" "artwork\n" "here" msgstr "" "Prevuci\n" "sliku albuma\n" "ovdje" msgid "Play/Pause" msgstr "Reprodukuj/pauziraj" msgid "Stop playback" msgstr "Zaustavi reprodukciju" msgid "Previous Track" msgstr "Prethodna numera" msgid "Next Track" msgstr "Sljedeća numera" msgid "Close" msgstr "Zatvori" msgid "Mute" msgstr "UtiÅ¡aj" msgid "Volume: %1%" msgstr "JaÄina: %1%" msgid "Add Item" msgstr "Dodaj stavku" msgid "Edit Item" msgstr "Uredi stavku" msgid "MusicBrainz Fingerprint" msgstr "MjuzikBrejnz Otisak prsta" msgid "Unrecognized" msgstr "Nije prepoznato" msgid "Recognized" msgstr "Prepoznato" msgid "User Selection" msgstr "Korisnikov odabir" msgid "ID Lookup" msgstr "ID pretraga" msgid "Fingerprint" msgstr "Otisak prsta" msgid "Metadata Lookup" msgstr "Pretraga metapodataka" msgid "Amazon" msgstr "Amazon" msgid "Discogs" msgstr "Diskogs" msgid "gnudb.org" msgstr "gnudb.org" msgid "TrackType.org" msgstr "TrekTajp.org" msgid "AENC - Audio encryption" msgstr "AENC - audio Å¡ifrovanje" msgid "APIC - Attached picture" msgstr "APIC - priložena slika" msgid "COMM - Comments" msgstr "COMM - komentari" msgid "COMR - Commercial" msgstr "COMR - komercijalno" msgid "ENCR - Encryption method registration" msgstr "ENCR - registracija metode Å¡ifrovanja" msgid "EQUA - Equalization" msgstr "EQUA - jednaÄenje" msgid "ETCO - Event timing codes" msgstr "" msgid "GEOB - General encapsulated object" msgstr "GEOB - opÅ¡ti ugniježđeni objekat" msgid "GRID - Group identification registration" msgstr "GRID - registracija identifikacije grupe" msgid "IPLS - Involved people list" msgstr "IPLS - spisak ukljuÄenih ljudi" msgid "LINK - Linked information" msgstr "LINK - povezana informacija" msgid "MCDI - Music CD identifier" msgstr "MCDI - identifikator muziÄkog CD-a" msgid "MLLT - MPEG location lookup table" msgstr "" msgid "OWNE - Ownership frame" msgstr "OWNE - vlasniÄki okvir" msgid "PRIV - Private frame" msgstr "PRIV - privatni okvir" msgid "PCNT - Play counter" msgstr "PCNT - brojaÄ reprodukcije" msgid "POPM - Popularimeter" msgstr "POPM - mjeraÄ popularnosti" msgid "POSS - Position synchronisation frame" msgstr "POSS - kadar sinhronizacije pozicije" msgid "RBUF - Recommended buffer size" msgstr "RBUF - preporuÄena veliÄina bafera" msgid "RVAD - Relative volume adjustment" msgstr "RVAD - podeÅ¡avanje relativne jaÄine zvuka" msgid "RVRB - Reverb" msgstr "RVRB - reverberacija" msgid "SYLT - Synchronized lyric/text" msgstr "" msgid "SYTC - Synchronized tempo codes" msgstr "" msgid "TALB - Album/Movie/Show title" msgstr "TALB - naslov albuma/filma/emisije" msgid "TBPM - BPM (beats per minute)" msgstr "TBPM - BPM (otkucaja u minuti)" msgid "TCOM - Composer" msgstr "TCOM - kompozitor" msgid "TCON - Content type" msgstr "TCON - tip sadržaja" msgid "TCOP - Copyright message" msgstr "TCOP - poruka autorskog prava" msgid "TDAT - Date" msgstr "TDAT - datum" msgid "TDLY - Playlist delay" msgstr "TDLY - kaÅ¡njenje liste numera" msgid "TENC - Encoded by" msgstr "TENC - koder" msgid "TEXT - Lyricist/Text writer" msgstr "TEXT - pjesnik/tekstopisac" msgid "TFLT - File type" msgstr "TFLT - tip fajla" msgid "TIME - Time" msgstr "TIME - vrijeme" msgid "TIT1 - Content group description" msgstr "TIT1 - opis grupe sadržaja" msgid "TIT2 - Title/songname/content description" msgstr "TIT2 - naslov/ime pjesme/opis sadržaja" msgid "TIT3 - Subtitle/Description refinement" msgstr "" msgid "TKEY - Initial key" msgstr "TKEY - poÄetni kljuÄ" msgid "TLAN - Language(s)" msgstr "TLAN - jezik (jezici)" msgid "TLEN - Length" msgstr "TLEN - trajanje" msgid "TMED - Media type" msgstr "TMED - tip medija" msgid "TOAL - Original album/movie/show title" msgstr "TOAL - izvorni naslov albuma/filma/emisije" msgid "TOFN - Original filename" msgstr "TOFN - izvorno ime" msgid "TOLY - Original lyricist(s)/text writer(s)" msgstr "" msgid "TOPE - Original artist(s)/performer(s)" msgstr "" msgid "TORY - Original release year" msgstr "TORY - izvorna godina izdavanja" msgid "TOWN - File owner/licensee" msgstr "" msgid "TPE1 - Lead performer(s)/Soloist(s)" msgstr "" msgid "TPE2 - Band/orchestra/accompaniment" msgstr "" msgid "TPE3 - Conductor/performer refinement" msgstr "" msgid "TPE4 - Interpreted, remixed, or otherwise modified by" msgstr "" msgid "TPOS - Part of a set" msgstr "" msgid "TPUB - Publisher" msgstr "TPUB - izdavaÄ" msgid "TRCK - Track number/Position in set" msgstr "" msgid "TRDA - Recording dates" msgstr "TRDA - datumi snimanja" msgid "TRSN - Internet radio station name" msgstr "TRSN - ime internet radio stanice" msgid "TRSO - Internet radio station owner" msgstr "TRSO - vlasnik internet radio stanice" msgid "TSIZ - Size" msgstr "TSIZ - veliÄina" msgid "TSRC - ISRC (international standard recording code)" msgstr "TSRC - ISRC (meÄ‘unarodni standardni kôd snimanja)" msgid "TSSE - Software/Hardware and settings used for encoding" msgstr "" msgid "TXXX - User defined text information" msgstr "TXXX - korisniÄki definisana tekstualna informacija" msgid "TYER - Year" msgstr "TYER - godina" msgid "UFID - Unique file identifier" msgstr "UFID - jedinstveni identifikator fajla" msgid "USER - Terms of use" msgstr "USER - uslovi korišćenja" msgid "USLT - Unsynchronized lyric/text transcription" msgstr "" msgid "WCOM - Commercial information" msgstr "WCOM - podaci o komercijalizovanju" msgid "WCOP - Copyright/Legal information" msgstr "WCOP - autorsko pravo/pravne informacije" msgid "WOAF - Official audio file webpage" msgstr "" msgid "WOAR - Official artist/performer webpage" msgstr "" msgid "WOAS - Official audio source webpage" msgstr "" msgid "WORS - Official internet radio station homepage" msgstr "" msgid "WPAY - Payment" msgstr "WPAY - plaćanje" msgid "WPUB - Official publisher webpage" msgstr "" msgid "WXXX - User defined URL link" msgstr "WXXX - korisniÄki definisana URL veza" msgid "MusicBrainz Release" msgstr "MjuzikBrejnz izdanje" msgid "ASPI - Audio seek point index" msgstr "" msgid "EQU2 - Equalisation (2)" msgstr "EQU2 - jednaÄenje (2)" msgid "RVA2 - Relative volume adjustment (2)" msgstr "RVA2 - podeÅ¡avanje relativne jaÄine zvuka (2)" msgid "SEEK - Seek frame" msgstr "SEEK - kadar traženja" msgid "SIGN - Signature frame" msgstr "SIGN - kadar potpisa" msgid "TCMP - iTunes compilation flag" msgstr "" msgid "TDEN - Encoding time" msgstr "TDEN - vrijeme kodiranja" msgid "TDOR - Original release time" msgstr "TDOR - vrijeme izdavanja originala" msgid "TDRC - Recording time" msgstr "TDRC - vrijeme snimanja" msgid "TDRL - Release time" msgstr "TDRL - vrijeme izdavanja" msgid "TDTG - Tagging time" msgstr "TDTG - vrijeme oznaÄavanja" msgid "TIPL - Involved people list" msgstr "TIPL - spisak ukljuÄenih ljudi" msgid "TMCL - Musician credits list" msgstr "" msgid "TMOO - Mood" msgstr "TMOO - raspoloženje" msgid "TPRO - Produced notice" msgstr "" msgid "TSO2 - Album artist sort order" msgstr "TSO2 - redoslijed sortiranja autora albuma" msgid "TSOA - Album sort order" msgstr "TSOA - redoslijed sortiranja albuma" msgid "TSOC - Composer sort order" msgstr "TSOC - redoslijed sortiranja kompozitora" msgid "TSOP - Performer sort order" msgstr "TSOP - redoslijed sortiranja izvoÄ‘aÄa" msgid "TSOT - Title sort order" msgstr "TSOT - redoslijed sortiranja naslova" msgid "TSST - Set subtitle" msgstr "TSST - postavi podnaslov" ���������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/po/sr@latin.po���������������������������������������������������������������������������0000664�0000000�0000000�00000101363�12246035113�0015101�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Kid3 Serbian Latin Translation. # Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012. Free Software Foundation, Inc. # # Urs Fleisch <ufleisch@users.sourceforge.net>, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012. # Mladen Pejaković <pejakm@gmail.com>, 2012. msgid "" msgstr "" "Project-Id-Version: Kid3 3.0.2\n" "Report-Msgid-Bugs-To: http://sourceforge.net/p/kid3/bugs/\n" "POT-Creation-Date: 2013-09-11 21:22+0200\n" "PO-Revision-Date: 2011-04-26 23:41+0200\n" "Last-Translator: Mladen Pejaković <pejakm@gmail.com>\n" "Language-Team: Serbian <kde-i18n-sr@lists.kde.org>\n" "Language: sr@latin\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Lokalize 0.3\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" "X-Language: sr\n" "X-Source-Language: C\n" msgid "Timeout" msgstr "Prekovreme" msgid "Usage:" msgstr "Upotreba:" msgid "Help" msgstr "Pomoć" msgid "Command name" msgstr "Naredba" msgid "Quit application" msgstr "NapuÅ¡ta aplikaciju" msgid "The current directory has been modified." msgstr "Trenutni direktorijum je izmenjen." msgid "Type 'exit force' to quit." msgstr "Unesite 'exit force' za izlaz." msgid "Change directory" msgstr "" msgid "%1 does not exist" msgstr "%1 ne postoji" msgid "Print the filename of the current working directory" msgstr "" msgid "Directory list" msgstr "Spisak fascikli" msgid "Saves the changed files" msgstr "Upisuje izmene fajlova" msgid "Error while writing file:\n" msgstr "GreÅ¡ka prilikom upisa fajla:\n" msgid "Select file" msgstr "Izaberi fajl" msgid "%1 not found" msgstr "%1 nije naÄ‘ena" msgid "Select tag" msgstr "Izbor oznake" msgid "Get tag frame" msgstr "" msgid "Set tag frame" msgstr "" msgid "Could not set \"%1\" for %2" msgstr "" msgid "Revert" msgstr "Vrati" msgid "Import from file or clipboard" msgstr "Uvezi iz fajla ili klipborda" msgid "Format name" msgstr "Format imena" msgid "%1 not found." msgstr "%1 nije naÄ‘en." msgid "Available" msgstr "Dostupno" msgid "Error" msgstr "GreÅ¡ka" msgid "Automatic import" msgstr "Automatski uvozi" msgid "Profile name" msgstr "Ime profila" msgid "Reading Directory" msgstr "" msgid "Started" msgstr "ZapoÄet" msgid "Source" msgstr "Izvor" msgid "Querying" msgstr "Zatražujem" msgid "Fetching" msgstr "Dobavljam" msgid "Data received" msgstr "Podaci primljeni" msgid "Cover" msgstr "Omot" msgid "Finished" msgstr "ZavrÅ¡eno" msgid "Aborted" msgstr "Obustavljen" msgid "Download album cover artwork" msgstr "Potraži sliku omota albuma" msgid "Export to file or clipboard" msgstr "Izvezi u fajl ili klipbord" msgid "Create playlist" msgstr "Napravi listu numera" msgid "Apply filename format" msgstr "Primeni format naziva fajla" msgid "Apply tag format" msgstr "Primeni format oznake" msgid "Apply text encoding" msgstr "Primijeni kodiranje teksta" msgid "Rename directory" msgstr "Preimenuj direktorijum" msgid "Number tracks" msgstr "NumeriÅ¡i" msgid "Track number" msgstr "Broj numere" msgid "Filter" msgstr "Filter" msgid "Filter name" msgstr "Ime fajla" msgid "Convert ID3v2.3 to ID3v2.4" msgstr "Pretvori ID3v2.3 u ID3v2.4" msgid "Convert ID3v2.4 to ID3v2.3" msgstr "Pretvori ID3v2.4 u ID3v2.3" msgid "Filename from tag" msgstr "oznake u ime fajla" msgid "Tag from filename" msgstr "oznaku iz imena fajla" msgid "Tag to other tag" msgstr "" msgid "Copy" msgstr "Kopiraj" msgid "Paste" msgstr "Nalepi" msgid "Remove" msgstr "Ukloni" msgid "Play" msgstr "Reprodukcija" msgid "Parameter" msgstr "Parametar" msgid "File path" msgstr "Putanja fajla" msgid "URL" msgstr "URL" msgid "Tag numbers" msgstr "Broj numere" msgid "Frame name" msgstr "Iz imena fajla" msgid "Frame value" msgstr "" msgid "Format" msgstr "Format" msgid "Command specific" msgstr "Naredba" msgid "Available Commands" msgstr "Naredbe na raspolaganju" msgid "File" msgstr "Fajl" msgid "Name" msgstr "Ime" msgid "Tag 2" msgstr "oznaka 2" msgid "Tag 1" msgstr "oznaka 1" msgid "Tags" msgstr "Oznake" msgid "Unknown command '%1'. Type 'help' for help." msgstr "Nepoznata naredba '%1'. Ukucajte 'help'." msgid "Unknown command '%1', -h for help." msgstr "Nepoznata naredba '%1', -h za pomoć." msgid "Files" msgstr "Fajlovi" msgid "User Actions" msgstr "Radnje korisnika" msgid "Network" msgstr "Mreža" msgid "Plugins" msgstr "PrikljuÄci" msgid "Main Toolbar" msgstr "Glavna traka" msgid "Opens a directory" msgstr "Otvara direktorijum" msgid "Opens a recently used directory" msgstr "Otvara nedavno korišćen direktorijum" msgid "Reverts the changes of all or the selected files" msgstr "PoniÅ¡tava izmene svih ili izabranih fajlova" msgid "Quits the application" msgstr "NapuÅ¡ta aplikaciju" msgid "Select all files" msgstr "Izaberi sve fajlove" msgid "Deselect all files" msgstr "PoniÅ¡ti izbor svih fajlova" msgid "Configure Shortcuts" msgstr "Podesi preÄice" msgid "Configure Toolbars" msgstr "Podesi trake alata" msgid "Preferences dialog" msgstr "Dijalog podeÅ¡avanja" msgid "O&pen Directory..." msgstr "O&tvori direktorijum..." msgid "&Import..." msgstr "&Uvezi..." msgid "Import from %1..." msgstr "Uvezi sa %1..." msgid "Automatic I&mport..." msgstr "Auto&matski uvozi..." msgid "&Browse Cover Art..." msgstr "Potraži o&mot albuma..." msgid "&Export..." msgstr "Izv&ezi..." msgid "&Create Playlist..." msgstr "Napravi &listu numera..." msgid "Apply &Filename Format" msgstr "Primeni format imena fa&jla" msgid "Apply &Tag Format" msgstr "Primeni format &oznake" msgid "Apply Text &Encoding" msgstr "Primijeni kodiranje t&eksta" msgid "&Rename Directory..." msgstr "Preimenuj &direktorijum..." msgid "&Number Tracks..." msgstr "Nu&meriÅ¡i..." msgid "F&ilter..." msgstr "F&ilter..." msgid "Convert ID3v2.3 to ID3v2.&4" msgstr "Pretvori ID3v2.3 u ID3v2.&4" msgid "Convert ID3v2.4 to ID3v2.&3" msgstr "Pretvori ID3v2.4 u ID3v2.&3" msgid "&Play" msgstr "Re&produkuj" msgid "Show &Picture" msgstr "Prikaži s&liku" msgid "Auto &Hide Tags" msgstr "Automatski &sakrij oznake" msgid "Select All in &Directory" msgstr "" msgid "&Previous File" msgstr "&Prethodni fajl" msgid "&Next File" msgstr "&Sledeći fajl" msgid "From Filename" msgstr "Iz imena fajla" msgid "From Tag 2" msgstr "Iz oznake 2" msgid "From Tag 1" msgstr "Iz oznake 1" msgid "Frames:" msgstr "Kadrovi:" msgid "Edit" msgstr "Uredi" msgid "Add" msgstr "Dodaj" msgid "Delete" msgstr "ObriÅ¡i" msgid "Filename" msgstr "Ime fajla" msgid "Focus" msgstr "Fokus" msgid "File List" msgstr "Spisak fajlova" msgid "Directory List" msgstr "Spisak fascikli" msgid "Configure - Kid3" msgstr "PodeÅ¡avanje - Kid3" msgid "Open" msgstr "Otvori" msgid "Save As" msgstr "SaÄuvaj kao" msgid "directory to open" msgstr "direktorijum za otvaranje" msgid "Kid3 ID3 Tagger" msgstr "Kid3 ureÄ‘ivaÄ ID3 oznaka" #. i18n NAME OF TRANSLATORS msgid "Your names" msgstr "Mladen Pejaković" #. i18n EMAIL OF TRANSLATORS msgid "Your emails" msgstr "pejakm@gmail.com" msgid "&Back" msgstr "&Nazad" msgid "&Forward" msgstr "Na&pred" msgid "&Find:" msgstr "&NaÄ‘i:" msgid "Find Previous" msgstr "NaÄ‘i prethodno" msgid "Find Next" msgstr "NaÄ‘i sledeće" msgid "&Close" msgstr "&Zatvori" msgid "&Tags" msgstr "&Oznake" msgid "&Files" msgstr "&Fajlovi" msgid "&User Actions" msgstr "Radnje &korisnika" msgid "&Network" msgstr "&Mreža" msgid "&Plugins" msgstr "&PrikljuÄci" msgid "&Keyboard Shortcuts" msgstr "PreÄice &tastature" msgid "Use custom app&lication font" msgstr "Koristi pose&bni font za aplikaciju" msgid "A&pplication Font..." msgstr "&Font aplikacije..." msgid "Use custom application &style" msgstr "Koristi posebni &stil za aplikaciju" msgid "Unknown" msgstr "Nepoznato" msgid "Use native system file &dialogs" msgstr "Koristi sistemske &dijaloge fajlova" msgid "&Appearance" msgstr "&Izgled" msgid "&Help" msgstr "Po&moć" msgid "&OK" msgstr "&U redu" msgid "&Cancel" msgstr "&Odustani" msgid "The keyboard shortcut '%1' is already assigned to '%2'." msgstr "PreÄica tastature '%1' je već dodeljena '%2'." msgid "&File" msgstr "&Fajl" msgid "&Open..." msgstr "&Otvori..." msgid "Open &Recent" msgstr "Otvori &nedavno" msgid "&Save" msgstr "&SaÄuvaj" msgid "Re&vert" msgstr "&Vrati" msgid "Import from %1" msgstr "Uvezi sa %1" msgid "Browse album cover artwork" msgstr "Potraži sliku omota albuma" msgid "Create M3U Playlist" msgstr "Napravi M3U listu numera" msgid "&Quit" msgstr "&Napusti" msgid "&Edit" msgstr "&Uredi" msgid "Select &All" msgstr "Izaberi s&ve" msgid "Dese&lect" msgstr "Po&niÅ¡ti izbor" msgid "Select all files in the current directory" msgstr "" msgid "Select previous file" msgstr "Izaberi prethodni fajl" msgid "Select next file" msgstr "Izaberi sledeći fajl" msgid "&Tools" msgstr "&Alatke" msgid "Apply Filename Format" msgstr "Primeni format naziva fajla" msgid "Apply Tag Format" msgstr "Primeni format oznake" msgid "Apply Text Encoding" msgstr "Primijeni kodiranje teksta" msgid "Rename Directory" msgstr "Preimenuj direktorijum" msgid "Number Tracks" msgstr "NumeriÅ¡i" msgid "&Settings" msgstr "&PodeÅ¡avanje" msgid "Enables/disables the toolbar" msgstr "Prikazuje/sakriva traku alata" msgid "Show &Toolbar" msgstr "Prikaži traku &alata" msgid "Enables/disables the statusbar" msgstr "Prikazuje/sakriva traku stanja" msgid "Show St&atusbar" msgstr "Prikaži traku &stanja" msgid "Show Picture" msgstr "Prikaži sliku" msgid "Auto Hide Tags" msgstr "Automatski sakrij oznake" msgid "Configure Kid3" msgstr "Podesi Kid3" msgid "&Configure Kid3..." msgstr "&Podesi Kid3..." msgid "Kid3 Handbook" msgstr "Kid3 priruÄnik" msgid "Kid3 &Handbook" msgstr "Kid3 p&riruÄnik" msgid "About Kid3" msgstr "O Kid3-u" msgid "&About Kid3" msgstr "&O Kid3-u" msgid "About Qt" msgstr "O KjuT-u" msgid "About &Qt" msgstr "O &KjuT-u" msgid " [modified]" msgstr " [izmenjen]" msgid "Toggle the statusbar..." msgstr "Prikaži/sakrij traku stanja..." msgid "Ready." msgstr "Spreman." msgid "&Clear List" msgstr "O&Äisti spisak" msgid "Clear" msgstr "OÄisti" msgid "Reset" msgstr "Ponovo postavi" msgid "Press F2 or double click to edit cell contents." msgstr "" msgid "Action" msgstr "Radnja" msgid "Shortcut" msgstr "PreÄica" msgid "Server" msgstr "Server" msgid "Accuracy" msgstr "Preciznost" msgid "Standard Tags" msgstr "Standardne oznake" msgid "Additional Tags" msgstr "Dodatne oznake" msgid "Cover Art" msgstr "Omot albuma" msgid "Invalid File" msgstr "Neispravna datoteka" msgid "Data received: %1" msgstr "Podaci primljeni: %1" msgid "Request sent..." msgstr "Zahtev poslat..." msgid "Year" msgstr "Godina" msgid "Length" msgstr "Trajanje" msgid "Filenames" msgstr "Imena fajlova" msgid "URLs" msgstr "URL-ovi" msgid "Directory name" msgstr "Ime direktorijuma" msgid "Encode as URL" msgstr "Kodiraj kao URL" msgid "Confirm" msgstr "Potvrdi" msgid "Output" msgstr "Izlaz" msgid "Command" msgstr "Naredba" msgid "Create directory %1 failed\n" msgstr "Pravljenje direktorijuma %1 nije uspelo\n" msgid "File %1 already exists\n" msgstr "Fajl %1 već postoji\n" msgid "%1 is not a directory\n" msgstr "%1 nije direktorijum\n" msgid "Rename %1 to %2 failed\n" msgstr "Preimenovanje %1 u %2 nije uspelo\n" msgid "%1 already exists\n" msgstr "%1 već postoji\n" msgid "%1 is not a file\n" msgstr "%1 nije fajl\n" msgid "New directory name is too different\n" msgstr "Novo ime direktorijuma je previÅ¡e razliÄito\n" msgid "Create directory" msgstr "Napravi direktorijum" msgid "Rename file" msgstr "Preimenuj fajl" msgid "C&lear" msgstr "O&Äisti" msgid "Execute " msgstr "IzvrÅ¡i " msgid "Could not execute " msgstr "Ne mogu da izvrÅ¡im " msgid "True if strings are equal" msgstr "TaÄno ako su nizovi jednaki" msgid "True if string contains substring" msgstr "TaÄno ako niz sadrži podniz" msgid "True if string matches regexp" msgstr "TaÄno ako se niz poklapa sa regizom" msgid "Logical AND" msgstr "LogiÄko I" msgid "Logical OR" msgstr "LogiÄko ILI" msgid "Logical negation" msgstr "LogiÄka negacija" msgid "Data" msgstr "Podaci" msgid "%1 is not writable" msgstr "%1 se ne može pisati" msgid "All Supported Files" msgstr "Svi podržani fajlovi" msgid "All Files" msgstr "Svi fajlovi" msgid "Track" msgstr "Numera" msgid "Absolute path to file" msgstr "Apsolutna putanja do fajla" msgid "Duration" msgstr "Trajanje" msgid "Title" msgstr "Naslov" msgid "Artist" msgstr "IzvoÄ‘aÄ" msgid "Album" msgstr "Album" msgid "Comment" msgstr "Komentar" msgid "Date" msgstr "Godina" msgid "Track Number" msgstr "Broj numere" msgid "Genre" msgstr "Žanr" msgid "Album Artist" msgstr "IzvoÄ‘aÄ albuma" msgid "Arranger" msgstr "Aranžer" msgid "Author" msgstr "Autor" msgid "BPM" msgstr "BPM" msgid "Catalog Number" msgstr "" msgid "Compilation" msgstr "" msgid "Composer" msgstr "Kompozitor" msgid "Conductor" msgstr "Dirigent" msgid "Copyright" msgstr "Autorsko pravo" msgid "Disc Number" msgstr "Broj diska" msgid "Encoded-by" msgstr "Koder" msgid "Encoder Settings" msgstr "Kodiranje podeÅ¡avanja" msgid "Encoding Time" msgstr "Vreme kodiranja" msgid "Grouping" msgstr "Grupisanje" msgid "Initial Key" msgstr "PoÄetni kljuÄ" msgid "ISRC" msgstr "ISRC" msgid "Language" msgstr "Jezik" msgid "Lyricist" msgstr "Tekstopisac" msgid "Lyrics" msgstr "Tekst" msgid "Media" msgstr "Medija" msgid "Mood" msgstr "Raspoloženje" msgid "Original Album" msgstr "Izvorni album" msgid "Original Artist" msgstr "Izvorni izvoÄ‘aÄ" msgid "Original Date" msgstr "Izvorni datum" msgid "Part" msgstr "Dio" msgid "Performer" msgstr "IzvoÄ‘aÄ" msgid "Picture" msgstr "Slika" msgid "Publisher" msgstr "IzdavaÄ" msgid "Release Country" msgstr "" msgid "Remixer" msgstr "Remikser" msgid "Sort Album" msgstr "Redosled sortiranja albuma" msgid "Sort Album Artist" msgstr "Redosled sortiranja autora albuma" msgid "Sort Artist" msgstr "Redosled sortiranja izvoÄ‘aÄa" msgid "Sort Composer" msgstr "Redosled sortiranja kompozitora" msgid "Sort Name" msgstr "Redosled sortiranja naslova" msgid "Subtitle" msgstr "Podnaslov" msgid "Website" msgstr "Vebsajt" msgid "WWW Audio File" msgstr "" msgid "WWW Audio Source" msgstr "" msgid "Number of tracks" msgstr "Broj numera" msgid "Extension" msgstr "Ekstenzija" msgid "Bitrate" msgstr "Bitski protok" msgid "VBR" msgstr "VBR" msgid "Samplerate" msgstr "Uzorkovanje" msgid "Channels" msgstr "Kanala" msgid "Codec" msgstr "Kodek" msgid "Escape for HTML" msgstr "Escape HTML" msgid "Automatic Import" msgstr "Automatski uvozi" msgid "D&estination:" msgstr "O&drediÅ¡te:" msgid "Tag 1 and Tag 2" msgstr "oznaka 1 i oznaka 2" msgid "&Profile:" msgstr "&Profil:" msgid "&Save Settings" msgstr "&SaÄuvaj podeÅ¡avanja" msgid "New" msgstr "Novo" msgid "A&bort" msgstr "O&bustavi" msgid "S&tart" msgstr "&Pokreni" msgid "Import Source" msgstr "Izvor uvoza" msgid "&Server:" msgstr "&Server:" msgid "&Accuracy:" msgstr "&Preciznost:" msgid "&Standard Tags" msgstr "&Standardne oznake" msgid "&Additional Tags" msgstr "Doda&tne oznake" msgid "C&over Art" msgstr "Omot a&lbuma" msgid "Browse Cover Art" msgstr "Pretraga omota albuma" msgid "&Artist/Album" msgstr "I&zvoÄ‘aÄ/album" msgid "&Source" msgstr "&Izvor" msgid "Source:" msgstr "Izvor:" msgid "URL:" msgstr "URL:" msgid "&URL extraction" msgstr "IzvlaÄenje &URL-a" msgid "Match" msgstr "Poklopi" msgid "Picture URL" msgstr "URL slike" msgid "&Browse" msgstr "&Potraži" msgid "Click Browse to start" msgstr "Kliknite na „Potraži“ da biste pokrenuli" msgid "Then drag the picture from the browser to Kid3." msgstr "i onda prevucite sliku sa pregledaÄa u Kid3." msgid "ID3v1" msgstr "ID3v1" msgid "&Mark truncated fields" msgstr "OznaÄi odseÄena &polja" msgid "Text &encoding:" msgstr "Kodiranje &teksta:" msgid "ID3v2" msgstr "ID3v2" msgid "Use &track/total number of tracks format" msgstr "Koristi format &numera/ukupno numera" msgid "Track number &digits:" msgstr "Broj &cifara numere:" msgid "&Genre as text instead of numeric string" msgstr "&Žanr kao tekst umesto brojnog niza" msgid "ISO-8859-1" msgstr "ISO-8859-1" msgid "UTF16" msgstr "UTF-16" msgid "UTF8" msgstr "UTF-8" msgid "&Version used for new tags:" msgstr "I&zdanje za nove oznake:" msgid "ID3v2.3.0" msgstr "ID3v2.3.0" msgid "ID3v2.4.0" msgstr "ID3v2.4.0" msgid "Ogg/Vorbis" msgstr "Ogg/Vorbis" msgid "Co&mment field name:" msgstr "Ime polja ko&mentara:" msgid "&Picture field name:" msgstr "Ime polja &slike:" msgid "Custom &Genres" msgstr "Pose&bni žanrovi" msgid "&Show only custom genres" msgstr "Prik&aži samo posebne žanrove" msgid "&Quick Access Tags" msgstr "" msgid "&Tag Format" msgstr "&Format oznake" msgid "Tag &1" msgstr "Oznaka &1" msgid "Tag &2" msgstr "Oznaka &2" msgid "Tag 1 a&nd Tag 2" msgstr "oznaka 1 &i oznaka 2" msgid "Startup" msgstr "Pokretanje" msgid "&Load last-opened files" msgstr "" msgid "Save" msgstr "SaÄuvaj" msgid "&Preserve file timestamp" msgstr "OÄuvaj &vremensku oznaku fajla" msgid "&Mark changes" msgstr "O&znaÄi izmene" msgid "F&ilename for cover:" msgstr "&Ime fajla za omot albuma:" msgid "&Filename Format" msgstr "Format imena &fajla" msgid "Browser" msgstr "PregledaÄ" msgid "Web &browser:" msgstr "Veb &pregledaÄ:" msgid "Context &Menu Commands" msgstr "Naredbe kontekstnog me&nija" msgid "&Play on double click" msgstr "&Reprodukuj pri dvokliku" msgid "Proxy" msgstr "Proksi" msgid "&Proxy:" msgstr "&Proksi:" msgid "&Use authentication with proxy" msgstr "Koristi &autentifikaciju za proksi" msgid "Proxy user &name:" msgstr "KorisniÄko &ime za proksi:" msgid "Proxy pass&word:" msgstr "L&ozinka za proksi:" msgid "&Metadata Plugins && Priority" msgstr "&Metapodaci prikljuÄci && Prioritet" msgid "A&vailable Plugins" msgstr "&Dostupni dodaci" msgid "Changes take only effect after a restart!" msgstr "Neke izmene neće biti ukljuÄene pre ponovnog pokretanja!" msgid "From Clip&board" msgstr "Iz klip&borda" msgid "&Import" msgstr "U&vezi" msgid "&Export" msgstr "Izv&ezi" msgid "&View" msgstr "&Prikaz" msgid "Text Encoding" msgstr "Kodiranje teksta" msgid "Text" msgstr "Tekst" msgid "Description" msgstr "Opis" msgid "Owner" msgstr "Vlasnik" msgid "Email" msgstr "E-adresa" msgid "Rating" msgstr "Rejting" msgid "Picture Type" msgstr "Tip slike" msgid "Image format" msgstr "Format slike" msgid "Mimetype" msgstr "Mime" msgid "Counter" msgstr "BrojaÄ" msgid "Identifier" msgstr "Identifikator" msgid "Volume Adjustment" msgstr "PodeÅ¡avanje jaÄine" msgid "Number of Bits" msgstr "Broj bita" msgid "Volume Change Right" msgstr "" msgid "Volume Change Left" msgstr "" msgid "Peak Volume Right" msgstr "" msgid "Peak Volume Left" msgstr "" msgid "Timestamp Format" msgstr "Format vremenske oznake" msgid "Content Type" msgstr "Tip sadržaja" msgid "Price" msgstr "Cena" msgid "Seller" msgstr "" msgid "Images" msgstr "Slike" msgid "UTF16BE" msgstr "UTF-16BE" msgid "Other" msgstr "Ostalo" msgid "32x32 pixels PNG file icon" msgstr "PNG ikona fajla 32x32 piksela" msgid "Other file icon" msgstr "Ostale ikone fajla" msgid "Cover (front)" msgstr "Omot (prednji)" msgid "Cover (back)" msgstr "Omot (zadnji)" msgid "Leaflet page" msgstr "" msgid "Lead artist/lead performer/soloist" msgstr "" msgid "Artist/performer" msgstr "Umetnik/izvoÄ‘aÄ" msgid "Band/Orchestra" msgstr "Bend/orkestar" msgid "Lyricist/text writer" msgstr "Pesnik/tekstopisac" msgid "Recording Location" msgstr "Mesto snimanja" msgid "During recording" msgstr "Tokom snimanja" msgid "During performance" msgstr "Tokom izvoÄ‘enja" msgid "Movie/video screen capture" msgstr "Snimak ekrana filma/videa" msgid "A bright coloured fish" msgstr "" msgid "Illustration" msgstr "Ilustracija" msgid "Band/artist logotype" msgstr "Logotip benda/izvoÄ‘aÄa" msgid "Publisher/Studio logotype" msgstr "Logotip izdavaÄa/studija" msgid "MPEG frames as unit" msgstr "Broj MPEG kadrova kao jedinica" msgid "Milliseconds as unit" msgstr "Milisekunde kao jedinica" msgid "Text transcription" msgstr "Transkripcija teksta" msgid "Movement/part name" msgstr "" msgid "Events" msgstr "" msgid "Chord" msgstr "Akord" msgid "Trivia/pop up" msgstr "" msgid "Export" msgstr "Izvezi" msgid "Format:" msgstr "Format:" msgid "Header:" msgstr "Zaglavlje:" msgid "Tracks:" msgstr "Numere:" msgid "Footer:" msgstr "Podnožje:" msgid "To F&ile" msgstr "U f&ajl" msgid "To Clip&board" msgstr "U klip&bord" msgid "&Source:" msgstr "&Izvor:" msgid "File Error" msgstr "GreÅ¡ka fajla" msgid "&Filter:" msgstr "&Filter:" msgid "&Expression:" msgstr "&Izraz:" msgid "&Apply" msgstr "&Primeni" msgid "Accuracy:" msgstr "Preciznost:" msgid "Cover Art:" msgstr "Omot albuma:" msgid "From F&ile/Clipboard..." msgstr "Iz &fajla/klipborda..." msgid "From T&ags..." msgstr "Iz o&znaka..." msgid "&From Server:" msgstr "S&a servera:" msgid "Check maximum allowable time &difference (sec):" msgstr "Proveri najveću dozvoljenu &vremensku razliku (sek):" msgid "Match with:" msgstr "Poklopi sa:" msgid "&Length" msgstr "tra&janjem" msgid "T&rack" msgstr "&numerom" msgid "&Title" msgstr "nas&lovom" msgid "&Start number:" msgstr "&PoÄetni broj:" msgid "&Destination:" msgstr "&OdrediÅ¡te:" msgid "&Total number of tracks:" msgstr "&Ukupno numera:" msgid "Create Playlist" msgstr "Napravi listu numera" msgid "Playlist File Name" msgstr "Ime fajla liste numera" msgid "Same as &directory name" msgstr "Isto kao ime &direktorijuma" msgid "&Format:" msgstr "&Format:" msgid "Cr&eate in:" msgstr "N&apravi u:" msgid "Current directory" msgstr "trenutnom direktorijumu" msgid "Every directory" msgstr "svim direktorijumima" msgid "Top-level directory" msgstr "najviÅ¡em direktorijumu" msgid "Playlist Content" msgstr "Sadržaj liste numera" msgid "For&mat:" msgstr "For&mat:" msgid "Incl&ude only the selected files" msgstr "UkljuÄi samo &oznaÄene fajlove" msgid "Sort by file &name" msgstr "Sortiraj po &imenu" msgid "Sort by &tag field" msgstr "Sortiraj po &polju oznake" msgid "Use &relative path for files in playlist" msgstr "Koristi &relativnu putanju za fajlove na listi numera" msgid "Use full p&ath for files in playlist" msgstr "Koristi punu put&anju za fajlove na listi numera" msgid "Write only &list of files" msgstr "UpiÅ¡i samo &spisak fajlova" msgid "Write &info using" msgstr "UpiÅ¡i &informacije koristeći" msgid "Preview" msgstr "Pregled" msgid "Create Directory" msgstr "Napravi direktorijum" msgid "From Tag 2 and Tag 1" msgstr "Iz oznake 2 i oznake 1" msgid "From:" msgstr "Iz:" msgid "To:" msgstr "U:" msgid "&Find" msgstr "&NaÄ‘i" msgid "C&GI Path:" msgstr "C&GI putanja:" msgid "Track Title/Artist - Album" msgstr "Numera/izvoÄ‘aÄ - album" msgid "State" msgstr "Stanje" msgid "No result" msgstr "Nema rezultata" msgid "No result selected" msgstr "NiÅ¡ta nije izabrano" msgid "Import from Tags" msgstr "Uvezi iz oznaka" msgid "Extraction:" msgstr "IzvlaÄenje:" msgid "Import from File/Clipboard" msgstr "Uvezi iz fajla/klipborda" msgid "From F&ile" msgstr "Iz &fajla" msgid "Download" msgstr "Preuzmi" msgid "Opening directory..." msgstr "Otvaram direktorijum..." msgid "Saving directory..." msgstr "Upisujem direktorijum..." msgid "" "The current directory has been modified.\n" "Do you want to save it?" msgstr "" "Trenutni direktorijum je izmenjen.\n" "Želite li da saÄuvate izmene?" msgid "Warning" msgstr "Upozorenje" msgid "Exiting..." msgstr "Izlazim..." msgid "Creating playlist..." msgstr "Pravim listu numera..." msgid "Import" msgstr "Uvezi" msgid "Error while renaming:\n" msgstr "GreÅ¡ka prilikom preimenovanja:\n" msgid " [filtered]" msgstr " [filtriran]" msgid "Add Frame" msgstr "Dodaj okvir" msgid "Select the frame ID" msgstr "Izaberite ID okvira" msgid "Rename File" msgstr "Preimenuj fajl" msgid "Enter new file name:" msgstr "Unesite novo ime fajla:" msgid "Do you really want to move these %1 items to the trash?" msgstr "Želite li zaista da premestite ovih %1 stavki u smeće?" msgid "Do you really want to move this item to the trash?" msgstr "Želite li zaista da premestite ovu %1 stavku u smeće?" msgid "Move to Trash" msgstr "Premesti u smeće" msgid "Directory must be empty.\n" msgstr "Direktoriujm mora biti prazan.\n" msgid "Could not move these files to the Trash" msgstr "Nisam mogao da premestim ove fajlove u smeće" msgid "Expand All" msgstr "Razgranaj sve" msgid "&Expand all" msgstr "Raz&granaj sve" msgid "&Collapse all" msgstr "&Sakupi sve" msgid "&Rename" msgstr "P&reimenuj" msgid "&Move to Trash" msgstr "Premesti u &smeće" msgid "Kid3" msgstr "Kid3" msgid "F&ile" msgstr "F&ajl" msgid "Name:" msgstr "Ime:" msgid "Filename from Tag 1" msgstr "oznake 1 u ime fajla" msgid "Filename from Tag 2" msgstr "oznake 2 u ime fajla" msgid "Tag 1 from Filename" msgstr "oznaku 1 iz imena fajla" msgid "Tag 2 from Filename" msgstr "oznaku 2 iz imena fajla" msgid "&Add..." msgstr "&Dodaj..." msgid "Move &Up" msgstr "Pomeri &gore" msgid "Move &Down" msgstr "Pomeri do&le" msgid "&Edit..." msgstr "U&redi..." msgid "&Remove" msgstr "U&kloni" msgid "&Insert row" msgstr "&Ubaci red" msgid "&Delete row" msgstr "&ObriÅ¡i red" msgid "&Clear row" msgstr "O&Äisti red" msgid "Format while editing" msgstr "Formatiraj pri ureÄ‘ivanju" msgid "Case conversion:" msgstr "VeliÄina slova:" msgid "No changes" msgstr "bez izmena" msgid "All lowercase" msgstr "sve malim" msgid "All uppercase" msgstr "sve velikim" msgid "First letter uppercase" msgstr "prvo slovo velikim" msgid "All first letters uppercase" msgstr "sva prva slova velikim" msgid "Locale:" msgstr "Lokalitet:" msgid "None" msgstr "NiÅ¡ta" msgid "String replacement:" msgstr "Zamena teksta:" msgid "From" msgstr "iz" msgid "To" msgstr "u" msgid "&Add" msgstr "&Dodaj" msgid "&Select all" msgstr "I&zaberi sve" msgid "&Deselect all" msgstr "Po&niÅ¡ti izbor" msgid "View Picture" msgstr "Pogledaj sliku" msgid "" "Drag album\n" "artwork\n" "here" msgstr "" "Prevuci\n" "sliku albuma\n" "ovde" msgid "Play/Pause" msgstr "Reprodukuj/pauziraj" msgid "Stop playback" msgstr "Zaustavi reprodukciju" msgid "Previous Track" msgstr "Prethodna numera" msgid "Next Track" msgstr "Sledeća numera" msgid "Close" msgstr "Zatvori" msgid "Mute" msgstr "UtiÅ¡aj" msgid "Volume: %1%" msgstr "JaÄina: %1%" msgid "Add Item" msgstr "Dodaj stavku" msgid "Edit Item" msgstr "Uredi stavku" msgid "MusicBrainz Fingerprint" msgstr "MjuzikBrejnz Otisak prsta" msgid "Unrecognized" msgstr "Nije prepoznato" msgid "Recognized" msgstr "Prepoznato" msgid "User Selection" msgstr "Korisnikov odabir" msgid "ID Lookup" msgstr "ID pretraga" msgid "Fingerprint" msgstr "Otisak prsta" msgid "Metadata Lookup" msgstr "Pretraga metapodataka" msgid "Amazon" msgstr "Amazon" msgid "Discogs" msgstr "Diskogs" msgid "gnudb.org" msgstr "gnudb.org" msgid "TrackType.org" msgstr "TrekTajp.org" msgid "AENC - Audio encryption" msgstr "AENC - audio Å¡ifrovanje" msgid "APIC - Attached picture" msgstr "APIC - priložena slika" msgid "COMM - Comments" msgstr "COMM - komentari" msgid "COMR - Commercial" msgstr "COMR - komercijalno" msgid "ENCR - Encryption method registration" msgstr "ENCR - registracija metode Å¡ifrovanja" msgid "EQUA - Equalization" msgstr "EQUA - jednaÄenje" msgid "ETCO - Event timing codes" msgstr "" msgid "GEOB - General encapsulated object" msgstr "GEOB - opÅ¡ti ugnežđeni objekat" msgid "GRID - Group identification registration" msgstr "GRID - registracija identifikacije grupe" msgid "IPLS - Involved people list" msgstr "IPLS - spisak ukljuÄenih ljudi" msgid "LINK - Linked information" msgstr "LINK - povezana informacija" msgid "MCDI - Music CD identifier" msgstr "MCDI - identifikator muziÄkog CD-a" msgid "MLLT - MPEG location lookup table" msgstr "" msgid "OWNE - Ownership frame" msgstr "OWNE - vlasniÄki okvir" msgid "PRIV - Private frame" msgstr "PRIV - privatni okvir" msgid "PCNT - Play counter" msgstr "PCNT - brojaÄ reprodukcije" msgid "POPM - Popularimeter" msgstr "POPM - meraÄ popularnosti" msgid "POSS - Position synchronisation frame" msgstr "POSS - kadar sinhronizacije pozicije" msgid "RBUF - Recommended buffer size" msgstr "RBUF - preporuÄena veliÄina bafera" msgid "RVAD - Relative volume adjustment" msgstr "RVAD - podeÅ¡avanje relativne jaÄine zvuka" msgid "RVRB - Reverb" msgstr "RVRB - reverberacija" msgid "SYLT - Synchronized lyric/text" msgstr "" msgid "SYTC - Synchronized tempo codes" msgstr "" msgid "TALB - Album/Movie/Show title" msgstr "TALB - naslov albuma/filma/emisije" msgid "TBPM - BPM (beats per minute)" msgstr "TBPM - BPM (otkucaja u minuti)" msgid "TCOM - Composer" msgstr "TCOM - kompozitor" msgid "TCON - Content type" msgstr "TCON - tip sadržaja" msgid "TCOP - Copyright message" msgstr "TCOP - poruka autorskog prava" msgid "TDAT - Date" msgstr "TDAT - datum" msgid "TDLY - Playlist delay" msgstr "TDLY - kaÅ¡njenje liste numera" msgid "TENC - Encoded by" msgstr "TENC - koder" msgid "TEXT - Lyricist/Text writer" msgstr "TEXT - pesnik/tekstopisac" msgid "TFLT - File type" msgstr "TFLT - tip fajla" msgid "TIME - Time" msgstr "TIME - vreme" msgid "TIT1 - Content group description" msgstr "TIT1 - opis grupe sadržaja" msgid "TIT2 - Title/songname/content description" msgstr "TIT2 - naslov/ime pesme/opis sadržaja" msgid "TIT3 - Subtitle/Description refinement" msgstr "" msgid "TKEY - Initial key" msgstr "TKEY - poÄetni kljuÄ" msgid "TLAN - Language(s)" msgstr "TLAN - jezik (jezici)" msgid "TLEN - Length" msgstr "TLEN - trajanje" msgid "TMED - Media type" msgstr "TMED - tip medija" msgid "TOAL - Original album/movie/show title" msgstr "TOAL - izvorni naslov albuma/filma/emisije" msgid "TOFN - Original filename" msgstr "TOFN - izvorno ime" msgid "TOLY - Original lyricist(s)/text writer(s)" msgstr "" msgid "TOPE - Original artist(s)/performer(s)" msgstr "" msgid "TORY - Original release year" msgstr "TORY - izvorna godina izdavanja" msgid "TOWN - File owner/licensee" msgstr "" msgid "TPE1 - Lead performer(s)/Soloist(s)" msgstr "" msgid "TPE2 - Band/orchestra/accompaniment" msgstr "" msgid "TPE3 - Conductor/performer refinement" msgstr "" msgid "TPE4 - Interpreted, remixed, or otherwise modified by" msgstr "" msgid "TPOS - Part of a set" msgstr "" msgid "TPUB - Publisher" msgstr "TPUB - izdavaÄ" msgid "TRCK - Track number/Position in set" msgstr "" msgid "TRDA - Recording dates" msgstr "TRDA - datumi snimanja" msgid "TRSN - Internet radio station name" msgstr "TRSN - ime internet radio stanice" msgid "TRSO - Internet radio station owner" msgstr "TRSO - vlasnik internet radio stanice" msgid "TSIZ - Size" msgstr "TSIZ - veliÄina" msgid "TSRC - ISRC (international standard recording code)" msgstr "TSRC - ISRC (meÄ‘unarodni standardni kôd snimanja)" msgid "TSSE - Software/Hardware and settings used for encoding" msgstr "" msgid "TXXX - User defined text information" msgstr "TXXX - korisniÄki definisana tekstualna informacija" msgid "TYER - Year" msgstr "TYER - godina" msgid "UFID - Unique file identifier" msgstr "UFID - jedinstveni identifikator fajla" msgid "USER - Terms of use" msgstr "USER - uslovi korišćenja" msgid "USLT - Unsynchronized lyric/text transcription" msgstr "" msgid "WCOM - Commercial information" msgstr "WCOM - podaci o komercijalizovanju" msgid "WCOP - Copyright/Legal information" msgstr "WCOP - autorsko pravo/pravne informacije" msgid "WOAF - Official audio file webpage" msgstr "" msgid "WOAR - Official artist/performer webpage" msgstr "" msgid "WOAS - Official audio source webpage" msgstr "" msgid "WORS - Official internet radio station homepage" msgstr "" msgid "WPAY - Payment" msgstr "WPAY - plaćanje" msgid "WPUB - Official publisher webpage" msgstr "" msgid "WXXX - User defined URL link" msgstr "WXXX - korisniÄki definisana URL veza" msgid "MusicBrainz Release" msgstr "MjuzikBrejnz izdanje" msgid "ASPI - Audio seek point index" msgstr "" msgid "EQU2 - Equalisation (2)" msgstr "EQU2 - jednaÄenje (2)" msgid "RVA2 - Relative volume adjustment (2)" msgstr "RVA2 - podeÅ¡avanje relativne jaÄine zvuka (2)" msgid "SEEK - Seek frame" msgstr "SEEK - kadar traženja" msgid "SIGN - Signature frame" msgstr "SIGN - kadar potpisa" msgid "TCMP - iTunes compilation flag" msgstr "" msgid "TDEN - Encoding time" msgstr "TDEN - vreme kodiranja" msgid "TDOR - Original release time" msgstr "TDOR - vreme izdavanja originala" msgid "TDRC - Recording time" msgstr "TDRC - vreme snimanja" msgid "TDRL - Release time" msgstr "TDRL - vreme izdavanja" msgid "TDTG - Tagging time" msgstr "TDTG - vreme oznaÄavanja" msgid "TIPL - Involved people list" msgstr "TIPL - spisak ukljuÄenih ljudi" msgid "TMCL - Musician credits list" msgstr "" msgid "TMOO - Mood" msgstr "TMOO - raspoloženje" msgid "TPRO - Produced notice" msgstr "" msgid "TSO2 - Album artist sort order" msgstr "TSO2 - redosled sortiranja autora albuma" msgid "TSOA - Album sort order" msgstr "TSOA - redosled sortiranja albuma" msgid "TSOC - Composer sort order" msgstr "TSOC - redosled sortiranja kompozitora" msgid "TSOP - Performer sort order" msgstr "TSOP - redosled sortiranja izvoÄ‘aÄa" msgid "TSOT - Title sort order" msgstr "TSOT - redosled sortiranja naslova" msgid "TSST - Set subtitle" msgstr "TSST - postavi podnaslov" �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/po/tr.po���������������������������������������������������������������������������������0000664�0000000�0000000�00000102504�12246035113�0013750�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# translation of Kid3 to Türkçe # This file is put in the public domain. # # Mehmet Yilmaz SUSLU <yilmazsuslu@gmail.com>, 2006. # Urs Fleisch <ufleisch@users.sourceforge.net>, 2010, 2011. msgid "" msgstr "" "Project-Id-Version: Kid3 3.0.2\n" "Report-Msgid-Bugs-To: http://sourceforge.net/p/kid3/bugs/\n" "POT-Creation-Date: 2013-09-11 21:22+0200\n" "PO-Revision-Date: 2011-01-15 17:42+0100\n" "Last-Translator: \n" "Language-Team: Turkish <kde-i18n-doc@kde.org>\n" "Language: tr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Lokalize 1.1\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Language: tr\n" "X-Source-Language: en\n" msgid "Timeout" msgstr "Zaman aşımı" msgid "Usage:" msgstr "Kullanım:" msgid "Help" msgstr "Yardım" msgid "Command name" msgstr "Komut Adı" msgid "Quit application" msgstr "Programdan çıkar" msgid "The current directory has been modified." msgstr "İçinde bulunduÄŸunuz dizin deÄŸiÅŸtirildi." msgid "Type 'exit force' to quit." msgstr "Çıkmak için yazın 'exit force'." msgid "Change directory" msgstr "Dizin deÄŸiÅŸtir" msgid "%1 does not exist" msgstr "%1 yok" msgid "Print the filename of the current working directory" msgstr "Çalışılan dizinin tam dosya yolunu gösterir" msgid "Directory list" msgstr "Dizin listesi" msgid "Saves the changed files" msgstr "DeÄŸiÅŸtirilen dosyaları kaydeder" msgid "Error while writing file:\n" msgstr "Dosyaya yazılırken hata:\n" msgid "Select file" msgstr "Dosya Seç" msgid "%1 not found" msgstr "%1 bulunamadı" msgid "Select tag" msgstr "Etiket seç" msgid "Get tag frame" msgstr "" msgid "Set tag frame" msgstr "" msgid "Could not set \"%1\" for %2" msgstr "" msgid "Revert" msgstr "Geri Al" msgid "Import from file or clipboard" msgstr "Dosya veya Pano'dan Al" msgid "Format name" msgstr "Biçim" msgid "%1 not found." msgstr "%1 bulunamadı." msgid "Available" msgstr "Ulaşılabilir" msgid "Error" msgstr "Hata" msgid "Automatic import" msgstr "Otomatik al" msgid "Profile name" msgstr "Profil Adı" msgid "Reading Directory" msgstr "Okuma dizini" msgid "Started" msgstr "BaÅŸlamış" msgid "Source" msgstr "Kaynak" msgid "Querying" msgstr "Sorguluyor" msgid "Fetching" msgstr "İndiriliyor" msgid "Data received" msgstr "Alınan veri" msgid "Cover" msgstr "Kapak" msgid "Finished" msgstr "Tamamlandı" msgid "Aborted" msgstr "İptal edildi" msgid "Download album cover artwork" msgstr "Kapak resimlerini al" msgid "Export to file or clipboard" msgstr "Dosya veya Panoya Ver" msgid "Create playlist" msgstr "Playlist Yarat" msgid "Apply filename format" msgstr "Dosya Adı Yapısını Uygula" msgid "Apply tag format" msgstr "Tag Yapısını Uygula" msgid "Apply text encoding" msgstr "Karakter Kodlaması Uygula" msgid "Rename directory" msgstr "Dizini Yeniden Adlandır" msgid "Number tracks" msgstr "Parça Numaraları" msgid "Track number" msgstr "Parça Numarası" msgid "Filter" msgstr "Filtre" msgid "Filter name" msgstr "Filtre adı" msgid "Convert ID3v2.3 to ID3v2.4" msgstr "ID3v2.3 dönüştürmek için ID3v2.4" msgid "Convert ID3v2.4 to ID3v2.3" msgstr "ID3v2.4 dönüştürmek için ID3v2.3" msgid "Filename from tag" msgstr "Dosya adı tag'den" msgid "Tag from filename" msgstr "Tag dosya adından" msgid "Tag to other tag" msgstr "" msgid "Copy" msgstr "Kopyala" msgid "Paste" msgstr "Yapıştır" msgid "Remove" msgstr "Kaldır" msgid "Play" msgstr "Çal" msgid "Parameter" msgstr "Parametre" msgid "File path" msgstr "Dosya Yolu" msgid "URL" msgstr "URL" msgid "Tag numbers" msgstr "Parça Numarası" msgid "Frame name" msgstr "Dosya adından" msgid "Frame value" msgstr "" msgid "Format" msgstr "Biçim" msgid "Command specific" msgstr "Komut" msgid "Available Commands" msgstr "Uygun komutlar" msgid "File" msgstr "Dosya" msgid "Name" msgstr "İsim" msgid "Tag 2" msgstr "Tag 2" msgid "Tag 1" msgstr "Tag 1" msgid "Tags" msgstr "Etiketler" msgid "Unknown command '%1'. Type 'help' for help." msgstr "Bilinmeyen komut '%1'. Yardım için 'help' yazınız." msgid "Unknown command '%1', -h for help." msgstr "Bilinmeyen komut '%1', yardım için -h." msgid "Files" msgstr "Dosyalar" msgid "User Actions" msgstr "Kullanıcı Eylemleri" msgid "Network" msgstr "AÄŸ" msgid "Plugins" msgstr "Eklentiler" msgid "Main Toolbar" msgstr "Ana Araç ÇubuÄŸu" msgid "Opens a directory" msgstr "Bir dizin açar" msgid "Opens a recently used directory" msgstr "Son kullanılan dizini açar" msgid "Reverts the changes of all or the selected files" msgstr "Seçilen veya tüm dosyalardaki deÄŸiÅŸiklikleri geri alır" msgid "Quits the application" msgstr "Programdan çıkar" msgid "Select all files" msgstr "Tüm Dosyaları Seç" msgid "Deselect all files" msgstr "Hiçbir dosyayı seçme" msgid "Configure Shortcuts" msgstr "Kısayolları yapılandır" msgid "Configure Toolbars" msgstr "Araç ÇubuÄŸunu Yapılandır" msgid "Preferences dialog" msgstr "Özellikler Penceresi" msgid "O&pen Directory..." msgstr "&Klasör Aç..." msgid "&Import..." msgstr "A&l..." msgid "Import from %1..." msgstr "%1'den Al..." msgid "Automatic I&mport..." msgstr "Oto&matik al..." msgid "&Browse Cover Art..." msgstr "&Kapak Resimlerini Al..." msgid "&Export..." msgstr "&Dışa Aktar..." msgid "&Create Playlist..." msgstr "Playlist &Yarat..." msgid "Apply &Filename Format" msgstr "&Dosya Adı Biçimini Uygula" msgid "Apply &Tag Format" msgstr "&Tag Biçimini Uygula" msgid "Apply Text &Encoding" msgstr "Karakt&er Kodlaması Uygula" msgid "&Rename Directory..." msgstr "Dizini &Yeniden Adlandır..." msgid "&Number Tracks..." msgstr "&Parça Numarları..." msgid "F&ilter..." msgstr "F&iltre..." msgid "Convert ID3v2.3 to ID3v2.&4" msgstr "ID3v2.3 dönüştürmek için ID3v2.&4" msgid "Convert ID3v2.4 to ID3v2.&3" msgstr "ID3v2.4 dönüştürmek için ID3v2.&3" msgid "&Play" msgstr "Ç&al" msgid "Show &Picture" msgstr "Resim &Göster" msgid "Auto &Hide Tags" msgstr "&Otomatik gizle" msgid "Select All in &Directory" msgstr "Dizindeki tüm &dosyaları seç" msgid "&Previous File" msgstr "&Önceki Dosya" msgid "&Next File" msgstr "S&onraki Dosya" msgid "From Filename" msgstr "Dosya adından" msgid "From Tag 2" msgstr "Tag 2'den" msgid "From Tag 1" msgstr "Tag 1'den" msgid "Frames:" msgstr "Çerçeve:" msgid "Edit" msgstr "Düzen" msgid "Add" msgstr "Ekle" msgid "Delete" msgstr "Sil" msgid "Filename" msgstr "Dosya Adı" msgid "Focus" msgstr "Odaklama" msgid "File List" msgstr "Dosya Listesi" msgid "Directory List" msgstr "Dizin listesi" msgid "Configure - Kid3" msgstr "Yapılandır - Kid3" msgid "Open" msgstr "Aç" msgid "Save As" msgstr "Farklı Kaydet" msgid "directory to open" msgstr "açılacak dizin" msgid "Kid3 ID3 Tagger" msgstr "Kid3 ID3 Düzenleyici" #. i18n NAME OF TRANSLATORS msgid "Your names" msgstr "Mehmet Yilmaz SÜSLÜ" #. i18n EMAIL OF TRANSLATORS msgid "Your emails" msgstr "yilmazsuslu@gmail.com" msgid "&Back" msgstr "&Geri" msgid "&Forward" msgstr "İle&ri" msgid "&Find:" msgstr "B&ul:" msgid "Find Previous" msgstr "Öncekini Bul" msgid "Find Next" msgstr "Sonrakini Bul" msgid "&Close" msgstr "&Kapat" msgid "&Tags" msgstr "E&tiketler" msgid "&Files" msgstr "&Dosyalar" msgid "&User Actions" msgstr "K&ullanıcı Eylemleri" msgid "&Network" msgstr "&AÄŸ" msgid "&Plugins" msgstr "&Eklentiler" msgid "&Keyboard Shortcuts" msgstr "&Klavye Kısayolları" msgid "Use custom app&lication font" msgstr "Uygu&lama için özel yazıyüzü kullan" msgid "A&pplication Font..." msgstr "&Uygulama yazıyüzü..." msgid "Use custom application &style" msgstr "" msgid "Unknown" msgstr "Bilinmeyen" msgid "Use native system file &dialogs" msgstr "Sistemin &doÄŸal dosya pencerelerini kullan" msgid "&Appearance" msgstr "&Görünüm" msgid "&Help" msgstr "&Yardım" msgid "&OK" msgstr "&Tamam" msgid "&Cancel" msgstr "İ&ptal" msgid "The keyboard shortcut '%1' is already assigned to '%2'." msgstr "'%1' kısayolu bu iÅŸlem için kullanılıyor '%2'." msgid "&File" msgstr "&Dosya" msgid "&Open..." msgstr "&Aç..." msgid "Open &Recent" msgstr "&Son kullanılanı Aç" msgid "&Save" msgstr "&Kaydet" msgid "Re&vert" msgstr "&Geri Al" msgid "Import from %1" msgstr "%1'den Al" msgid "Browse album cover artwork" msgstr "Kapak resimlerini al" msgid "Create M3U Playlist" msgstr "M3U Playlist Dosyası Yarat" msgid "&Quit" msgstr "Çı&k" msgid "&Edit" msgstr "Dü&zen" msgid "Select &All" msgstr "&Tümünü Seç" msgid "Dese&lect" msgstr "&Seçme" msgid "Select all files in the current directory" msgstr "Dizindeki tüm dosyaları seç" msgid "Select previous file" msgstr "Önceki dosya" msgid "Select next file" msgstr "Sonraki dosya" msgid "&Tools" msgstr "&Araçlar" msgid "Apply Filename Format" msgstr "Dosya Adı Yapısını Uygula" msgid "Apply Tag Format" msgstr "Tag Yapısını Uygula" msgid "Apply Text Encoding" msgstr "Karakter Kodlaması Uygula" msgid "Rename Directory" msgstr "Dizini Yeniden Adlandır" msgid "Number Tracks" msgstr "Parça Numaraları" msgid "&Settings" msgstr "Ayarla&r" msgid "Enables/disables the toolbar" msgstr "Araç çubuÄŸunu açar/kapatır" msgid "Show &Toolbar" msgstr "&Araç ÇubuÄŸunu Göster" msgid "Enables/disables the statusbar" msgstr "Durum çubuÄŸunu açar/kapatır" msgid "Show St&atusbar" msgstr "&Durum ÇubuÄŸunu Göster" msgid "Show Picture" msgstr "Resim Göster" msgid "Auto Hide Tags" msgstr "Otomatik gizle" msgid "Configure Kid3" msgstr "Kid3'ü Yapılandır" msgid "&Configure Kid3..." msgstr "Kid3'ü &Yapılandır..." msgid "Kid3 Handbook" msgstr "Kid3 El Kitabı" msgid "Kid3 &Handbook" msgstr "Kid3 El &Kitabı" msgid "About Kid3" msgstr "Kid3 Hakkında" msgid "&About Kid3" msgstr "Kid3 &Hakkında" msgid "About Qt" msgstr "Qt Hakkında" msgid "About &Qt" msgstr "&Qt Hakkında" msgid " [modified]" msgstr " [deÄŸiÅŸti]" msgid "Toggle the statusbar..." msgstr "Durum çubuÄŸu açılı/kapatılıyor..." msgid "Ready." msgstr "Hazır." msgid "&Clear List" msgstr "Listeyi &Temizle" msgid "Clear" msgstr "Temizle" msgid "Reset" msgstr "Sıfırla" msgid "Press F2 or double click to edit cell contents." msgstr "" msgid "Action" msgstr "Eylem" msgid "Shortcut" msgstr "Kısayol" msgid "Server" msgstr "Sunucu" msgid "Accuracy" msgstr "DoÄŸruluk" msgid "Standard Tags" msgstr "Standart etiketleri" msgid "Additional Tags" msgstr "Ek Özellikler" msgid "Cover Art" msgstr "Albüm Kapağı" msgid "Invalid File" msgstr "Geçersiz Dosya" msgid "Data received: %1" msgstr "Alınan veri: %1" msgid "Request sent..." msgstr "İstek gönderildi..." msgid "Year" msgstr "Yıl" msgid "Length" msgstr "Süre" msgid "Filenames" msgstr "Dosya isimleri" msgid "URLs" msgstr "URL'ler" msgid "Directory name" msgstr "Dizin adı" msgid "Encode as URL" msgstr "" msgid "Confirm" msgstr "Onayla" msgid "Output" msgstr "Çıktı" msgid "Command" msgstr "Komut" msgid "Create directory %1 failed\n" msgstr " %1 dizinini yaratma baÅŸarısız\n" msgid "File %1 already exists\n" msgstr "Zaten %1 adında bir dosya var\n" msgid "%1 is not a directory\n" msgstr "%1 dosyası bir dizin deÄŸil\n" msgid "Rename %1 to %2 failed\n" msgstr "%1 ismi %2 ÅŸeklinde deÄŸiÅŸtirilemedi\n" msgid "%1 already exists\n" msgstr "%1 adında bir dosya zaten var\n" msgid "%1 is not a file\n" msgstr "%1 bir dosya deÄŸil\n" msgid "New directory name is too different\n" msgstr "Yeni dizin ismi çok farklı\n" msgid "Create directory" msgstr "Dizin Yarat" msgid "Rename file" msgstr "Dosya adını deÄŸiÅŸtir" msgid "C&lear" msgstr "Temiz&le" msgid "Execute " msgstr "Çalıştır" msgid "Could not execute " msgstr "Çalıştırılamadı " msgid "True if strings are equal" msgstr "" msgid "True if string contains substring" msgstr "" msgid "True if string matches regexp" msgstr "" msgid "Logical AND" msgstr "" msgid "Logical OR" msgstr "" msgid "Logical negation" msgstr "" msgid "Data" msgstr "Veri" msgid "%1 is not writable" msgstr "%1 dosyası yazılabilir deÄŸil" msgid "All Supported Files" msgstr "Desteklenen Bütün Dosyalar" msgid "All Files" msgstr "Tüm Dosyalar" msgid "Track" msgstr "Parça No" msgid "Absolute path to file" msgstr "" msgid "Duration" msgstr "Süre" msgid "Title" msgstr "Åžarkı Adı" msgid "Artist" msgstr "Sanatçı" msgid "Album" msgstr "Albüm" msgid "Comment" msgstr "Yorum" msgid "Date" msgstr "Tarih" msgid "Track Number" msgstr "Parça Numarası" msgid "Genre" msgstr "Tarz" msgid "Album Artist" msgstr "Albüm Sanatçısı" msgid "Arranger" msgstr "Düzenleyen" msgid "Author" msgstr "Yazar" msgid "BPM" msgstr "BPM" msgid "Catalog Number" msgstr "Katalog Numarası" msgid "Compilation" msgstr "Derleme" msgid "Composer" msgstr "Besteci" msgid "Conductor" msgstr "Orkestra Åžefi" msgid "Copyright" msgstr "Telif" msgid "Disc Number" msgstr "Disk Numarası" msgid "Encoded-by" msgstr "Kodlandı" msgid "Encoder Settings" msgstr "Kodlayıcı ayarları" msgid "Encoding Time" msgstr "Sıkıştırma süresi" msgid "Grouping" msgstr "Gruplama" msgid "Initial Key" msgstr "İlk anahtar" msgid "ISRC" msgstr "ISRC" msgid "Language" msgstr "Dil" msgid "Lyricist" msgstr "Söz Yazar" msgid "Lyrics" msgstr "Åžarkı Sözleri" msgid "Media" msgstr "Ortam" msgid "Mood" msgstr "DuruÅŸ" msgid "Original Album" msgstr "Asıl Albüm" msgid "Original Artist" msgstr "Asıl Sanatçı" msgid "Original Date" msgstr "Asıl Tarih" msgid "Part" msgstr "Bölüm" msgid "Performer" msgstr "Sanatçı" msgid "Picture" msgstr "Resim" msgid "Publisher" msgstr "Yayıncı" msgid "Release Country" msgstr "Yayımlanan Ülke" msgid "Remixer" msgstr "Remiks yapan" msgid "Sort Album" msgstr "Albüm sıralaması" msgid "Sort Album Artist" msgstr "Albüm Sanatçısı Sıralama Düzeni" msgid "Sort Artist" msgstr "Söyleyen sıralaması" msgid "Sort Composer" msgstr "Besteci sıralaması" msgid "Sort Name" msgstr "BaÅŸlık sıralaması" msgid "Subtitle" msgstr "Altyazı" msgid "Website" msgstr "Web Sitesi" msgid "WWW Audio File" msgstr "Ses dosyası WWW" msgid "WWW Audio Source" msgstr "Ses dosyasının WWW" msgid "Number of tracks" msgstr "Parça Sayısı" msgid "Extension" msgstr "Uzantı" msgid "Bitrate" msgstr "Örnekleme hızı" msgid "VBR" msgstr "VBR" msgid "Samplerate" msgstr "Örnekleme oranı" msgid "Channels" msgstr "Kanallar" msgid "Codec" msgstr "Kodlayıcı" msgid "Escape for HTML" msgstr "Escape HTML" msgid "Automatic Import" msgstr "Otomatik al" msgid "D&estination:" msgstr "H&edef:" msgid "Tag 1 and Tag 2" msgstr "Tag 1 ve Tag 2" msgid "&Profile:" msgstr "&Profil:" msgid "&Save Settings" msgstr "&Ayarları Kaydet" msgid "New" msgstr "Yeni" msgid "A&bort" msgstr "İ&ptal" msgid "S&tart" msgstr "BaÅŸla&t" msgid "Import Source" msgstr "İçe Aktarma Kaynağı" msgid "&Server:" msgstr "Su&nucu:" msgid "&Accuracy:" msgstr "&DoÄŸruluk:" msgid "&Standard Tags" msgstr "&Standart etiketleri" msgid "&Additional Tags" msgstr "&Ek Özellikler" msgid "C&over Art" msgstr "&Albüm Kapağı" msgid "Browse Cover Art" msgstr "Kapak Resimlerini Al" msgid "&Artist/Album" msgstr "Sanatçı/&Albüm" msgid "&Source" msgstr "&Kaynak" msgid "Source:" msgstr "Kaynak:" msgid "URL:" msgstr "URL:" msgid "&URL extraction" msgstr "" msgid "Match" msgstr "EÅŸleÅŸme" msgid "Picture URL" msgstr "" msgid "&Browse" msgstr "&Gözat" msgid "Click Browse to start" msgstr "" msgid "Then drag the picture from the browser to Kid3." msgstr "" msgid "ID3v1" msgstr "ID3v1" msgid "&Mark truncated fields" msgstr "" msgid "Text &encoding:" msgstr "M&etin kodlama:" msgid "ID3v2" msgstr "ID3v2" msgid "Use &track/total number of tracks format" msgstr "Parça/&Toplam Parça Sayısı biçimini kullan" msgid "Track number &digits:" msgstr "&Hane sayısı:" msgid "&Genre as text instead of numeric string" msgstr "" msgid "ISO-8859-1" msgstr "ISO-8859-1" msgid "UTF16" msgstr "UTF16" msgid "UTF8" msgstr "UTF8" msgid "&Version used for new tags:" msgstr "&Yeni tag'lar için kullanılan sürüm:" msgid "ID3v2.3.0" msgstr "ID3v2.3.0" msgid "ID3v2.4.0" msgstr "ID3v2.4.0" msgid "Ogg/Vorbis" msgstr "Ogg/Vorbis" msgid "Co&mment field name:" msgstr "Lütfen yeni dosya is&mi yazın:" msgid "&Picture field name:" msgstr "" msgid "Custom &Genres" msgstr "" msgid "&Show only custom genres" msgstr "" msgid "&Quick Access Tags" msgstr "" msgid "&Tag Format" msgstr "&Tag Biçimi" msgid "Tag &1" msgstr "Tag &1" msgid "Tag &2" msgstr "Tag &2" msgid "Tag 1 a&nd Tag 2" msgstr "Tag 1 &ve Tag 2" msgid "Startup" msgstr "BaÅŸlangıç" msgid "&Load last-opened files" msgstr "" msgid "Save" msgstr "Kaydet" msgid "&Preserve file timestamp" msgstr "&Dosya timestamp deÄŸerini sakla" msgid "&Mark changes" msgstr "" msgid "F&ilename for cover:" msgstr "Albüm kapağı &için dosya adı:" msgid "&Filename Format" msgstr "&Dosya adı Biçimi" msgid "Browser" msgstr "Gözat" msgid "Web &browser:" msgstr "İnternet &Tarayıcısı:" msgid "Context &Menu Commands" msgstr "Açılır &Menü Komutları" #, fuzzy msgid "&Play on double click" msgstr "&Çift tıklayın çal" msgid "Proxy" msgstr "Vekil Sunucu" msgid "&Proxy:" msgstr "&Vekil Sunucu:" msgid "&Use authentication with proxy" msgstr "Vekil s&unucu ile yetkilendirme kullan" msgid "Proxy user &name:" msgstr "Vekil su&nucu kullanıcı adı:" msgid "Proxy pass&word:" msgstr "&Vekil sunucu parolası:" msgid "&Metadata Plugins && Priority" msgstr "&Metaverisi Eklentiler && Öncelik" msgid "A&vailable Plugins" msgstr "&Kullanılabilir eklentiler" msgid "Changes take only effect after a restart!" msgstr "Yeniden baÅŸlattıktan sonra etkinleÅŸtirilir!" msgid "From Clip&board" msgstr "&Panodan" msgid "&Import" msgstr "&Al" msgid "&Export" msgstr "&Ver" msgid "&View" msgstr "Görü&nüm" msgid "Text Encoding" msgstr "Karakter Kodlaması" msgid "Text" msgstr "Metin" msgid "Description" msgstr "Açıklama" msgid "Owner" msgstr "Sahibi" msgid "Email" msgstr "E-posta" msgid "Rating" msgstr "Puan" msgid "Picture Type" msgstr "Resim Tipi" msgid "Image format" msgstr "Görüntü Formatı" msgid "Mimetype" msgstr "Mime-türü" msgid "Counter" msgstr "Sayaç" msgid "Identifier" msgstr "Tanımlayıcı" msgid "Volume Adjustment" msgstr "Ses seviyesi ayarı" msgid "Number of Bits" msgstr "Bit sayısı" msgid "Volume Change Right" msgstr "SaÄŸ Ses Seviyesi DeÄŸiÅŸimi" msgid "Volume Change Left" msgstr "Sol Ses Seviyesi DeÄŸiÅŸimi" msgid "Peak Volume Right" msgstr "SaÄŸ Üst Ses Sınırı" msgid "Peak Volume Left" msgstr "Sol Üst Ses Sınırı" msgid "Timestamp Format" msgstr "Timestamp biçimi" msgid "Content Type" msgstr "İçerik Türü" msgid "Price" msgstr "Fiyat" msgid "Seller" msgstr "Satıcı" msgid "Images" msgstr "Resimler" msgid "UTF16BE" msgstr "UTF16BE" msgid "Other" msgstr "DiÄŸer" msgid "32x32 pixels PNG file icon" msgstr "32x32 piksel PNG dosya ikonu" msgid "Other file icon" msgstr "DiÄŸer dosya ikonu" msgid "Cover (front)" msgstr "Ön Kapak" msgid "Cover (back)" msgstr "Arka Kapak" msgid "Leaflet page" msgstr "Broşür Sayfası" msgid "Lead artist/lead performer/soloist" msgstr "Ana Sanatçı/Söyleyen/Solist" msgid "Artist/performer" msgstr "Sanatçı/Söyleyen" msgid "Band/Orchestra" msgstr "Grup/Orkestra" msgid "Lyricist/text writer" msgstr "Söz yazarı" msgid "Recording Location" msgstr "Kayıt Yeri" msgid "During recording" msgstr "Kayıt sırasında" msgid "During performance" msgstr "Åžarkı söylenirken" msgid "Movie/video screen capture" msgstr "Film / Video" msgid "A bright coloured fish" msgstr "Parlak bir renkli balık" msgid "Illustration" msgstr "İllüstrasyon" msgid "Band/artist logotype" msgstr "Grup/sanatçı Logosu" msgid "Publisher/Studio logotype" msgstr "Yapımcı/Stüdyo Logosu" msgid "MPEG frames as unit" msgstr "Birim olarak MPEG çerçeveleri" msgid "Milliseconds as unit" msgstr "MBirim olarak milisaniye" msgid "Text transcription" msgstr "Yazı Çevrimi" msgid "Movement/part name" msgstr "Hareket/Bölüm adı" msgid "Events" msgstr "Olaylar" msgid "Chord" msgstr "Akor" msgid "Trivia/pop up" msgstr "Trivia/pop up" msgid "Export" msgstr "Dışa Aktar" msgid "Format:" msgstr "Biçim:" msgid "Header:" msgstr "Üst Bilgi:" msgid "Tracks:" msgstr "Parçalar:" msgid "Footer:" msgstr "Altlık:" msgid "To F&ile" msgstr "&Dosyaya" msgid "To Clip&board" msgstr "&Panoya" msgid "&Source:" msgstr "&Kaynak:" msgid "File Error" msgstr "Dosya Hatası" msgid "&Filter:" msgstr "&Filtre:" msgid "&Expression:" msgstr "&İfade:" msgid "&Apply" msgstr "&Uygula" msgid "Accuracy:" msgstr "DoÄŸruluk:" msgid "Cover Art:" msgstr "Albüm Kapağı:" msgid "From F&ile/Clipboard..." msgstr "D&osya veya Pano'dan Al..." msgid "From T&ags..." msgstr "T&ag'den..." msgid "&From Server:" msgstr "&Sunucudan:" msgid "Check maximum allowable time &difference (sec):" msgstr "Kabul e&dilebilir en fazla süre farkı (saniye):" msgid "Match with:" msgstr "Åžununla eÅŸleÅŸtir:" msgid "&Length" msgstr "Sü&re" msgid "T&rack" msgstr "&Parça No" msgid "&Title" msgstr "Åžar&kı adı" msgid "&Start number:" msgstr "BaÅŸlangıç numara&sı:" msgid "&Destination:" msgstr "He&def:" msgid "&Total number of tracks:" msgstr "&Toplam Parça Sayısı:" msgid "Create Playlist" msgstr "Playlist Yarat" msgid "Playlist File Name" msgstr "" msgid "Same as &directory name" msgstr "" msgid "&Format:" msgstr "&Biçim:" msgid "Cr&eate in:" msgstr "&OluÅŸturulacak:" msgid "Current directory" msgstr "Mevcut dizin" msgid "Every directory" msgstr "" msgid "Top-level directory" msgstr "" msgid "Playlist Content" msgstr "" msgid "For&mat:" msgstr "Biçi&m:" msgid "Incl&ude only the selected files" msgstr "" msgid "Sort by file &name" msgstr "" msgid "Sort by &tag field" msgstr "" msgid "Use &relative path for files in playlist" msgstr "" msgid "Use full p&ath for files in playlist" msgstr "" msgid "Write only &list of files" msgstr "" msgid "Write &info using" msgstr "" msgid "Preview" msgstr "Önizleme" msgid "Create Directory" msgstr "Dizin Yarat" msgid "From Tag 2 and Tag 1" msgstr "Tag 2 ve Tag 1'den" msgid "From:" msgstr "Åžuradan:" msgid "To:" msgstr "Åžuraya:" msgid "&Find" msgstr "B&ul" msgid "C&GI Path:" msgstr "C&GI Yolu:" msgid "Track Title/Artist - Album" msgstr "Åžarkı Adı / Sanatçı - Albüm" msgid "State" msgstr "Konum" msgid "No result" msgstr "Sonuç yok" msgid "No result selected" msgstr "Hiç sonuç seçilmedi" msgid "Import from Tags" msgstr "Tag'den Al" msgid "Extraction:" msgstr "Çıkarma:" msgid "Import from File/Clipboard" msgstr "Dosya veya Pano'dan Al" msgid "From F&ile" msgstr "&Dosyadan" msgid "Download" msgstr "İndir" msgid "Opening directory..." msgstr "Dizin Açılıyor..." msgid "Saving directory..." msgstr "Dizin kaydediliyor..." msgid "" "The current directory has been modified.\n" "Do you want to save it?" msgstr "" "İçinde bulunduÄŸunuz dizin deÄŸiÅŸtirildi.\n" "Kaydetmek istiyor musunuz?" msgid "Warning" msgstr "Uyarı" msgid "Exiting..." msgstr "Çıkılıyor..." msgid "Creating playlist..." msgstr "Playlist Yaratılıyor..." msgid "Import" msgstr "Al" msgid "Error while renaming:\n" msgstr "İsim deÄŸiÅŸtirirken hata:\n" msgid " [filtered]" msgstr " [süzgeci]" msgid "Add Frame" msgstr "Çerçeve Ekle" msgid "Select the frame ID" msgstr "Çerçeve numarasını seç" msgid "Rename File" msgstr "Dosya Adını DeÄŸiÅŸtir" msgid "Enter new file name:" msgstr "Lütfen yeni dosya ismi yazın:" msgid "Do you really want to move these %1 items to the trash?" msgstr "%1 ögeyi çöp kutusuna atmayı gerçekten istiyor musunuz?" msgid "Do you really want to move this item to the trash?" msgstr "Bu ögeyi çöp kutusuna atmayı gerçekten istiyor musunuz?" msgid "Move to Trash" msgstr "Çöp Kutusuna Taşı" msgid "Directory must be empty.\n" msgstr "" msgid "Could not move these files to the Trash" msgstr "Bu dosyalar Çöp Kutusuna taşınamadı" msgid "Expand All" msgstr "Tümünü Aç" msgid "&Expand all" msgstr "&Tümünü Aç" msgid "&Collapse all" msgstr "Tümünü &Kapat" msgid "&Rename" msgstr "Ad DeÄŸiÅŸti&r" msgid "&Move to Trash" msgstr "Çöp &Kutusuna Taşı" msgid "Kid3" msgstr "Kid3" msgid "F&ile" msgstr "D&osya" msgid "Name:" msgstr "İsim:" msgid "Filename from Tag 1" msgstr "Dosya adı tag 1'den" msgid "Filename from Tag 2" msgstr "Dosya adı tag 2'den" msgid "Tag 1 from Filename" msgstr "Tag 1 dosya adından" msgid "Tag 2 from Filename" msgstr "Tag 2 dosya adından" msgid "&Add..." msgstr "&Ekle..." msgid "Move &Up" msgstr "&Yukarı Taşı" msgid "Move &Down" msgstr "&AÅŸağı Taşı" msgid "&Edit..." msgstr "Dü&zen..." msgid "&Remove" msgstr "Ka&ldır" msgid "&Insert row" msgstr "Satır &Ekle" msgid "&Delete row" msgstr "Satır &Sil" msgid "&Clear row" msgstr "Satırı &Temizle" msgid "Format while editing" msgstr "Düzenleme ÅŸekli" msgid "Case conversion:" msgstr "" msgid "No changes" msgstr "DeÄŸiÅŸiklik Yok" msgid "All lowercase" msgstr "Tümü küçük harf" msgid "All uppercase" msgstr "Tümü BÜYÜK harf" msgid "First letter uppercase" msgstr "Cümlenin ilk harfi büyük" msgid "All first letters uppercase" msgstr "Kelimelerin ilk harfi büyük" msgid "Locale:" msgstr "Dil:" msgid "None" msgstr "Hiçbiri" msgid "String replacement:" msgstr "" msgid "From" msgstr "Åžuradan" msgid "To" msgstr "Åžuraya" msgid "&Add" msgstr "&Ekle" msgid "&Select all" msgstr "&Tümünü Seç" msgid "&Deselect all" msgstr "Hiçbirisini &Seçme" msgid "View Picture" msgstr "Resmi Göster" msgid "" "Drag album\n" "artwork\n" "here" msgstr "" "Albüm kapak\n" "resmini buraya\n" "sürükleyin" msgid "Play/Pause" msgstr "Çal/Duraklat" msgid "Stop playback" msgstr "Çalmayı durdur" msgid "Previous Track" msgstr "Önceki Parça" msgid "Next Track" msgstr "Sonraki Parça" msgid "Close" msgstr "Kapat" msgid "Mute" msgstr "Sessiz" msgid "Volume: %1%" msgstr "Ses: %1%" msgid "Add Item" msgstr "Öğe Ekle" msgid "Edit Item" msgstr "Öğeyi Düzenle" msgid "MusicBrainz Fingerprint" msgstr "MusicBrainz Parmak İzinden" msgid "Unrecognized" msgstr "Tanımlanamayan" msgid "Recognized" msgstr "Tanımlanan" msgid "User Selection" msgstr "Kullanıcı Seçimi" msgid "ID Lookup" msgstr "ID Görünümü" msgid "Fingerprint" msgstr "Parmak İzinden" msgid "Metadata Lookup" msgstr "Meta-verisi Oku" msgid "Amazon" msgstr "Amazon" msgid "Discogs" msgstr "Discogs" msgid "gnudb.org" msgstr "gnudb.org" msgid "TrackType.org" msgstr "TrackType.org" msgid "AENC - Audio encryption" msgstr "AENC - Ses ÅŸifreleme" msgid "APIC - Attached picture" msgstr "APIC - İliÅŸtirilmiÅŸ Resim" msgid "COMM - Comments" msgstr "COMM - Yorumlar" msgid "COMR - Commercial" msgstr "COMR - Ticari" msgid "ENCR - Encryption method registration" msgstr "ENCR - Åžifreleme yöntemi kaydı" msgid "EQUA - Equalization" msgstr "EQUA - Ekolayzır" msgid "ETCO - Event timing codes" msgstr "ETCO - Olay zamanlama kodekleri" msgid "GEOB - General encapsulated object" msgstr "GEOB - Genel kapsülleme nesnesi" msgid "GRID - Group identification registration" msgstr "GRID - Grup tanımlama kaydı" msgid "IPLS - Involved people list" msgstr "IPLS - İlgili kiÅŸi listesi" msgid "LINK - Linked information" msgstr "LINK - BaÄŸlanmış bilgi" msgid "MCDI - Music CD identifier" msgstr "MCDI - Ses CD'si tanımlayıcı" msgid "MLLT - MPEG location lookup table" msgstr "MLLT - MPEG konumu ÅŸema tablosu" msgid "OWNE - Ownership frame" msgstr "OWNE - Sahip çerçeve" msgid "PRIV - Private frame" msgstr "PRIV - Özel çerçeve" msgid "PCNT - Play counter" msgstr "PCNT - Çalma sayacı" msgid "POPM - Popularimeter" msgstr "POPM - Popülerlik ölçümü" msgid "POSS - Position synchronisation frame" msgstr "POSS - Pozisyon senkronizasyon çerçevesi" msgid "RBUF - Recommended buffer size" msgstr "RBUF - Önerilen tampon bellek boyutu" msgid "RVAD - Relative volume adjustment" msgstr "RVAD - Bağıl ses düzeyi ayarı" msgid "RVRB - Reverb" msgstr "RVRB - Reverb" msgid "SYLT - Synchronized lyric/text" msgstr "SYLT - Senkronize edilmiÅŸ ÅŸarkı sözü/metin" msgid "SYTC - Synchronized tempo codes" msgstr "SYTC - Senkronize edilmiÅŸ tempo açıcıları" msgid "TALB - Album/Movie/Show title" msgstr "TALB - Albüm/Film/Gösteri Adı" msgid "TBPM - BPM (beats per minute)" msgstr "TBPM - BPM (Dakikada VuruÅŸ Sayısı)" msgid "TCOM - Composer" msgstr "TCOM - Besteci" msgid "TCON - Content type" msgstr "TCON - İçerik tipi" msgid "TCOP - Copyright message" msgstr "TCOP - Telif hakkı mesajı" msgid "TDAT - Date" msgstr "TDAT - Tarih" msgid "TDLY - Playlist delay" msgstr "TDLY - Åžarkı listesi gecikmesi" msgid "TENC - Encoded by" msgstr "TENC - Sıkıştıran" msgid "TEXT - Lyricist/Text writer" msgstr "TEXT - Söz yazarı" msgid "TFLT - File type" msgstr "TFLT - Dosya tipi" msgid "TIME - Time" msgstr "TIME - Zaman" msgid "TIT1 - Content group description" msgstr "TIT1 - İçerik grubu açıklaması" msgid "TIT2 - Title/songname/content description" msgstr "TIT2 - Åžarkı adı / İçerik açıklaması" msgid "TIT3 - Subtitle/Description refinement" msgstr "TIT3 - Alt yazı / Açıklama düzeltmesi" msgid "TKEY - Initial key" msgstr "TKEY - İlk anahtar" msgid "TLAN - Language(s)" msgstr "TLAN - Dil(ler)" msgid "TLEN - Length" msgstr "TLEN - Süre" msgid "TMED - Media type" msgstr "TMED - Ortam türü" msgid "TOAL - Original album/movie/show title" msgstr "TOAL - Orjinal Albüm / Film / Gösteri adı" msgid "TOFN - Original filename" msgstr "TOFN - Orjinal dosya adı" msgid "TOLY - Original lyricist(s)/text writer(s)" msgstr "TOLY - Orjinal söz yazar(lar)ı" msgid "TOPE - Original artist(s)/performer(s)" msgstr "TOPE - Orjinal sanatçı adı" msgid "TORY - Original release year" msgstr "TORY - Orjinal piyasaya çıkış yılı" msgid "TOWN - File owner/licensee" msgstr "TOWN - Dosyanın lisans sahibi" msgid "TPE1 - Lead performer(s)/Soloist(s)" msgstr "TPE1 - Ana sanatçı(lar) / Söyleyen(ler)" msgid "TPE2 - Band/orchestra/accompaniment" msgstr "TPE2 - Grup / Orkestra / Misafir Sanatçı" msgid "TPE3 - Conductor/performer refinement" msgstr "TPE3 - Orkestra ÅŸefi / Söyleyen düzeltme" msgid "TPE4 - Interpreted, remixed, or otherwise modified by" msgstr "TPE4 - Yorumlayan / Tekrar düzenleyen veya deÄŸiÅŸtiren" msgid "TPOS - Part of a set" msgstr "TPOS - Bir seri'nin parçası" msgid "TPUB - Publisher" msgstr "TPUB - Yayımcı" msgid "TRCK - Track number/Position in set" msgstr "TRCK - Seri'nin Parça / Konum Numarası" msgid "TRDA - Recording dates" msgstr "TRDA - Kayıt tarihleri" msgid "TRSN - Internet radio station name" msgstr "TRSN - İnternet radyo istasyonu adı" msgid "TRSO - Internet radio station owner" msgstr "TRSO - İnternet radyo istasyonu sahibi" msgid "TSIZ - Size" msgstr "TSIZ - Büyüklük" msgid "TSRC - ISRC (international standard recording code)" msgstr "TSRC - ISRC (Uluslararası standart kayıt kodu)" msgid "TSSE - Software/Hardware and settings used for encoding" msgstr "TSSE - Sıkıştırma için kullanılan yazılım / donanım ve ayarlar" msgid "TXXX - User defined text information" msgstr "TXXX - Kullanıcı tanımlı metin bilgisi" msgid "TYER - Year" msgstr "TYER - Yıl" msgid "UFID - Unique file identifier" msgstr "UFID - Benzersiz dosya tanımlayıcısı" msgid "USER - Terms of use" msgstr "USER - Kullanım ÅŸartları" msgid "USLT - Unsynchronized lyric/text transcription" msgstr "USLT - Senkronize edilmemiÅŸ söz çevrim yazısı" msgid "WCOM - Commercial information" msgstr "WCOM - Ticari bilgi" msgid "WCOP - Copyright/Legal information" msgstr "WCOP - Telif Hakkı / Kanuni bilgi" msgid "WOAF - Official audio file webpage" msgstr "WOAF - Ses dosyası'nın resmi internet sitesi" msgid "WOAR - Official artist/performer webpage" msgstr "WOAR - Sanatçı'nın / Åžarkıcı'nın resmi web sitesi" msgid "WOAS - Official audio source webpage" msgstr "WOAS - Ses dosyasının resmi internet adresi" msgid "WORS - Official internet radio station homepage" msgstr "WORS - İnternet radyo istasyonu'nun resmi adresi" msgid "WPAY - Payment" msgstr "WPAY - Ödeme" msgid "WPUB - Official publisher webpage" msgstr "WPUB - Resmi yayımcı" msgid "WXXX - User defined URL link" msgstr "WXXX - Kullanıcı tanımlı URL baÄŸlantısı" msgid "MusicBrainz Release" msgstr "MusicBrainz Sürümünden" msgid "ASPI - Audio seek point index" msgstr "ASPI - Ses tarama nokta indeksi" msgid "EQU2 - Equalisation (2)" msgstr "EQU2 - Ekolayzır (2)" msgid "RVA2 - Relative volume adjustment (2)" msgstr "RVA2 - Bağıl ses düzeyi ayarı (2)" msgid "SEEK - Seek frame" msgstr "SEEK - Tarama çerçevesi" msgid "SIGN - Signature frame" msgstr "SIGN - İmza çerçevesi" msgid "TCMP - iTunes compilation flag" msgstr "TCMP - iTunes derleme" msgid "TDEN - Encoding time" msgstr "TDEN - Sıkıştırma süresi" msgid "TDOR - Original release time" msgstr "TDOR - Orjinal piyasaya çıkış zamanı" msgid "TDRC - Recording time" msgstr "TDRC - Kayıt süresi" msgid "TDRL - Release time" msgstr "TDRL - Piyasaya çıkış zamanı" msgid "TDTG - Tagging time" msgstr "TDTG - Etiketleme zamanı" msgid "TIPL - Involved people list" msgstr "TIPL - İlgili kiÅŸi listesi" msgid "TMCL - Musician credits list" msgstr "TMCL - Katkıda bulunan müzisyenler listesi" msgid "TMOO - Mood" msgstr "TMOO - DuruÅŸ" msgid "TPRO - Produced notice" msgstr "TPRO - ÜretilmiÅŸ uyarı" msgid "TSO2 - Album artist sort order" msgstr "TSO2 - Albüm Sanatçısı Sıralama Düzeni" msgid "TSOA - Album sort order" msgstr "TSOA - Albüm sıralaması" msgid "TSOC - Composer sort order" msgstr "TSOC - Besteci sıralaması" msgid "TSOP - Performer sort order" msgstr "TSOP - Söyleyen sıralaması" msgid "TSOT - Title sort order" msgstr "TSOT - BaÅŸlık sıralaması" msgid "TSST - Set subtitle" msgstr "TSST - Alt yazı ayarla" ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/po/zh_TW.po������������������������������������������������������������������������������0000664�0000000�0000000�00000077521�12246035113�0014370�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # ChungYu, Hsu <chungyu.hsu@gmail.com>, 2010. # Urs Fleisch <ufleisch@users.sourceforge.net>, 2010, 2011. msgid "" msgstr "" "Project-Id-Version: Kid3 3.0.2\n" "Report-Msgid-Bugs-To: http://sourceforge.net/p/kid3/bugs/\n" "POT-Creation-Date: 2013-09-11 21:22+0200\n" "PO-Revision-Date: 2011-01-15 17:43+0100\n" "Last-Translator: \n" "Language-Team: Chinese (traditional) <kde-i18n-doc@kde.org>\n" "Language: zh_TW\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Poedit-Language: Chinese\n" "X-Poedit-Country: Taiwan\n" "X-Poedit-SourceCharset: utf-8\n" "X-Generator: Lokalize 1.1\n" "X-Language: zh_TW\n" "X-Source-Language: en\n" msgid "Timeout" msgstr "逾時" msgid "Usage:" msgstr "用法:" msgid "Help" msgstr "求助" msgid "Command name" msgstr "指令" msgid "Quit application" msgstr "離開應用程å¼" msgid "The current directory has been modified." msgstr "ç›®å‰ç›®éŒ„已更動." msgid "Type 'exit force' to quit." msgstr "類型 'exit force' 離開." msgid "Change directory" msgstr "" msgid "%1 does not exist" msgstr "%1 ä¸å­˜åœ¨" msgid "Print the filename of the current working directory" msgstr "" msgid "Directory list" msgstr "目錄清單" msgid "Saves the changed files" msgstr "儲存更動的檔案" msgid "Error while writing file:\n" msgstr "寫入檔案錯誤:\n" msgid "Select file" msgstr "é¸å–檔案" msgid "%1 not found" msgstr "沒有找到 %1" msgid "Select tag" msgstr "é¸å–標籤" msgid "Get tag frame" msgstr "" msgid "Set tag frame" msgstr "" msgid "Could not set \"%1\" for %2" msgstr "" msgid "Revert" msgstr "還原" msgid "Import from file or clipboard" msgstr "自剪貼簿匯入" msgid "Format name" msgstr "æ ¼å¼" msgid "%1 not found." msgstr "未找到 %1." msgid "Available" msgstr "å¯ç”¨" msgid "Error" msgstr "錯誤" msgid "Automatic import" msgstr "自動匯入" msgid "Profile name" msgstr "設定檔å稱" msgid "Reading Directory" msgstr "正在讀å–目錄" msgid "Started" msgstr "已開始" msgid "Source" msgstr "來æº" msgid "Querying" msgstr "正查詢" msgid "Fetching" msgstr "接收中" msgid "Data received" msgstr "获得的数æ®è¿›è¡Œ" msgid "Cover" msgstr "å°é¢" msgid "Finished" msgstr "已完æˆ" msgid "Aborted" msgstr "已中止" msgid "Download album cover artwork" msgstr "ç€è¦½å°ˆè¼¯å°é¢" msgid "Export to file or clipboard" msgstr "匯出至檔案或剪貼簿" msgid "Create playlist" msgstr "建立播放清單" msgid "Apply filename format" msgstr "å¥—ç”¨æª”åæ ¼å¼" msgid "Apply tag format" msgstr "套用 Tag æ ¼å¼" msgid "Apply text encoding" msgstr "将字符编ç " msgid "Rename directory" msgstr "更改目錄å稱" msgid "Number tracks" msgstr "音軌編號" msgid "Track number" msgstr "音軌編號" msgid "Filter" msgstr "篩é¸" msgid "Filter name" msgstr "檔å" msgid "Convert ID3v2.3 to ID3v2.4" msgstr "從 ID3v2.3 è½‰æ›æˆ ID3v2.4" msgid "Convert ID3v2.4 to ID3v2.3" msgstr "從 ID3v2.4 è½‰æ›æˆ ID3v2.3" msgid "Filename from tag" msgstr "檔å從標籤" msgid "Tag from filename" msgstr "標籤從檔å" msgid "Tag to other tag" msgstr "" msgid "Copy" msgstr "複製" msgid "Paste" msgstr "貼上" msgid "Remove" msgstr "移除" msgid "Play" msgstr "播放" msgid "Parameter" msgstr "åƒæ•¸" msgid "File path" msgstr "檔案路徑" msgid "URL" msgstr "ç¶²å€" msgid "Tag numbers" msgstr "音軌編號" msgid "Frame name" msgstr "從檔å" msgid "Frame value" msgstr "" msgid "Format" msgstr "æ ¼å¼" msgid "Command specific" msgstr "指令" msgid "Available Commands" msgstr "å¯ç”¨çš„æŒ‡ä»¤" msgid "File" msgstr "檔案" msgid "Name" msgstr "å稱" msgid "Tag 2" msgstr "標籤 2" msgid "Tag 1" msgstr "標籤 1" msgid "Tags" msgstr "標籤" msgid "Unknown command '%1'. Type 'help' for help." msgstr "未知的指令 '%1'. 輸入 'help' å°±å¯ä»¥é–±è®€å¹«åŠ©è¨Šæ¯." msgid "Unknown command '%1', -h for help." msgstr "未知的指令 '%1', 或用 -h 求助." msgid "Files" msgstr "檔案" msgid "User Actions" msgstr "自訂動作" msgid "Network" msgstr "網路" msgid "Plugins" msgstr "外掛" msgid "Main Toolbar" msgstr "主工具列" msgid "Opens a directory" msgstr "開啟目錄" msgid "Opens a recently used directory" msgstr "é–‹å•Ÿç›®å‰æ‰€åœ¨çš„目錄" msgid "Reverts the changes of all or the selected files" msgstr "é‚„åŽŸå…¨éƒ¨æœ‰æ›´å‹•çš„æª”æ¡ˆæˆ–åªæœ‰é¸å–部份" msgid "Quits the application" msgstr "離開應用程å¼" msgid "Select all files" msgstr "鏿“‡å…¨éƒ¨æª”案" msgid "Deselect all files" msgstr "全部ä¸é¸" msgid "Configure Shortcuts" msgstr "設定æ·å¾‘" msgid "Configure Toolbars" msgstr "設定工具列" msgid "Preferences dialog" msgstr "喜好設定å°è©±æ¡†" msgid "O&pen Directory..." msgstr "開啟目錄(&P)..." msgid "&Import..." msgstr "匯入(&I)..." msgid "Import from %1..." msgstr "從 %1 匯入..." msgid "Automatic I&mport..." msgstr "自動匯入(&M)..." msgid "&Browse Cover Art..." msgstr "ç€è¦½å°ˆè¼¯å°é¢(&B)..." msgid "&Export..." msgstr "匯出(&E)..." msgid "&Create Playlist..." msgstr "新增播放清單(&C)..." msgid "Apply &Filename Format" msgstr "便“šæª”åæ ¼å¼(&F)" msgid "Apply &Tag Format" msgstr "套用標籤格å¼(&T)" msgid "Apply Text &Encoding" msgstr "将字符编ç (&E)" msgid "&Rename Directory..." msgstr "更改目錄å稱(&R)..." msgid "&Number Tracks..." msgstr "音軌編號(&N)..." msgid "F&ilter..." msgstr "éŽæ¿¾(&I)..." msgid "Convert ID3v2.3 to ID3v2.&4" msgstr "從 ID3v2.3 轉æ›è‡³ ID3v2.&4" msgid "Convert ID3v2.4 to ID3v2.&3" msgstr "從 ID3v2.4 轉æ›è‡³ ID3v2.&3" msgid "&Play" msgstr "播放(&P)" msgid "Show &Picture" msgstr "顯示圖片(&P)" msgid "Auto &Hide Tags" msgstr "è‡ªå‹•éš±è—æ¨™ç±¤(&H)" msgid "Select All in &Directory" msgstr "选中当å‰ç›®å½•中所有的文件(&D)" msgid "&Previous File" msgstr "上一個檔案(&P)" msgid "&Next File" msgstr "下一個檔案(&N)" msgid "From Filename" msgstr "從檔å" msgid "From Tag 2" msgstr "從標籤 2" msgid "From Tag 1" msgstr "從標籤 1" msgid "Frames:" msgstr "訊框:" msgid "Edit" msgstr "編輯" msgid "Add" msgstr "新增" msgid "Delete" msgstr "刪除" msgid "Filename" msgstr "檔å" msgid "Focus" msgstr "焦點" msgid "File List" msgstr "檔案清單" msgid "Directory List" msgstr "目錄清單" msgid "Configure - Kid3" msgstr "設定 - Kid3" msgid "Open" msgstr "開啟" msgid "Save As" msgstr "å¦å­˜æ–°æª”" msgid "directory to open" msgstr "開啟目錄" msgid "Kid3 ID3 Tagger" msgstr "Kid3 ID3 Tagger" #. i18n NAME OF TRANSLATORS msgid "Your names" msgstr "Chung-Yu Hsu" #. i18n EMAIL OF TRANSLATORS msgid "Your emails" msgstr "yayow at mail dot shps dot kh dot edu dot tw" msgid "&Back" msgstr "返回(&B)" msgid "&Forward" msgstr "å‘å‰(&F)" msgid "&Find:" msgstr "尋找(&F):" msgid "Find Previous" msgstr "尋找å‰ä¸€å€‹" msgid "Find Next" msgstr "尋找下一個" msgid "&Close" msgstr "關閉(&C)" msgid "&Tags" msgstr "標籤(&T)" msgid "&Files" msgstr "檔案(&F)" msgid "&User Actions" msgstr "自訂動作(&U)" msgid "&Network" msgstr "網路(&N)" msgid "&Plugins" msgstr "外掛(&P)" msgid "&Keyboard Shortcuts" msgstr "æ·å¾‘éµ(&K)" msgid "Use custom app&lication font" msgstr "使用自訂應用程å¼å­—åž‹(&L)" msgid "A&pplication Font..." msgstr "應用程å¼å­—åž‹(&P)..." msgid "Use custom application &style" msgstr "ä½¿ç”¨è‡ªè¨‚æ‡‰ç”¨ç¨‹å¼æ¨£å¼(&S)" msgid "Unknown" msgstr "未知" msgid "Use native system file &dialogs" msgstr "使用原始系統檔案å°è©±æ¡†(&D)" msgid "&Appearance" msgstr "外觀設定(&A)" msgid "&Help" msgstr "求助(&H)" msgid "&OK" msgstr "確定(&O)" msgid "&Cancel" msgstr "å–æ¶ˆ(&C)" msgid "The keyboard shortcut '%1' is already assigned to '%2'." msgstr "快速éµ'%1'已經指定為'%2'." msgid "&File" msgstr "檔案(&F)" msgid "&Open..." msgstr "開啟(&O)..." msgid "Open &Recent" msgstr "開啟最近的檔案(&R)" msgid "&Save" msgstr "儲存(&S)" msgid "Re&vert" msgstr "還原(&V)" msgid "Import from %1" msgstr "自 %1 匯入" msgid "Browse album cover artwork" msgstr "ç€è¦½å°ˆè¼¯å°é¢" msgid "Create M3U Playlist" msgstr "建立 M3U 播放清單" msgid "&Quit" msgstr "離開(&Q)" msgid "&Edit" msgstr "編輯(&E)" msgid "Select &All" msgstr "å…¨é¸(&A)" msgid "Dese&lect" msgstr "å–æ¶ˆé¸æ“‡(&l)" msgid "Select all files in the current directory" msgstr "选中当å‰ç›®å½•中所有的文件" msgid "Select previous file" msgstr "鏿“‡ä¸Šä¸€å€‹æª”案" msgid "Select next file" msgstr "鏿“‡ä¸‹ä¸€å€‹æª”案" msgid "&Tools" msgstr "工具(&T)" msgid "Apply Filename Format" msgstr "å¥—ç”¨æª”åæ ¼å¼" msgid "Apply Tag Format" msgstr "套用 Tag æ ¼å¼" msgid "Apply Text Encoding" msgstr "将字符编ç " msgid "Rename Directory" msgstr "更改目錄å稱" msgid "Number Tracks" msgstr "音軌編號" msgid "&Settings" msgstr "設定(&S)" msgid "Enables/disables the toolbar" msgstr "開啟/關閉 工具列" msgid "Show &Toolbar" msgstr "顯示工具列(&T)" msgid "Enables/disables the statusbar" msgstr "開啟/關閉 狀態列" msgid "Show St&atusbar" msgstr "顯示狀態列(&A)" msgid "Show Picture" msgstr "顯示圖片" msgid "Auto Hide Tags" msgstr "è‡ªå‹•éš±è—æ¨™ç±¤" msgid "Configure Kid3" msgstr "設定 Kid3" msgid "&Configure Kid3..." msgstr "設定 Kid3(&C)..." msgid "Kid3 Handbook" msgstr "Kid3 手冊" msgid "Kid3 &Handbook" msgstr "Kid3 手冊(&H)" msgid "About Kid3" msgstr "關於 Kid3" msgid "&About Kid3" msgstr "關於 Kid3(&A)" msgid "About Qt" msgstr "關於 Qt" msgid "About &Qt" msgstr "關於 Qt(&Q)" msgid " [modified]" msgstr "[已更改]" msgid "Toggle the statusbar..." msgstr "切æ›ç‹€æ…‹åˆ—..." msgid "Ready." msgstr "已就緒。" msgid "&Clear List" msgstr "清除清單(&C)" msgid "Clear" msgstr "清除" msgid "Reset" msgstr "é‡è¨­" msgid "Press F2 or double click to edit cell contents." msgstr "按 F2 或雙擊來編輯清單內容。" msgid "Action" msgstr "動作" msgid "Shortcut" msgstr "å¿«æ·éµ" msgid "Server" msgstr "伺æœå™¨" msgid "Accuracy" msgstr "正確率" msgid "Standard Tags" msgstr "標準 標籤" msgid "Additional Tags" msgstr "é¡å¤–標籤" msgid "Cover Art" msgstr "å°é¢" msgid "Invalid File" msgstr "檔案無效" msgid "Data received: %1" msgstr "接收 %1 資料中" msgid "Request sent..." msgstr "é€å‡ºé€£ç·šè¦æ±‚..." msgid "Year" msgstr "å¹´" msgid "Length" msgstr "長度" msgid "Filenames" msgstr "檔å" msgid "URLs" msgstr "ç¶²å€" msgid "Directory name" msgstr "目錄å稱" msgid "Encode as URL" msgstr "編碼æˆç¶²å€" msgid "Confirm" msgstr "確èª" msgid "Output" msgstr "輸出" msgid "Command" msgstr "指令" msgid "Create directory %1 failed\n" msgstr "建立 %1 目錄失敗\n" msgid "File %1 already exists\n" msgstr " %1 檔已經存在\n" msgid "%1 is not a directory\n" msgstr "%1 䏿˜¯ç›®éŒ„\n" msgid "Rename %1 to %2 failed\n" msgstr "%1 æ›´å至 %2 失敗\n" msgid "%1 already exists\n" msgstr "%1 已經存在\n" msgid "%1 is not a file\n" msgstr "%1 䏿˜¯æª”案\n" msgid "New directory name is too different\n" msgstr "新目錄å稱太奇怪\n" msgid "Create directory" msgstr "建立目錄" msgid "Rename file" msgstr "更改檔案å稱" msgid "C&lear" msgstr "清除(&l)" msgid "Execute " msgstr "執行" msgid "Could not execute " msgstr "無法執行" msgid "True if strings are equal" msgstr "字串相等的話則為真" msgid "True if string contains substring" msgstr "å­—ä¸²åŒ…å«æŸå€‹å­å­—串的話則為真" msgid "True if string matches regexp" msgstr "å­—ä¸²ç¬¦åˆæ­£è¦è¡¨ç¤ºå¼çš„話則為真" msgid "Logical AND" msgstr "é‚輯 AND" msgid "Logical OR" msgstr "é‚輯 OR" msgid "Logical negation" msgstr "é‚輯 NOT" msgid "Data" msgstr "資料" msgid "%1 is not writable" msgstr "%1 檔案無法寫入" msgid "All Supported Files" msgstr "æ‰€æœ‰æ”¯æ´æª”案" msgid "All Files" msgstr "所有檔案" msgid "Track" msgstr "音軌" msgid "Absolute path to file" msgstr "檔案的絕å°è·¯å¾‘" msgid "Duration" msgstr "æŒçºŒæ™‚é–“" msgid "Title" msgstr "æ­Œ(曲)å" msgid "Artist" msgstr "演唱(å¥)者" msgid "Album" msgstr "專輯å稱" msgid "Comment" msgstr "內容註釋" msgid "Date" msgstr "日期" msgid "Track Number" msgstr "音軌編號" msgid "Genre" msgstr "曲風" msgid "Album Artist" msgstr "專輯演唱(å¥)者" msgid "Arranger" msgstr "編曲者" msgid "Author" msgstr "作者" msgid "BPM" msgstr "BPM" msgid "Catalog Number" msgstr "分类å·ç " msgid "Compilation" msgstr "曲集" msgid "Composer" msgstr "作曲者" msgid "Conductor" msgstr "指æ®" msgid "Copyright" msgstr "版權" msgid "Disc Number" msgstr "碟片編號" msgid "Encoded-by" msgstr "編碼方å¼" msgid "Encoder Settings" msgstr "用於編碼的軟硬體設定" msgid "Encoding Time" msgstr "編碼時間" msgid "Grouping" msgstr "群組中" msgid "Initial Key" msgstr "" msgid "ISRC" msgstr "ISRC" msgid "Language" msgstr "語言" msgid "Lyricist" msgstr "Lyricist" msgid "Lyrics" msgstr "Lyrics" msgid "Media" msgstr "媒體" msgid "Mood" msgstr "心情" msgid "Original Album" msgstr "原專輯å稱" msgid "Original Artist" msgstr "原演唱(å¥)者" msgid "Original Date" msgstr "原出版日期" msgid "Part" msgstr "退出" msgid "Performer" msgstr "表演者" msgid "Picture" msgstr "圖片" msgid "Publisher" msgstr "出版商" msgid "Release Country" msgstr "版本地区" msgid "Remixer" msgstr "混音" msgid "Sort Album" msgstr "排åºå°ˆè¼¯æ¨™é¡Œ" msgid "Sort Album Artist" msgstr "艺术家专辑排åº" msgid "Sort Artist" msgstr "æŽ’åºæ›²ç›®æ¼”出者" msgid "Sort Composer" msgstr "作曲家篩é¸ç§©åº" msgid "Sort Name" msgstr "標題排åºé †åº" msgid "Subtitle" msgstr "字幕" msgid "Website" msgstr "網站" msgid "WWW Audio File" msgstr "æ­£å¼éŸ³æ•ˆæª”ç¶²é " msgid "WWW Audio Source" msgstr "" msgid "Number of tracks" msgstr "音軌編號" msgid "Extension" msgstr "副檔å" msgid "Bitrate" msgstr "Bitrate" msgid "VBR" msgstr "VBR" msgid "Samplerate" msgstr "å–æ¨£é »çއ" msgid "Channels" msgstr "é »é“" msgid "Codec" msgstr "Codec" msgid "Escape for HTML" msgstr "脫逸 HTML" msgid "Automatic Import" msgstr "自動匯入" msgid "D&estination:" msgstr "目的(&E):" msgid "Tag 1 and Tag 2" msgstr "標籤 1 與 標籤 2" msgid "&Profile:" msgstr "設定檔(&P):" msgid "&Save Settings" msgstr "儲存設定(&S)" msgid "New" msgstr "新增" msgid "A&bort" msgstr "中斷(&B)" msgid "S&tart" msgstr "é–‹å§‹(&T)" msgid "Import Source" msgstr "匯入來æº" msgid "&Server:" msgstr "伺æœå™¨(&S):" msgid "&Accuracy:" msgstr "正確率(&A):" msgid "&Standard Tags" msgstr "標準 標籤(&S)" msgid "&Additional Tags" msgstr "é¡å¤–標籤(&A)" msgid "C&over Art" msgstr "å°é¢(&O)" msgid "Browse Cover Art" msgstr "ç€è¦½ Cover Art 網站" msgid "&Artist/Album" msgstr "演唱(å¥)者/專輯(&A)" msgid "&Source" msgstr "來æº(&S)" msgid "Source:" msgstr "來æº:" msgid "URL:" msgstr "URL:" msgid "&URL extraction" msgstr "ç¶²å€æ“´å±•(&U)" msgid "Match" msgstr "比å°" msgid "Picture URL" msgstr "圖片網å€" msgid "&Browse" msgstr "ç€è¦½(&B)" msgid "Click Browse to start" msgstr "按ç€è¦½é–‹å§‹" msgid "Then drag the picture from the browser to Kid3." msgstr "然後從ç€è¦½å™¨æ‹–拉圖片至 Kid3." msgid "ID3v1" msgstr "ID3v1" msgid "&Mark truncated fields" msgstr "標記被截斷的欄ä½(&M)" msgid "Text &encoding:" msgstr "編碼(&E):" msgid "ID3v2" msgstr "ID3v2" msgid "Use &track/total number of tracks format" msgstr "使用「音軌/音軌總數ã€çš„æ ¼å¼(&T)" msgid "Track number &digits:" msgstr "éŸ³è»Œç·¨è™Ÿä½æ•¸(&D):" msgid "&Genre as text instead of numeric string" msgstr "å°‡æ›²é¢¨è¦–åšæ–‡å­—è€Œä¸æ˜¯æ•¸å­—字串(&G)" msgid "ISO-8859-1" msgstr "ISO-8859-1" msgid "UTF16" msgstr "UTF16" msgid "UTF8" msgstr "UTF8" msgid "&Version used for new tags:" msgstr "使用新標籤版本(&V):" msgid "ID3v2.3.0" msgstr "ID3v2.3.0" msgid "ID3v2.4.0" msgstr "ID3v2.4.0" msgid "Ogg/Vorbis" msgstr "Ogg/Vorbis" msgid "Co&mment field name:" msgstr "內容註釋欄ä½å稱(&m):" msgid "&Picture field name:" msgstr "圖片欄ä½å稱(&P):" msgid "Custom &Genres" msgstr "自訂曲風(&G)" msgid "&Show only custom genres" msgstr "僅顯示自訂曲風(&S)" msgid "&Quick Access Tags" msgstr "快速访问标签(&Q)" msgid "&Tag Format" msgstr "標籤格å¼(&T)" msgid "Tag &1" msgstr "標籤 &1" msgid "Tag &2" msgstr "標籤 &2" msgid "Tag 1 a&nd Tag 2" msgstr "標籤 1 與 標籤 2(&N)" msgid "Startup" msgstr "啟動" msgid "&Load last-opened files" msgstr "" msgid "Save" msgstr "儲存" msgid "&Preserve file timestamp" msgstr "ä¿ç•™æª”案的原來時間(&P)" msgid "&Mark changes" msgstr "標記變更(&M)" msgid "F&ilename for cover:" msgstr "專輯å°é¢çš„æ–‡ä»¶å(&I):" msgid "&Filename Format" msgstr "檔案å稱格å¼(&F)" msgid "Browser" msgstr "ç€è¦½" msgid "Web &browser:" msgstr "ç¶²é ç€è¦½å™¨(&B):" msgid "Context &Menu Commands" msgstr "å…§æ–‡é¸å–®æŒ‡ä»¤(&M)" #, fuzzy msgid "&Play on double click" msgstr "雙擊上播放(&P)" msgid "Proxy" msgstr "代ç†ä¼ºæœå™¨" msgid "&Proxy:" msgstr "代ç†ä¼ºæœå™¨(&P):" msgid "&Use authentication with proxy" msgstr "代ç†ä¼ºæœå™¨è¦èªè­‰(&U)" msgid "Proxy user &name:" msgstr "代ç†ä¼ºæœå™¨å¸³è™Ÿ(&n):" msgid "Proxy pass&word:" msgstr "代ç†ä¼ºæœå™¨å¯†ç¢¼(&w):" msgid "&Metadata Plugins && Priority" msgstr "中繼資料外掛工具 && 優先級(&M)" msgid "A&vailable Plugins" msgstr "å¯ç”¨çš„外掛程å¼(&V)" msgid "Changes take only effect after a restart!" msgstr "æŸäº›è®Šæ›´åœ¨é‡æ–°å•Ÿå‹•å¾Œæ‰æœƒç”Ÿæ•ˆ!" msgid "From Clip&board" msgstr "從剪貼簿(&b)" msgid "&Import" msgstr "匯入(&I)" msgid "&Export" msgstr "匯出(&E)" msgid "&View" msgstr "檢視(&V)" msgid "Text Encoding" msgstr "字元編碼" msgid "Text" msgstr "文字" msgid "Description" msgstr "æè¿°" msgid "Owner" msgstr "æ“æœ‰è€…" msgid "Email" msgstr "é›»å­ä¿¡ç®±" msgid "Rating" msgstr "評比" msgid "Picture Type" msgstr "圖片格å¼" msgid "Image format" msgstr "圖片格å¼" msgid "Mimetype" msgstr "Mimetype" msgid "Counter" msgstr "計數器" msgid "Identifier" msgstr "識別å­" msgid "Volume Adjustment" msgstr "音é‡èª¿æ•´" msgid "Number of Bits" msgstr "ä½å…ƒæ•¸" msgid "Volume Change Right" msgstr "" msgid "Volume Change Left" msgstr "" msgid "Peak Volume Right" msgstr "" msgid "Peak Volume Left" msgstr "" msgid "Timestamp Format" msgstr "時間格å¼" msgid "Content Type" msgstr "內容類型" msgid "Price" msgstr "價格" msgid "Seller" msgstr "" msgid "Images" msgstr "圖片" msgid "UTF16BE" msgstr "UTF16BE" msgid "Other" msgstr "å…¶ä»–" msgid "32x32 pixels PNG file icon" msgstr "32x32 åƒç´  PNG 檔圖示" msgid "Other file icon" msgstr "其他檔案圖示" msgid "Cover (front)" msgstr "å°é¢(å‰)" msgid "Cover (back)" msgstr "å°é¢(後)" msgid "Leaflet page" msgstr "å°å†Šå­" msgid "Lead artist/lead performer/soloist" msgstr "首席演出者" msgid "Artist/performer" msgstr "演出者" msgid "Band/Orchestra" msgstr "樂團" msgid "Lyricist/text writer" msgstr "作詞者" msgid "Recording Location" msgstr "錄製地點" msgid "During recording" msgstr "錄音期間" msgid "During performance" msgstr "表演期間" msgid "Movie/video screen capture" msgstr "電影/å½±åƒèž¢å¹•æ“·å–" msgid "A bright coloured fish" msgstr "明亮高彩魚" msgid "Illustration" msgstr "æ’圖" msgid "Band/artist logotype" msgstr "樂團╱演唱者標誌" msgid "Publisher/Studio logotype" msgstr "出版商╱錄音室標誌" msgid "MPEG frames as unit" msgstr "以 MPEG 訊框為單ä½" msgid "Milliseconds as unit" msgstr "以毫秒為單ä½" msgid "Text transcription" msgstr "" msgid "Movement/part name" msgstr "" msgid "Events" msgstr "事件" msgid "Chord" msgstr "" msgid "Trivia/pop up" msgstr "" msgid "Export" msgstr "匯出" msgid "Format:" msgstr "æ ¼å¼:" msgid "Header:" msgstr "標頭:" msgid "Tracks:" msgstr "曲目:" msgid "Footer:" msgstr "é å°¾:" msgid "To F&ile" msgstr "至檔案(&i)" msgid "To Clip&board" msgstr "至剪貼簿(&b)" msgid "&Source:" msgstr "來æº(&S):" msgid "File Error" msgstr "檔案錯誤" msgid "&Filter:" msgstr "篩é¸(&F):" msgid "&Expression:" msgstr "ç®—å¼(&E):" msgid "&Apply" msgstr "套用(&A)" msgid "Accuracy:" msgstr "正確率:" msgid "Cover Art:" msgstr "å°é¢:" msgid "From F&ile/Clipboard..." msgstr "自剪貼簿匯入(&i)..." msgid "From T&ags..." msgstr "從標籤(&a)..." msgid "&From Server:" msgstr "從伺æœå™¨(&F):" msgid "Check maximum allowable time &difference (sec):" msgstr "è«‹å‹¾é¸æœ€å¤§å¯å…許的時間差異(å–®ä½ç‚ºç§’)(&D):" msgid "Match with:" msgstr "符åˆï¼š" msgid "&Length" msgstr "長度(&L)" msgid "T&rack" msgstr "音軌(&R)" msgid "&Title" msgstr "æ­Œ(曲)å(&T)" msgid "&Start number:" msgstr "起始編號(&S):" msgid "&Destination:" msgstr "到(&D):" msgid "&Total number of tracks:" msgstr "音軌編號(&T):" msgid "Create Playlist" msgstr "建立播放清單" msgid "Playlist File Name" msgstr "播放清單檔案å稱" msgid "Same as &directory name" msgstr "與目錄åŒå(&D)" msgid "&Format:" msgstr "æ ¼å¼(&F):" msgid "Cr&eate in:" msgstr "建立在(&E):" msgid "Current directory" msgstr "ç›®å‰ç›®éŒ„" msgid "Every directory" msgstr "æ¯å€‹ç›®éŒ„" msgid "Top-level directory" msgstr "頂層目錄" msgid "Playlist Content" msgstr "播放清單內容" msgid "For&mat:" msgstr "æ ¼å¼(&M):" msgid "Incl&ude only the selected files" msgstr "僅包å«é¸å–的檔案(&U)" msgid "Sort by file &name" msgstr "便“šæª”åæŽ’åº(&N)" msgid "Sort by &tag field" msgstr "便“šæ¨™ç±¤æ¬„ä½æŽ’åº(&T)" msgid "Use &relative path for files in playlist" msgstr "播放清單中使用相å°è·¯å¾‘(&R)" msgid "Use full p&ath for files in playlist" msgstr "播放清單中使用完全路徑(&A)" msgid "Write only &list of files" msgstr "åƒ…ä¾æ“šæª”æ¡ˆæ¸…å–®ä¾†å¯«å…¥(&L)" msgid "Write &info using" msgstr "坫入便“š(&I)" msgid "Preview" msgstr "é è¦½" msgid "Create Directory" msgstr "建立目錄" msgid "From Tag 2 and Tag 1" msgstr "從 標籤 2 與 標籤 1" msgid "From:" msgstr "從:" msgid "To:" msgstr "至:" msgid "&Find" msgstr "尋找(&F)" msgid "C&GI Path:" msgstr "CGI路徑(&G):" msgid "Track Title/Artist - Album" msgstr "音軌標題/演出者 - 專輯標題" msgid "State" msgstr "狀態" msgid "No result" msgstr "æ²’æœ‰çµæžœ" msgid "No result selected" msgstr "æ²’æœ‰é¸æ“‡çµæžœ" msgid "Import from Tags" msgstr "從標籤" msgid "Extraction:" msgstr "æå–:" msgid "Import from File/Clipboard" msgstr "自剪貼簿匯入" msgid "From F&ile" msgstr "從檔案(&I)" msgid "Download" msgstr "下載" msgid "Opening directory..." msgstr "開啟目錄..." msgid "Saving directory..." msgstr "儲存目錄..." msgid "" "The current directory has been modified.\n" "Do you want to save it?" msgstr "" "ç›®å‰ç›®éŒ„已更動\n" "è¦å„²å­˜å—Žï¼Ÿ" msgid "Warning" msgstr "警告" msgid "Exiting..." msgstr "離開..." msgid "Creating playlist..." msgstr "新增播放清單..." msgid "Import" msgstr "匯入" msgid "Error while renaming:\n" msgstr "更改檔å發生錯誤:\n" msgid " [filtered]" msgstr "[已篩é¸]" msgid "Add Frame" msgstr "新增訊框" msgid "Select the frame ID" msgstr "鏿“‡è¨Šæ¡†ä»£ç¢¼" msgid "Rename File" msgstr "更改檔å" msgid "Enter new file name:" msgstr "輸入新檔å:" msgid "Do you really want to move these %1 items to the trash?" msgstr "您真的è¦å°‡é€™ %1 個項目移到資æºå›žæ”¶ç­’嗎?" msgid "Do you really want to move this item to the trash?" msgstr "您真的è¦å°‡é€™ 1 個項目移到資æºå›žæ”¶ç­’嗎?" msgid "Move to Trash" msgstr "移至垃圾桶" msgid "Directory must be empty.\n" msgstr "目錄裡é¢å¿…須是空的.\n" msgid "Could not move these files to the Trash" msgstr "無法將這些檔案丟到垃圾桶" msgid "Expand All" msgstr "全部展開" msgid "&Expand all" msgstr "全部展開(&E)" msgid "&Collapse all" msgstr "全部折疊(&C)" msgid "&Rename" msgstr "æ›´å(&R)" msgid "&Move to Trash" msgstr "移至垃圾桶(&M)" msgid "Kid3" msgstr "Kid3" msgid "F&ile" msgstr "檔案(&I)" msgid "Name:" msgstr "檔案å稱:" msgid "Filename from Tag 1" msgstr "檔å從標籤 1" msgid "Filename from Tag 2" msgstr "檔å從標籤 2" msgid "Tag 1 from Filename" msgstr "標籤 1從檔å" msgid "Tag 2 from Filename" msgstr "標籤 2從檔å" msgid "&Add..." msgstr "新增(&A)..." msgid "Move &Up" msgstr "上移(&U)" msgid "Move &Down" msgstr "下移(&D)" msgid "&Edit..." msgstr "編輯(&E)..." msgid "&Remove" msgstr "移除(&R)" msgid "&Insert row" msgstr "æ’入列(&I)" msgid "&Delete row" msgstr "刪除列(&D)" msgid "&Clear row" msgstr "清除列(&C)" msgid "Format while editing" msgstr "æª”åæ ¼å¼çš„編輯" msgid "Case conversion:" msgstr "大å°å¯«çš„轉æ›:" msgid "No changes" msgstr "ä¸è½‰æ›" msgid "All lowercase" msgstr "全部å°å¯«" msgid "All uppercase" msgstr "全部大寫" msgid "First letter uppercase" msgstr "第一個字æ¯å¤§å¯«" msgid "All first letters uppercase" msgstr "所有字的第一個字æ¯å¤§å¯«" msgid "Locale:" msgstr "地å€è¨­å®š:" msgid "None" msgstr "ç„¡" msgid "String replacement:" msgstr "字詞替æ›:" msgid "From" msgstr "從" msgid "To" msgstr "至" msgid "&Add" msgstr "新增(&A)" msgid "&Select all" msgstr "å…¨é¸(&A)" msgid "&Deselect all" msgstr "å–æ¶ˆé¸æ“‡(&D)" msgid "View Picture" msgstr "檢視圖片" msgid "" "Drag album\n" "artwork\n" "here" msgstr "" "拖拉專輯\n" "圖片\n" "至此" msgid "Play/Pause" msgstr "播放/æš«åœ" msgid "Stop playback" msgstr "åœæ­¢æ’­æ”¾" msgid "Previous Track" msgstr "上一音軌" msgid "Next Track" msgstr "下一音軌" msgid "Close" msgstr "關閉" msgid "Mute" msgstr "éœéŸ³" msgid "Volume: %1%" msgstr "音é‡: %1%" msgid "Add Item" msgstr "新增項目" msgid "Edit Item" msgstr "編輯項目" msgid "MusicBrainz Fingerprint" msgstr "MusicBrainz Fingerprint" msgid "Unrecognized" msgstr "未辨識" msgid "Recognized" msgstr "已辨識" msgid "User Selection" msgstr "ä½¿ç”¨è€…é¸æ“‡" msgid "ID Lookup" msgstr "" msgid "Fingerprint" msgstr "Fingerprint" msgid "Metadata Lookup" msgstr "" msgid "Amazon" msgstr "Amazon" msgid "Discogs" msgstr "Discogs" msgid "gnudb.org" msgstr "gnudb.org" msgid "TrackType.org" msgstr "TrackType.org" msgid "AENC - Audio encryption" msgstr "AENC - 音效加密" msgid "APIC - Attached picture" msgstr "APIC - 附加圖片" msgid "COMM - Comments" msgstr "COMM - è©•è«–" msgid "COMR - Commercial" msgstr "COMR - 商業" msgid "ENCR - Encryption method registration" msgstr "ENCR - 加密方法註冊" msgid "EQUA - Equalization" msgstr "EQUA - 等化" msgid "ETCO - Event timing codes" msgstr "" msgid "GEOB - General encapsulated object" msgstr "" msgid "GRID - Group identification registration" msgstr "" msgid "IPLS - Involved people list" msgstr "" msgid "LINK - Linked information" msgstr "LINK - 連çµè³‡è¨Š" msgid "MCDI - Music CD identifier" msgstr "MCDI - 音樂光碟識別" msgid "MLLT - MPEG location lookup table" msgstr "MLLT - MPEG ä½ç½®æŸ¥è©¢è¡¨" msgid "OWNE - Ownership frame" msgstr "" msgid "PRIV - Private frame" msgstr "" msgid "PCNT - Play counter" msgstr "PCNT - 播放計數器" msgid "POPM - Popularimeter" msgstr "" msgid "POSS - Position synchronisation frame" msgstr "POSS - ä½ç½®åŒæ­¥è¨Šæ¡†" msgid "RBUF - Recommended buffer size" msgstr "RBUF - 建議緩è¡å™¨å¤§å°" msgid "RVAD - Relative volume adjustment" msgstr "" msgid "RVRB - Reverb" msgstr "" msgid "SYLT - Synchronized lyric/text" msgstr "" msgid "SYTC - Synchronized tempo codes" msgstr "" msgid "TALB - Album/Movie/Show title" msgstr "" msgid "TBPM - BPM (beats per minute)" msgstr "" msgid "TCOM - Composer" msgstr "" msgid "TCON - Content type" msgstr "" msgid "TCOP - Copyright message" msgstr "TCOP - 版權訊æ¯" msgid "TDAT - Date" msgstr "TDAT - 日期" msgid "TDLY - Playlist delay" msgstr "TDLY - 播放清單延é²" msgid "TENC - Encoded by" msgstr "TENC - 編碼方å¼" msgid "TEXT - Lyricist/Text writer" msgstr "TEXT - 作詞者" msgid "TFLT - File type" msgstr "TFLT - 檔案型態" msgid "TIME - Time" msgstr "TIME - 時間" msgid "TIT1 - Content group description" msgstr "TIT1 - 內容群組æè¿°" msgid "TIT2 - Title/songname/content description" msgstr "TIT2 - 標題/æ­Œå/內容æè¿°" msgid "TIT3 - Subtitle/Description refinement" msgstr "" msgid "TKEY - Initial key" msgstr "" msgid "TLAN - Language(s)" msgstr "TLAN - 語言" msgid "TLEN - Length" msgstr "TLEN - 長度" msgid "TMED - Media type" msgstr "TMED - 媒體型態" msgid "TOAL - Original album/movie/show title" msgstr "" msgid "TOFN - Original filename" msgstr "TOFN - 原始檔å" msgid "TOLY - Original lyricist(s)/text writer(s)" msgstr "TOLY - 原始作詞者" msgid "TOPE - Original artist(s)/performer(s)" msgstr "TOPE - 原始演出者" msgid "TORY - Original release year" msgstr "TORY - 原始發行年份" msgid "TOWN - File owner/licensee" msgstr "TOWN - æª”æ¡ˆæ“æœ‰è€…/授權者" msgid "TPE1 - Lead performer(s)/Soloist(s)" msgstr "TPE1 - 首席表演者/ç¨å”±è€…" msgid "TPE2 - Band/orchestra/accompaniment" msgstr "TPE2 - 樂團" msgid "TPE3 - Conductor/performer refinement" msgstr "" msgid "TPE4 - Interpreted, remixed, or otherwise modified by" msgstr "" msgid "TPOS - Part of a set" msgstr "" msgid "TPUB - Publisher" msgstr "TPUB - 發行者" msgid "TRCK - Track number/Position in set" msgstr "TRCK - 音軌編號/ä½ç½®" msgid "TRDA - Recording dates" msgstr "TRDA - 錄製日期" msgid "TRSN - Internet radio station name" msgstr "" msgid "TRSO - Internet radio station owner" msgstr "" msgid "TSIZ - Size" msgstr "TSIZ - 大å°" msgid "TSRC - ISRC (international standard recording code)" msgstr "" msgid "TSSE - Software/Hardware and settings used for encoding" msgstr "TSSE - 用於編碼的軟硬體設定" msgid "TXXX - User defined text information" msgstr "TXXX - 使用者定義文字資訊" msgid "TYER - Year" msgstr "TYER - 年份" msgid "UFID - Unique file identifier" msgstr "" msgid "USER - Terms of use" msgstr "" msgid "USLT - Unsynchronized lyric/text transcription" msgstr "" msgid "WCOM - Commercial information" msgstr "WCOM - 商業資訊" msgid "WCOP - Copyright/Legal information" msgstr "WCOP - 版權資訊" msgid "WOAF - Official audio file webpage" msgstr "WOAF - æ­£å¼éŸ³æ•ˆæª”ç¶²é " msgid "WOAR - Official artist/performer webpage" msgstr "WOAR - æ­£å¼æ¼”出者網é " msgid "WOAS - Official audio source webpage" msgstr "" msgid "WORS - Official internet radio station homepage" msgstr "" msgid "WPAY - Payment" msgstr "" msgid "WPUB - Official publisher webpage" msgstr "" msgid "WXXX - User defined URL link" msgstr "" msgid "MusicBrainz Release" msgstr "MusicBrainz Release" msgid "ASPI - Audio seek point index" msgstr "ASPI - 音效æœå°‹é»žç´¢å¼•" msgid "EQU2 - Equalisation (2)" msgstr "" msgid "RVA2 - Relative volume adjustment (2)" msgstr "RVA2 - 相å°éŸ³é‡èª¿æ•´ (2)" msgid "SEEK - Seek frame" msgstr "SEEK - 尋找訊框" msgid "SIGN - Signature frame" msgstr "SIGN - 簽章訊框" msgid "TCMP - iTunes compilation flag" msgstr "TCMP - iTunes的編譯" msgid "TDEN - Encoding time" msgstr "TDEN - 編碼時間" msgid "TDOR - Original release time" msgstr "TDOR - 原始發行時間" msgid "TDRC - Recording time" msgstr "TDRC - 錄音時間" msgid "TDRL - Release time" msgstr "TDRL - 發行時間" msgid "TDTG - Tagging time" msgstr "TDTG - 標籤時間 " msgid "TIPL - Involved people list" msgstr "TIPL - åƒèˆ‡äººå“¡æ¸…å–®" msgid "TMCL - Musician credits list" msgstr "TMCL - éŸ³æ¨‚èªŒè¬æ¸…å–®" msgid "TMOO - Mood" msgstr "" msgid "TPRO - Produced notice" msgstr "" msgid "TSO2 - Album artist sort order" msgstr "TSO2 - 艺术家专辑排åº" msgid "TSOA - Album sort order" msgstr "" msgid "TSOC - Composer sort order" msgstr "TSOC - 作曲家篩é¸ç§©åº" msgid "TSOP - Performer sort order" msgstr "" msgid "TSOT - Title sort order" msgstr "TSOT - 標題排åºé †åº" msgid "TSST - Set subtitle" msgstr "TSST - 設定字幕" �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/�������������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�12246035113�0013132�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/CMakeLists.txt�����������������������������������������������������������������������0000664�0000000�0000000�00000005176�12246035113�0015703�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������if (WITH_GCC_PCH) set(_headerFilename ${CMAKE_BINARY_DIR}/allsys.h) set(_gchFilename "${_headerFilename}.gch") if (NOT EXISTS ${_headerFilename}) message(STATUS "Generating ${_headerFilename}") if (BUILD_KDE_APP) set(_includeRegex "#include <[Qkcp]") else (BUILD_KDE_APP) set(_includeRegex "#include <[Qcp]") endif (BUILD_KDE_APP) file(GLOB_RECURSE _srcList "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/*.hpp" "${CMAKE_CURRENT_SOURCE_DIR}/*.c" "${CMAKE_CURRENT_SOURCE_DIR}/*.h") set(_includes) foreach (_src ${_srcList}) file(STRINGS ${_src} _inc REGEX ${_includeRegex}) if (NOT "${_inc}" MATCHES "#include (.config\\.h.|.QAudio.*|.QMedia.*|.Qt?Plugin.*|.Qt?Test.*)") list(APPEND _includes ${_inc}) endif() endforeach (_src ${_srcList}) list(SORT _includes) list(REMOVE_DUPLICATES _includes) set(_txt "/* automatically generated */\n" "#ifndef ALLSYS_H\n" "#define ALLSYS_H\n" "#ifdef __cplusplus\n") foreach(_inc ${_includes}) set(_txt "${_txt}${_inc}\n") endforeach(_inc _includes) set(_txt "${_txt}#endif\n#endif\n") file(WRITE ${_headerFilename} ${_txt}) message(STATUS "Generating ${_gchFilename}") file(REMOVE ${_gchFilename}) set(_additionalFlags "-DQT_CORE_LIB -DQT_DBUS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_XML_LIB -Wundef -Wcast-align -Wall -W -Wpointer-arith -fno-check-new -fno-common -Woverloaded-virtual -fno-exceptions -D_REENTRANT -DQT_NO_EXCEPTIONS -DQT_NO_STL -fvisibility=hidden -fvisibility-inlines-hidden -DQT_ASCII_CAST_WARNINGS -DQT_NO_CAST_TO_ASCII") if (CMAKE_BUILD_TYPE MATCHES Debug) set(_additionalFlags "${_additionalFlags} -g -DQT_DEBUG") else (CMAKE_BUILD_TYPE MATCHES Debug) set(_additionalFlags "${_additionalFlags} -O3 -DQT_NO_DEBUG -DNDEBUG") endif (CMAKE_BUILD_TYPE MATCHES Debug) list(APPEND _args -c ${_headerFilename} -o ${_gchFilename} ${_additionalFlags}) get_directory_property(_dirinc INCLUDE_DIRECTORIES) foreach (_inc ${_dirinc}) list(APPEND _args "-I" ${_inc}) endforeach(_inc ${_dirinc}) separate_arguments(_args) execute_process( COMMAND ${CMAKE_CXX_COMPILER} ${CMAKE_CXX_COMPILER_ARG1} ${_args}) endif (NOT EXISTS ${_headerFilename}) set(GCC_PCH_COMPILE_FLAGS "-Winvalid-pch -include ${_headerFilename}") endif (WITH_GCC_PCH) set(PLUGIN_LIBRARIES) add_subdirectory(plugins) add_subdirectory(core) add_subdirectory(gui) add_subdirectory(app) if (BUILD_TEST_APP) add_subdirectory(test) endif (BUILD_TEST_APP) ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/���������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�12246035113�0013712�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/CMakeLists.txt�������������������������������������������������������������������0000664�0000000�0000000�00000002326�12246035113�0016455�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������include_directories( ../core/config ../core/utils ../core/tags ../core/model ../core/import ../gui/forms ../gui/dialogs ) if (BUILD_CLI_APP) add_subdirectory(cli) endif (BUILD_CLI_APP) if (BUILD_QT_APP) add_subdirectory(qt) endif (BUILD_QT_APP) if (BUILD_KDE_APP) find_package(KDE4 REQUIRED) include (KDE4Defaults) if (BUILD_SHARED_LIBS) # FindKDE4Internal.cmake will overwrite our RPATH if LIB_INSTALL_DIR (which # defaults to ${CMAKE_INSTALL_PREFIX}/lib) is not an implicit link directory. # In practice this means that most values for CMAKE_INSTALL_PREFIX other # than /usr will cause the RPATH to be overwritten and the kid3 executable # will not find its libraries. This happens also for the default value # /usr/local for CMAKE_INSTALL_PREFIX. # The RPATH is set again here to reverse this. list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_PREFIX}/${WITH_LIBDIR}" _isSystemDir) if ("${_isSystemDir}" STREQUAL "-1") set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${WITH_LIBDIR}") endif ("${_isSystemDir}" STREQUAL "-1") endif (BUILD_SHARED_LIBS) add_subdirectory(kde) kde4_install_icons(${ICON_INSTALL_DIR}) endif (BUILD_KDE_APP) ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/cli/�����������������������������������������������������������������������������0000775�0000000�0000000�00000000000�12246035113�0014461�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/cli/CMakeLists.txt���������������������������������������������������������������0000664�0000000�0000000�00000005246�12246035113�0017230�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������### Check for readline set(READLINE_INCLUDE_DIR) set(READLINE_LIBRARIES) if (WITH_READLINE) find_path(READLINE_INCLUDE_DIR readline/readline.h) find_library(READLINE_LIBRARY readline) if (READLINE_INCLUDE_DIR AND READLINE_LIBRARY) # Check whether it needs to be linked against termcap set(_CMAKE_REQUIRED_INCLUDES_TMP "${CMAKE_REQUIRED_INCLUDES}") set(_CMAKE_REQUIRED_LIBRARIES_TMP "${CMAKE_REQUIRED_LIBRARIES}") set(CMAKE_REQUIRED_INCLUDES "${READLINE_INCLUDE_DIR}") set(CMAKE_REQUIRED_LIBRARIES "${READLINE_LIBRARY}") CHECK_CXX_SOURCE_COMPILES("#include<cstdio>\n#include <readline/readline.h>\nint main() {\n readline(\"\");\n return 0;\n}\n" READLINE_STANDALONE_FOUND) if (READLINE_STANDALONE_FOUND) set(READLINE_LIBRARIES ${READLINE_LIBRARY}) else (READLINE_STANDALONE_FOUND) # Check whether it works with termcap find_library(TERMCAP_LIBRARY termcap) if (TERMCAP_LIBRARY) set(CMAKE_REQUIRED_LIBRARIES "${READLINE_LIBRARY};${TERMCAP_LIBRARY}") CHECK_CXX_SOURCE_COMPILES("#include<cstdio>\n#include <readline/readline.h>\nint main() {\n readline(\"\");\n return 0;\n}\n" READLINE_TERMCAP_FOUND) if (READLINE_TERMCAP_FOUND) set(READLINE_LIBRARIES "${READLINE_LIBRARY};${TERMCAP_LIBRARY}") endif (READLINE_TERMCAP_FOUND) endif (TERMCAP_LIBRARY) endif (READLINE_STANDALONE_FOUND) set(CMAKE_REQUIRED_INCLUDES "${_CMAKE_REQUIRED_INCLUDES_TMP}") set(CMAKE_REQUIRED_LIBRARIES "${_CMAKE_REQUIRED_LIBRARIES_TMP}") endif (READLINE_INCLUDE_DIR AND READLINE_LIBRARY) if (READLINE_LIBRARIES) message(STATUS "readline found: ${READLINE_LIBRARIES}") set(HAVE_READLINE 1) else (READLINE_LIBRARIES) message(FATAL_ERROR "Could not find readline") endif (READLINE_LIBRARIES) endif (WITH_READLINE) configure_file(cliconfig.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/cliconfig.h) include_directories(${CMAKE_CURRENT_BINARY_DIR} ${READLINE_INCLUDE_DIR}) set(cli_SRCS maincli.cpp abstractcli.cpp kid3cli.cpp clicommand.cpp standardinputreader.cpp ) if (HAVE_READLINE) set(cli_SRCS ${cli_SRCS} readlinecompleter.cpp) endif (HAVE_READLINE) set(cli_MOC_HDRS abstractcli.h kid3cli.h clicommand.h standardinputreader.h ) qt4_wrap_cpp(cli_GEN_MOC_SRCS ${cli_MOC_HDRS}) set(cli_SRCS ${cli_SRCS} ${cli_GEN_MOC_SRCS}) add_definitions(${QT_DEFINITIONS} ${QT_EXECUTABLE_COMPILE_FLAGS}) add_executable(kid3-cli ${cli_SRCS} ${cli_GEN_RCS}) target_link_libraries(kid3-cli kid3-core ${READLINE_LIBRARIES}) if (APPLE) set(cli_BINDIR ${WITH_LIBDIR}) else (APPLE) set(cli_BINDIR ${WITH_BINDIR}) endif (APPLE) install(TARGETS kid3-cli BUNDLE DESTINATION . RUNTIME DESTINATION ${cli_BINDIR}) ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/cli/abstractcli.cpp��������������������������������������������������������������0000664�0000000�0000000�00000007602�12246035113�0017465�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \file abstractcli.cpp * Abstract base class for command line interface. * * \b Project: Kid3 * \author Urs Fleisch * \date 10 Aug 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "abstractcli.h" #include <QTimer> #include <QCoreApplication> #include <QThread> #ifdef Q_OS_WIN32 #include <windows.h> #endif #include "standardinputreader.h" /** * Constructor. * @param prompt command line prompt * @param parent parent object */ AbstractCli::AbstractCli(const char* prompt, QObject* parent) : QObject(parent), #ifndef Q_OS_WIN32 m_cout(stdout, QIODevice::WriteOnly), m_cerr(stderr, QIODevice::WriteOnly), #endif m_stdinReader(new StandardInputReader(prompt)), m_returnCode(0) { } /** * Destructor. */ AbstractCli::~AbstractCli() { } /** * Prompt next line from standard input. * Has to be called when the processing in readLine() is finished and * the user shall be prompted for the next line. */ void AbstractCli::promptNextLine() { emit requestNextLine(); } /** * Set return code of application. * @param code return code, 0 means success */ void AbstractCli::setReturnCode(int code) { m_returnCode = code; } /** * Exit application with return code. */ void AbstractCli::quitApplicationWithReturnCode() { QCoreApplication::exit(m_returnCode); } /** * Execute process. */ void AbstractCli::execute() { QThread* conInThread = new QThread; conInThread->setObjectName(QLatin1String("conInThread")); connect(conInThread, SIGNAL(started()), m_stdinReader, SLOT(readLine())); connect(conInThread, SIGNAL(finished()), m_stdinReader, SLOT(deleteLater())); connect(m_stdinReader, SIGNAL(lineReady(QString)), this, SLOT(readLine(QString)), Qt::QueuedConnection); connect(this, SIGNAL(requestNextLine()), m_stdinReader, SLOT(readLine()), Qt::QueuedConnection); connect(this, SIGNAL(requestTermination()), conInThread, SLOT(quit())); connect(conInThread, SIGNAL(finished()), conInThread, SLOT(deleteLater())); #if QT_VERSION < 0x050000 connect(conInThread, SIGNAL(terminated()), conInThread, SLOT(deleteLater())); #endif m_stdinReader->moveToThread(conInThread); conInThread->start(); } /** * Terminate command line processor. */ void AbstractCli::terminate() { flushStandardOutput(); emit requestTermination(); if (m_returnCode == 0) { QTimer::singleShot(0, qApp, SLOT(quit())); } else { QTimer::singleShot(0, this, SLOT(quitApplicationWithReturnCode())); } } /** * Write a line to standard output. * @param line line to write */ void AbstractCli::writeLine(const QString& line) { #ifdef Q_OS_WIN32 QString str = line + QLatin1Char('\n'); WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), str.utf16(), str.size(), 0, 0); #else m_cout << line; m_cout << QLatin1Char('\n'); m_cout.flush(); #endif } /** * Write a line to standard error. * @param line line to write */ void AbstractCli::writeErrorLine(const QString& line) { #ifdef Q_OS_WIN32 QString str = line + QLatin1Char('\n'); WriteConsoleW(GetStdHandle(STD_ERROR_HANDLE), str.utf16(), str.size(), 0, 0); #else m_cerr << line; m_cerr << QLatin1Char('\n'); m_cerr.flush(); #endif } /** * Flush the standard output. */ void AbstractCli::flushStandardOutput() { #ifndef Q_OS_WIN32 m_cout.flush(); #endif } ������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/cli/abstractcli.h����������������������������������������������������������������0000664�0000000�0000000�00000005342�12246035113�0017131�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \file abstractcli.h * Abstract base class for command line interface. * * \b Project: Kid3 * \author Urs Fleisch * \date 10 Aug 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #ifndef ABSTRACTCLI_H #define ABSTRACTCLI_H #include <QObject> #ifndef Q_OS_WIN32 #include <QTextStream> #endif class StandardInputReader; /** * Abstract base class for command line interface. */ class AbstractCli : public QObject { Q_OBJECT public: /** * Constructor. * @param prompt command line prompt * @param parent parent object */ explicit AbstractCli(const char* prompt = "", QObject* parent = 0); /** * Destructor. */ virtual ~AbstractCli(); /** * Write a line to standard output. * @param line line to write */ void writeLine(const QString& line); /** * Write a line to standard error. * @param line line to write */ void writeErrorLine(const QString& line); /** * Flush the standard output. */ void flushStandardOutput(); /** * Prompt next line from standard input. * Has to be called when the processing in readLine() is finished and * the user shall be prompted for the next line. */ void promptNextLine(); /** * Set return code of application. * @param code return code, 0 means success */ void setReturnCode(int code); public slots: /** * Execute process. */ virtual void execute(); /** * Terminate command line processor. */ virtual void terminate(); signals: /** * Emitted when the next line shall be read. */ void requestNextLine(); /** * Emitted to request termination. */ void requestTermination(); protected slots: /** * Process command line. * Has to be implemented by concrete derived class. * @param line command line */ virtual void readLine(const QString& line) = 0; private slots: /** * Exit application with return code. */ void quitApplicationWithReturnCode(); private: #ifndef Q_OS_WIN32 QTextStream m_cout; QTextStream m_cerr; #endif StandardInputReader* m_stdinReader; int m_returnCode; }; #endif // ABSTRACTCLI_H ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/cli/clicommand.cpp���������������������������������������������������������������0000664�0000000�0000000�00000067230�12246035113�0017303�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \file clicommand.cpp * Command line interface commands. * * \b Project: Kid3 * \author Urs Fleisch * \date 11 Aug 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "clicommand.h" #include <QStringList> #include <QTimer> #include <QDir> #include <QItemSelectionModel> #include "kid3cli.h" #include "kid3application.h" #include "fileproxymodel.h" #include "frametablemodel.h" #include "importconfig.h" #include "exportconfig.h" #include "filterconfig.h" #include "fileconfig.h" #include "rendirconfig.h" #include "batchimportconfig.h" #include "batchimporter.h" #include "downloadclient.h" #include "dirrenamer.h" #include "audioplayer.h" /** Default command timeout in milliseconds. */ static const int DEFAULT_TIMEOUT_MS = 3000; /** * Constructor. * @param processor command line processor * @param name name with which command is invoked * @param help help text for command * @param argspec argument specification */ CliCommand::CliCommand(Kid3Cli* processor, const QString& name, const QString& help, const QString& argspec) : QObject(processor), m_processor(processor), m_name(name), m_help(help), m_argspec(argspec), m_timerId(0), m_timeoutMs(DEFAULT_TIMEOUT_MS), m_result(0) { } /** * Destructor. */ CliCommand::~CliCommand() { } /** * Reset state to defaults. */ void CliCommand::clear() { if (m_timerId != 0) { killTimer(m_timerId); m_timerId = 0; } m_errorMsg.clear(); m_args.clear(); m_result = 0; } /** * Execute command. */ void CliCommand::execute() { if (m_timerId != 0) { killTimer(m_timerId); m_timerId = 0; } int msec = getTimeout(); if (msec > 0) { m_timerId = startTimer(msec); } connectResultSignal(); startCommand(); } /** * Terminate command. */ void CliCommand::terminate() { if (m_timerId != 0) { killTimer(m_timerId); m_timerId = 0; } disconnectResultSignal(); emit finished(); } /** * Connect signals used to emit finished(). * This method is called after startCommand(). The default implementation * invokes terminate() in the event loop. It can be overridden to connect * signals connected to terminate() to signal termination of the command. */ void CliCommand::connectResultSignal() { QTimer::singleShot(0, this, SLOT(terminate())); } /** * Disconnect signals used to emit finished(). * This method is called from terminate(). The default implementation * does nothing. It can be overridden to disconnect signals connected * in connectResultSignal(). */ void CliCommand::disconnectResultSignal() { } /** * Called on timeout. * @param event timer event */ void CliCommand::timerEvent(QTimerEvent*) { setError(tr("Timeout")); terminate(); } /** * Get parameter for task mask. * @param nr index in args() * @param useDefault if true use cli()->tagMask() if no parameter found * @return tag versions. */ TrackData::TagVersion CliCommand::getTagMaskParameter(int nr, bool useDefault) const { int tagMask = 0; if (m_args.size() > nr) { const QString& tagStr = m_args.at(nr); if (!tagStr.isEmpty() && tagStr.at(0).isDigit()) { if (tagStr.contains(QLatin1Char('1'))) tagMask |= 1; if (tagStr.contains(QLatin1Char('2'))) tagMask |= 2; if (tagStr == QLatin1String("3")) tagMask = 3; } } if (tagMask == 0 && useDefault) { tagMask = m_processor->tagMask(); } return TrackData::tagVersionCast(tagMask); } /** * Show usage of command. */ void CliCommand::showUsage() { cli()->writeLine(tr("Usage:")); cli()->writeHelp(name()); setError(QLatin1String("_Usage")); } HelpCommand::HelpCommand(Kid3Cli* processor) : CliCommand(processor, QLatin1String("help"), tr("Help"), QLatin1String("[S]\nS = ") + tr("Command name")) { } void HelpCommand::startCommand() { cli()->writeHelp(args().size() > 1 ? args().at(1) : QString()); } QuitCommand::QuitCommand(Kid3Cli* processor) : CliCommand(processor, QLatin1String("exit"), tr("Quit application"), QLatin1String("[S]\nS = \"force\"")) { } void QuitCommand::startCommand() { cli()->app()->updateModified(); if (cli()->app()->isModified() && !cli()->app()->getDirName().isEmpty()) { if (!(args().size() > 1 && args().at(1) == QLatin1String("force"))) { cli()->writeLine(tr("The current directory has been modified.")); cli()->writeLine(tr("Type 'exit force' to quit.")); terminate(); return; } } disconnect(this, SIGNAL(finished()), cli(), SLOT(onCommandFinished())); cli()->terminate(); } void QuitCommand::connectResultSignal() { // Do not signal finished() to avoid printing prompt. } CdCommand::CdCommand(Kid3Cli* processor) : CliCommand(processor, QLatin1String("cd"), tr("Change directory"), QLatin1String("[P]")) { } void CdCommand::startCommand() { QStringList paths; if (args().size() > 1) { paths = args().mid(1); } else { paths.append(QDir::homePath()); } if (!cli()->openDirectory(cli()->expandWildcards(paths))) { setError(tr("%1 does not exist").arg(paths.join(QLatin1String(", ")))); terminate(); } } void CdCommand::connectResultSignal() { connect(cli()->app(), SIGNAL(directoryOpened(QPersistentModelIndex,QList<QPersistentModelIndex>)), this, SLOT(onDirectoryOpened(QPersistentModelIndex,QList<QPersistentModelIndex>))); } void CdCommand::disconnectResultSignal() { disconnect(cli()->app(), SIGNAL(directoryOpened(QPersistentModelIndex,QList<QPersistentModelIndex>)), this, SLOT(onDirectoryOpened(QPersistentModelIndex,QList<QPersistentModelIndex>))); } void CdCommand::onDirectoryOpened( const QPersistentModelIndex& dirIndex, const QList<QPersistentModelIndex>& fileIndexes) { Q_UNUSED(dirIndex) QItemSelectionModel* selModel = cli()->app()->getFileSelectionModel(); if (selModel && !fileIndexes.isEmpty()) { foreach (const QPersistentModelIndex& fileIndex, fileIndexes) { selModel->select(fileIndex, QItemSelectionModel::Select); } selModel->setCurrentIndex(fileIndexes.first(), QItemSelectionModel::NoUpdate); } terminate(); } PwdCommand::PwdCommand(Kid3Cli* processor) : CliCommand(processor, QLatin1String("pwd"), tr("Print the filename of the current working directory")) { } void PwdCommand::startCommand() { QString path = cli()->app()->getDirPath(); if (path.isNull()) { path = QDir::currentPath(); cli()->app()->openDirectory(QStringList() << path); } cli()->writeLine(path); } LsCommand::LsCommand(Kid3Cli* processor) : CliCommand(processor, QLatin1String("ls"), tr("Directory list")) { setTimeout(10000); } void LsCommand::startCommand() { cli()->writeFileList(); } SaveCommand::SaveCommand(Kid3Cli* processor) : CliCommand(processor, QLatin1String("save"), tr("Saves the changed files")) { } void SaveCommand::startCommand() { QStringList errorFiles = cli()->app()->saveDirectory(); if (errorFiles.isEmpty()) { cli()->updateSelection(); } else { setError(tr("Error while writing file:\n") + errorFiles.join(QLatin1String("\n"))); } } SelectCommand::SelectCommand(Kid3Cli* processor) : CliCommand(processor, QLatin1String("select"), tr("Select file"), QLatin1String("[P|S]\n" "S = \"all\" | \"none\" | \"first\" | \"previous\" | \"next\"")) { } void SelectCommand::startCommand() { if (args().size() > 1) { const QString& param = args().at(1); if (param == QLatin1String("all")) { cli()->app()->selectAllFiles(); } else if (param == QLatin1String("none")) { cli()->app()->deselectAllFiles(); } else if (param == QLatin1String("first")) { setResult(cli()->app()->firstFile(true) ? 0 : 1); } else if (param == QLatin1String("previous")) { setResult(cli()->app()->previousFile(true) ? 0 : 1); } else if (param == QLatin1String("next")) { setResult(cli()->app()->nextFile(true) ? 0 : 1); } else { QStringList paths = args().mid(1); if (!cli()->selectFile(cli()->expandWildcards(paths))) { setError(tr("%1 not found").arg(paths.join(QLatin1String(", ")))); } } } else { cli()->updateSelection(); } } TagCommand::TagCommand(Kid3Cli* processor) : CliCommand(processor, QLatin1String("tag"), tr("Select tag"), QLatin1String("[T]")) { } void TagCommand::startCommand() { TrackData::TagVersion tagMask = getTagMaskParameter(1, false); if (tagMask != TrackData::TagNone) { cli()->setTagMask(tagMask); } else { cli()->writeTagMask(); } } GetCommand::GetCommand(Kid3Cli* processor) : CliCommand(processor, QLatin1String("get"), tr("Get tag frame"), QLatin1String("[N|S] [T]\nS = \"all\"")) { } void GetCommand::startCommand() { int numArgs = args().size(); QString name = numArgs > 1 ? args().at(1) : QLatin1String("all"); TrackData::TagVersion tagMask = getTagMaskParameter(2); if (name == QLatin1String("all")) { cli()->writeFileInformation(tagMask); } else { foreach (TrackData::TagVersion tagBit, QList<TrackData::TagVersion>() << TrackData::TagV2 << TrackData::TagV1) { if (tagMask & tagBit) { QString value = cli()->app()->getFrame(tagBit, name); if (!value.isEmpty()) { cli()->writeLine(value); break; } } } } } SetCommand::SetCommand(Kid3Cli* processor) : CliCommand(processor, QLatin1String("set"), tr("Set tag frame"), QLatin1String("N V [T]")) { } void SetCommand::startCommand() { int numArgs = args().size(); if (numArgs > 2) { const QString& name = args().at(1); const QString& value = args().at(2); TrackData::TagVersion tagMask = getTagMaskParameter(3); if (cli()->app()->setFrame(tagMask, name, value)) { cli()->updateSelectedFiles(); cli()->updateSelection(); } else { setError(tr("Could not set \"%1\" for %2").arg(value).arg(name)); } } else { showUsage(); } } RevertCommand::RevertCommand(Kid3Cli* processor) : CliCommand(processor, QLatin1String("revert"), tr("Revert")) { } void RevertCommand::startCommand() { cli()->app()->revertFileModifications(); } ImportCommand::ImportCommand(Kid3Cli* processor) : CliCommand(processor, QLatin1String("import"), tr("Import from file or clipboard"), QLatin1String("P S [T]\nS = ") + tr("Format name")) { } void ImportCommand::startCommand() { int numArgs = args().size(); if (numArgs > 2) { const QString& path = args().at(1); const QString& fmtName = args().at(2); bool ok; int fmtIdx = fmtName.toInt(&ok); if (!ok) { fmtIdx = ImportConfig::instance().m_importFormatNames.indexOf(fmtName); if (fmtIdx == -1) { QString errMsg = tr("%1 not found.").arg(fmtName); errMsg += QLatin1Char('\n'); errMsg += tr("Available"); errMsg += QLatin1String(": "); errMsg += ImportConfig::instance().m_importFormatNames.join( QLatin1String(", ")); errMsg += QLatin1Char('.'); setError(errMsg); return; } } TrackData::TagVersion tagMask = getTagMaskParameter(3); if (!cli()->app()->importTags(tagMask, path, fmtIdx)) { setError(tr("Error")); } } else { showUsage(); } } BatchImportCommand::BatchImportCommand(Kid3Cli* processor) : CliCommand(processor, QLatin1String("autoimport"), tr("Automatic import"), QLatin1String("[S] [T]\nS = ") + tr("Profile name")) { setTimeout(60000); } void BatchImportCommand::startCommand() { int numArgs = args().size(); const QString& profileName = numArgs > 1 ? args().at(1) : QLatin1String("All"); TrackData::TagVersion tagMask = getTagMaskParameter(2); if (BatchImportConfig::instance().getProfileByName(profileName, m_profile)) { cli()->app()->batchImport(m_profile, tagMask); } else { QString errMsg = tr("%1 not found.").arg(profileName); errMsg += QLatin1Char('\n'); errMsg += tr("Available"); errMsg += QLatin1String(": "); errMsg += BatchImportConfig::instance().m_profileNames.join( QLatin1String(", ")); errMsg += QLatin1Char('.'); setError(errMsg); terminate(); } } void BatchImportCommand::connectResultSignal() { BatchImporter* importer = cli()->app()->getBatchImporter(); connect(importer, SIGNAL(reportImportEvent(BatchImportProfile::ImportEventType,QString)), this, SLOT(onReportImportEvent(BatchImportProfile::ImportEventType,QString))); connect(importer, SIGNAL(finished()), this, SLOT(terminate())); } void BatchImportCommand::disconnectResultSignal() { BatchImporter* importer = cli()->app()->getBatchImporter(); disconnect(importer, SIGNAL(reportImportEvent(BatchImportProfile::ImportEventType,QString)), this, SLOT(onReportImportEvent(BatchImportProfile::ImportEventType,QString))); disconnect(importer, SIGNAL(finished()), this, SLOT(terminate())); } void BatchImportCommand::onReportImportEvent( BatchImportProfile::ImportEventType type, const QString& text) { QString eventText; switch (type) { case BatchImportProfile::ReadingDirectory: eventText = tr("Reading Directory"); break; case BatchImportProfile::Started: eventText = tr("Started"); break; case BatchImportProfile::SourceSelected: eventText = tr("Source"); break; case BatchImportProfile::QueryingAlbumList: eventText = tr("Querying"); break; case BatchImportProfile::FetchingTrackList: case BatchImportProfile::FetchingCoverArt: eventText = tr("Fetching"); break; case BatchImportProfile::TrackListReceived: eventText = tr("Data received"); break; case BatchImportProfile::CoverArtReceived: eventText = tr("Cover"); break; case BatchImportProfile::Finished: eventText = tr("Finished"); break; case BatchImportProfile::Aborted: eventText = tr("Aborted"); break; case BatchImportProfile::Error: eventText = tr("Error"); } if (!text.isEmpty()) { eventText += QLatin1String(": "); eventText += text; } cli()->writeLine(eventText); } AlbumArtCommand::AlbumArtCommand(Kid3Cli* processor) : CliCommand(processor, QLatin1String("albumart"), tr("Download album cover artwork"), QLatin1String("U [S]\nS = \"all\"")) { setTimeout(10000); } void AlbumArtCommand::startCommand() { int numArgs = args().size(); if (numArgs > 1) { const QString& url = args().at(1); Kid3Application::DownloadImageDestination dest = numArgs > 2 && args().at(2) == QLatin1String("all") ? Kid3Application::ImageForAllFilesInDirectory : Kid3Application::ImageForSelectedFiles; cli()->app()->downloadImage(url, dest); } else { showUsage(); terminate(); } } void AlbumArtCommand::connectResultSignal() { DownloadClient* downloadClient = cli()->app()->getDownloadClient(); connect(downloadClient, SIGNAL(downloadFinished(QByteArray,QString,QString)), this, SLOT(onDownloadFinished(QByteArray,QString,QString))); } void AlbumArtCommand::disconnectResultSignal() { DownloadClient* downloadClient = cli()->app()->getDownloadClient(); disconnect(downloadClient, SIGNAL(downloadFinished(QByteArray,QString,QString)), this, SLOT(onDownloadFinished(QByteArray,QString,QString))); } void AlbumArtCommand::onDownloadFinished( const QByteArray& data, const QString& mimeType, const QString& url) { cli()->app()->imageDownloaded(data, mimeType, url); terminate(); } ExportCommand::ExportCommand(Kid3Cli* processor) : CliCommand(processor, QLatin1String("export"), tr("Export to file or clipboard"), QLatin1String("P S [T]\nS = ") + tr("Format name")) { } void ExportCommand::startCommand() { int numArgs = args().size(); if (numArgs > 2) { const QString& path = args().at(1); const QString& fmtName = args().at(2); bool ok; int fmtIdx = fmtName.toInt(&ok); if (!ok) { fmtIdx = ExportConfig::instance().m_exportFormatNames.indexOf(fmtName); if (fmtIdx == -1) { QString errMsg = tr("%1 not found.").arg(fmtName); errMsg += QLatin1Char('\n'); errMsg += tr("Available"); errMsg += QLatin1String(": "); errMsg += ExportConfig::instance().m_exportFormatNames.join( QLatin1String(", ")); errMsg += QLatin1Char('.'); setError(errMsg); return; } } TrackData::TagVersion tagMask = getTagMaskParameter(3); if (!cli()->app()->exportTags(tagMask, path, fmtIdx)) { setError(tr("Error")); } } else { showUsage(); } } PlaylistCommand::PlaylistCommand(Kid3Cli* processor) : CliCommand(processor, QLatin1String("playlist"), tr("Create playlist")) { } void PlaylistCommand::startCommand() { if (!cli()->app()->writePlaylist()) { setError(tr("Error")); } } FilenameFormatCommand::FilenameFormatCommand(Kid3Cli* processor) : CliCommand(processor, QLatin1String("filenameformat"), tr("Apply filename format")) { } void FilenameFormatCommand::startCommand() { cli()->app()->applyFilenameFormat(); } TagFormatCommand::TagFormatCommand(Kid3Cli* processor) : CliCommand(processor, QLatin1String("tagformat"), tr("Apply tag format")) { } void TagFormatCommand::startCommand() { cli()->app()->applyId3Format(); } TextEncodingCommand::TextEncodingCommand(Kid3Cli* processor) : CliCommand(processor, QLatin1String("textencoding"), tr("Apply text encoding")) { } void TextEncodingCommand::startCommand() { cli()->app()->applyTextEncoding(); } RenameDirectoryCommand::RenameDirectoryCommand(Kid3Cli* processor) : CliCommand(processor, QLatin1String("renamedir"), tr("Rename directory"), QLatin1String("[F] [S] [T]\nS = \"create\" | \"rename\" | \"dryrun\"")), m_dryRun(false) { } void RenameDirectoryCommand::startCommand() { TrackData::TagVersion tagMask = TrackData::TagNone; QString format; bool create = false; m_dryRun = false; for (int i = 1; i < args().size(); ++i) { bool ok = false; if (tagMask == TrackData::TagNone) { tagMask = getTagMaskParameter(i, false); ok = tagMask != TrackData::TagNone; } if (!ok) { const QString& param = args().at(i); if (param == QLatin1String("create")) { create = true; } else if (param == QLatin1String("rename")) { create = false; } else if (param == QLatin1String("dryrun")) { m_dryRun = true; } else if (format.isEmpty()) { format = param; } } } if (tagMask == TrackData::TagNone) { tagMask = cli()->tagMask(); } if (format.isEmpty()) { format = RenDirConfig::instance().m_dirFormatText; } if (!cli()->app()->renameDirectory(tagMask, format, create)) { terminate(); } } void RenameDirectoryCommand::connectResultSignal() { DirRenamer* renamer = cli()->app()->getDirRenamer(); connect(renamer, SIGNAL(actionScheduled(QStringList)), this, SLOT(onActionScheduled(QStringList))); connect(cli()->app(), SIGNAL(renameActionsScheduled()), this, SLOT(onRenameActionsScheduled())); } void RenameDirectoryCommand::disconnectResultSignal() { DirRenamer* renamer = cli()->app()->getDirRenamer(); disconnect(renamer, SIGNAL(actionScheduled(QStringList)), this, SLOT(onActionScheduled(QStringList))); disconnect(cli()->app(), SIGNAL(renameActionsScheduled()), this, SLOT(onRenameActionsScheduled())); } void RenameDirectoryCommand::onActionScheduled(const QStringList& actionStrs) { QString str = actionStrs.at(0); if (actionStrs.size() > 1) { str += QLatin1String(" "); str += actionStrs.at(1); } if (actionStrs.size() > 2) { str += QLatin1String("\n "); str += actionStrs.at(2); } cli()->writeLine(str); } void RenameDirectoryCommand::onRenameActionsScheduled() { if (!m_dryRun) { QString errMsg = cli()->app()->performRenameActions(); if (errMsg.isEmpty()) { cli()->app()->deselectAllFiles(); } else { setError(errMsg); } } terminate(); } NumberTracksCommand::NumberTracksCommand(Kid3Cli* processor) : CliCommand(processor, QLatin1String("numbertracks"), tr("Number tracks"), QLatin1String("[S] [T]\nS = ") + tr("Track number")) { } void NumberTracksCommand::startCommand() { int numArgs = args().size(); int firstTrackNr; bool ok = false; if (numArgs > 1) { firstTrackNr = args().at(1).toInt(&ok); } if (!ok) { firstTrackNr = 1; } TrackData::TagVersion tagMask = getTagMaskParameter(2); cli()->app()->numberTracks(firstTrackNr, 0, tagMask); } FilterCommand::FilterCommand(Kid3Cli* processor) : CliCommand(processor, QLatin1String("filter"), tr("Filter"), QLatin1String("F|S\nS = ") + tr("Filter name")) { setTimeout(60000); } void FilterCommand::startCommand() { if (args().size() > 1) { QString expression = args().at(1); int fltIdx = FilterConfig::instance().m_filterNames.indexOf(expression); if (fltIdx != -1) { expression = FilterConfig::instance().m_filterExpressions.at(fltIdx); } else if (!expression.isEmpty() && !expression.contains(QLatin1Char('%'))) { // Probably an invalid expression QString errMsg = tr("%1 not found.").arg(expression); errMsg += QLatin1Char('\n'); errMsg += tr("Available"); errMsg += QLatin1String(": "); errMsg += FilterConfig::instance().m_filterNames.join( QLatin1String(", ")); errMsg += QLatin1Char('.'); setError(errMsg); terminate(); return; } cli()->app()->applyFilter(expression); } else { showUsage(); terminate(); } } void FilterCommand::connectResultSignal() { connect(cli()->app(), SIGNAL(fileFiltered(FileFilter::FilterEventType,QString)), this, SLOT(onFileFiltered(FileFilter::FilterEventType,QString))); } void FilterCommand::disconnectResultSignal() { disconnect(cli()->app(), SIGNAL(fileFiltered(FileFilter::FilterEventType,QString)), this, SLOT(onFileFiltered(FileFilter::FilterEventType,QString))); } void FilterCommand::onFileFiltered(FileFilter::FilterEventType type, const QString& fileName) { QString eventText; bool finish = false; switch (type) { case FileFilter::Started: eventText = tr("Started"); break; case FileFilter::Directory: eventText = QLatin1String(" ") + fileName; break; case FileFilter::ParseError: eventText = QLatin1String("parse error"); break; case FileFilter::FilePassed: eventText = QLatin1String("+ ") + fileName; break; case FileFilter::FileFilteredOut: eventText = QLatin1String("- ") + fileName; break; case FileFilter::Finished: eventText = tr("Finished"); finish = true; break; case FileFilter::Aborted: eventText = tr("Aborted"); finish = true; break; } cli()->writeLine(eventText); if (finish) { terminate(); } } ToId3v24Command::ToId3v24Command(Kid3Cli* processor) : CliCommand(processor, QLatin1String("to24"), tr("Convert ID3v2.3 to ID3v2.4")) { } void ToId3v24Command::startCommand() { cli()->app()->convertToId3v24(); } ToId3v23Command::ToId3v23Command(Kid3Cli* processor) : CliCommand(processor, QLatin1String("to23"), tr("Convert ID3v2.4 to ID3v2.3")) { } void ToId3v23Command::startCommand() { cli()->app()->convertToId3v23(); } TagToFilenameCommand::TagToFilenameCommand(Kid3Cli* processor) : CliCommand(processor, QLatin1String("fromtag"), tr("Filename from tag"), QLatin1String("[F] [T]")) { } void TagToFilenameCommand::startCommand() { TrackData::TagVersion tagMask = TrackData::TagNone; QString format; for (int i = 1; i < qMin(args().size(), 3); ++i) { bool ok = false; if (tagMask == TrackData::TagNone) { tagMask = getTagMaskParameter(i, false); ok = tagMask != TrackData::TagNone; } if (!ok && format.isEmpty()) { format = args().at(i); } } if (tagMask == TrackData::TagNone) { tagMask = cli()->tagMask(); } if (!format.isEmpty()) { cli()->app()->setTagsToFilenameFormat(format); } if (cli()->app()->getTagsToFilenameFormat().isEmpty()) { cli()->app()->setTagsToFilenameFormat(FileConfig::instance().m_formatText); } cli()->app()->getFilenameFromTags(tagMask); } FilenameToTagCommand::FilenameToTagCommand(Kid3Cli* processor) : CliCommand(processor, QLatin1String("totag"), tr("Tag from filename"), QLatin1String("[F] [T]")) { } void FilenameToTagCommand::startCommand() { TrackData::TagVersion tagMask = TrackData::TagNone; QString format; for (int i = 1; i < qMin(args().size(), 3); ++i) { bool ok = false; if (tagMask == TrackData::TagNone) { tagMask = getTagMaskParameter(i, false); ok = tagMask != TrackData::TagNone; } if (!ok && format.isEmpty()) { format = args().at(i); } } if (tagMask == TrackData::TagNone) { tagMask = cli()->tagMask(); } if (!format.isEmpty()) { cli()->app()->setFilenameToTagsFormat(format); } if (cli()->app()->getFilenameToTagsFormat().isEmpty()) { cli()->app()->setFilenameToTagsFormat( FileConfig::instance().m_formatFromFilenameText); } cli()->app()->getTagsFromFilename(tagMask); } TagToOtherTagCommand::TagToOtherTagCommand(Kid3Cli* processor) : CliCommand(processor, QLatin1String("syncto"), tr("Tag to other tag"), QLatin1String("T")) { } void TagToOtherTagCommand::startCommand() { TrackData::TagVersion tagMask = getTagMaskParameter(1, false); if (tagMask != TrackData::TagNone) { cli()->app()->copyToOtherTag(tagMask); } else { showUsage(); } } CopyCommand::CopyCommand(Kid3Cli* processor) : CliCommand(processor, QLatin1String("copy"), tr("Copy"), QLatin1String("[T]")) { } void CopyCommand::startCommand() { TrackData::TagVersion tagMask = getTagMaskParameter(1); cli()->app()->copyTags(tagMask); } PasteCommand::PasteCommand(Kid3Cli* processor) : CliCommand(processor, QLatin1String("paste"), tr("Paste"), QLatin1String("[T]")) { } void PasteCommand::startCommand() { TrackData::TagVersion tagMask = getTagMaskParameter(1); cli()->app()->pasteTags(tagMask); } RemoveCommand::RemoveCommand(Kid3Cli* processor) : CliCommand(processor, QLatin1String("remove"), tr("Remove"), QLatin1String("[T]")) { } void RemoveCommand::startCommand() { TrackData::TagVersion tagMask = getTagMaskParameter(1); cli()->app()->removeTags(tagMask); } #if defined HAVE_PHONON || QT_VERSION >= 0x050000 PlayCommand::PlayCommand(Kid3Cli* processor) : CliCommand(processor, QLatin1String("play"), tr("Play"), QLatin1String("[S]\n" "S = \"pause\" | \"stop\" | \"previous\" | \"next\"")) { } void PlayCommand::startCommand() { AudioPlayer* player = 0; QString param; if (args().size() > 1) { param = args().at(1); player = cli()->app()->getAudioPlayer(); } if (param == QLatin1String("pause")) { player->playOrPause(); } else if (param == QLatin1String("stop")) { player->stop(); } else if (param == QLatin1String("previous")) { player->previous(); } else if (param == QLatin1String("next")) { player->next(); } else { cli()->app()->playAudio(); } } #endif ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/cli/clicommand.h�����������������������������������������������������������������0000664�0000000�0000000�00000031210�12246035113�0016735�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \file clicommand.h * Command line interface commands. * * \b Project: Kid3 * \author Urs Fleisch * \date 11 Aug 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #ifndef CLICOMMAND_H #define CLICOMMAND_H #include <QObject> #include "trackdata.h" #include "batchimportprofile.h" #include "filefilter.h" #include "config.h" class QModelIndex; class Kid3Cli; /** * Base class for command line interface command. */ class CliCommand : public QObject { Q_OBJECT public: /** * Destructor. */ virtual ~CliCommand(); /** * Reset state to defaults. */ virtual void clear(); /** * Execute command. */ virtual void execute(); /** * Get command name. * @return name with which command is invoked. */ QString name() const { return m_name; } /** * Get help text for command. * @return help text for command. */ QString help() const { return m_help; } /** * Get argument specification * @return argument specification for command. */ QString argumentSpecification() const { return m_argspec; } /** * Get description of error. * @return error message, empty if no error occurred. */ QString getErrorMessage() const { return m_errorMsg; } /** * Check if an error occurred. * @return true if an error exists. */ bool hasError() const { return !m_errorMsg.isEmpty(); } /** * Set error message. * @param errorMsg error message, null string to clear error */ void setError(const QString& errorMsg) { m_errorMsg = errorMsg; } /** * Get timeout for operation. * @return timeout in milliseconds. */ int getTimeout() const { return m_timeoutMs; } /** * Set timeout for operation. * @param msec timeout in milliseconds, -1 for no timeout */ void setTimeout(int msec) { m_timeoutMs = msec; } /** * Get processor handling commands. * @return processor. */ Kid3Cli* cli() const { return m_processor; } /** * Get list of arguments. * @return arguments, the first element is the command name. */ const QStringList& args() const { return m_args; } /** * Set list of arguments. * @param args arguments, the first element must be the command name */ void setArgs(const QStringList& args) { m_args = args; } /** * Get result code of command. * @return result code, default is 0 for OK. */ int result() const { return m_result; } /** * Set result code of command. * @param result result code */ void setResult(int result) { m_result = result; } signals: /** * Emitted when the command is finished. */ void finished(); protected slots: /** * Terminate command. * Must be invoked when command is finished, which is set up in * connectResultSignal(). */ void terminate(); protected: /** * Constructor. * @param processor command line processor * @param name name with which command is invoked * @param help help text for command * @param argspec argument specification */ CliCommand(Kid3Cli* processor, const QString& name, const QString& help, const QString& argspec = QString()); /** * Called on timeout. * @param event timer event */ void timerEvent(QTimerEvent* event); /** * Start specific command. * This method has to be implemented by the specific subclasses * to execute the specific command. It should invoke terminate() * when the command is finished. */ virtual void startCommand() = 0; /** * Connect signals used to emit finished(). * This method is called after startCommand(). The default implementation * invokes terminate() in the event loop. It can be overridden to connect * signals connected to terminate() to signal termination of the command. */ virtual void connectResultSignal(); /** * Stop command. * This method is called from terminate(). The default implementation * does nothing. It can be overridden to disconnect signals connected * in startCommand(). */ virtual void disconnectResultSignal(); /** * Get parameter for task mask. * @param nr index in args() * @param useDefault if true use cli()->tagMask() if no parameter found * @return tag versions. */ TrackData::TagVersion getTagMaskParameter(int nr, bool useDefault = true) const; /** * Show usage of command. */ void showUsage(); private: Kid3Cli* m_processor; const QString m_name; const QString m_help; const QString m_argspec; QStringList m_args; QString m_errorMsg; int m_timerId; int m_timeoutMs; int m_result; }; /** Display help. */ class HelpCommand : public CliCommand { Q_OBJECT public: /** Constructor. */ explicit HelpCommand(Kid3Cli* processor); protected: virtual void startCommand(); }; /** Quit application. */ class QuitCommand : public CliCommand { Q_OBJECT public: /** Constructor. */ explicit QuitCommand(Kid3Cli* processor); protected: virtual void startCommand(); virtual void connectResultSignal(); }; /** Change directory. */ class CdCommand : public CliCommand { Q_OBJECT public: /** Constructor. */ explicit CdCommand(Kid3Cli* processor); protected: virtual void startCommand(); virtual void connectResultSignal(); virtual void disconnectResultSignal(); private slots: void onDirectoryOpened(const QPersistentModelIndex& dirIndex, const QList<QPersistentModelIndex>& fileIndexes); }; /** Print current working directory. */ class PwdCommand : public CliCommand { Q_OBJECT public: /** Constructor. */ explicit PwdCommand(Kid3Cli* processor); protected: virtual void startCommand(); }; /** List directory. */ class LsCommand : public CliCommand { Q_OBJECT public: /** Constructor. */ explicit LsCommand(Kid3Cli* processor); protected: virtual void startCommand(); }; /** Save changes. */ class SaveCommand : public CliCommand { Q_OBJECT public: /** Constructor. */ explicit SaveCommand(Kid3Cli* processor); protected: virtual void startCommand(); }; /** Select file. */ class SelectCommand : public CliCommand { Q_OBJECT public: /** Constructor. */ explicit SelectCommand(Kid3Cli* processor); protected: virtual void startCommand(); }; /** Display or set tag mask. */ class TagCommand : public CliCommand { Q_OBJECT public: /** Constructor. */ explicit TagCommand(Kid3Cli* processor); protected: virtual void startCommand(); }; /** Get tag frame or file information. */ class GetCommand : public CliCommand { Q_OBJECT public: /** Constructor. */ explicit GetCommand(Kid3Cli* processor); protected: virtual void startCommand(); }; /** Set tag frame. */ class SetCommand : public CliCommand { Q_OBJECT public: /** Constructor. */ explicit SetCommand(Kid3Cli* processor); protected: virtual void startCommand(); }; /** Revert changes. */ class RevertCommand : public CliCommand { Q_OBJECT public: /** Constructor. */ explicit RevertCommand(Kid3Cli* processor); protected: virtual void startCommand(); }; /** Import from file. */ class ImportCommand : public CliCommand { Q_OBJECT public: /** Constructor. */ explicit ImportCommand(Kid3Cli* processor); protected: virtual void startCommand(); }; /** Automatic import from servers. */ class BatchImportCommand : public CliCommand { Q_OBJECT public: /** Constructor. */ explicit BatchImportCommand(Kid3Cli* processor); protected: virtual void startCommand(); virtual void connectResultSignal(); virtual void disconnectResultSignal(); private slots: void onReportImportEvent(BatchImportProfile::ImportEventType type, const QString& text); private: BatchImportProfile m_profile; }; /** Download album cover art. */ class AlbumArtCommand : public CliCommand { Q_OBJECT public: /** Constructor. */ explicit AlbumArtCommand(Kid3Cli* processor); protected: virtual void startCommand(); virtual void connectResultSignal(); virtual void disconnectResultSignal(); private slots: void onDownloadFinished(const QByteArray& data, const QString& mimeType, const QString& url); }; /** Export to file. */ class ExportCommand : public CliCommand { Q_OBJECT public: /** Constructor. */ explicit ExportCommand(Kid3Cli* processor); protected: virtual void startCommand(); }; /** Create playlist file. */ class PlaylistCommand : public CliCommand { Q_OBJECT public: /** Constructor. */ explicit PlaylistCommand(Kid3Cli* processor); protected: virtual void startCommand(); }; /** Apply file name format. */ class FilenameFormatCommand : public CliCommand { Q_OBJECT public: /** Constructor. */ explicit FilenameFormatCommand(Kid3Cli* processor); protected: virtual void startCommand(); }; /** Apply tag format. */ class TagFormatCommand : public CliCommand { Q_OBJECT public: /** Constructor. */ explicit TagFormatCommand(Kid3Cli* processor); protected: virtual void startCommand(); }; /** Apply text encoding. */ class TextEncodingCommand : public CliCommand { Q_OBJECT public: /** Constructor. */ explicit TextEncodingCommand(Kid3Cli* processor); protected: virtual void startCommand(); }; /** Rename directory. */ class RenameDirectoryCommand : public CliCommand { Q_OBJECT public: /** Constructor. */ explicit RenameDirectoryCommand(Kid3Cli* processor); protected: virtual void startCommand(); virtual void connectResultSignal(); virtual void disconnectResultSignal(); private slots: void onActionScheduled(const QStringList& actionStrs); void onRenameActionsScheduled(); private: bool m_dryRun; }; /** Number tracks. */ class NumberTracksCommand : public CliCommand { Q_OBJECT public: /** Constructor. */ explicit NumberTracksCommand(Kid3Cli* processor); protected: virtual void startCommand(); }; /** Filter files. */ class FilterCommand : public CliCommand { Q_OBJECT public: /** Constructor. */ explicit FilterCommand(Kid3Cli* processor); protected: virtual void startCommand(); virtual void connectResultSignal(); virtual void disconnectResultSignal(); private slots: void onFileFiltered(FileFilter::FilterEventType type, const QString& fileName); }; /** Convert ID3v2.3 to ID3v2.4. */ class ToId3v24Command : public CliCommand { Q_OBJECT public: /** Constructor. */ explicit ToId3v24Command(Kid3Cli* processor); protected: virtual void startCommand(); }; /** Convert ID3v2.4 to ID3v2.3. */ class ToId3v23Command : public CliCommand { Q_OBJECT public: /** Constructor. */ explicit ToId3v23Command(Kid3Cli* processor); protected: virtual void startCommand(); }; /** Set file name from tags. */ class TagToFilenameCommand : public CliCommand { Q_OBJECT public: /** Constructor. */ explicit TagToFilenameCommand(Kid3Cli* processor); protected: virtual void startCommand(); }; /** Set tags from file name. */ class FilenameToTagCommand : public CliCommand { Q_OBJECT public: /** Constructor. */ explicit FilenameToTagCommand(Kid3Cli* processor); protected: virtual void startCommand(); }; /** Copy between tag 1 and tag 2. */ class TagToOtherTagCommand : public CliCommand { Q_OBJECT public: /** Constructor. */ explicit TagToOtherTagCommand(Kid3Cli* processor); protected: virtual void startCommand(); }; /** Copy to clipboard. */ class CopyCommand : public CliCommand { Q_OBJECT public: /** Constructor. */ explicit CopyCommand(Kid3Cli* processor); protected: virtual void startCommand(); }; /** Paste from clipboard. */ class PasteCommand : public CliCommand { Q_OBJECT public: /** Constructor. */ explicit PasteCommand(Kid3Cli* processor); protected: virtual void startCommand(); }; /** Remove tags. */ class RemoveCommand : public CliCommand { Q_OBJECT public: /** Constructor. */ explicit RemoveCommand(Kid3Cli* processor); protected: virtual void startCommand(); }; #if defined HAVE_PHONON || QT_VERSION >= 0x050000 /** Play audio file. */ class PlayCommand : public CliCommand { Q_OBJECT public: /** Constructor. */ explicit PlayCommand(Kid3Cli* processor); protected: virtual void startCommand(); }; #endif #endif // CLICOMMAND_H ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/cli/cliconfig.h.cmake������������������������������������������������������������0000664�0000000�0000000�00000000301�12246035113�0017640�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cliconfig.h. Generated by cmake from cliconfig.h.cmake */ #ifndef CLICONFIG_H #define CLICONFIG_H /* Define if readline is available */ #cmakedefine HAVE_READLINE ${HAVE_READLINE} #endif �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/cli/kid3cli.cpp������������������������������������������������������������������0000664�0000000�0000000�00000057075�12246035113�0016525�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \file kid3cli.cpp * Command line interface for Kid3. * * \b Project: Kid3 * \author Urs Fleisch * \date 10 Aug 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "kid3cli.h" #include <QDir> #include <QCoreApplication> #include <QItemSelectionModel> #include <QTimer> #include "kid3application.h" #include "fileproxymodel.h" #include "frametablemodel.h" #include "coreplatformtools.h" #include "clicommand.h" #include "cliconfig.h" #ifdef HAVE_READLINE #include "readlinecompleter.h" class Kid3CliCompleter : public ReadlineCompleter { public: explicit Kid3CliCompleter(const QList<CliCommand*>& cmds); virtual ~Kid3CliCompleter(); virtual QList<QByteArray> getCommandList() const; virtual QList<QByteArray> getParameterList() const; virtual bool updateParameterList(const char* buffer); private: const QList<CliCommand*>& m_cmds; QList<QByteArray> m_commands; QList<QByteArray> m_parameters; }; Kid3CliCompleter::Kid3CliCompleter(const QList<CliCommand*>& cmds) : m_cmds(cmds) { foreach (const CliCommand* cmd, cmds) { m_commands.append(cmd->name().toLocal8Bit()); } } Kid3CliCompleter::~Kid3CliCompleter() { } QList<QByteArray> Kid3CliCompleter::getCommandList() const { return m_commands; } QList<QByteArray> Kid3CliCompleter::getParameterList() const { return m_parameters; } bool Kid3CliCompleter::updateParameterList(const char* buffer) { QString cmdName = QString::fromLocal8Bit(buffer); bool isFirstParameter = true; int cmdNameEndIdx = cmdName.indexOf(QLatin1Char(' ')); if (cmdNameEndIdx != -1) { isFirstParameter = cmdName.indexOf(QLatin1Char(' '), cmdNameEndIdx + 1) == -1; cmdName.truncate(cmdNameEndIdx); } QString argSpec; if (isFirstParameter) { foreach (const CliCommand* cmd, m_cmds) { if (cmdName == cmd->name()) { argSpec = cmd->argumentSpecification(); break; } } } m_parameters.clear(); if (!argSpec.isEmpty()) { QStringList argSpecs = argSpec.split(QLatin1Char('\n')); if (!argSpecs.isEmpty()) { QString argTypes = argSpecs.first(). remove(QLatin1Char('[')).remove(QLatin1Char(']')); if (!argTypes.isEmpty()) { char argType = argTypes.at(0).toLatin1(); switch (argType) { case 'P': // file path return false; case 'T': // tagnumbers m_parameters << "1" << "2" << "12"; break; case 'N': { // frame name static QList<QByteArray> frameNames; if (frameNames.isEmpty()) { for (int k = Frame::FT_FirstFrame; k <= Frame::FT_LastFrame; ++k) { frameNames.append( Frame::ExtendedType(static_cast<Frame::Type>(k), QLatin1String("")). getName().toLower().remove(QLatin1Char(' ')).toLocal8Bit()); } } m_parameters = frameNames; break; } case 'S': // specific command if (argSpecs.size() > 0) { const QString& valuesStr = argSpecs.at(1); int valuesIdx = valuesStr.indexOf(QLatin1String("S = \"")); if (valuesIdx != -1) { QStringList values = valuesStr.mid(valuesIdx + 4).split(QLatin1String(" | ")); foreach (const QString& value, values) { if (value.startsWith(QLatin1Char('"')) && value.endsWith(QLatin1Char('"'))) { m_parameters.append( value.mid(1, value.length() - 2).toLocal8Bit()); } } } } break; default: break; } } } } return true; } #endif // HAVE_READLINE /** @cond */ namespace { /** * Split string into command line arguments supporting quotes and escape * characters. * @param str command line string * @return list of arguments. */ QStringList splitArgs(const QString& str) { QStringList params; for (int pos = 0; ; ) { QChar c; do { if (pos >= str.size()) return params; c = str.at(pos++); } while (c.isSpace()); QString param = QLatin1String(""); if (c == QLatin1Char('~')) { if (pos >= str.size() || str.at(pos).isSpace()) { params.append(QDir::homePath()); continue; } if (str.at(pos) == QLatin1Char('/')) { param = QDir::homePath(); c = QLatin1Char('/'); ++pos; } } do { if (c == QLatin1Char('\'')) { int spos = pos; do { if (pos >= str.size()) return QStringList(); c = str.at(pos++); } while (c != QLatin1Char('\'')); param += str.midRef(spos, pos - spos - 1); } else if (c == QLatin1Char('"')) { for (;;) { if (pos >= str.size()) return QStringList(); c = str.at(pos++); if (c == QLatin1Char('"')) break; if (c == QLatin1Char('\\')) { if (pos >= str.size()) return QStringList(); c = str.at(pos++); if (c != QLatin1Char('"') && c != QLatin1Char('\\')) param += QLatin1Char('\\'); } param += c; } } else { if (c == QLatin1Char('\\')) { if (pos >= str.size()) return QStringList(); c = str.at(pos++); } param += c; } if (pos >= str.size()) break; c = str.at(pos++); } while (!c.isSpace()); params.append(param); } return QStringList(); } } /** @endcond */ /** * Constructor. * @param parent parent object */ Kid3Cli::Kid3Cli(QObject* parent) : AbstractCli("kid3-cli> ", parent), m_platformtools(new CorePlatformTools), m_app(new Kid3Application(m_platformtools, this)), m_tagMask(TrackData::TagV2V1), m_fileNameChanged(false) { m_cmds << new HelpCommand(this) << new QuitCommand(this) << new CdCommand(this) << new PwdCommand(this) << new LsCommand(this) << new SaveCommand(this) << new SelectCommand(this) << new TagCommand(this) << new GetCommand(this) << new SetCommand(this) << new RevertCommand(this) << new ImportCommand(this) << new BatchImportCommand(this) << new AlbumArtCommand(this) << new ExportCommand(this) << new PlaylistCommand(this) << new FilenameFormatCommand(this) << new TagFormatCommand(this) << new TextEncodingCommand(this) << new RenameDirectoryCommand(this) << new NumberTracksCommand(this) << new FilterCommand(this) << new ToId3v24Command(this) << new ToId3v23Command(this) << new TagToFilenameCommand(this) << new FilenameToTagCommand(this) << new TagToOtherTagCommand(this) << new CopyCommand(this) << new PasteCommand(this) << new RemoveCommand(this) << new PlayCommand(this); connect(m_app, SIGNAL(fileSelectionUpdateRequested()), this, SLOT(updateSelectedFiles())); connect(m_app, SIGNAL(selectedFilesUpdated()), this, SLOT(updateSelection())); #ifdef HAVE_READLINE m_completer = new Kid3CliCompleter(m_cmds); m_completer->install(); #endif } /** * Destructor. */ Kid3Cli::~Kid3Cli() { #ifdef HAVE_READLINE delete m_completer; #endif } /** * Get command for a command line. * @param line command line * @return command, 0 if no command found. */ CliCommand* Kid3Cli::commandForArgs(const QString& line) { if (line.isEmpty()) return 0; QStringList args = splitArgs(line); if (!args.isEmpty()) { const QString& name = args.at(0); for (QList<CliCommand*>::iterator it = m_cmds.begin(); it != m_cmds.end(); ++it) { CliCommand* cmd = *it; if (name == cmd->name()) { cmd->setArgs(args); return cmd; } } } return 0; } /** * Display help about available commands. * @param cmdName command name, for all commands if empty */ void Kid3Cli::writeHelp(const QString& cmdName) { if (cmdName.isEmpty()) { writeLine(tr("Parameter")); writeLine(QLatin1String(" P = ") + tr("File path")); writeLine(QLatin1String(" U = ") + tr("URL")); writeLine(QLatin1String(" T = ") + tr("Tag numbers") + QLatin1String(" \"1\" | \"2\" | \"12\"")); writeLine(QLatin1String(" N = ") + tr("Frame name") + QLatin1String(" \"album\" | \"album artist\" | \"arranger\" | " "\"artist\" | ...")); writeLine(QLatin1String(" V = ") + tr("Frame value")); writeLine(QLatin1String(" F = ") + tr("Format")); writeLine(QLatin1String(" S = ") + tr("Command specific")); writeLine(tr("Available Commands")); } QList<QStringList> cmdStrs; int maxLength = 0; for (QList<CliCommand*>::const_iterator it = m_cmds.constBegin(); it != m_cmds.constEnd(); ++it) { const CliCommand* cmd = *it; QString cmdStr = cmd->name(); if (cmdName.isEmpty() || cmdName == cmdStr) { QStringList spec = cmd->argumentSpecification().split(QLatin1Char('\n')); if (!spec.isEmpty()) { cmdStr += QLatin1Char(' '); cmdStr += spec.takeFirst(); } cmdStrs.append(QStringList() << cmdStr << cmd->help() << spec); maxLength = qMax(cmdStr.size(), maxLength); } } foreach (QStringList strs, cmdStrs) { QString cmdStr = strs.takeFirst(); cmdStr += QString(maxLength - cmdStr.size() + 2, QLatin1Char(' ')); cmdStr += strs.takeFirst(); writeLine(cmdStr); while (!strs.isEmpty()) { cmdStr = QString(maxLength + 2, QLatin1Char(' ')); cmdStr += strs.takeFirst(); writeLine(cmdStr); } } } /** * Execute process. */ void Kid3Cli::execute() { if (!parseOptions()) { // Interactive mode AbstractCli::execute(); } } /** * Open directory * @param paths directory or file paths * @return true if ok. */ bool Kid3Cli::openDirectory(const QStringList& paths) { if (m_app->openDirectory(paths, true)) { QDir::setCurrent(m_app->getDirPath()); m_app->getFileSelectionModel()->clearSelection(); return true; } return false; } /** * Expand wildcards in path list. * @param paths paths to expand * @return expanded paths. */ QStringList Kid3Cli::expandWildcards(const QStringList& paths) { QStringList expandedPaths; foreach (const QString& path, paths) { QStringList expandedPath; int wcIdx = path.indexOf(QRegExp(QLatin1String("[?*]"))); if (wcIdx != -1) { QString partBefore, partAfter; int beforeIdx = path.lastIndexOf(QDir::separator(), wcIdx); partBefore = path.left(beforeIdx + 1); int afterIdx = path.indexOf(QDir::separator(), wcIdx); if (afterIdx == -1) { afterIdx = path.length(); } partAfter = path.mid(afterIdx + 1); QString wildcardPart = path.mid(beforeIdx + 1, afterIdx - beforeIdx); if (!wildcardPart.isEmpty()) { QDir dir(partBefore); if (!dir.exists(wildcardPart)) { QStringList entries = dir.entryList(QStringList() << wildcardPart, QDir::AllEntries | QDir::NoDotAndDotDot); if (!entries.isEmpty()) { foreach (const QString& entry, entries) { expandedPath.append(partBefore + entry + partAfter); } } } } } if (expandedPath.isEmpty()) { expandedPaths.append(path); } else { expandedPaths.append(expandedPath); } } return expandedPaths; } /** * Select files in the current directory. * @param paths file names * @return true if files found and selected. */ bool Kid3Cli::selectFile(const QStringList& paths) { bool ok = true; FileProxyModel* model = m_app->getFileProxyModel(); foreach (const QString& fileName, paths) { QModelIndex index = model->index(fileName); if (index.isValid()) { m_app->getFileSelectionModel()->setCurrentIndex( index, QItemSelectionModel::Select); } else { ok = false; } } return ok; } /** * Get indexes of selected files. * @return selected indexes. */ QList<QPersistentModelIndex> Kid3Cli::getSelection() const { QList<QPersistentModelIndex> selection; if (QItemSelectionModel* selModel = m_app->getFileSelectionModel()) { foreach (const QModelIndex& index, selModel->selectedIndexes()) { selection.append(QPersistentModelIndex(index)); } } return selection; } /** * Update the currently selected files from the frame tables. */ void Kid3Cli::updateSelectedFiles() { int selectionSize = m_selection.size(); if (selectionSize > 0) { if (selectionSize > 1) { m_app->frameModelV1()->selectChangedFrames(); m_app->frameModelV2()->selectChangedFrames(); } m_app->frameModelsToTags(m_selection); if (m_app->selectionSingleFile() && !m_filename.isEmpty()) { if (TaggedFile* taggedFile = FileProxyModel::getTaggedFileOfIndex(m_selection.first())) { taggedFile->setFilename(m_filename); } } } } /** * Has to be called when the selection changes to update the frame tables * and the information about the selected files. */ void Kid3Cli::updateSelection() { m_selection = getSelection(); m_app->tagsToFrameModels(m_selection); if (m_app->selectionSingleFile()) { m_filename = m_app->selectionSingleFile()->getFilename(); m_app->selectionSingleFile()->getDetailInfo(m_detailInfo); m_tagFormatV1 = m_app->selectionSingleFile()->getTagFormatV1(); m_tagFormatV2 = m_app->selectionSingleFile()->getTagFormatV2(); m_fileNameChanged = m_app->selectionSingleFile()->isFilenameChanged(); } else { if (m_app->selectionFileCount() > 1) { m_filename.clear(); } m_detailInfo = TaggedFile::DetailInfo(); m_tagFormatV1.clear(); m_tagFormatV2.clear(); m_fileNameChanged = false; } if (m_app->selectionTagV1SupportedCount() == 0) { m_app->frameModelV1()->clearFrames(); } if (m_app->selectionFileCount() == 0) { m_app->frameModelV2()->clearFrames(); m_filename.clear(); } } /** * Display information about selected files. * @param tagMask tag bits (1 for tag 1, 2 for tag 2) */ void Kid3Cli::writeFileInformation(int tagMask) { if (m_detailInfo.valid) { writeLine(tr("File") + QLatin1String(": ") + m_detailInfo.toString()); } if (!m_filename.isEmpty()) { QString line = m_fileNameChanged ? QLatin1String("*") : QLatin1String(" "); line += QLatin1Char(' '); line += tr("Name"); line += QLatin1String(": "); line += m_filename; writeLine(line); } foreach (TrackData::TagVersion tagBit, QList<TrackData::TagVersion>() << TrackData::TagV1 << TrackData::TagV2) { if (tagMask & tagBit) { FrameTableModel* ft = (tagBit == TrackData::TagV2) ? m_app->frameModelV2() : m_app->frameModelV1(); int maxLength = 0; bool hasValue = false; for (int row = 0; row < ft->rowCount(); ++row) { QString name = ft->index(row, FrameTableModel::CI_Enable).data().toString(); QString value = ft->index(row, FrameTableModel::CI_Value).data().toString(); maxLength = qMax(name.size(), maxLength); hasValue = hasValue || !value.isEmpty(); } if (hasValue) { if (tagBit == TrackData::TagV2) { writeLine(tr("Tag 2") + QLatin1String(": ") + m_tagFormatV2); } else { writeLine(tr("Tag 1") + QLatin1String(": ") + m_tagFormatV1); } for (int row = 0; row < ft->rowCount(); ++row) { QString name = ft->index(row, FrameTableModel::CI_Enable).data().toString(); QString value = ft->index(row, FrameTableModel::CI_Value).data().toString(); if (!value.isEmpty()) { bool changed = ft->index(row, FrameTableModel::CI_Enable). data(Qt::BackgroundColorRole).value<QBrush>() != Qt::NoBrush; QString line = changed ? QLatin1String("*") : QLatin1String(" "); line += QLatin1Char(' '); line += name; line += QString(maxLength - name.size() + 2, QLatin1Char(' ')); line += value; writeLine(line); } } } } } } /** * Write currently active tag mask. */ void Kid3Cli::writeTagMask() { QString tagStr; switch (m_tagMask) { case TrackData::TagV1: tagStr = QLatin1String("1"); break; case TrackData::TagV2: tagStr = QLatin1String("2"); break; case TrackData::TagV2V1: tagStr = QLatin1String("1 & 2"); break; case TrackData::TagNone: tagStr = QLatin1String("-"); break; } writeLine(tr("Tags") + QLatin1String(": ") + tagStr); } /** * Set currently active tag mask. * * @param tagMask tag bits */ void Kid3Cli::setTagMask(TrackData::TagVersion tagMask) { m_tagMask = tagMask; } /** * List files. */ void Kid3Cli::writeFileList() { printFileProxyModel(m_app->getFileProxyModel(), m_app->getRootIndex(), 1); } /** * List files. * * @param model file proxy model * @param parent index of parent item * @param indent indentation as number of spaces */ void Kid3Cli::printFileProxyModel(const FileProxyModel* model, const QModelIndex& parent, int indent) { if (!model->hasChildren(parent)) return; QSet<QPersistentModelIndex> selection = getSelection().toSet(); for (int row = 0; row < model->rowCount(parent); ++row) { QString indentStr(indent, QLatin1Char(' ')), nameStr; QModelIndexList indexesWithChildren; QModelIndex idx(model->index(row, 0, parent)); QString propsStr = selection.contains(idx) ? QLatin1String(">") : QLatin1String(" "); if (TaggedFile* taggedFile = FileProxyModel::getTaggedFileOfIndex(idx)) { taggedFile->readTags(false); taggedFile = FileProxyModel::readWithId3V24IfId3V24(taggedFile); propsStr += (taggedFile->isChanged() ? QLatin1String("*") : QLatin1String(" ")) + (taggedFile->hasTagV1() ? QLatin1Char('1') : QLatin1Char('-')) + (taggedFile->hasTagV2() ? QLatin1Char('2') : QLatin1Char('-')); nameStr = taggedFile->getFilename(); } else { propsStr += QString(3, QLatin1Char(' ')); QVariant value(model->data(idx)); if (value.isValid()) { nameStr = value.toString(); } } if (model->hasChildren(idx)) { indexesWithChildren.append(idx); } writeLine(propsStr + indentStr + nameStr); foreach (const QModelIndex& idx, indexesWithChildren) { printFileProxyModel(model, idx, indent + 2); } } } /** * Process command line. * @param line command line */ void Kid3Cli::readLine(const QString& line) { flushStandardOutput(); CliCommand* cmd = commandForArgs(line); if (cmd) { connect(cmd, SIGNAL(finished()), this, SLOT(onCommandFinished())); cmd->execute(); } else { writeErrorLine(tr("Unknown command '%1'. Type 'help' for help.").arg(line)); promptNextLine(); } } /** * Called when a command is finished. */ void Kid3Cli::onCommandFinished() { if (CliCommand* cmd = qobject_cast<CliCommand*>(sender())) { disconnect(cmd, SIGNAL(finished()), this, SLOT(onCommandFinished())); if (cmd->hasError()) { QString msg(cmd->getErrorMessage()); if (!msg.startsWith(QLatin1Char('_'))) { writeErrorLine(msg); } } cmd->clear(); promptNextLine(); } } /** * Called when an argument command is finished. */ void Kid3Cli::onArgCommandFinished() { if (CliCommand* cmd = qobject_cast<CliCommand*>(sender())) { disconnect(cmd, SIGNAL(finished()), this, SLOT(onArgCommandFinished())); if (!cmd->hasError()) { cmd->clear(); executeNextArgCommand(); } else { QString msg(cmd->getErrorMessage()); if (!msg.startsWith(QLatin1Char('_'))) { writeErrorLine(msg); } cmd->clear(); setReturnCode(1); terminate(); } } } bool Kid3Cli::parseOptions() { QStringList args = QCoreApplication::arguments().mid(1); QStringList paths; bool isCommand = false; foreach (const QString& arg, args) { if (isCommand) { m_argCommands.append(arg); isCommand = false; } else if (arg == QLatin1String("-c")) { isCommand = true; } else if (arg == QLatin1String("-h") || arg == QLatin1String("--help")) { writeLine(QLatin1String("kid3-qt " VERSION " (c) " RELEASE_YEAR " Urs Fleisch")); writeLine(tr("Usage:") + QLatin1String( " kid3-cli [-c command1] [-c command2 ...] [path ...]")); writeHelp(); flushStandardOutput(); terminate(); return true; } else { paths.append(arg); } } if (paths.isEmpty()) { paths.append(QDir::currentPath()); } connect(m_app, SIGNAL(directoryOpened(QPersistentModelIndex,QList<QPersistentModelIndex>)), this, SLOT(onInitialDirectoryOpened(QPersistentModelIndex,QList<QPersistentModelIndex>))); if (!openDirectory(expandWildcards(paths))) { writeErrorLine(tr("%1 does not exist").arg(paths.join(QLatin1String(", ")))); } return !m_argCommands.isEmpty(); } /** * Select files passed as command line arguments after the initial directory has * been opened. Start execution of commands if existing. * @param dirIndex file proxy model index of opened directory * @param fileIndexes file proxy model indexes of selected files */ void Kid3Cli::onInitialDirectoryOpened( const QPersistentModelIndex& dirIndex, const QList<QPersistentModelIndex>& fileIndexes) { disconnect(m_app, SIGNAL(directoryOpened(QPersistentModelIndex,QList<QPersistentModelIndex>)), this, SLOT(onInitialDirectoryOpened(QPersistentModelIndex,QList<QPersistentModelIndex>))); QItemSelectionModel* selModel = m_app->getFileSelectionModel(); if (selModel && !fileIndexes.isEmpty()) { foreach (const QPersistentModelIndex& fileIndex, fileIndexes) { selModel->select(fileIndex, QItemSelectionModel::Select); } selModel->setCurrentIndex(fileIndexes.first(), QItemSelectionModel::NoUpdate); } if (!m_argCommands.isEmpty()) { if (!dirIndex.isValid()) { // Do not execute commands if directory could not be opened. m_argCommands.clear(); } executeNextArgCommand(); } } void Kid3Cli::executeNextArgCommand() { if (m_argCommands.isEmpty()) { m_app->updateModified(); if (m_app->isModified() && !m_app->getDirName().isEmpty()) { // Automatically save changes in command mode. QStringList errorFiles = m_app->saveDirectory(); if (!errorFiles.isEmpty()) { writeErrorLine(tr("Error while writing file:\n") + errorFiles.join(QLatin1String("\n"))); setReturnCode(1); } } terminate(); return; } QString line = m_argCommands.takeFirst(); CliCommand* cmd = commandForArgs(line); if (cmd) { connect(cmd, SIGNAL(finished()), this, SLOT(onArgCommandFinished())); cmd->execute(); } else { writeErrorLine(tr("Unknown command '%1', -h for help.").arg(line)); setReturnCode(1); terminate(); } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/cli/kid3cli.h��������������������������������������������������������������������0000664�0000000�0000000�00000011215�12246035113�0016154�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \file kid3cli.h * Command line interface for Kid3. * * \b Project: Kid3 * \author Urs Fleisch * \date 10 Aug 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #ifndef KID3CLI_H #define KID3CLI_H #include "abstractcli.h" #include "taggedfile.h" #include "trackdata.h" #include "cliconfig.h" class QTimer; class ICorePlatformTools; class Kid3Application; class FileProxyModel; class CliCommand; #ifdef HAVE_READLINE class Kid3CliCompleter; #endif /** * Command line interface for Kid3. */ class Kid3Cli : public AbstractCli { Q_OBJECT public: /** * Constructor. * @param parent parent object */ explicit Kid3Cli(QObject* parent = 0); /** * Destructor. */ virtual ~Kid3Cli(); /** * Execute process. */ virtual void execute(); /** * Access to application. * @return application. */ Kid3Application* app() const { return m_app; } /** * Open directory. * @param paths directory or file paths * @return true if ok. */ bool openDirectory(const QStringList& paths); /** * Select files in the current directory. * @param paths file names * @return true if files found and selected. */ bool selectFile(const QStringList &paths); /** * Get indexes of selected files. * @return selected indexes. */ QList<QPersistentModelIndex> getSelection() const; /** * Display help about available commands. * @param cmdName command name, for all commands if empty */ void writeHelp(const QString& cmdName = QString()); /** * Display information about selected files. * @param tagMask tag bits (1 for tag 1, 2 for tag 2) */ void writeFileInformation(int tagMask); /** * Write currently active tag mask. */ void writeTagMask(); /** * List files. */ void writeFileList(); /** * Get currently active tag mask. * @return tag bits. */ TrackData::TagVersion tagMask() const { return m_tagMask; } /** * Set currently active tag mask. * * @param tagMask tag bits */ void setTagMask(TrackData::TagVersion tagMask); /** * Expand wildcards in path list. * @param paths paths to expand * @return expanded paths. */ static QStringList expandWildcards(const QStringList& paths); public slots: /** * Update the currently selected files from the frame tables. */ void updateSelectedFiles(); /** * Has to be called when the selection changes to update the frame tables * and the information about the selected files. */ void updateSelection(); protected: /** * Process command line. * @param line command line */ virtual void readLine(const QString& line); private slots: /** * Select files passed as command line arguments after the initial directory has * been opened. Start execution of commands if existing. * @param dirIndex file proxy model index of opened directory * @param fileIndexes file proxy model indexes of selected files */ void onInitialDirectoryOpened(const QPersistentModelIndex& dirIndex, const QList<QPersistentModelIndex>& fileIndexes); /** * Called when a command is finished. */ void onCommandFinished(); /** * Called when an argument command is finished. */ void onArgCommandFinished(); private: /** * Get command for a command line. * @param line command line * @return command, 0 if no command found. */ CliCommand* commandForArgs(const QString& line); void printFileProxyModel(const FileProxyModel* model, const QModelIndex& parent, int indent); bool parseOptions(); void executeNextArgCommand(); ICorePlatformTools* m_platformtools; Kid3Application* m_app; #ifdef HAVE_READLINE Kid3CliCompleter* m_completer; #endif QList<CliCommand*> m_cmds; QStringList m_argCommands; TaggedFile::DetailInfo m_detailInfo; QString m_filename; QString m_tagFormatV1; QString m_tagFormatV2; TrackData::TagVersion m_tagMask; QList<QPersistentModelIndex> m_selection; bool m_fileNameChanged; }; #endif // KID3CLI_H �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/cli/maincli.cpp������������������������������������������������������������������0000664�0000000�0000000�00000003102�12246035113�0016575�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \file maincli.cpp * Main program for command line interface. * * \b Project: Kid3 * \author Urs Fleisch * \date 10 Aug 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <QApplication> #include <QLibraryInfo> #include <QDir> #include <QTimer> #include "kid3cli.h" #include "loadtranslation.h" /** * Main program for command line interface. * * @param argc number of arguments including command name * @param argv arguments, argv[0] is command name * * @return exit code of application. */ int main(int argc, char* argv[]) { QApplication app(argc, argv); app.setApplicationName(QLatin1String("Kid3")); Utils::loadTranslation(); #ifdef Q_OS_MAC QDir dir(QApplication::applicationDirPath()); dir.cdUp(); dir.cd(QLatin1String("PlugIns")); QApplication::setLibraryPaths(QStringList(dir.absolutePath())); #endif Kid3Cli kid3cli; QTimer::singleShot(0, &kid3cli, SLOT(execute())); return app.exec(); } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/cli/readlinecompleter.cpp��������������������������������������������������������0000664�0000000�0000000�00000007620�12246035113�0020670�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \file readlinecompleter.cpp * Abstract base class for readline completer. * * \b Project: Kid3 * \author Urs Fleisch * \date 22 Sep 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "readlinecompleter.h" #include <cstdio> #include <readline/readline.h> #include <cstdlib> /** Installed readline completer. */ ReadlineCompleter* ReadlineCompleter::s_completer = 0; /** * Destructor. */ ReadlineCompleter::~ReadlineCompleter() { } /** * Install this completer to be used with readline. */ void ReadlineCompleter::install() { s_completer = this; ::rl_attempted_completion_function = completion; #if RL_READLINE_VERSION > 0x0402 ::rl_completer_quote_characters = "\""; ::rl_filename_quote_characters = " '\"\\\t"; #else ::rl_completer_quote_characters = const_cast<char*>("\""); #endif } /** * Readline completion function. * @param text contents to complete * @param start start index in rl_line_buffer of word to complete * @param end end index in rl_line_buffer of word to complete * @return array of matches or 0 if there aren't any. */ char** ReadlineCompleter::completion(const char* text, int start, int end) { Q_UNUSED(end) char** matches = 0; if (start == 0) { matches = ::rl_completion_matches(text, commandGenerator); } else if (s_completer) { if (s_completer->updateParameterList(::rl_line_buffer)) { matches = ::rl_completion_matches(text, parameterGenerator); if (!matches) { ::rl_attempted_completion_over = 1; } } else { #if RL_READLINE_VERSION > 0x0402 ::rl_filename_quoting_desired = 1; #endif } } return matches; } /** * Readline command generator. * @param text partial word to be completed * @param state 0 for first call, >0 for subsequent calls * @return next completion string, allocated with malloc(), 0 if there are * no more possibilities left. */ char* ReadlineCompleter::commandGenerator(const char* text, int state) { if (s_completer) { return completionGenerator(s_completer->getCommandList(), text, state); } return 0; } /** * Readline parameter generator. * @param text partial word to be completed * @param state 0 for first call, >0 for subsequent calls * @return next completion string, allocated with malloc(), 0 if there are * no more possibilities left. */ char* ReadlineCompleter::parameterGenerator(const char* text, int state) { if (s_completer) { return completionGenerator(s_completer->getParameterList(), text, state); } return 0; } /** * Readline completion generator. * @param completions list of completions * @param text partial word to be completed * @param state 0 for first call, >0 for subsequent calls * @return next completion string, allocated with malloc(), 0 if there are * no more possibilities left. */ char* ReadlineCompleter::completionGenerator( const QList<QByteArray>& completions, const char* text, int state) { static int listIndex, textLen; if (state == 0) { listIndex = 0; textLen = qstrlen(text); } while (listIndex < completions.size()) { const QByteArray& name = completions.at(listIndex++); if (name.left(textLen) == text) { char* r = reinterpret_cast<char*>(::malloc(name.length() + 1)); qstrcpy(r, name.constData()); return r; } } return 0; } ����������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/cli/readlinecompleter.h����������������������������������������������������������0000664�0000000�0000000�00000004146�12246035113�0020335�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \file readlinecompleter.h * Abstract base class for readline completer. * * \b Project: Kid3 * \author Urs Fleisch * \date 22 Sep 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #ifndef READLINECOMPLETER_H #define READLINECOMPLETER_H #include <QList> #include <QByteArray> /** * Abstract base class for readline completer. */ class ReadlineCompleter { public: /** * Destructor. */ virtual ~ReadlineCompleter(); /** * Get list of available commands. * @return command list. */ virtual QList<QByteArray> getCommandList() const = 0; /** * Get list of available parameter values. * @return list of possible parameter values. */ virtual QList<QByteArray> getParameterList() const = 0; /** * Update the list of possible parameter values. * @param buffer buffer containing command name and partial parameters * @return true if list updated, false if file name completion shall be used. */ virtual bool updateParameterList(const char* buffer) = 0; /** * Install this completer to be used with readline. */ void install(); private: static char** completion(const char* text, int start, int end); static char* commandGenerator(const char* text, int state); static char* parameterGenerator(const char* text, int state); static char* completionGenerator( const QList<QByteArray>& completions, const char* text, int state); static ReadlineCompleter* s_completer; }; #endif // READLINECOMPLETER_H ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/cli/standardinputreader.cpp������������������������������������������������������0000664�0000000�0000000�00000005215�12246035113�0021233�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \file standardinputreader.cpp * Reader for lines from standard input. * * \b Project: Kid3 * \author Urs Fleisch * \date 10 Aug 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "standardinputreader.h" #include "cliconfig.h" #ifdef HAVE_READLINE #include <cstdio> #include <readline/readline.h> #include <readline/history.h> #if RL_READLINE_VERSION < 0x0600 #include <cstdlib> #endif #elif defined Q_OS_WIN32 #include <windows.h> #else #include <QTextStream> #endif /** * Constructor. * @param prompt command line prompt * @param parent parent object */ StandardInputReader::StandardInputReader(const char* prompt, QObject* parent) : QObject(parent), m_prompt(prompt) { } /** * Destructor. */ StandardInputReader::~StandardInputReader() { #ifdef HAVE_READLINE ::rl_cleanup_after_signal(); #endif } /** * Read the next line. */ void StandardInputReader::readLine() { #ifdef HAVE_READLINE char* lineRead = ::readline(m_prompt); if (lineRead && *lineRead) { ::add_history(lineRead); } QString line = QString::fromLocal8Bit(lineRead); #if RL_READLINE_VERSION >= 0x0600 ::rl_free(lineRead); #else ::free(lineRead); #endif #elif defined Q_OS_WIN32 WriteConsoleA(GetStdHandle(STD_OUTPUT_HANDLE), m_prompt, qstrlen(m_prompt), 0, 0); const int numCharsInBuf = 512; wchar_t buf[numCharsInBuf]; DWORD numCharsRead; QString line; do { ReadConsoleW(GetStdHandle(STD_INPUT_HANDLE), buf, numCharsInBuf, &numCharsRead, 0); line += QString::fromWCharArray(buf, numCharsRead); } while (numCharsRead > 0 && line[line.length() - 1] != QLatin1Char('\n')); while (line.length() > 0 && (line[line.length() - 1] == QLatin1Char('\r') || line[line.length() - 1] == QLatin1Char('\n'))) { line.truncate(line.length() - 1); } #else QTextStream stdOut(stdout, QIODevice::WriteOnly); stdOut << m_prompt; stdOut.flush(); QTextStream stdIn(stdin, QIODevice::ReadOnly); QString line = stdIn.readLine(); #endif emit lineReady(line); } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/cli/standardinputreader.h��������������������������������������������������������0000664�0000000�0000000�00000004044�12246035113�0020677�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \file standardinputreader.h * Reader for lines from standard input. * * \b Project: Kid3 * \author Urs Fleisch * \date 10 Aug 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #ifndef STANDARDINPUTREADER_H #define STANDARDINPUTREADER_H #include <QObject> /** * Reader for lines from standard input. * * An instance of this class can be used as a worker in a worker thread. * The blocking readLine() method can be called when the thread is * started and then after lines have been processed. Availability of a new * line is signaled with lineReady(). The controlling thread should only * communicate with the worker thread using queued signal-slot connections. */ class StandardInputReader : public QObject { Q_OBJECT public: /** * Constructor. * @param prompt command line prompt * @param parent parent object */ explicit StandardInputReader(const char* prompt = "", QObject* parent = 0); /** * Destructor. */ virtual ~StandardInputReader(); public slots: /** * Read the next line. * This method will block until a line is read from standard input. * When the line is ready, lineReady() is emitted. */ void readLine(); signals: /** * Emitted when a line from standard input is ready. * @param line line read from standard input */ void lineReady(const QString& line); private: const char* m_prompt; }; #endif // STANDARDINPUTREADER_H ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/hi16-app-kid3.png����������������������������������������������������������������0000664�0000000�0000000�00000001432�12246035113�0016575�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������‰PNG  ��� IHDR���������óÿa���bKGD�ÿ�ÿ�ÿ ½§“��ÏIDAT8¥S;LSa=÷ÑÒKo/}€m¥ X ¢(5 bÓЉDƒÁà#Ñè(ƒ:˜82¨ƒ‰“0Q Ñ8¸¨a±‚¬¥D E}Q T¹¥-}á@hˆ1ñLßÿåü'çûr>à?AìmÔ5ÛïªuåWišN/~þð¦÷oÔÅÞki<uËv²•5”⊊ämrU©ÉãžÜO€Þ)ÔG,­R½±Ç ÕeCkÈ1Å0kÎzêšÏݸvxLR-%V::^&vþ‘�Àqy²®áµ„åH [�He²Eˆ&·H«­å‘  }Y²6–do÷õu«v; „V»#f2Kó.x¼^|sB¥Rƒ‘ª¡Pé_Ú^ì|ð‘ÇO/TÅyI4ï øD[ÏF½¥ x*(ŒU> ¥L¼M )°œ‚Tê•gúû»JAÀÄp±+y‚· ¶mG3YÈ8 6qql~Q$MÃX^Ú93Œ,$m¬¥îØl– �  )a0KS—r¹-2FP(Ë&PmÐ û‰Tt’œÖë]FCM¥¼dÚÝ”ˆ„‰ôf¶xü‹÷-½:79^,âìéþ~Ci 5 tâuhUtŨªTî!&&—“3ßøûÕ8¯×5äs_ñ-l.L?h¬PhïÝl¨·˜ðÎEញ€¦H(K¶GT+Å¢£5²öV‹V/RÒ/x÷[$…Ò÷ʶ³Æ(—•é8,…ã0UÉÁŒ|È„ ˆäå&FøÕ!ÈÝ.—kÃó=ê€ç¯f1ìðC!cÖæ·SÃh3˜Zó­<pÌæ²c4ö`~q}(Š_¢Ž×– ÉaU‰;'—½á•Ät0ÄÌÎóÏFGGÃÀŽÉl6 Úí§V-–b³¾ ïžó¬ ;νÜ}ÑÔÔ$·Z­¢áþÎvÔˆ¨Îô����IEND®B`‚��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/hi32-app-kid3.png����������������������������������������������������������������0000664�0000000�0000000�00000003776�12246035113�0016610�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������‰PNG  ��� IHDR��� ��� ���szzô���bKGD�ÿ�ÿ�ÿ ½§“��³IDATX…å—ilT׆ß{gî}lÇöx{lcbâcl†¥à…’¨m´`(Y@Uh¥VJ©RU•©ð£*ªÔÄZ N„¥R ƒa2ØŽñ†—ÁïöسؾËÜ¥?Œ'v¼@©"Uê÷çê~ï9ïyÎw®Î9øâyiµæÄèÄÄ} %±T–e1.ôy}ã�1IÖ= ÖÙù‹´)¶yà9þaZêkC]žŸ÷yý7�йDCRæ²SìÇ;ßý5ÝÛÞ‚¼œldØÒà÷ûápª¬))|þ`‚°÷Ê7� ‹²Z«¶ïúÕXs[´ –$ŒQàI=:z‡ ó!Ânw8žx:¡€ïæ‹�³ä)cnîÝøT›:Ìs(.v‚¢¨ˆ8Êpˆ2ÅCÒZ J ó…¿Tã²^@9S2:{Ñ9.×1Ϧ”ÐPõ�«öü$¢I’ –��1æD4VU`˶TC]Ý•öà@�;Vf“$’'(y—L(9ܲmÛ_FgkÚÓ¼/®|K"Ö-Y~,„ü‚‚ˆÎpatõû#ïf‹w¯_„}‘=zñbuþîÝ+œ2Iü„¬A¬‚L$J Ï}÷×…sÕÍsV@oI.‘2sÞ: õÕÓÐé izJ‡° Nu ´K–}‹<ð»Ë«íK‘̪óöÛ‡MŽœ(ë—dR5S&èãëKçÃ%¯S� ”%ˆ¢�š¢ÐÜX‡£§N##-%«×N3™ŸWŒúÚj$¥Î§ßÚ~²�7¡=û=:Äû cËL�!eÌ˻ǯÛl9ž%p‹¿]¸ …1k׬ÅëW!ËòT…÷«j±re‰Z«5®ž¬…8ÃF8µsûÑOf0Úì–¯Ë$´ºˆ ’ 0c£`X# �¸VñïìØŠîö–iF´J‹ŒÌù„ÖdÚ:‘;qb[2!I‘À¦#Ç˦—n@¶¤l’RçM8s:Z1<4åÓý²«w+Š ÐÖT7Í(>)²$A­ÖÙ#“€b?dâ§„$§$ñ÷ÃÇwì˜�´Êøu!œ™ƒÖ¶&XÓç#ƨ…,ËÐjÔÐiT ¨éÛ‡!ÆŒ‘P�4M«à½÷6ÄtvŽlùøl[ÑéSÍ_4ÕܾÑuÀétš¦<€&­-iŠG_Àì……HJŒìƒQ¯EC«ÑQQÓ�(•¾>Déèèµ¥ËT? 4±~‘ß½-·ìðJ3sócõ+Ö(Z)ïšÜO�4¥Y(šã J�Ï<a û l¦X´=nĺÒWp¿²nw6oÞŒ�Ž…¯«-wÑ]à½Ø´Þ¦úñ–Üäí?xY—‘­øãá*ñs—·_§£é’¢dêú-oZ›Çû§ €‰ÓP­Í[Ò$æ¦Ê:#@*�…`Çíy„¯oRÅ¢Â"´{££¹Í5PQ"Šíf˜‘eIN=\ñÛÛ)úòÔ4ÃÎÊŠîƒ/§Çív×ô³ Í>§Ûín¾ÚرÚÊ,=Ë\Ì–WÄ„$h5 P¢7èGë—îmƗ盋UöìÝê€NKa®ðùXiAaò~B–šªú*£4êצ›zúFvøÕä D‚6²é„´Óaµ6/Ç¢W,HT¡tiŠìXâµ�€?¯ÏKøÎš dÚ¦qýóöÐÉZW0>Eïû`ßÒÌ$‹ž({÷ZûõÏ*l3L„Zm+*Ê~|öðzЦ¦?Üsq±0¬€wÊòQ7+D]“M­~¡0?^™šb��ìûàî »¦¿ÔårUÏzÖŸ5/Õ’’¨/LM1NUÑ XâµØû¦å[0Ï=ërÄ›µXe"õȧ£é4JMõ£¢Íã½4Û}��0ìç>üôžÁ¯ç—/IÆ=P«•ØûfÜ5}ÓúÊ"À ¾ Óx+¿w\s$A¼€œñ>0n·»6&zÙØÈhzÝW3Œ2ÐÐi)tõŽ Ù¢ÇºWm�€0 0Cã³~@–&™€ò©¥$‘ŸcV ù sV��DA:y»²[ˆÌL–ñÉåx»‚8sþq¤]=Ðå†ZÇ!d )@ĽX‹½ð9üëŽWnñøeY–™9+��þ {äâµÖÝß~5Í �ÇN×ãüÕV0Œ€Ú_¤]˜Òz@hM�m�A£&î]é V~ÑÃq¼8 Ò§ÿ‘Ûí®}&@UUU{\ìòßcŽ5ipë^'F€Z¥À†ÒôH»$ûøº“àéâ³[ýÜí{ÁžþQ^A7‡ü\¹J¥ºQQQ1å_â™��0Æ þóNçï¿ÿZ–Ú£?À¡x‘o¬Ï� 8<¬é“ï<èöÕ?’Q ñgFYþjuuõ“¹¼ŸëÏÈáp˜­‰úú£×ÄI’ŒÑ±0Z<Üuõ]Õ=,ʃ¢ Ÿ †/Øl¶‡åååâ³]ÿ��(]½ü’ÓaYÖìñ‡} ·|>æ Çq7ëêêFž×ç…œN§†eÙÅJ¥Òãr¹¼/:àÿ\üáô#a‘oœ£����IEND®B`‚��kid3-3.0.2/src/app/hi48-app-kid3.png����������������������������������������������������������������0000664�0000000�0000000�00000006740�12246035113�0016611�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������‰PNG  ��� IHDR���0���0���Wù‡���bKGD�ÿ�ÿ�ÿ ½§“�� •IDAThíXytTUžþî{¯^­©T¥–„$$HÈbÈR a Ðì-бAè°¨í´sZѵi}zìé£3ÝÃÑ# ›ˆ-"¨²) ’T6;D–°f«l•¥’Ô«·Üù#$!¦ ÐóÇ¿sêÔ{ï~ïþ~ßý}ïÝû.ð~Â=Üý Ïó æpÇÏYŽ“¥­ËÓ|ºÃã.P{¿¼îR€ÆaùË`¦ŠSñ£ �ÐÕå…(Š^Q÷6ÖT½ æþ¥# 1Ï2š­»Í!ÛŠgÿ–Ðpœ+9o›cSA)Åþ]›P{½ªµ«³c][cýžDâ½`GÈÔXìgâbƒŸ[ÿœúêSüýÌqŒ 1ÑQhk¸‚“G°|Å*,zd©æÔÉãR ¿ßוÿÉ�7®AyzTD”fõ‹Àþ6"99Ó¦?ÕGˆ‰‡Ͼ<Šb÷žýÜòe‹ÿȲ¬ÔÚTÿöýO`†Éc £¢vêõ†¨•k_@íõ*X-!˜6}Æ�’à— Ê ’'Îg Ãú×^ÅŽö°:ƒñMÁ4ó¾ga ÐYB_MÖEaa0YCqðã­Xøð£ƒxÞn¡ï8*6A66mÜ„ óT£ñS�!÷-󛸣�>xŽb¶¾®21ÙÓæA’$DG; Õjq»}þçãR'áÒåkðxZ±àá%Áf˨Üû—zîô D1!¶Oä%O©Tû·`ôØxœ=qi¦$w â k“ç=†7ÿð{|øñ^:°o¡ª]•*Šb�P ’»su #)‚LÈB ”HŒ¢¼È²²qåÊ wp» t‘c)þ“‘è‚ !jµW.VÂáð_�œJ…”I³±{×øÍ¿¾Â[ìy¹ÛrÏÝ–³0w{Î5(´TaÈç„à7 xŠ�O( ¹ Rνeûê©w+€ w|LS'ŘD(uWaÐA«7ÀÛîAppPÀ›DIx=69 û>ùæü|>T<ŸÞÐб[¡x‰�Ç(Å ä%AZoôšUydõªÜ ú�€ÒOî$  …t!ö×Äðèy˜:¿§ŸÖfè4Z¨x5(†¹Ù?…,Ëà8î¶@âÓ§à›“'ðÈc˱éý§^}yÖÂÕkr;Ñ×®ÚúŸ¹Ûr")!¥w0¨j½ñAÅú*YÝ×F¼íÐë `Y®ïáu×Õàµß¾ˆ—_z{?ÚI’†� !}ò6oŠåO¢­]J^³&·s(nîöœ‰ø5eÄŠ&–ÐéÒ§U`û‹C IBàPJ±eóû æ±˜5 ““±yãH²2d NÅC”dð-4m0€ø!É¿ ( ÷ç‘ÒFŒ9‰Ë ÄhÈÒëÑÝÕ E– мm­hlëð((©Dúø$D9bqãbÅmƒÅ$9QTxóZÄòÃÒ@œÍykQŠ$…¡‹Ö­Ü\9lZKØŸhÊÄhDÅ "C0ºü‚�µF‹ææ&(·hß´�/?·¥…'n,‘€£‡`ÖìÙPëtsÜþþ®µ¡„Qþ‚ ŒBçåýÒ4\zlZì’ˆ-Þ6xÛ[ƒúÚPÚï÷sU70z” N':Û=C3[Ãð}y âÁ©T ƒâHÒl�È…!×s·ålŽ€$Æa¸Õ÷:6Y!øE´6»1n¼µµÕ`h¿ß½Ý�€Lg*¼í­CG#„a¡Öê¡âX=ny æîÈ™B(ù@!tÉš•y³×¬Ê] j üzãÆÎÛ àµ†±Ô2ô¤F$ŽCsC=âHÇ¥ •àYÚ׬Ӫ�‹ížÆ¡�`Y Ã2�ԽשB–P |s´Z“‘‘ñ»ì)Yg÷í¹ïñØ·ïÚŽØØXu þ8�ÑËúŠÛ|àHá1¸XYЉ3BSS3&Oüï/CmAÈ͉Íín„!øöë5°E Ë1�4iii6†aæîÛséÑ–yGBœY˜è¥uŒ B“×E¦cM&Ó �[ „4ÖŠ|<`€ÜüïàœŽªC;ÑTW„ÔL„GD‚9[ª7C«QCQ9y™“f`ð‚¢,”¹Î"Ĩc“'” ~%ÜnÕù2SÂøŒ;Io#jžÕ*”âTy ((fMâ÷¬Ê $ wÄMĘ«ÊëFƒ`X ² „&ïOX¾êYŒKvbïæ·±bõ3øï¿þ4¦pÌœœ‚Êï˱jÝ?£èܵþ”¢­©žº+ho¨Bs}5ôz Æ'š1ÉgŠav=� «[Â;¹¥8‘_#q*†ùÅò8;ÚLL ~õÒ×T–•È’’’½‘¢´Ô=O ¾úˆNžÇÞšt¯ʰðEÅ¡(ÿ’3²á“(8ŽÇïׯÇñÃ_€Š>¬}úY¨5jèY *ËÑZ­îkýâl˜6ÝŠÌÔ7Ö †ìÖ ›Ký%Ê´™ŸÅÅÏ©­éôú¢ÌüþÍцÙõB»s€¿¨hï,I•¼(Û©§!& IDÿÏ/€†ØÑQxÇ8¤fMÇ‘Ï>DöôYˆŽ‰…" påÇ¡O·£4ÿ8щ)<þp,ž:‹æ9’d…Õ¢!ƒ“ohêÂ[ﺘììˆÅñ‰¦?2,4F£fÙ·ßÔ.ˆŠ0ê­-wþbKøêš÷Y¨¼Ö°„„Ø·ÒØ Ôq³=–" ¦¬�™áᘽp)Ž~²!F êkk¡ÓªžbÇ„TÒÇÛúl1\TœoÆ ëOI……EêÍÛr¶†T­y2÷õ©Ù Ÿ\–9%+¿|î0(¥qÅÅÅ—†pv]èè÷$Ž{ˆÚ#ÔˆˆF'ì]Í0øÚA@0Ά ©d¦†"nŒ) -†‹nŸ„ÇÖðwû¤%ÅÅÅ_�@VVV  Tnø™ê¸1&<óo_ûª®¶½ér¹Þ¸“€^„j-¡oËœj)o±©“ Ìq 3-iÉ6h5'¾‹—[ñú[gÀ 9ÁŠœ'’`·ê†-âëS×éŸßq)t·B‰‡eÈêù³£¹Y—ÎÀÞ—»«âZ~ÁYÇpô»gL´íg{¶,èa�øá¢ϽrÇ@’DGáÕç³<l¥oÄñÓÕŠÏ')3¸™Ù‘}ñ<­><¾îKªP:¡¸¸Ø _¨É¼ÔÛ)ÖW^ha“â-I qfXÌZ,˜ë@úx;¾ý®o¿[Œ7~;!¦Á›�–lCZ²A€o³Iƒ”¬þòs˸€ìÌÕÕÕu9S–‰™è ²r]"ŠË±ò±Dd¦…"Ì®Gî® Ìš6z¸¡ú v÷X„Ü’%¥à KÝ㪫kß@‡»±�eë±onH²B‡äülJ$Î]hAcsÏÏ™bGfZ(öªºcÿTºšæKÀ" º¼ýœi“"@˜œNç `„{£6›í2(y)1ÎÌEŒ2䘂Õ8™_ Ç ×j q!³ë¡Qv¬Ô x�Ïu å"ÐÙø½@ïjR€×jcϹJÅàâ•V¹¾¾‹ÔÔÖîQÊËË;–ì?zêºt;ÞŒìÑ8~ºz 0cÏb’*@·h©j ê" å2àóô$ûcð@oxmÎôÑ*,@F²¹ �Eeû·ßÕ.jõàºÊܨ®íÀ…*j꽈ë¯TGÐr¥tžÑÖšmÀÿh>ô´úp£Æ  ##ƒ±��‡Šî‚¢:ÕŒìÈ ÙT‚ƒG¯ö¬ »ë~÷BVð«“gU=ÉjÍ€Ö0ªþ6QTP~®®òœ)ªë®®õjU*¶AågŠ‹‹Å p¹\bVV懇O^Ï™‘É÷©:q GN^‡B) „ ØÈ¸Wk:›�kèB�¹ç¸wF¢”¢êj\e 8[â*~hæ@ 2ßúEås�‡ Μý¡·Ï»©�d™ît•¹×uxEz†kïKðûû‡—W1˜1e`…¬ €±àx€½E›§Õ‡¢²•º¥ÂR·ìíôó<ÏV‚ü9!äpwww~EEÅÀã{àr¹NOž”Õpª :lÁÜ�@k[ÿÖ:¯b‘>ÞŽ”¤O!€Ú�‚ŒÒ²&•¸é™âz_uWËólƒ"Ñ¢,ÅqÜÑüü³ÍÃÉå®� ~QÞzðصçÌQÀ‹ãñN^9˜73 Ïæ¤ö“oÚ¢¸¼ß× çÎ{8ˆ,CNÝ´ÅÑ‚‚~[Œw½|ÌÈÈ |ç»"ÔÖ³`kmÀ«Xè´š[ºQTÖ�W¹[*,q+ÞN‘SóÌ9Ÿ ï§”öûýg†²Åÿ‰��Èž’uþKÆ=±8‚ ÷¼-JÝôLI½¯¦®S«áÙzQV¾”ezˆçù¯ Zî5áãž8Î^ÅntDIW®uð7ß'D‰�p¤¨¨èü}ÊsHÜ“��ÈÌÌœO)MR¥0888ÿĉ·¥Âÿ7ü/E±až]X����IEND®B`‚��������������������������������kid3-3.0.2/src/app/hisc-app-kid3.svgz���������������������������������������������������������������0000664�0000000�0000000�00000010074�12246035113�0017163�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������‹m…ùD�hisc-app-kid3.svg�í[koÛÆýÞ_Á+Ip%jßÅv‘› RÜ¢ô3-Ò¶YH*Žóëï™%EQe˵Ó"·(¹ï93sf—=ýöÓÍ<ú˜å,_œ xÌQ¶˜æélqu6øåçw#7ˆÊ*Y¤É<_dgƒE>øöü›ÓFÑ›"Kª,ngÕuôÝâC9M–Yô⺪–“ñøöö6ž5…q^\_F£z–¯¾‰¢Ó.ÊI:=4í—«bÚ¥Óq6Ïn²EUŽyÌǃMóé¦ùmvY™¯Ši=ütÚmY¤—›¦XÉ­ ¸÷~ÌÄXˆZŒÊ»E•|-Ê“NW,°¯«`ŒQ·iy\«É§9Äpp1¡¶;;D¿Ä¿m‡V†õ^/Ñ3‹Y5~ûóÛñºõˆÅi•v†Y÷ÚšwK‹ä&+—É4+Û)Bÿëlvu] ‹Yý ųôl€}ñú¥é0iÃb%CÕzE“4Ÿ^$%0¾Îo²ñª(ÇË"ÿ-›B­f©Ϧù¢ÜëCË:Pƒx-ƶº3›¡j ¸Tp;K«ë½ÅB—ðpGçx:½Éª$Mª„JëÝ­K¸t2´A+€dòãÛwõÞ§Óɯyñ¡yÅ$ù œ·Å§étRͪyvþCRTQ~U×YônžTÑwØuô&ŸÏ!¬<zñ+,èõê*:r‚”€"õòtÜŽ²5pš•Ób¶ ½ÇÛUåꂼ)l¶ðŸäª[Ö”Îgç×Iy}:n^úZtgèvL–ËùlšÐ"îí>Ío–«*+îo°pHð&ëkR—mm1ˆnO$¡åêb>+¯³bk¨õõÎÖÄ]ÝvŒ(_Â?ÎgK(–ìh°³ÚVgo“Åâ.z E/ú4I‹\O»³ðž5Ò°Sr·yÿŸ{³Ñ ¹†òZÁîdû8» ‡G¼IªóÙMr•‘ëý7<` ©Ø¶Ê»eÖ])éxFzCQ:½™Q§ñOÕl>ÿŽ&l­�«‡%d‹òáqûbÖ¤ìéÛü&™-»[›'‹«f<o„×¾¯ýѸqH]õ~w5=8~h!Ûû[f„Pþ®ýý˜Áë§«àìö÷”ßÎʪ˜]¬žଘ}„gû˜‘dËÎØAÜx›�1îDˆÓñ:„„·6lQHK?βÛz˜‹¼H³bšÏóâlpbÂoЭÊ•¹MP«ãEÓõë:þNÙ¬Ž9ג˽J !X¬­UÆïV.£v"¶™¨[_^'i~‹Av+og TŒZªàôM8æL£ ÍûUw=UŸóüæl ce¥0Æ5մص@/ï©(¯óÛ«Õ !ólP«µüÊE²¬òžŠìVE—4š'wYÑ0Ÿ§ivYn´Bo-]�—Ë’â¿E’Î:Ñ„šm×pËXkc§e•/7H D %hÛâüò²Ìª ª±©eu7Ïê^£ †ÉIÝæU(jÔ<a±ñž;ãÝ«.¨N/z¦ç¿cÚÍl§ãm9Ü+¶«æý—,ùl°*³â'â«ÿ[üÒ8x¼E!Ÿ ÐÙXrí7Û¸ã(*Ž©N[*å”R;7Øýç"Y”I@8œO/Xì¹7Úþ`¢ÚYf†ZÆš9¡†#ic­¸·/÷@n@)ÀäºÈ²œô�¥¦gMÏÉü6¹Ûx¨ãñÇÍÃøã¦`[ûü¬qó;°+?8Õ£€Ö+ŸãÔCr|Hú‡´öhõï-Ó8'ö�{¿¹<'¬û 瀑õä–ñþ™‚ôúÙ9¢ìÔ0c‚$ñbµÄ‹ˆV¨Ž-4çÿ‰’ôÚñž$A¾æÃÓ2©®ë¦Øû÷×±ãNY5ä&ž o¢7‘±Þy5"VÊKá"éc%”0vÈel„p\GÊĆ9#Ýã(D\¡Ñ6g½dÌ9-Ω"Åca¹±Ø½Ž½tÎEJÅR3ãÅPøØHiÑUYLâ-ºJ 8-¨H¥§"fÎö•uˆ&mÑ©µli,èvþ!Moî>œ ^´<$êrõ_«¦qà—H¢&‹|‘½¢§5±º.)Vól’}Ìyš¾ª§œœø4A4j^kR91ãF Hr]NH¬'E¾Z¤ÝÂßòÙb»4MÀm‹"¹Û ã†Ñæ¹Û®G¶.kÃÍWÑ]ymÖ�«z\¬˜9oàܼ(PtI*ã>–†KñrM'7XëØlöi™Õè.]ÎÀÌm¼ÃwÛ}:Ðn“äiF . tÚþ¶,œ–÷…ù#@�žGÜú^yޏе–Zh”[7JF#;!Ð\GØ¥sÖ¯# Hæ°wŒ3š ²2{ ûà0qØ…M9�wã”Shìbî•`³‰Ø"h�½$rÙ,Â[¯¥¢bϘÔ*¢åÈÙ:*ÄR4àtÍuš¬ÈzEÆö“¡ê…Z7ŠÆUƧ´†Áàa˜÷(„ízÉ•¢B&0+Ê`ÙT;X;Ó°fúC"N¡ÉÐÇØ·±Z‡Btp†«æ’»0¦ÖŒ)1T1gÁØÓ²°pæ%uÉ`Ýàó4.$‚1Ž8 ?¤57 #­{ø<T¡%T�¡Ãmáó‚â3Ò†½‘dV3^+Ô*f°!c.½·µš¥Á„¶_÷Ÿ£ ¸:Æ~¢.qÈ{¬»M:ÖMÜkÄs¿!#ËÏŠù ÿ™¨}ãŽfÇl­ô· “]CÛZ;R½í«ø#·C‡|¸óîø­ìšš‡ù@©¤!2‚²{†h'%4ÍzÓ€þ›KÂ.gÚˆ{2Ê!ˆL”jtÀLâ"g5x §jðr Î6àÕF)�^B¤5"´up_�(f¾µÉ,†z¨—2,·oØ´ÁGò‡üÔgø¨~õ­ÛS[›bÚJŸ^¸T<M³ "œ+åÞ«Ãv[Bïo‹Np8ƒ·!·ÐS}w6°\B ûÕzƒ€xHÕSK„%¶!ÅIñð>>jG^Gë¡¢¶WÄ"Ž¿Hßyª×¾oüÏíöC#ù|I˜:8S%½ÈFÏ%’{z¦Ì:ŽBiàêàõ×ãIP°Ú³${$,†{°‡ûSÇaJüé=Œ(&ü! ÔA =¦A qžA\ý ¨¤2J÷a½[õî Drµ€îÚá¡õv¬ *Öj· rðˆeÃ#;ôÍð�N h¨ŸpI·p± HŒƒä_Ôù$ÆE öŠª”Xðß`¾ªýT{W·õ‘ñnF¢Œg ‡•Ü¥ mYV»ÙÈŪªöðû ÅvÔµrpŠÂ%˜²\“ÈÞrâ tЃwÙ÷ˆé>”:–àS ¼ éAø¬XAˆ” D˜¤X°ŽhÄAÒÃT+÷’˜€FÆX3"Ð71 °Pxmk""¤JÃw#eè刚Ž81‘vñ=2؆Ÿƒ¿#%.½HâÌh"aˆÍ€{Ȫ=Îx"-˜eÈ#ÀôšNF)41µ5à@ØAM‚aQLVç¶s^p89zšA’™éÓœ$§‘â! 5û1F÷íu6RÆÅy_"w`¢äª©¶=4†œ �¦ÀùvÒHðG¨DÔŽ×Ñ­{­=°+4È’×¾oüÇOçÞÙÆÙXÔ0$UÈà˜­“14°O 2ϸ žèÒ�¾*üOWøþAØãy§ç– !Í‘ÄÓpc÷ âÅŸð59ù2ÉÉCä$|yÞ¡k½ iºe‚‚Þ„Xî5Cràâ@¼BÖN:èâ±6ZrPÓ©žÓ–"9GHWDÀ:”áðŒNÀ“ˆÏ.æ$+Ø‚ºbÚñKè‰Ú^ôßí ý·?X 6Ll®qÑp.ˆI:M¾ ä(p>¦§P8$ËÖ8£ÂAž÷’ÈÝø H×פ l)Ò{± m.´ˆ¥Âdð¨`¼=²çZõø„Ç UÌ_œìß!½|‚6@9õs*dÓÒ°£32c¥4õá2 Ðh¨-€»>ŠA3J2°X ìséñ1›áŒßy %Â!™Õ°Gæà„¹jNÙ¤aÞÓš—f&#Êf`DÜ Á tªMoÙûþÅö)Ú¿_yåq¶Ó¥Ü ñWK÷œú~i#¢<é)Ákx*tcék( àt^Ь„:åRhº‘¨ÏïéJMJfÈI + Ú•ÃyÅx0âÊZ:F•4qc4$Æ÷®¡/ñ@\yÖ¯ŠONº?/”€œä\“ ¤ô†‡ü‘ò;…�!(ý“V…ôFBÇÉHbfá¾p@à¯!}ÓÈójMJè#éÁ{.,XPP›5Üi ×…€L9dß"z=¨W§ã«¾Ë`Ͱ(GWyØ cô¹L±’9ËJC<B3òÆäZ••š ‘–ã/±;ºÊ¢[\dÍ‚ZÁPÁ͆�žP^ ¥}ѱ? ZSRÍ5A“’nä©ÐIa´&îÌ- ‡š.§AslD!¹¹WTÆYµÅˆšŽ ´EÈ�}Ö<© …@2hJ.1·¦‹!ÇÀ´èÒ RUŠšy½ˆ!ö¡,§1gŠN0 ¹GÚ5(9z »)xI!HjÎ9­BÚ¯-q÷^I~޾§‹k"ë+<ÁÂq€ò bPï”b&2jÉ :È—2.ÂÈÒß´¥œTÁŒ±Æ¶#’* 6¡\wê¶ Skb'Øs·%«$E6šnYgê¦Ô¶k퀺³—¾]·�ÞÜØ¯ÑûnìO²,ÍÜ#.ÂdøíÞéiލ¬?ör¾ïþ®½vß1>úôÁqˆ&|Óà4ÝdA¹šÎŸ¹eÀŸ>‡ »-)kGmÁý}OÑžhÕ®h·ÜÂ×/%ŽüRbGe’¾`Ѱî1¡JIC©=ý¿DЕ,ˆœ°ôeK{l Ãé lß7Áç]åJ·«?J{ 0‚#¯Ù«DÞÚŽ¶[NEÖëÙ«¤Œw½ÄgÆÌ¡ó†íã¥Ú(ŸdÄ”[ÐO‹cÍõð×0 ¬Î Šààcv�7\“RSjC4–QÔ"äƒl1ñò�šè¤æ„¸àÄÓ—ôµƒç`ä ¥¦üs�þïb݇¼ªótîýë>$BÐ ÏU±!³ _€J9C}XJ§nh©¸BþCÄD ¯4)!‚OðŽTÅ,ròÀYšyUІNIˈŽ™D7B¶ä”§oæÑ1› _Ï1X øŸ’ŠÂ¡ªÓšFÂDZÀ§Î‚¦÷.¼'Bò{ý%U½e{Gë–(¹“Î>¨G¤†=•!S`Š""ªBîId’>cOfzWBæ~ ý…Laš%àù»¾ aœ!ùUO t‡YÇ)ýÿeçßü·?eÎ5=����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/kde/�����������������������������������������������������������������������������0000775�0000000�0000000�00000000000�12246035113�0014455�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/kde/CMakeLists.txt���������������������������������������������������������������0000664�0000000�0000000�00000000764�12246035113�0017224�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������set(kde_SRCS mainkde.cpp kdeplatformtools.cpp kdemainwindow.cpp kdeconfigdialog.cpp kdesettings.cpp ) add_definitions(${KDE4_DEFINITIONS} -DKDE_NO_COMPAT) include_directories(${KDE4_INCLUDES}) kde4_add_executable(kid3 ${kde_SRCS}) target_link_libraries(kid3 kid3-gui ${KDE4_KDEUI_LIBS} ${KDE4_KIO_LIBS}) install(TARGETS kid3 DESTINATION ${BIN_INSTALL_DIR}) install(FILES ../kid3.desktop DESTINATION ${XDG_APPS_INSTALL_DIR}) install(FILES kid3ui.rc DESTINATION ${DATA_INSTALL_DIR}/kid3) ������������kid3-3.0.2/src/app/kde/kdeconfigdialog.cpp����������������������������������������������������������0000664�0000000�0000000�00000004564�12246035113�0020303�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \file kdeconfigdialog.cpp * KDE configuration dialog. * * \b Project: Kid3 * \author Urs Fleisch * \date 17 Sep 2003 * * Copyright (C) 2003-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "kdeconfigdialog.h" #include "contexthelp.h" #include "configdialogpages.h" /** * Constructor. * * @param parent parent widget * @param caption dialog title * @param configSkeleton configuration skeleton */ KdeConfigDialog::KdeConfigDialog(QWidget* parent, QString& caption, KConfigSkeleton* configSkeleton) : KConfigDialog(parent, QLatin1String("configure"), configSkeleton), m_pages(new ConfigDialogPages(this)) { setObjectName(QLatin1String("ConfigDialog")); setWindowTitle(caption); setSizeGripEnabled(true); addPage(m_pages->createTagsPage(), tr("Tags"), QLatin1String("applications-multimedia")); addPage(m_pages->createFilesPage(), tr("Files"), QLatin1String("document-save")); addPage(m_pages->createActionsPage(), tr("User Actions"), QLatin1String("preferences-other")); addPage(m_pages->createNetworkPage(), tr("Network"), QLatin1String("preferences-system-network")); addPage(m_pages->createPluginsPage(), tr("Plugins"), QLatin1String("preferences-plugin")); setButtons(Ok | Cancel | Help); setHelp(QLatin1String("configure-kid3")); } /** * Destructor. */ KdeConfigDialog::~KdeConfigDialog() {} /** * Set values in dialog from current configuration. */ void KdeConfigDialog::setConfig() { m_pages->setConfig(); } /** * Get values from dialog and store them in the current configuration. */ void KdeConfigDialog::getConfig() const { m_pages->getConfig(); } /** * Show help. */ void KdeConfigDialog::slotHelp() { ContextHelp::displayHelp(QLatin1String("configure-kid3")); } ��������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/kde/kdeconfigdialog.h������������������������������������������������������������0000664�0000000�0000000�00000003302�12246035113�0017735�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \file kdeconfigdialog.h * KDE configuration dialog. * * \b Project: Kid3 * \author Urs Fleisch * \date 17 Sep 2003 * * Copyright (C) 2003-2012 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #ifndef KDECONFIGDIALOG_H #define KDECONFIGDIALOG_H #include <kconfigdialog.h> class QString; class QWidget; class QComboBox; class ConfigDialogPages; /** * KDE configuration dialog. */ class KdeConfigDialog : public KConfigDialog { Q_OBJECT public: /** * Constructor. * * @param parent parent widget * @param caption dialog title * @param configSkeleton configuration skeleton */ KdeConfigDialog(QWidget* parent, QString& caption, KConfigSkeleton* configSkeleton); /** * Destructor. */ virtual ~KdeConfigDialog(); /** * Set values in dialog from current configuration. */ void setConfig(); /** * Get values from dialog and store them in the current configuration. */ void getConfig() const; protected slots: /** * Show help. */ void slotHelp(); private: ConfigDialogPages* m_pages; }; #endif ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/kde/kdemainwindow.cpp������������������������������������������������������������0000664�0000000�0000000�00000046242�12246035113�0020031�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \file kdemainwindow.cpp * KDE Kid3 main window. * * \b Project: Kid3 * \author Urs Fleisch * \date 9 Jan 2003 * * Copyright (C) 2003-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "kdemainwindow.h" #include <kapplication.h> #include <kurl.h> #include <kconfig.h> #include <kaction.h> #include <ktoggleaction.h> #include <kstandardaction.h> #include <kshortcutsdialog.h> #include <krecentfilesaction.h> #include <kactioncollection.h> #include <kedittoolbar.h> #include <kconfigskeleton.h> #include <QAction> #include "config.h" #include "qtcompatmac.h" #include "kid3form.h" #include "kid3application.h" #include "kdeconfigdialog.h" #include "guiconfig.h" #include "tagconfig.h" #include "serverimporter.h" #include "servertrackimporter.h" #include "kdeplatformtools.h" /** * Constructor. * * @param parent parent widget */ KdeMainWindow::KdeMainWindow(QWidget* parent) : KXmlGuiWindow(parent), BaseMainWindow(this, m_platformTools = new KdePlatformTools), m_fileOpenRecent(0), m_viewToolBar(0), m_viewStatusBar(0), m_settingsAutoHideTags(0), m_settingsShowHidePicture(0) { init(); } /** * Destructor. */ KdeMainWindow::~KdeMainWindow() { delete m_platformTools; } /** Only defined for generation of translation files */ #define MAIN_TOOLBAR_FOR_PO QT_TRANSLATE_NOOP("@default", "Main Toolbar") /** * Init menu and toolbar actions. */ void KdeMainWindow::initActions() { KAction* fileOpen = KStandardAction::open( impl(), SLOT(slotFileOpen()), actionCollection()); m_fileOpenRecent = KStandardAction::openRecent( this, SLOT(slotFileOpenRecentUrl(KUrl)), actionCollection()); KAction* fileRevert = KStandardAction::revert( app(), SLOT(revertFileModifications()), actionCollection()); KAction* fileSave = KStandardAction::save( impl(), SLOT(slotFileSave()), actionCollection()); KAction* fileQuit = KStandardAction::quit( impl(), SLOT(slotFileQuit()), actionCollection()); KAction* editSelectAll = KStandardAction::selectAll( form(), SLOT(selectAllFiles()), actionCollection()); KAction* editDeselect = KStandardAction::deselect( form(), SLOT(deselectAllFiles()), actionCollection()); setStandardToolBarMenuEnabled(true); createStandardStatusBarAction(); KAction* settingsShortcuts = KStandardAction::keyBindings( this, SLOT(slotSettingsShortcuts()), actionCollection()); KAction* settingsToolbars = KStandardAction::configureToolbars( this, SLOT(slotSettingsToolbars()), actionCollection()); KAction* settingsConfigure = KStandardAction::preferences( this, SLOT(slotSettingsConfigure()), actionCollection()); fileOpen->setStatusTip(tr("Opens a directory")); m_fileOpenRecent->setStatusTip(tr("Opens a recently used directory")); fileRevert->setStatusTip( tr("Reverts the changes of all or the selected files")); fileSave->setStatusTip(tr("Saves the changed files")); fileQuit->setStatusTip(tr("Quits the application")); editSelectAll->setStatusTip(tr("Select all files")); editSelectAll->setShortcut(KShortcut(QLatin1String("Alt+Shift+A"))); editDeselect->setStatusTip(tr("Deselect all files")); settingsShortcuts->setStatusTip(tr("Configure Shortcuts")); settingsToolbars->setStatusTip(tr("Configure Toolbars")); settingsConfigure->setStatusTip(tr("Preferences dialog")); KAction* fileOpenDirectory = new KAction(KIcon(QLatin1String("document-open")), tr("O&pen Directory..."), this); fileOpenDirectory->setShortcut(KShortcut(QLatin1String("Ctrl+D"))); actionCollection()->addAction(QLatin1String("open_directory"), fileOpenDirectory); connect(fileOpenDirectory, SIGNAL(triggered()), impl(), SLOT(slotFileOpenDirectory())); KAction* fileImport = new KAction(KIcon(QLatin1String("document-import")), tr("&Import..."), this); fileImport->setData(-1); actionCollection()->addAction(QLatin1String("import"), fileImport); connect(fileImport, SIGNAL(triggered()), impl(), SLOT(slotImport())); int importerIdx = 0; foreach (const ServerImporter* si, app()->getServerImporters()) { QString serverName(QCoreApplication::translate("@default", si->name())); QString actionName = QString::fromLatin1(si->name()).toLower().remove(QLatin1Char(' ')); int dotPos = actionName.indexOf(QLatin1Char('.')); if (dotPos != -1) actionName.truncate(dotPos); actionName = QLatin1String("import_") + actionName; KAction* fileImportServer = new KAction(tr("Import from %1...").arg(serverName), this); fileImportServer->setData(importerIdx); actionCollection()->addAction(actionName, fileImportServer); connect(fileImportServer, SIGNAL(triggered()), impl(), SLOT(slotImport())); ++importerIdx; } foreach (const ServerTrackImporter* si, app()->getServerTrackImporters()) { QString serverName(QCoreApplication::translate("@default", si->name())); QString actionName = QString::fromLatin1(si->name()).toLower().remove(QLatin1Char(' ')); int dotPos = actionName.indexOf(QLatin1Char('.')); if (dotPos != -1) actionName.truncate(dotPos); actionName = QLatin1String("import_") + actionName; KAction* fileImportServer = new KAction(tr("Import from %1...").arg(serverName), this); fileImportServer->setData(importerIdx); actionCollection()->addAction(actionName, fileImportServer); connect(fileImportServer, SIGNAL(triggered()), impl(), SLOT(slotImport())); ++importerIdx; } KAction* fileBatchImport = new KAction(tr("Automatic I&mport..."), this); actionCollection()->addAction(QLatin1String("batch_import"), fileBatchImport); connect(fileBatchImport, SIGNAL(triggered()), impl(), SLOT(slotBatchImport())); KAction* fileBrowseCoverArt = new KAction(tr("&Browse Cover Art..."), this); actionCollection()->addAction(QLatin1String("browse_cover_art"), fileBrowseCoverArt); connect(fileBrowseCoverArt, SIGNAL(triggered()), impl(), SLOT(slotBrowseCoverArt())); KAction* fileExport = new KAction(KIcon(QLatin1String("document-export")), tr("&Export..."), this); actionCollection()->addAction(QLatin1String("export"), fileExport); connect(fileExport, SIGNAL(triggered()), impl(), SLOT(slotExport())); KAction* fileCreatePlaylist = new KAction(KIcon(QLatin1String("view-media-playlist")), tr("&Create Playlist..."), this); actionCollection()->addAction(QLatin1String("create_playlist"), fileCreatePlaylist); connect(fileCreatePlaylist, SIGNAL(triggered()), impl(), SLOT(slotPlaylistDialog())); KAction* toolsApplyFilenameFormat = new KAction(tr("Apply &Filename Format"), this); actionCollection()->addAction(QLatin1String("apply_filename_format"), toolsApplyFilenameFormat); connect(toolsApplyFilenameFormat, SIGNAL(triggered()), app(), SLOT(applyFilenameFormat())); KAction* toolsApplyId3Format = new KAction(tr("Apply &Tag Format"), this); actionCollection()->addAction(QLatin1String("apply_id3_format"), toolsApplyId3Format); connect(toolsApplyId3Format, SIGNAL(triggered()), app(), SLOT(applyId3Format())); KAction* toolsApplyTextEncoding = new KAction(tr("Apply Text &Encoding"), this); actionCollection()->addAction(QLatin1String("apply_text_encoding"), toolsApplyTextEncoding); connect(toolsApplyTextEncoding, SIGNAL(triggered()), app(), SLOT(applyTextEncoding())); KAction* toolsRenameDirectory = new KAction(tr("&Rename Directory..."), this); actionCollection()->addAction(QLatin1String("rename_directory"), toolsRenameDirectory); connect(toolsRenameDirectory, SIGNAL(triggered()), impl(), SLOT(slotRenameDirectory())); KAction* toolsNumberTracks = new KAction(tr("&Number Tracks..."), this); actionCollection()->addAction(QLatin1String("number_tracks"), toolsNumberTracks); connect(toolsNumberTracks, SIGNAL(triggered()), impl(), SLOT(slotNumberTracks())); KAction* toolsFilter = new KAction(tr("F&ilter..."), this); actionCollection()->addAction(QLatin1String("filter"), toolsFilter); connect(toolsFilter, SIGNAL(triggered()), impl(), SLOT(slotFilter())); const TagConfig& tagCfg = TagConfig::instance(); if (tagCfg.taggedFileFeatures() & TaggedFile::TF_ID3v24) { KAction* toolsConvertToId3v24 = new KAction(tr("Convert ID3v2.3 to ID3v2.&4"), this); actionCollection()->addAction(QLatin1String("convert_to_id3v24"), toolsConvertToId3v24); connect(toolsConvertToId3v24, SIGNAL(triggered()), app(), SLOT(convertToId3v24())); if (tagCfg.taggedFileFeatures() & TaggedFile::TF_ID3v23) { KAction* toolsConvertToId3v23 = new KAction(tr("Convert ID3v2.4 to ID3v2.&3"), this); actionCollection()->addAction(QLatin1String("convert_to_id3v23"), toolsConvertToId3v23); connect(toolsConvertToId3v23, SIGNAL(triggered()), app(), SLOT(convertToId3v23())); } } #if defined HAVE_PHONON || QT_VERSION >= 0x050000 KAction* toolsPlay = new KAction(KIcon(QLatin1String("media-playback-start")), tr("&Play"), this); actionCollection()->addAction(QLatin1String("play"), toolsPlay); connect(toolsPlay, SIGNAL(triggered()), app(), SLOT(playAudio())); #endif m_settingsShowHidePicture = new KToggleAction(tr("Show &Picture"), this); m_settingsShowHidePicture->setCheckable(true); actionCollection()->addAction(QLatin1String("hide_picture"), m_settingsShowHidePicture); connect(m_settingsShowHidePicture, SIGNAL(triggered()), impl(), SLOT(slotSettingsShowHidePicture())); m_settingsAutoHideTags = new KToggleAction(tr("Auto &Hide Tags"), this); m_settingsAutoHideTags->setCheckable(true); actionCollection()->addAction(QLatin1String("auto_hide_tags"), m_settingsAutoHideTags); connect(m_settingsAutoHideTags, SIGNAL(triggered()), impl(), SLOT(slotSettingsAutoHideTags())); KAction* editSelectAllInDir = new KAction(tr("Select All in &Directory"), this); actionCollection()->addAction(QLatin1String("select_all_in_directory"), editSelectAllInDir); connect(editSelectAllInDir, SIGNAL(triggered()), form(), SLOT(selectAllInDirectory())); KAction* editPreviousFile = new KAction(KIcon(QLatin1String("go-previous")), tr("&Previous File"), this); editPreviousFile->setShortcut(KShortcut(QLatin1String("Alt+Up"))); actionCollection()->addAction(QLatin1String("previous_file"), editPreviousFile); connect(editPreviousFile, SIGNAL(triggered()), app(), SLOT(previousFile())); KAction* editNextFile = new KAction(KIcon(QLatin1String("go-next")), tr("&Next File"), this); editNextFile->setShortcut(KShortcut(QLatin1String("Alt+Down"))); actionCollection()->addAction(QLatin1String("next_file"), editNextFile); connect(editNextFile, SIGNAL(triggered()), app(), SLOT(nextFile())); KAction* actionV1FromFilename = new KAction(tr("Tag 1") + QLatin1String(": ") + tr("From Filename"), this); actionCollection()->addAction(QLatin1String("v1_from_filename"), actionV1FromFilename); connect(actionV1FromFilename, SIGNAL(triggered()), app(), SLOT(getTagsFromFilenameV1())); KAction* actionV1FromV2 = new KAction(tr("Tag 1") + QLatin1String(": ") + tr("From Tag 2"), this); actionCollection()->addAction(QLatin1String("v1_from_v2"), actionV1FromV2); connect(actionV1FromV2, SIGNAL(triggered()), app(), SLOT(copyV2ToV1())); KAction* actionV1Copy = new KAction(tr("Tag 1") + QLatin1String(": ") + tr("Copy"), this); actionCollection()->addAction(QLatin1String("v1_copy"), actionV1Copy); connect(actionV1Copy, SIGNAL(triggered()), app(), SLOT(copyTagsV1())); KAction* actionV1Paste = new KAction(tr("Tag 1") + QLatin1String(": ") + tr("Paste"), this); actionCollection()->addAction(QLatin1String("v1_paste"), actionV1Paste); connect(actionV1Paste, SIGNAL(triggered()), app(), SLOT(pasteTagsV1())); KAction* actionV1Remove = new KAction(tr("Tag 1") + QLatin1String(": ") + tr("Remove"), this); actionCollection()->addAction(QLatin1String("v1_remove"), actionV1Remove); connect(actionV1Remove, SIGNAL(triggered()), app(), SLOT(removeTagsV1())); KAction* actionV2FromFilename = new KAction(tr("Tag 2") + QLatin1String(": ") + tr("From Filename"), this); actionCollection()->addAction(QLatin1String("v2_from_filename"), actionV2FromFilename); connect(actionV2FromFilename, SIGNAL(triggered()), app(), SLOT(getTagsFromFilenameV2())); KAction* actionV2FromV1 = new KAction(tr("Tag 2") + QLatin1String(": ") + tr("From Tag 1"), this); actionCollection()->addAction(QLatin1String("v2_from_v1"), actionV2FromV1); connect(actionV2FromV1, SIGNAL(triggered()), app(), SLOT(copyV1ToV2())); KAction* actionV2Copy = new KAction(tr("Tag 2") + QLatin1String(": ") + tr("Copy"), this); actionCollection()->addAction(QLatin1String("v2_copy"), actionV2Copy); connect(actionV2Copy, SIGNAL(triggered()), app(), SLOT(copyTagsV2())); KAction* actionV2Paste = new KAction(tr("Tag 2") + QLatin1String(": ") + tr("Paste"), this); actionCollection()->addAction(QLatin1String("v2_paste"), actionV2Paste); connect(actionV2Paste, SIGNAL(triggered()), app(), SLOT(pasteTagsV2())); KAction* actionV2Remove = new KAction(tr("Tag 2") + QLatin1String(": ") + tr("Remove"), this); actionCollection()->addAction(QLatin1String("v2_remove"), actionV2Remove); connect(actionV2Remove, SIGNAL(triggered()), app(), SLOT(removeTagsV2())); KAction* actionFramesEdit = new KAction(tr("Frames:") + QLatin1Char(' ') + tr("Edit"), this); actionCollection()->addAction(QLatin1String("frames_edit"), actionFramesEdit); connect(actionFramesEdit, SIGNAL(triggered()), form(), SLOT(editFrame())); KAction* actionFramesAdd = new KAction(tr("Frames:") + QLatin1Char(' ') + tr("Add"), this); actionCollection()->addAction(QLatin1String("frames_add"), actionFramesAdd); connect(actionFramesAdd, SIGNAL(triggered()), form(), SLOT(addFrame())); KAction* actionFramesDelete = new KAction(tr("Frames:") + QLatin1Char(' ') + tr("Delete"), this); actionCollection()->addAction(QLatin1String("frames_delete"), actionFramesDelete); connect(actionFramesDelete, SIGNAL(triggered()), form(), SLOT(deleteFrame())); KAction* actionFilenameFromV1 = new KAction(tr("Filename") + QLatin1String(": ") + tr("From Tag 1"), this); actionCollection()->addAction(QLatin1String("filename_from_v1"), actionFilenameFromV1); connect(actionFilenameFromV1, SIGNAL(triggered()), form(), SLOT(fnFromID3V1())); KAction* actionFilenameFromV2 = new KAction(tr("Filename") + QLatin1String(": ") + tr("From Tag 2"), this); actionCollection()->addAction(QLatin1String("filename_from_v2"), actionFilenameFromV2); connect(actionFilenameFromV2, SIGNAL(triggered()), form(), SLOT(fnFromID3V2())); KAction* actionFilenameFocus = new KAction(tr("Filename") + QLatin1String(": ") + tr("Focus"), this); actionCollection()->addAction(QLatin1String("filename_focus"), actionFilenameFocus); connect(actionFilenameFocus, SIGNAL(triggered()), form(), SLOT(setFocusFilename())); KAction* actionV1Focus = new KAction(tr("Tag 1") + QLatin1String(": ") + tr("Focus"), this); actionCollection()->addAction(QLatin1String("v1_focus"), actionV1Focus); connect(actionV1Focus, SIGNAL(triggered()), form(), SLOT(setFocusV1())); KAction* actionV2Focus = new KAction(tr("Tag 2") + QLatin1String(": ") + tr("Focus"), this); actionCollection()->addAction(QLatin1String("v2_focus"), actionV2Focus); connect(actionV2Focus, SIGNAL(triggered()), form(), SLOT(setFocusV2())); KAction* actionFileListFocus = new KAction(tr("File List") + QLatin1String(": ") + tr("Focus"), this); actionCollection()->addAction(QLatin1String("filelist_focus"), actionFileListFocus); connect(actionFileListFocus, SIGNAL(triggered()), form(), SLOT(setFocusFileList())); KAction* actionDirListFocus = new KAction(tr("Directory List") + QLatin1String(": ") + tr("Focus"), this); actionCollection()->addAction(QLatin1String("dirlist_focus"), actionDirListFocus); connect(actionDirListFocus, SIGNAL(triggered()), form(), SLOT(setFocusDirList())); createGUI(); } /** * Add directory to recent files list. * * @param dirName path to directory */ void KdeMainWindow::addDirectoryToRecentFiles(const QString& dirName) { KUrl url; url.setPath(dirName); m_fileOpenRecent->addUrl(url); } /** * Read settings from the configuration. */ void KdeMainWindow::readConfig() { setAutoSaveSettings(); m_settingsShowHidePicture->setChecked(!GuiConfig::instance().m_hidePicture); m_settingsAutoHideTags->setChecked(GuiConfig::instance().m_autoHideTags); m_fileOpenRecent->loadEntries(KGlobal::config()->group("Recent Files")); } /** * Store geometry and recent files in settings. */ void KdeMainWindow::saveConfig() { m_fileOpenRecent->saveEntries(KGlobal::config()->group("Recent Files")); } /** * Set main window caption. * * @param caption caption without application name * @param modified true if any file is modified */ void KdeMainWindow::setWindowCaption(const QString& caption, bool modified) { setCaption(caption, modified); } /** * Get action for Settings/Auto Hide Tags. * @return action. */ QAction* KdeMainWindow::autoHideTagsAction() { return m_settingsAutoHideTags; } /** * Get action for Settings/Hide Picture. * @return action. */ QAction* KdeMainWindow::showHidePictureAction() { return m_settingsShowHidePicture; } /** * Update modification state before closing. * Called on closeEvent() of window. * If anything was modified, save after asking user. * Save options before closing. * This method is called by closeEvent(), which occurs when the * window is closed or slotFileQuit() (Quit menu) is selected. * * @return false if user canceled, * true will quit the application. */ bool KdeMainWindow::queryClose() { return queryBeforeClosing(); } /** * Saves the window properties to the session config file. * * @param cfg application configuration */ void KdeMainWindow::saveProperties(KConfigGroup& cfg) { cfg.writeEntry("dirname", app()->getDirName()); } /** * Reads the session config file and restores the application's state. * * @param cfg application configuration */ void KdeMainWindow::readProperties(const KConfigGroup& cfg) { app()->openDirectory(QStringList() << cfg.readEntry("dirname", "")); } /** * Open recent directory. * * @param dir directory to open */ void KdeMainWindow::slotFileOpenRecentUrl(const KUrl& url) { openRecentDirectory(url.path()); } /** * Shortcuts configuration. */ void KdeMainWindow::slotSettingsShortcuts() { KShortcutsDialog::configure( actionCollection(), KShortcutsEditor::LetterShortcutsDisallowed, this); } /** * Toolbars configuration. */ void KdeMainWindow::slotSettingsToolbars() { KEditToolBar dlg(actionCollection()); if (dlg.exec()) { createGUI(); } } /** * Preferences. */ void KdeMainWindow::slotSettingsConfigure() { QString caption(tr("Configure - Kid3")); KConfigSkeleton* configSkeleton = new KConfigSkeleton; KdeConfigDialog* dialog = new KdeConfigDialog(this, caption, configSkeleton); dialog->setConfig(); if (dialog->exec() == QDialog::Accepted) { dialog->getConfig(); impl()->applyChangedConfiguration(); } delete configSkeleton; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/kde/kdemainwindow.h��������������������������������������������������������������0000664�0000000�0000000�00000006653�12246035113�0017500�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \file kdemainwindow.h * KDE Kid3 main window. * * \b Project: Kid3 * \author Urs Fleisch * \date 9 Jan 2003 * * Copyright (C) 2003-2011 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #ifndef KDEMAINWINDOW_H #define KDEMAINWINDOW_H #include <kxmlguiwindow.h> #include "basemainwindow.h" class QAction; class KRecentFilesAction; class KToggleAction; class KUrl; class IPlatformTools; /** * KDE Kid3 main window. */ class KdeMainWindow : public KXmlGuiWindow, public BaseMainWindow { Q_OBJECT public: /** * Constructor. * * @param parent parent widget */ explicit KdeMainWindow(QWidget* parent = 0); /** * Destructor. */ virtual ~KdeMainWindow(); /** * Init menu and toolbar actions. */ virtual void initActions(); /** * Add directory to recent files list. * * @param dirName path to directory */ virtual void addDirectoryToRecentFiles(const QString& dirName); /** * Read settings from the configuration. */ virtual void readConfig(); /** * Store geometry and recent files in settings. */ virtual void saveConfig(); /** * Get action for Settings/Auto Hide Tags. * @return action. */ virtual QAction* autoHideTagsAction(); /** * Get action for Settings/Hide Picture. * @return action. */ virtual QAction* showHidePictureAction(); /** * Set main window caption. * * @param caption caption without application name * @param modified true if any file is modified */ virtual void setWindowCaption(const QString& caption, bool modified); protected: /** * Update modification state before closing. * Called on closeEvent() of window. * If anything was modified, save after asking user. * * @return FALSE if user canceled. */ virtual bool queryClose(); /** * Saves the window properties for each open window during session end * to the session config file. * * @param cfg application configuration */ virtual void saveProperties(KConfigGroup& cfg); /** * Reads the session config file and restores the application's state. * * @param cfg application configuration */ virtual void readProperties(const KConfigGroup& cfg); private slots: /** * Open recent directory. * * @param url URL of directory to open */ void slotFileOpenRecentUrl(const KUrl& url); /** * Shortcuts configuration. */ void slotSettingsShortcuts(); /** * Toolbars configuration. */ void slotSettingsToolbars(); /** * Preferences. */ void slotSettingsConfigure(); private: IPlatformTools* m_platformTools; /** Actions */ KRecentFilesAction* m_fileOpenRecent; KToggleAction* m_viewToolBar; KToggleAction* m_viewStatusBar; KToggleAction* m_settingsAutoHideTags; KToggleAction* m_settingsShowHidePicture; }; #endif /* KDEMAINWINDOW_H */ �������������������������������������������������������������������������������������kid3-3.0.2/src/app/kde/kdeplatformtools.cpp���������������������������������������������������������0000664�0000000�0000000�00000021204�12246035113�0020551�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \file kdeplatformtools.cpp * KDE platform specific tools. * * \b Project: Kid3 * \author Urs Fleisch * \date 30 Mar 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "kdeplatformtools.h" #include <kurl.h> #include <kio/copyjob.h> #include <kio/netaccess.h> #include <ktoolinvocation.h> #include <kmessagebox.h> #include <kfiledialog.h> #include <kconfig.h> #include <QCoreApplication> #include "kdesettings.h" /** * Constructor. */ KdePlatformTools::KdePlatformTools() : m_settings(0), m_config(0) { } /** * Destructor. */ KdePlatformTools::~KdePlatformTools() { delete m_config; delete m_settings; } /** * Get application settings. * @return settings instance. */ ISettings* KdePlatformTools::applicationSettings() { if (!m_config) { m_settings = new KConfig; m_config = new KdeSettings(m_settings); } return m_config; } /** * Move file or directory to trash. * * @param path path to file or directory * * @return true if ok. */ bool KdePlatformTools::moveToTrash(const QString& path) const { KUrl src; src.setPath(path); KIO::Job* job = KIO::trash(src); return KIO::NetAccess::synchronousRun(job, 0); } /** * Display help for a topic. * * @param anchor anchor in help document */ void KdePlatformTools::displayHelp(const QString& anchor) { KToolInvocation::invokeHelp(anchor); } /** * Get a themed icon by name. * @param name name of icon * @return icon. */ QIcon KdePlatformTools::iconFromTheme(const QString& name) const { return KIcon(name); } /** * Construct a name filter string suitable for file dialogs. * @param nameFilters list of description, filter pairs, e.g. * [("Images", "*.jpg *.jpeg *.png"), ("All Files", "*")]. * @return name filter string. */ QString KdePlatformTools::fileDialogNameFilter( const QList<QPair<QString, QString> >& nameFilters) const { QString filter; for (QList<QPair<QString, QString> >::const_iterator it = nameFilters.constBegin(); it != nameFilters.constEnd(); ++it) { if (!filter.isEmpty()) { filter += QLatin1Char('\n'); } filter += it->second; filter += QLatin1Char('|'); filter += it->first; if (it->second.length() < 60) { filter += QLatin1String(" ("); filter += it->second; filter += QLatin1Char(')'); } } return filter; } /** * Get file pattern part of m_nameFilter. * @param nameFilter name filter string * @return file patterns, e.g. "*.mp3". */ QString KdePlatformTools::getNameFilterPatterns(const QString& nameFilter) const { if (nameFilter.startsWith(QLatin1Char('*'))) { int end = nameFilter.indexOf(QLatin1Char('|')); return end != -1 ? nameFilter.left(end) : nameFilter; } else { return QString(); } } /** * Display error dialog with item list. * @param parent parent widget * @param text text * @param strlist list of items * @param caption caption */ void KdePlatformTools::errorList(QWidget* parent, const QString& text, const QStringList& strlist, const QString& caption) { KMessageBox::errorList(parent, text, strlist, caption); } /** * Display warning dialog with yes, no, cancel buttons. * @param parent parent widget * @param text text * @param caption caption * @return QMessageBox::Yes, QMessageBox::No or QMessageBox::Cancel. */ int KdePlatformTools::warningYesNoCancel(QWidget* parent, const QString& text, const QString& caption) { int rc = KMessageBox::warningYesNoCancel(parent, text, caption); switch (rc) { case KMessageBox::Ok: return QMessageBox::Ok; case KMessageBox::Cancel: return QMessageBox::Cancel; case KMessageBox::Yes: return QMessageBox::Yes; case KMessageBox::No: return QMessageBox::No; case KMessageBox::Continue: default: return QMessageBox::Ignore; } } /** * Display dialog to select an existing file. * @param parent parent widget * @param caption caption * @param dir directory * @param filter filter * @param selectedFilter the selected filter is returned here * @return selected file, empty if canceled. */ QString KdePlatformTools::getOpenFileName(QWidget* parent, const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter) { if (selectedFilter) { QString selectedFile; KFileDialog diag(dir, filter, parent); diag.setWindowTitle(caption.isEmpty() ? QCoreApplication::translate("@default", QT_TRANSLATE_NOOP("@default", "Open")) : caption); if (diag.exec() == QDialog::Accepted) { selectedFile = diag.selectedFile(); *selectedFilter = diag.currentFilter(); } return selectedFile; } else { return KFileDialog::getOpenFileName(dir, filter, parent, caption); } } /** * Display dialog to select existing files. * @param parent parent widget * @param caption caption * @param dir directory * @param filter filter * @param selectedFilter the selected filter is returned here * @return selected files, empty if canceled. */ QStringList KdePlatformTools::getOpenFileNames(QWidget* parent, const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter) { if (selectedFilter) { QStringList selectedFiles; KFileDialog diag(dir, filter, parent); diag.setWindowTitle(caption.isEmpty() ? QCoreApplication::translate("@default", QT_TRANSLATE_NOOP("@default", "Open")) : caption); diag.setMode(KFile::Files | KFile::LocalOnly); if (diag.exec() == QDialog::Accepted) { selectedFiles = diag.selectedFiles(); *selectedFilter = diag.currentFilter(); } return selectedFiles; } else { return KFileDialog::getOpenFileNames(dir, filter, parent, caption); } } /** * Display dialog to select a file to save. * @param parent parent widget * @param caption caption * @param dir directory * @param filter filter * @param selectedFilter the selected filter is returned here * @return selected file, empty if canceled. */ QString KdePlatformTools::getSaveFileName(QWidget* parent, const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter) { if (selectedFilter) { QString selectedFile; KFileDialog diag(dir, filter, parent); diag.setOperationMode(KFileDialog::Saving); diag.setWindowTitle(caption.isEmpty() ? QCoreApplication::translate("@default", QT_TRANSLATE_NOOP("@default", "Save As")) : caption); if (diag.exec() == QDialog::Accepted) { selectedFile = diag.selectedFile(); *selectedFilter = diag.currentFilter(); } return selectedFile; } else { return KFileDialog::getSaveFileName(dir, filter, parent, caption); } } /** * Display dialog to select an existing directory. * @param parent parent widget * @param caption caption * @param startDir start directory * @return selected directory, empty if canceled. */ QString KdePlatformTools::getExistingDirectory(QWidget* parent, const QString& caption, const QString& startDir) { return KFileDialog::getExistingDirectory(startDir, parent, caption); } /** * Display warning dialog. * @param parent parent widget * @param text text * @param details detailed message * @param caption caption */ void KdePlatformTools::warningDialog(QWidget* parent, const QString& text, const QString& details, const QString& caption) { KMessageBox::error(parent, text + details, caption); } /** * Display warning dialog with options to continue or cancel. * @param parent parent widget * @param text text * @param strlist list of items * @param caption caption * @return true if continue was selected. */ bool KdePlatformTools::warningContinueCancelList(QWidget* parent, const QString& text, const QStringList& strlist, const QString& caption) { return KMessageBox::warningContinueCancelList(parent, text, strlist, caption, KStandardGuiItem::ok(), KStandardGuiItem::cancel(), QString(), KMessageBox::Dangerous) == KMessageBox::Continue; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/kde/kdeplatformtools.h�����������������������������������������������������������0000664�0000000�0000000�00000012316�12246035113�0020222�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \file kdeplatformtools.h * KDE platform specific tools. * * \b Project: Kid3 * \author Urs Fleisch * \date 30 Mar 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #ifndef KDEPLATFORMTOOLS_H #define KDEPLATFORMTOOLS_H #include "iplatformtools.h" class KConfig; /** * KDE platform specific tools. */ class KdePlatformTools : public IPlatformTools { public: /** * Constructor. */ KdePlatformTools(); /** * Destructor. */ virtual ~KdePlatformTools(); /** * Get application settings. * @return settings instance. */ virtual ISettings* applicationSettings(); /** * Move file or directory to trash. * * @param path path to file or directory * * @return true if ok. */ virtual bool moveToTrash(const QString& path) const; /** * Display help for a topic. * * @param anchor anchor in help document */ virtual void displayHelp(const QString& anchor); /** * Get a themed icon by name. * @param name name of icon * @return icon. */ virtual QIcon iconFromTheme(const QString& name) const; /** * Construct a name filter string suitable for file dialogs. * @param nameFilters list of description, filter pairs, e.g. * [("Images", "*.jpg *.jpeg *.png"), ("All Files", "*")]. * @return name filter string. */ virtual QString fileDialogNameFilter( const QList<QPair<QString, QString> >& nameFilters) const; /** * Get file pattern part of m_nameFilter. * @param nameFilter name filter string * @return file patterns, e.g. "*.mp3". */ virtual QString getNameFilterPatterns(const QString& nameFilter) const; /** * Display error dialog with item list. * @param parent parent widget * @param text text * @param strlist list of items * @param caption caption */ virtual void errorList(QWidget* parent, const QString& text, const QStringList& strlist, const QString& caption); /** * Display warning dialog with yes, no, cancel buttons. * @param parent parent widget * @param text text * @param caption caption * @return QMessageBox::Yes, QMessageBox::No or QMessageBox::Cancel. */ virtual int warningYesNoCancel(QWidget* parent, const QString& text, const QString& caption); /** * Display dialog to select an existing file. * @param parent parent widget * @param caption caption * @param dir directory * @param filter filter * @param selectedFilter the selected filter is returned here * @return selected file, empty if canceled. */ virtual QString getOpenFileName(QWidget* parent, const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter); /** * Display dialog to select existing files. * @param parent parent widget * @param caption caption * @param dir directory * @param filter filter * @param selectedFilter the selected filter is returned here * @return selected files, empty if canceled. */ virtual QStringList getOpenFileNames(QWidget* parent, const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter); /** * Display dialog to select a file to save. * @param parent parent widget * @param caption caption * @param dir directory * @param filter filter * @param selectedFilter the selected filter is returned here * @return selected file, empty if canceled. */ virtual QString getSaveFileName(QWidget* parent, const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter); /** * Display dialog to select an existing directory. * @param parent parent widget * @param caption caption * @param startDir start directory * @return selected directory, empty if canceled. */ virtual QString getExistingDirectory(QWidget* parent, const QString& caption, const QString& startDir); /** * Display warning dialog. * @param parent parent widget * @param text text * @param details detailed message * @param caption caption */ virtual void warningDialog(QWidget* parent, const QString& text, const QString& details, const QString& caption); /** * Display warning dialog with options to continue or cancel. * @param parent parent widget * @param text text * @param strlist list of items * @param caption caption * @return true if continue was selected. */ virtual bool warningContinueCancelList(QWidget* parent, const QString& text, const QStringList& strlist, const QString& caption); private: KConfig* m_settings; ISettings* m_config; }; #endif // KDEPLATFORMTOOLS_H ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/kde/kdesettings.cpp��������������������������������������������������������������0000664�0000000�0000000�00000004670�12246035113�0017514�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \file kdesettings.cpp * Wrapper for KDE application settings. * * \b Project: Kid3 * \author Urs Fleisch * \date 07 Apr 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "kdesettings.h" #include <kconfig.h> #include <kconfiggroup.h> /** * Constructor. */ KdeSettings::KdeSettings(KConfig* config) : m_config(config), m_group(0) { migrateOldSettings(); } /** * Destructor. */ KdeSettings::~KdeSettings() { delete m_group; } /** * Use settings subgroup. * @param prefix group name */ void KdeSettings::beginGroup(const QString& prefix) { delete m_group; m_group = new KConfigGroup(m_config, prefix); } /** * Finnish using settings subgroup. */ void KdeSettings::endGroup() { delete m_group; m_group = 0; } /** * Set value for setting. * @param key name of setting * @param value value for setting */ void KdeSettings::setValue(const QString& key, const QVariant& value) { if (m_group) { m_group->writeEntry(key, value); } } /** * Get value for setting. * @param key name of setting * @param defaultValue default value * @return value of setting as variant. */ QVariant KdeSettings::value(const QString& key, const QVariant& defaultValue) const { if (m_group) { return m_group->readEntry(key, defaultValue); } return QVariant(); } /** * Remove setting. * @param key name of setting */ void KdeSettings::remove(const QString& key) { if (m_group) { m_group->deleteEntry(key); } } /** * Check if setting exists. * @param key name of setting * @return true if setting exists. */ bool KdeSettings::contains(const QString& key) const { if (m_group) { return m_group->hasKey(key); } return false; } /** * Write unsaved changes to permanent storage. */ void KdeSettings::sync() { m_config->sync(); } ������������������������������������������������������������������������kid3-3.0.2/src/app/kde/kdesettings.h����������������������������������������������������������������0000664�0000000�0000000�00000004352�12246035113�0017156�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \file kdesettings.h * Wrapper for KDE application settings. * * \b Project: Kid3 * \author Urs Fleisch * \date 07 Apr 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #ifndef KDESETTINGS_H #define KDESETTINGS_H #include "isettings.h" class KConfig; class KConfigGroup; /** * Wrapper for KDE application settings. */ class KdeSettings : public ISettings { public: /** * Constructor. * @param config KDE settings */ explicit KdeSettings(KConfig* config); /** * Destructor. */ virtual ~KdeSettings(); /** * Use settings subgroup. * @param prefix group name */ virtual void beginGroup(const QString& prefix); /** * Finnish using settings subgroup. */ virtual void endGroup(); /** * Set value for setting. * @param key name of setting * @param value value for setting */ virtual void setValue(const QString& key, const QVariant& value); /** * Get value for setting. * @param key name of setting * @param defaultValue default value * @return value of setting as variant. */ virtual QVariant value(const QString& key, const QVariant& defaultValue) const; /** * Remove setting. * @param key name of setting */ virtual void remove(const QString& key); /** * Check if setting exists. * @param key name of setting * @return true if setting exists. */ virtual bool contains(const QString& key) const; /** * Write unsaved changes to permanent storage. */ virtual void sync(); private: KConfig* m_config; KConfigGroup* m_group; }; #endif // KDESETTINGS_H ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/kde/kid3ui.rc��������������������������������������������������������������������0000664�0000000�0000000�00000004714�12246035113�0016201�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd"> <kpartgui name="kid3" version="13"> <MenuBar> <Menu name="file" noMerge="1"><text>&File</text> <Action name="file_open"/> <Action name="file_open_recent"/> <Action name="open_directory"/> <Separator/> <Action name="file_save"/> <Action name="file_revert"/> <Separator/> <Action name="import"/> <Action name="import_amazon"/> <Action name="import_discogs"/> <Action name="import_gnudb"/> <Action name="import_tracktype"/> <Action name="import_musicbrainzrelease"/> <Action name="import_musicbrainzfingerprint"/> <Action name="batch_import"/> <Action name="browse_cover_art"/> <Action name="export"/> <Action name="create_playlist"/> <Separator/> <Action name="file_quit"/> </Menu> <Menu name="edit" noMerge="1"><text>&Edit</text> <Action name="edit_select_all"/> <Action name="edit_deselect"/> <Action name="select_all_in_directory"/> <Action name="previous_file"/> <Action name="next_file"/> </Menu> <Menu name="tools" noMerge="1"><text>&Tools</text> <Action name="apply_filename_format"/> <Action name="apply_id3_format"/> <Action name="apply_text_encoding"/> <Action name="rename_directory"/> <Action name="number_tracks"/> <Action name="filter"/> <Action name="convert_to_id3v24"/> <Action name="convert_to_id3v23"/> <Action name="play"/> </Menu> <Menu name="settings" noMerge="1"><text>&Settings</text> <Merge name="StandardToolBarMenuHandler"/> <Action name="options_show_toolbar"/> <Action name="options_show_statusbar"/> <Action name="hide_picture"/> <Action name="auto_hide_tags"/> <Separator/> <Action name="options_configure_keybinding"/> <Action name="options_configure_toolbars"/> <Action name="options_configure"/> </Menu> <Menu name="help" noMerge="1"><text>&Help</text> <Action name="help_contents"/> <Separator/> <Action name="help_report_bug"/> <Separator/> <Action name="switch_application_language"/> <Separator/> <Action name="help_about_app"/> <Action name="help_about_kde"/> </Menu> </MenuBar> <ToolBar noMerge="1" name="mainToolBar"><text>Main Toolbar</text> <Action name="file_open" /> <Action name="file_save" /> <Action name="file_revert" /> <Action name="create_playlist" /> <Action name="previous_file" /> <Action name="next_file" /> <Action name="play" /> </ToolBar> </kpartgui> ����������������������������������������������������kid3-3.0.2/src/app/kde/mainkde.cpp������������������������������������������������������������������0000664�0000000�0000000�00000007060�12246035113�0016574�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \file mainkde.cpp * Main program. * * \b Project: Kid3 * \author Urs Fleisch * \date 9 Jan 2003 * * Copyright (C) 2003-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <QFile> #include <kdeversion.h> #include <kapplication.h> #include <kcmdlineargs.h> #include <kaboutdata.h> #include <klocale.h> #include <kconfiggroup.h> #include "fileconfig.h" #include "loadtranslation.h" #include "kdemainwindow.h" /** * Main program. * * @param argc number of arguments including command name * @param argv arguments, argv[0] is command name * * @return exit code of application. */ int main(int argc, char* argv[]) { KAboutData aboutData( "kid3", "kdelibs4", ki18n("Kid3"), VERSION, ki18n("Kid3 ID3 Tagger"), KAboutData::License_GPL, ki18n("(c) 2003-" RELEASE_YEAR " Urs Fleisch"), KLocalizedString(), "http://kid3.sourceforge.net", "ufleisch@users.sourceforge.net"); aboutData.addAuthor(ki18n("Urs Fleisch"), KLocalizedString(), "ufleisch@users.sourceforge.net"); KCmdLineArgs::init(argc, argv, &aboutData); KCmdLineOptions options; options.add("+[Dir]", ki18n("%1").subs(QCoreApplication::translate("@default", QT_TRANSLATE_NOOP("@default", "directory to open")))); KCmdLineArgs::addCmdLineOptions(options); KApplication app; QString configuredLanguage = KConfigGroup(KGlobal::config(), "Locale").readEntry("Language"); Utils::loadTranslation(configuredLanguage); aboutData.setShortDescription( ki18n("%1").subs(QCoreApplication::translate("@default", QT_TRANSLATE_NOOP("@default", "Kid3 ID3 Tagger")))); aboutData.setTranslator( ki18n("%1").subs(QCoreApplication::translate("@default", // i18n NAME OF TRANSLATORS QT_TRANSLATE_NOOP("@default", "Your names"))), ki18n("%1").subs(QCoreApplication::translate("@default", // i18n EMAIL OF TRANSLATORS QT_TRANSLATE_NOOP("@default", "Your emails")))); #if KDE_VERSION >= 0x040500 // Should not be used, but seems to be the only way to update the "about data" // with translated information. KGlobal::activeComponent().setAboutData(aboutData); #endif if (app.isSessionRestored()) { RESTORE(KdeMainWindow) } else { KdeMainWindow* kid3 = new KdeMainWindow; kid3->show(); KCmdLineArgs* args = KCmdLineArgs::parsedArgs(); if (args->count()) { #if KDE_VERSION >= 0x040600 kid3->confirmedOpenDirectory(args->allArguments().mid(1)); #else QStringList args1; for (int i = 0; i < args->count(); ++i) args1.append(args->arg(i)); kid3->confirmedOpenDirectory(args1); #endif } else if (FileConfig::instance().m_loadLastOpenedFile && !FileConfig::instance().m_lastOpenedFile.isEmpty()) { kid3->confirmedOpenDirectory(QStringList() << FileConfig::instance().m_lastOpenedFile); } args->clear(); } return app.exec(); } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/kid3.desktop���������������������������������������������������������������������0000664�0000000�0000000�00000002031�12246035113�0016133�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������[Desktop Entry] Name=Kid3 Name[sr]=Кид3 Name[sr@ijekavian]=Кид3 Exec=kid3 %F Icon=kid3 Type=Application X-DocPath=kid3/index.html MimeType=audio/mpeg;audio/ogg;application/ogg;audio/x-flac;audio/x-flac+ogg;audio/x-vorbis+ogg;audio/x-speex+ogg;audio/x-oggflac;audio/x-musepack;audio/aac;audio/mp4;audio/x-speex;audio/x-tta;audio/x-wavpack; GenericName=Audio Tagger GenericName[de]=Audio Tag Editor GenericName[es]=Editor de etiquetas de audio GenericName[fi]=Äänitiedostojen tagimuokkaaja GenericName[fr]=Éditeur de balises GenericName[it]=Editor di tag audio GenericName[nl]=Audio tag editor GenericName[pl]=Edytor znaczników audio GenericName[ru]=Программа-редактор аудио-тегов GenericName[sr]=Уређивач аудио ознака GenericName[sr@ijekavian]=Уређивач аудио ознака GenericName[sr@latin]=UreÄ‘ivaÄ audio oznaka GenericName[sr@ijekavianlatin]=UreÄ‘ivaÄ audio oznaka Terminal=false Categories=Qt;KDE;AudioVideo;Audio;AudioVideoEditing; StartupNotify=true StartupWMClass=kid3 �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/qt/������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�12246035113�0014336�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/qt/CMakeLists.txt����������������������������������������������������������������0000664�0000000�0000000�00000017745�12246035113�0017114�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������set(qt_SRCS mainqt.cpp platformtools.cpp kid3mainwindow.cpp recentfilesmenu.cpp browserdialog.cpp messagedialog.cpp configdialog.cpp mainwindowconfig.cpp shortcutsmodel.cpp shortcutsdelegate.cpp ) set(qt_MOC_HDRS kid3mainwindow.h recentfilesmenu.h browserdialog.h messagedialog.h configdialog.h shortcutsmodel.h shortcutsdelegate.h ) qt4_wrap_cpp(qt_GEN_MOC_SRCS ${qt_MOC_HDRS}) set(qt_SRCS ${qt_SRCS} ${qt_GEN_MOC_SRCS}) if (APPLE) # Icon and properties for .plist file set(MACOSX_BUNDLE_BUNDLE_NAME "Kid3") set(MACOSX_BUNDLE_ICON_FILE kid3.icns) set(MACOSX_BUNDLE_SHORT_VERSION_STRING ${CPACK_PACKAGE_VERSION}) set(MACOSX_BUNDLE_GUI_IDENTIFIER "net.sourceforge.kid3") set(MACOSX_BUNDLE_COPYRIGHT "Copyright (c) 2003-${RELEASE_YEAR} Urs Fleisch") set_source_files_properties(kid3.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources) set(qt_SRCS ${qt_SRCS} kid3.icns) set_source_files_properties(qt.conf PROPERTIES MACOSX_PACKAGE_LOCATION Resources) set(qt_SRCS ${qt_SRCS} qt.conf) elseif (WIN32) if (MINGW) # support for Windows resource files with MinGW set(CMAKE_RC_COMPILER_INIT windres) enable_language(RC) set(CMAKE_RC_COMPILE_OBJECT "<CMAKE_RC_COMPILER> <FLAGS> -O coff <DEFINES> -i <SOURCE> -o <OBJECT>") endif (MINGW) # add Windows resource file with icon set(qt_SRCS ${qt_SRCS} kid3win.rc) endif (APPLE) if (UNIX AND NOT APPLE) find_program(GZIP_PROGRAM gzip) # install desktop file and icons for Linux add_custom_command(OUTPUT kid3-qt.desktop COMMAND ${PERL_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/fixdesktop.pl <${CMAKE_CURRENT_SOURCE_DIR}/../kid3.desktop >kid3-qt.desktop DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/../kid3.desktop ) add_custom_target("desktop-file" ALL DEPENDS kid3-qt.desktop) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/kid3-qt.desktop DESTINATION "${WITH_DATAROOTDIR}/applications") if (GZIP_PROGRAM) add_custom_command(OUTPUT kid3-qt.svg COMMAND ${GZIP_PROGRAM} -cd ${CMAKE_CURRENT_SOURCE_DIR}/../hisc-app-kid3.svgz >kid3-qt.svg DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/../hisc-app-kid3.svgz ) set(qt_SRCS ${qt_SRCS} ${CMAKE_CURRENT_BINARY_DIR}/kid3-qt.svg) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/kid3-qt.svg DESTINATION "${WITH_DATAROOTDIR}/icons/hicolor/scalable/apps") else (GZIP_PROGRAM) install(FILES ../hisc-app-kid3.svgz DESTINATION "${WITH_DATAROOTDIR}/icons/hicolor/scalable/apps" RENAME kid3-qt.svgz) endif (GZIP_PROGRAM) install(FILES ../hi48-app-kid3.png DESTINATION "${WITH_DATAROOTDIR}/icons/hicolor/48x48/apps" RENAME kid3-qt.png) install(FILES ../hi32-app-kid3.png DESTINATION "${WITH_DATAROOTDIR}/icons/hicolor/32x32/apps" RENAME kid3-qt.png) install(FILES ../hi16-app-kid3.png DESTINATION "${WITH_DATAROOTDIR}/icons/hicolor/16x16/apps" RENAME kid3-qt.png) elseif (APPLE) install(FILES ../hi48-app-kid3.png DESTINATION ${WITH_DATAROOTDIR} RENAME kid3.png) endif (UNIX AND NOT APPLE) add_definitions(${QT_DEFINITIONS} ${QT_EXECUTABLE_COMPILE_FLAGS}) set(qt_RESOURCES kid3.qrc) qt4_add_resources(qt_GEN_RCS ${qt_RESOURCES}) add_executable(${KID3_EXECUTABLE} MACOSX_BUNDLE WIN32 ${qt_SRCS} ${qt_GEN_RCS}) target_link_libraries(${KID3_EXECUTABLE} kid3-gui -lstdc++) if (APPLE) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../../../macosx/Info.plist.in ${CMAKE_CURRENT_BINARY_DIR}/Info.plist) set_target_properties(${KID3_EXECUTABLE} PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_BINARY_DIR}/Info.plist) endif (APPLE) install(TARGETS ${KID3_EXECUTABLE} BUNDLE DESTINATION . RUNTIME DESTINATION ${WITH_BINDIR}) if (APPLE OR WIN32) # Put Qt libraries and plugins into installation package to create a # standalone bundle on Mac OS X and Windows set(plugin_dest_dir ${WITH_BINDIR}) set(bundle_app "\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${WITH_BINDIR}/${KID3_EXECUTABLE}") if (APPLE) set(plugin_dest_dir kid3.app/Contents/PlugIns) set(bundle_app "\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/kid3.app") endif (APPLE) if (WIN32) set(bundle_app "\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${WITH_BINDIR}/kid3.exe") endif (WIN32) string(REPLACE "/./" "/" bundle_app ${bundle_app}) # Install Qt JPEG and Phonon plugins install(DIRECTORY "${QT_PLUGINS_DIR}/imageformats" DESTINATION ${plugin_dest_dir} FILES_MATCHING PATTERN "*jpeg${CMAKE_SHARED_LIBRARY_SUFFIX}" PATTERN "*jpeg4${CMAKE_SHARED_LIBRARY_SUFFIX}") if (HAVE_QT5) if (APPLE) install(DIRECTORY "${QT_PLUGINS_DIR}/platforms" DESTINATION kid3.app/Contents/MacOS FILES_MATCHING PATTERN "libqcocoa${CMAKE_SHARED_LIBRARY_SUFFIX}") install(DIRECTORY "${QT_PLUGINS_DIR}/mediaservice" DESTINATION ${plugin_dest_dir} FILES_MATCHING PATTERN "libqavfmediaplayer${CMAKE_SHARED_LIBRARY_SUFFIX}") elseif (WIN32) install(DIRECTORY "${QT_PLUGINS_DIR}/platforms" DESTINATION ${plugin_dest_dir} FILES_MATCHING PATTERN "qwindows${CMAKE_SHARED_LIBRARY_SUFFIX}") install(DIRECTORY "${QT_PLUGINS_DIR}/mediaservice" DESTINATION ${plugin_dest_dir} FILES_MATCHING PATTERN "dsengine${CMAKE_SHARED_LIBRARY_SUFFIX}") endif (APPLE) endif (HAVE_QT5) if (HAVE_PHONON) install(DIRECTORY "${QT_PLUGINS_DIR}/phonon_backend" DESTINATION ${plugin_dest_dir} FILES_MATCHING PATTERN "*d4${CMAKE_SHARED_LIBRARY_SUFFIX}" EXCLUDE PATTERN "*${CMAKE_SHARED_LIBRARY_SUFFIX}") endif (HAVE_PHONON) # Install and fix up all dependencies of the executable and the plugins. # On Mac OS X only keep the i386 code in the Qt libraries and plugins. install(CODE " file(GLOB_RECURSE QTPLUGINS \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${plugin_dest_dir}/*${CMAKE_SHARED_LIBRARY_SUFFIX}\") if (APPLE) file(GLOB_RECURSE QTPLATFORMPLUGINS \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/kid3.app/Contents/MacOS/platforms/*${CMAKE_SHARED_LIBRARY_SUFFIX}\") set(QTPLUGINS \"\${QTPLUGINS};\${QTPLATFORMPLUGINS}\") endif (APPLE) file(GLOB_RECURSE KID3PLUGINS \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${WITH_PLUGINSDIR}/*${CMAKE_SHARED_LIBRARY_SUFFIX}\") include(BundleUtilities) set(bundle_libs \"\${QTPLUGINS};\${KID3PLUGINS}\") if (APPLE AND BUILD_CLI_APP) set(bundle_libs \"\${bundle_libs};\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${WITH_LIBDIR}/kid3-cli\") endif (APPLE AND BUILD_CLI_APP) fixup_bundle(\"${bundle_app}\" \"\${bundle_libs}\" \"${QT_LIBRARY_DIR}\") if (APPLE) file(GLOB_RECURSE _qtlibs \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/kid3.app/Contents/Frameworks/*Qt*\") file(GLOB_RECURSE _plugins \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/kid3.app/Contents/PlugIns/*.dylib\") foreach (_universalBinary \${_plugins} \${_qtlibs}) execute_process(COMMAND lipo \${_universalBinary} -output \${_universalBinary} -thin i386) endforeach (_universalBinary \${plugins}) endif (APPLE) ") endif (APPLE OR WIN32) if (WIN32) if (NOT HAVE_QT5) # Unfortunately, the binutils which come with the gcc-4.4.0 in the MinGW # for Qt generate an executable which cannot be analyzed with dumpbin. # This happens when Kid3 is linked with libav (used with Chromaprint). # Therefore, the dependencies for kid3.exe are not added to the bundle. # This workaround adds the missing files. install(FILES "${QT_BINARY_DIR}/QtNetwork4.dll" "${QT_BINARY_DIR}/QtXml4.dll" DESTINATION ${WITH_BINDIR}) else (NOT HAVE_QT5) if (EXISTS "${_qt5Dir}/../ported-x32/bin/libjpeg-8.dll") install(FILES "${_qt5Dir}/../ported-x32/bin/libjpeg-8.dll" DESTINATION ${WITH_BINDIR}) endif () string(TOUPPER ${CMAKE_BUILD_TYPE} _build_type_uc) get_target_property(_qt5xml_location Qt5::Xml IMPORTED_LOCATION_${_build_type_uc}) install(FILES ${_qt5xml_location} DESTINATION ${WITH_BINDIR}) endif (NOT HAVE_QT5) endif (WIN32) ���������������������������kid3-3.0.2/src/app/qt/browserdialog.cpp�������������������������������������������������������������0000664�0000000�0000000�00000013036�12246035113�0017710�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \file browserdialog.cpp * Help browser. * * \b Project: Kid3 * \author Urs Fleisch * \date 10 Jun 2009 * * Copyright (C) 2003-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "browserdialog.h" #include <QTextBrowser> #include <QLocale> #include <QDir> #include <QPushButton> #include <QVBoxLayout> #include <QLabel> #include <QLineEdit> #include <QToolButton> #include <QStyle> #include <QAction> #include "config.h" #include "loadtranslation.h" /** * Constructor. * * @param parent parent widget * @param caption dialog title */ BrowserDialog::BrowserDialog(QWidget* parent, QString& caption) : QDialog(parent) { setObjectName(QLatin1String("BrowserDialog")); setWindowTitle(caption); QVBoxLayout* vlayout = new QVBoxLayout(this); QString docDir; #ifdef CFG_DOCDIR docDir = QLatin1String(CFG_DOCDIR); Utils::prependApplicationDirPathIfRelative(docDir); #endif QLocale locale; QStringList docPaths; #if QT_VERSION >= 0x040800 && !defined Q_OS_WIN32 foreach (const QString& uiLang, locale.uiLanguages()) { QString lang(uiLang.left(2)); docPaths += QDir::currentPath() + QLatin1String("/kid3_") + lang + QLatin1String(".html"); if (!docDir.isNull()) { docPaths += docDir + QLatin1String("/kid3_") + lang + QLatin1String(".html"); } } #endif QString lang(locale.name().left(2)); if (!docDir.isNull()) { docPaths += docDir + QLatin1String("/kid3_") + lang + QLatin1String(".html"); docPaths += docDir + QLatin1String("/kid3_en.html"); } docPaths += QDir::currentPath() + QLatin1String("/kid3_") + lang + QLatin1String(".html"); docPaths += QDir::currentPath() + QLatin1String("/kid3_en.html"); for (QStringList::const_iterator it = docPaths.begin(); it != docPaths.end(); ++it) { m_filename = *it; if (QFile::exists(m_filename)) break; } m_textBrowser = new QTextBrowser(this); m_textBrowser->setSource(QUrl::fromLocalFile(m_filename)); vlayout->addWidget(m_textBrowser); QHBoxLayout* hlayout = new QHBoxLayout; QPushButton* backButton = new QPushButton(tr("&Back"), this); backButton->setEnabled(false); connect(backButton, SIGNAL(clicked()), m_textBrowser, SLOT(backward())); connect(m_textBrowser, SIGNAL(backwardAvailable(bool)), backButton, SLOT(setEnabled(bool))); hlayout->addWidget(backButton); QPushButton* forwardButton = new QPushButton(tr("&Forward"), this); forwardButton->setEnabled(false); connect(forwardButton, SIGNAL(clicked()), m_textBrowser, SLOT(forward())); connect(m_textBrowser, SIGNAL(forwardAvailable(bool)), forwardButton, SLOT(setEnabled(bool))); hlayout->addWidget(forwardButton); QLabel* findLabel = new QLabel(tr("&Find:"), this); hlayout->addWidget(findLabel); m_findLineEdit = new QLineEdit(this); m_findLineEdit->setFocus(); findLabel->setBuddy(m_findLineEdit); connect(m_findLineEdit, SIGNAL(returnPressed()), this, SLOT(findNext())); hlayout->addWidget(m_findLineEdit); QAction* findAction = new QAction(this); findAction->setShortcut(QKeySequence::Find); connect(findAction, SIGNAL(triggered()), m_findLineEdit, SLOT(setFocus())); m_findLineEdit->addAction(findAction); QAction* findPreviousAction = new QAction(this); findPreviousAction->setIcon( QIcon(style()->standardIcon(QStyle::SP_ArrowBack))); findPreviousAction->setText(tr("Find Previous")); findPreviousAction->setShortcut(QKeySequence::FindPrevious); connect(findPreviousAction, SIGNAL(triggered()), this, SLOT(findPrevious())); QToolButton* findPreviousButton = new QToolButton(this); findPreviousButton->setDefaultAction(findPreviousAction); hlayout->addWidget(findPreviousButton); QAction* findNextAction = new QAction(this); findNextAction->setIcon( QIcon(style()->standardIcon(QStyle::SP_ArrowForward))); findNextAction->setText(tr("Find Next")); findNextAction->setShortcut(QKeySequence::FindNext); connect(findNextAction, SIGNAL(triggered()), this, SLOT(findNext())); QToolButton* findNextButton = new QToolButton(this); findNextButton->setDefaultAction(findNextAction); hlayout->addWidget(findNextButton); hlayout->addStretch(); QPushButton* closeButton = new QPushButton(tr("&Close"), this); closeButton->setAutoDefault(false); connect(closeButton, SIGNAL(clicked()), this, SLOT(accept())); hlayout->addWidget(closeButton); vlayout->addLayout(hlayout); resize(500, 500); } /** * Destructor. */ BrowserDialog::~BrowserDialog() {} /** * Display help document at anchor. * * @param anchor anchor */ void BrowserDialog::goToAnchor(const QString& anchor) { QUrl url = QUrl::fromLocalFile(m_filename); url.setFragment(anchor); m_textBrowser->setSource(url); } /** * Find previous occurrence of search text. */ void BrowserDialog::findPrevious() { m_textBrowser->find(m_findLineEdit->text(), QTextDocument::FindBackward); } /** * Find next occurrence of search text. */ void BrowserDialog::findNext() { m_textBrowser->find(m_findLineEdit->text()); } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/qt/browserdialog.h���������������������������������������������������������������0000664�0000000�0000000�00000003023�12246035113�0017350�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \file browserdialog.h * Help browser. * * \b Project: Kid3 * \author Urs Fleisch * \date 10 Jun 2009 * * Copyright (C) 2003-2009 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #ifndef BROWSERDIALOG_H #define BROWSERDIALOG_H #include <QDialog> class QTextBrowser; class QLineEdit; /** * Help browser. */ class BrowserDialog : public QDialog { Q_OBJECT public: /** * Constructor. */ BrowserDialog(QWidget* parent, QString& caption); /** * Destructor. */ ~BrowserDialog(); /** * Show context help. * @param anchor name of anchor */ void goToAnchor(const QString& anchor); private slots: /** * Find previous occurrence of search text. */ void findPrevious(); /** * Find next occurrence of search text. */ void findNext(); private: QTextBrowser* m_textBrowser; QLineEdit* m_findLineEdit; QString m_filename; }; #endif // BROWSERDIALOG_H �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/qt/configdialog.cpp��������������������������������������������������������������0000664�0000000�0000000�00000023412�12246035113�0017471�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \file configdialog.cpp * Configuration dialog. * * \b Project: Kid3 * \author Urs Fleisch * \date 17 Sep 2003 * * Copyright (C) 2003-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "configdialog.h" #include <QPushButton> #include <QLabel> #include <QString> #include <QCheckBox> #include <QComboBox> #include <QTabWidget> #include <QVBoxLayout> #include <QHBoxLayout> #include <QApplication> #include <QFontDialog> #include <QStyleFactory> #include <QTreeView> #include <QAction> #include <QHeaderView> #include "shortcutsmodel.h" #include "shortcutsdelegate.h" #include "contexthelp.h" #include "mainwindowconfig.h" #include "configdialogpages.h" /** * Constructor. * * @param parent parent widget * @param caption dialog title * @param shortcutsModel shortcuts model */ ConfigDialog::ConfigDialog(QWidget* parent, QString& caption, ShortcutsModel* shortcutsModel) : QDialog(parent), m_pages(new ConfigDialogPages(this)), m_shortcutsModel(shortcutsModel) { setObjectName(QLatin1String("ConfigDialog")); setWindowTitle(caption); setSizeGripEnabled(true); QVBoxLayout* topLayout = new QVBoxLayout(this); QTabWidget* tabWidget = new QTabWidget(this); tabWidget->setUsesScrollButtons(false); tabWidget->addTab(m_pages->createTagsPage(), tr("&Tags")); tabWidget->addTab(m_pages->createFilesPage(), tr("&Files")); tabWidget->addTab(m_pages->createActionsPage(), tr("&User Actions")); tabWidget->addTab(m_pages->createNetworkPage(), tr("&Network")); tabWidget->addTab(m_pages->createPluginsPage(), tr("&Plugins")); { QWidget* shortcutsPage = new QWidget; QVBoxLayout* vlayout = new QVBoxLayout(shortcutsPage); m_shortcutsTreeView = new QTreeView; m_shortcutsTreeView->setSelectionMode(QAbstractItemView::NoSelection); m_shortcutsTreeView->setItemDelegateForColumn( ShortcutsModel::ShortcutColumn, new ShortcutsDelegate(this)); vlayout->addWidget(m_shortcutsTreeView); m_shortcutAlreadyUsedLabel = new QLabel; vlayout->addWidget(m_shortcutAlreadyUsedLabel); tabWidget->addTab(shortcutsPage, tr("&Keyboard Shortcuts")); connect(m_shortcutsModel, SIGNAL(shortcutAlreadyUsed(QString,QString,const QAction*)), this, SLOT(warnAboutAlreadyUsedShortcut(QString,QString,const QAction*))); connect(m_shortcutsModel, SIGNAL(shortcutSet(QString,QString,const QAction*)), this, SLOT(clearAlreadyUsedShortcutWarning())); connect(this, SIGNAL(rejected()), m_shortcutsModel, SLOT(discardChangedShortcuts())); m_shortcutsTreeView->setModel(m_shortcutsModel); m_shortcutsTreeView->expandAll(); m_shortcutsTreeView->resizeColumnToContents(ShortcutsModel::ActionColumn); #ifdef Q_OS_MAC m_shortcutsTreeView->header()->setStretchLastSection(false); #endif } { QWidget* appearancePage = new QWidget; QVBoxLayout* vlayout = new QVBoxLayout(appearancePage); QGridLayout* fontStyleLayout = new QGridLayout; m_useApplicationFontCheckBox = new QCheckBox(tr("Use custom app&lication font"), appearancePage); m_applicationFontButton = new QPushButton(tr("A&pplication Font..."), appearancePage); m_useApplicationStyleCheckBox = new QCheckBox(tr("Use custom application &style"), appearancePage); m_applicationStyleComboBox = new QComboBox(appearancePage); fontStyleLayout->addWidget(m_useApplicationFontCheckBox, 0, 0); fontStyleLayout->addWidget(m_applicationFontButton, 0, 1); fontStyleLayout->addWidget(m_useApplicationStyleCheckBox, 1, 0); fontStyleLayout->addWidget(m_applicationStyleComboBox, 1, 1); m_applicationStyleComboBox->addItem(tr("Unknown")); m_applicationStyleComboBox->addItems(QStyleFactory::keys()); connect(m_applicationFontButton, SIGNAL(clicked()), this, SLOT(slotSelectFont())); connect(m_applicationStyleComboBox, SIGNAL(activated(QString)), this, SLOT(slotSelectStyle(QString))); connect(m_useApplicationFontCheckBox, SIGNAL(toggled(bool)), m_applicationFontButton, SLOT(setEnabled(bool))); connect(m_useApplicationStyleCheckBox, SIGNAL(toggled(bool)), m_applicationStyleComboBox, SLOT(setEnabled(bool))); vlayout->addLayout(fontStyleLayout); m_useNativeDialogsCheckBox = new QCheckBox(tr("Use native system file &dialogs"), appearancePage); vlayout->addWidget(m_useNativeDialogsCheckBox); QSpacerItem* vspacer = new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding); vlayout->addItem(vspacer); tabWidget->addTab(appearancePage, tr("&Appearance")); } m_fontChanged = false; m_styleChanged = false; topLayout->addWidget(tabWidget); QHBoxLayout* hlayout = new QHBoxLayout; QSpacerItem* hspacer = new QSpacerItem(16, 0, QSizePolicy::Expanding, QSizePolicy::Minimum); QPushButton* helpButton = new QPushButton(tr("&Help"), this); QPushButton* okButton = new QPushButton(tr("&OK"), this); QPushButton* cancelButton = new QPushButton(tr("&Cancel"), this); hlayout->addWidget(helpButton); hlayout->addItem(hspacer); hlayout->addWidget(okButton); hlayout->addWidget(cancelButton); okButton->setDefault(true); connect(helpButton, SIGNAL(clicked()), this, SLOT(slotHelp())); connect(okButton, SIGNAL(clicked()), this, SLOT(accept())); connect(cancelButton, SIGNAL(clicked()), this, SLOT(reject())); connect(cancelButton, SIGNAL(clicked()), this, SLOT(slotRevertFontAndStyle())); topLayout->addLayout(hlayout); } /** * Destructor. */ ConfigDialog::~ConfigDialog() {} /** * Set values in dialog from current configuration. */ void ConfigDialog::setConfig() { m_pages->setConfig(); const MainWindowConfig& mainWindowConfig = MainWindowConfig::instance(); m_useApplicationFontCheckBox->setChecked(mainWindowConfig.m_useFont); m_applicationFontButton->setEnabled(mainWindowConfig.m_useFont); if (mainWindowConfig.m_style.isEmpty()) { m_useApplicationStyleCheckBox->setChecked(false); m_applicationStyleComboBox->setEnabled(false); m_applicationStyleComboBox->setCurrentIndex(0); } else { m_useApplicationStyleCheckBox->setChecked(true); m_applicationStyleComboBox->setEnabled(true); int idx = m_applicationStyleComboBox->findText(mainWindowConfig.m_style); if (idx >= 0) { m_applicationStyleComboBox->setCurrentIndex(idx); } } // store current font and style m_font = QApplication::font(); m_style = mainWindowConfig.m_style; m_fontChanged = false; m_styleChanged = false; m_useNativeDialogsCheckBox->setChecked(!mainWindowConfig.m_dontUseNativeDialogs); } /** * Get values from dialog and store them in the current configuration. */ void ConfigDialog::getConfig() const { m_pages->getConfig(); MainWindowConfig& mainWindowConfig = MainWindowConfig::instance(); m_shortcutsModel->assignChangedShortcuts(); if (m_useApplicationFontCheckBox->isChecked()) { QFont font = QApplication::font(); mainWindowConfig.m_fontFamily = font.family(); mainWindowConfig.m_fontSize = font.pointSize(); mainWindowConfig.m_useFont = true; } else { mainWindowConfig.m_useFont = false; } if (!m_useApplicationStyleCheckBox->isChecked() || m_applicationStyleComboBox->currentIndex() == 0) { mainWindowConfig.m_style = QLatin1String(""); } else { mainWindowConfig.m_style = m_applicationStyleComboBox->currentText(); } mainWindowConfig.m_dontUseNativeDialogs = !m_useNativeDialogsCheckBox->isChecked(); } /** * Show help. */ void ConfigDialog::slotHelp() { ContextHelp::displayHelp(QLatin1String("configure-kid3")); } /** * Display warning that keyboard shortcut is already used. * * @param key string representation of key sequence * @param context context of action * @param action action using @a key */ void ConfigDialog::warnAboutAlreadyUsedShortcut( const QString& key, const QString& context, const QAction* action) { m_shortcutAlreadyUsedLabel->setText( tr("The keyboard shortcut '%1' is already assigned to '%2'."). arg(key). arg(context + QLatin1Char('/') + (action ? action->text().remove(QLatin1Char('&')) : QLatin1String("?")))); } /** * Clear warning about already used keyboard shortcut. */ void ConfigDialog::clearAlreadyUsedShortcutWarning() { m_shortcutAlreadyUsedLabel->clear(); } /** * Select custom application font. */ void ConfigDialog::slotSelectFont() { bool ok; QFont font = QFontDialog::getFont(&ok, QApplication::font(), this); if (ok) { font.setWeight(QFont::Normal); font.setItalic(false); font.setBold(false); font.setUnderline(false); font.setOverline(false); font.setStrikeOut(false); QApplication::setFont(font); m_fontChanged = true; } } /** * Select custom application style. * * @param key style key */ void ConfigDialog::slotSelectStyle(const QString& key) { if (key != tr("Unknown") && QApplication::setStyle(key)) { m_styleChanged = true; } } /** * Revert the font and style to the values in the settings. */ void ConfigDialog::slotRevertFontAndStyle() { if (m_fontChanged) { QApplication::setFont(m_font); m_fontChanged = false; } if (m_styleChanged && !m_style.isEmpty()) { QApplication::setStyle(m_style); m_styleChanged = false; } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/qt/configdialog.h����������������������������������������������������������������0000664�0000000�0000000�00000005545�12246035113�0017145�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \file configdialog.h * Configuration dialog. * * \b Project: Kid3 * \author Urs Fleisch * \date 17 Sep 2003 * * Copyright (C) 2003-2012 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #ifndef CONFIGDIALOG_H #define CONFIGDIALOG_H #include <QDialog> class QTreeView; class QLabel; class QCheckBox; class QString; class QWidget; class QComboBox; class ShortcutsModel; class ConfigDialogPages; /** * Configuration dialog. */ class ConfigDialog : public QDialog { Q_OBJECT public: /** * Constructor. * * @param parent parent widget * @param caption dialog title * @param shortcutsModel shortcuts model */ ConfigDialog(QWidget* parent, QString& caption, ShortcutsModel* shortcutsModel); /** * Destructor. */ virtual ~ConfigDialog(); /** * Set values in dialog from current configuration. */ void setConfig(); /** * Get values from dialog and store them in the current configuration. */ void getConfig() const; protected slots: /** * Show help. */ void slotHelp(); /** * Display warning that keyboard shortcut is already used. * * @param key string representation of key sequence * @param context context of action * @param action action using @a key */ void warnAboutAlreadyUsedShortcut(const QString& key, const QString& context, const QAction* action); /** * Clear warning about already used keyboard shortcut. */ void clearAlreadyUsedShortcutWarning(); /** * Select custom application font. */ void slotSelectFont(); /** * Select custom application style. * * @param key style key */ void slotSelectStyle(const QString& key); /** * Revert the font and style to the values in the settings. */ void slotRevertFontAndStyle(); private: ConfigDialogPages* m_pages; ShortcutsModel* m_shortcutsModel; QTreeView* m_shortcutsTreeView; QLabel* m_shortcutAlreadyUsedLabel; QCheckBox* m_useApplicationFontCheckBox; QPushButton* m_applicationFontButton; QCheckBox* m_useApplicationStyleCheckBox; QComboBox* m_applicationStyleComboBox; QCheckBox* m_useNativeDialogsCheckBox; QFont m_font; QString m_style; bool m_fontChanged; bool m_styleChanged; }; #endif �����������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/qt/fixdesktop.pl�����������������������������������������������������������������0000775�0000000�0000000�00000000332�12246035113�0017054�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -n s/^Name=Kid3$/Name=Kid3-qt/; s/^Exec=kid3/Exec=kid3-qt/; s/^Icon=kid3$/Icon=kid3-qt/; s/^Categories=Qt;KDE/Categories=Qt/; s/^StartupWMClass=kid3$/StartupWMClass=kid3-qt/; print unless /^X-DocPath/; ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/qt/images/�����������������������������������������������������������������������0000775�0000000�0000000�00000000000�12246035113�0015603�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/qt/images/application-exit.png���������������������������������������������������0000664�0000000�0000000�00000002156�12246035113�0021567�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������‰PNG  ��� IHDR���������Ä´l;��� pHYs��Â��¼Ï���tIME×  Ò¡a���bKGD�ÿ�ÿ�ÿ ½§“��ûIDATxÚµ’_hUÆ¿ûgfw6Ù MI¤¤MI— `#DHEÒ†àCŒ…š¡­4¢(Jüƒˆ +bOyRúÔ§úXi(H‰¢ÖÚ$&6kÂnSEÈn²»ÙÙ¹3ã™ÛlZÖ"âŸs¸s¿ßùöì࿨ Bí ÕWÎ ™sçv{³³§×§¦zT6˃Šôwû{öí ¶õ÷ÿ¼º¼Ü¿{|<%Aå-/Mî]]àž÷×ép÷ò¤DÁ÷ÛV?=ÿ€! Nž=ÛoØU.ãß”cei¹Tœ_F&sëg1†t]@Ϲ˜ïß=9çú^X;××ÁhÏLmÛÜ{$wc!ÂN$°ÿÀ0}áê’I NÐ\K ÐÜ/"º°�nšð*WHP›`EJìÝK>®Á]4Ànm…*•ô;’îKtÖÙ×§¡tW{*ïcUàBeÒ™3°Ói ékc[o/ÐÙ eµüŽ$zzày”R(¥RÚC^ Ö‰·VASÝbQƒÂº:1ûÆÆ ›šôÙ®înÜ”\÷>Bu¯VV079‰Àu¡9ŽïÎľ”zåŠÖÖ0wê}¨©¢Ó7´wbûaJ-%…?€Ê­Á¥¤<›Ï¡lÛw$k‹‹–#f!j€$O½ƒ=Ï¿ ¿¡©2@'å2¸qzÒ³áäÖ‘ÿ=‹äÌO¦·ìÜûÎ\»’XQx}„dÁ¯bú‹ÏÑþø“Z_¹ôÄ¥ópŠ.JÄ)†!ÀbÑêU€ć)<UwòXì)t 딤-ø®'à{V¨ D òIòK^ æÔ1H ­ˆeÂ} ƒBzu7çPÿÛ|8@Ã#Fß„Ik3l„`3ã68œ¸SëÅ· ú$MáœtüÃ#0'FÐY$¡uÿᣨ}ù òÐ|È?'fºã¤Æ×ßE÷±gB£ÇR?B<Œ·Qk8¾}5‰h4 Ó4Ñ~äiÜûÆIíFxó ¹eN©uý{dß:æÚ`álSœ4(=‹H$¢ï557ß‚…œJbÚ ®Á~�L#ÿÍeØW¿ÂòKƒpK6Ê (:@¡8eÀµü:>53o¿ÄwÏk/¬Â…$YÛ­~¶0#8Á+c‡PbA„ gs(ó@™“ó/ bÃF  "V ß[S°£Xür}{Û£ÎÒ/&"0 J² êI’#,=@IÓ™2 j<Xf™<ƒ«ü) fŒyï56ŽØ…Â'¤>ZÄ?(!3*�¦¥Žãÿ¬?�–°Í÷¢Öl����IEND®B`‚������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/qt/images/document-export.png����������������������������������������������������0000664�0000000�0000000�00000001471�12246035113�0021451�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������‰PNG  ��� IHDR���������Ä´l;���sBIT|dˆ��� pHYs��v��v}Õ‚Ì���tEXtSoftware�www.inkscape.org›î<��¶IDATxÚ¥”¿OSQÅÏ{}DkS‚tÐ2• &„Aþ‚(‹F!·º90‘˜(– NÆ4Mˆ00Ib&ü 1cÒ Ah•ôQúãµ¶¥à÷^ÞϾ;x’“sóòò¹ßž{_%�!òmüŸ¾]\\ìÁ&…A3Ù܆,K$—)QÊ ñ57H¶´ëzOÏ ˜Cœ`Ób0©pzŠd2‰@ �Ç#¡Mò%˜OH/0°À ¨¯üº³ƒÑÑQh• "‘Ȥl�Ìéd§x(©O0z{‡ o---½‚%@b6`‚uWWB¡¶¶¶ ( fgg¼^oxqqñtIäûyUÝ0ádJpô{||Œýý}4›M4 îz½Ž‘‘ ¡V«©ñx<K¹FßpðÉÉɆduimà´U—Ív•Ëåß±X¬|vvö–WA0'À9úï$¿ßcjjJð\`Áì7À9•‘ík×ĉDÂà•¶[Íår888`ݺ:ä///ÿjµZë ïØ €ûúúص‚¡t:l6Ë¡š¦ÕVWW¿ÓfŸØÁ™×MD@êÊóósèÀöhâÏóóó¯¡Ë11Œ´ž á²þ‘ÐmB*•B&“Ù]YY‰º> tš˜OX­V1<<ŒÍÍM‚«ùµµµÚ¤˜�ñÉ» ù|ÅRÝþnô÷ËããwŸÒó/Âÿ èëÁy¯Ô#ŠÅ"B!º%ÙúääùB¡ð‘jiÁ’a1Ü—J%ø|>$··ë33_å>´ H ^C¥¢AUÕÒôô£9‚&Z„X|âÝ¿:ˆ 8<ü±;1qï Aßÿ j´p“|‡ìAgý$']ŠÀôûù z…ì%_eÖ×׬ä–ÉMr•\Óó[ë©éÙü '.¶(üp����IEND®B`‚�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/qt/images/document-import.png����������������������������������������������������0000664�0000000�0000000�00000001426�12246035113�0021442�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������‰PNG  ��� IHDR���������Ä´l;���sBIT|dˆ��� pHYs��v��v}Õ‚Ì���tEXtSoftware�www.inkscape.org›î<��“IDATxÚ¥•ËoQÆ¿^Z6Cb%ÁÄÚG qáÚÝh”¤Kÿ…ÿŠ Ý˜4Ñ]÷vEÓ`Jµ±PÝ) Ïë9¯x¡ OòåÜ “ß=ó}3  ! —¦iqj[ø¿úâV|¸uUµ>èºF‡è˜vºî<o¤.WÀ0ž«À‡(òðz½ØØÜ„Çãq€j°®«Àf±€{Á âÌ4AvñÄÄÀéÚ鲃Çã1 ÃÀÆú:Œ@�_ÏÏAtÚ’pIk§/?X[Ãéé)úƒÒzÅïÇUµ ]žR5±œE6›ýD›Ç.—«Ëê÷ûžh4aÑRÉ$&“ .ËeÉ·Û M Ñ T.ŸÏg‡÷$“Éx©Š}eK&Ô9˜H8lïõ¹ƒÕåL5óV8·>TÁÕ`õ vxl [A5W‚þò8‹ÙGØãTÊöô{¥‚P($ƒÔpÙKYTÏ((qxx(ºÝ® —Eäóyaš&¯mF#[ä¹RÌС¨b¡€íímN¥R ½^ô¤¨¦å>¯Åó+Ün·AS¢Ùl"™L‚lRB¹ÄÔîËÃK$Èår<1{즄’% \l \\|+ø©ô¸Óéº}A ºø_i:–ý]­Væ™),ËÇÇŸÛ@ À«/|¼¤”UÉØö¾Õjá~<˪öÓéûFã# 8Е\GKà æü~?r''ý½½Wo*ëA»7¿ àŽ·××ÔëõöîîË}‚¾'hkÙïñ˜¼ëá†â<ÊåËÒÎÎÓ×}+C¹Tÿyw©="¹psý åØSÞ̃Æé6ék¶^‘:K' IìcoÖñzÖ;³>ü Û}Ç~'ûñ����IEND®B`‚������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/qt/images/document-open-recent.png�����������������������������������������������0000664�0000000�0000000�00000002034�12246035113�0022343�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������‰PNG  ��� IHDR���������Ä´l;���bKGD�ÿ�ÿ�ÿ ½§“��� pHYs�� ×�� ×B(›x���tIMEØ$Èýƒ��©IDATxÚ•[LUÆsYXXX. ívMÍr1B_¼€ñI_š“†ÔgŸšú FcMÓøXÃEIHû¦i4Õx샖Vk©Ü„m]v¹ËË^faoì츙,ŒÛcü’æÌÉÉïÿÍwÎä’‰ÿ"Ù*òÔÙ:ŠJëÑ´a¿‡ñ‹ “/öÀÝÝÝ/vvvžÖv'ãihxl‘ ÃË®Ž–&·mg]°×¸÷…ÒÊ=ÿcò|ÿ÷Ÿð€L^Õuõõš¦!ŠbŽ'pub‰Kãiz_;ɳ͇°Š*"ØNÑÀðÜ£g{‹öׯòKφ†DAÔ¡ûUH‰óê‡S¼ò|+§Ú\)—X_ö­â¶C÷ñ‡immjÂvø]@,� ¢°çV4 ¸2ò'–J/ä VI#O06ö+Þy/±˜Â‘2Sín¤òÊ.Žu= cH‡D‘gT•Qo‡tq¨L"‘LŽ„™žžÆç÷á™ñäÅq?y‚Ò »5Vqôñ]°)SÝi¾A6f; Öb´,©TŠh4ÊÈõë$‰Ü|1éÜš3<TbY¨1¤Ã4؃Šù&’$ár3ŠŒ¥° ªþeýïSUY…ó°“r»¯<aâ?MiDî‰2œP}l±Xx¦ÑÆbd›ÏÆWQ³Pf+ÃåtQ[SKIi)Ám•Ë·–sΣ‹L9'ß6b16RÊŸnvÓ~g‚Ñ¿PÓ)N4;p”!ZŠ˜ $èûa›%Ãï×Ȫkû òÝ-—ËŹŽÞþy• 32WfÖq—[HeþØRÙ.±ßýb(të“O„üÏÍ3Jã±¶Vú¬¾¹íáFØÂ’"#‹ÐVšæ¹£Åx‚%£ƒ°hFa”1gµZiii¡¡¡—"‘²,S[[‹Óéäòvt P Àº[¼_CTSSƒÃá@UõÓa¬ à~° j†ëÒa™LF?ÏÉdRo ÉÂQ%½/˜|ƆÌrPýgP…µµuÂá°ÑêÚÚý½=_“—hr\àΔ¿î.‹áóùºú:âñ­±wΟ{y3Ü0ììì …Ø mêÐÅÿÝž¾Þ“SS¿�›r5ƒ%FuU5“““ƒ};´bºdL\L2ÁE‚›7Þëïïºvmdà@°×ëÝœ½ivj¼gsÐx"ÉüâÒüù³o¾±¼´¼Îú—)¨@†Ñßゼy2)N����IEND®B`‚����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/qt/images/document-open.png������������������������������������������������������0000664�0000000�0000000�00000001434�12246035113�0021070�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������‰PNG  ��� IHDR���������Ä´l;���sBIT|dˆ��� pHYs��v��v}Õ‚Ì���tEXtSoftware�www.inkscape.org›î<��™IDATxÚµ“ËjA†ÿS§ú6—ÄDР(A‚èV\$H|�"ø®t%¾Œ‹¼ƒä DÑˆŠ‰DÐ$šÛLgúRå©t:¦Ó3AŒþðÏTMM}õŸÓ]d­ÅÿÆ!Ñy�çÊ)þLVüEB. ‹...-͆"9JL{F9.TNššº  ~0óþíN:1}¤4|Ï“oå@UUÕÀ̬p@û“8»pëÚn\9 ­ÙA]Úº•r®ŽÅgo>|rïñÂZ Ì»(2TZà~¯ºX?TÍäåQŠ“|´Ö ÏcJs g:-Æ%ìðƒF)·Ï÷}ÔÀÌ>:q†¸g ªÐ2] ë ß‘}:`Ô[Áš:²'�­´ Tœ°Öýklõ2t“¼²±ÜÒÊC)ºúhÎXk ÿP³Ú•|gr|s¸éY�&OÑ ýZ"P_�, uâJi¬n¦ë³sŸOèÐ÷²×Ë©=5Áƒ<FZê7˜Ž¾È¶øÀzÐcu#FàëL7¢ Ë Ü 5ò\ÀlбR4(n Ï÷óÊ6W¥€5Ö6À0èêvü$àt#�² 0 oÅ ^¤©ùpJ’Û=ƒ(dDF·¢`E·›Á2g"Ï ¥–­œ¾w«úWT|Ÿä€ ±ÉùÊåÞË5ï’˜?Å;éõ€S0 ¬‘VÀ$`!'nMBt’¡‘“fÃ͆÷QG´³(¥\eY^lŒw1ú·ÂZ ouê£"f—ت¦gÞhê­¼Ð<†<ÏÄÅÉßVÖþòýUКPw^/Î?{:>}×DÑ0»ò#WM;JÌ÷W/Ÿ“›p¢Ñj]Ú{8tŒgºÛÛï„ù¡{�Ú8¾¬xK˜Ù/qåùTÚÕy©����IEND®B`‚������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/qt/images/document-revert.png����������������������������������������������������0000664�0000000�0000000�00000002116�12246035113�0021434�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������‰PNG  ��� IHDR���������Ä´l;���sBIT|dˆ��� pHYs��v��v}Õ‚Ì���tEXtSoftware�www.inkscape.org›î<��ËIDATxÚµ•[lUÆ¿3³3»Ûí.è•m\(m#­á)mR#!jMŒ&*‰/>Ð}ðB4¾ôÁø`Ô¨/JŒ&’Àƒ A‚ÆKE U¨¤-)¸@K»Ýº°îfa.{æœãÙi¦dºÔŒ_òÍ5óûóÿÏ…!ð(àmBâ�ñßtMôÀžâ†aPJAˆ[ ¤dHÍ_ßNs;¡ªB×õN�e`bÙ6LÓ„¢iÅû]^È“¤ðêúÁð®Àò\Y}'UU ›Çåó? uq7s3UÄP—XUŸÁÒeÍnaLîþüígߟúóøœ¾2‚ïö½†õëѸ¬k¯FtiVÁD6=‰ôp~ür ÷?´ «|N™&ûúºD0ÌÅàoã‡6lÚñ¤SøÛzÚQYƒåØ:~ýv /;ØùÆAhzȽMÓº„ƒnbNM<ú|‰„"=Õõy´=¶D±À ,( [–#‘ÌãÓ×»ÐûÞI¨jÐß ÆeèÞÚäN„Q % ÎPœI¡˜IYÔpzU‚õq�5õ<¼ù^ìw'^xë+?X$ŒÏBqã€9ä/ ÂÎçp=Cp#K 8¨‹žÅ¢ê%ˆÞ×@4ê͵83:‚±ÓßûÁœs8”J(Q ¨•EþÚ/(ýC1Ô´lE}G¦Ç/àÄèQ¬ËN£­ð5îY»j¸Â…lÞÔ„O>Ø=Ì8µAÄMmÎA $ÏoOíúÈ÷œÚ»ɉ½h½p ‘Ön,Ž)>°b›7aÞHʤ90n¢¢¶áÚVlØ®ÀHDzâœïéyñꞆVµ Ü$L·âÕaâë9~åÈá«ËÕÀ8!ZP—é9%5dàü©ÏÐøp.µÐÐд” hÑ%ÜB<ÎŒˆ9pßþÔŠ—¶5¿è¿Ú‘ÎdÒ¢) šîP„#‹Ê^Y5¼Ó™*èŽ X•Pƒ•°— þ ž„®¥:-Û†aÓ¸%ŠÅ¢ ” Çq\3ÆçÜõ7>ö¶ÝãÃ'ûí–•+(q<û:N\ iæÐ;~`þÎL"‹Í…:;2äô¾üÊ#c—’§|ÃC™J9UõªÏ¥¶¢™…ŠÌBGOÓW÷¼¹ãÄÀïÇÊÿ e* (äöy6(gL‚˜Üg‡ƒr¨1ŒY½»{· ‡Oå=Þ({9¿¯Ò¥5̱…C a¦ÅÏ?Iµ4¯\ë^»€‰\ÜÍ?oJ^7Ñ?'Äåch°r����IEND®B`‚��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/qt/images/document-save.png������������������������������������������������������0000664�0000000�0000000�00000001436�12246035113�0021067�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������‰PNG  ��� IHDR���������Ä´l;���sBIT|dˆ��� pHYs��v��v}Õ‚Ì���tEXtSoftware�www.inkscape.org›î<��›IDATxÚµ•]kA†ßÝM‚oô&é—”šŠ½Åký¼‘Z?ÀoêMAHñ¨?£‚¿@èHHJAk‘‚Ûš¸™ÝYçÌÄa– Bßp朰sž}÷lvãåyŽ£P‰–×®oGQ4ë>î®<BYe¸'ô=�<O¯ Qm²‡„gX½)$ææævÞ¾7¥Á6˜]^^Fwk ç/]ʼnãÇ0ÄÆ‚,Ü�U˜ü+bè~ù !6>lLZÇRJ´Ö[hži‚g™2èY°…JÃ3‰N§£ÁRæ°`šs¦€t‚–Ë>6>uqݼ09õR(–DÁñBsAŸÀó8ðpëâ<\©C097Yª…æ+„$·Æ±EǧK£M,Íðqs‡Ñ•Å)d2'¨q­X®c:`ÀªfI†Ëç¦`ÝšÕ¸Ú—Cçqj›QÈ8ÎmæØìüÀa´8{Ê‚5'wÁt BPiŸ>9É S;޹ ~vÓ\4ĹÀ°„§aV:r#dd̰\Çf¤Õûw0†ˆå:¶£[dpÔñø}@èÉû«—¯VQ«Õþëléöúýžîž4?O!0ýT8ç¶) C<}üiš¢Z­âÙ‹ç°r^z{{%ìïÛ~!²"8ã¼ÐÈ¢ˆš©va ¬CpN@ê‚€XX z« ¡ÃS®ƒ µ‘pnßhÔ_o4ðuç›só„”1c~©¤¿"IIÁâQQsì[¨,fjoLµvÜïõŠŽ}/¸÷3 [µ‰ ‚kÐʃ‡HyªoRO58@ë~ð;BÌbð4¯T°»»[w¶»oNÏÌ <Øo!‡ßn·QQíó/¥ Wß(M]3Æ °\Š(ٟ餻P—ÿïâ€����IEND®B`‚����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/qt/images/edit-select-all.png����������������������������������������������������0000664�0000000�0000000�00000001167�12246035113�0021266�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������‰PNG  ��� IHDR���������Ä´l;���sBIT|dˆ��� pHYs��v��v}Õ‚Ì���tEXtSoftware�www.inkscape.org›î<��ôIDATxÚµ•KKÃ@Çw£gÑ õ¦«ëû¨Ä×EJ¯úeÑO >=iÁb+ÄGZ¨mBm mHÒ®Û»YÜÄÅaf“Ùßüw’6!þÃi! àÐþGÞ3úBÁÔyM?Qˆ›(À‰G…6vœ,ÜÈ[KsóÖ!_À¼‹`Î<˜ÀêðôcÁ…ø@{:Z=Uû€â{Çìø,r W¦B ŒˆSз`YšìŽOàÜúê^'1ˆ•S¸˜=¬Ý£”«Rœ+³µð©—âSÑòTˆ*¤×h#>§k9~Žbç0Éò@^i_Wà+K2Šºâéq# ¨™Ë{;Ûä`¢øàüÇÇ^Ÿ¦×åàÕ™A–‹jå&‚Íäh^?gl’&Å—Ù¼—ba$!òsÿÈ °oï'x �µ-Ø•w0=Üì·W.•J:¾]``a£ó:y©eùC6æÃãLu"qfD"‘9œß{ƒ]Å<ˆÐhGƒ¦RI+ݘÐ4-é?cW±ïC«V«àîöšA77·fT5}ÊshW:ü±r¹Œ Ã@¦i"˲ˆÛ¶0 Õj5âõšX,†âñÓJ0Ø=êì]á{�‰Q!ÅbQ_XXQÕ›¸_Ä…¿ùæåð¾§ï >�™¯ÔÞ¶‚;†����IEND®B`‚���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/qt/images/go-next.png������������������������������������������������������������0000664�0000000�0000000�00000001654�12246035113�0017700�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������‰PNG  ��� IHDR���������Ä´l;���bKGD�ÿ�ÿ�ÿ ½§“��� pHYs�� „�� „ªâcy���tIME× �ƒ/Ò��9IDATxÚÝ”ohVUÇ¿çþ}ž={¦­Á²½±Ù 4*µ(i$Š6ÊE %Tì…,|„"‰)ôBd¾( Â^:TЬË)âD¦Ó)›kÿsÏžûœûœ{ÎùõÛ“=̦"}/ßËápùÜïýžsþ3‘Ët\êo_øáÁö'ßùò܇Ä`,Z€_žƒ§/'=ùO]7µ«ëûuSÈù×· ¾¾^̱ÍýWæÖí4It¼¾©å±—ç1>.Q K^ï7ÍmÌèhXùõâ{®¢ö¥-ެ– ¤²Jé=_ì@b,ˆ�âKF1ÿt•®÷ËÏ!œO.\¥îšx㶯Z§U†Æel")Ž iUE¾  %;fа¼ñêL±ð™šfsªaå7³îš¸~ÑGÔ¼ù]ÈÄÂq]@°á€þ*›Ól k [kÑ›1ÚÎçrÒlèúnÍ·¸%·<¨yz[ÃS³Pˆ5”´ ÛZÁ¦ÒØð¤f'‰R [¤ñDm 7UÌ^1³zΊG/RÞd'¦´H^˜Á²ùN<ï ,;7<o,¢¼Âͱq òÎDnl ÆÍ¬™óëßjiœ ‹ 7Pýh ²UUH§Ï· æ:h8‘T£ÈI£H"–R¸óT:…(Öàõ!áú˜Ì#l©†H~€P(ZÄŠ0f¢rJÝ{~�/`{>|ßGE&Høû<¯ûЦ^ïï?lÁ{[‘™^‰ÊG²ÈNË¢‚„×q‘9‰µp] !Pkí!]™B>ÿº¯õ ¹üþóîcwìŠyË6îì¼²D ß²D¶º ë?ÞÏ ÃU$ˆ‹Š]„"…ßÎü‚¾žÞVÇõ× þº·©Üñ¹#ûÞÄ?([û…aÀÉuéË|ÉõãÔ±ÖHÆÉÇ [вÊ੤0B@?Çù¶“gᆫrG/¡¬û;aD:Äp4‚#-{Mÿõîœr{¾ëhòÀÇfDä,ݺÇȾ\lo»š(µ:¾qæ4†ê¼} ]÷ügáãÏeñ¿ÖŸÞ¼¯¹ÈðA����IEND®B`‚������������������������������������������������������������������������������������kid3-3.0.2/src/app/qt/images/go-previous.png��������������������������������������������������������0000664�0000000�0000000�00000001613�12246035113�0020571�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������‰PNG  ��� IHDR���������Ä´l;���bKGD�ÿ�ÿ�ÿ ½§“��� pHYs�� „�� „ªâcy���tIME× 4$z,��IDATxÚÝ“IhA…_UwÏÒéL¢YÅ-&*Š ¸†¸€¨x4ŠûADA<EoŠzS‚8—“ŠBŠ‚\.n¨Ñ˜‰“dzzºº«; ‚&*zò5 Šúê¯÷ÿÿ_S7_X¼öàÍûíÏZ1ŠÌßnJSÉ‹-UN4Í®±ntu/°ç¯À ›Ò5"(´7ͪ]³c]ñÎ¥oà¯*ž¶õÒšaíWÏ®iœ;‚s¸ÚÌÍŒxÙ‚’'Æ·l^5‹V–— ïA0,îfQ1o»A¨ bÄÙ�úœ–Т?…n¹T%,š^~hÛªzš°(Ü‚‚šEæ^<ßÇE1ÇePŠû¢¬$&ö½Ð-òC“¶\Üj[¤uÅÜŠ²ºq)¦ Ã0@ih B†(¥" !´9x€1mW:ðöîYb~7F%€:[í¨]ÓKáØy]¥iI˜¦¥á*‚JBx•RF`pŒÁÓQ}Î |˸aCÛÉY×ä1ÁŒúZ>c�!ˆÅ�K §ÚDÃ)h†Ò $Pô>¸èíËà}Ï'hÎ7°”gÊÍûèùT€ã”ÂvضB<A`Y!*¡@ ¤BÀ% Eå^ô<ä]ް‘J ƒ hå^t¦êWœ,°òþ\~‘WdÄc®¯à1À €b À8ˆ HE¢ÞSB¢Ë$çQƽ=¯À²¯Ž}͸çÎaÀ¾qË[:3Yï<5ŒZ'U5ÏÒŽY–Ž&¦«7£H„ˆÖR)øz/l®´v¢êÆ=URm•UÕÍ3ç,@*U†d2D<®áLÓÀ©Íáü‚˜I+¡×IŒ­¬€RòÖÇîëëFPå‚Üß­ß3 —¬t&Ö5 ™ÐðD<ªúäþf°ÌËÏSŒ LwýO¯¶¹ƒýóïu^~ôäaW4 ¦Ž%®ŸM¨…ÑDñ ¼¸ýZr±ìñîã×Ò§Ä@_1ðÏ”¿xIiÝÒ7ÍGZÕ¤¦õ £è®åC?»&íý` ¹Ïýì»ü7úÇ^Päf›����IEND®B`‚���������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/qt/images/help-contents.png������������������������������������������������������0000664�0000000�0000000�00000002036�12246035113�0021075�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������‰PNG  ��� IHDR���������Ä´l;���sBIT|dˆ��� pHYs��a��aÁ0Uú���tEXtSoftware�www.inkscape.org›î<��›IDATxÚ…•KˆU†ÿsêÕ3=Ý Ó“Œh2A IpИ(† ŠY¸Õdå ºÜãVĵn"ÈBÄ"*Ñ€¢ 1:>0N¿œî¡_uÇ[·ª§0Ná…ÓÝÛÅwþúë4Š拈Î9iº2ç_ܼ§Vo°±Öc±Öi¶l.â®´É­Öö‡ëï½~sÎ QBßÚÚº¶±±0 /4,… 3@ ®€µ×®Pì/~pcxâ»+?¾óœÉÁåzzss­V J)<øÌËÒ<z?”¶˜ÌÄJv’ÄFÓ4Û";£0•ÃÍõõVÀî`ÛétÐï÷‘¦)êg|æÔÝXNÝVó§òºÐ`èHœÓò,ñBg\¹œFQ„ ¼c"ÆíÅÉ5ÆÊR€ÁXöá»c`y‘½Ü<*BZf\.U€‹ ! l÷‰Ñ¬'¸o-Aª‡›!vv._b¢¤„Í*ÁîÅyWAèàĨŌç;„¿Go^í80À \8·‚—žZÅÖÕ.TÀv¬ã83{ÇÌYN«»ÇñK'3ã2¼~ôݯ={'îªáV;-ÀTy¸‹Súµ›b01Pš°ºÄèòI8ÒŠ­Å„]3.¢À¬Ò±ˆä3yÇí¡Å—]h&„z wM8ºßz ·v”¿¯È¸: cL‘3ûŒA€¶v:J¥ðÄ©:f¸xmèÜ“7Tgl’$ñ3ì3ØG’G·?Nþe>z|o2À43˜áà(¬sZÌpæ0Söê×Ù“u¼ÿõÉ`!b¤ uZ1Ç6‹b:zÇ>â_ðFñÓŽ‹+`™`ª ¥cfFYT€KçôWc QhaÜ6ð7¹ªŠÂ[ "HÆÚÿÈÝùó,oÄkC¸‡ƒHÞô­–ÿß1‚ÖÚü!½kƒ‡ï­áÉE\¹1Æ÷*XX·Np ¸€úŒÁ1icAeÆxh=ÆJ±Ö$|ó»g‘P +Á2(Œ¡¡ÌùÒÇ}<r|ŸÝœ@[¶ßX\UU¯×ÃÞÞžŸåéx$6…ÙÜS¶ýðÛ¡ßççåwN¥ üy»Ý–ÙlFãñédÏFJ³gÎʼ8S“ÑÀD;1Ýn÷§Ç”R³þö«:/À¶V±ˆ›Q4WŠÖ„À0³înõ)½zéhñ×4þ-™[‘É‚����IEND®B`‚��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/qt/images/preferences-system.png�������������������������������������������������0000664�0000000�0000000�00000002115�12246035113�0022133�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������‰PNG  ��� IHDR���������Ä´l;���sRGB�®Îé���bKGD�ÿ�ÿ�ÿ ½§“��� pHYs��¯��¯^‘���tIMEÙ2-áïý��ÍIDATxÚ“kL“gÇÏÛöm)£Ò•DJiUp‹ ¬´##c†¹v£3#Ë\Â0[6/! .Ûõ¢6Y„ÄQܲ›’˜R41&›ØQT>Ì&X dÖ®èJ—µïåÙÁ„Ä3¶=É?yrÎ?¿÷¼ç<©eµZô&“©133³œã9q4ú÷ƒP(xÞápž½:z-é@››ÔúžøÇ’H$BfçfÉÍ[¤ïB/o±}ÛŽQÊÔw+ë/¹ÿ $þoœ,Å–HkëQ®¬ìû4Mÿ€–7QBH•.{)kKv¶LÊq0l(Š‚A·ûÌøø„Ë»â¤ÀÊ_–¿533„`Xü€J©œÆÚ“h:`iQQQÙôôïÀ² Š–aaÛöm•k×­¥Ÿ5š­MÙÂd©ooªx­ªªÊ †(i†T*à!#SZˆãÙX¬-mll\ß°»áÐÂÂâád;¦ Ç£òóó¡ï‚ Žkî,‘€Z­y/C"½»áÕ ª<Uíõk×’K´Zmõƒ?f¡ÏÕúÒRèw»á¶× @�4 deeIi Mýzã;44|.0ÕÒÒ\£×éK´ÅÅð§ÏB� à|o/ð<r¹üé ¹w÷±ï³{0wNô"hggÇgååå§ü~?`×ðÁfÓòƒuê|ÐëJ@,C à‡p(Ì|ÞòÅ@"‘°€ÿ¹OºD§ZÌæï„´øÀg`1²Ç5Í?â»NuÅ|}!0?666‰ó�ê/ùߎv×Ë6½cpÜñÞ©öx<£ÈÁ9®ež’ïìüù·ööö‹ØÙM´Î¢‚¨%ƒ"ð¼°Xͯ8{“[>þˆhÖhˆN¯#F£‘ ð{šöô¢¥%|ábž=ÛöZ6œuv÷äÌÍ=-‚U²U°sç\¹jßo¯Eß $+£ Í–&‹"GqðäÉ.Q0nžc9н®…Bq¿¾n—íé¯'"¥2Wòõ7_v¬ÎUÖMMMa‡2HÄ‹ÅpQjx¿Ò²Zl{Ñ{ E’ ,6ó>–ã·v;œûÃáð'[?­™_¾—øÂpÛã'Ú¾?ŒùKèå •p8O{+*6ãQŽ¢ÚN´~µ«¡žüÔñ#g4:0— i„@"–D•yy¹x^–0Î0«KKõäòȈëÊ•Ñ#˜‹¤E£Ñ_jjªú4% ⌫GF.¸\­X i…:rpÃ$êpaÔíIoÿg¸ó>Iü …Œ/­ ?����IEND®B`‚���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/qt/images/view-media-playlist.png������������������������������������������������0000664�0000000�0000000�00000001200�12246035113�0022170�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������‰PNG  ��� IHDR���������ójœ ���sRGB�®Îé��� pHYs��ì��ìu85���tIMEÙ0l,JK��#PLTE������������������ööö!P‹!PŒ$SŽ*X“+Z”-\–0^™2aš6dž6dŸ7eŸ7fŸ9c˜=i Ly²Nz±X„ºp—Èv“¸~›À”´Ü–¶Þ—·ßšºâ›¼æœ¼ãœ¼ä ºÜ À確ڢ¼Ü£ºÖ¤Ãê¤Äì¦Åì§Çî¨Çî¨Èî¨Èï«ÂÞ¬Äà®Äà°°°°Æâ³Çà´Èá¶Ì踸¸¸Êá¸Ìå¹Êá»»»»Ìã¼¼¼ÀÒçÁÁÁÃÃÃÄÄÄÅÅÅÆÆÆÇÇÇÉÉÉÊÊÊÓÓÓÔÔÖÖÖÖÚÚÜÞÞÞßßáààâääåæææçìòèèéèíóéîôêïõëðöììíìðöìñ÷íñ÷îòøðôøóöùôôõ÷÷÷úúúüüüÿÿÿ—ÎaÛ���tRNS�,-2ü¢¨ï���bKGD`Å·|���ÃIDATÓc` 0b L,¬€…‰™-°13°„†ú@F¬ ¬¾®®žP�d‘/PØÛx…°�ˆ°£¡‹½«™—‡ƒ™’pB|BB,¦j¬†˜JkÉ)hË*êêhÈ(kʨË€…ÅÕŒDU,,UmMô­•Ìõ$ÄÀÂRÂBœÜüì¼"<‚œ\¼|’³£ÂƒÂýƒ""CüƒÃüC¢ñZ™àdåìfogç‡éÀ84ÕÌØ aÆ Ø# �Ü3b§“ÊÆ>����IEND®B`‚������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/qt/kid3.icns���������������������������������������������������������������������0000664�0000000�0000000�00000666560�12246035113�0016071�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������icns�mpis32��f‘�/GM6ˆ�[”sK†� +µ—pr)™™ª� 9µ‘uafžž¡€�=iSlusi��›’��3L?ƒ€u-™œœ��#]8‘uux?xŠB�  Rnxq˜ÕÑ„™h��_ŒªÉ€î½a‡†�CÝî§ŒÀ€îØê¾L€� .›îê¢ÝíåÜÓÖìm� zî“bîÒÓÜÜ«‰l� WÖ\Œé¾Žw].ƒ�3€œ€d@ˆ�9@†�‘�=Y\Bˆ�7n¢¡‚W†� -Y‘Ä«Š~1™¢ª� ZgœÄ¦ur ¡¢¡€�FN†dxfŽv�� ’��7}‡^_Z‘„¢¡ž��@‡‡[lRŸ‰Av‚ŽB�$crd}‚z‚‘ÇÂ…“e��+GUhˆ¡½€Þ²_�HÏÞœ…´€ÞÊÛ³G€� .’ÞÛ˜ÐÜÍ»ª²Üh� tÞ‹^Þ¨«½Æ¡e� TÇX„Ú³†qY.ƒ�3y“z`:ˆ�9@†�‘�EekMˆ�E³³“e†� :i¢Ô¦=f^U� py¬Ô¾®Œzaba^€�Fa¤t‰{•®«‚ �am��E˜¥oqv ®®”$^aa��M¥¥m}m¬®®š@PX\:�.x›ŒsŒŽyngyxV_J��;UaWfiu€…pFWX�?~…f[q€…{„q=€�.`…ƒd~‚…„L�R…]H…iXL� CzEZƒqZQD.ƒ�3UaTH:ˆ�9@†�s8mk����������������������~éäw�����������†þÿÿÿª���������>ýÿÿÿÿýX ����ðÿÿÿÿÿÿíiIƒ��Øÿÿÿÿÿÿÿý�n��sÿÿÿÿÿÿÿÿÿb›l��›ÿÿÿÿÿÿÿÿÿ»ÅÊì#�2ûÿÿÿÿÿÿÿÿþ÷ÙÙ��AàþÿÿÿÿÿÿÿïqÁÉ����›ÿÿÿÿÿÿÿÿÿÿô6��� Þÿÿÿÿÿÿÿÿÿÿ›����¬ÿÿÿÿÿÿÿÿêÄh����[üÿÿÿôÑ©p������ÕݶŠ0��������������������il32��PÆ� —�(NmeV2”� …TŒšh~o“� wW°µµˆfkv‘�+v€µ}udqH� JL´µµ´vuuM£ ��Ÿž›¢‡� uY€µ�¬uQihž€žŸ…� a€µ�§uM¡,ŸŸ€�›…� ~d.P‘usvF�€ž„� ~_>1˜PtuA„ €�šœ€ƒ� €vW21Q·ZuG§��Ÿ€žƒ��  XE515õ;u \¥��œœŸ›‚�� 5bE0@ÿ=u j™"�pƒJFt^-��‚‰`*LÚLu n‡1GH{´á¤?�� €nE/O¦xo_UHg¡ÖîÖV]ŠÔD‚� € /|±’tOWÅìîe€žf\2‚� WHQ~´ä…îO‡ž1s©Bƒ�  2[ Öî˄îªC5EÊåD…�>éîJ]V¾„îÁœÐîîh5…�D°î£™æ|Ù…îëîî¶E…�3dîß]îì¦îíåÛÒÎÞîéF†�DâîhÓ€îéà×ÏÎâîîs3…�A¥€îL|îîØÎÐÙãëîÞ­P3…�1Xîî=3OîîÓÎÔÝçí齄LEC3‡�FÛîN3:˜îîíëîΙ\EF4+Š�:˜î_>sìîÝ«pEH;1Ž� 3PîìἃNEB5U’�E}ZEF2+–�953°�Æ� #%& —�2]vf>!”� ,œ`˜§wŒy'“� *Žf¿ÄÄŸ~v€‘�,H‹œ€Ä–{|Q�(Z[ZÂÄÄ€ _°'��Ÿ¢¡¢‡�!XI‰f€Ä�¼€[tk€¡¢¡¡¢…�D‡?—n€Ä�¸`­2ŸŸ€�¡¢…� 5…‡=”pž@^£P�¿¡¡„� -}‡‡=“kNN¦^OŽ €�¡¡€ƒ�!h€‡>ŠcENj¿pV³��Ÿ€¡ƒ��8‡UfQFNQ÷L q°#��¡¡Ÿ¡ ‚��>‡uErRM[ÿN ‚£*�p†JCv^-��8‚‡6 k?fß\ ˆŽ5EFu©Ò¦—=�!k|„B‚QB\´“ˆq\Ie˜ÈÞÈT\ŒÇC‚�._loqh?‹¾žyQV‡¸ÜÞ`¢g‡X2‚� &;Y_a4aIPy©Õ…ÞM 1oŸ@ƒ� %0F]—ÈÞÞ~z¾„Þ B5D¾ÖA…�?ÙÞGZR³„Þµ”ÂÞÞd5…�C¦Þ™ÖvÊ…ÞÙÞÞªC…�3_ÞÑZÞÜ›ÞÛ̺©¡ÀÞÚD†�CÓÞdÆ€ÞÖIJ£€¡¢ÆÞÞn3…�?›€Þ”JuÞÞ´¡¦¶ÈÙÞТN3…�1UÞÞ…<3LÞÞ«¡­¾ÑÝÙ²~JDA3‡�DÌÞL3:ÞÞÜØÞÁ‘YBD4+Š�:Þ[=nÜÞÏ jDG81Ž� 3LÞÜÒ±|LD@5U’�DvWCE2+–�953°�Æ�-./*—�;lˆvI(”� 3²m¦¶†šƒ0’� 4¢uÏÔÔ¹˜‚‹&‘�7TŸ¬€Ô²®•‰X�2migÒÔÔÓ€® s».��`ba^]‡�)lYt€Ô�΀®­g|Daab€a�`…�T¥K­{€Ô�Ë®tº2``€�``„� B£¥J¨~«Pnµ®«‰W�€aa„� 8š¥¥G¨x^lµn­®]˜€�ba€ƒ�(€€¥KžpXlƒÈŠ®h¾��``abƒ��E¥eu^Xloøa® ‰»+��a``aa‚��L¥O„bkwÿ`® ¬2�PV?;PF-��E‚¥@¸wT€äl® ¤–7;<Sl€dc8�+‚—›ž¢N“^›Uiۤ„_DPcz…zBEYy;‚�9t„‡ŠI¤˜Î©|MK^s„…IUaH[F2‚� .Iltw=iDH[l……?`a1Og8ƒ� .:EJcz……WUv„…g:5<u;…�9ƒ…=EBp„…qbx……K5…�:j…d_S{ˆ…l;…�3I…F…„eˆ…ƒ<†�;€…Ky‹…P3…�9f€…a>R‰…~h@3…�1C……[83?……ƒpW>;93‡�<|…?36_‚…waE:;4+Š�5`…F8O„…~gN;<61Ž� 3?…„oV>;:5U’�<SE;;2+–�953°�l8mk���������������������������������������������������������������������������“ÍÉy �������������������������lôÿÿÿÿñe�����������������������<þÿÿÿÿÿÿý•���������������������÷ÿÿÿÿÿÿÿÿý�������������������Úÿÿÿÿÿÿÿÿÿÿû%������������������´ÿÿÿÿÿÿÿÿÿÿÿÿÝ�� ~. ����������}þÿÿÿÿÿÿÿÿÿÿÿÿý©‹ÿ•Ê“ÿJ��������Aúÿÿÿÿÿÿÿÿÿÿÿÿÿÿð(���R�������ñÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿö���ùW������ÜÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþK���Q ������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¦��ùñD������ãÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿØ��£bÿa�����ñÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿß�ÿŸÜóì����âÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïÊêùÿÿÿûv���� ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿüèùÿÙ����àÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿé–öŠñê$����»üÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿé n4ëüˆ������'Ïüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷ÂaÚþÿâ��������!ùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûÿÿÿë0��������•þÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ˜��������-ëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿæ��������àÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿí<��������‚ûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþì2��������ëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿñêÕ†-����������Öÿÿÿÿÿÿÿÿÿÿÿÿøéã¤N �������������n÷ÿÿÿÿÿÿÿþëê¾h�����������������êÿÿÿÿñéÕ„5���������������������´ïêâ£L ������������������������� ?���������������������������������������������������ih32�� wÿ�š�§�((£� %‚–laƒƒ3¡� 5›RE†”{E–¥ Ÿ�$˜MwµzHSÄ� €~_µ¯uuWB›� *“6«µ�¢€uO[®š� dS‚µ�šu8^™� ƒZ‚‚µ�‘uq#É��ÿœžŸž� ‰:£‚µ�ˆ‚uQp‡œ†‹� '’+³‚µ�ƒƒu!ˆ€›ª��žŠ�� € 0˜,µµ´±µµ~ƒuIŒk"¤„�ž‰�� ;›2µŸ/)c±yƒuq,Å„�œ‰�€ 9›,´</jM…u(Ë„�œˆ�� ‚ .˜)¨&11½mX„uI—b‚�œ›ˆ�� ƒ &“0–%11nâ)„udS¥ �¢žŸ‡�� ƒ †J|#11Dÿ]j„u&Ì�žžž†�� „ vpd%117ÿ^„u'Ó€�œ��™„� „ d”?,11Aÿ¥P„u)Ñ€�œœ^3;LŽJ@ƒ� „ ?›+d*1SÿŒ\„u,Ë�02M<4[’³žG4„�� „ ‡T|,0]ýJy„u.œ+:3I´åìꬉ9„�€ k‘?›*1sI©ufUG4?k¤Öîîäz:=—ºË41ƒ��…7—8ž©gr®µ€[J:5]’Æì‚îwFw›NêL3„��„t…Lµ­jG3J´å…î Ø3pžœ:°“:…�€ <;L5?o¤Ö‰î Ã5Uœœ�=™Ó32†� €)55^“ÇìŒî æ;2��06ÈîV3ˆ�  5€µåîÉJMvØˆî «5648îîœ:Š�AíƒîÅ383RÍˆî ¾}uªíîîÝ42‰�35Ä„îFÆYÜîa4Š�:„îŽ^îî½jŠîéì€î§8Š�3?ëƒîÎ4Ùî…î íäÛÑÎÏçîîä72‰�.6¹„îRš€î�à‚îêàÖÏ‚Î�à€îm7Š�8u„î’Y‚îíæÜÓ…ÎÔë€î²7Š�39ç‚î»v3Ó€îáÏ„ÎÏÖßèîÝ’73‰�37®î564q€î�ЂÎÑÚãì€î軈O394‹�6h€î©9334€îÙÎÎÔÞè€îí˘b77932�35áîîW€3:À€îíçë€îÝ©u?4:34+‘�9£îîF338kƒî纇O39726•�3]îîŸ46èîîíʘa77:33™�34ÙîîÞãîÛ§t?4:41+� 9˜î繆N39629¡�28X77:44¦�4313ù�ÿ�š� $"§�#11&!£� ,˜°qš™> ¡� ?µ`M‘Ÿ†N£±$Ÿ�2²[ƒÄ“Y^Ð%�"–”kÄ¿kKÎ! ›�*:¬AºÄ�µ€afºš� $6v©]‚Ä�®E¨g™�w(šj‘‚Ä�¦‹+×��ÿ££ ¢ Ÿž�e‚(¡D²‚Ä�Ÿ‚cz‘  …¡�¢‹�K‡i3«4‚Ä�›‚(؉¡ª��¢¡ Š�4‡‡Y;²3ÄÄÃÀÄÄ—ƒZ—t'¤„�¡¡‰� %„‡‡MFµ;Ä­;6mÀ“ƒ‹4Ï„�€¡‰�y€‡ KCµ5ÃIKšY…1×!„� ¡¢ˆ�i‡ U8²0µ7NNÆzj„Z¢k‚�Ÿ £ˆ�H‚‡ b0¬9¢8NN‚æ6„{[®��¢€¡�Ÿ‡�!„‚‡ v$W‡6NN_ÿi‚ƒ.Õ� ¡¢¡¢¡†��5ƒ‡ †Š„n5NNSÿšt„/Þ€�¡¡ �� ¡™„�D„‡ #v®F8NN\ÿ¯b„3Ý€�  a3:M‘I@ƒ�?„‡ =L¶2oANkÿ˜q„6× �02M<4XŠ®¢F4„�+…ƒ‡ o'c‰9MtþY„7¤-93G{©ÖÜÛ¬¡‚9„�n{|€‚„† ~ªF¨8H‚Tº€{bK4?gšÈÞÞÕt9=™µ¾41ƒ� 0nprtvxz@B±C«·p}¼Ä“kR;5Zй܂ÞqFxŸ…LÛJ3„�.`fgikmjˆœTÄ»—oH3I{©Ö…Þ Ê3t¡¢Ÿ9¥‹:…� CZ]_ab1H”AN5>kšÈ‰Þ ·5U¡ �;Æ32†� 5QTL@65\‹ºÜŒÞ Ö:2��06»ÞS3ˆ�-6yªÖÞ¼HKqÊˆÞ  5548xÞÞ“9Š�!@݃Þ¹373PÀˆÞ ²wo ÝÞÞÎ32‰�35·„ÞC‡·VÎÞ]4Š�:{„Þ†ZÞÞ±eŠÞÕÙ€Þ8Š�3>܃ÞÁ4ËÞÞ–—…Þ Ü˺§¡¤ÒÞÞÕ62‰�.5­„ÞO‘€Þ�Ñ‚ÞÕò£‚¡�ÀÞi5Š�8o„ÞŠV‚ÞÜÍ»©…¡­×€Þ§6Š�38ׂÞ¯p3Æ€ÞŤ…¡°ÁÓÞΊ73‰�37£Þz464l€Þ�¥‚¡¦¸ÉÚ€ÞدM384‹�6d€ÞŸ8334{€Þµ¡¢®¿ÑÝÞÞݾ^67832�35ÒÞÞT€39´€ÞÝÒØ€ÞΟo>4:34+‘�8™ÞÞE338fƒÞ×®~L38526•�3YÞÞ•46xÙÞÞݽ]67933™�34ËÞÞÐÔÞÌo>4:41+� 9Þ×­}L39629¡�27U67944¦�4313ù�ÿ�š�&,*¦�%$)89-'#£� #4®É‘¯®G%¡� HÏpVž­‘X°¾*Ÿ�:Ìi‘Ô°khÛ+�(«©xÔЮ®UÙ&›�3BÄLÌÔ�É€®vpÅš�,B†Áh‚Ô�Ä®Sµq™�$‘/°y¡‚Ô�¾®©3â$ €�bcaa``a�#|Ÿ/¸PÂÔ�¹‚®w…œbc…a�b‹� \¥<Ã=Ò‚Ô�¶‚® ­/ä!Sabb^U��€aŠ�?¥¥mDË=ÔÔÓÐÔÔ³ƒ®k£}![„�ba‰�-¡¥¥^OÏDÔ»ECzаƒ®¨<Ù„�cab‰�%”€¥ ]LÏ<ÓVh”¨c…®;â&„�`acˆ�!€¥ hAË9ÄJllЈ‚„®l¬s ‚�ca`ˆ�W‚¥ w8ÅC¯Mll—êF„®•e·��]€a�`‡�(¡‚¥ ‘)³d”Jllzÿwƒ®­6ß��b‚a†��Cƒ¥ ¤%ž–yGllpÿ¤Œ„®9é€�cab��aa\„� T„¥ *†ÇOGllxÿºw„®=è"€�aaF36>[=@ƒ�M„¥ JVÐ;|Zl„ÿ£‰„®Aâ%�02>73E]kb<4„�5£ƒ¥ ˆ,´q–GkŒþi§„®B­/53=Vl„ƒhaZ7„�#†–˜›Ÿ¢¤&ÁN¶Ha‘`Í€®­’rO3;Kdz……R67^ov31ƒ� ;‡‰‹Ž’•LLÉN»Æ{ˆÌÔª}Z;4I]s„‚…Q;P`V?ƒ>3„�8u|~ƒ…#œ³\ÔË¢vG3@Ul…… {3Na`c6j]6…� #Roqtvx;S§GN5:Odz‰… r4Uaa�8_y32†� "AdgZD44J]t„Œ… 62��04t…B3ˆ�55Ul…u=>P{ˆ… g4346T……a6Š�$9„ƒ…s353Avˆ… pTPg„……}32‰�34r„…;[rWC}…G4Š�5U„…[F……oK…f6Š�38„ƒ…w4|……ccŽ…52‰�.4m„…@`€…�~…M5Š�5O„…]D“…k5Š�35‚‚…nP3y‘…}]53‰�35i…U443O…‚nX?364‹�4K€…f6334U‰…„v_G€532�34€……C€36q†…}gP83634+‘�6d……;335Kƒ…‚nW?35526•�3E……b44T‚……„u_G55633™�34|……~€…|fO83641+� 6_…‚nV>36429¡�25B55644¦�4313ù�h8mk�� ��������������������������������������������������������������������������������������������������������������������������������������������������������������*q–‡I����������������������������������������¸ùÿÿÿÿó¡�������������������������������������BéÿÿÿÿÿÿÿÿïS�����������������������������������÷ÿÿÿÿÿÿÿÿÿÿûx�������������������������������� ßÿÿÿÿÿÿÿÿÿÿÿÿüs�������������������������������°ÿÿÿÿÿÿÿÿÿÿÿÿÿÿúB������������������������������zýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿð����������������������������Dúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ±���������������������������ñÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿû0��/$q™”��������������� ÜÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÏVKâÿÛçÿïâÿ<�������������·ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿâ€.��{ÿè�������������ýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÃ�������’d������������Fúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿí������6ÿ¢�����������óÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿü(������Ÿú,���������� àÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ}�����MÊE�����������“ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÂ����öÿÿ��������� ðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿë����Vÿ½˜ð“��������5ýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿö��6ÿS��Õÿ2�������Vÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿø ��‰ÿ_}ÊýÿÖ������Gþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿú fÞþý÷ÿÿÿøc������ùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýåÿ÷ýÿÿÿÿÿÿþÄ������áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûýþÿÿþ������Süÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿù÷ªìåöÿ÷r�������”üÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþKÿþHèÿÿÔ��������Sóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿû£�Êÿÿÿ$��������®øÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùL�� øÿÿ÷~����������¤øÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþü¤“óùÿÿÿà������������¿ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýûÿÿÿÿÿþ3������������üÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùŽ�������������ºýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿì������������ZùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüB������������ øÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúŸ�������������©ûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿô������������Kúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿô������������ñÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿöÿéS��������������˜ùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøü÷»o)����������������<ýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûøþÓ†@�������������������çÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþöÿêŸW������������������������‡÷ÿÿÿÿÿÿÿÿÿÿÿÿøü÷¹m(����������������������������-þÿÿÿÿÿÿÿÿûùþÓ…>��������������������������������ÚÿÿÿÿþöÿèV������������������������������������aúöü÷¸l'�����������������������������������������It>����������������������������������������������������������������������������������������������������������������������������it32��7 ����ÿ�ÿ�ÿ�ÿ�ÿ�ÿ�ÿ�ÿ�ÿ�˜�  ô�  î� „� é� �2KcikcM1�æ� � =u•…“v9 � ä�;†ƒ–“˜œŠM�á��pœ€—v@%5U„›Š= ß� ��‰€›p"�JX\C"*’Õá½q� Ý��� Š€“8 c¤‚µ ´ži&áî©6 Û� ˆ›†"�D©†µ «d+� J¹øÐM Ù� p›™‰j´‡µ uuX�,›íãd Ø� ��Kœ‘“%�f‰µ�‘€u p)�‡åçi Ö� �”œœE�Iе�…‚u >�ƒãäd Ô� �h’“uªŠµ�yƒu I†æã\Ò� �"šƒ™*�„е�­…uK•òÑE�Ñ� dˆ”m0³Šµ�¢†uE�+¨û·/� Ð� �’|œ.�Œ‹µ�—‡u2�B¼ùœÎ� ��K†Ž„ ²‹µ�އu seÕìnÌ� � ztœP�lŒµ�…ˆuh ‹óÉB�Ë� �'„›& Œµ�}‰uN�/¶ûžÉ��Ix‘‰ &´‹µ³vŠu%_ÜâdÉ� �imœj�^Œµ�«‹ui•úº-��›œžž’¯��ryF�ˆŒµ�¢Œu?�CÎñ|‡��Ÿ�ž…�™€ª��€ �.m†)£Œµ�™Œur ‡øÂ4�ƒ�€œƒ�žŠ�Ÿ§� �Cg–±Œµ�‘u M�6Éò�3œžœ”�ª¥�WfšŒ 3µ�Šu t †õÄ6�|‰�›€žžœ™ƒ�ž¥��‚ [n€Lµ�„ŽuK�:Ìó| [�œ‚žŸ†��Ÿƒ�’£��ƒ\tk�_µ�~Žu s ‘ý¼&5‹€Ÿœœ™‹�‚� ¢��ƒ \{]�qµ�xu @�KÝæekž™‘��œ€�ÿ¡�„ !Y~R�|Œµ�³u m¬ÿ¤[“�€�¡¡�„ �%U€G�‚ƒµ´¬¦²‚µ�®‘u-iíÔ@�“��ž€�œ¡�… �)SƒE�‡µ´fWQ\w£€µ�«‘uY�1Êø“��œ€�ž ��† �.S…E�ˆ€µ ´yH R€³µ§‘ut œÿº’��œ‚Ÿ��‡ �/R‡C�‡€µ ˆC '1Pv) =r±£’u6�`îÚH�‘�‚�œž��ˆ�-P‡I�‚µµ¨W -113Æä]7xž’u^�/Îøz�ƒž�€�ˆ�)N„R�zµµ~& 1Aì÷gDz“u £ÿ®�‚�žž�‰ �)L\�sµ²` ƒ1}ÿóOLn’u,qõÓ5���œž��Š �#Kh�eµ£Jƒ12×ÿÚ)M’uN�BÞñ]��œœœÿ��‹ Mz|�Pµ“7!„1}ÿÿ/h‘ul�#ÆÿŒ�™œ€�›ž��Œ Ps‡=µŠ!)„16óÿó8M’u¢ÿ´Œ�„�›œ�Œ Of$µ…+…1¸ÿÿ—4n‘u-wúÏ,Œ��š…�›š��Ž OZœš´ƒ.…1†ÿÿè#[‘uH�QåãH�Š��ž†ž•—�Ž JU–3�›ƒ+…1�a€ÿg H‘u^�7Öøb�‰��™‹—�� �=PŽL�z‹#'…1�K€ÿ¦8sup"Çÿ‰��ÿž™œƒ�ÿ”�� �/L‡j�Q“0"…1�=€ÿÛ+h‘u°ÿ« ˆ��š€œ��ÿƒ”� � Ouˆ !£D…1�=€ÿý+]‘u"˜ÿÁ‡��™„��ž�ž”� Rb˜¥X…1�IÿLW‘u0�~ýÐ!‡�‚�ª„��—�ª”��‘ NT™8�†o/„1�LÿcS‘u=�fõÙ,‡��ÿ‚�0433M€œ=3“��‘ �=MŒa�SŒ,!„1�_ÿjP‘uJ�Uðà8‡��›œ@434‚3x33@’��‘ �(L{ˆ ¦O„1�vÿ`S‘uS�LëçD‡�€Žq4†3X•6334’��‘ Td›$�˜t+ƒ1�ŒÿBU‘uY�CæèG†�-e]A…38b•¿ €3‘�’ NR–K�dDƒ1�Ÿ€ÿí&Y‘u_�;áéL�€��@‰3P…¸åîîÀ€�§€3�4�’ �4K‚#´r)‚1�­€ÿ®3d‘u b�:ÜíO� 2‡3Cs§Ú‚î�ì¼N€3��‘Sg™#œ¥Q01�¶€ÿaSr‘u`�:áÜH/…38c•Èì…î�ÓÓ€3�� PS—R�_µ„91¡ÿÿ¾)r•‘u_ 6[5…3Q†¸æ…îÝÍÚë­€¥ìÒ€3�2�†‰�3Mƒ† ±³o'€1dÿèG J™³|ukYG5…3Cs¦Ú†îÏyA€3�:ÈîîY€3��Œ‚Vaœ3�‚µ¬h-+11šT*vµµ™‰uq_M<…38d•Èì‡îìŒ6ƒ3¤éî3�$‘��’MMq6µµ®qD 0h¬µµ³z„uteSA†3R…¸çŠîçgƒ3 [Œ}oYÔîîÙ5332��€Ž�$Sp—"›µµ´’fN=CWw¬µ�—ulYG7…3Ct§ÚŽî�t€3?\1‘€Š33Oåîîa€3‘�†ˆ ST˜dKµ´£’˜©ƒµ³v_M<…39d–Éìî�¼€3Q˜œ€�—€3Œî3�7’��Œ‚ �0Q~– ˆµ²•rN4…3R…¸ç”î�l3…€3>éîá8332“�  WX™aK…µ¡~Z:…3Ct§Ú—îå9334ÿ„’1€3ºîîl€3“��Ž �0Q|–$›€µ¬ŠfC…38d—Êìšî�¹€3��ž‚œ��€3‡î3�5“��ƒ‡ UW˜p<•qN4…3S…¹çžî�¦€3��‚�œ€�433rîîç<€3•��ˆ �,RzŒo41…3Cu§Û¢î�ª€3€�žžŸ�433z€î�w€3–��‹ E@?…38d˜Êí¥î�Ç3‡�€3�”€î�¹€3�5—���†)†3T…¹ç©îìC332†��5€3�Ì€îëD334™��ƒ/…3Dv§Û®î�3„��+€3�O‚î�‚€3›��€�ƒ38e˜ÊíŠîÀƒkv©åžî×9€3�1‚�32€3�¬‚î�€3�5œ��3T†ºçŒîÒB6€39D‚äî�£3243243tí‚îíJ334ž��33LÑî�ƒ„37IÄîž5‡3xè„î�‹€3 �$33‘î�L†3B©îÀR„3B¦†î�Í€3�5 �'33i‘î�Ô‡3@ îë°vWGRl¡âˆîT334 �1337Þ‘î S339GMID533A©°î�–€3�+Ÿ��.€3�Ÿ‘î š33<èîîÒ’F;3Bůî×4334 �€3�]‘îØ433³îêŽB3Fܯî_332 �€34Õ‘î^33tƒîÈF5]í®î�¡€3�.Ÿ�3�”‘î33;çƒîÖM9˜®îß7€3 �€3�R‘îÝ533°„îÞG8ÖžîíãÙÜáì‡î�j€3 �3�Ë‘îc33p…îÓ4U›îëâÙÐÎÑå†î�ª3 �€3�‰‘î£338å…îº3®—îèÞÔ†ÎÐë…îå:€3 �€3Hìîß633ª†î}Q“îìãÚЊÎ�Þ†î�s€3 �3�À‘îf33k†îíÇîèßÖÏÎ�؆î�µ€3�1 �€3�~‘î¦338á–îíäÛÑ‘Î�܆îé@€3 �€3@éîâ833§“îéàÖÏ”Î�å‡î�~€3 ��1€3�µ‘îl33eîíæÜÓ—Î�Ûˆî�À€3�1 �€3�s‘î«336Þ‹îêáØÏ˜ÎÓä‰îìE334 �€3:åîß933¡‰îâÔ˜ÎÏÖàéŒî�^€3 �3�ªîÚN4€3�`‡îíÖ—ÎÒÛäíîÞ”4€3¡�€3�h‹îì“9ƒ34Û†î�Ü”ÎÏÖàèî齊W43�U¡�€36݉îåd†3�†î�Ñ‘ÎÑÚäìîÍœh:„3�2¢��.€3�Ÿˆîç]ˆ3…î�íÎÕÞçîÝ«zEˆ3¤�€3�]ˆî�{ˆ3�;†î�ÓŠÎÐÙãëŒî齉W4†321@¦�€34Õ†î�½‰3�U†î�߇ÎÔÝçíŒîÍšf:‡32.ª�3�”†î�]‰3�r†îíÙ‚ÎÏØâëŒîÝ«xD†3244¯�€3�R…î�ÅŠ3�£ˆîãÛÙÏåí‹î黉V4‡32@²��4€3�Ë„î�ˆ‰3?å˜îíÌ™f9‡341·�€3�‰„î�l‰3�‘–îݪwD‡345»�233Hìƒî�hˆ3Kç’î軈U4‡32+¾��1€3�Àƒî�r‡3:Èîí˘e8†3441Ã�€3�|ƒî�Ÿ†3;¼îÝ©vDˆ3�2Ç�233?è‚îèR„3OËŠî纆T†3434+Ê��5€3�²ƒîÓU3G™è‡îíʘe8‡323Ï�233p„îíÌ©§Äì†îÛ§vD‰3Ó�2338äŽîç¹…S†3245+Ö��9€3�§ŠîìÊ—d8‡329Û�233e‡îÚ§tC†3443ß�2335Ú‚î縅R†32313â�3sæìÉ•d8ˆ3�9ç��2€38;‡346ë�+2ˆ31@ï�$4‚3449õ�9126ÿ�ÿ�ÿ�ÿ�ÿ�ÿ�Á�ÿ�ÿ�ÿ�ÿ�ÿ�ÿ�ÿ�ÿ�ÿ�˜� ô� %)&#% î�+„�é�+ �:Wtz}u[9�æ� % �G‰¯…¸¬ŠC�ä�$Eƒ¸¯¬²·¸¡Z�á��„·€¸²‹K+$>c›¶¸¸¡G ß� ��$¡€¸¶ƒ(�"P_bG# 1 àéσ�Ý� �&¢€¸¬Bm±‚Ä Â¨o-žêó»?Û�  ¶¸¸ž(�M¸†Ä »{5� VÊúÜZÙ� „µ´¸¡ vÃ‡Ä °l�4°òës Ø� �W·ª¸¬,�s‰Ä�¦€ Š2� œìíy Ö� �®¥··Q�SÉÄ� M�˜êìt Ô� �z«¬¸‰ºŠÄ�“ƒ Z›íêkÒ� �(µš¸´1�”ŠÄ�½…\%¬õÞQ Ñ�  tŸ®¸€8ŠÄ�´†U�2½üÉ8� Ð� F"«‘¸·6�œ‹Ä�«‡>�NÎû± Î� �L-�X§¸› &Á‹Ä�¤‡ Ž!váñÌ� �2y Žˆ·¸]�zŒÄ�ˆ€  öØN� Ë� ‚Z�.𕏶-±ŒÄ�–‰`�7Èü´É� x‡3�Vª¸¡ .‹Ä�‹-oåêuÉ� g‡ƒ {€·¸|�kŒÄ�»‹«ûÌ5��  ¡ ’¯� �P‡‡u†Ž¸¸R�—ŒÄ�µŒN�NÛõ‘‡�Ÿ ¡¡¢£„¡ ™¢¡£ª� �5€‡ ^�5€¸¸0µŒÄ�­ŒŒ úÓ=�ƒ�¿ ƒ¡�¢‰¡¢Ÿ§�„€‡ B�Ox¨¸°ÀŒÄ�¦^�@Øö— �3¡¡ œ¢„¡�¢‹¡�ª¥�z‡+dw´¸¤<Ä�¡  šøÔ@� ‰¡¢ ‚¡ ¦¢‚¡�¢¥�j‡ † j€¸¸–XÄ�œŽ]�DÚ÷ ]ˆ¡�Ÿ†��Ÿƒ¡�’£� �T‚‡ mˆ¸¸|�mÄ�—Ž Ž§ýÎ-7€¡Ÿ¢ ¡™‹��¢¡� ¢� �:ƒ‡ z l¸¸m�~Ä�’ O�Xæívn¡¡£‘�¡�ÿ¡�"…ƒ‡ s'h“¸¸`�ŒŒÄ� †Àÿ¹ [“�¡¡¢¡¡�|„‡ j�,d–¸¸S�’ƒÄº´Á‚Ä�¾‘7zòàK�“��¢€¡�¢¡�m…‡ d�0a™¸¸P�–Äšo_Xd€±€Ä�»‘n�:Ùú““�‚¡ � �X†‡ ]�7aœ¸¸P�—€Ä ƒO  "XŠÂĸ‘°ÿË%’��¢‚¡Ÿ� �=‡‡ V�7`Ÿ¸¸N�–€Ä “H?NhŠ<D|Àµ’B�oòåT�‘�‚¡�£ž�%…‡‡Y�5^Ÿ¸¸V�Äĵ_GNNPÎès<‚±’s�7Üú��¢¡� ž�~ˆ‡]�0[›¸¸`�‡Äĉ,3N\îø}J‰’·ÿÁ��¢¡�žž�p‰‡ a�0Y˜¸¸k�€ÄÁhƒNÿõgUˆ’6…÷ß>���¢¡ž� �\Ї g�)X•¸¸z�oİQ!ƒNOÝÿß@^’`�Nçõm���£€¡¢ ÿ� �B‹‡ q [¸¸‘�ZÄŸ=4„Nÿÿ :€‘…*Õÿ¡Œ�™ €¡� ž��(†‹‡ w ]‡¸¸ž DÄ–(A„NRõÿõP_’¶ÿÅŒ�„¡�¢œ� {Œ‡ }]x¸¸¨'Ä"E…NÂÿÿ¥@‡‘8‰ûÜ4Œ�� …¡� š��C‡ ƒ \j·¸´$ÂI…N—ÿÿì7'p‘X�_ìëU�Š��¢…¡¢ ¢ª—� ~އ #Vc°¸¸<�ªŽ D…N�x€ÿ|Y‘s�@áús�‰��™…¡�¢¡�£–�@‡ 6�H^§¸¸Y�†—(>…N�d€ÿ³EŠ(Öÿ¢‰��ÿ¡ž¢¡ ƒ¡�ÿ”� �r‡ K�8Yž¸¸}�\Ÿ56…N�X€ÿà%6€‘Âÿ½ˆ�� ¡�¢��ÿƒ¡”� ‡ e%]Џ¸Ÿ '°K#…N�X€ÿý>&r‘* ¬ÿЇ��¦¡„�‚¡� ”�3‡ x _s¸¸²#µ_…N�cÿak‘;�’þÝ'‡��¢¡�ª„��›¡�ª”��D‡ „\c³¸¸B�”xK„N�fÿwf‘K�xøã4‡��ÿ‚¡�0433N€¡ =3“��O‘‡ 3�HZ¥¸¸r�^—25„N�vÿ~b‘[�dôèB‡�ƒ¡@434‚3‚¡¡z33@’��H‘‡ S�/Z¸¸Ÿ ´U„N�‰ÿuf‘f�YñíO‡�¢¡¡‘s4†3W˜‚6334‘�A‘‡ sbu¸¸µ*©}EƒN�œÿ[h‘n�NíîT†�-g_A…37^¶¤¡¡€3‘�,‘‡ „\`°¸¸X�rªI ƒN�¬€ÿð=%n‘u�EêïY�€��@‰3N~­ÖÞÞ¼€¡�§€3�4�†‘‡ >�=X™¸¸”*Ã{A‚N�¹€ÿº,;{‘ x�Dæò]� 2‡3Añޡ¸K€3� �u‚ƒ„„…††Š‡gay¸¸´)¬³XMN�Á€ÿwZ‹‘w�DéáP/…37_¼Ü…Þ�É¡Ɉ€3��P}~€€‚‚ƒ„„…††ƒ‡€]b²¸¸`�kÄ?!N®ÿÿÇA{ª‘v ;\5…3O­Ö…ÞÎÀËÜ®€¡§ÝÄ€3�2�%"yzz{||}~€‚ƒƒ„……†‡:�;Z›¸¸žÀÂx,%€NzÿìaQ¦Â–‚iP6…3An̆ÞÁs@€3: €¡ÂÞÞU€3�1guvvwxxyzz{||}~€€‚‚ies¸¸·<�Ä»p2ENN¨k0€ÄÄ­‰ŠrX?…37_¼Ü‡ÞÜ„5ƒ3„¡¡¦ÚÞÞ’€3�$�12pqrrsttuvwwxyyz{{|}}|Z[©¸¸…=ÄļzI$ 6q»ÄÄ”„yaG4…3P~­×ŠÞ×cƒ3 ]€rVÇÞÞË5332�,^lmnnoppqrrsttvvwxxyzH�)`„¸¸²(©ÄÄÞoUCI^»Ä�«ƒiP8…3AnÌŽÞ�n€3@^1”€¡Ž33LÖÞÞ]€3‘� ghijjklmmnoopqqrsstumab²¸¸uSİž¤·ƒÄÂŽrW?…38_޼ÜÞ�°€3Rœ¡¢¡�›€3„ÞÞ™€3�7‘� cdeffghhijjklmnnoppq6�8^“¸¸¯$¯ˆÄÁ yR4…3O~­×”Þ�g3…¡€3=ÚÞÒ7332’� Q`abbcdeefgghiijkkm] fg´¸¸pT…Ä®‡_:…3AnË—ÞÖ8334ÿƒ¡ ’1€3®ÞÞg€3“�6[]^^_``abbddeffghh,�8^‘¸¸°*¨€Äº”lE…38_Ž½ÜšÞ�­€3��ƒ¡£��€3€ÞÞ£€3�5“�"�IYZ[[\]]^__`aabcdV ef²¸¸„B yQ4…3P~­×žÞ�œ€3��ƒ¡€�433mÞÞ×;€3•�&NVVWXXYZ[\\]^^_`*�4`¸£~61…3AoÌ¢Þ� €3€�¢  ¢�433t€Þ�q€3–��$ISSTUUVWWXYY[[R OEB…38_½Ý¥Þ�º3‡�€3�Œ€Þ�­€3�5—�COPQRRSTTUUME‡3Q~­×©ÞÜB332†��5€3�¿€ÞÜB334™� >LMMNOOE7…3BpÌ®Þ�z3„��+€3�M‚Þ�{€3›��8IJJ=ƒ38`½ÝŠÞ´|fpŸÖžÞÉ8€3�1‚�32€3�¢‚Þ�¶€3�5œ��3A3Q®×ŒÞÄA6€38B{ÕÞ�™3243243nÝ‚ÞÝH334ž��"33JÃÞ�|„37G·Þ•5‡3rÙ„Þ�ƒ€3 �%33…‘Þ�J†3AŸÞ´P„3@œ†Þ�À€3�5 �'33d‘Þ�LJ3?—ÞÜ¥pTFOg˜ÓˆÞQ334 �1336Ð‘Þ O338EJGC433@Ÿ°Þ�Ž€3�+Ÿ��.€3�–‘Þ ‘33;ÙÞÞĉE:3@¸¯ÞÉ4334 �€3�Z‘ÞÊ433¨ÞÛ†A3DͯÞ[332 �€34Ç‘ÞZ33nƒÞ»D5YÝ®Þ�˜€3�.Ÿ�3�Œ‘Þ”33;׃ÞÈK9®ÞÑ6€3 �€3�P‘ÞÎ533¥„ÞÐE8ÈžÞÝɵ¼ÅÙ‡Þ�e€3 �3�¾‘Þ_33j…ÞÆ4R›ÞÙȶ¦¡¦Ì†Þ� 3 �€3�‚‘Þ™338Ö…Þ®3¤–ÞÝÑÀ®¢…¡¥Ø…ÞÖ:€3 �€3FÜÞÑ533 †ÞwO“ÞÚɸ¦Š¡�À†Þ�n€3 �3�´‘Þb33f†ÞݺÞÓÁ°Ž¡�´†Þ�ª€3�1 �€3�x‘Þ337Ò–ÞÜ˺§‘¡�»†ÞÚ?€3 �€3?ÚÞÓ733“ÞÖò£”¡�͇Þ�x€3 ��1€3�ª‘Þg33aÞÜͼ©—¡�ºˆÞ�´€3�1 �€3�n‘Þ¡335ЋÞÖų¤˜¡«Ë‰ÞÜD334 �€3:ÖÞÑ933˜ˆÞÝǬ˜¡£²ÃÖŒÞ�Z€3 �3� ÞË…L4€3�]‡Þܯ—¡¨ºËÛÞÐŒ4€3¡�€3�d‹ÞÜ‹9ƒ34̆Þ�½•¡°ÂÓÞÙ²ƒT43�U¡�€35ωÞÖ`†3�x†Þ�¨‘¡§¸ÊÚÞÀ“d:„3�2¢��.€3�–ˆÞ×Yˆ3…Þ�ÜŽ¡¢¯ÀÒÞÏ¡tDˆ3¤�€3�ZˆÞ�tˆ3�:†Þ�ªŠ¡¦¶ÈÙŒÞÙ±‚T4†321@¦�€34džÞ�²‰3�R†Þ�Á‡¡­¾ÑÝŒÞÀ‘b:‡32.ª�3�Œ†Þ�Y‰3�m‡Þ�´‚¡¤µÆ×ŒÞΡrC†3244¯�€3�P…Þ�¸Š3�™ˆÞ˺´£ÏÝ‹ÞÙ¯‚S4‡32@²��4€3�¾„Þ�‰3>Ö˜ÞÝ¿b9‡341·�€3�‚„Þ�g‰3�‰–ÞΠqC‡345»�233F܃Þ�dˆ3I×’ÞدR4‡32+¾��1€3�´ƒÞ�m‡3:»Þݾa8†3441Ã�€3�vƒÞ�–†3;±ÞΟpCˆ3�2Ç�233>Ù‚ÞÙO„3M¾ŠÞ×®Q†3434+Ê��5€3�§ƒÞÆR3F؇Þݽ`8‡323Ï�233k„ÞÝ¿Ÿ·Ü†ÞÌpB‰3Ó�2338ÕŽÞ×­~P†3245+Ö��9€3�ŠÞܽŽ_8‡329Û�233a‡ÞËnA†3443ß�2335Ì‚Þ×­~O†32313â�3nÖܼ_7ˆ3�9ç��2€38;‡346ë�+2ˆ31@ï�$4‚3449õ�9126ÿ�ÿ�ÿ�ÿ�ÿ�ÿ�Á�ÿ�ÿ�ÿ�ÿ�ÿ�ÿ�ÿ�ÿ�ÿ�˜� ô� -2/$#+.'í�!5&„� !$é�6# �"Cd…Œ„gB� æ�'/�QÇ…ÒÄžM�ã�/# O³ƒÒÈÅËÑÒ¸g�á� —Ñ€ÒËžU1€ )Gq²ÐÒÒ¸Q ß���)¸€ÒЖ-�$VgjL# 7­êñà–!� Ý��+¹€ÒÄKxÀ‚Ô Ñ´v3¯ñ÷ÍHÛ� ·ÐÒÒ³-�WÇ†Ô Í”? bÛüèfÙ�  —ÏÍÒ¸$…Ó‡Ô Å®®ƒ;Å÷ò Ø� cÑÂÒÄ2�‰Ô�¾€® §=�$²óôŠ Ö� �"ƽÑÑ\�_Ó‰Ô�·® ­]­òóƒ Ô� � ‹ÃÅÒœ "ÌŠÔ�°ƒ® l°óòy Ò� �.ϯÒÍ8¦ŠÔ�Ï…® o*Áùé\ Ð�  …¶ÆÒ‘AÓŠÔ�Ȇ®f�9ÑýÚ?�Ð� U&æÒÐ>�®‹Ô�‡®J�XßüÅ Î� �]6�c´¾Ò± -Ò‹Ô�½‡® «(†ìö’ Ì� �=”¢œÑÒj�ŠŒÔ�·ˆ®› ¶úæY� Ë� "Ÿn�3°ªÒÐ3ÄŒÔ�²‰®t�?ÚýÉÉ� “¥>�b¡ÂÒ¸7Ó‹Ô�Ó‹®7~ïò† É� ~¥  ’ÑÒŽ�{ŒÔ�Í‹®œ ÁýÝ<� �_aabba`m¯��b¥¥"š¢ÒÒ_�ªŒÔ�ÉŒ®^�Yèù£ ‡�`a`ab…abf€bª� �A€¥ s�>’³ÒÒ7ÈŒÔ�ÃŒ®©±üâE� ƒ��€’a�`§� %¡€¥ P�Z‰ÀÒÉ $ÑŒÔ�¾®r�Iæù¬ ��caac`„a�`„a�bƒa�U¥�•¥4sˆÎÒ»FÔ�º® ­ ¯ûãH�M‰adba€baaf„a¥� ¥ ¤ y“ÒÒ«eÔ�·Ž®p�Mçú£ 8ˆa�`†��`ƒa�m£� �f‚¥ }œÒÒŽ�{Ô�³Ž® «»þÞ3#V€a`baaf‹��`a�_¢� �Gƒ¥ • {¤ÒÒ}�ŽÔ�°® `�eïó‡Baaf‘�€a�b¢�)¢ƒ¥ Œ-v¨ÒÒm�œŒÔ�Ò® ¢"ÒÿÎ7“�ba`^¡�—„¥ ‚�2r«ÒÒ_�£ƒÔÒÉÃÑ‚Ô�Б®BŒ÷ëV�“�a�`¡� ……¥ {�8o¯ÒÒ\�¦ÔÒ§xf`l‹¿€Ô�Í‘®…�Bæü¦“�‚a � �l†¥ r�>o²ÒÒ\�§€Ô ÒŽU'`–ÒÔË‘®­ÄÿÜ*’��ca�`Ÿ� �K‡¥ i�@nµÒÒY�¦€Ô ŸNWl‚žQI†ÐÉ’®P�÷î_�‘�‚a�cž�-£‡¥m�=kµÒÒb� ÔÔÄfblln×ìŠ"BÆ’®‹�?éü¡��ba�bž�šˆ¥r�7h±ÒÒn�–ÔÔ”2Flxñù“$R˜’®­ÊÿÔ��ba�bž�‰‰¥ w�7e­ÒÒz�ŽÔÑpƒl¢ÿö^¤’®B—úëF���c€a�bž� pŠ¥ ~�/dªÒÒ‹�|Ô¾X.ƒlmãÿäW%r’®t�Yðù}���ca�bž� �Q‹¥ Š%g£ÒÒ¦�dÔ¬BI„l¢ÿÿ°%F›‘®¡/äÿµŒ�fb€a�bž��1¤‹¥ ‘ j™ÒÒ´ KÔ¢-[„loöÿögs’®ÉÿÖŒ��bƒa�`œ� —Œ¥ ™jˆÒÒÀ,Ô›(_…lÌÿÿµM£‘®Dýé;Œ�_`„a�_š��R¥   iyÑÒÎ*Ò™"e…l©ÿÿïL/‡‘®j�lóò`�Š�†ababj—� šŽ¥ +cqÉÒÒD�¹š&^…l�€ÿk‘®‹�Iìüƒ�‰��fŠa�b•�N¥ B�Rk¾ÒÒe�•£/V…l�~€ÿÀS«®¦.äÿ´Š�‚a^`ab‚a•� �Œ¥ ]�@e´ÒÒ�h¬;J…l�u€ÿæ3A›‘®ÓÿΈ�_b€a�b‚�‚a�b”� ¥ {*iÒÒ¶.¾Q1…l�u€ÿýR/Š‘®3 ¿ÿß!‡��f€a�`„�ƒa”�?¥ ’ nƒÒÒË'Çg…l�}ÿv%‚‘®G�¦þé-‡��ba�U„��^a�U”��S¥ ¢"iqÌÒÒK�¥‚h„l�€ÿ‹{‘®[�ˆûí;ˆ�‚a�0433>€a`73“��`‘¥ >�Rg¼ÒÒ‚�j¤7J„l�ÿ”w‘®n�røñK‡��^‚a@434‚3TaaQ33@’��X‘¥ e�6f¥ÒÒ¶$Ä\!„l�ÿ‹{‘®{�eöô[‡�baaZN4†3C]U4334‘�P‘¥ Œ o†ÒÒÏ0»ˆ_ƒl�­ÿs!~‘®„�Yôô`†�-JE9…35G^qbaaU€3‘�6‘¥ ¡$imÉÒÒe�€¸O,ƒl�º€ÿòT/„‘®Ž�Oñõf�€��@‰3@Wm……q€a�f€3�4� ¤‘¥ K�Fd®ÒÒª1Ó… Z‚l�Å€ÿÅ=D”‘® ‘�Mï÷k� 2‡3:Of|ƒ…ao?€3� �Ÿ ¡¢¢¤¤Š¥~"pŠÒÒÍ/½Â_kl�Ë€ÿŽc¨‘®�MñçY/…35H^t„……�yay\€3��b™š›œžŸŸ ¡¢£¤¤ƒ¥joËÒÒm�xÔ›E.l¼ÿÿÐZ"…Á‘®Ž @]5…3@Wm……}w|„i€ad„y€3�2�%(”••–—˜™š››žŸ  ¢¢£¤¥G�Dg°ÒÒ´ÑÒ‚23€lÿï{W´Ò²®œzY7…3:Oe|†…wR9€36`€at……D€3�1 ~’’“”••——˜™š›œŸŸ€tƒÒÒÑD� ÔÊy8 _ll·ƒ"6ŠÔÔÉ®¦†dC…35H^t„‡…„Z4ƒ3Uaadƒ……a€3�$�1=‰Š‹ŒŒŽŽ‘’“””•–—˜™™—"ghÁÒÒ˜FÔÔÌ„O1*;zÊÔÔÒ±„®¬oN4…3AWm‚Š…‚Jƒ3 EZZSMCz……|4332�,r„…†‡ˆ‰‰Š‹ŒŽŽ‘’“”•W�0o—ÒÒË-ºÔÔÓ«x\HOfŒÊÔ�®œzY9…3:Of|Ž…�O€38E1\€aY33?……G€3‘� ~€‚ƒ„……‡‡ˆ‰ŠŠŒŒŽ… nqËÒÒ†\ÔÒ¾«²ÆƒÔÒ©†cC…36H^u„…�o€3?_a`a�^€3Z……d€3�7‘� zz||}~‚ƒ„…†‡‡‰‰ŠB�@k¨ÒÒÈ)¿ˆÔѬU4…3@Wm‚”…�L3„a�`€38ƒ…€5332‘� cvvxxyz{|}~~€‚ƒƒ…r uuÍÒÒ€]…Ô¼d;…3:Of|—…6334�„am1€3n……L€3“�Cnrsstuvwxxzz{|}~6�Ak¦ÒÒÉ0·€ÔÉžrG…35H_u„š…�m€3��b‚ac��€3X……i€3�5“�"�Ymnooqqrsttvvwxyzk stËÒÒ–H¬€T4…3AWn‚ž…�e€3��ba�`€�433O……‚7€3”�/_iikklmnopqqsstu3�;m£Ò¹91…3:Pf|¢…�g€3€�€a�`�433R€…�Q€3–��-Yefghhijklmmood WKE…35H_u„¥…�t3‡�€3�^€…�m€3�5–�%Rabcddffghh[N5†3AWm‚©…„:332†��5€3�v€…„:334™�  K]^^``aP8…3:Pf|®…�U3„��+€3�?‚…�V€3›�EYZ[Dƒ35I_u„Š…pVLPgž…{6€3�1‚�32€3�h‚…�r€3�5œ� �>N3AWn‚Œ…y:4€36:U…�d3243243O„‚…„=334ž� �#33>x…�V„35=r…b4‡3Q‚„…�Z€3 � %33Z‘…�>†39g…qA„39e†…�w€3�5 �'33K‘…�z‡39c…„jPC<@Lc€ˆ…A334 �1335~‘… A336<><;4339g°…�^€3�+Ÿ��.€3�c‘… `337‚……y\;639s¯…{4334 �€3�F‘…{433k…ƒ[:3;}¯…F332 �€34z‘…F33Oƒ…t;4E„®…�c€3�.Ÿ�3�^‘…b337‚ƒ…z?6`®…5€3 �€3�A‘…}433j„…~<5{®…�K€3 �3�v‘…H33N……y4B®…�g3 �€3�Y‘…d335……n3i­…6€3 �€3<„…433g†…S@®…�O€3 �3�q‘…J33L†…„t®…�l€3�1 �€3�T‘…e335¸…ƒ8€3 �€38ƒ…€533f¹…�T€3 ��1€3�l‘…L33I¹…�q€3�1 �€3�O‘…h334~¸…„;334 �€36…~633c¹…�F€3 �3�g…|Z?4€3�G·…~]4€3¡�€3�K‹…„]6ƒ34|²…ƒpYC43�U¡�€34~‰…H†3�T¯…waK6„3�2¢��.€3�cˆ…‚Eˆ3«…~hR;ˆ3¤�€3�Fˆ…�Sˆ3�6§…ƒpYC4†321@¦�€34z†…�p‰3�B¤…w`J6‡32.ª�3�^†…�E‰3�O …}hQ;†3244¯�€3�A……�sŠ3�dœ…ƒnYC4‡32@²��4€3�v„…�Y‰38˜…„v_J6‡341·�€3�Y„…�L‰3�\–…}gQ;‡345»�233<„ƒ…�Kˆ3=‚’…‚nYB4‡32+¾��1€3�qƒ…�O‡36t…„v_I5†3441Ã�€3�Sƒ…�c†37o…}gP;ˆ3�2Ç�2338‚‚…‚@„3?vŠ…‚nWA†3434+Ê��5€3�kƒ…yB3<_‚‡…„u_I5‡323Ï�233N„…„vgfr„†…|fP:‰3Ó�2335Ž…‚mWA†3245+Ö��9€3�fŠ…„u_H5‡329Û�233I‡…|fO:†3443ß�2334|‚…‚mW@†32313â�3O„u^H5ˆ3�9ç��2€357‡346ë�+2ˆ31@ï�$4‚3449õ�9126ÿ�ÿ�ÿ�ÿ�ÿ�ÿ�Á�t8mk��@������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ')' ���������������������������������������������������������������������������������������������������������������������.Vqz‡Œ”‘zlM*��������������������������������������������������������������������������������������������������������������� Cu‘¿äùÿÿÿÿÿøá¾“sI�����������������������������������������������������������������������������������������������������������8u¢çÿÿÿÿÿÿÿÿÿÿÿÿÿðºQ��������������������������������������������������������������������������������������������������������_ãÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûÄG�����������������������������������������������������������������������������������������������������p¯üÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿø©m#���������������������������������������������������������������������������������������������������vÄÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿá‚A������������������������������������������������������������������������������������������������qóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷›[����������������������������������������������������������������������������������������������bÖÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ²d ���������������������������������������������������������������������������������������������M¶ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÅk�������������������������������������������������������������������������������������������6—þÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÎi �����������������������������������������������������������������������������������������„ûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÈg ���������������������������������������������������������������������������������������qïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÂZ�������������������������������������������������������������������������������������c×ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ©I�������������������������������������������������������������������������������������O¸ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿü’.�����������������������������������������������������������������������������������6šþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõ|��������������������������������������������������������������������������������� …ûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÞd�������������������������������������������������������������������������������rðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ±<������������������������������������������������������������������������������cÚÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿû…�����������������������������������������������������������������������������P»ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿã]����������������������������������������������������������������������������7žÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ§(��������������������������������������������������������������������������!‡üÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôp������������������������������������������������������������������������rñÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÂ6���������������3‰¦ªªÆŸ������������������������������������������������dÜÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿú|��������-ÍÞÅh2öÿÿÿÿÿÿ§s€'��������������������������������������������P¾ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÈ7�����„ãÿÿÿÿÿ¿ÿÿÿÿÿÿÿõ»ÿÿý‘�����������������������������������������7 ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿú{1Èn¤ÿÿÿÿÿÿÿ´ÿÿÿÿÿÿÿÄþÿÿÿÿÿ���������������������������������������!ˆüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿèìÿÿÿÎãÿÿÿÿÿÿê!^³ÑªªØ„Êÿÿÿÿÿ†��������������������������������������tóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿµÿÿÿ÷»‹ ���������Ëÿÿÿÿÿ������������������������������������dßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿäcÂl ��������������¤ÿÿÿÿ#������������������������������������QÂÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõ­��������������������°ÿÿÑ�����������������������������������8¤ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþª����������������������9å´�����������������������������������"ŠýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇ,���������������������7õÿþX����������������������������������võÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿò\���������������������°ÿÿÿ­���������������������������������dáÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ”�������������������,ÿÿÿÿ‡���������������������������������QÅÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÈ)�������������������³ÿÿÿý$��������������������������������9§ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿîO������������������4ÿÿÿÿ§��������������������������������"Œýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿý������������������hÿÿÿø"�������������������������������xöÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ­�����������������,õÿÿ’�������������������������������fäÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÖ/���������������$ÏûØŽ[�������������������������������RÈÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðN��������������êÿÿÿf��������������������������������›ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüx��������������}ÿÿÿÿþÿR������������������������������[ëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ������������+ùÿÿÿÿÿÿü3����������������������������›ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ»������������¿ÿÿÿÿÿÿÿÿxq` �������������������������WêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿØ,�����������ýÿÿÿÿÿÿÿðëÿÿï/����������������������� ˆÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿê@�����������äÿÿÿr|Aÿÿÿÿ¸����������������������/ÆÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõZ����������+š½Èµ4����ûÿÿÿÿQ����������������������Lêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûp���������êÿÿÁ�������‹ÿÿÿÿº����������������������füÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ€���������xÿÿÿú�������¼ÿÿÿÿ ���������������������xÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‘��������õÿÿÿê����S—Þÿÿÿÿÿ£ ��������������������Šÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ£�������.ÿÿÿÿ_;‚ÆüÿÿÿÿÿÿÿÿÿÈ�������������������‚ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ«�������`ÿÿÿÿ´ðÿÿÿÿÿÿÿÿÿÿÿÿÿÿl�������������������rÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ­������œÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÛ�������������������\ùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ´��<‚Çüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ6������������������Iéÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¹7k°ñÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ������������������,Æÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿöÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿç����������������� ˜ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿB������������������cùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿœ������������������3Îÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿï�����������������…ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿQ������������������7Òÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¹*Vÿÿÿÿÿÿÿÿÿÿÿÿÿ«������������������sùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúÕÀÈòÿÿÿóÿÿÿÿÿÿÿÿÿ÷������������������—þÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ× Ðÿÿÿÿÿÿ‡Ÿÿÿÿÿÿÿÿÿÿ`�������������������!£þÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿIõÿÿÿÿÿÍòÿÿÿÿÿÿÿÿº��������������������Œôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿð�îÿÿÿÿñ$��®ÿÿÿÿÿÿÿÿü�������������������� \Öÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ×��Aÿÿÿÿb���ÿÿÿÿÿÿÿÿÿo����������������������0¤ùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÝ���BÔÚZ����™ÿÿÿÿÿÿÿÿÿÉ�����������������������pÚÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿù ����������¾ÿÿÿÿÿÿÿÿÿþ"�����������������������E‘ßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿu���������5þÿÿÿÿÿÿÿÿÿÿ{������������������������� FŠÚþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿñ-������� ÒÿÿÿÿÿÿÿÿÿÿÿÕ��������������������������� C…Ôþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿá9�����Àÿÿÿÿÿÿÿÿÿÿÿÿÿ0���������������������������� @Ðýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ»qUf¨öÿÿÿÿÿÿÿÿÿÿÿÿÿÿŠ������������������������������<{Êýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿâ�������������������������������:vÆþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ?���������������������������������7Æÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ™�����������������������������������]ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿí���������������������������������� ôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿN�����������������������������������¥ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ§�����������������������������������Kÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿô ����������������������������������ëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿZ�����������������������������������–ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ´�����������������������������������<ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿú����������������������������������àÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿi�����������������������������������‡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÃ�����������������������������������-ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ�����������������������������������Òÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿx�����������������������������������xÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÒ�����������������������������������þÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ*�����������������������������������Ãÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ]�����������������������������������iÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿt�����������������������������������úÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ<������������������������������������´ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿâ������������������������������������Zÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿð8������������������������������������� ôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõŸ���������������������������������������¥ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþΉC�����������������������������������������KÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿåŸ[���������������������������������������������ëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõ¶q,��������������������������������������������������–ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýΈB�����������������������������������������������������;ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿãŸY���������������������������������������������������������Ýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõµq+��������������������������������������������������������������„ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿý̆B�����������������������������������������������������������������*ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿãžX����������������������������������������������������������������������Ïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿóµo)��������������������������������������������������������������������������uÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýˆ@�����������������������������������������������������������������������������ýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿâœW����������������������������������������������������������������������������������Àÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿó´m(��������������������������������������������������������������������������������������fÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÊ…?�����������������������������������������������������������������������������������������ùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿá›V����������������������������������������������������������������������������������������������±ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿó²l(��������������������������������������������������������������������������������������������������Wÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüʃ>����������������������������������������������������������������������������������������������������� óÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿß›U����������������������������������������������������������������������������������������������������������„ÿÿÿÿÿÿÿÿÿÿÿÿÿò°l&�������������������������������������������������������������������������������������������������������������� ÊÿÿÿÿÿÿÿÿüÈ‚>������������������������������������������������������������������������������������������������������������������”ûÿÿÿß™S������������������������������������������������������������������������������������������������������������������������C=!��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ic08��Ôõ��� jP ‡ ���ftypjp2 ����jp2 ���Ojp2h���ihdr������������colr��������"cdef����������������������jp2cÿOÿQ�2�������������������������������ÿd�#�Creator: JasPer Version 1.900.1ÿR� �����ÿ\�@@HHPHHPHHPHHPHHPÿ]�@@HHPHHPHHPHHPHHPÿ]�@@HHPHHPHHPHHPHHPÿ]�@@HHPHHPHHPHHPHHPÿ� ����Ó´�ÿ“ß‚8OÜØß⃦ÜH¡í1¬y'§Îþ<ôRÖÉÆä$xMsÃ}T|P(æ™äFí˜ï}2iËOö$BÄyç$ÝÁ ]Nl%#frHïß‚HÌ×2·‡¶� W ÕŠ…¸#þØ=‰¶&u»*3yEÀ/Ìê8n»Ìê`†¼+È£öZ=+OÅÅ0é(€Ñçi‚<½þ9´ÿ|™û‰ß‚8Ì×2·‰JÖXtGYd"‚¼9µ®Ótp¶ O”ûÑï;K©v;P»oDLRî4šóµÝëäUÛà* ޾nÒ—ÆÀ2‡M{Íဵ߂Xu+àv¼ƒžk^’n66á̼™*…¼‡yÈ 0et¡‰úϾÓKº§ÃÒ> Ñ’µù.Þ"ƒæ‚< „½x‡Ã`äDkÚÿß™ôü#ð=]¼fv(ˆžY:©UUVDãß’ÅZÌß‘{Ð"k$ %d}=;{³ÉS…Ø—Ùìé„5C’ù–£v§MºûH+ÎYe6‡ÛñI¤˜¾jºãÊ·pKÀ?š½°<Œè ×¾×ÎŽùw­vÑ ƒ†¶§R2/­.Å~Ö·¯QÞ¶aúçoÎØ(Û˜rd|¸åß½½½Ñ*­óS /%€ý¹³‘fžÌò«X¼] ~'QWz EÏÀþ~‘ø €²~l𢕜½ë½%&*¾WuÏ1‘•2ZÀB ¥Hw¦w£{`Àxç¨ðÌðŠÃf×BNœ”¢%µFò¿2OŠ9-h ËëÂÎNLËœLAã­¿â ìËdXÿM›iU°&Òòc™árÞTuÛÁ@=qÙ10Yé.Å~Ö·¯¬äg±^wxçÃüíôÏí÷R@VÃ…&³×ì2¡ž5Õ.‡/�kùd‹]!|É?g{‚dFXÊfuÂÞ57ÏÀò~1ø€]¹Äé�ýDæ�UýàýŸ|<“"Øìð{©›Ä›¶U²·ÁÕ!ˆd“¥Œó5®çXÐðÐÚ²ßiÀ(m ßRýåJϼ·y ¸ŒTj¢º4ŸÁ*Ž—Œ ‰nÕ˜ E_Öáí~=P¤ê=ÚY'˜ýšÀÆåð‘JÉéAK Œ÷)Ù„I'N‘#]Û±ÿüf¾Eµµç2NHµí- mî¼KÍi-›ýùiù]%8³¥Ã¥VÕßß™õù OÍ/§?bWƒ9n_ÛS%Ôkz,E!]ŸÜ'@YƒnËåÖ+pÃEøz¸Šù犜‡¼ül°Aц‹J@ o›§Rÿ4%Íö†i‡MP»›À~Úx@9ãBSuI>Endà¯éÝ…¬¢¼Kxô}g‰F¥†ª¿ úcÁm_y„ß2ÿ4–äÜ4«ìš¾òâì&¢Üš*õ1è^<Ý«·‰PBñ€¹iJRCX /²ux…ì D›°›”ÏïþÞçϵŸ…c?:ä‚¢žøE­vQˆ¦¯ßdµ\Í´‹£M±÷r€Â¹B„‘z%ÑÈî?¹q]¯¯Ç©< á,üq¡™ÄÀ<’¾®}ŸßZºsWê|ðö©º§ ™$ñºèäfr)2‘€jœ¥d-h: =•6Žì¹LNóÄœxÑ ïü;GPef1¾‹áWîpC„Ñ(œXÚ“4ù9Âqñ¥"¤^3dž qó¿7 ²ógµg§*ö3ÐM5…#|étÞ)3 Àô§a²ƒv&ÕÿC²*Üa>-ììØSŸÛ®pêqß`)@‡èLÏúp˜Á u‘gêÑGøÜl¦§H Êçh]ׯÜÏø¦í«–$ó^j¡9ç 6÷z‹îbck!=îÛYú u€¯ý’21†É)Dª—:vÄHý&_½ëŒr">¯¸•é;AÁPPðÅæFr zþ!(Ä>H§"¤cò_øv­¬oƇd·:]­ë¿Îì}¾Ç8pO­ëPq‘KvG޳T¡ÈŽì,›p½ç.›=«ED}äèÂm×ÁáÁ)¡–Bp@Z¯×c•4—£nYsÉy õpï f§ …â»+nå QòmÕ ôv`xRZ“p*x­‘Ûù”Iôˆ×VÝÃ}1>úö~öºK¨GÝ^ç]äÇŽ€ +¡À©ï?Þ4´‘‘ ©© ãÏ¿Ÿ…}?;vÖŽÆÏÓ®okG€„‹ö‚&™PiA•zÐϹ¿ã‘8Ác–¯á ŲǾ<¢Pk&fx˜æ]—˰¦mu¡bþÅ­I†Ô ªZ»îÉï¥.H/ÕOôasCнS78W®Ció—Ò!ü» 3õ³M^+| 9›œf2õˆn)øÉs¿6 £?•Ô ƒÊù®¡Ÿ&˜ÔâéZLU'õÆU¢ ƒ2ÓùÈ/þЉ§2/’ö›(¨fÕsö|,¾=ßÑÒüï3ÐM‚ºÑJ†#Îx,åXç»4äÁªi�#ÂuK-2ÅU§(P…» aÍ&ãÓM“>\ Gc~”—torÓ`+s4σØpI5-EÔ¯×#^Ûé}MnCŠäëY5&ºû4ÏIþÓt`ÕáÝ ¯ƒ .øoÆÊ%«î@šYå5×\JúnHË‘g»ŠxÓ"«)òæýÛNÒ&­Œ vYÛk¤"ááðÄIKXóE½¿wÉ9_(Ä>H§"¤cò]vÉhPRÛî˜s²Q~sà \}Ëø|÷9Œ”þlwx&œ¤Vúiò|�[˜«ðF ‹[ZYß'%œ{žÈÛ 1tñL¿5ß Pµ ÷7ý@a$ª®z`î ~iEÉzAÞ¥ÿ;ƒzûäN– eÚ.Ü8’]ý®è)¨¡Pnpü,âÉ€Î*Œb¢ÌÖÑñzדûN6öÖ }kúŸ²-ÈGZ¢¢äjbîy8)+^?6ËßϵŸ…i?:Üs††Å¬CpÁ‘-ËÚÌ×±…Î ƒzî*7{v”õ¶‘ï…U^r!ÚŽ å!öOt|ÁÕˆ©é¹¾ÈÀ"G£!ê4$Gføk´â©WŽÇ5æ‹GÎ9C¾€GŸY‹ÉWÚòú˜þGþe;$¸¬-âÓšœ¤E€y&ÍØÑ=5—uý òPb$„5<Ó? Ù“D³S‹ cs©e™}Ášs"a<#y›}–ÑÇå<®ôѶÇKÙ“'3Ñïf`[é2Z8h4,°5Y¥x|¿2$È3ýNQbÇQqüìCƒþ~äUË´¬~wKƤ{À.%-U÷”Ø‹2* îu˃Š#ïEâÃZ49k›ç&5ƒÖÒË(†HŒ0Ò2Rt­+BÕ\Š_qñd6Aü_,4ú‚Ÿüì¤1Õ©0Ùz×òD„ÑÿD Ã´óFÜÜGž П^ŸË|M~¸5CÑ;(–Ø¿{PÑhë;“(Ä<ð£"¤ Ç©Ó>BJ:ˆ? ]×]ɬûЉêðœ˜Yç€àãi(N•+ü¬w$¶6$ÕºòáÒÚN‘ š( aÁÉÔ[>îDù`¹ßPåÀÒü ._ ¾ú�]˜~°e8/dû,Æ ƒ…()@Q:ŪflRÏa+Kü|[c©¸;ÞÒ«p(z‚ËÓV1ûj„aø/Æø,_¸” ßÕÑ?¨„ä#—Ž�5`$¿ß”Ðnbˆ?ß9~tìüéЈ˜Sæ¬è>–%&â š9´Õ”¨&ô,é`ú“Œû[)vRÜ:Œ€‹±ÕÔ;ëÓ3mÕS§\ÎqÆ ;„€PŠ|¥â8\(ʶ%"6!nS¨MÒ?„5EdAÿoþEëMFþ±~ñð¡dh¶øË±H;Ã"?ÏF¢LQ@=žëϰÏw{ÔÉë–U“¿’ÓŒ£×æ?>¼õÛ±†êì–oƒºÂãëlLГv¾§Tb÷“ u©{.elˆ¡uª‘PD ó¼y-ÖJv2eTOªLÁ¿ :|Ã<³UFîœ-C³ŒžiÏco"hNJO’iÜ`Š2êOÌÂW9ÒÝÏXxhGõ`¹:•€“ö¬?ÎoôkÍ»E'4W)7[­ÓiŒì~ݾ/8nû¸ñ�°ÿ;g™àëÙŒ�CbÍÝY+î-|œÑ^ÂPìç¢ùQ êà¹íOƦ(•¤1î½°ï0áÚS¶‡|ü¡äym¼)àÈFsuü6g‡ztSŽmd^šÿ:íñ®;›°ˆ"6,a`ÙL+í9¥ªÚÓ†–¡òþÏÜö`¨Ý³šõ´ÜÄQÙsVŠ‚ÖU÷>t¾5³ü9†ñ6bøþQïÔߟ@“ð߉ùôÉ'Õ‡ Ñæ Ô«×;»ö÷~Vè«®®ò5²ä‰¼*a´WÃEo.î•‘~kR£Ž§óU¬mŒåí!a{§³šÍ}›014-’ø£rü%y¹qIü¢†loÏÑéÊ=âT‚¡í¢]ÙCÉhá ›‘¾ÄzgkÊN©™"dÿI_¹&ÂVe´ÑaÂ’ Õ¨Tvé›ö´P}{¼HÙ 82«q a¶¶WXÆ—‡îšB�Ì)ƒ¨Ç9ÔpË8}YM¯w¦jõþ/½c¡b¸×z8)MƒcÛ“ð©gÏZbW×ui)ÂRQÖãr@PÞ_zJS­ƒ@Ë+æâ“ºde!PÕË—Ï~øL¼¡0ïÓm´c HÐúv d¦È4¹Š¡–µ{òa¥bÞ,H°þw|ðqú«W9+Dƒ6͘;ñ’0r1±hvVv“‡ŒáÉàÉk&˜ýˆà1fz‹p>䛿Qþ–¸#^19cVtO1]'Âësð tRu9÷9=D˜ý“¶Å'¢8‚ííW¯Þ÷ü¤÷Ñ„´�þ㾨ÝîÞŽ¸Õ­„C­·…«•;`™S°ù7ÉQpǶ»ÏŽR0Ç­owÕSq!PZ"óÑ»$¥‚«à]&û"š°hc óÚÎRØ{Ñá¹Á?JßM ª¨Ô‡„ÙÂ&Ý.­ùêa–€ŒãBÉ úþ•TçÙ5º¬<áëªLÕÎ/Y»[¡ MÇ£˜ÈAÝPjÄÅ3£f,4ŒQÃáqMUsÏÉà ™U°âC§DÊoB¹}ÖNF^&3‚mýОOóÈÐ xeTgp)Ͼ¶|èHÚ§üÅJFT{/rGG†—Ý6¢´íGêbX1s‚^Ý(À«üvÚV Ý2÷�óú«ýW]†Š×8RË®§Ç DMèË|»ßzŸÿ=£{r#+D=tèJ‰8 Ñ8‘«åI¹KþϽ¿'tµ¿Ýs7©ßÎຖÿHGÓYº;¯éÚý넆,òùí)M¨{þx¿!Ê{½q ¶U»•–<= S\ pãÛw"­XJªd$;ñ¼;Vš©ò##¾×¬ŒΈó(õ5ªÛIHð'eöúšª8ŒŠ}}åë\7Ç!úy¿7yØf´rí²¯Í¸ ;D·¡x?¹k¤(Hi×ÑÄY“ò&ýþ^&¸Íí…|~ïâæõ`o»¡·Œ›¸À'Û”³èŠÖ2ƒá> â^tCép1½·Š6¿ð¨"PS¬¬—•;CŸMî~Ô&€=@æé7nêÁ-Z½qe¡JÖ¶etzþÒ�ÿr«*d$•Œ[˜l¥–Â];éÒœä×èÒôf“¶´·EOfz$b`Íõ`–#µ&žfi³Ëò8ádê·€ØêÝk`ºÉÿBÙ­Ò²#è/CcÌBôñºçò×­²:ו›©·±µ´04ì"ä³ Ò1³ù9`q`v™sŠŠ¸®r‡%õÈO–ÔÈ(ÂvÈk˜¾$}40vÜ å·wz9HظmËáÊ:èím‡}þw·ÁcïP︭ñ©@úh“©Ç‹6ȺW+½­ûDúì6ôJYVyÐ[&Oaøµ—Y“¤Œ¾«VÂ`sk\W\š ˜v~ýg;'*¤nÍçÒ+÷œkÂêÁ†z,BW¢Ó®^XáÍg.h!®ôˆDm_& ãLi Ä{D¤SÍÄ‹VÐøÜLÔá�ë]Úd²‰nÒ@À¾ŒãK Ô“vÜÆÝÞÍNòZ¼¹çÇ–Žmm¸%’Èm¯S¦!Ö©¾åð^‚ÒPžæùôlÉt>™`ýêºåöfÏP'`¹·~v{4ÜÁÔMwf·_ù¼AÕ6® ½bÑ‹„‚W!,ÅQ%»Gà¬^mâc~™)TÄÐɲu�#Äí—È÷§‚?²Þ¨©˜¾I}n„3É7qQ¶$19+2¡"=$<©ÚýQLõàϤV1›Èµë’®[/ߟDsðèR~}€Àv‰zÿ;ÿ<õ'«Q{Ñ'V9 –jÝÙôfá%(2Ã@ˆÌ§Å™7Jª^>` $É¢Q5)TVŸÃÄ�dH—שd¿“Méì‹>Ä/ƒ×1>c7IA¢t=.&/Vw{yï»™KúÒÔ \ Ð ô5š+Ä%îªUܶ‘rôï~ÖìÐ+õ†ºp.3£KU£`©•Xx{m"Ÿ錎° ø!R}8,¨t#…*¢Ÿ$”ø¾@¬¶*æÂœÞµ­øƒˆUUð¡ô"i6æ½ÙtÎ}“ïi-ÞÚá¤Å\ë»XE>Û¡7ÃN´ì Öí$*KM2~ì®Èxxþ´lz8)€•–z*Kª5«IxåSLó†çs¬Ž»Õ/Ä”Òáfß]ºO*áy±À¦ÿAX|BŒ„Ááfg<çQ¾xeŽ¥Êd/eÞ…–¡L“÷�&pÐ{E¶­–7nŒ¾ŽÖ°xëïøÆ ð~ïǵ…+ 6ï!ÃW q ‘.øëZQØøk‰„·EõòrFËÎŽ[NóWfbãUB©ô¥É¦k ‚ƒøˆØÅo˜Î«Ëä¨=‡Ð,–jŒ3\»Ó�YL,qM~¯Qq²_Õ[Ûä¾þÏ9Ðu ¼h?V¿YE‹]bõ’®=ø¹Â Èa;¤LÙc¼T@«º’äTÖ7Åp…ØÌ'!àøž/:8h4wÑ4ÖÔ¶ÒŽÓ÷ûOM¯Œ´~qË–âýhëcaÏúeLjl4õë7˜X)ê¹û5ŠICËsí)‰Ï%¹œðêøàû]ñ{ßX®BgÛP/Ǥ9¡*mcFë<Ççäº fþHÅÊŠæG6é9¡±Q +|Nü¬5‚O]…”k³ 1Y‚9œ²—à˜œzã™CK6á¹úmÁ³Â©‚‘ ýø6£¬„·c>¶þ² ]úm¨–c€(v¤¶áIƒ¹cÞÇÓ "r‚ixjÍ©ÒÿzYtPå…·Ò:-Æ‹–}¦Nn]½¡Qd ÙóÑPö6æ­oC:Ø†Š£‡—~U÷ª·»ÙŸtf.Á.û¾XÔ ¼ìíâ÷á¼ÈQpõPüú "BŠm0WLÿf,}²f탼V•–‡Ã%Ø;ÞLwh†vÚþ,\µ‚¸«ª^SÄÖ‘häwåó=éo2”-Zü‚Ô{ÎúQXÚ¡¡(|×2Ü]¼Æ°U©$°ÛB<dbM¶”'…¼t&#1˱C„%³×`èæ8RvW¬,ÆóúƒÔW4AÐ]vÚ˜[EÊ Qßg1N~"œŠe¡ºÕ%7Óý7 ;ºô×b‰‡×üí®NޤgÛE#ZÔU27å yK$Gw¢’³[Mñ§kw·O^ög gÎ,Í=Æ‘{ë ¬f:„fͯˆ$,RX%œÔÈp™Ä7ëH ÉÓåKT¡“<´c ŽpœKchœÈØ®r›Ñ̨0 3]ÈI¶£Ãý»r×M>[TW^þåpñ5`™2}XÀ bT(‘ªËA…£ðI·. Ð.±xJ b…B‘/ØÉ8…À[×¹ÂNã²`ëί°£¢‰ç`n(¹Žè1Q Äó…¨ ›,>2ÊÞw†Þfì?ÿ/m4‰Ì9áß~tö7Æ·ÚU·4D Ü Œx:´I¿õG²N˜áÔTþ)ÿ0R¿`6u… ÖjÌÅÿ~æë/û›bûÉc f»û›'~·»åÖ’ý`vïïëp wU9•m@Æ¢.† ãÁ 2ÀY‚²¶u}°éŽ†Âšq¨1“PKVªÝP$[ùUüàe©ózðúæt6í£Ñ$ä°f—Z»Vk^ìð¶¡éæjR†�»®h$vë ‚˜÷F–ÄÙ‚â, >Ô-`רEÑiK˜•\¾®šè!Ám Êß³¯¨áÉƒÔÆÇîû +TuqƳ2¶¢*PÊê³y¶Z;ƒAjy8¦U$¡>™�¦zšÍR5­±>üeòª\¬wu¥àØéF>½%“¦ãS áÜÓ ÅD™I7[à±`éMa Þt¾)‡MnÎòÏaë‚€$HF¡e±1€×õ0kè ²°XˆØƒÀl‘º'ßžþßžñOÏ}�¶ã¹*ãÊ®%ø½Ü¢%“¬O;½_áá˧ÜmD_^ã©¢HËíœ":<’yÁÅqóWï~#q ¸· U�ú 2õaÞ ïóØ3íx÷¨`:[¾7…ñ£è¯zÔΨƨ4ÝX*7&ìöqý*üYm(´HD'¿äÙÖ(1f<Éé\p-cê‚Çø=±ž±àlñ,8ì¦^þÚ™š÷‹†JdÛ‚±ù¤¡<}²ÏÌ…!¥%¨>÷邱.wÕ‰G2sPÉà pŒ&ò±HÂY[r0‡Oó1j�«.­äZÈ“$† 8/˜gç轕ºO�'J™×´w8yÃ&¬ò)‰+RD0aÆ"´µoø›œ¼þöšä"ïÆ^mciìΚ¨ùr.†¨³“§-?¯írç?.'eä\8k¢R”눚æâ4w|Wp<"©›{ä| É¢ibpÏÕ˜ucý|¹kö4ÊIšž‡q¸còÍ¢‹ÙÚ @~ÿ0UJÉ%1 †G-¦¹”ƒGnÜ;x°ÔûvPü;=TSè§ÿV½×8‡d8¿â5´ò^Rsérj(åÅeêo2–:¡aHû¼g]­ õhâ0&‰î¼$YSâðEv4Ë¥… ’ySi˜A€Ùƒ!©¡ý\`wg8Ôneð9аïWq¼/BÕæÝ¨a;XÚÙžJÖO-(QB2Z¹z’Í#…R(¼aÚ4fiÏ™0–ót”M¤ýBQ™^ú!·‚ÄÀEr6Y)lÖµ ŸðÉ“Nv93à>äpØI)ðæëFÊoc¬.õ^ ¦~º O§CÓ‹¤(ÛØŒwè3„˜G‰n ˽”p"Â:¡D"®2;ZÍuõ[«#'±RNõ {ϲ¹–Õ/¿©é¤®“"m‚ÐPô+ta/žÙ§”Š÷\LÞ„’Ò€ÛÛ'±4̳šSø³:(ÑmÔB.!m!Â'KöõðK¸€VnK„ø Frü!±  ºÄšY¨@èó@à(ਦ6HkH@Õº‚ê¬fÄÃæÑy&]qܬMkûþvzAh»îÓføW¤·ò¤¨újCk©ªÙ­¼ ù䞦@ ḭ̀N*£½y¾•©Ì˜ÎYÆžätpäºÙŒ´ðÂ'(Ó€S;¥‚£¹ÕÅd>­ÕÈÀÐBÂ,.moE,°#šbW¢è'>y1 ù7Nw9?áÇÐÓpV ­Q½Š ýÆí ÆªLw–GdŸ\#´ÜîÃá ø¿Ž&@ÔqMÀò’š0-n¢?hk 4c)ÊFfO%’q—+ÕË¿•˜fͯ„"6à(4Sftéùjx“hïéܘ©ÔV÷Ňgš­7Óµù`]OºZUeiÞÿü1© \ìAÅvÎ×™!ìÑÝ©âk¤6¢Ê[Æ(Šù‘FØc=øe:B/÷*ŠÎ ¶{ëÍðnG›»+™å%Ú­C ¥ƒ3ëöì¢3¹bžÐ¾)à‡×]?4þ„qêû'¥ƒ'!�ÈöÒôWDxÕ”›Ô¤Œ¡¬"‰IZeöãc„õÐ1s{$XÓ xÜÊ.gT¶gž±|ÔñOrÝ!iCöªoݬéØ7 rs¿”ôàÐϽ†•!¤Ì„§ÉÁ¶¸Ÿ¦¢ó7èá’©'þ*­$Îk4Ã*ïêQwXó4Žwßê35v*g%üÛ±�ú*há§Ú3”ÓCè‰ò?y(´d#ð¾·\ ´]/Ù†09E}¶‹ü÷I®6O >¶¤#ÿRÚpÁëûg /fQíˆ+¥ÝÇõl…À2H—™²M«"øáR•µÚNJ—ø‰%qÂiض™f¯‰0ÖuCj¼Ÿ' Õ(JÄÄBß Cý¢ÂºÉ•,sL¾ÇW³Õ%r´ !Ùúx¨ë–!€RKÀÄᜠö–ù%OåR,g7PöjDs¯u·Ü`û‚U±g)_L}æËßž¨ÏÃT'çª`áwÔÑ™åŒ&€µùIXŒÔɯù¥þSq]ô…kÅ©î«q¬f6¡e3±…™Ár§}í“Íý[.*DÐÑñ'»­á˜¦2ª&ÑláÚ6\ ¤ºHË_Î.¨Š%x}C§Y'‡�¤Š<3'ÞwþÏZõêëo¶ø4#ΛFÔx“€öSaO]×»øÚRaBöXvè^ÚZ-îg•h„×^m“hè=BÂÐ2î ž{n‡’¬2Ä„ðùÆ##¨÷uÈ´EMôØ~H_ƒ61uÄBÆP‰!Mny/û9#¨Šoˆ÷ŸVìƒ U­”a΢Ÿ)g›UE­’ñB¶Æ°˜j жf_[šÿi{;'º Ü[­vŸjL¦'ͬÀ¾G0åö½z^${š+Èb.Ë"Ÿ#‘wV„¹]ÉRõö-ód>——Úó«Å¹½\4p¯QË`JftŒ%ðщJãhæ«r—CrA£Äxú0ÓäêKøÃ‹Þ R²YÙ“9µ†`¯]o鉙‹‰<fÆDʵ&²¡­,k~¬j^ß$ƒÌ–±xAYPšij1/'Ê^oè® Ü ë½<¶+¡Íhتõ'/­”ô ḻ#,BZÁÌdctåI¾%jW÷î[9u§!«‡ «7æÑ¯U#JÃDÃ/Äþ¢úï!äTC1®Ñ}bJ”ì/‰©‰Z„Ø nJŽWÒvd!$¿Ÿ›ÄlMDÂa9اß2@Þjû ð®ý¹È‡Ì$‡U;;—Ä£OÇí¨¿ÉKK ë:y pG.Ç„W»Š§ ¶ AºéRÒTgÛb-i4cT)¼gÒ ã³†$Œ=ÚŒfœë�±ÉBq¤VcÃÒì#Z¯�« °¤Â.ZªYz8ý?¯YïôÁï.Ðmnìä‰k2æ·çHJ�ÉSpÛ‘ª•m ÜÇñ»xXrõöM® s˜Ww¶§ÿ8ü!¯ÑåÆI熄*}Ž„»•]Ö¹‚òc ™&7—ž0ùÇôŠ›cÀHˆ 9T®óÎxâP?úS Œ£Ûñ`%0º~ ô{‚M,‡^%øÚ†œÊ×ä3·§)i/Èüs;úØëêk!1D{ÿª;ä_çQ„y$ Ã7xØþëðæ“´¦{» \(v ¶*Yn2L%î°8[ÿT}q’_I àº:£UñT¯÷Õ(vJ ‚Î7ŸB·â”GôÚ²ü±n¡þ>~tÐÛc vø¡‹ ˜Ù˜K‹‰ÀH#¥ŒF—͇üˆœ…5“ë0q›5›J¨f‰hj²ee•“ ’ÏÈ­°ÀÁNæ–J^å»ÏÃÕŠ~¥Ñøzœ€ð\$ÉvÅPØÀ3гUHZÆ‘Zý'<éË„l×SU­û`Ý΂“)tÉŠGœïï§-0`V~YGè�ÐàåŽetçCXPt^\­Þ‚™o™Ô0…×Ùð/µc ;&ã ïùœ¨²ª¡äÌ–ì~Ú-ÞëŠäÚgüñü<(÷›o‘nÜ„¢`^ß<¦–MF"í¯:ÛÝã®mS‰`fðu=3ì—]”J—È òµ¿ 9 Eäg rFEkMÕ RÒSûm˜Ÿ`:èѲàHç'ä%zG!ç':Ù^ÂAæ¨w,Ýb•-_püy œ-B?Ðm„ÌSEñ„·ÊÒé=çñéWÞ¬˜ZÉ´‘É™øïÉOX}l‰9_Í3­¡£nøZ#O¤8Õ: g¦˜$ˆú´í­"ëÙYŸ’Ö^éÁ¤™zޏ(óÓ·-”ê•ôÃ$]¤e!ùäLs»l@¸Kx „ðÈ~8Ô{Ⱥwhkéáz±ªÏÔöÎ<†¼m_ÔìÖ»êôE¦ÞÉ¢ø÷�¦©läù‡}Ê~¼7Ì,h:+?¦ü_ÌAÖ1_›FÄp¦Ï¦{_‰ØPËísV*Ô,/ftiÍ*sA»²ç$Êž‹ä&^¸·¹2Ш£ ƒ¡4~ ÆùÓ‚D£âªFsÌB•‘g 8©È¸ÈK@5ó\FÑÖýýfwq-Ê”Š¶ ÁB¢óñAJ5lñøõ¬U„ükL¶ˆ 6¤\® ‚LºÝQ•_ŸÝV‘úšl©`$×ïádKv˜ szæ·Þzí7ÕZƒ7^cãðvö«o&ÜÝ'ÂnÊ%fÏ÷î$:$áóxß×ÍΞÈ=n“‹¡‚wÖ›o6±] "ÉŸ|r>±^§˜ØŸzÐ"ÝW:µÃ:cÌœ=ôû6Ö¡ö¯-ñèH´V`éôˆ÷~¯H[6qRéËáKï_ãQò0T—@”΢¶ºéǘL³¸øgí.bò™é2^S ÌkýƒTìº-µlÏX¢/éIëJÊ%¨s²OÞ¼C÷l??k˜mµ ÷Šx.Ë�Tä+"qÐ!!\sÉ¥$¿ò:…:U]êsâXðˆÞ=œ7•*ÃÔQ°LÃèi›šG¸‘ר,Ðǯ¡¦ÏvVôIA(ÄXY•ÂÛõ£„Ü3’Ç·ÖW^eÚlI IÜüm¯&=:’ÿ *ñ¿*|õǰCQ!Î]þwîA‰)+Xuè‘ $�Y0ØÈËnw›½VøgþtèCÁ1;ñjÖvMAH ˆm²†Åq-͘­~)U5<™„níE<ƒõAŒà`Hï‚uäq|Ù'᜶GYðÑ߯Tý™Ãƒ]6óëêØ%þ•íõ Sç™é¡gÒ5é¾Ô…=N6)aû@±GÆHÆö‹¤øV{ÿTÍ[ Xúq% Ìɰàë |…s^„>){#•­#ƒ¬:k?Z9„ù_ð˜¼î]Ÿ™ƒHC7÷vËÀù=ùTP?Ϩ\åÆËŸí×96äZ:Ž�Šúj32å„Ò,Æð~¥H£8ä-,ái©”u Æû’Qn³â0lGå·°l}+½Iû–9QmcÚë>–ŒÏ¦öU³oãä‚à›Àe,Ë$1æÇ¶e–¿HÃëþs€c©…ˆÊãîhê“;¹VkpfzX­³±&©å#š¦zƒOŸO ?&&°ð'Jf´ê¤Í ë|B¦Ÿ¸kn’oÁjñ­j´xcùë´vï1%j¶}ʨŠÌjPk¯¶üY.sèz”¦ ŠdÑš¡ZñúvÙw( ñÒÐÑ]øÌY>÷+ÔoÅÜäÏ¡ôæÕÖo悜Ÿ®h•+;Tð…2°9Hr„“PQEòÅ+qØÙÄýÕ"ØÍ×BðPÁé²ѱ˜2o  88+Þƒn9Û%§a-«û2¯¦ èÌ>ÿ ®ó< Ä\ú\ÅDM¢>/ÝqãÍúnõ€uŸ*è ÍØifÆÆ›Cn"Í9ˆcô'ž$ÆPX.OÄŒ6¼=“Õ»="‚™iPp…�õ“bÀ²Xà” ens!1°ó Ö"ï<Ö»¢@9¼‡V“ˤ9é0ÐÒMÕYc‚_M,ž†e×u/ùêò®kŽO·Á¶ºnIúU¶‘&BæBʲN—4(U…ž ãýè~ŸS[)ƒçùä¶kçw;ËÑËäíÔ³ºP‹©=;KJ¬œÿTõZKÜ,“Ú W:‹h¡Á‹DUØkss´Pʶfò¬” ðvF/‚ ïâô¹™¼€•Å€à¥hê�s§"œ]%¯¨u%Ì3iï08¯.&‚i@{«–“Ô@ÅÀȹpÅa}WGÌõy-rðo$2ÕO"ò7 Rßhù+uÇ}Ï’‹«ˆM  ºoÞiª®R¯ù|(P¯FÖ³þU.„/=v£U,y‚×õŸá"$¦¾CDÐÌUó†dÙG,6ßÒ‰¼¢ ¨Tzq,+ºÎbùщhˆÌZ/ªo´ÝReñÚ\{*4_F_wMñÅSÔŠÈmf4Œ70’=£A­"WcÁÊÞDûE>¯%«×.îúXÀÇÅ$¹ð—"ûÇårFRÿFÝæO›q.S39•y½÷©{qúºÁÈ“ffJбPÀ2Èï#&pÈc¸]¢>g+ØÈ4ûPéú³z¼ ÏB73*ÄìB¡PàÝ[všJɵ*eõÑhté/ý©U‹)]ARí½>ý٠н™ìË+.’:2?Ükv‹"ø=$ìÅþðÜmÈø:×*EñUêà³`¿ÖœmÔŠ¿=ÑTqõ­ûöÃo@0c Ž/¢ 1ú­SÐßÀZ~ÒTðü;9`©N ŽŒŸkÍ…„SQÉ€jšæÌ÷iv+ ²*ÐÛ>çïv„‡Î¢ ÔOÆ>ž‡ˆ¨9bíN+§¾dÌ›$âñÛ4q1:ŽÑ’h´8.ò -êI’­(1³FMm/<ÂÅùI¨Ž;عáéH“n¥t(+ûâ:²èËY™à¶XÐqe媋á C •¿æc–-W"ÿ/–õl‚ªæùxtd‡*j¨Pþ¾Dÿ?ß•92ްX÷È(v¶s‚cúP ·4'g䵤 ç,Âå¯Ï Þ!è/™d|‚ZÖ¦ž&ïÙ@áÜׯj•Õ‘Š"û9T’­´3ÇÔµÿ"bþœ·ŽÝƒÞ[TÒ?ÛTMÓFo² \GpØ ÿny�atïþØ0tX‡>Š ½å€xNMMù¤¶d:7³LPéNgo"î ¼4:®†*˜Úçõ®¿e8GÃÕèo5wÖ¥} Ø®3‡"C-L?»ì£4câÛM‘0¤®Ÿ+KUÅwØ”ÊÿjµUšh—(×ç7믷U«­ü)Ýè<1­f¨.ƒ¯~çì€Ìþ8ü@ÏI³Ø›¿Íéˆ æÀ&åÅßškPÇÑdá`ãÿ?î›™rÂ2Æ‘¼;`öl[rUîì×Ë}_–iàUn,§Ã u3h?âe…%p@B’«Î ÈƹØCP>_ E=·ž¾º5<ÔÏJ‹ü7œoIà•Cù`1Ï(è ÍÜHÒöÉŸ¹»} *È =Ü0Þ5ÀÇȘX~¶×|8µ´¦X”ä#Ÿ ³³+z] +ƒ¹Í6¶ï‹êÑ:™rxLú‡”lž¿üôàܰø›*[˜Ÿ…›–µî‹¢òVôv64AÏ*°æ¥(¦DÛPr’+Oã 9ãçsRŒä³¤1ÐjÎuó÷ŠkVe¯íúI7ŽóëLN¸Ã�[>iÖÜVûYer’}·vÐG'–ED4bn«§ëiÖ§G翼ŽC �?®ö©mƒì7B¶<â)†<æÒM2‹£ !öÌž»¬/þ†\Jév·CØèJÿ9Ö|©~Ól”¼È¼- ¹þE—*Jx©Øú–¦[°#á.Ù,¾…;-a¸³Š)_³ùŠ·eâŸ&«{©-•ó`­T€„à¥0E¸œœãAXëµÏCZcžê´. âà0Åò€8ܘ,\ÞÂ¥Þ9í öÅbEŠ&‡gl¦þ.‚õgÓ•¢Pº|}è„^º]»e1Ú Ðã4\Ì^kuS˜Ye£[0¢žÒ\W. ÈàU=㭚ظ,'}Ó>e í×1½¥>î.¯È"VB(Ôœb¦,º˜ó®è.?©q²2|Ñ¡¯ $¹¿ë¦¹÷ë}z9šãÊê¦Q›ÑÌWx 6’øjÈ •nnx;é~ 8Ðõ=ä+ä¸ÚÏ*§c8@|ò$Ësj¼>¹GGPV‚)¦.D^'{œ·ËMµzP‚Ãõ/¦Ü= çúSf6X}ƒS&„Æ…Û[½t÷×Ñ£ËðŽR{­w3�…_6•æH›JïþE¬`|Â=•]U;±2Ò­x¡TØ©ÞË­¢Ã`®ÒÌÜŽ/ÚDüvK ‘¿¬ýÌZS¶îË2)†y3ª` âs0ûÔ›ôzðkú2ññ ?›#\�Q§d€T¤Ì¶1¤Ö«î­Gy�)9´1¹‹okÕ³ûÙrƒÕ4S#-ÄÓÑ¢îÂøa=JÔ=ÿ0][3)`P¤_sõ@‡Å—Œ �t]'|(m¹ù†›¦à͘v– ƾZ¾ú3_­c¹_é"<ŽlSßÍ$†Âõò¸‡Š•A°Ìâ])s!%ÀÞo¨®ÃáL³—’»K¹>Én­;êaò½þLÉ$èymTôŸÜ]Ùñ£íD/6ÊB©œŽ~-ÀvÎÚtéDþyÎ)½ª±D¥þyáðKAz7N‹ø]V^9ÄÉŸ"Ìý±d®£<ïbñ €Ç÷=sK¦§¶Añ€*òb1ß…]½Ã¬Fðí¿…©4žñ€S”.þ86“/ÂÄdˆGb îzîBê:äèÔ`eÔ6ÏÖ·kN÷ã€hN³YV0z§1QÊí8Cg çàú;%z2îÃö\ÿ|"€!]aÊ­6¾übmºßK(fÐÑyÅ}Š`t é ¨pô©?gr|þ/–ì<Ck x9½ñÖ1ÔƒxºMf¡ûž>òY­®Ì5¡ùà}x’6ÄðçùDíEqž 6Ïè1òª'µü×Îär¤¤?Ru‹ËzÝÔ8•LvÕ+–eâ™á-3¡¸MÁëÆÀ¢ꂼH$l ú'X½qõ þS¬¬íÑ÷í¶Q‚ÓÓcd+óJ/d?Ù¿wȹx·Cžþ zt‡ÏÃÖ–~®ÑøzÄ�à• UÏHeÄý«ãÚ7;Ô×nÿ\à¥}uˆ‡¶Œpò¯^Í;Á$$Ïz7o:´YRcs¨M‚ϰbÐÌ÷zý<vb÷ckˆG¯½¡W•ã_‹Ýꈧ™fJ­ÄÎßæ‘(W¶Øû‚^7Ö7ÐõÈDõ£ˆŸº;û-TK¤ÁÏíŒc¬%ôé§>j,òÛbn©ÁdºC¥Ê†¾ÿ gŠ™ <Ðv§M&oÛžå. kËïÛ™Úíòð>‰Tw�`À16«d(^µTׯèy¬ ˆÑ ó¶ùŒ§ù_ð~…I<™yi'rÓ"s Q?d¾¯ävfwì&ìZ³ÛŸÌ©'ñ{ŸVI>¿®†ïUSØ$ý¹%§»©­}UXŸïrä 6Xùc›eV ”VbJ¯’k-AeWd¥úúÀš37äPeãMšq'¦Á½›C¹¾åÕe%ç‘ïÒåæs·ÊA'‚Ñ 6èf1`wÅö°YyYq9®3ð‡í9-‹—�cJt<©ÏeyðfÕm <á}Þ·~“åZª(¡ä.IõLÉ3 o±†’›pRkï8µû$—§iÝ›žäsù>–OΜî}ëyª…�¯Þú©¤Äzî"]¢¢~`wÞž \ YìOØyè,›ÑÇ z¿ÀŒèhhubÉébç§îœ®Ø`ìx/ÏsÏE¹øÌÌK¸²ê+ÅY¥FŸjÆñ†ÇíŒÉð ÉÒJ"¢ºÄiÆ‚€ÆŠðšÄc¢4Èâfîö„clhTf° S 5›µÞ*2ÿÖ^Žkx¹u=¤KåbÚx&xrÏ®†(ÎJó‡)ž\×T%±…ùrR-ŸÒ‘éðjŸ^”Òàìt*‹ÞùIÏç9Ÿ“æÆد”Ñ"‹YF¹~Föb4,£þÃàÖÍb×@¤¶vÔÿEÍ­]+áDZ*X¤Ä?±'i!žîÇX³žºBsšêÄáøp¯¢à§!«-’UÚ K&¯<2>W1S|‘’Ü ÆúÄ&´$Ûo~~ 6Ï�fƒœxsžG`ˆ÷¨îªlék Ê©|¼(×vu½Ëg‚‚ Çáü…¦áòΨfT¡Ìù¾ »@cùÒûã&ù geÊ(�³Ø¡]±WSöç¦æŠÆ)lZ 7U·Ç…ò’:_Õ†¦«u1ªÜñýs·9i$etŽîÉQ-¦3ìþÁ„`»O·g]¢TMÚ ýœIˆœd ¯º?Ô\aÛuå.Î2hKI€[¶‹t A„ëEÈœÛxyZ‡eÞ"œêÂW¨+¢ó0½ÄßúþÖ>€fo:¬·H®™Ïh³FI˜aÕ*6Þ%^䉵Ê©}ÏFy~ІËä¬ÀÕxG¯ØÒÅÁqN ¥èu¶“¦DÝ.ª•4q ŠWôïYjzCI <Ú[N0 üÙ<ÒR»XD´ŽrÒ¾jn¶uaO¤4d�¡,²n‹ü‹ãK?ÁW/D‡`ÔºÂ30V®¢ð×n^ ô  •haVk&VC¥Cõdbòw&\4h¬‚B¤;wI'–GE5·°ó­Â0_зZ%ßî14œªñ/¥ÔõÌnÝÁ�Æsqrq¸_šØÝ—Çd ¿ýw~ÓŸËât%d–ÔN"'[‡ãyü—­ïz#‰îØF.j¦?þ¿U‰rµÙüI“O ¿ÞK“ä¶À|m9RbðòMÔ÷,¹ù1ê•‹…Iu?UÅü ˜"r·I»«1Xãt 3›'#åA6ÈÑáâë7ž�iq|&e×÷Îàö…ìÊäüE¶û!Úƒ¹îê0Û0˜Õ{óM|<Žb/$P¼úÍ7çn1ùçE©µFo>ÛÜKeûUæ’éJ�h”/çKò9š šé´ïÓ܆±³g'2ýWç`šÔ„mÇks‡ø�{q´ö VE_ÎÆE€ÜÒãy~Þ:Ü2þa·ËdE“òN²ëÌ{ˆ$éF×è ¾‚‡ûÇRoÕVAõ!Pûn‰#ù^«ñÙ  0{7[iÈZ3Ö`ýÃh++ÍÉ‘¹³Ì³ˆsÖ ¥³Vͪýò>Žv§Ê@âï)$©|5 ÜJ·P·ï·WúN4^P»˜óäæÉt"žÿE–‡‹'¦¿J–S?ïñ·œ8É.èõá÷ý°Ì ¥›`är»úvЭR³©6Zî™±!Õ°–&uœ%¬ÅøoÀÚa pG¸úí$Õây»-¡ègºëmÊñƒEqo2nö… êƒ`»F»fù oìLÒuª…ÀuÁWäè© Ij-3ûA%lrà½GQù~™3ÒWÓ-N-h‚7ÆìLØGž¸Ô ’ù,Ëæ T%ŠîÌ÷R7Šèq „qÎ÷É‹QXf´�©ÜÆç/Äuc‚¬Òh|K|®äÀJœ†²i ©z3'Þš÷)Sº¯Íoº%ʵ~A¥¼R!>°çñÑ‹ÐjŸ„êi`¹Þa‹4³'0cPy;"=ÊLµ £Ë†ý.‚ªž3máï˜wáülj ‰HS5N4 Zõ[+£æ¶ØÈɉ܎þƒ=iâcç{€VTÝ#L‚ƘUe¾õˆÈ§yÁlzFˆy)\Âèƒï%œX°3Ž#ö}ÖT·öž'p¸úV¹vz;­ºæŒÔwµ£ðð£Ä3ë°¥u´OXö;‚—§ˆþEáÓ:µò®€ ì_†èµvãué6W·å(¹OŽ�UbZC#îb ±;xáêzR^¯"#)Дˆ ¾ò pÇ^ƒ¤œ>ºHù˜ygÊø¶ª&RH*ô€T(z¸ lªÜaÚk¢>„3•´ÙélÐúާo1qÊUÏY‰@ÅüŒ÷÷õ€)À›¼î¹¨õ*TBòlÙ?^ø °)Ô“ªÖœ·‘¼êZFÁtq¥ó”‹Ã…ããËuY"!Ge\ca¯Hsirݹd·”ž›ãoÅqm®³Z¬h–Žœ1T›6â ]å‚—¡êe_y¿qUêQ¨àŽ^ç§±J¯Ø&rÁ3û.6%Í‚8ý"˜‰¯Å‡ê¶ùÓ-—l—¿¤lGÕÊ»YÜî$q²ã¬ òs“pÅ”%Z ”‚V¾U“ôZ6“Ôq‹„²¸ ˜â.ýc«DˆZÕ<$<ørÖlýŸ¤göYFRZr+n£¥Éy›¹�ÐIt;ÐH|[Ò¨J(ÃÕB»â^ÝÓœ˜Mìü(, Ò >„* X3 ²»«C&#€¶ ÿST8p“§æí<`¤P,Òë/'(óß¡Šªu–Ž×ö’Td>E{µÿXm ª2S®$ôm$ ³;Ilœmù>° u ,á¢ÌÐÛµ×ɦ¸¾­'ï }n'˜Ân�Þr2t0%ÇBAÎãv‚G1Î…PÞÒè_éÚuE3'Ìînu’<«-3ˆ €ú/Éoé@ç·ýkEüf \üùRÖŠ]@jA©4›Û€Åbæxêå8ïq ·2ÔW]ÞYp˜(˜hn”­Ò¥ïŽ«ÆÄ‹); ‡õL!°A;~–‡¨Zܸ mÒýÈLYërÃt?»¡ô_Ø‹¢mˆ>Œt2Ai­’ºP¥®j^zTT]ÈÅ€WÂßè[¢Ézµ¦îf†;ÃZV´×`ÑÛ׸yñ¡‰4ÑïI¾1•öL:OAí‰×üs½ºÉÜ?«¸9ëvŒÁ_¾Žíf”Šåâe…%p@B’¬.ìoÇ’(9"tj†–r€ÊÓéø»±æà £È:}3Êæ€õtA<ûǼ4õ¦× /�$F×f†«ÌLý¸îœ)Ýh9Nê�gýé{Ò9ølI²ÍRàoµ208–…tN°ˆÝÉaùr¢”×–aù”2Œ·SÕ$ ÷.È(yE¹ÜqÑrÑÒÓ¹ìõÁÁ'"ºÔAÞ³üV©žg( l^x3*á{¸LƒZÍD”@ü´]ÏPüòƒæ]LàF^ì/( œ8à¯&B3Ÿ#™\ ©°a~e„ ‚”†^ãÕ.,réJôÅJi¬cQÜäµÍ²¹Ê%ÁwÀUѯóÿ1«óÂLûRQÙšÄ=$ÊûëOr¯Á¤ˆŸê2œë¹'«%¨/9á…‹†Þò‡GMX°±N‹„RHáÈèˆb�ÃÞ›°žÛàqÁÎ%-Ø›“Z_Î} tƒÜ(…'|<CQöÿ9:èÑkÊós@!UÝÂ…¡¾Á?F‡\›U—9Jcìî…fq�}U>9؃ò) É×/"~ÍùQb¬Ø(ê²z¶Þ5€æq¯|51X[æáÇA ¶b—ýPƒ’Ï¡.߯ÏðÀªv"´Ðqj1|ôœP·qäâýŽEóAs ÷åèg¶Öö™ç€òUòÎç»Rò,×?E\ÇÀ;ÏnÏuàVi$îE$ÞùW¦Âa«Q(Mý32»õF8§ý 6¡£·ÿFau]'>zEàô1³o‚ž=×xVïnþ?>`<1ÃÓs¢‰þ` ïÖ×Ã,±ßb¼Fñz‚)ú ßã I?lõÙ9 ÉSÁ¼"ÍRñ@MjÍÍvBþ&fÒï¬Ô‚KAF Á•ÅÿN~kÕE§© ¹€Êõ¬1Ö̱‡Ïio±t ¢eXùì´ú@ÌœüÍ‹l†êY¥f¡£×oÅæÚ˜˜–½1¡’sÏ 5û ÒÓ.œKP!òâ-Á#1†"ÒåÏÆû[2×1H6âÍ^»êÞaÈCÙ‡¬-£|j_=Ò#î^ÀËô3'=”‚íþ:éˆqo‹°U•�dÞ!„Ц nè7#4­(¹±g¾ýˆÅ VG¦…?cËÂ]Z8è˜å”kgŸv ¢ê+0ª<‰'ÈÝâüö³5ÈfÀïfZl-LUÖ6gÏ¿âê„l‚™¨¨¿Ù{Dâƒ84€ÎY>¸$¦ÿL€¿‰a·Šb\QC$ „YÖ¿g—÷«ÍT1#í;[âeTƒ¢ÃQ=«ÈxÚ‹FÛµÊüúÖuq„<•l|­ ƒ ×5;æùÄa +ÒkÖ­íœ> 2Ô×"Þ-gdÁýOd‡¢S‹±¡²˜Á-¤)HQÖ&ÚW¦ƒW-püª¨\ì+†'m&³—\©øÈáomÙËXK/réTfXžÔ?ôs6 véÉ”=æ;(º2­aꬠûå ×ÿM@ý6\ø]s¼Aå`e#+"ûÙwtˆoìU€rEí´+ŒOÓ„bÛŽ“®Yz‚o¥B6 -¦(º ?Þ¤·SÒ£#›À¼Xï‚íø ïç`ÚÆ¼3ïµ÷Ei_äô2¢îÑ'ºÇ:«&ú AJÁz ³­ˆ±™¸×ëŸ.{)#œA]… bê¤ø]´3úîÓId;¸½ Dd{ÂB›1}„¶Ž9“#ù›!Û÷a ÝQ!°DïE“ƪwˆ †¦jd˜ÙÍ|»¸ìiBœjÌÏ8¿âu3¨¯½— |ÿ?Ó<jk˜éÿ\Æ-N}ì‘kiógA²ôe oKÝP÷'§Bóßú6)ÅŸC¾Ijxʰ=«ÒÕÜŒ¬Sž¦K#A¿U&`ðÖ«q¥:Óº˜Ï¼nvßÏÃÔ’~™ñøzw€àTî\~ó\Ÿ]œ+úeç~Ì\Bèv“f†¼ ¡šZaüô÷Ú’g´‹X¨²ÔkîØ2!PjO?çödhF%jçUJR¶,ìŸg¯‡e¦SxX¢,zá²S(±Ì¤­ˆ!Ñf¦|b`fЉÞÇàÕ²Z7Tr𿉏7¹æ/qRnÇ ¢Òßn,ÍÖD+ËÉlÙ)—‹ˆÑ ˆB Cò{ýtoXÚÓ/ç¹'ƒßG€¢xOº‚èŸoàFÍcëïk.Žº³>´TMa}¡o?{“tA¸ÀKHßÍ׌Š8'tYùâï±!݉n±“ã›n”3ªXw‰ô}ªÙ„Zm ‰;t^g«ýt£E³¶ÖH‡÷ÄÄ…dXé¸R[s0¼6»Ö‹õËoqÓ_C5ׯÇG¨*“m²&]W*an¥)}$Í)+Š& èv‰ ó‰I™ôåØ„Ú¼k$îì,~y |-íd硃Æ|¡¸ÝDkk0䊓Øv"óO´|£V¬Zæ;WHÚxµ%Ê…Í\¿8M±!½ûi¾^¥¥Xë/[rÛE€¤QÛô‡©.ÌZ[»þmØ"C Ò’š‘äI›=iìóGó»îSÂÛãºH‚Pò6`® ]‰¤bЂµ oûdF–I-:Ô^"Á¿Ø-¹´Ýç;dT>­}P9ãÉîFœâÚ8#Eѽ¸„ÌOQ®Wšè›œââL‹Q5*P7Š£”ÃT†Å®®Ÿ¨öü ¦ÍÓ0 쾌ùþÒü.ÍŽá\67€Ð.wQðwGl‚vª*\iX_åKÓâ…™ãèRøùÿÛk{²™£4//®Kh:PÓcG²fÚ!_/¦—]q°g É Jߣ\ôNúF†#.öĸêˆÓÚ/Ìï?Hnø(¥7™�¶Ÿl ×Euãß…ý†Í¶ÄÈZ!.M‚…af¨¬¯Þ¤“/Œ<a#´¹¹�C5áB²/—«CyuK$Gù Ý# °YOh<%§—¯PÙœnr¾kØx'-¿„î%Oi¶Îâ´É<+VgÄ€:Á kÉñM‹ÓEZoÿ]í)ûëZ('æÓ‘Jfao‡þC¡á7z£'¿f@BgìÈHöæÿ®î‹*u…ÞÝh­ë»Ä„ ã#Iý8gƒ-‚´”üa;8ÅéZ¨Áò¼Ÿy̘åâ4@ƒ¸qX_¹xZ­†¬ÁJCsÊ%n4)pcy¶Ä½~unÔ£Ùabÿ‚wy4r,{д ²^ó9>åCrÔcì÷sg"Ó®ÞTÎ1%2gI KçyB4]CáœbÆp­®¤ÎŒ{w¤-ñàeØü}#Ú¨3ú†ß 0ì«8@`·…à(ô×øµC·ä‚Œ6Qi•M¨KŠz¼g6æJ*‘˜ÍÞé Ü¹ˆ¸>è{ ¤JæÃI›T'ƒ”ú?Oöü PöšáIbìùÂR¦Ó0ÃÙ’˜áðÄYñ}×K)c¦æÙ;þcÃ>³]*Îùi™é[þL•2ú"SŒø* “µ×è$¿"n…Á÷¨X Wœ0�…ø¥^ÉÔzÅórÊ0<h-*%m¦8ñd±õ«¾e>$È£@ {%g¶a nJ ?—´ëªÀÈx1“ØPkKò/U#±(¤å³ÌkRxNaSâœõ2%]9âùUÖñI ÊÆœ¤W “ÓÛ ?U-Øýsæµ-ÕšªÍŸÏŽm83ÏúЋê?3Î'>I€ëv—Õ„”¹±(Îö¹‚aïsöruu¥E<ªý’Î{‡å*¿ó¯°4ê°cÞî~1"Äܸ‚UnÐ#[tÉYxèÃÎ#à$oƒ]P† PÉú…kœçB–ö‹8XÞ’}Ä!duž*ŒyïæIQ±×=,N.G¢ bª²Öu¸îÉ2øÇZviĆ8À:=TĈ�Õ‚¬LÄîiíúV‘c³œîë+KSŒ‰PÙðÜKHØ,-rÐêâø½9MfÅU±Ê!yEÌ–zÃ2¯Jìc=¯6¸ì— NÅ?´ªåæ2¶ÎO\‹;ˆ4xÖ!Á=xWucà[ ’Ý<€»J f™÷ 8(瘲WÎpµs[\hùB á˜7o §¾Iî@xÓ¢ƒ–VëÆ2ê'<'ëîRÕ`²®è³JÍ[é/JñÌ&}œí¼+Ö(褽¿~v¡/Õ#´Ö„H nc)'€=aÛå­{—æ’ƒNXgôd÷0¯òCp$°u;ÀL$> Í>`lØ4€{€¥õÁÿµL¶™/<¦”(ýÞNuh­èP}íÒ´áPÀhû¶j”Á`®Nø¥ûGûÓòí–߯6›—~@‡ŒæºÇ‚•R |Àë{Ž’s©ÿk å"àeàëOÕ„ÙåRä|”)¯Ð¥DB¥€íÜ|ÎÆÔd+T\vá¶Ô,£lçgùYsã3¨ï±ùê©—Š¤‡~0©&¯ó[4!‘tp[×8¹û¨Óhоߞ,62Üìˆ?/¤9<ª¢½³ ’ÝÊB£,BÎÁfŒjÃ9äBµCK0âì 7@>…]vcˆ©ê®|á ]`±2¥D˜^Y6Ï º†`¨Vðò–²1çdrÁ±gÏ…6 ´åuS„å4T(帱5üÐÓ%‰4²-eú,à5*W»ÏèÝðô‘d<y™¶d’¢íÄv(û81í¼+f}4æ¾Æmí^1̽šðì}; èÅ/«VpfwåÜnjÝ#Ò*§}gws D^é5êJ=…»Ã|…Ö³KÚ¼�À½4Ö1÷t¡#£deŽC6 «ëB¤A}µ�ciÏ7Zô¢¯»Ÿ£µDrä ¢ˆ±šf6’cð#þ§f¢ãøÎ’®©âžãGÆç §!‚‡.e!4½y‹ÎΊ 6å£'fè]"²ßV#ê{¡-…óLÆØËÞ:»\Q»CóÂR¢Èw6T[c&ú kYVøRM¶ô_[ŽälÞ+ /ÀÛ0Šß Ô¿ûX­CIÍòÔÌsr“Ýs‘ãh(öZž/|ØäðnBÊ;4Xf5ýU`ª)¿—D<Ë7iVPó“#.w³)²(—ö4ÿ /Í™-¶TÁÜFÜv±<ª5[$î…k­—V4™8ã’6}Ô (î°&‰,É&:òRñ’£sTlqœ> éõ¾èçÎÞÿô‹8ï÷×>Þu!Mb`‘t›q”ónØ`Ðç4–šF�Û¼ÂB&ä)ô?ÁÆ]гÌè6o×<ckFàEÞ[2"â¼¼žÀ#Ø?àI¼ºü-F0ˆW5üdÚŽÀ™S•ª÷àqõ¼TUŠ ‚AS µl«sRº‘CñN%¥~=WµoU@/¬fÚRd´ÿÎöTÉjÎŒª+W@¼ún¡SC½¨ùEiH´ CI„¦@¶<²MÄ#ÓÇRÚ~”:=¹ßŠqfn¨@rœ®’,²ë¹î«s¦übmBdÑMךBë;pru68vZ3- ¯Dg8ò^3§Â0g¶$ZØà4(?Á()ÆŽS“;œÖÉßF»ïÁP¨uÏBÔ”eÿjlŠë Ç \òÊC¸¹*WXƒ}õd&oÛ“ ÖuõT-×âõ¡fø U|êc7Ñ‹lÝH2½» ´ƒlØÏ›Èxz§…ƒÃ QÌ0¥~D¸›f¬Ìr™Òâ28vÑ® #¯N¨à’© ;jöÿ\RKs¦©E©SÆš´ô¿¢SP._!ÜB­@ç ¸vŠ Ð%Îðø²†LºÝsÞ©`ƒã2þ:º[‹È TÎПI-Œ5Ÿ«NÍpæBé-y¹Øì9{ˆÐRž§ÙÓÏ,ò! 鬠ëþ3=RW¡$ŒPƒ¿˜Ú¾™°r•šK¶-²m½æ²>ÿi¤¥ó[SÅ`¶Í[50‰(É&_O’,Ju‰žQ¦22ý2r»�ï•]ÅûD¡eÙ2C 82�ê©ô iÁFóàâšÝØIs*bnÚ»è§Ó5$ÀâOÚ!½TD:©„J$˧Dñ¬W«FfI3‡DY&;(FZI‘&ÓØDúûNçYêR³¡‡0@p–*³®Å¡‰2! 6CˆÑë…qò†ì¸úÆ ‚¥w„ÉWa-Ù÷ª_Ì?F}æùq¸?a÷‘\NUÔÊmIV05 ú¶=8Á31 ¬gpiè–P ¸ÚwXŽ": 5£&:¸‚¹þuG’,_ø wüeŸ%{+ÛgØ !Nn Z«bôôÖ­dnø>e¸*„‹ÛŽ¿½H×ðíZ‰-O™´n›“…òËÍáõ fÏ?E"Õýþ¦XÐ`z„¢†�çf¼vâx¬ü)¹˜IljBTG¹ÏØé2íÉ8>–4(M;I­L±¤N5©¢6.Eÿ"[Ëäñ¥_¬°ylN­Ýiç lË1Ë KF—@y„&x:7‘1ªLK¸ºvf×—¯ °pg˜G‘j»ÿ Öu¹ÿIµ×ìpÛË,µ±ø(0-¬Ñ/�—>}bŽaüAÄ0݉‰~ …t€Nj¢Áh6Ù6‹Æï÷ßâkj¼ié¢xÝVX'n­ðÄ»Añæ°aà‡!Ko„mÌEÆU ß©9T2QÖ» hqb63ñI+<½x}‹£ï¢ürÄ{àQš„D¼t’ÞÕ{¤>°N‹!à•˜ÜU¶Ø.øàSšžéžo¦Û3:ÐØ»ò{¡ÙgIp_´ä:Ñ+•Ãå™ «N»CÄ·—Ó&ÊC^j¾öÄu×Ú]ÅœÅ8‘#OáuÙV_(@­ ïÎ5<ñðõ±æGšeº*㓳]ƒ¤AÇÜÉ :²nPø,ùüt™|QÛIî=<¾/}J¥OV± UÓ FaÏz>1‡’WÊSØ;°H¬ÓýÙyy­z1ÒÖîÏ0>¾1ÎNz°w$ߟXsðë?¬�ò¿ªØcÑ'7.±r¦zdC0ø•ìÖ™/H4±sAy†D-›¯ˆ¯h{ýW–Ñw”ׯºÏà~Ò!\ÀÁ„LQúÞÑ2>Ñ%lÏ¡7Á\£(6à0¢ñ^õ ¿³]``ªóŽÒÈ@~'ÞèùÀYÖ¢ÿYÞ¥xJTWJÉãˆß´¤~$’‘ýhY‹é”ÝM£J¤¦`¯´JBà‡€ã^Î|R®„hA{¾ñþWÖÖè™8 èîWžµò.à ¬n¤_sàCd>ÏÚà œ­Z¼Ÿø¸‘‰h£­øª~lwùa…ƒLßfß–T./Q°]µà{ØÓˆýùâɉÓ2„a’»[móð½NÉ•±‚\]ž{CjI+{LbÛ; M#W6rõÞ°¸.ºá^IÒŠÛ|36f=ÄžrK8{оë7ª­˜Wà‡¥À†»þš"Î ›þ9̹›£6÷³nr8|Çn¶=)ôåÓöÏ„¯™<";ŽúÔLÖ/– <a!áæ.ÚÁ°7®iL¤òñb§£Ð~†câ0ÄÔÆJ#'úúÅþ€Å}+à%ѢȆŸõÝv³ sñžúq§;9öÕáàÊk²¢,N/ºMî4G~NÖtå,bmM*'êIrc•Ýû©þvÉÆÚ[†ê!ó›÷²ÕM@z7ÑØ…›ñKS#JXçí~Z¼h5¬àöZØ(G9É'ÀŠ5^Îzœ…l>•a¯e—Î8hR¯Zã7™ Œ°vîQ- ÆË]˜8†È؉Óe£âûZÀ²½€*b–ÃF|¤Ù&l:Üò-œîj"éÐfª€fïü¯£ý”è„,t2íËt}`×'q†Ý cyãÎïR—Õð]༉<íëú™$Ê]²«`? ¦´@.¢ã…ý˜‡âv$nb@Ö¾"ì¿MP´éTUuR[§’#àº7¥­î¬›V!|ïE$û°Î}ÎíD>+9·øîñŠ©æ‰ô°¢åÇö¾íwé¦üý%´¼$©±°XwÝÃàFƒT6I5ˆoÿF¹¦k“þã¦(Ÿg—u…"&&ÜΙÀF-YéUbc¾^ùx_µ _b¶†ƒ 0&-Ì•6&ª/ò'“–Š©ú;¼“dÉÍ€Bdùž(ðõ5£¹MÈ„!ܸÞóÏI"Ô±ïóx4éEf—O^ËfþdíÿI¿aök™ÝGŸ¢ÝÏ0—wñ¤ì‡Q¨ósUý°}N¢º=ë½Ôu‹o Î¹›uxé '4[‰{ l ®ªõˆï£6ê^´¥XXyºØh5*•^²äŽf»Ž!°€$Usš˜Z¨´3]¶D3ÃéòRQÚìÆÖb8a.Ÿ ÈÈè8Û­Væÿ"!ì«•ž}é¿0œƒUê{θO„Àb”:ùuò;6 Õ¤o·´ùû“cº¢– ajY²,CðrìR‚j9pµ´YKZ ÚDÞvšÎ¦¡õ‰Rv é™Ão±Ü±!ØðO`ÅI{½´öi!!EÕYÀú]ó:[M·Û½À¿v,U 7¼‚&ïÁoXÎXH¨}NÆ„¢Ñœ)¶ŸNÿye¶Hl ¶Ÿ,{''ªÀŸ:ê:u�É^_Hw&;EVbÀ ˜³„£=Â^Õ”ö¥V}+Fþ­ßˆl£Œžˆ9‡q¸÷‰’ÒPøÅJã ¡ßyžÝ < Æ‹Î\‚ß�„EÔ¯™Gñkãý”f�­ÁE_óKéàùìÀzFС"®œ†ì†Ç !ש£É.d.TöÙ|¦á°é,Ò7ñYZýâö]Ó9{¤ÎoæHȯèž:8ƒ9:® ¦HŠ´ÙhµÉëöˆÈ¿—P'î¹Þ:ù/RÊ$€KÀ~Ç” ºrÐ_%Ãf4›@é8¥ŸºGýŸ+æ$½N!}›1—³}Mdƒ}%ƒŒÖ 9\¢‚Ø-çÃ|M9A"7γ4X  8&˜î©Ùl•ýˆ?í—~†§<øßŒ3ios5N¢Áiê†Æa«U~�&²¡gI7QaâϺÿBÀ þâùH”u1TOU©oòswž†Tô ÚP{1ÀŠrn‡‹Ÿ‡‰º™…£ü" ÐT×jò·T„ÊãqÞ5ùÍɬMñºì&øïKcH™Ê|9…Ç­§lb¾‹_öˆ÷¦˜apçsM%=á0;ž°I«™ØØ¯ö(ÍñÀ3Ùh×f‚£˜ü.1RDJ-7~Q âϪÛbJ,ȹ \¦ªòY_aT}5XÝ$$´®[(W¢ÎTŸŽ6ò™@ñwâÑSóºZGñr‹—vŒ\¶uR”]èÐ|«þ,—>ÚÊbq™ä•J¾;AeþìÞÉ ÄFƤYÞòsd¯ *J ²$[M d:`Wý4'ž nh¯ç““רÆÓÈæW˜ ‰öÄí ɵ@¬:’Þ)øõÙ£%CõSCó8Í÷Î (ª«Aþó]4¾fŠèg1ɸ]rŒ.cÈ>˜' ËšZª3HÞÈX϶Ûñ(ª4¼æõ™­¤ËB8ãë'ÒÛźc*—OTë)õP.¡‰ØFC¬¤——̲I:ˆs‘0,òÛÞW(Œ²Ï&¢`Íðöï\…‡dq{2'¯I°ÿéâ·Î ¸Ã¬Ñ›Ë‹ßÔ¶½”Aa,¼KAÈ ¦ÕˆMFû+£5Ïv„~i>^—L†ÊÃG)Ø�iïçfU¬G,¾¿åööùü:ê¾Þ¶ï·¯,¾Þ¨¿‡TWÛ×-ööY‹íë{øtç}½UßoWàîGŠúxá2hÐJKÌỎ¬Æ†R:Â>cÊç¸Ûý?äìÙYÖ×s% µ«'øjw†Óå!n˜<vo’Ø^Ì~ —nÙ²>ÑŠPáAàÒSO '^WÇJÇÐ^¹'a;nó->‹š›×\hMX¥ð¶EßÅ.^?;w@|ç"nÌ’¥¡MÐEŸx9 7.©Œ«ù+V¨(C‘’gZŒx6MUBs¢*o«cÕ±§Û®L‚iÄäÂñ*cpftY–Y ´-V`4`Ä@@u6™™™øf¡£`µLùL¬Ý±Aû®«8§ííôÁ¿HuࢀÁNÀÂ?iHáN·ßXgM_#e‰¶qr.³bÚ„sµß]4×ÿL¢Ö9?À\j Ò Ã’wÞñÉ;ŸðpŠ\Œ··®St9·rʰÎ<>zÃoG†„Ý ÿOïZNþH7´GëÍð<*¨<ÑCÓYãÚ­ ‘iáÑ=Ýn¦ óªlN…`d¬Š#ÄJ…ÖBÇÏ€ÿn“ÆaEº=ê~ÃÃ4ðsB{, z_³â³ŠÎˆ^É茻%š]}Zvk”GŒÍùm cà–WD4 Q»À?Ì|ÃŒØòÈÃ} YxößÄÐ'ûhR«âÎΖšxjUcŸs¹)è/ó–Q©.´€u‚‰RAg¡ÔqÕOœŠÇþ> ûÏìØ=uó—VUo:¯7 :@™!reµ³’¡7е@÷‘}Yô›~@Þû›Ñ6ðä'áw®$#tÿŽÈ(qOO>O8JP?_2mù¾,†Pm¢»88¿ÏHŽ¢ò 3´ W—²7®œZ°âå WøÈÍ[À@ŠÉîgô˜ŠÙá†çÀŸ R�g"“ ºN�”CNmœ]A]ôÜïø/žéÌÛO©Òìõ]ûÆ›ßÿ3j“êÙI)XfгÅã$³/c5t¶áAêUX&¡¡ƒz³kMÂ…£Ü«6èî»õv`»ÝS—µcâËêlOe·iβ¾bP®G#2Šâ‹ tñNŽ› ûëâMÊ,ŽaK"¨ l‡uØ'4ŸE8ùnÌåýì¥%Z¬±&1qfgö¬ž}†:TFNF‚F’o5rm3ïHìlg…j›L.½»v¤Y©ä–ˆ¦ç‹ñ¦,c&º{-h@è:¡›Œ%"è5fÝ)k)ݼÿVYÝa*Ûú‡yRó¬èh«û„uŽåúÎú}M¼DXelµ²ÊHò~~‚Rø”÷œÁÚ‰DEÏ™Œê¶êÊï•p$ø|ÿ,ÿoÊ›p«^�SwH œE¿D¢ÀDRlVê iMF–aHós6É€Æ9ÈÚŒ€c¼6¥ÖX¼»ò8î^Ó!¢sþøv=o’AÀ‹@öò±Ä÷ø j—×tõ܉ÁóßMæŸyü Ž$$FzùÉ"‚›^_-öYÈõMïM<«’=<¯.ø¥BÚ;”T1Ãk)¹5z­)^Y{Ôf’‘1äl Æ(š6ºÏ{Üqþ¶6þjáe×Ã~±å¶W¸C‹Æ 0žä:€&‡‰Ü[%á…%ÁЉ0Ø,a;Ñx´ó›7’¦³ÿV‹^óªïüê(—Ýã hMhB• ¥Q†ÇŠ?ë˜eP¬î µ¯Ò4¶åÜ MÐàêéA²×Å´¬¼¹±Ð©!ñÝc·ºÝÂGÒÙ E?@ â7×þ“Ø´N]±npbo–ôN+‡¨<3)ò8M¯ÙŒ%ëàC^­$n@ÿ±¶&EPÚîz·’Àv€Ú ¾œù·Æ™}ÆLó_·.AEqOaJ¢ÁÀTuži@-…ýjægïÂØâƒw¾û³µ(ozŽm;æ{ò€­uû>I«/$)ÛÁÓÕYþ<ö¼-ÃÜËÅ<o"Ä8È?'õi;—€ðB»5ѺŽÇ3nzð¥ IÊ+ÓQx)†t‹É±ÖZÐï¬?¸#vmâšhG‡Ö‰hè«d{ç&e§ qW(*ZFV —Þ:M÷GIbR·¹R"ìà6Û¢¶ €»åž¢A^ÙW,¾§¼È)ý ØüФ5”àn“_¥¾UqÎÝz„a0äSŒ{Qô˜ž÷ŠÅŒMãý©b_¶ˆtx­O”(~Œ1.ý®ï4»œ§^<â>Ûª^D·‚è‹=9yw<Jx« ºIÜŠpC†Ÿ‚gB ïyÈL‰ú¦X&ãÝ—ž4ð¬”Ò K’~— !_åŽä*BÎÚXñ°Éé.,.w×ŒŽ¡†ÆÚSÅ£±÷Ø�ôªØdW1"8=Î#¶7M¼ÞpþQ;Tß·F+ HÚþ/øçïøÈнÓõo°k{![S…ª³¼Ë`Æyž¯¯_“+¹‡™Ø›æ¿±l˜ý% #Š)éûПȲ·Ùž&¸¡=æóD^ó’Ì¡ Ý5r¯UVÆ–ò–ÖŒœô“Û²éAÍ`ú=òlBsˆöf: —`IÇ)”Ã5V"Š;BŠbúº}Ð$kôƒ–ÂÖç›<Á™ÒôI÷¿ê—Ì,sºa„„%­¢}³¶ÄÔJ7T<ž‚éMz‚¹+ÐÑJ<ž>W© ès»ûZgeûÿ9‰j8ªæ{q÷ü –·íŠžÁƒL€ÌÁóV 'fP«œÉéGÇÛ¿SŸ «ÌÀ9æ*‚°2I¨˜ã*çZ9m`Ï÷IÌ·Bô6@á¿Aψml Å*‚²î;ÂC=–¤ƒêTÔ<­ÑsP Wø’íÃ’\÷.zäV+˜‘üÔÊi&øÉ' Åäp4@©luóý·-hSE[nA‚̉ î€Í }›Ü?IíŽojÏØ»ÉF¦U¡óÖòŒoéõ,.‡ý˜°ØyðC¥:l"|ÿ#v?CMª Lá©!¼½V’ñp’#é¥SpãàÇ-ĆÜu‰Aãuäv.N÷-èÍç‚ë{mØv³Fý2Zš`šXîœ;wûùì-¯�Ò»ŸéË/m²B}…޳pŠy;[’«zâE KuJzñÎ~P7ó—ý1Ê–×Ðüç MÏk¾œG—¥ä›[2í¿8emh~vèg8RYò ¹ýpúe'µôvaƒU‹”N36ëCõ®µvØ”L/æÊG<ÜÖø-a‰ûÁM ” A÷R¡…Wû’’„5ûS M:†n‡~|ÎnMøð6‡“­Ÿyh16I+ŠÕƒ‡²óôS´ñ­ nIµÎ/¾ÌÝŠƒy)í0TÝMŽWšª"�ÁÏy¥ÛK9d„ôÉŠ†â`+¶ß>šXãvj”Ây w+$ú§cÞ|ý}HØZ73ã‘v¢>·þ aâRV6º»UÚDÉ–z>oÒpæ'ÚWþ ~âG3£_y Ë,ý;eAéõôìÌHñD=CªB‘N†ø X~ºjÏ2íÉ{µ0`%¹ÍötktúàS˜Ä±¼)ª–VUÆË˜ ̾ë¶í%¬Ò ǹ Çê/g'Å4Êy|\6Ãдa#º²Ó:xäÝ„i²2Ÿ {ú·# Ó›lî–`’S¢¯£„ªî­~µ-šÑèÅz6’¥Í ÷I¨›Î(kÿCŽ+lºÛ¼R°ˆÜg Ãžr…ç‹+X*G?üÔ¯é7c¯w|^BÄŽ¾Pè LifÔt€+œó†¡|p¬ýëó B¹÷úÚVš“sÜÀöU§ÒsðÎT*K•T3RÞ™W‹cŠ»Ã›ÒxÍ #\?“ùvcEò ¨ÈC |B,§{ÚM²F–˜,ÏøÔ Nf<M5gak}‹IH×Êx W6¿YÐÖþ ŸúD‰u>o,\¡eè,Ž“ßXUQ~G!¥üøßżöw›‡vm76¨ö©šfyÓÛè·+CˆÇ‡)ÇlÇsŽè íl|ÜìFŠÛÝÊö_iX=B’¶zÞ4øzYËí ‹«IkNUf¸‹ûº~:ë¦Q+©‹–Ë@ÕÒºö9µ&NÔÌÃÑá^é¼ 2/ã}ô©æa“´rnÁÍ|h˜L¯GÔÓs‹—t« eàf g~Ô–ü?¶: |?‡‹Áý±ínU¡×ÌðÖYÜÃUÇ;ƒƒ Úà Lò†ùhÐ;)pQÇâ«ÍF‡’[-”áègÍlæ£íîýôM<ö9M ¾áÉiTFfŠ9¯œP+ PY÷Y΋ÂÓ4ì{àæb>ÒПêD "¢B]Fº›<䯀1µªB‡YnÌ ÃÌLo¢îL»$ß–'Šû`zµ{;n‘§å-íôÀõï0¢¯`Ô›Eq?Ðÿnœ“EúˆO«¨ÖÕÚˆê<´è­f´•2âÉŠÖ5½É&ä·Ñ\PTúC9Ë:¢±t`eìCwç'á,~X%9ØÊðCj]ˬ–v½ê´ØPPöOç"qË«L,傊y¾8P |÷d{TA¯ÜN˜¹&ôJ[Æ7¥5œá¦{í™?ç9ÒX©4wô_Œhrüø+‰Õš ^/‘óŒÑøÊyYÛ‹Ê„Ôú¦hpÁKô›Û‹yHêvbwÝöÆ‹\Íwñ`žI�ñõ7én©çÀ£¬å¦O)­à›(ûf<¦9+ß½^@Ÿs5g`~_mºÃ’‰å©ŠßúÎ#ªñ­Ù ÀÒµï25¥¢²lÎŽ¸—â“Ȫ4Èþ`†"…Ø{~ø ²ÓDf°íe^¸gÄœ3b¿œ€#ø>>Ûôb½Êû²ÖøÇTòW‚B'aoBCÚ¡_ê©L5¦a[[%!ñ8DË¢Š$ƒCOTÕòmÇŽØb4'!ó!óþ…0cÜOÓ%å9/™qÕ„],‡Ž J þâ¼@V: ±E±æ‡u¯“)"$ÃF©auÞUA6 J5y÷‡fqâËd*ÒþU>YѼ^Ó6!í6RY8TaD5š'ûMÙT»€d™“ÜTÌ’…úJmí^ØfÏMò¸TA%K]'‹dà†-Ü©Çoæ�jG�Ýæßë6XE‰›¾ÜAøc)ä´Ê~(ï‡jP±ÞK'®Ëo†¤=n@™:©Dc¬4:ñ|çUî¹*HÊÒ¹/w‘ Î9X›wy)Q¶LêƒEÙbÆìú0õu‹»zÝŒAîyü®‘PÍ50jÿxzf@"êÖ_ÈFþAЦ´+5ðbÜRÂkÕnKHE%0Òû2]”v胟R µÒ{ÀãîÀ©_I¯Pªà'­eã€I$O~:ȤDÈ΢W¿  g»X%#Oå yŒñQ3‹ŠÜm‚çP°š¢w6_wÄHö†sÍM±ü,¦ç6ÙO!<¢„Æ=Ž½í·§ œì !Àãs™÷Wy´ÇY@>P•v§›“çµýWò0±ÿX3‡HŒÓ©M y0 Ä„y�Þ(ŠõÔáFt#CPm¥2¸>¹‘ߢv­1*¯4àÀÖ3j@VK³%‹Ô?îéÁE Î’1¥Ó—ªM3ù1èÕ¾$taæûº÷(<å{¤‚.tÀ¢ísƒx Iþ@ À¼tÄŠ4„ô'z¡÷á;¸[Ìõò\ê*‹> ufí_e*ÅòŒ.ó6xû&‡OÞ:.€JËÆ«P¿ÂãÉ‚çæãÛ´û¼_¡Ýž&+,ˆ¹]:«¬0?®˜¦dµÕéÒº5}@²|t¿mä»l> Õû—ûûd&[yÛLB]Ã9"¥}áñ¡H >Å®<%škò „> 8Þ·'íA© Ìµv™•5›Ÿ —ïú]c\Ù™&Šœv]ÒËmaƒ‹e¿»ÿBéï•|œÆ[F8Ñ+ð(€ê’&0Ùƒ U�¾C= ì³ñ‡CaÂñi¯W ñ¯¦Û‘ìÙüPT¨ûœt}w(ëBÔïM¶i¯#Ѱċ"˜7«G/÷›§b¥ nËøgД#ˆà$ý³¾H~.ÿU 0�ÿ-ömn%·AÒ㣒z ‚ÈôbýÜLÀÒÌüÉtÑ'Ñ›ä°9ñ´¢XÑZ›håLÿSCžH1­ÓÛÏál O¤8²·BoÄðÓ|6d šuçJAúöé½Xû"Du~̼AÄ�ÒPˆ#:È�Ï Ãÿ¶ç 2!¸lÚQ.“äbÂß{Ÿ‹‰ >† -l…¢Œ"ƽˆ{ÔÖ ÿ ±fBÿB6O E(\ðà2Üäùá'ô+T¶²d5Gb’s‡akW{h]P>f”Õ_Ëá×:¿ÖŸ{ñ^–¾ì<bL-[õc ü× ü˜®w±�á$|å’Yš#võŠøi_Ò»5I$¬nUÙB½Îv+`AïÿAü‡ªÇ¥(ÖÚ­eVÿÈnÏÁr„#­C<b D­Ðrócý;–£#Ù¤»üZ¾«9±Š„XmòÏN›ÇÅp–œ‚ÏAÓCdqœ˜#ñ¸˜¼)ÚKñ·€ÎΞ›N¯ù¬-4ýá”Y¦=ùÓV»mr»ÛSÛ‡‹î~M¹ÏFì$`8 î#O´J@ÉðK‚=§‚âJ\gg¡õ•Þ¨(‹`ÔZãS'ŽQ¹~êe®lß|R� û¼âM€|Á€Ù‡!S]Òàk1±cUXçÐ;ÒʯGÑžHi¸Û:M[Š )\)ѥυOn@·œÝ!*Y¹àÒ-ë·¾'9Äa[X€âGTLêP³œV‘] MÕ˜Lõ†p£ói²‰ C½eúZ™þpï&ò/…˜y,ÅtçÕg`Zåxd ÁC†l°Âº­ÔÊ ÎV!¬DråÔR-7<¢Ž½kN¹QÖÞü¿ö :cÚ«Èÿ ‹L ItCMlå ç ìÑÃRâgÓÿ%;]2ù§ê$éR„éÈôÕ+IÜmÝ®Æî”dÏ7`¸TööªÏ5ŠŒ^êOSo;‘¡Ì‹”È,‡‰&h=pìðŽ‰§=U¤pb û†¨"†v¢•æÈöÊ#Žz’5e™'!©>ôÈuÈø¯fXìׯ`݆íñ2ÚÐý&ÿxFGv“•®Ï›£‘€ü °v˜è<ÔMÛ(†+3LúžÏyY°"¸èq;Î*¾>à„¼É,ÇçÐ'oz¿ºëæ«ä5‘Za§ð.àmsY"~ö½¸a.K/–ÁÀ›I唸qY¥xT†ç|¼ W?º¯wjôÁ+Y)+}YäûðYýÚØxŒn™ºÛ«%]ð¥ _;TalZ)½‹ö5¦Ýeô·u4>f¬~–<1ð‡ºëä•PÛe3r-d )&ÃÃ- ,“MóT®Ýз9>ÇÆqU¦µ†Wq ¢¸üÕ­RkC‰štt®Etý<Ê\|ôpfI2Ìní¯šKX‘â?BiÅØj(}#¶ÉSShï¸L.îÊZM–t\y§§—58²ÜPÑPïu,¿¦µ¹G䷚˙aÅ/–!)ŒRfx¯¨„&<êÅŒˆ0©)O)A æ}±ù‹óúóÐ9qŒ¼~N¢¥´XõT7¿¬YÒ²nþïÀyä @¨øÊ‰Á¾Ü¬µT·LóXSÂð/,Ã,í=«ßvè=íïy£G²pÃõº]XUAnŠÖQsp3EZ±¥ÄúÍ÷à©x×øò*ÁvÏBõ/þésiw }Â/,ƒ³Á¹Ô¥X7oÚËÚónøU3.㟬9s#o‚¢ŒRÖBí}»à×Ü|„éü<;»mõLÂx¦hÓÊÉÓ|73N.Deݧú»r>™²ŒUõ–`Ué^²¢²î™ELD†°ßâiP£Ä²OÐ\KÔKh^Òs#Û×jü|#¶ž |œ2‘0²C~hôäQã>|æ9 å»<9emÔU¸æ?cËÙÈÈ,/Ÿõ—ª:Ý2²˜œ 695)”»Æ ‰¸¯2C-ý› ÅŸ�³x¾¥0UkÕœà0òÿA“ ÝL!aò‰÷‡ÜÉ´x@f…çÏír)Ñn|ÏaD³x:·¥ÂÒ¶Ãé¦[Æ.é?b71ym‘L­%e¯ÔC)¢.Ò€™l;ÝwÓ?(â$ÅYC0‚ñ€üú“Iø”<ö%ÿw 5¸€sûÀX½Sá´(üT‡ŽƒÌ-Lñh‚z=Õ~?ëƒMŽEŠÕKÇüñ{‰Q c—j�bÌB”Òå¾vž›2ÒêÕ ÕYýÒhŽ{ éðj¥¸ügy¨ N£˜‹ºŸ+øý®oÇÃ×9‹‹èwɇþU 1@×àÛÜvžS©æø(ù=\œçò¯~ÛÆ™·úÔ[ìtA¯S…šµ3¦¾êjØX%SÚ¼2%°à¹ûŠŽãÛ¿­ÇÖä´º,ç¾Ñûêâ±øaøqi@5®ÈÍãëwÍȯŽL?`ŽSL^ïðî—ªÉÇZ^ÿ'7|¹ö|°#‚Óž`w;c1x¢bU`ÅWâÞÙe±L%bË?Þ(Õ+Á` M€×EÖž™ç6v7åÃΈêåK‰Ú7#𨥉 N&‹�°BpáÒû?¯Ýë¼y’µºg‹]L€¦"³¦ ÈÙn¹ŒéþÇ }ÿj¤wIëÚƒPsBK�È_ŸޚNãCt%Ø ‘Å,¨fô� ÀÙh¨yµIáýÕØ,Ô/‰ñWØ«/ÿTؘ,†}臮Ø}ÅæÂñ35èi”÷$ã(M–Y•¤ÖsiGï¾ Øý# ,ôF‡¯È“ûÒZŸîŽ¡,áv�£¢¥jî\óÚ–½RÙÂ71Bk軕Lƒôb²ìûEÁpÕÜrã® ¢…$—µv މÈ;äEÝKP”·™1™·ñìcØò^Ë17ÝÁø(•@Eu¦Û”OÁx› >LNâ^G^“øÑOîͺ 7k^M ´…Ç+; ×Q|„•UIV€ö%š&d�þÄñÅ(h"èäÔ9‡ß ñ ì_¹zá<%‘ÚäknëÊÜÚ%lóæq¸ç¶Ž-Lò é›o“ §xê•êxõÍ>=è)m€uó¿Ãd߆rn[òV®U›hN;ã4¥Þ±ŸÉ@ ‚º`̦„.ò—Ù îÂLç+RÇ/÷ô¾þ|Á@(%YÝúÎ+ïÍ[ÖâÀªp(Š¥ÖÜû ‰K ‚bÏs©Iíµøu(-R AN-Y ZRßúÂPÐüTC ¬¹pz™,»õ6àÁÇß°òûÖŽh8Pz×ÛXˆMþæÜÁÿ{¸“í^Í-‘ŠÓí*“ÆPY Íã.ý‘sîÒ­Ô–t¡�>8§Æi8S6¾©ôæ ˜‰h—pÖ2Ø{¦`0Üüd®!±QäWiŸJºàc“”þÐûV¯%9cöƒžg±/Í Uø= &“¢>taCþj„2‹4Þ·‚þ€Ø_1zß9S•‹ñè˜gìµj…^ÕâwÒ³ò«^<m¡‚æü½ë‰3oÑ5Œ&SŽæD�¯½ÊrdÅC†}ºÝÁ|‘ÙJt&©±EͲÑk ?ˆË°b—zóÙ˜Á`<¿ª¥lû'+—YH§?Žï²ðIvæ¡5ëºø¸|)èYé¬7œž3 µ8£—°àùÓ·°ÏF¬ZO›.à‡FpeÏoï…[ŒÌ&æ…ýX©]Ògü µóÂ:âãÎ|­I™¾À™ã€p‚f©äÏy$,&yßß?o9rð¬¸|˜‡>ø=‰Bãh9É"rZ4H|lÐød4£È÷ ”¾9ý'T­ñíÑø>[Þ±~ÃuëøÄ4sÿ ä€SóÛžwô&Ç9EÏ®J”£ôt_ø?wÒ/–Ë_½M KeIÏÓ³�âKØ¡SþIf19J˜¥×â&éþ×y¶Þàkt MbCºúGaehFßTñ‘ËMÑ•ìð^èƒÁ/¡fºk\‹x €DŲ<µÍ[œÈ±PŒäh¤—±{²ºÅ ûy^ôª¹fKjU.=SŒŒ‘s“q@äëöê8ŒÉ„ªì]×"HN»¹Pš¯D€ ?Ájº®ìxÉîO1Êþü¬Ô'03aÖ¼Kx‡G»·§5·D'?k„Ƚ>J“0èEaÚ Vd@p÷x¬Ðg~Д€8KŽ®Ÿjiÿx{³w½ßÚ´X2|¨l~cº´ÄþŸ„ìò&†×ý!›>ÝD˜$ ]ëDâ]oo½íöÖ=,”† xën¶kÿq±ûà 6#T¶€mW±ã{Ѭ³7 +­M¦«¶h¸Rhã,Ü 7RÓ1°ã<¬¿Y›týP$xöô¢·EQ±ÍÍß:ɰX�}‹¢ð¿ìüënZ¥i¼¾vèW±¥rüã=ÝWQͼÚ:– qX ´•˜£IWr*eý¨òið£PCX‡×V~£­™”ßÝòH£&9 ÖM­ÄM|þhM\¦QØ'3–[ñ•аŒ>Ç~U+c'Ѧºgrgòdš»\ߎÀ²Š¹ÿ1ì–:!± 3¶æÆ6„Ž)“Ùz‡‘£xƒË³8K@tU´Ú뤟&Ý&ãü'ŽþÝL¸•e5ÿŠµÌ½zU)ÕnVNÏ"eI€Xâ‚s UF0œÁ;ãW“Ìe^~…413¥ºÙ¹V©£¯|§¡Oò}VQT=*&¶Ñ~ ¸x”kÇÕ1eݵ8{Å'žRÖÞ)ÚäO߃�^Hœ25*ŸTÿwß 4WøM%pZðì÷Š9z¯Æÿ<¶¦`ö�S‚NÌMñÄ7¶œFh¦O_úÿžRxBô<Á[+V †Ñ­Æ¤«‹+€EÉg?§µÉû™KÁÐHršÔWÿ‰Eã#«Ó8ž•hึî9~[x]jÏ Mö÷L5½ ¦ö$-™†B­±æ¹`ÖŸä«‘#U9’\ §ŽmE¤ï„›ZŠÚp' ´3– êr9È„›‡K¯7]$ãY ¯H÷92­§«³¶2 äD8r—]CÕvŒ‹óä0¾¦3s$[Q¾e%;ÚðMñhÉA¶gºvÃï¯}É#8󀜢ÐểŸ[±SÊe¼ì–ã*òW³¼«c¾3éÑĬUzל>鬠ú5zP´Ó¥ä9 —PQúÝ 9ävZDŽ».ÊÓÜ 2º`m‚æK¦úfÅ@r>¹gw5‹5ïÙaü)éZðþÄrÆ8ö жí4àµ5ž251AÞêΡBwz·Lׯ-úoŠ´CØlbo/™±â+H×p4Ò€lŠÏ‰Â, �ºË-sZùæý4… åQQÐ’½[ÈGQ@Ð fk7’² ¿¹"œ›ë_}R%îØÝ¤9G—T>#A_áÈHðN­OïDV ÊT9èØ)~ „Û›âÖŒ}l€·|Úæh,GwaEk4åY£u>¦Žºƒëõ̸0×™ÿ 'NkÖåÜݹFLj ˜˜µBèêßåö÷çü:í¾ÞÌ/·°\¾ÞÅ¿‡U—ÛÙ-ööm‹íí'øu}½zßoY êB3¼ðW'ïâe µZ—Zoú”ÞäñŠº…–ÊÌÑ‹Ô=À=3üt•¼0{.ï`Ø0†œƒÙ~{ ‹ÀÑ£:„ÆQ{Dõ±Ï.:Ñ °·œó®¯ž>ù3œÒ/C{6Éo‰ðr÷žZˆ—j1�Øct! ”†ÂcÊgLixmà)}ß Ë‹d?b/åܤ÷qTÒbeCü ;'5Æaoáú®?&¼ìÏ¢¥1+Œ$o‡1qÚ>ޢ곟Æ[óDùL+¥`&uROÓU'_}1 ëþÉÓ„]0ºC÷"®ÅbúÒ+Zá3ªù¡†#šŽqé£CJíd…bm«¯…NõFìÊFÍ3Ö³m4uæ60âNáÎ}v‡qg—zÿAšñ0qÛŸ)E]g™tØ]ÈÛîý/mn–ȧögF¯æõ¾Àõ¾)2±š¯´Õ>¯½5˜"êXœ'©ê±ëDA´(Çí½•üä6 ƒaÙˆ/!(b)DôöfdRm§WðÊ‘ñ7Í>é?Õ¬…ð3q`ð†ïléâ" ô¡gýÅŒ»eFNL»s¬P˜äC¬Àl°í£«¾ôaç€t¤N 2€mïq<¸µ•n»—@j%ð�{Ë«•M>жu¬.Í3¤æL¼æ}Å;”ð„eÂ-ºÞñè†ò–¿D ê0nßÏÅVìØÒmЏ…†‘3`__" b·Gózw/öœ“ü.òxZÄbN™"YÚQ¯]'šëŽrÉÊñ’ÿs8ctÙɼ×�§céÎÄkq¹´öɃ@–ù{³(áãú`†;0&Á­Mh,öSàìÌ‚HyØhEaL;)'L8æÏ|NVéuuzYÂz¥ÅŽËùŒ.Žéä°LÁðRŸÇ1L!•¦íT ؈Ҷï$±s¦”ªyÓ~»ð G9Ú—Ÿ#o9wç÷¦%† Šä äW‹¹‡l'¢w˜E“3z*¹7|äýÆ5œO›Zœ½Räæ÷exršÛ2†¢ÏÃCy=Ä|c Ayδ‚.{@TîqÙÕùœ˜¢iúdõ&6ZÈ^-ÖiH˜D¤ü<~XD~}ÿ}3C}ŠÄÍØÐ¢‚„y<ÊùÖ·ˆk4OF+bL¥èfZ%ÔµŸi 3çEõÂ^檹ÐpŠ8«» %Æ‹^—’ˆïbIHöù¨+«OÐkÈž š‹ øpOpƒ,eqò‚íP¸\ WóÑࢫd‰ v2b‰ƒM ÌSwî;A09`/ð˜'6| õ€q†nA©h£²ñ¦,c&º{-h@è:œZ]Š*¡`!ü[íób÷ù嘮i`!£•)]“X3‘É$ó±¸E£Ð Pü±ËÄ”bsY7˜Ç³Vâb{Û¸$'rwøá@Ç |‚®´Ý¶ªÌqhá7, u™GŽlcêo"™+!ªóÔ*\êêk üÏ,Í@¶!3*…¹`¬ó‚åð†kÖì¿ë°ÐpAwã-Ë‘³™H˜Š¹Iÿ+²ÿpêÈšÚ¸ê™ È}PÕào òW§zºl<ÎL%Ús€ ¯È„ò½a*‘ùàñ_.˜ÈšÁ„t zø©œÚ¿+Ñíg'·¨BшQ Ö[_f°ãÍòégúÏs?†ò�̉؃òXH/‘øqØ©Ä÷ pì½ùdµVЀw(EmvOftÂÈË.(õ8ººTÔnN�HV¦Ž\neŽùK ǦÑÔexé ÅTE¥™ô„ôŠ˜Þcóðáw\H×-Ñù„…$wUÔ £”\”,HFw‘ Ç@Wˆ€V›Á©^´å+AÿDe#1>0Nê ßO§|o9%]´¾«ÁXLß,½CÊÓã1Dâë‡ëFbå`Ž*Hõ$=ÐÄ,É ©oŽViÀ½CtÍþ ÄíS½qÕƒ7ë9ÕWŸ^&¾X~Ý4<ܱV¸!¸g~ s†ÔgL6h“®à\}PW²®0ÜrA!ç@&'èê ƒ¡�†“Y.ÊÔZ§G†è¢×)eÚñ3KϵíÆ÷Ê+ˆ0C4³nüį¥w±œÊÈMˆ¨ò',QÌ&9Acw!X‘GIÛëÍ´\Üjá•›¯ A¼’KkP–mÏç) ªC1=BHÃ^¯Kn¤ ¼Êr’Fzš¢f£èÛ>€˜H$ŠQ;HÞ‡ŸÑ¥Çx sr<â%˜Ž»8Bnÿv¼ÙISej 0rž¬á0’®ñ]b× ¨ÅGÿNm×h’³å§&„wÚ’t%Sþç’ZŠËá§r#RÏ•­Ô=2 1Ç¡¤ HÆwT…ÑÊžy }ç`ž.ÒÁ;Dð�Ñ9zs²W7 ÓXáªÞ*ÕTÀ"áûD勳kÐ !ÚÙº=Õ4`åêÄ�q^u†Œ_‚Îæ„4[$Ñuß7c¥pKÄka#]š-£Ñ‘ôò”Møž)к?§Ø)ìâÖÉÿQ—e=M¨læiJغà™ršéwç0—b†P¡ÌÉ÷‚}ǰû>ìoX˜ë²m@ÔÖÇ•HFY'gêµ@ªZí²ÑóˆCÎÀôŸN|<ù¨€%‰þý:d4ìæ¦»EÜù÷Šî6†wq‹#Fóƒ]ué N=¿¢ÔNŒ0›_¡UÉñ¸ÂwÁ6HºâÝ»Ö÷:WYa²ƒ<Zºž"e*MªêRÚ:5^ÂD,¬ËäŸÆÂ)Ih2*¡êÑ•GúÑç.[¸ Ï,ñnFm_ØrD§/×oPšß£©\…ýÐêo3¥b"P»°º\ltÅæè1CkPeÿJŠ2:p+Ó)ÿ&À¬›HZ–W‚Â5'ÿ+úOÎñŒ­SEÌèjþ­Ð‘x×x:WÙ€ôömKÀ,o¹"?5R&7ñÎ¥�Š~ÑÚH9ÎSòÆw¶\-]B{4™÷¬ã½Utl¡Ñ4kËÕ?ë8³êÍMïgqíiïÕ 6.ˆuS­×ßÉM—)Ö£·�{W'ÁNÔU!O®èäypºZú`𪄤“Ìt|Æð/M. ¡²œznäpÑûÁ3Ç<nA¼ 0¨5´m‘f®‚�›˜(1Ð˼Äðm0IÆÔkâï)±¨—»NÓŽGU#ÍKûWÊÌ'Ä¿ÞpJ#úËv+ÜÕGÿ~|®ñ gäs•çËŠºò€ /Œ^$}cUH<«hDßô&¨:i*=*ð̶”�½¶qþ˜}uC”"r``\:?MÅ»¢Ô°&¢Ýåå bûQýfw#Ÿ¶$¿�ÃFÅæËÕ]{ä±±kÄboSuˆ"š(®"u^ â£Ò•í‘ccñ±þR0¡œ—âz›[2í¿8emhèWl2#އ2ƹùùi‹À%¶çÿPÅýZ’‰ cp{> ¥ÿ\6f líߺ(ñѤ½¼+dçÿZ ¬5.ˆ0¶è ÄX-z(𞙡eá&Ëå4ÅN¨|ƒzmvd”÷ LU¯3χi ¯…þìCj ÎÍ'IykªÙÐp8ðõ-ñ†Ì(ÇÅÅK 'jäd“zøZ»­…– oowhé³9ß´ðÚÒηÞÄÑšꨧHÚaÇó‰7žý­ú-m]¨3i¸ ½,q mÁC–´2˜*6>+8s3pÁ'ðÒo)ª-‘<ó=ò¾:Ýèð6*/±ƒþDê<ë ÌU#wÁp2fygíbP¸ªüQ¦«Æ/´åšE1òÈz5N=VÊÚ˜Ýo¼$4ëT…f›%JF%Ú)‚È^?Œ°Á\ÅÍk Çæ)çúv‚C ø°þH/0n µˆ[c~«ŒP™+ãMªP&QÿdÁÈ®;/³rV–(d¾ž‡Ìi˜�ò]nÉU©8¸]EÒÖ~dû @®º¸ÖÚ¹Ç~Çîom‰^‰ðhÌòdè²Æ?ZÃCºX¨»9¥ù$fˆÊ`ÅÒ (¢¯ýê¼üºDT,žäkôƒ!Rrð—eŠ-Puú¬`!¨Ô#Êmyù#b÷ŠÃðu@S†0è.Œ¬Ds'7 ÖRï=1˜èÑé¡Ii­ÜV$| ß‡ŠØ¬§dz‚ì«!OôOVå@¼FóFp¨aOçäðxG’F9çèß>iü•^Yk"N}vx«'Y­ýUÅà}Ù8°[׳ªz²§L Ig?*>¸Ñ€ûcé&ænrÐCa"Ž˜'ŸJ#j’> zÜt‚*ßK»Ž¨=ašÇ~¦m¦%RŒÅ|߃ éúßOìT¥ÌGTU1& 7¸cÏ %KÊÝej\¾ªŠ½š®¢?€õ�½Ùîp8Êd¹ÞÕG(ÁÂÍ£¾à“ƒæa“´qß°¸ïO¤³ƒì~ëÒ-£\´ù‘èô¥†Ó7 ã_aà)(23ʬË÷¯ ´©˜Ôµa­˜b_φ k” ÐugŸÔû zõh‚¬nKãÃëˆ\©º_=t©Š …˜ûm–þGo,UwNÿs‚K\••<³ÍçTèrÓ¾—4',é/tíÞ)[O&²¯“œØÌVñIT4»»¥¢žaÄÄ&§Û9Ó„ ¬Š˜žÿ:ãQÇÿkMWQ/d´¤ç`vÕŠx*èÓ³ó¾VA‘Ôï d�±ÙdXM¤H#˜ÄG|¸-±<…‚Ü‚]z >g¶DG6Ä£ð5Ë?ܤhLÌÉ c¼·upSÊ*x€PnÌ]¹mà¡74Ùâ_T¾X ì™É10]·ka(+B*: §A³ãò>ØÕ�F„ÿ ºïÄ ÈhÐ?OŠÌ¬ ‡ò=ñ5*gü‹«N„Ÿßb;ïg"bžuûY–Õ`rˆœÔâ—âQ°kË“ÌUe×� Šæ†a³5pE¸K2žŒŠÍC$"VǪà/9öÚVžâ̯<UNOñ?1¶âո𠦏é\i+ôiÀ뙫ç]j£åŸédêú†öS—³Ï u½bîn4Ó@UJêÎÄVºT´cÿ¶Š½é½h¼H)É´LX<qõµ?ãîžû-Ò~A–ÐV4‡¬w €ñôãGhĘØx©âHêä?«Òɹƒ447g”-ší#à¢qÛ¬Ì$½"hîtïÛôÿxDy€‹• Ï÷� ÷¦ù9 -Q`|Ù<³\T‘2Â=w™Ùš‰ZiÂ¥èì,eMWÀNZ8FêÖ˜Vl!ô€hPôÅb š¸qëéwˋچT–¨£2ÑÞ£ ?‹eˆ† EÅ…·Yý½N‚ŽjQÜä Wä ã=‘òQq€<Ri€À„2쪅€ñ6A¬ r‘Q šx̳p–ZãâI.š¯Ò—çjê@12•Ö[U×l<´É÷‡frF ^®hiÅ“ Ôq§-aÚ'º ¤™¬G••îõry¯2½Hv‡œ©ýCoáösg0‚w32ûR_89„h”;z3ìÌØžÕ\5é¦P ¾ÛÆgë6/üà goĤ­Æ±ôw£Îý”î¯3ëý0€ÃÄYÖõ>•l6©Ð¿kÆWÕ]õW +ÿ/\08!î- ˜Ê µ»½Ü ¢!£g9¶Y� eL–H$€q³³XÆÐ?mq»:¿òD¤l¾È÷qБ9ˆÜ>¿jüœBà9’\³m¥¥HEà†ãèò8Ôýb"îuUihm^—k åÃoÓ*{:¢ Ñ($%0BU`¼®Xj¿c\nî�‰ÙÙFL,*G¢–Kª—úý4‹g”;Ê£Å]RG;Ei+üMò:Ç&²9ìoÂT†ÓÃØùå¹âöïJú¡ªD²Î•N™¨âr=iq·�“µ˜ [Û Ø|ÀI-XÂÁÐh'` °6ê²ud(G™¡ÏßfåîO]„¡˜µ¬+rfޝÐÎTºþzÅÛŒã Äb•’[E-‡—AÙ¶zìÔ·;àPÿjN¹:‡ûXgÔ¦ÏÀ¼Lvß6¤j_9dq⎓\n9nµ¸Ã¸)ZJ¡…ÄzC㓌ð“Có1~Pçq^ÃjíF^%þ05ëqV©HU_¢q\©Æ“]d ‰KǨ!=¬_JåFP)_Ió)ÆS5ðoÿ «¤ì(aê-oÕ§ß$[/¯1êaãžÊdˆ\ÿt/»×SÇ€ÉÜØAÍ»†fD”ôÕȳBÓÐ,2kqd&šfB¶Wì„[-b¸»7ÏðW¯*‘˜FµëM`Ù³ÜubúÕÀÏÆÑ}¸Æ³o¢ù3”`–1‘óÞXWß›ª8–DÁŒ/¡’¢EmÑì—i^U‹:MÕmq ÊÜZê¬ÒU4ƒXü3󪈻.lsœv3¸°[*Ù»Þ{¤ÿXOó‡‰v%Œúw3@OtƒüïV²Ô'œG¦ÁWM¡ÜY÷MHˆ§<­DvÛï(0é-ÀÃeˆ œ6T팤¢VÁdÜRÒÒ!L0±c'”–D1ìâx¤Î;ûÉgû½x»u/¹9‘•yůòúpÒž]eãd6!ùCuõZ#¿Li eŽ–ÙŠù†M•5ïæì;OR{êê4)Fõ:Û/óѯ˜Y+Ë0x\™ä¨x"Äuî§„]ª#áþšO‡àÏA6Ê�#OÌZ×Zc;Ùа°æèÑ%`ë€4$ùwY~J ‹¯õß6“Õ†¦6ëq@û&¹ÏZ6‘óàAæØùƒ»çÑT¢ÍŒx”å¼{ÀU—y浌ÿxwo:%ýà ˆ˜}5Þq¦µ’°7õ6Ɉq"”‚»‘Ï:]eì‘ß ,’×+†š’ñSUÍ•»WûâTuÑ/VÑãaÐDаqX„ûrofKA‹õbÊ2ëp0Šñmã•Ï!¹8*rÇD-Ôï1pù¡>Èݽ<wp!O²%w4}ÅúïC�”ê`»óŽJ¦/à´w€à·æU“í b¤Vˆ[¿Y‡À%+fl`UR‚lüÝe¸-qp.×G ´£ò J|¶r wQø‰ ‰x© ¹ƒð\0°‡3ZfpÃñ !æ-|‚@25òo›ü,·êÉë°¬“Î}Ä»Y„¸‡öÉ8r· çÂûÓvuõxˆ£UРã\IÔ,þYúõ&¸HdHKn 2Ø‘`3ãÉŸÐ×U[¢ô @Æm\WËTNt|Œx Æ$?cæŠî®‹¦7ià yv!‚ › æ›wÇ\…)ý!Kð�®<©¬È >”ú½ï©î/g0zƒv%‰q©8. ‹—â_û%ãÌÀd Š?ÓÃ) ‰Üõn÷C†uâ]–²öJ›óÒIÅðý[øs=w•Ûà,xGg;}ÆÆ_�e\Ìý±5xÎy=¼Åº-„5pâBžm½QÁ_g¦a‚©R1 °ÀEõ�;í 3>°H~IU«ê¹r‘Ânã`Öa¨'µýdZØC<¾=ƒÝ$ZìCo¸&ò´€7¾Ûä-aFtv×ovhÛÿœ¯Ä¡†…„ú •‡ãÃâ|¦ÕSÆçÊ< †5k3ìÑËŒ‘ >úåûœ°D$Šà‹ÜEÛƒ,Èh4)Ü®3ÞìX›ŸE”¿4õÀIuꂜt4«d5IàÂéßî•Ñ ¢Í®k¶¯fÿ\¨rYuq»$‘ÊYçåHzJÞ 3‘Ž(½ Ü’F *'””ÙÞ^Kµ=í—½s5(lƒ86~Ê…C.ß=™»—²¹^Q/Í08†{¹„¢ðÅ*uãX\åù•ÜÓ)ù67ûñdíW bSñ˜(õu!¹é©Ø1®ü¥KkÚ_Mã[Ž ¹Æñ<ëW¨° ¿¸ûˆ¸èޢϭ4‰–ê]É{t+@^×.mÑ>*žÞ¹È V¿1¢Â©r ô´’ HE=rÂ<Bz>]bƒÜˆûª»l&¿^ÐYð4šŠR/…tôøu2ù û@a:Ö$y‘ÇfEUß]åRÈo•ëµ0áÕü•Â(öÌŠÂx�¸v™®Z]Ô©,Cfå^0y¼ênZ-¥.²KØ»üáöË´yÔÖ€ŒG›W§€ Õk:Ɔ=UähS¢çyVü29ˆêE'UTÛ Ë¥ÐÜSDU3pÐò¤5Ù¿‹�Ãæh5`¸øLg" ´0 BJoj8ojC@�"Wª¯ú0¢)ŽÍ¨m³É?ë'®Ph™¶òSÇ—å)–‡Y?èåtÇVêÅŸè…OCÿê:ØÌiV™Å*E瑇p4í%’rŠš¥ä:¬x§Ú%ZˆÊpmÒ‰HøßÀæ"H 8uñ·èü‚•‹‰õð:aÙíúÌB¸‰óZµSÒ'ˆ¤òßâQÕ”¯ó#a/™(¿»& =µ5sŒìràõ¸>Cãæ9 å»<9g©»öãt08’q‰ŠQòN“R&Mwü§±(½!tš+/ckÉz.ÛE_Ð{`|æ:síÀô�coªfÌ×+4_WÍ׳UCOÎ:…Èû: Þ/$|TDI¡múa²Iå9Ÿ]Lq¦àÛÀÁÙìêl=RxD£íëk´£ k–d×üøpè?5G<`sŒõ%üIŠ\Dö‡NX—ÙÂîVµq!³©_Ä·qŽ 6/ ;x¦ß¼ $@ëd±ö˜`–T²¼7çß[Ž Á0‘2“o`éÿÌÚ;ª~ggA_êW» LÂÀ㳂ž@=IÖMP&.L"a}f½Ô‘Ë"œq¥B§”yEd¿H:]Î:L©¿úݬŒì2O¢‰\XåÑZB¸š…IàwÛW‹ætØZ#–ý’RR¼¯?剾4Úô”aVåu·@jï=¼`¿ŒÑ&æÞ7~(Þ‘g´ 6?–âCÙRû€Ê"u7<i°ý.Iõ£žMµ¤¤Rjæ£<¨`ñ›Q·¿oáB n© ³ò¡PamíÔÈ-0²À”šzrXÄ’Ú)ÏTyÎ+(ÅÇ4ò;¦fu „ªäèÀŒU6÷-;-þÊøý­’nXg(åq'ïO·f;öŒbзíh þDÚü­ÅÅ Ç…õ«A›Unö{Ì¿Wlø‘µnY§}<L¤ ûdÚp©ªM#Jhé7ñÑïkä‚相œP ‡Ã„¬�â§¥Ž(µS¸Òí[¹CµPî•é áÊ»‚³+þ‘ë´“^†–>L¬˜#Þ_º¢??q?Ý mÁNÇb´ÉÃa!¿ÆP¤‰/D@¬jýQ¼C�Ûíx‚˜×˜dàŠLî3s[Mé,$+u±Â�X®”A—Ì®ÊZ;8‡¯Av¼:Ç|�ËyS›Õ-¹Ûí§b®JÀ¬BÎ_š7æ )k÷nšô 3±\‘­™&ýhEYZìkÛh}/G)ÚöQj‹øCeq¦ßQ ã"«Ò»°h“Êù–jÝ\“™&ã0FšÃarèYr˜vœq {Ó€Wë³tþ6[¨œŠ‰D÷lãòß3kÇ5ï:z+ñìcØÞ^­ÊS£ ŽN`^®“ÓjÇðïAz ï„ï;O¡ÁœƵƧ€d{ ¸Ÿ Y ÊS}„Nux=íd1Ò2Þz(]} ‚{'ìx꺬èñ¥œð5ôà—]þnh2¡Ü&|Æ”i oØ-ë²øy,wå©™B#ˆ»¾½5ç°UÕÐp2Ôý’^þÀ—ÑgƒN®•Bcƒ“£´,€"#À´Uþ)¡˜ÌT®OÀvèä¢ó^ûïÏF ! Uù‰&é¿w‡ã’_ç™}`´D­"ïoGÕ²>À|ö¹[óõ8gŒ“ÿvÈ º¦´`n»D•ûFÁ9Í>µ ôD°©j®Í &ÿò¿ùP¯Q|?]ÎçAÙ‚âøÃ?y<DdìÙEøl‚ �qSøü÷ÈRu‡²ážnM)6¿Î‡Ôר'dG2e\Ò°ü낵V{‚×q²Ì#ת­6q8Œ9pˆô ®äS�É<_„„/&y¸(™ACÿ6Yì�ºÃ›ú¼×ÔI›pÓ38qŒ)ÌÁãLå2 I.= ý0„Ü,ÌÚ‹NÉ¿{Wù.{߉$ÙQ§ TIbÅ©½‚ã§´põ·S#ŠÒJ¼ß{3‡DAô0žŠ}Ï_¶Ñ6 áäW讀 (csÝ‹)Ί€tÍ&BÚ�¥²g¬ ~!OÄ:Ѳ«ÄÕÿ÷ë>ã~¯z²€V$Ùf\nUNT‰e�˜vHqóÿQ‰f˼.¥ÿLrp¨rNhÅf2‹Rìâ§ž äh/À¨PþY{§*ÇŒ|¦‘>ÖË…i¼…î æ}†Zï¹`‘ ã€p‚f©äÏyIkɤ« M{·!We„õ 'Б£wã7Lf›Šwçk<14êóß#úzÅ™¢ìz+¹+⑾Ц$K¸}NÕK×Íæ­o€Á¾1ÏÍ‹Ê×f­{îS§„åcPqºß~Š—Ï|·ïi¡=æ´AÜHíæ-* ïa\€*G=¤?kŽÖdàZÆÏ¥EbfΕ8óÀh&|H‡µ\æPJ•nÂÅ•U9!%¹±‡š»¼xM,6®Þ:ÎÅHhÛf»©¼­]ì‡KîÞ!¹Îë$ž*ç¿–Ò÷–Tü›åÃCÑ|ƒvú’ŠÈèM‘uj\þáà½#õ¿"±ÝŽY@Î $Í5<çAÊ%ZÎÌgn›æû¼ù‘)NË ëĪ,þmjöt³è—ô�TÎ%0œûÛ†iØ®ûX‚Ñ™ Ù+�8Õ%#Ï£—=›Æø06¡^î?ÇyTÊw+ZŒžZü<o—†Ê…] ódµäÌï¥ÓŽÑø¥a ñÕvÓrû`;¢=Š¢”v‘c}“F UØ B/ªµ»Þnw%Ì—Dð€î¤ZŽÐ73Õ€C1%¢›#/N s~`ÅJÝÓ@:&ÑÞÆŒ´}ôßP&GópÕ˜›&*ÜkéVÓJãl�"Í£^³Y'°:À£S€ðÿ�»[_ÙÄÅ}–ó_§q®¥ÆÎ%zâxÄý‡Ñ¬ >œL†¨JÙ]ð|wvy¥NŒøõPðaßzkv,0­ñ"e—Mv¼´ÁÉ]nPÏ/Κ£ÚÙ£{í4Òq`)Ôöa]IÝY5[Óvfpy�ÈçŽ"‰<¿[€ÿê& âÓ¥“qè¡ÝؼоÐ.Fêè-™:¹JpI1no¤© [íÓ$Yd´:eJޥ؆ûÖ9I6'Á1#]·ºXeÊeSYSMHÇL¾Çl¯xŒ®Z„@3"†÷¾ÐZ ºŠµË€çL»’§±´×Lw‘¬Ý’Þnù>i8Û®Œ%½9ܳƒW+A 9-U˜¹cxÊT­Fð¤HУºKÝ—ëtq1eG}E³4C:ðÉŸØ2lå\Ä˯02)¤„/é@/·}!JN÷Ú"Ñ"å¦åWÏ›­…cKj Ç85&«’õPæ­Wû…Öo~f4N®ÌZ®àÿHg 2tšŸ"d™×YúÈsÒà„rÑÎDožÛ³ lÔzLu4Ñ5òfÐÒ·¡M/b_œ¼ü‡ºàÐ`Äü´ÊÔrüø ZÐK!jÒ½”¦JÓJ¾#ò<wkæbB¸^¿ˆ&ݾt5ÕÌVÃL=N€Dõ[®úF©>ðþàqÞ‚ÀÕ¦Æø’õîCo‘Ûhv¼ÆO �‹jç~3qŠ3AbI¨mlµoS4* _jam÷Ð=ªDš_ûAѦ…Øž Mß—ò­u< ÷žcӡЕJˆ¿P ÚF\éÐGNÿ‹gêÖ'¦MÅ·åšñNlÖ0)¬¸|§¡oyÞù¼óÜÉ Ý¿±•MlØÕÜd}.½¨´Æ»±ÑKè?íf6¨–Wù*-ßÒwñgkCQÀõúZ[ÐI¼ÆJ"J¥f¿©«Ó]=lg1k@ì±™ù×T¬Å†Ï<¨Á9V_LœÒfðãHaí>ÿ\éŸ,'æ4ÇPviXÁÄ@{ÄlS_µ3¶ß’Ú%SôWƒnmBÖž›­¥GÀÿv†ì@Þ×Û u‡âð}Ý.lø£^=4Èéý£¸û¨rR„ä+½Aq¼ÀîñtPpz¥ß‚Çïrû%x+wg¸œ/!æmÎI °Ýÿqv~§õTDJ¦Ãø|^¬‘FÆS!£r$]¾½Ò_ý?’˜yÊk=æ›gÊG>óE¼|0XÆ*'Qãöú þÞ³¿o^Ÿ·¥,~ÞοoQ?·®ÛÒ¾·¶oÛÓGíêëöô.êB2ÌWzpòãîó WC¼CüåkÈ:ë°à Æ×±nŽxPÒP`F/m}?’Cä!¬,ðf3’Ù>P¢-íü5°ì zð9® ÞžÏ#0|R_æÏ2T \wûcó5K ç¨Ëüæ€îf /…\ûäpÛÈÏŒÈÚ\áàŒ“noƒÁ¾³Ÿ.¢ EB|°|ºiíÖz¹¶˜~¶91üù |Xzü¤©°4L.Õö¯=û¸m†ó½Ý”É£Ñ×üO$“®! õB´ï6†·ÔuêdãX¬-ƒŠ/ãÅý“¬ ´Â¨O:‚Àr-Ÿþ<ñ 'ŽK#©Yk³°õº¡²sfî4#ŒÞ¶— êUýr,$“ÖÚó’îÁ•vàYIƒš#QpS•¡²²íIQ‹KNƈ÷̓¸òùË$¹Mµ¼lÝMåÞ.×| Æy÷¥€©MÈD»3•¡^|”ìd7_G4ÝITè7£zrJt¥eê–0^8>)몛r›¢ÙÊB_`¢^}D̤0?Ç´Wí©^ý¢Kÿ^ŠuWy›Ð ,ë�控‹°æé¬d;;¯›)ò­„ Fò¹´gkÈ®)c3´€´Ÿñi(í¦À}tG`}ÄR©¼ZŒŒj ¥ìêÑáóy S«äðlm’ÏvÖ£Ú\W<ÔõÉ›!¹ .pžQyÜ ±h™35—î¥Zô‚y!=šƒLn'h—ÏV_šu—–dœ=&gzèí—•ØiÉy­¨â±o: Üâ_¤à Ê?‡3Qå,j[õÇåƒÐ_‰†¨®¯p,EÖÎë(¢Úå$´º¾ÍM4µ:gpódÎ ˜ÑH*oûTb¬±¸„'{e†ÆÆ»¸vÎRÀ Ñ™t> ƒïy.¶ O]wŸðôù|ºwé2wøiSËv™Ú‰óR°`E“Þ\yT%cuÿyˆfòÄÈW1Hs#¢8Hòö²8¸ä°hÉßËy\ü6GT‚¢0Ôu ÿ@½!ÆAãùO“_¸"_Á;™.;µ„Š-rʤòÿ> †FZT«¥ Ú¦¡,F>Ü~-ç ŸÐ�͈ÀÒK,t‚bðAþ‘¯®'› -Á>@¹¹*H}g^‡ŸQP‡i—¼ù&^fÉ�K€LŸ?m=ÃèÑÎâÉ¡˜ kÅE‹ZÜ!:É}„´hÇâ2v¥¹¤FOá¢!cëu¦FÄÖzÀuÃÛ`n™_¾?vúÿ;PV1íKùQo\“¸$¶îÔ ZÊÚŸ¨jÏ ¾›T…MÙžÓÉÿgï/üéîÊX¥%ºÐœØƒÿG°L*©¾X»kݧ’XØJ½î•ñ´Ó÷[ok¶,‹ðl™l3±Ý·‰åä•Àü”ßvví|öü%¯ÒV2çÐMçë|RK=?pÇwk§‹½»ÿH-„�í<¢oO§ ÆÓ)…&‘)5ÕõºG°Håroâ+±¿kþGùkÉ@¾–ç1¹¦ó9Sdç€~¨\Ž C?Ëð)øßâÏôFª•oÞid&•Kæ#?B~©dâV«ë­X£Ï Mî�|>4 CÌ:ÈþÂÓBú¹ Õ HÅÒΟà>¡pRÓÏu!î'ÈÔ YЛ©þ<¦‰0"G8ºYüIònXJœ€;ÀЦ†3µ±óœ[VÝ\ÛI7Ñ¡ª³Rí©%K¶˜¸~³µ¼XˇèkËj½y›Æ’[ðŸûUy‹ï™Òû¬*)G+)õ3xØ»º¬Ï0½ëÁ"-=rTÿ2äK´_Ø¡W]Ã\óÛ‚ý%ëqUwšê|Ú¹4¥Çz}æCïqrödø—­…´ œ»uXAt¾œúÍÐÞs›ÛÓ[þMվ먉|‰x Îr0Ãam"½0:‹j·9ò÷°Ñ»c8!ÐG§›žþóÈ”-!‹ˆn6‹¢öBtk,µt—ù~(Ú~Þ&J¯Ðšs£dÎDöWKúD¾IFüÖÌ*29âãÖvþ©nGWp× Áv ™ û¡Õa¶°–PãBJ°Å'X{ç‰ú}›ŠB4­e¥ Óê¬&ÙÇPÞò#íIû7‰Rr糎ŒŠêûD{¹6ÈÅ_øYÌ˽]Ínð~@à±I’?MÞ&©øð^6YîärY_«\”ãáM‘¢žšQ„Qm‰}#âÜjªŠ oîÒ‰q;SO÷Íñ°w’ƒÌ9¥‰7ò¶š=D”xΙ¤½H3ÈÑX­ ?r£–™ŠŸš‚-àòå6îièL(¨…5á„ç=ÿ4ÉŒJ8KÈZM(Ü&Å6ÕZZáÚx°n—y^©œª6B,]·Ä€RQ¯Rwç,@´¡÷¢QÁb Y˜žûuk3Gs”&ì ]4Òït áõu þI‰ÉE‘…oG“úÍ�‚±©ÄÓP™k)´CJËûØ$G¾ÎÁ¥ Àûl> Ãw!cfÆN –€±Þùhq<&jñ³TXS3¹‚3 0Ê@˜�C Þ‰éþå€×u^.î>¼Dåj\ alCÖ£"b$È,ÏU¬1Ûm,¶ù‚š„ÌŽ&¼RaÄ¡ TÌ¿sUˆ8JÜ:¨”:fÞþÉ‘Tü¬$Õ >¿hï_÷)_ª{Eì9t²õèl‘@´}h;29²¹,ß!‘"+«û…é© OìVŒ¹Dm ¸sħ8øòÒXÂ;t-•®ÂL$¦.< ûó”Ê ‚qáy2쫻܋¥–f¶#á2Øvˆ‡Î„±uüzô(êïaè0¢lЊ† þEU]õèx5ì® 8#F<`¦ž ‹fƒ¼Ç ÷©u“¿®Š`±éòO­^Ðæp˲¡6ŸË^Šº©ˆ’>ª[Ù±¨LÍ¿AÕHeÏ}òûÇñäuŠàä‘þ L¥Jþ¤ñ°ÌYr÷ 6Ùî³´D%´²8<¨ ›áM9 A€0ìE&Œ †wœ¶ñÉ~¾7_f™»I>Ètv?iAy1 qÖ3 µx$`o‘. *¨›më�B-Ø{^;8 Œ 4æmÁÆã3Å…~!QÛè_rÿ^i”F>*e¹’c\µáß]˜­d±îÞÛQL­”ë+Z „z¤¤%Ç´è>‰¸’â1LxãM÷6óuï!X$Y=TˆïTôdh¶ïAÌšÈÑNº_CÀ›8YåkgÙŠu$ªÈc}´ZšÉ¿Ý7©±žÖ™Á¤xˆÕY®²©HKR4Àw—ü"ÅÏy¿)´y뇗(cפ±l9u‚Ñ¢b)Ré²áv…3uDÄNGž>žy:s|†m T’{aÔ}‹0l÷15 ÒÖXèä§,µ¤i˜+�ª¸oÙ÷ˆ™èÙ `•B¦áÌ'ö\ì mM²%~Ó]e%ÏF”[º¤ç¸+¢û_8j~‘ æ¨Z+šÓù§=r•B«5ûÜWPÇ Žjú‹ZN‚M\yÖÑùø‰Ø¨,¬îC€D+:umÅ´KÄÒožÈŠÐ-¨as¢y­Ë®T ÓP7ø�vãÜ¢F}#¶¦qSf³ð¥ ô”œ"tn—ð 2‘¶“ì­'[òËxx=´L…v߆ËPË襂~™µ›|¬.nŸ:ýY­qn\læY:(Ûuýë€g÷Ûe†¹¨\0X$½:zAì �a!¸BÔÍHVÌ¥ª……Bö¶›‚Lo#z ¾ºÈÄDþíÍíœ_bêÌçÁLñÖC.H>AlØç®aÀ‚+&A—÷JM æ xÿo™¤šÀr¥¼Ã‡�’w™•Á.Jg- _·ˆÀ^�ÒÑKÞ”¿Ë&íõÐÖԉɥŠô\昀£ã-zXJV0õa_®í }G™ÖŽŸ�Ò\›\þÚËY& E™'Á«¸ƒÃŸÇø�Lå!üýT‰ k.§[ÐÖFÔÛP´àûÇ/ ·¿F¨eÀcðŽÔšºŒP—€ {±…5†R•aÜ$íÆ/5þ¹©¶cr¢j¶6ÌædÆ%üyº‹†e7Žn!A­´P|Õœ,úª¢7ÄQhÊ—P÷QňW*.—z?æa3¨Ì,'få0›X¬Ãó˜¥ï £þ"g¾ï;SÛ>š(Τb¿"¼Î‰šît�Öé¤o,Ïç lÒÂÒ\=ûåZÎLËd VÛݪ{ ø(ÎÛF^az£z0ºn•§"YJÆœäþYo›ÅihÿAw¢½À—#b(BšµÎ̦vOÚ2²)¥¯êtD_AMMƒ«D”‡ìÜ™$¸máÒ[rÓkeçÓàgZÝÛ†ìª\©U Žø¬;Å–[Ù:O®)è\†šÄÒA¸¦})°áKuÊ*=òž7q·@™¼MM§=¸–£x%5u¯…T†¸W³›³L¾ ƪ(u¯€jx6%Ü�;Y%S¾À¼;åÌR*wù'Öƒ„*-ÛßÀ®ãá‘F–1.óåT,Økž½úÎ=Ã1…UA7>p…,§6¦ÃWŽì'—oŽ%ÚäÂØ©y0D8Ý™£t¸º¿Ýœce¤ÔGÔKy%xÜE§¥ð‹Ä.h¬šZhZŠîÀ×5”ѹ– žt­Ý2QgJ)¯Á£­¶3³3ž]1WtQ;¸£â:”F!H¿´×¿Æm“Øg¤Ûf¬“PÕ“èë·º{zŒz+ðÛ¢Æyª¡iV®ßJÖŠa}0]õ”uï›pé¿G‰G‹‰­òR>~([0™ïkeê®@KPñ ¬ÃrؼåÉTX)P½ ·_ö|ž¸nP@] ­¡+7’_-Š,�ÂvK?<~Ü®ÉÉ656Djcy¯í†_ïºf©ë•¦ñ§"ÿ;Ÿ.Ñ~Alqû¡w7*Φ@.,ø‚ƒŠoE;¢.`Õ‚c=Ir ³€TúÖ’d²ÐzB¼v<(¤z¸ƒQãøÂÐ{èPå—¯c<•ÓGÍ1þÒ‘äMˆ1{‡jñ“pQÀ©Ö Î6u&×›±(Ë9õ1ú$0µ±ž$N8ÙBꌊàÛëÖRúÛ¶+Õ"­ŠÞêòuxù�Äãþ†Ÿ|W.çdÙùyÁ)D/ ɮﴑ£"ªÙÚe)Vã¦È–p7¾îös°bÄØFÌ¿tö/ðoÒÄìGÇÚïí ÖÊÓ5¶†„‚<´ØIWø‡v¾ °p¥I(©–ÀGh l®7àkê÷>¬kJö!™ê Œ"£\¥úéÊ;¨:¸´ñܵYuûBSöVRÔL)¯o)¿`:9“ߥ÷íÄzž‡¦~ÖÚÀêóå,’awýíp˜{lPÏ"vúe5r)¨-=-»NAéìjÑÙ‘£%–äÔÁP;±w{O¡uÒWMŒ²„F®ú†¨Wñ µE‘ÎÉóÛwf8/¡²÷C{ÄpÖc}ûª#�ë$¦©H¶ŸY<­F!Ý»f:»{ƒ —°¹j°N½ù#|¬qQwÂþhZ“Û™Ë|AM½ˆ7zE÷E¹Èg…´E£Ê0 Àe,óXoi-öÚŒé™dDu‘àu!*·;Ãlð1•»pA…oíl3À½~{•ÒmÉ{6qŠžÞ g7æßÖ">ÿVÜâ€n™>Âe·£–‚'t9wXÃ&f"Ƕ¤›Kƒ»²úÈ}Cc¾I§B×Ùñ;ÄäôV¨éËaÛÚþ+±ŒuT¿àuÛ{Y¨¦ß}¦¹í“`¯ €-¢£õ‚úÈŸë9ŒO* DÂÌ©jƒQåæv,ÆzŸ·WÛÓ‚a´Ð˜n»]7ù%«=b8猈RSüRañÏÀ¦�­²—‡_;¬¹ë×D¸ÛñçGÓWK.¿*ä†ì±ÉY¤mq=-ÿž$îüÄ›> p˜•Cþ‚}n=«Ñ¬ å½èJÎK4iš¤nkQg1MÅA~ªlJž‚1d€ÏjýÒjÙ·sòê+Â7Þ €GgîlÉÁçYµLoY-vöëß›§¶+óG@ƹà›r &X­EÓÐkÃÒO_Õ4YÓ­üF]¦?@(Ø;‹Ö`0¬ŒŸTÈ<?éò$¹Æ£Ÿ™ØýVL¶J™"çu*†ƒnð"M[×NÀ·}äýP÷$¡~ëw“ˆ¨üØtù†ù qèx¨v >ïÝG$zO€3nEI²*öö¶_%e÷ßüiA×ùF:Wê®èô… 1³<'’#öéß54¥ ì“›Q™.<Z+1áÔvzϾÜÛO²‚í&™� Û©Ëb/JKÄ-Oá‹¡/s‚ÉQÖCêûâÐÇêµMh¡QFz{g1ÞN½e­G2Œ®LµañL 2  ?#ëÕð!U˜¢àÎímŒ»ÜïAWšì¼›p¨-¡:mCKÃàSðõËr^.œb1×né?ã-öAñêBmŸT¢Û¾;gn“«ÂÄ£H¾}©ý‡Ç馘™^v.Îl;h·`{Ü@ ‡v•φk'(·é$ëâí´ÝpÁ¾ûÝèU¹e4pÀ—£¤^@•Ûî7窖J¦Ã*^F0ËBÊÈà·" Sñ›´>ãf£±ExU_ž^žVU7#%¨˜=¡ÁD… ·Ù¶)B çWO ²¯µ«ÔÞyŠôbßY‰Ðï÷Gk-žKFŸÑQS¾Ùúfl¶KÒ¹N)” ÁÎñÝÈto'å$â}÷°ØYœ}è{ïQ€¦ñ¤IÞã%ˆy®œCl -8ùý8Ÿi‰6}茯³–‡ñ^rV¡‡-¤è[ e7 ¬Ž»­¥êñ~P|I}‘DÇî^¼)»ÿ¿€ê`üS×¥ÍÃ’Ãñð#”…°Œ #¢¨Öm<ÂǨϘæ#ž»oÁš CDÜv·j²&Öe8£šA~ 8Þ;0@çu6ãÌcç°“]ÄR ªøm#iÿOÜ„zó`õ®~]<gôÖ„·~ÚVGŒzyÀý¡=œ 2ÏZØÒèÊÍyë…!ª 8`¼lýz!žçÒ¢äaü\ŽÜ«»¸;ºfìšé„:I¢Óº‡H¥ž*§å2¢õ2Ó¶TŠºõ ûûÔw“F�ðÉV¾lU�dC^Ì´Ê/wA“ÚjpL±É‰´‹”DZ °…yqçµEBI¬Ó)µ™ê) ûÀ×$tH€št—]áy¬P1Bå¤�4Ò.Ú7#Ø6šã°Þµ˜ûZh„ÔéÜFSFÿ`¸T!$L*&íM÷q1ïþï¤äP é+¬(Vþ‘ûsß9º{PÜÊí)±NmÛÖ’U™©Xü¿¡ œ]gIféÚ†Áç˪‘C=“X¾C¡2¯$¬´CÙágM2Í[ 8IÛ«ý²�³ûé§T=ú=µL˜i5Æs6hß@¢]âODïQÅæm|uç£!?ól( X)¢,XZ唯ºÔÖ…ø¾®ÁÙ^¤y4ºdsùé}´“ïÿJ„[²yfÒÔ¬ÐÙjü¥ kå&%!øo2B†î9#}X3h»M+7õÿ´¤Ì@6Öݶgý×¥ÇÎg«ëî “c„²ž¿!D.7þ"ÂcŸMo‚¬”›Vg ¡µõU)¶—½Ÿ•“Y{=a‹Í7 Ci·Ü¾iüãª@Šnð.•[K ®BœH£*z"·µyWÓ4î÷}¥Dùסã.çftÀ®ÿYÏxàOµ:¶È 2žÐ¡MÁ؃c[ciFB,ùäùU‚“æ9 U‹ƒã mÝdô?Œ½>LÐwz²®SZáàýÎÿ˜t?Áöü¯o)‚\L@ všHTž{PSC h)©a°Ÿƒ¥¤Wõ,i‚ÂnLëO?«q,£ 29¹’y€ùÂ1éŽcÝ—‘(×øOšâW YŒÃM½8L²–Š&KfÒÄäO02‚„' %L±þÿ+ åæÍb·[ˆ­ÊÕU/)hóIIJkઔ‰ÕGU•Þ83Þ ºå`OQ¾šCݶB™F®çAå*³ü…½½(²Ç«+Žæà‘iÉ9kzj>²^³ðu™¦V*Fjt0HÆ`ïƒgÔù,‹UÎdmãÉšÍÊ—׃˜ÃÔ#puÏ6§™Â ÞLJoh`î°Ò (¤{æÚïJ51/– :J-}ÐŒõþ-ítâ%ÔÀI/š¢j‚æõßÐFê#<÷$Æ„_º~ÏÅcò•ûãNJ ²‡¬ïÙÙãFX"ôP,—…NÓ‰VG¢Åо&½ŸÖ†+ÑbY.§õ^91#.@Ÿ´µ#tÈùÕÀ’oº«½ö°hÞáÝ¿P{¿xùâPí¡4Û6-—­ö>ÅßÃs%Kÿ¸!V¿óti8©U£*ðDõ€Œ –ÃâMÌQ¾p¾=tE쳨#úø;ûÞÄÚ©‰ì£ã$´.•T8zÖøÇt™RNÅåµqÈÿ;å¿/áé¢4-ñ¿Ñn#Ž›ór½v«fõ¡_h-÷@d0èñ[ ç£1´›õïOdX^ÙÖ½ªy¤1ä¼Y°ÂÕYÊwºÿ=ÅŽamW S£©Bw„³È)¹éŒÐžÁN <—ê’阧QzbúƒÄ*Ðe¹Ì©Øô¶SèrwëdiWþRè,.ê’\m€Ç؆ٵBŽ‘»1†)]B­ÿ;š­�ÕÁiÃÔUÑbÕ….7¸8‹Õ1Ãaëï;çÀòsãŽk,] ~QóÃæ`$*ÁzÛ¶%¡‘s9#äd [¶¬ß2 Òy'9bkwópc ›HëZÈMkÀËŸù9vx&õ­“Ù²J*™áß2/°Ìê› ã„ ÀýI;8m$ëøQù¤ÑPMÚŠ•Ÿ€Ç”³»ËʇuéÓú!´Ð¨¬Åš¾ôSð‚,cAŠ ½£[LHP  ò”ŠýäïÇ£¢T8s· yâÖ:NÝ G�•µÀ†X®<x‰¬ZÞ%³ áØÍý<><b ›¹Ú;´ ·wDL±Iok“yÂAo4án­Êš­6†tìqiøOåfèIvÄì£ëæA^ÚÎØøŸqýù:FÈð£tV}”:¦‡ø9Ö}‹: æÔ,ƒIÊù‚ÝÒR¦�^z­miJÝ&LHXæ~Çÿ9o²H-À´?0´µht¬oY5]¡î¼u™º!Ø_¶ />e$xÚóUÈ¿‡þºŽíü*<Ì&ºåz¢.–MóïpiF˜m'åÕåƒEff4"F(À¯ Jº!ŠÇ[â¹;³wG±41I í@_ ¦ÕS pAYŸàÈ(϶ʄK«&°x8Ó É/ÖÐæ<½° ©ÎžÀP�‘-¹yWc+¬ñóÿ+ƒÕ;C¸{¹MðþJ§3.Êžú·¹ëw¬/]²#À¿ÊúFÂäи`KW…ÅdUÙB {s(ËÁÏO uáUœ7MD­—¨ý•tËgÄà9#Ûc,ö6\©}�ÇC±Ö "­õQ0PUL9ÉÍv†ªA‚¡rûÎ= jQäü=®&wD85J¾Wv7”÷;s«’€sC1n'Õ’­žSéà ÐeO?¥H ÿ›X?öêg$3¬ND™ 6äv×tL°…·„*“ü¡"_ £XSY7Ñ¿NÞàLÜ]»ˆ3Ëö‹ÝϵŽü± J ÆÅTè»·€@ôÌú¼ ϸ±Ü†É§Rh;*K–Ëúƒ–¬jZìÅz$ô‰å*ŸOFO†Ú©8©sÅ,C•ѱÅÄqß}ñŸ{§Áˆrƒžó¥?;6N2Ў¨’s}Í'ÈN\ß[WøN-Ë· '.àËuT‘c‡e`¼ïñ+EWOêûñè¶]˜Óoû\Þ‡z“�²xÓ_Œ9ÇÛ]á“z‚CGª5åxÊýÙ¨3™ïï o[;‹}t êÔ{:ca}ˆ‡ñîä¡=ØÑ_ Ý£qzÅÅ˹ҫܹYÇ€S(Û¬NÎxGPÔ)Ì"G ½P7®Åß� fêgïú9âêí.ÛÈ<—æûRÇP‡ý÷r+ q û¶2Éh/ÜaHh×q( ž©¿qé!l½ä ÞÆÅÛ¨ьܰ+ bÂ/4’ü˜Á§mŠåï'©‹~ÑêŸ>‘º‰¿´¹™öž„3 òÐd»õqd=ô"¡¥¤Ä ßc䊉\õ:…^—id»׋ X0yéZ:þúˆÆ·{•áð0œX}Ì€ú ¤˜%÷©bUüDÚúhÀtD©«"„?dókÊG‘Pÿ^ÀÊø¥£…2BˆtãÍ �nœn§°;ÛÌJ‰õ $ñoû¾ª Ó<ÔW$Ñ„ p¾PÀÓ5ÖP—±3ôq?ÜË.êHPŸã­“ç¹&ÔÈkUZ-–wÝ#½µS3Î@CÝŸ<Uf€ûS‹ÒcúCO+[ !9ʾMß"MzáÄØ M1EN?Ó·G‚.\Œ¨áðË>ñRI|^Û}#6˜òµ99\ÃíÂ~Ø)Ø"¥¢FÀð¯ß…úÜÜh>O%ÄnkMVîWšüã‚çk KpLá~F岕ú—§e˜¹°< ý+<ÆE¿ÚYZ‰¿1èBØ4eíÕhh™ÈÁD–ý m?0]>B¦?ß±zœ �ÃèÔ Ôžq:p€,œYëT‡Öß|ÎtG9ä[Õ¹`W#ßÅÑž„�;'ø¬ú'[¡òÜó¬r’‚¿¢¿V¯³‚S×Ûç$qoŒ,uÇ,KCä¶¾Þ„§>ª¾0Jƒ ‹}Ø–Ìgð\ñjteh—ìsËÃkÙjk¤‚%®j‹~Võ•bÀðòíÈsÔî`ÕÂyι™´ ük³³ÿRÜ>T+;F^8n.+û¯þbÎ5wª¬´±7ÑC  Záîù·QÖª§ÐÈÛì&ðS3KsI xû¾&ú (7€ðÙ›Öhª2ü9�÷Úï]1$Zã—ˆáÛylÐeÜ‹ŽŸÇc*UÒb—Jr®6_ÝÏôX] ž0 ð…3.cŠx ‡îá£ÎnÁ-²'§q#¡l˜g*mº?г¿¯ÏКËþÃí¢Ãhni¸È&m(‚n[7&„‹åïÜéy½_Ž·gêˆs«õD๰Û.ÚUÍ p£q” Ás—áhB}‚!ÅâGÉþ›Œ #þ=yÿy͆½Q±N²Ë&¿‚™Ñ×ÃÜü¾F®ÚÈ’•H±$Ys—‚…T5žpÔšxÉ&&ê·Òz °ð÷çÇ"L`–\âDa'Wþ´rì/-…£Hò÷?pÀ¿åöèÇðèBûnŸá§™}º5ü:¾ÛŸøj®/·D¿†í_m±ü4¨øV5¥7úî=4¢š_’ìd¾e†º–8)'ˆyuÞCžXúgÏž‘íÐÿ~aCV–±–Üþ\-&$9!ÁY.|WºÒbt•༆j/Ÿ]ê_eGe*àuzøÊ%c¾ü]?ÎðuÏê;…ü¾€3të6>M©J¾+¿´ÜÄ~“@}É�ok)-cr‘³öÌ=¢º=Šà.V)Bxí‡òopQpù>Öµ²Þ‚`—sëøè:“[Zhé3Jà€Í´L0÷mpvîÂ\óc/ÙáïMã)`" ¢"3µC6“›O+`z¹[Kãà\v,V”¬ž,s#ÓÏ Þ¦@ˆOÐ:4Ù"lLT~é;ñ‚—­¤ÐR›x6Ñh,Õ6ãfÜœ §”O˜K5|ÿ3˜,µäDhSÊ  ·G”⮘‘&˜Ú^¹GëaĉC¸Ëš‘·ü…µÁØê |"PFûíÏ“ê%¦²®ÀøË¢ŒïçÅò[Á¦ôÄ ^÷ ïËËò¨ÔCIÛsüR-/5 )45z!î_!fvS3ã(ÇùÕCeä‹Ó÷Ρ¿Èm"J«¾š��­´ŽT`ßÌÅð˜~ê^Ⱥ¢ç±(Yƒâ®Ê^­ \äS,@+°<µ x¼ó«fhÎÇ„fén“˜= é<¹è,°y·ðì èBt½J©køö.MCKÏHÖgGNÙ8?6½~X¶3?©Qô»° à÷{%…݆V} ±—htúÀeY½À*Lß„ÒîAÁÍdÖÝ0ùÍÉB:Í&uô—ª©ª¬éGž,|÷¨­—°ÓÜš'ŽUÂ>…²-zE,:žM2¤ tÈžðà</˜*ÑÕ줃ùt,W4qPDÄ<ùkË«¾+À¿ìkèm]:Q‚Cx'´¯W]&Lê”|ÇŽAÄʼn.†½I×£–1€Fýjâ›!˜Ã"“å×÷H> 3MÜÒN}íÙÈå¬Ù¬!¡©™°/aíQ 2ˆ•2;Äzd‡Q±p¸|P¤}oBN(~Ngª× ¿&…äåuPåÉ¥2Gmoa/mW1º bøv˜‹=GDœŒpW•¼ã7&- q©|k¼~ Ÿ”©µ2ÿ Ö°WÔEuï”#™h”ý´7õxÒT,Ö“ìYðŠîÊÈ+‰Ð¡¨y£'o¡w¹®Õ3E;7ò‰d¬ƒî+!)=‘úÔßã·á0–eè%–½}§HªG{Ópœª\y wppùM¹ˆ?f|€¸èÌ9á„}‚ l=ßæÝJ[ö6;g¹d–SÓ’”Ìåu,y~¡NÉífé!A‘\ë°öb‘©©éª§]êAqDãòI »bð¤B,ЇXÚ¬Ê+‰¾›û^Ÿ˜níû©7OBô°¿í¾dÃÊv‰B<š1Ò¿‹~¢9LŠg…iÛ’=;‰w„lû••‘©0á,®bÆE\œ^r¼ÞüylVy÷»Â èÙFR2'²Þµ×\nD_^®7œË¤,Ò¤i‹(+Ó„&÷MÀ‘`·½Ç×’5«‰•~@Lps r«®e[um£•|œ=‘pt³p\R+ô$ß#|oá짘Pgì¨×ƒüÑKOSf[O½”ô°ÿy‰:Þßì]}(Ý^ã”R=ßNÎo\SxSÄäÄ ç ¹ äûô: ,ÛZºÜ%¾( G(¢µœS’ÌǤçZVŸSŠ*eܹÊ]¯’"sÚ—?Ö÷Ç‚¸{%¡hA…Ÿ{\[Ùèû‰ï2E·pßáž7õÿE,Ä–7ØxQ¿iI¾hëJl9Ö>ýÀrH&ïkzädR('Ë¥)eŽ`ôCÁ ÊŸãúcñª€¹`ó_—z28Eh!/”œ’)À•–Òª"®Û$§MVÍ–AÏ#lwñô©Ìΰ¥÷à¯S4ýÁd9çï5Æ4Ûšknèöæ­:zp¢é“K±Îθ±  7íNH‰7±Þ¤uaY#y5io;ýžßÞWÍ÷õò.7!_1™0=V’ŽÔV¹éUtQÎà £î³\4éŠþÏŽ_è°2_Ä“¼ø ˜Ü³°Öê:¤Þn·Q®o ̧õûcA¹¿ŒåãÞ»ULJVdÌq3fÄK¹?ðsõ“¡yѨ¤ÕŸ ™]¡|4nI ³Ršx»aâŒbÆŸú»ÇãÃÔàP¶–’š�° Üß^Ú Š:¼.bòæf³¿›«Eløq—»´#¸Íå´˜À=ŰÉÏCèQQ/"IÝßék M�ÏFyyÛ;êþŸ°rês4OPÛwu´ŠCœk)€ä_hÂõI[:‡†v¡÷�Åëa£™y UÛ%õž«ÎIž@$>ÝÛWN‹‚+%3›µÀ»˜è4C·ž 7‘*¨’áˆ'1F<ÉÇP…·ãæh2cf#­Iî¬Í¹.Ã0B³*×£rºnE Ûß?Óí|ú8ž°þ’d7ß„ ~}öz»(0–qˆS>ÎX¦dÊQ?éâÁSK)d?váq­qnŠ›~¹TãJQ=}šéÖ?Šò¡–‰‡ûÀ‰GÑìLïG]ëö^pi¾.‚.Á„œÞLc˜:T€ë)Œ`«{̹5ñO]W<JÇÑWx6ƒŒŠSÈM"Ҝ˗<çy«TMjQ»É±£¶8ùô�ãDùŒÃº3Ñîõ Óøá¨Œë{Y…ã¸uÝA¤@fNB6ÇfI–ÖñúFˆÏ0}³;ðVïÇ’?R¹ñááhù7K[€‘»4µüFч±£XeWv' ¹Àí"èÔ Dÿ÷аÀt‘Ù¥KÍô’çÃŽŽ‘‘¨‡$%Ö Î²g|+⌆å@$šƒü— à$mQá(=]ØÜö)¿-ŽÒÛmÈeĦç8E£?¯‡š¼>VaºXcqv£ðV' HFÈábó«sKNäó%:„[ ²‡‡ufQ#iÏS¸w[ÐM\ ö}+¬ WŠ…ö 9ñH#äÕ·�¦¡‡1³þÉDCm†·Fnióy áÈå]IÞ\¸å°ìdñÍÞl…˜E©1jú‚WK“N ª…¬î�_¼Ê¼Ä)ju6îÊd1Y~S-×­%Õ\è†Ó,Ë.°.!¨xA4¨(k:=’²ò Ý3KAPs:Ÿat±<‘~‹Ê€Ó ßüÊ£†ñ+ñ¦4Qôk4šy:Y#Y©ƒ¡@#÷ÌtÞý¢[¦æî)`ˆ?Âå÷;ªgïÈE+|Øpj€°oë=|Cšd‘[âö¯bï@׌ū;•Î5¬_°ÊïZz±§Å´Çp;諎é€yX6LsÖî„$U–¹FétÅ/&4E(J1ìÂDÔ‘E½{–S|Ò3æ�ˆ…NðÕ#Ó‚Â|OqÒF¤©g©zDv¶ !Üþ÷8K[ڽ߈Âjôޏ·í„Žé#ÛBßµSlÚ>‰v^G§,çH4ë¸âæ.'"ÛQBì}ðw³dx K£ÞSÉ^掘 (|†(«J3>ŸèëÜ«ûᣤ4.ÎÝ<Ïß “ œb®·ÈŠnÿá RK15ÄèÉé½›¬9D_5 ¾š›Ü\ž!>˜<)xX[ŽОÑ&`;(„vÓ‘”RÀ‚ôŒ¾$ãŠíɟО4èßöÁ<¹°³©Y¼”!6yΙP^�•°±Â_: ›å2À³¹ØÝ¸qò ¹*ÂÐà ÷`lDJ%¼A.ú«“ßï?F_B8êxˆyÙ=x]ô4Ñ¢^K.ÿE½a³JùmðZS{â*šD˜ŽÚßèŽ3Àu™‘€Z£ÅbåßZˤZ©^2ÉŒd0/Ciç@`D«¤Ô ‹@’ØÐ9!@Uæ &{zD"^¹4ÁP-tJ¬;ÂÈ:SÀĦ%†»cýš[ÎHéà?ñåv²®’¿`ìkñ»Æi ÑyÈÀiJ5=ÑÒUÃyÐúqÅ‘³ Š­o£`r\O“Ʈ㭬pd)Þzè‰ÕÓ…ìµä>ùÜÕÿ^÷“7›ÚBñw±ªÏ-Ï”¬ß‚tBéîh¸ÁFó’n¨eXknŠ@18B¢f:ržç§˜±´%ä 8­>>ÒŸŸºd§ëšr§Ôå$,•±ÕÕRÉÎвxl|¡gøŽ~^fxd¨lRud,Ä”;‰ò^Õú­ÌŸƒ–&ê`xðX™ ©UÖ´£ÑÇK‚7B¼Ñ %ˆ¢ Í6|§~;x»® ÆLFñYË8É\îñ«6–Ì!]‘mÆkˆ×�† ƒcÜZ8ÅpVe–¹WRå,"ÝXÀ=ÌÁ‡„Aß“¨wn0×bcå[„»S]‹0¬<q!& kžAŽ©9N…·'²z-OáàUÞÚr êÔÔ±ËA38÷‘‚yRû½á›ÖkèΙ”ë`eäŸËÿ½€ˆÒ°2ÀQÃ=*2ò]†î°”OÈÁÀ=·å0Ôkýq"÷.Y7mMÖ:ۤƶˉ‘×>¶5Œ?þ((Òe§›Ÿ\Ü;K÷£µì±MƒèD¾­×ðýØFkW™CIúå�¢nàÂáy<·[—|uÇ$¥, ýÜZš)Œœv *R#ø@G$FÚM„¿o­Wr3Ö’bìÀ�CK´"P—Ò»¶4¶&Dâô›ÿ|{ð/{QS]oVo]âF‚Ø¿º@w‚qÒéÁ%^˜¶.¢¯)mu¶ä yV¸ï}°Gõ*ò Èyß@ztfD|ÙÄ~bÔƒðâ:Ÿ6¯+œÀż·¾€a±N‘Ö¯ÊlŽôb—Tºü0¡Ù®Õ¼õs8üa¥,Ü>ßÛ9~‡3–}éG©³sgCÔnI®×'[1ñß0*i$Am1àžkçÁ+‰á º îâ´‘ð kiÜ.Ý5¼æ1[oáÝØ/*‘Xõ?(§Â½, øB/FFf­wA9þR5‡š.¨KLfXâŽÇ•ˆž›/¤LôVûÃç f.I("ë øŒ¤0rÑN–zܦ]Tm—ýI}¼dä‘£ù"•˜ø„Ç(…É*Š$¬X²ºà`p¹ïd%Œú1H«m´Ê´’B­ŒóUZ$r›#ŠÇàØ®øB±Ï.éºê/Åèø˜P)öR¹ŸÊ˜uMÍfeÔ%‡˜ŒRЂèoÐ~ç ·:<$ìˆ,’ …Í`ѵø ×D>äN°ÿÙic09�ü��� jP ‡ ���ftypjp2 ����jp2 ���Ojp2h���ihdr������������colr��������"cdef����������������������jp2cÿOÿQ�2�������������������������������ÿd�#�Creator: JasPer Version 1.900.1ÿR� �����ÿ\�@@HHPHHPHHPHHPHHPÿ]�@@HHPHHPHHPHHPHHPÿ]�@@HHPHHPHHPHHPHHPÿ]�@@HHPHHPHHPHHPHHPÿ� ���úÛ�ÿ“ß…æP;'ËmuyÝOo¬­ŒÈ¢¦M!œHE61ò’?. 4¶7æÉ=ý­¹^€;~ÛnÆÔ´åX I°)À¢ª,@`\EùÁ~ÒAvV¤Ê:QLQfH\7¢+ Qâd•\\Tlè*¶`vGJ²?OÒ`csY“ÂÓlYb>r–’spƒ-f©¥µþÓkÌMÑWVk¡$§³´¯<AÕ‡ È ¬¬ ²‚èw’ý±¨¦~]É`@kVcß-<›Y¿Æû ¤áaŽ­E­HŒÁ”@4^Ðç5¶!é^ˆË€£mLFˆë2¨—IQ– \ kß…úPUv¶èùÙù~5èp¼è=­múakÓÀ{Õ(a"•Ol¿â¡tøtÔÖ 1émw ¬2%FaTFT–)û8ùËJbîMÍÌ2€T º“°®¥]ør{©ˆ?…¶4ܔŹ]Æcx,ºVÎB„ïšaÆ>Ç[¿ÉºS—7 °êÇèƒÛM‰O™lÓ{¸–ÉÔCqÖ<ݤiÎÜ~™†­Þ”ùT!¥¨ZbE9Îg<ÏÂû¥àƒÏ`™ÎhÓDfçdR»ýíÇfzd0ž0™´S,ÿwV¿+Ü׎»xk¹kú Ÿ"ý‹åØhX´n»›‚إܯúÜšÿsïß…èPUv¶èùÙù~7`Îv;ag'eƒWZè+!´Á©F'þ¹¡Fí7æ±I|RÖír䮋ԢuYV$ðõ¡)‹¹74 Ì p¤ÖÎEÅó£©_ÐktPaÛi­ÐÈ6õI¹$R­rKÏçÓI‰râŒ~ôÃÅC ËÑHõj’ÝtÀÖ}ëS}Hˆ—G9 &l¨ x´ù�XØ3^öcµ"A9;{(cM_ç~‰ÀM_©BÞð‘"·þ¼|”VeßíËø&ˆIÖÆ¶ k’¶=Áùô.Dd}ŸŸ\¥Åþh©ÙÏ`åì®Ìpç~áK ù€àÅ2[¨ô[¬K_ß…æPpéöo%å’ê©ùnE³!†ÎÓ£ùöß=\oˆˆÓ¯À&æJÄ›7é=,„z£á–hO2¶Y]ÏÿcfAL.1ã€'&¤4ø•› Á#Ûjª\ÍÞ …§% ~;ñ ‰×°¾6èѱ¶$¡Óp2F`Øì(™5ÂÚ‹ÿ#D5'àÚ3Èëõ°W^8§ª´U8Š ‰ô™?XÚÅA%À3ƒ—ÍC°«íld¿·Ù:Àû¨\V#Ô>‰mooWãøqá8hG‰ª×oEŸþŒa_Š– X¼]pPØð`YFóúítXG·î¼ ?Œ Õ¬Q5ìlžÏ·Ÿ…i?:ô‚¢žøE­vQˆ¦¯ßdµ\Ïd*%`ã/G€3;ýzËܦMxènÞ �{FC’cUiNÎEÜ¿AÝé$ûœ‡_ OÖ*O ¼ÎkHE¤biÂlÀÔQ¼ ²Ù?’©,¯Ò›©õWÉ·¾q—TÔŸ—ɰ/YÓ€ÀUUQ蛌åÝ:ECs}^®ynñ"O€fÝÈšÏvd+WÈ0ßÅ´@×9òY_%›YH~<h´ÈæáróS3Ðî˜t¾áq÷1d†@¨„ì^s6|Ô硊٥Õ‰( ÑÑkÔ�öœÒž±PWµK à¼Î+¬ßªÛ/ô¢']4ƒ{„.šæÍÿz’?îÛ,ÜC¦ˆ®©Èt ©³«`«ÊÈ g¯²ƒT:“:n[ä¨r ùZIÄ+‰^çãVëUð°Ãzò?+Ë$`¸œ'ÍSI§gxÆ­Àðó¢K軽ÿ5îL»þ)ï>SŽ™0é¤"^C(Ä>H§"¤o äÿmy“ßø÷d0÷òåÿ{rr *îw# µ5¢FË«‰z[êtËNÌ 7†[Ïw³T »Å%ÉœTw“šœ¬çú¼Ói£&_WæÂ6âÐ+kõ¯º{àà õœ êüý±9L‘I’œKrÕÈÞ(¶ ='�yqtå¡®O? ö±´ä>h÷WWkõ[Åêu/âqöÒ…ÇÕßN +p;ÔaƘ [ponNÉ‘F‰!ŽeÿÏÂÀŸ…?;vÖ8*N^» W6Ä87 |¤Ó-)OèC‘-öhˆÑ ‘îM 6ã@f.I˜EÿjHÂ3w<¶ŸX*ð½„ÜÖWmíZ‘×÷gÆaÅAnBZ?F;œ)?&00%nÔã!«Q} Q²@¸éËÀ,uÐ:ƒƒLFë× @A?V¤ê%V–J°ºÝ1>’ÝõvJ<Þ“N¹¿?,ÊŽ>.YäO-l¤øÐߣ;AÛ²®7!–é¹'3Ðî˜t¾áqø/ 7ð˜¬^ÖaÌÈTž~>Vûný)Ô`XseÃê9 È;"HTIÔ|7Ò¸hßëöXd¡+NÇ—8Žæ|\Åàž2?iÞ»Ž”žpõÇ=܆NwYEE™­GY~ãñ+þVìàv¶WFtÉcdÙ¹ú2ô¨&‚|LT¦±,X·[¦CÊEË_¨¤™, œ;ÖHL_;BàR?'rþÙŒhKŒòhÖš1Ý|q÷=ù_(Ä>H§"¤iLÀi=ñ¦]îÔ�é#.çðÀƬgÄ2‹‚jêHìJvøiUPŽ-·5þ¯F3 fcoõ-K €“©¨Õ»‘Îoü_<¥œ‚Õ¼ë:Ê ×ó¨å[Ôñ™R8 Ëö Ía:]Ä–uÔ¿€±›12ÍÝ~jêï8õ"7HНÀS-<Á&¼ó*p`Ú�Ì<-Óœ¤8©Î÷Xlºuï«£RËÚkÿ~[³—' _‘0$³.ö¿ÃÕ 0ƒžÏ´Ÿ…c?:Üs†±¡»¢˜­øvf~÷?NŒÊáÞ?Üö ‚J\EOŸƒá·œyeúº\)€šNéàq†ÿT;ÿZ#ñ¨`,;V.‚ÊH/Ú§¨ÿZäx lýc¬£wññ0ˆ›½T„ʲUJôáBOk`#ô뜢ÃrÄ£4™ Åð÷/*^YN}³ ÎtkË`Ba2éI<q¨þNT¦ÅNâî?/@ïReî(3å8«Ú bF!PÒ–¬e­Áu ˜'tª‚E2•± µ,�ºdy¹`¶Ûä\eKd³4¢Ü?×w˜@0ÔÕïÍ&;Ÿ%!ôØ…Ng¢Þ(2[ ó ÝZ”'„êÿyüà•UŒjŸ£Kf¦€üBõqÑ-λD1 üY ÏÙ�eúAr±HTœðr5Vuö-ê]_Øðî‘`€çhaUªfͪ%üÊ ñ ÅB‘ÚšNsïe¿(Ä<ð¢�e½ê)é­P”ºb·!׊Gö‘æ(û‰UsÙÏôwÈÑ~Ð墯EÔ{yu}9öõÇËÔuâ¢5óDHChVf„#Û v˜^XÙ$±1ŠÇßÛÝ ™l  L_k_h,A|Û•%~¥ØÎMwËÞe,å&âÐ>æÑ£o4ÁNgssu“…Yƒ”`F“û¢r¬¨EâÉ?pF.– Žd;Í|2t/ :\Á]ÃܘD.ñÙ€4° 9ñüß9~uüéðˆ˜SçX¬Ü¶c™ïÂQ’Õ`â-Oõ}£viK˜­_¿nIQŽ¢Sd[Ïôw¹SýoÒ\ØðÒXD¨øŒž]‘{I®òÍ]Lm¬d°ÊäÏ"P[¨(ü”·Úð¡có=ê¾:ïþâ‘'† Uàƒg1+Öe›³åt\šgx}Ä3õtc²së×TBpI¤v í¹‹Ôo߆êì–oƒ©/æ8‘dÜþý$¦Œâ³Çü¤ÐrM´Å^ÿ1]a2¸N)bµìl)#¡Nõ®4…½Oð ôïRÁ$-ü#Qöne³2šÄw­æ—(-;°ÁeNRæÐïöÏî³ìÚÚgýJåà~ûR2޵µuã ]7°?²±á%FCNñÿS«gw4—ÊÀä°NøŸf6›A(åQ±W“™²¸ôóö.a„Z–LqÚ*Ýñôµq².JúŽ| 6Ö僆õaò¢|A‚ßM.Y,ÂÔ´òÕ`޹V7@Â. ™¦âE.aèõŒXZs«c¦sEŸßœc_rò@: b®¾¤ß¥NÈeüfTÚíµýiþÏL¿#FP¶<¡�áê“ Y_\­jÔ‘Ý=K<"qq´@SŽ/zeLßߟA7çÐ üú Àv‰|êI™OéþR4'ï•hóÔªÅÞéÛK45FÑ{cF–‘ «Øb'6ÿYð²§DO)X+Ã~†lK°qpÑî‚…¢Ì•¯¾H¥f,k>܉²çJ´¹e`ª1¨~ëômÖÆ #êØ6U@Á$|wH•PBðÁ dÈß„èUŠF‹bíÊãJ8¬V(•)¯ë::²y\šêÛÿdc(}v)„ãi†¥Xx¯ óh¬1à½ä¿ŽfÉ‘œûäö9ÎÎ,]ï—ñé¡ë:ÄIñ9l]Ð!{¬¿L¯|`rÐ+@OêÆÅgÙ*ný¶ÏSt_ÇšÖ·%Ç~Mí‡IZÛ/[Ýc¸£)LP¬Ž6ƒçáìížÏèþ[0š¥Œ×ͧ͠˜’l7Ú%|At l&bÝ{‹à Áö "ûæ•©;Jeu†&ÙbÂwÄèédãñ…Îbt]^¦á,¹1„fú$•)¬4/! ¯É‰®@ÁŠb ëhÈÃÀžó^J¶4½jÎÛs»†«®†NêíóÛ›² ÜxF[â8%Ù„ê@,Å¢eŸ„À{ìERp ÊWl°Ly Ôl}/ÌêÙ2pì2°žsÞŠÀ/‚m½ ÆöË ¼ÏdD2èH#Gnå#c³F¸°- Pz IŸp-À }Ž;<;óOù‚?œuäûöåáÉ(� ¼!±ãì¿wsÆæ©Ê½‹2>Áí™õÎ=5?žª˜´ kà÷¶ŒËö%+m²q¥Ë(ýõPNaÙr ò q¹´¦)êJÊýkb²6bHžô«•MQÀ ÃBžGGQŠDùß�Në1°d˜¨áo¡è€è„EÀ°ð311 ÉWŽ¿žl%ð é„Y'öéfÅ·[fË*ÍÛ¢ö™A„6K&›CîÞãàæ G_UN$®a·AÖÉçžVu¦òQÏ¿uÄ?”áe“j´4ÔYÀ†Û?Áåwû—ç`{O–!r°6úîê0|GéB®Â"j£- Ê®hû?]Õö"á}ïzFṺ"ˆä¬ÚÓ–•òvò�=”åOk\ +'fv…3…A¤AB…ó2³žfàa¸g›ÝbB`¥&sìÒ­;~P«½âÑ_Úk .7Ní ßWk–Ÿ"ßåèØPŠQÀÄâI8äî-Pì% =34EîÔG'oX 1Ñé-áž„ø�¥d2.Èä8QV“¯ßŽ�;ÚÔ<™%K0;}x¿ÕòÔ+)Ø‚©1BLEJDyúèS´<Òm½F‚™8¶p¤›^}l²“v”†®^lC­?2­÷øî¤¤Ôémão„îWÄK‚'p1÷~˜ò¿ã¤•˜fª±¯ÆÑ<"þEòl¶½ëB±5 #Å"->åáz·Ô,+!¼¡®{ÐtÎso³JõˆÐ}²½Õɼ%îX0äã¨w;®˜ß`Ԃσõ®H–&$8{Ä 8…mêÍþ¬šÿ,k‚wýØì·aÉæé ay„b+°PYBÇúýèaR@y:’ôGÒ:"™ÒǾ²TUà> ‰´8şߥî™ï({‰Ô¦ÌáËâ7;Hï-dÍ›cÛ4–€Ÿ'íïÔúMìù§¶?÷™Ó¼ÆLÑqtV™¼õ=Ë +Îõσïkx[¬‚çû&>bC&ÞÎ<º¹¾‡;§z‰èÔ&îªVšLGxlwúr…¥ŸJ ×K±Ø7g„á‚ 0<$Ì)O~ƒ¶Ž‘¦æ_ê,¨{ÉÍ’SWùÐÖ"Ÿ ¦ªèýº4'˜tpêücŒÆ*ÌêJ“ÿ<¡À+¥Ç¿­§ñ8… UhÄ8Dð7·`§DØäV„jˆJõ® ¬Ëe h‚ÉÈ`U0þQÚôº'íÿDÑ=çB/™ñ¹éÌoŸnàg£#݇ƒfn¯Œw+Ï C}}«ª‰§´ÓBÐ`ŒËkoŠ).KªGbA §aŽ )<‹S©\ÌðŽ"02¹„koâQL⓽M7IÍz>"6òè æôð¯0¨äàhÆówߟE÷çÐÄüú1Àv‰|êCLk mÙ /z*)0¥ztµ½"_2&[¬7TmÕ ¯HÄ>ˆ"D†F°êqõ¿ÑÅ\Ö½Æqß‚nYsœT‚í¦csÒŒçÆ®`ÇÉÍ%… º@Š´|ô¥p`Ý,žö”+“ÄH~Aq §Wø¥¶øhö7Á2m DûFj73©.jtz¼¹ §Y—5å GèѪâI¶Z<a’p?;þvõY±*¦×’Z‹nPÙ’÷\: HÇ#ù)h…káI+'ebÕrO Z0çËIë×CC×+6:¨ç³y1OA²ÄAÿ— o×õ½+lâÙꎳ1g÷™„c´-É?¿èŽ N@ê‡í°™ Öº±« ‚[�ޭɝýû‡Qì¶³ÉˆŽŒîüMÿ3í’+ yÎKù,ùLê ë'¦›³0 ø$m.)-ì>�Ãë 4LÁ¬Äî•3èƒgC<%øÃ%—«�¤�ÓŠ‘ŠlU˜Ÿ=t/’ÈôÃ¥\öœ”Ýót3"ˆ!Ì8YFä(‡¾ì¡.H¶K±©à¼“Ô%Ò.ƒ_~¾’CR).EÝàáÍå¬bOÿ{«v3Þö»Íïv.X÷+/ï3©XˆóžŠÅÉÏþlIvbˆ²‹wr”Æõë®�ïA.™¡;»5Î`•Ô»ÞÃ~Òú˜EMÏ?‡ŒqÑ‘ôâ¶°âU¢'ÝB8Wq¼/>7¶ãm…kø÷š²PÚ ªã+ (Ìaòžwç†Ýàö8äÕ*AŠÚ÷<ä|»˜ðjwÉd˜‘´Å4yÜ!HŽÑ€xr×¹Zéc Ò036²‰Ü~LˆL¢T–ßTrúÛMvª:nÆdC¬Ûr°X?7Ħùн¼qd)ÒøyWÁÖê…$‚Rîý5¾…Ip[lšà.íÙBƒýÒú¾$¸F¥-þ Š@Þo¬½p™Su4!Æ�‹Ÿqru¼5ßòŸýôí€ì| õŽø¢·yýåîý}?ÓqÔÑu °²Ö‘ýªëÀR+ú„ðÁ 2Lì`‹Ð«ËÝ ÌJ~®êfö±^ó:–MXëNªWä×ëU&¬R²÷„ž¹ÑÐíý¡ÌjBÅ>_�0*Úèº:%äC[寶>N·„45¦S“¾½„½th\iûy™u0 ×\IŸ"FD ÈJÅ¡äX„°\ÙBïCƒ_¹žþú+EþD¾?!!YüW0�1õ ¾0N'†pþŠI ýƒ4ÒÍÅg›þÞÈ^–/l7{LLCó`\õc|zOˆ`6A±>ó@§Q’êè6¿×fÏúÿQ€ôé ÎÅ¿Ùå‚Àü W‡•wF e;ùÏè\ÞìÛ©xÏÔŒoecIc ±ÞÚè¸ë½Ó¬˜%Åä¿Lq„<c¯–•˜fª°öw·xª÷¶ :RÄà1ÅÍsžîòÇ"–n(ê”ÅàŸFŒžÐ÷Ž®z&¬D¢à \»ä¥Fî¬f_uãY÷­cÔ!ÙÛ1WøjKw|ß‚ÿL·étk¡è]­¯ã¨L$nQ³¯2¯SÓ©†qî&Õ ™ÖÒKWT?œ6`Q}ê °Ô¤˜DqÜGV´vjYp\<Ë[½„Á `.Íj9ö^ ¢â*kÑý±ƒë³sT:&ãY Z73ù%øo4@8“Ö1óYw¹ù–¹ÙIØï9_bl>Zë6¨’h“Ùô|Áì}¥øÄ×î\P‰:¬ACÃq˜2Œ= |¡Ý^@UË® öŽ¡>â Ÿú5"AYò ȯŸÖ*÷p®ø›ßŸšc`ͪ#¦úu+ormôÖûÀ²yâ {—ø hæNw ‚#Ä¢¥lŽ(LQvD- p¬ 2­ûš:„rÎô¢„§ÒÌiÞñ  ‰®&MåöËËs?˜{ylj…;N†®•oN"'ò>„e«Pé|ÝE–v•.fäE½c'aû’¼’|$ÌÙÊ"šÕÕJ—s”ň‡Xa’/ƒµ³÷j6ól±ûj6DdÌ¢»áîx0%ómØÁ³®½¶ñx ,u„—O½`XsàÔÒ¿á÷tYjñwdx=‡JÉü—- áê=XeQŽy¡S¾>žö´1ÃÙŒ�m5 ŒËÄéoÒl@ÿߟ@—ç¼sóßжã¸ßuñF) RpRûÚ 1þ¡—HfàÚ¦(9r'ÀÞrÔ¢‘ü±OÎVòÖÉLúg£ÉŠ¿<vG+#Ù‹³~mW”ýfþVåC¼Ù+½¤A`1$‘*.céX¡¬.&O¦ü6æ,QFp‹žI|ÛèºË2EÔâEÀêò5¸Á­xü6 Öò{™õS¢hÍÚÑeAe`$„ ]„$¾ Æ’ 4�2-޽ÈË”CN546Aü Ý 4Ï´õê¡adŠhž2eÿm´6ˆ´LBøêd98sU¥…&m (=×°××–çGÑUìø¸H‡cHh’†ä5ójAP†¤ƒãïù›ºW3Ô€øÌuTVñJ¿Z¢èd5¬fºƒvÃápÿuÏp?YˆSéߨöÄÔÉÂ$§Ü˜ÿf k0}߯ÖCŒª‚ ¿ Ñ"òDØueº"‰óÉêÛ+¼$Mƒ’u_![š™Ä—ЕcÔ÷ý«3âÖ,úŒÕ}ñž ï ÀÀ tTŒ2€oƒe”¥Ï&(7æx§z‰ÎCˆYÜv1ֳįeî7]Þ=NqímtsKÿÐ?ž— Ð)`«-CE^.Ljžëþ©±¦™aöS.|#T2ž "$¬xãRé½¶<«§?ˆêac;Ûiн‰˜]²M ©a9 öy`î¬P·/šÔÿ�ÉÛ¶¤Æ?I§3%E�‘=YÙzm?Ì8t·yFY}J0a]|Är{‚î'Ñ®„^¿4—Ûx>±d&Ô^9ï¡;ÉéÁÏ8ëÍ7Û …zÝY¸ùÒ]$¶—®ZÅ]’OºßDÀoÃFù8-AŒßjÛåoíI?_¢VÌ>ª’ XNyØO\ïLÃ{šÀgªâî3Ãð­BoV[MOZš,FB÷‡.Š#Ƕ㠂h!Ñ…÷º÷ÝÆÎ—Da^!—ZÙÖ›<ÙÔÖæõœmû‚?p7+—-‚T;fOº˜‹uÌ 'X�š2“[Ó–ãFSÔÌœÅ@¾1îIÍê¥Y¦GÏ 9h`1Ø#TJ›_Q…5Ôo C(ÊZièÆH¾¶çLÔA)B“ZV·ùø[Ó™ZE´3*Œ¬E•x åˉ½XJgBÆF÷rh~䯠º®MZ4JÂŽ» WU76*μ¥ž¢`õ þQ“Çer”•Vq@h?ð“GûÑ´‚ozS Ÿœ+âå\~´ß>Ät ׇ‘ -âÊF½Ê<ÚB¸¿ë ød+§sGM ö .]¼ˆdG ÐX%øÒ°çš?çsœÝ‹•˜fͯ ÔHž¸6*åU#4tŒe^Ñ“#êS±4p¥ƒ¢LʇÊʶ[­qÍ�š)š@™bñŠâ}š”»ÑŠåj×¶/CIv3ôF"êmxE¥1¯pg›•Qz÷pR÷ϬQOK8"ÿ^õÝu‹[±ÀÂä…Ìá5Ó:Øâ_a%É $€[8 XW µ9ä ³ÛÇ5¬«é‹ßÝ—â!ƒ Ã:?ä5mÇEÎ’'¦5uÕAÕQA‰¾¿}-иND=_#û¾GHßVH]iP‹QG&ÀmUÓA¬ð¢¾e9‹gi1CiPñÝŸq¿RÞ(-\ϺxxAˆÂlñº¥%¾UyŸÔ¶úÑ1ì¢CéY#Žx®=bÍLøQ´ *wd8|òÝ üÍD箇#ó˵®¹& š_X½>Lè lýû©Y c`j·Ib‰¿ìNëü¾<t"ßÉXPKíÂÙie­æ§Ç~sVãkHŸÅ+šŠ­Ì9j<Ð)Öw]§ð[õæ¦h³ »Žé ÿMIüë|þ`ønR }¶E–‰VËñ~WÐAÖ=æiô…Åî83”¯‡`à€†Âz˜ÓK{Ÿº¿EŽ({Ì‚—.u~KϦÑà7ÐÜbàS•±ÝÚ EhjŒ™NUrµo«ïsŠ4RÍW=xßžªÏÃV§ç«`áwÔÑ™é„G‚­½RX×—ÿ+Pòü´ìª?ð¤ÍC 5Þ¥'cÐ%¦~æx{­|l‹*ü,N[÷3±ä7£N `› |Þvã\ý–g3ª>ü˜æK’^Е}MKº£&Tã°C72±PÈX÷‘µlÉÉL58{§’Ë NøÆí凟Fïõ‘bÔÌêÅh¢Ý•µ.?-kß8"Ƕ¹ÓzŒ·³¤¼§xW˜YH©Z½¡{-Âðòhõùð†+Ü8ðÇ>�°cn5ó;þ£<=£¡p‰&S‡™EàY¢ŠÂMzÅÒð;€¸‚Þw{èXzk‚CKØk®8lñd5\SÒuSÀ#óJ¾„ÙõØÎ—îMcGןY¨Š¦åЇ¡¶Žqö¢ã$ÙŽÖ…„Xð*šv„M«¡ÿdòy½Ò_eÓ=k|W#º¬«™+A9Q¾ % D<‘$<»µ>ð2v[c׸’ÜX”Íú¼î-‚F·&îfx`¬zcó„o½Û£ZOéš"[O â¹V½çDQö¼ëhÖQ!!LaÏÆ[ixï½=[m(ögœ@®êà Îeö™jFiÔ:eÅvæJ-PuecÔê¢øX¥‡ÃÇæ5:j„ý—bs#é{Zx€9ZîdÀ•ªrúâV70 h¦% º áxùcfÙÄ2c†o6Ñ™üÔ3&¹÷ÇvF² lX¨7œþÿAÌéLFópp%»EºÁj(›¨ P[8QF©G¸ŽæÙÅ[Þчâ^Ðwèk=ÛP¥Ô“Ã÷q6 Æèì‡À›Áâ|B¯±œb¥ÖSòc.눛ÇÉ–‹ÑiyjàJ²¹4›',oÏ‘‡çcßþvzeð ]6Kmz µø¼ÕÆDy }¤‹Â.Z‹`÷Åñtîªy•·+ÛŠ§³¿Ð€ü,5ÊéE˜n§½EÍBíkê!±o­'÷^©e”¤½F¥€™†·A}=´øŠŸ´.ø5;lŠ>íŦy–mÎçy—,\`Ö$ù~ãF šµWlZ¶õ’ j<ÄöÃßø¿…~*Eú}4à±X¼êäÚrÉ üy´¸^Ò<^ãòo ª¥‡M&]Rg·wÑ› Ô6†UæR$"g¿“_nÐ�o¡1,LjÜS‘cêá}†|cýý(ú`9${¿w^ŠaÌ‚ÉÏù2sþÂù|Q­ HW¯ã+óˆ—]e{f0ýQþÕuxÖ-—Úi>!Î6ðC[kØ[.Ÿ÷á%‰oZà +LN‰q׫YúÁ¯ÅmÙê«–솴atT–8rí6Ù½ý�èC†¡®(л¨0Åý|F#¯¾癜X6ÏÃÕ’~¦Ñøz¥€àŸ{l9Ø„­ðf èEަI·ŽÀNƒ}ü‰Ò·_u3ËerØz‡·7ýa²ýÜ1?€Œ}½zã̬­G¤û…ϳÔD…‚xërí›>­;> 4ôÅlÝEaË@±Ë Íä÷qöÒUç¶TâD ›±ÎÓ“?û²uïѸââ {U7ÔÝþN›¨‹dÉ4Î…ôžå°Fq˜ÀÿL¯Õá¼Á3v€qlhÉ.&i‡—BbàaÒŠAÐT\°æªWÎO¯…0X¿Wáԣ'C‚¶@JB,§o¢¼€°Ò$�à†VÈ–TT „«.ó.7ò¨žf=¤ÈšñÑØå°ÂôM”~ˆ‡ayHëù$æ®Êsc{ ͽÊsmjt\) æ9Á–v—¹£+¦18¢!þÙ$8Є~‚—´ém_Ì6¥_ïA”jÝÉx¥Q‚Ц®îØÆJu"¥Ýtd’-ý ]¡î 0õ2Ôa~?à�µN ü-t&Ú4Hˆ0î‚ð1qj[ _Ì–IœÍ5¢ÚgÑvË ˆêTˆ+W7WkZæ‘$&ÈÇáùkìîˆÓ$ü™®mTón¯Ó!ºI_)•ÉjLíï½&¹ßbKõr\K k0)ºùâ*?ªuÖ¨Y°¢fnWªÊ\³‰w’èjÌœÃ(}b°LDÛv!¸ŸÎ@ƒ-ûàŽ/èŸé†vû¿èXÈRyê`FÓg@øæ6 Œ*½ Çl$TÀZêZJ\F˜º¹&læÆØ4Ðã·@…§\‰`!é)Ê™e™ÔbGL´ðÆoCmS¶âUËìñŠ+{3‡ãU) ZV,ÞO~ï+®­0Àó¢þ³ªˆ¬‰†ûJZëPM®Ü è\Ç_7Þ–“U@51/ˆn Oß­BaŠjÕÔ¤™¢I|ñ>ÂëŽ G|1ÿJ¡zTs½òû?2×dÅÖÎU¢…3‹Ì?üçüì†ó¤Ówèù¹ «âµ¹GËÅó¤iɵÆN*iD%l›Ì ueËðu1Äð#N®e•~’a@¼*ŠBc×;é4Ž0PÔ‹±ã–¼Ê,wÍnàN˜E;T¹mR Ù)–× Ê+/KÒ”ò¾ì<ô{ %7Ì:îžYÐ8{—s0Ï{)”Ó*lX.µ¯kïL_Mõ#p†{p³ÑN÷÷so Ó@øü!’M‘;Ï”z~xƧò„5ÆéÚqÔü”ñãˆ/¼'ˆl]ò¿O‰ +j)í·> %Eu¾–í¦ÐD�RÁP�×ùiƒßÜBU²QUX¼U[ݰýLg¤Çt*‰… ˜½ôbÉÊü@õùÿM.Ê û’ô¿5×�(ü…à¡'¤ uë'·HPø×8H¾oQ�¿ò|DÛËÖ¶§m匲© _“) Ì›½ÕÕ¹õì0‡_xÁrÀÚßñ·+Ýß°!Žj×`¯k‘~çÏßa.‡ cÁ3X Óžd™È¶¶a¹„>é?/ÓEYïÀÅ´Ô‡æ=X„1Ê…Æýiõp& jÿPI“Å|'šGØ»‘ë'Ý´0îÞO­L tCžO¿bòT1œµºÂÅÿq­žùWsކ•«aÌßB31ÐZ0ƒH³ e¥WS—ä¯a )Æ”‡Î.%(„ݧ J»{Éß@ª>´"óàJ|3ïåù…&„(ޝv5d°3âe˜ ýJ¼ÓgUe;¸W»žIÓû6O’ªõy£~vÙc×§ &³K@[ࣨøªÉôms)Q_Ü`EÙ`éâBËouc(Ò†n�/àªE»–0߆<ô^$ûYï/I¡M“gdZ™MðçfvCŽ…•âHÓŠÆ„41üÕ“ê_(cXª—À·ºG‹aŽ0¶jãfK™1 }!ϲ+,™¤±/º1Ú%Ú güÁ‘9v@±Jß' OaL» íš‹ƒáFD”0y9¸V”Pñ©BèßéÎjYL`àhâ�jg)ÏB9Ò5á�¼W†f£ÞT¼J-›ÇZ2žëˆ†!—‘âb¾…¤2[ø"H8GýàšøY‘+?”WÿXȪÕ,o´ªsJ3ÜNSóx°ëäýꪡn•çðí.S¯hL]k’þîæñ‹#ŠËCL§CTŒ£õ½£xc@åo§<ã„<f&ö‹ã€õ$§7Ò#ÍÀŠÆ'É8«ÜŽ0×b/͘™‘;c9òÎ&éÈj rðˆnÌF´jKÀk”Rcß,zp­Y'ÎJT¾‘ º–÷ÂMrç›ú†ì#°ŒF0+´‚'N:ãY…¥x³é횇,ýç+ªxùаî÷µ.Ä>¼úOÙaÕ‚C0Z_⇴%¥õ^@ØüO :£ŸWÇ` ¼žaV}^û#÷a6—çêŸxòžiÁõJc}˜Òhlð¥oê¿â±. Åh¸ZÄx§‹±{j»iÃ&ò?J9t¢ O=`›‹G±0¶lVX]ì0Í} í0‘©ËJÓ¹ Žö7þôÿp�(3ƒììb›"‘3ålZß=îÑÐß(™¨.ú&ñ¯öªâu!×€ƒp¦,áöã[EDн¡b°ÆªüAwY|1JÿåäYj¸£Ë…ѱ¸TnÏÖQ´qVKàÇè f©%çÕÚpœ­`U3€§OT Ç'˜ùµ×*æâÞ†§“*\ä*F5Ù!®œ†ð–>*õÍ?®A“©í¢wŒ X{{‹Š±ÌT@š6Ð$ùÉi ŽŒæ$Å4Ói©Yÿn½IÏSŽÑWÈ‹+Æ™yyÊÆY죹úm0o#Ë}‚¸‚^Ô¿à0Þm²ô¹dG ÑA¡^+÷û냣Ô{Rîâ|¿V>à3ÿÅÓ©/è÷pì¡ÍÜt"òÉ »$Ÿ»f8Y#áªðwWkãy[€wш-ý©ËVÔ=´ê½c¿*À‡ý´=á«O”âFvºƒÑÂH�fq«òÛìSdÚЙ媲;¤È ½jýŒTOY·KHh͹¿ãôL/m`1ȉ»'‡µ­â ‘¶<$âÏ硾6Äj"œs9<:Qrsúýô ˆÕK¾äS /ËžÀeíÅ @–sÇS±$K¶îãYⱃ½È±ïà<bI 7èA ÅëǪýì[Ϧ ±Þ~˪U®·»‹¹Õ /û-5é#=ÅÓ„¦ÊBБFú¢áÐDºÜÓ3ã­¨áÎÞ�@*û˜FiŸ�Ú{"¹¤ÉV æ%ÓÝ‚‚U…Yky‚ßÎ(b ¼³,·¶àþ…“OBÁ)[ij ïÀgoïV|%ûÇé9yYWu‚òÑoâÖ±ñ©‚×<Ó‡í ÁKÐK9·—‘êÏn’»ó-Ò°cènµ$it-S­¦/€¶Î§šCµÒÏeÿ ž²Î 7qQ,}µŠ-å1IGU+ceÁï‘’;Mš¼ØòŸ44ùZÌòߘ¥ùˆhô—VÛïZ%ºMÛÌ÷ˆšõ­ ºDÜC®þ_ìûVnvù‡‘æâhqÉIðЊ¡W ¨k‚/‹?Z¢£xV–°â ×FÐjd¡Ï6²ßh‡üéÑjLaIç׿}Ò]çµ»PBÅoN)= ‚T”ŸÈ„Å¥Y1²”;ž ýsÐüÁ$Åù£¨ˆ{ñ寀‡dëI@Ûœ_,HÝç…ï»À@Àó—œaX÷<&±6)aÅ>"²ñw. mEy¾È¸\𥴱‘¡¥‘*ãn$ÍÕ<¯–C1ªô”^SåzHá {_ÎdGšW¿¿Lûñ¡ýztÆ,ÕEjÏ8’Ⱦy\/-âJ9͒ɯ@ù¯¦+iý­ºaÏ~¬Ìn·aWWFúÿ1(ù–b„(·ÜpKÏ ÖÛ6z´ÂÎrNÅN 롹SÌöýÜ �!*âz•ÕvA÷LÚÌöCÁÁÁmk", Ñ4Óy 'Éù¶¥L%o±Õ¦ </LC>§Ó‡ —â@„²ºE¬àëÚoü¨m†1ü^ø­ÕL€g‹€³¤xãÆ0´|× ­mPchÈøæËŒÆÑ¯0xxGžµ*b×üŸ<ãKtz’´`o‹x«'Þ=sŽQ…XêUÈÚì ѸÅf?FÔé#æ¯\ªîrÚŸþ² ×#"µ˜ª9k† å  ýª1—˜“~8ÚVœA gi'àU@ü´à9z½Ö@J*&ƒ½v•"´Õü�á_ìCö”‹Z'édï¾&W\fª Ÿ­̧³-{"ª(ç Ñ,ì`q¸XªkOÓ²,U²¹É ÙqX烱GëwwnÊTòk ŸŨ:.‚û¢Œ¬LÜY9Ï 4³üÕ¤Œ"PÃK¨µd2C#°0(7[Q6ƒìN"î:ŒÇuœ¬¨§¨‚€>þljÖó»Í C4 ¹1¶‘F¢%‹HGN> Ÿ ÊýDóP~ ÷oýú â'b…;ý̦Úo¢ÈÌ Y3:šŒ©y5eÚã>ÌWÉÒ)(&$|ùn»äðÓ<Ï(ƒí,Jô{ýuK|;j|©?¿|­:¦ÞÈË-‹´qïj9±[uqhùªE\&nX´\a6?Ì©“ïcî UŸ>oûã·Ë5hÚÎ=³5§‚hâp8µÑ/Uˆn½Í±]2­È^”@³Ã ²‹ŽM‹8Yg÷é- è‹ÂT9>´Eu;t“]Óþ´ô[ÉÎ*ÙÖáFÇ!豚$ÇycËû몿ßßÐ] ªêl©?%þéîn¾)Æãb˜�±œ}Šâ¤u$F…F¯ÍËkíòåï³³œH÷ˆ]ûü$�Èžtñ€Ò’°9§ºÐHGÔ«´iÊÔÞŠeÖ_V®>õdéýNB5X_¢‚SEš —¢Õ5›Pá›#Þ¿ìCQS°pçÑDáqCCîãëx#ñsžÓnÖ¤àj´â,#Âfεåè\¿7LéÌkYÕ) ¾úÔ+‚†w¢Q‹ÛÚ÷°gÌûT)"ÃÞ5ê®(~~L Ÿä ª Ö][¼ó9NU]c‹§N£•p ŽäGe·Q^ç¾~çÀ¡Ñ»4XõôÔÇ,~ °SïJœé¹SÄoLß4¯Ä¬4ÊjÁÒF€Ü>£a3þw—Ñðf±@®™×ŬM.‡?_Þ÷­f)|,<µˆõ˜IÁ¶çßèË÷Õ«ð >0 n¨!…Ê îq: yBËf‚îF(%r¦ë4pèÌsS9oH„eó,Q³1 ŽÈ|ïÏÏÃÖÞ~²1øzÔ€ÑÔ¨η7œ=¹D²”LUÒiþ²d·žC°köð ]`^3W.ÒfHÚç ÜZÞpÌž8qéèú2J1GwÞÐaŒ·¶ÎÊ­ú¤‚>´àJÍUøÔ^—ÃEqcn¬ÓÄf•û Îaò¸Ot=�þ�ðYxº\)94×ÕLë¶«4T©i™«¾"±¢›jòý-Á-MéPUÿd¥šy÷­Ñ¹ Õzú�#À䔤+…ǃy´4�ÜöÞ\—™CDǰŒ£†³Ž¢oq €Jýëfª}„ú[h OÎô ¦SgjêJ_$ž|„bàm+è¡»ÛÚë�t¥Zaù/›¾Vë¿€úôÁÒÌ„ÇJP6Ï͇s¡'7Ç<t!Ð8î –G-©; ÑÂÙþç6§—õÉ¢)QYhh6m\ý\𾇆-ééö:LW¤–w4ŸýcÏ1\¡9ñ:³ASžr®÷ÃÒx™ZÉbk½Aþ|÷S{×qƒ_97Ìа/wü•—èÖ Ž8{Êlý-†q’[5Õð (zYôôÍ«ß\F¿ºð>å·öcj×Èxv0cåB-¡Ô±U´£·Ò‚…ÂR1\ö±äQDä¨b[%låK «¦,ù?w>ÃÚ oáM£®1s]®+c]dsêòÞ­÷øÁ·–å=Ä(×[`ÍcMš¬·î\å¨<§ê§€L÷@ý·BrÂÖÓˆ+ãïÏaè´KÜc3´ ¦›$ Ø;JrÀ=c™†ÁS$YÑ¢*¤x½ß&ÊɆ´åQ™¯+©h‡VÖ´ße/¥oïx¦ÜAݪòâ*cƒ®/ÂÁŽ¹ÑŽAËýÊÍÊÛ+à:ÔJk&˜Ñ¹­tË…„øÂdW–HLô8æ¹æxÉ:5Úw Ô ó’ά 3Ò j5ãƒ?–º­êÛ‰†äÜÇQO„¯¦½?` …!c8}iÉ eû6)äòñGáÔþ­™/¨nbv¡×šÑ@4ÖP_!Œú±ILjÙJka.׺X$Ÿ¼IvÔ†ü)õ¿NQ*Z |ÂW¦%ëObè2ë׈? lœÜ…‘òÙÊ€'Ði”([rµ½ÃõÑR¿#œ’ÙÊž ¿”4á$×6¢¢1À÷Þy³Œ<DyB†‹é³ Eæ‘ òÛ8ÊÛ“¶¥Ÿ0C_„”‰²À/ÛNRQ“õf "|[}ìçæÜ[¤�’­ åþ@Ñ~È\´ß(7e¿'[WGX1È ¼Ed…ïò1at\;7FRÉÙ¥¡€‘}Žˆ<š‚¶¾êCu–9mè;Ðj©/…»ÂEûŽÄTÁI&¬C$êðg؉@6†pÂ*¹ëÓgVn¼òe{cÄñõ—°ç9}©dfó(4ïÏkÃ[ H…0ñ=„Ä‘î:?#· 7}¼ªyø‡¼…ÔwLV=|$ ÊÞ=m¦{<€?¤:×åÎ'vž: µ‘Ó½¼ òÆÇ¨não}“rõ] Õ—†¿ùË£Ê$^6h–—ý•.!B #ÅìO §U‡K “ÏÒ?e·U¸÷Ò/{˜;Ιf¾‰x#ïþ§Z¿cœÅaRb7%¶p#Åë[/*п K¶â¬¿34öe" w¿æÀ2"êiŠúšÈ̵1Ä*ª‰ù¶š6&¦H346“r³ñ¥C)Qur×ec´XEÞ–A†,M‚^Ïó:Í}C†þ:òüÚÜ úŽ[ïõþ<qŠ®Ñ†MQâÄ÷žsPwÍϹÞ[;útÞVK¯¦*÷A½–\5nÞ%-yjÓ�cޱyìʦl‰¹w•ßÚxm ¥Çëá‹›úõxs\46ÚÉÊx.I…Ÿ"Ù÷3ÿBÑÊÌE¥žÁß}kÁP«1\‘r1@PÑjéUIóä¾»Ò†dËIÊ:‰À,‡ÉQ’zpŸþ`è´¸P(V¹Œ3J¤&L›¿b‡ŽÊÌ�r é¼À1t«ƒ[±úñL·»%4L0»ÜJñÐÄ×=ýq k‚Iáb ø„d£›®4+ Só¶ø­¬LH+¹©” Sl!QÝgÅ =Ö‘¸å]¿õ!çK¿“´sè2I±à"a3´™¼m_›³àm|è_2™ORÜNªC# 5‰Æ¿Ò¸•Ÿ&B±|èoWÞçÒä ÛÜ e"Nç,ASå—‘{ÜQ(gµ­FaÓo¸=‚H”]ôÒùr“§RëÄ‚T ‰5ª F¢æ>ñÙ"§˜ôç«Æ¾Ê´>e`¯¬¢k*VföÈ00GÞF°/£Ú2 ÌFÿ(¼¹h‰KB7Ÿ9,¢ÛOs1bEE�Ÿ»ÐC³»éε¬`§ôT‡åîšåørwöömhΧ™úïS2_™`OË™N…Ú#?Cëwe.?KnüT6{^j,äü e²- 0Y¿äÁÿ!¼‹Âô6‹É¦löêÚqÀþT Þy,¢Ü0€¸¹¸ÿbtøînÔ“XâqôÇÿ«\„Ü¥àlj)‡®ýß”µe!…±Ê[ºÚô#E¦©"lNëϳ18“h#áêweý×›<ArF í_×VXW»Wp@Õ&F%ÉÝ›ªAÜ »Äµc«_™wL¥6\ì³Ã<£P1GJÑ«RÄl~“©EýÄË`…5ûþ'ƒ!„ÒßS-Ô<5ågÖP—“¤lXÆ! @.*ˆGWÌTm_šI` çµ…{u¦¿¦keXžœiŒTÆÕe¥E/—ó++Ðþ©ÅhùÌ+¿<#Ø…§/NyÛ/釱/ÄEb¥Ð7Q>ø}AŒ‡’Øû-7Y$Çi×O¼³Ë¾¥¼5ßmx²î2夺Â6'Ì,l6‡;wïö¤ŠJkñ¡k :Aâ sóKëjè¶æs’ž( xR5öS;׆«‚0djTûœ7MEKBåHo Z3¨-aN¡à[ðª, ¤æ¶ØÍK™Û gÑëNV ]'\‘e‚‘ñ\Ÿ{ðûÚ‹Ÿú¤»NÁY6Y›ÌòSF|\U|†éëtôj_j—÷ ±lRÂ0‰¿–}d9/JŸÔ÷¥vøXýØþ­;™à•}3Œ7§Ù^îçÄ¥6¥Å¦q KjÕŸ¾wóµ_0—p€¹¨}œNTïƒÞœ§PÑ^‰ Ï7(—hÆærË‰š§µ“\ÆË@ý¦É<)`)¦ÀSØ«†-;Z8UmT7LÚæ+'Œ¿AýkËvX‘Ë[¤š -Ï4è@:?:cËë(/B9›7qâ JºÍNLëÖ( Ë6 àâEbYÁÁR„WlVçr›°VòT'½wYPéhO°-ƒ,[ÆòñеƷö©·¹CF+rÈK³Hð˜m"šBÈfAÚûe·ÁORûdžø”S’û‰E }Í `²$¼†gÁ?P°€®‘ïâVõà¦úç»|X$5èjeëvmŠöø¹Ï­ÆÅa£$.>ã;É„„k°byÕ8øÉä'yIç\ç’%°Jf§‰Ìmƒ\Ç k Mõ4–,oW§€ /‰6åB¸0 "wkVëv2T;|ªøSå=ð|È7å,xr´LR©çk¾ÚŸZ¹®F磬Õù½æmr1Ü1|ZÊP]‚ DÈ`½iÅj'è…¼Ö'¢s?¹?>'Û§½B‚ŒÚž<m' ÝO3c«¯Î,á)gML ŒðêŸÑ'™GBRi ¹'Öö7ØÚÿ~n®¥ŠjúºCØ|ˆ'ë‚7Xu¼o½Ù0 Où~"é!e¸©™ÉÒ`ëÇŽêKc-"a~`ÿSa·uÏĆ€ڳ™¸ý ¦ß1ÌÆ¬Ý<Œƒ|ð„¦RÇùE”ñ‹ñ+ XÕ]©epß`1v¢ÖFƒÈÌ*›X/;ô4ª™‘’*R•œeK«P[‘Þ¥Ž¤`›Õægí9Î0×2Ã̆$w‹5†î©Sûo—ú·=.Â.ì*HNሎ8þ7[FÖD‹jë @  ŒÎ1ô«Íý�Ùhú]¾¼{9Q†ÉB”*é¤KÒàÁÕ£Y!°Ç˶íîS.ÿekþó ß2ôÖ€øI�ôg>¹ÅÓÆ³ŠAž¾ C ŸpœkQ¡l1§`Cƒ|Ÿ”Ïá}ÌÜ™¥ <ö)3~ì!Œ&UIdS¶ÆõYã~’R½<&KG,4¤&ë@E‹žB rè Î>.Ǧõ.¿4M½ù2‹e¢²UàJïqº@ ­B¸„íí¶`ŸØÁ¨Ïßo¶$ß‘Û$u{r/öÿ{g'¶\ŒN_à¢Ê»x!- é¨×œÖðÌÌ×hç3úAVÅN´ ó×Rû¬`õË6~zfÄcmcŒC'àƒ|GÝã jó2QÄaÆÛ!8DtŒóïŸw÷KÆ•¬qòÒ0Ös+ÙÊ%Ö–àÿ#˜ßðˆÝúÃéO%#êA߆¥¤´Õ¡J«ëYBURó÷Âø/ª˜È´餚ǹ&�íè³Yò©õcPa‡(@:b0ƒPºùY1²¶�™“û|­õM¡ ЉOE014ë_ÈywÛÁlHÚš‚MÈÒ’z"»j¡\ ÷¯)a7¤õŽœÁT¿ÒCñÿ};šœÍÓ¦¶X^DJJ¥~¦ƒÞƒeì°=Öö�æ®Ô@‘zL¡IÆR³% ÿCÑ©Zv¥;R”?¥? ¿ù wFþ]s^n*Þ·^ã :�í«îN¢˜øì¶ÚŽ<dA$vÁ¸-p”Àzí¢eQÇ ÁC¾Õ7•pU,þk´Ú¹½Læk›þ‚2:5Sr6Ëò®D\aCº\‰<{ BÓhð±1´Ì‰ØH¾ÒåòíÑYÁ½ÑVJÂt ¢ ?$¢í=ʃ¥;T½éÂÛø§Z£¸ûg9GéÞM° ßàÄÿ<­Ü*ä„üªAÿPu«c*óÓ“Z£8ÜÊ9Ë;SN.¯nte>Ý«…ýáY\{9cn‹‘ã}ÌCvÏH³¶ePIà¡Dvjp«vp-<ê‹ú<Ieqä;(®´‹èéx8¹*žg˜]}1mˆOy Þ[HåêX—ô/Ô•JýS¶¢õ¥ÜÀÚJ3¿ NÊþÌc8g?{t’düÜhÓò7ñ·Š†±aÁM#ØœJËnŸ “”Ëègÿ+`F>d•=F^z98¥Ü%!Äw±Ž]Í GÉæWX¥\î*Swõ&§5ÿ(¡{ ÍU:–øI*Ï€Å07æ‚%²Ð‹ŠR€Å1³žî¾3!ô˘·¬$™@ŽéÇáQ³{„!úªv*_¦ÁŠ‹)¢Qÿw®Sê"0ã%ç&cFùØ^|‚ÓÅßÅmï (Ozî¦Ý²úù}çÿ*rS&Ú6Á½%ø‹&ÄAáo±é̵מéÚæ¡¦4rrIä±bô|Ïžƒhgwxü<tñ’ ¡â”éq>K®LÀx\õÀF™GVÌú‡é.Q=&øãFéÿ&T†­kÔ ƒ Di«vqðâ½H€øãzUjc ‚òÀĬPê _ö¿‡Œù®¼½®‡Ôb09¤©2Gg@h}‚z|¸Ž’rʶ¬I—`Š*®tny= ‚ÏÃÔ¶~‘øz‰€ÑԨΥ¼Ñø„a[ü_9 eÍ5Ùσ¡ä‘ v›>‹¤*z,ÙÈ,s–Ï’-jn,RþEÌå†cÐí[sÏÖ Ž?=yðNu"°±B¼Ö:ñN£ ê±ÓtM£§š¤±sÕ ×Uº�ºÍý;⛑M€Ëµù^^$à Y§äõ&â–�Æ£¸{DC­H)ßÂs‡”‘§Á2‹ÂîØQY"®�-ßìËå¯Àñ)iNá×x; ò12£'ÐóùHïø•A5/hë²@æ ·™ê”À9Ä»û„ñËx¬TnªƒnV媋nÞP‹ÇÃÄx¢9-m*'3¼“|¿A¢Q€kd­è{ $œµ7Ê1h'c‰Ýúò¤Èk=d§ÚÀî$ÂÀ­Û /}¯¸.dÇú³]»J ¡û›gË”,D pÅ5¸ê #¹nž¶9‰‡+4ûbé¡'T$ú¿€¿Tj¿þEþîtö›Ê\K²ÔºòA¿!s¨ÂtÐÐuîõÈù¾‰ðÙ§­éðˆ$!3xmk£ 0ÑÚ?!+€èzŠœÒ.ª¿^±`dأƮäÞp$ÿj—«~²7Ú‰'ùz…ÅQG²_­¹ÝË\ëÖ}A[æp—Ví°ÒÐÞ߈÷r{Àq‹Ía€¤y¯_J‰ÚØÍ©”'q§\Çv-ÕlÙZ­-’DäfLž ÓŠŸYÚJÍ¡0dmØ‹¥§Î q•q êNìºVºÂ^gÎà¥ÓÚ(ÓÕ›Õ&3>×àÆ¢…³þ„•öTòy?¨�ªÚRdë£E¥€‹Ü¥8ÿy&P†Õ*ïï÷§TEf|:Hû]Ä{ú7HneÄn87õꪡ‹e¤GBgõRÃq쥿êŽz´î5;ÔÑ_¦4HÕ>9“°ÎG+­dÑÜlœÊ4de¦¯/ù äc6HöuLô GxBžÉ3SJRl—¤2IW«bj#¥]…èJ,Ũ-,bvÑ‘©hçZs³c«ËÈÞÙˆEÀ=4œ1AìJ3¹û:Ã|€j?[:ç t ¬ÔKyÜO 4ûšk ÿ�þŒ‹¯/3ªËÛ­Ì®yõ^B–Ÿa¾UÜ#䵎º8ÊÐ;´—þÓ)<‹s—g+Š‹m~Õfì)¯‹ ¼´M¼AÆ2§V$™©,* VœKVÚž—óoLü© Vª28ŠNpÙÓDUO*¼)/ä}惶Õ3çÇû+Ì¥—}pàKàò‚0­æ«Øµ#‹º{×p LdìÂÒ‘C£j¥4KââÕÖo¨àVDhø�µvz<i‘'/ç…¹J’ƒ@؈â·zƒ.¿ˆÞMV{ôE ä1ÐiÁ–ôøld&pâÆŽµ¼— «s#âÁÒ]|ßÊâ$PQ]s“0ÛxöÅòÐÄ­(ÙxýçÐѶîõ»¸¯–ÝQ(s é \—_ÛÏŸD�èWjÑ‚.°\â–{Ù­¬Ð~ÁYgò›*kºD[9Õƒ¹QEx™qØxjuÒU¡ổî“Q2œGLW·+Tú8=öEÏ®¾Îñ÷º1.=ïkàH‰‡w`Ìaå€!E„§¸Goôn°¨;C!Ösak飵N£&Yr$“M’9W4ÄMo4G~ÒsYp#ællIœKoØ®4njO 'Ãind8¿A Àùk€t¯qÚ¥þŸÒ†`u0+³úMû Ãâ®´½>~\îÜ€ì±inÛj¤•æÎñ!ÔÆŸ¤‹`Þ–ß1(õZ:@¡dˆ-!mO?ÿ%øÃkb:)å)8ïhUÅø‡€Sk«Y²DQgJ)[ÈôºÔàrHô„iM;½QºžêÖ›{ÔelÏkëßl7Ñ™Aí% º(x&Ù¤¤©€cª$À6c…Çö‡Òûø±î7Ojªë<'Ý¿yg«CÔÿ%˜ÿ{K˜NìþjfÊ«7!�‚§G’v.µïšS‚\`joY11|;íc >ÂG*ù½»%;„ÃÞ¦ÁxpΤZJtÇæÆÎtßâM܃ f+ûgÅÁèžK§éò†˜,UªQ¬És<2Ç´y)~> êÄe[›A°Ü8çì ‘‹w­/ÆE mRöÞ"GÞ\ÍðåSHåÌêžyØgLÆuÇšü夯–Zâ^:‡<+ ÒÙ.“$–;5šàhö}BC¶¾ˆ#`Î!è¦óü]O˜T¯Ât5 ÉJNÈÈNYò+ù³Á1êÇÖK¶ óæNæëφoƒò%©ï]‚ôõfÛçý¼”qk8˜"(Ad ·c|ÌßÜâ‹È+.c&ÓýS$5®0Ûûºz®I¾ »ÒwF1Ž´êæªpógó—ý�nLQ_sÞ×´Ôµ¡Ò8VÔ\O»u¶JÚ¢|VëȶF3tÎsÿ.ø-OSã ŒE61ÅC!~ìú8ßœáˆê.&Te•Ây5ØxüZÔ¼Ð]ʈÃGj•ånyh´SBì“Ò§‡?â3ÇÈLò]” ðÍåá9¢Í­³é™ow½4õ”¦êÇ=MÀ°-+ÃXã«QŠY, ÇÂÙªˆQ —¶ÓÀÍÑAÇ6ýŸµ@üp7k•Z§ð>¨â[â…½‚5¨al_TX£†r„ ®uûQˆFØŽ”aå¢JU„ÜÜÒÙø2X(WÄ÷ƒýµèƒ¶æ5Hau¿Z¬× µÀ“´![™Ú;ÎC-`€•źÎ\¹àÈ»îèürGIø¾�ÿóÊmØ‚&'ÿl ©€ �¤­ºwé,>c\E:Ü Á´FZÂ1Ç‚Á1d\YØ=×ßXY.›7¾µHn÷\OhI+I2°B«TAàË=³|}3…›‹v,Döô ØÆâå otYü-ey×&Í‘Î@ù´¼%n†”تû¡PÄ<FC˜Æ•?†]ê‚©Á¡ÃY®ÿ%dJøˆº ¶"(´³Áwª×̽®Ê�l¹ ;èü€s%´¶´ÈFÓJûÝl°å(°‘cµ5SK>{rûY œÂv¨ÉͰy„ƒÛôd%3²òâT•-x[Wتö#¨P·´¸àË#™x÷œo€è'8ÙÜØVß§+³S(uè¿î뺪½þ‡$ñDÎË ™µrÓ©Å#†7¤‡yúg»ïöØkXʆnEí~om¤Æ÷y«ñƒFuKÇŒÑ Û´ “ˆqvžþeWøüØ5Ø«T„ Éxtv6ÑCš<GŸo):C Än}©|+;z )Í–"WeP/Bd)í¾Ûœ»g'ÅQ…扇Íïîj;Ⱦ»3òËîÌlxÜÜMÑ&d’õ i„;ìÜÚ’bô™ÊSèt?E )@ -éWFs6TŒ)%0ºò Jõ}›r¯4d!…@;+8!çýñRŽ2A¤b ¬S¸¿²œìéU¥Þ`ñÇg¬¤Ú™q¦¯„mâ7ç¸ÒV'Dz°óÖE­øÝOTxøðÛ0û Wâ÷TkÓ$¿5 ÕM±àRÀûƒ¨e'¬Þe�ë§}@¯"8\øØ\ ‚¿z �ìW|ÚNOVÈôˆ$€T·B§¦lk›j0¼7ºàÀ›ˆ]˜û-¯pÉuòðw¨Åˆã6«w'yFŒ®\– óš{Ù„DÊú˜¶œ/Ï81)žçéÂmôIoé OJ.xË&$΀Âuœ‰"I™‚/P¬©ä·¡ŽmeQè3´w€(C³Ì?›×‘X–GxÜqà” cÝc'žˆìæxÌvù£¤…ŸÅâ¶CÛe¼Úß+®jÂù6®Oó®Za|‰˱iáÖ%*÷8Vnc˜Ä?¤˜èo}¹�c&¯LØO]~—2ÐdÙ^‚͹ÅÔ0 ©3ƒx._S! ]&;ç­S6¾ŽÇ"”îmÀKT4GI¡·d̪V=î1—¨¤HvÿNþ^Ʊ ô0sbþ§†æìµó“Þ(ž1fÐx©ÆhUÆE„s榘—$SW°#u„lW‰F”ƒáÇ€QJÏXŠ®*TÄcD'×±í(Aˆj $Y‰3–°Ù¥ªøæŠtá�.¾v”ä/"ÐVof±]éÉAߘ.Ëã‹•R´…ÇÓeh„ëáÐb~ã‘ìñ3¡Ã¡è²‡Eýëvy¢c} Ù'Èü«Š~œCÇ#lfÆž´á4—oFÖc×ßov€)À0iÅ'YC´»ššÜà—h¤‹½´³¶.2 ÿ\iÏm½'Æ4†–0åŠIšôúe欰*ž®¥Pµ@Š1™Ý½WY•¾,ÔûsË;�œ¡ôbÏí¯ƒðG 3ˆnnC@×>uÜ?é㲋)™3ƒ;ˆ!É' ktµë¬¥†;ÄY­Þ”2ùb°†Uá)ðNƒ¢%k.ï6À+·4Êœø ë0Ñ0ˆCSVé‘’:"TÌ ž_Ñ™lÇ yaeMw_xO)ó0)ØLýÅE^”¡°ìt€^@‚< äR‰“c•ŒËs˦ÆoGŽ¿8ПËû_±?Cµp¼ùö‡Š?«b±Uª„ëä-i HÄ E–øŒ‚R%»õ’]…>Yo} «}œ\œñÕÊRyök*$àg°m²Uó|®8û"tsÎã+ÆqFùûeú4}Ãê¯ ñ86Ef€Ž^£“-$e]óW9h;ª}Á {Í:íuÔTíjNöw¦[´6ßâ´.ƒë¸aµ·°PB· šh Oš¿·À"_žñnݨåVñdº./ÄÃy¢ªÒ…~ш³b °,&Øswîpe*¨Kð¾h±?!Wø¦!š¸ÎÕÁéuƒYÞý(Бá² |Ö«,¾Ç<Ð¥¥ìe‹]f�°ÎOW¢¥“.Ñ=?a—°V ì!¤€è‚„ˆîÿ`“›S53¤´?˜˜C\_Ò*3Ê÷‹§È¼&Ö0èvEiX^ [ø]ßîX™'¢ø Áož½Ù`n,Ý4ÇuMá û’7ïà8°ÿ0)ä6\6¼¢´ óâߟZ×ç×$~r€ò¿ªÑ%Ûú‡mÅ™BŠ[4ª*]Nƒ²^]AÖ×ç"‹ÎRcfZŽ—5uKÕ°/vôb}§©Mºwµ ˆpE’¶ì’÷š“¸Ž'½„w½WÞôo³ã¡; óÖ³CEÏXtS‰F×£÷ÌdÑ• `_…rnçúîþt“’Ô,þ(GStçÔåàÙ*¹{5d½HŠ•)HsþX”7²Ç¯Â6˜š½°'2ó­³w댻ÂÎcc. Íï¿®:âÐ|g7Ñ^c…,оÏ1Dk#–Ð/\5Ñ×I±vx1$sècó¤‰ãïÑ„’ˆ=4Fü¶aN«rÄ5ÒK9_ M7ôb쑌�vo Lm%“K6I5óÚØ>gQ%BcŽJ¬§k.3Ç+@¥5K*,š=¢‹ßqDÇ †ÒÚ/ºòØ6ýC>ÇÛÈÊÙ¢§oO‡T:îÙ·M®¬‡EñàÅëú![¿[�xp¡Ý?OÌãMy/œ8†ÛƕլáÜUýMßÀOy 0ZS®¡ô_vÅf«Mᆵ ©C®ßÁÿÓ/†}¢#/‚Ø|qž âiz»·þ¶ ˆžVpjBZvz@%ÿ?�h×xf´o^˜±Å<?1þ®r¤˜­ßb—Êg˜ö¸¢?¤9f,EŽåvñgé¹Ëìt»çà|§O$ýB_Åù~þ ìÝ-#ChûuÕo-µ•œ�z£w»Èü÷œ¾—°öÌ ,¬ü|½P¨" `2sÜ'mƬ÷3’)áï.ê Z¤4‚)]…UK4†‚°s‘ÊÂUû£â'Î?üø0B+i1ÀòqÁÞH©À¨àM,‹„â^÷ÅBsèª ËõYo0€ÌzJ…QÅÐ1ç·‘¼Æí@Eu›7« 9© 6R[.Êsõ—„£\n’ê{N± ü$>™’nVc‘˜ x Àz—ë-¾uñ‰æ³uçk ý„gåÿb|Æ)|$®cJ™t)­`¾ˆ!ïòÈ?C=”lj«ðÁB`i—¦2­U +¤a¬lIÍ2;�íœþ$«o€°KæQxˆå ñ{c O;Cº”‰Ï×v¶S".PHM#Sümç¥Iz[6_[f8ìºsî( ý¢ ü±íùúmSc ç{¨Ö#$`Gü“óÓY�ý _y©;ПnR™.Ñ’/ÀYûöJ'vj]×RzÒ´Yô.óÄÂì öÅF�·£Œ >8Ï ³MmÉQT³Vüwåè # ÿòžlïÍGJçU±rd7é盼9·u¨ õßÓeüS–%†f- á]‚H~[™òK!‡DÄÂY(`¿Û<Êd€»íd_~šùE–„n�j¨|ä'P}&xM>çDëfÞùA_ÿb“;’(™žünéþSF´ûl?žój‘JhC˜¸ò¨a?õfõcvÔÅ€ Å`‚~¬ü_ê2æÎ¬“è¢Å žÖˆ½ñ©¨ÌyPô†BQàñ†vž²…¨…£­3ù/üî½ñ  €„ZIGg]6]Á˜ÌÔùr}œ0páCf€@îhuJ½™©m©é³Z º<>ÔG(ÆÀY„“JCáò jj¹£Y±eÈÃéÚ ¯aâêá�ª1…Hšž®ú!®y@ð¨RO„¥¾¹ŽAmÎO9J¿ygSGޏ´ï{…©§‰ Gž~Ý >¢—ÒÚc宽„×Ã>Ö¥¡ëzF5¹kغ4Ýõñ¢Úð¬òBƒâƒæ„]<"îPµåæcB.'˜À95á VÄûQP:ujL»&‹ÿ.à4¿´ævF}«$YÒ7~©µBq(7êcJ8ý³ßgº‚2tUŒ°rêÿ@“ÙP â\¶ãc ²½¿÷SùçL«õÑÒ‘õÅÖ^éÉQÀæHÈñL¬ƒØÒ˜[Z–ã"ÿ' ÑÖPånêcËu§(S«Âc”»Oö¢Q˜ÇH¬šýWÃwv½Ô ƒSåœY‰SIðÎyû²°y~ëôiˆZ þæ³oÈ?Üãr¥¸0Õúv4&>y°‰¯ÖÎpI§äÆ¥"rõÑ&ð×±**SŒ‘hQž™Š×…ôË)«f'EóìŠB—Û¯¶&éD-/Š«cšvª$çsª–†$‰UB}A cÙðÈ>»eæå-JN§p°î·J­°”0^ø0$«º§çµ8¿0?¶ÕN iªÂ´H~’0½éØJEe|Ëøò¨‘BZH÷Xµ.hh•s¨2âN¦²Û¡vºšp¸ñW~úÞBwöñJ±dïähý_`ü»RD7“RpK‰åžŽš†„b™T¥6’¯ØF&„Ñöi‡5ÕxV·bWÆê1Ê®JHáy¨p&çæÁê¥7>I3#Û…D˵ÿZnYá0{÷Ö¹páèÜ®—9XnÒÐÂJŽ#Ü(V' \ɰ)švbG«éJh.‘ƒ¢©[Æktº Ù¾ÂpQ9ØtÆ"‘£•(«~Vº$ЦÈÜ ûÑfôfµ+pàPBûg<´QŸ§—ÄÌ9¿x²¬ŸÃêkS× ¹t)GÿfÞlF¾H&üI†à lZ»ÌÚó<+ß*Y ßX(¢)% 3]‹ÅANƒ#-­Aø¸@pÞ¼iÑ›\G]B.µÎÆZ²ô»°FdΚ›;1…í×Uq%²`{s$̈ây0ƒ'Þ©£Zyì”ÜÏÁØ·4‡ÉGÏ4ùÑN]˜Q,ý¶ C©*Þ¿§iV";ƒºœ®’j?;>ñ»/‡Ù&nÒ ÎÈVô™lT²ä‡è¦±ž-YúA¬ÌIk–Ç(³ÍëõŸ¥‘ß5˦¼¯æþdsƒÊñô@SÄF¬äÈÓ‹²¬}¿çðî÷øvS}½­_ooY}½gº/·´«íï+ó?á×çðí‹øw&ö”—“¨¿°J¤‚ܱ©Db՗×Á ›Ùmãà'L•ÿ"÷•‰'Ø›-ÙÙC†¤“Î(c\z­w% Ìq~hèÒÍà’U,Ó]Vãƒ\ºž»ÛS[jmz} œ®ß? ¾å‹ ¦R)lj—"~åë°¾ v… ôM<~©™¹Ïþ Ps]­ôÜ­ð¨?�#&ÏÿIAO¼~ŽÎ†LˆúŒ­UŒc;7:K’øMráÌЬ7`Þ ÷BÖñ¢ÜIã2`Í46h`Eð¼ú2¹|–mÉncЖúfu¸÷‰ÆúQ˜™kUdǾŒt {ÍÞ`ÔŒHùÊðÝBÁjÕ,ÚUo2­Z]éT‘!Aû×ðgƒP-‚xÛà%`«{ vv‹ó Ô’¸[õ$÷iJD¿ì!CB@·mkѬù!xâß•-°.͘µ’îî*(—ŒÖ9g*Ø¥:ðâW¹sŠ®EÿY)Ê?ß$¾ÖbZ(UàJm‰Á»h³/æËqpÍA7öÜDþ�N]=<UÁø2TŠ’·¥ JyÁ½ VÐ0L¬ä^dååA|PűoF`u‰¥:áùÞðñ,{òªVÁ¦®öë3HßÐŽ‚Ýxb9ÚШ¤#Ùj–®ïøEæ›G‘ §¤dPÖW„zî¥Ñk2R1ô¡\¸@!Fðif•øJGT˜Cïü-Î ß÷ù6 «â·E»æS$©"i+«ŸðG|ôíK°—‚h,†TÃq<°®µ‘F©¯é`ïn(¡X¹�£9Ú3Ž Ašø^ñµÏßbð ‰Zw$œÎœ"­ý]"BLn¯TàõñÌ“L&bÉ;0cM–©Îê¡1å¡–‹¡%{ÛÔü“Pí°,÷snöǪ†F"1ý\hy{ëd°Ñ< K¾ë¼–±c½÷�*ȹe.Ûr$ÍŠý€{U|I­ϒÔâè !EB0„À‚9@ÿ[zq^»„.Ú®ž{1dÒÿQ{™óþÛ'ud{=æÔe­¯&}iç ‡^…Dæp\Ìžbq˜çéùœ‡0Ó˜Gýö0Øã·n)ù4åw‰¼y콘?•^‡ŒÒàÖ‰uLCëý«ç¶Â´Ô•‚@ø{äŒ&‡�žÕ›÷äê©–rÀÒ­<K­e?j¶Y½<Kˆ¤L :8z{è3Vt¢ÞȤyn¯dªÞ¡;¿]Ô?|ÿD–¯2�½P g7P»DŒžã4 bÉDÆÀ|õԚžô3ñ«ª@ý¤ÉñÀ¬ÀËL rº·{³l ­ ƒû€æH;KYðTòFÎóJ«Þ¸×|’ÊØ«z¤º\I¾0Çä·è2,(TRõ#"1{Ý/ˆ(ï ‰±åw=­´S:q—tgÊ6½ŠŠ $SÒà¤îL¿•g%³›x©Nè4tçãÆ'}è¨ù»‡’Z(NbEFÎ^Èsáç •­ŒŸÈE@Àæ˜Ii›MÎ9‰áæêÝ5J"Ï) Cpõc-FóÁRZgUX Tž…  ðÆI›ÏÉ·ÓÀCtuÿl8[»®°F»ëË»q†P»«Ïûs~Iª6öÁÉnu…÷Ùצ2îË×/ƒîŠYköžÓ.Þ¶[a›[5‡pÄÚìâZsÎ ¨òEª¢ìdZs¨J„ÿ 8+&=ޡàvµÓæBPFKs›Xf“êΈÀÔEfßqÃ…Œ¢ªƒˆS_R&(¢­Rú?¨lbWümìê]EPØšâ8ðî+rÕ[ïÉs=Þ¬‰TÀ+Š‘ÞÃqú*û9¢¿2”¯÷iªû‡M!ÂrCmqð¼´¦¹}ó£ããÐåÖ3Cùjùkou»<Lô›šéi9Ðõo§“·ª£²³÷ªj‹­©@¸·2ŽvÉ‹7v°ò ka0Ón­’Ä^š(©n å@Òùæ©£p‰˜“OÅÀ”ÜXzD´bQ9› dAYò7·¯ñ½B Fz3»Õ&}U²ª—¹p¤Ò¾þH¹\ÌeÏU‰ê_“OoØ¿ÿ·2®…Ð&Êç/œši¸{·2įqŽWË­+.3ìÊ `ö#³1œ)¦™H>·… ; ú‡®gݳöiBDþë[˜ú‰«mp*ÅxòûGô¨µ&Ÿ»\-@y˜1` Cñf˜Q Gs‹Nj鮯௦¹+)ÙÇ…êJÅ“ÐÖW¯0¶Aü»¨•öÉ j•*ªF¼èå'Ž”i4ù ¾ôsgÍ®(µsåªY"OËœNÎ}WäEÃõºÏ3dá·]ÿMŽã é—’¬2-ÝCÕdʲ=–¬ cUþ€ß¶ùûá$ a~˜žø«‹U, ÉN§BQ#b'x´™ª5i…‰€ùzý’NãPïjW3;<¢h·b´ä#)n/IiµüÂàˆ×ê˜Ò`3A7Áž¦ ”›o³p‘ò ƒEeœ}zFTÌÁ‹Öh¨BßÝwF[䜽W¬EwÀ[k¶1é&YÚ2œ{9Ÿ&]É ±8D§Ñ«c˜xÖ(€[ ~ ¯Üm œ;¥onP?Ôë6è¶(~p%¹WUê} Ñp.ÂQ6¨€nFORO4û D5rz¸G ŸñžÑFqÇ{”G2« Tò.\@ –Zðâ”\ldôl)…Ÿ5^å@dŠË'Àj9N/Ó ·®‰~m¬Ã¨é<¨N˜^� ×wp©$…° »¡ýN'Ë׺/ÌÊh—ùzÔì[‰a7 êr¬õáÃ*I4úq2µ‰4nøÑ†rKâç°:L•r1›u:cþY›ÒòW·:/€?(ŠRM½íÆpdýn1ì¸øC<É~€¹ty—“³¬Ä*ã8©òÊ… &¡-~¹o�á’Íܺç6»�kº6ÈO8–t+ÏÍ¥ML%\²5‚ òüyo¥âeÄZ<, kÏm°·Áˆ £/tãÐæB\ú®.þ(&²µl¢BÖáU´ÓÖ˜\C¸]_g‡`_wýrÏ9K#„¸&6›—ç»_—ŽÒ”)`g°¾êàÇepDY“oµÜŸ´›©¿Æ¥%œp}ð:s°«•F¤¯`¿(êd )jµdk¦!üGP!;µ÷;Cõ#<Q§¶.Ö Ùîv2Ò³óAL�Ç)ù@¶3«榿ÏB¦( ¬Ÿ…£êY¨ÜM…Ãú½ÔE´Ï¥};¬¥º5.^¼™þî…àò80…Ù%é£k2eªp‚DÏc7aškÏ’CW& ¸nm¨âY|!í]¬F`�$Q@*ŽkD8-ìqK @q#‡…5)Û÷×QúÍ.°;b}`bp3ò›õ¼,~À‹3íÓuª×»Ye§®·ƒ$ŸafW‰(§eµ¿¾ñœõÉÓÚ“Žã€îá Â–ÞÉ¯Õ ˜w"%Ãìu\­,ôÖk’lS†iD*Àú×éˆ; ©œéÄì8+–¶‹¤i>°YزZ0Éâ@?lù´ŒˆXq³dÁNô‡€"@7>œÖ"qëõ$±6#ò©÷-*q(À~1!‡¾¡ÜÙEª³ñŠïMßw LM1%ëgt¿Ž •ÁO§%uÉßøƒ�^GmÀYR¶)¥Ýî©u<Ú!‚Ïæ’¯dQEÉÈè®$ʧÖФiÎÖÓâ@Êf*žk=œr¡îü>„äßãáÎH=Ȱp>†~}² BázbjÊ¢gxQœ( _!}¿kOæÅìÑ\lÁ ¦GÍ@<ÿ}$EUŽŸ 'ý‚²ø·Ð¸xáÞÃ!ã¬6¯mÝY¥Aô‹\’ïZ ¾Üž?m_bBšþ,s¨Fœˆt;ú1{ƒÒB©£ !†/ÙùhÚoôÈØã/—¢ÊÈ1ü¿ð_ĹTf)ºža+o-+»mì“ „©‹wÕáYKVÄBÜ•ï6¯=®\ ­úÂ.èqW­8t=u|K!Ž+EXlþïh „=Ó" O>p>Ô0è[¾ÑÔ祃¥7CS-©¸Ü”(8ñéûz.ŒA ¶ÉÇQmoü³[ªzp±5Um§¿ Û¾ú½ @¡v”ð˜J徨’vU| U<#‰ÿtÿw ¢;`ã6 VjCB¼Iq!÷M ~†a‡þNû•ç§G›¯µŸ6 ð{H»´*¹µª–X“䆎¿¡,¡{·Klà:²PlX®˜1Jz)H‹þÓÑ=:òÞLa8Á°5Õ"Ò&É~nõxÍ5¶Ø‡’’—Òiõ3uÜîºÔÒXãìß P†¡·køz‘E„Ïpü'u^éßoØÖ¶Û°}ž;+¶uN”Øï‚WaS¹Øóƒ}Z•ïVŸŸ¢T?|ä[yoE¼DOÆÇ”£€hýi"íR„|®¢b”ÐŒ“@Õ>£´h_×âçÄêCoÀòìóÂJ’ŠøHáP­ùzQ£�ÑkÛM–ý×òâ&˜N?”!&ò“þFjÊÞä;9ÜbHÚÒMCFÄy>Y´Ü¾nµrÙ󸫠Ùd¥† …+ç'�§Vß|Ew¯ë€UNOFe• çà \âXˆ×ÿ×òÝÛ%ºlßC>Â:åHoéê…I(“Tº%&ö‹ýèß¡ã$ª²fÌ¥´ò`‡Ã¦¼n‰’ zŠæ›l"·%š^êw3!95Ì{8±aÇã2¬/æ(ÄÐK ,àŸÇ£' 8¶wõ6¶K-Š› ­{Ð:ØðLjÉÍ]¶.£ä EN–òf…j.Ñp1&!{Ó>À ¿ïüŽZöYœuFZ«&É)òÙŽ¯%cWÈõe–CEøkØuûÔôWµ<bÙâ‹?ZðCxOf“r m%ùÆ žÀTŸ¿¦ÚšBÜ7CBöUiBEE\D~…[ïB¼ê-OÄ$ÉiE— Ü¼ö—½çFãM†¹^HÅî›ñsnƒ•ƒŠj+¥¼÷ýFæ.9mnË…$<Ϩªï)1AÙYÀÓlÀo†ÁI7ÀE—ãß~ÇnÑëQ}M’DÖî6€'¶Í¥ª-ß Tö:á P>5ޝÆ7SíþO¶¦Éåà®»%îE|¼îÞ@Z¾T鯆dDgÓÍ‚žªZÀÞg÷ä1º¤q”µáTVÖ{ø—r³hadáP�STõ‰G'ç÷+�Ï¥xêÆínoëÜ¢ZŒÇϱ®ªLRŠvYÕÊâBP’mlNÁÿw3D¼KÃJ&Öé£Äá ݤ‘«U¼0Õ’·ÖØÖ½¢ÇÐ{Z@ÔJÝ;?Šöi“È+q`¹Ý'GKwö…îðþ<rBB“å—ãôš¦;Õÿ»§p>¹ÚhNû~’ÿh™¥zQ íºi®žM-÷Ò`©Æ´e[2à>X~ϼ‰Å<çãf÷vxLl,ºf,øN<V«Ä öÅÅ”TäÜÝ­Ó³šÛÀí[tÇ»·©³Æ3Úâ]…©sI´«ÚÝ‚&¯8;ÙŸkuÕ³u&@Ä/Á¹F q„>a‘òÌW ‹8>’G¦6ä/›?vå!Û»�âE]Å‚dª›³-«ã`® ɪú•6ó%cªÑl´yÁüŒºÌ¨!n˜›Œ àå`ÂN1D˜. ‘ý¬ §rèôúÂùê>ÁƒnD„ù—ŠL«Ù}HcÚ¨ÛÖ÷^,ÎD"¦ülÑb˂蠯 QäeýŽ q|³1³JÂ1óm¸19›ŒrÉzYP„‹vIÎÃà’ԠǯËžDzÛýp.¿jܧ�q §P4¶EMŽôâèÁ@C…~¥®Á­÷ëGcéiÍBáøå™álj…ˆôO¬`ˆ—mæÓàlÙm°®²c;,HјpKI(é*œE¥e仕áG»¤áñ3žeùÇÎ(`NsðD $f5¸áTÌ¡H¾á™æÒ7Q}²²·ŠÈ!ÐzA§º•× &— VYÿ8FÚ6‘=azÊrmÑ ¼è—hî¶Å-á™:ap®s‹åÖÀÂþj¼~?X˜˜$è“ pQ¿4O¶¦e4ù}DØÁZ¡Œ¥MhŽ=ÄîIzmÇl†Yàå{Á§Ÿì%àëG»|“Q(߆¢zÇ bTCI2Ìt‰AÇn±½ÜÓSJa÷gŸÅ }«h5òÍù{<ÖÕe ¸å"!�pì¯ FÕ˜-ƲÖã›’”ëlˆÅ®5¯£#8Z^ÕjÏï=mJw¯×{¼Çßû6¬Â¥Q~»†“ qÝÏ×ßÀb/8uxyĉ´³ºÓ„Ô—ÑI1§/Àô×ø§Æ´ÛûÔŒ•Ɖî1îœ{2MáFµBš‡³B¹þæÜ‹„…gåæ°‚LÏ›{,Y±ïE'&!•´¹ JWù4Ø©w‚Äç‡ÈÏi£JËh†+\o‰õ6N¡+ïrqä˜)?¼4ùyzÅÌ–µÂ†zOY±ÎxG=Vån‚k»?Qô®Ä‡}ù&7-õÅæfû%0SlVh@”'_s¬óÁú%†R˜ÓUA Žú„3&;¾pêÜIÍìOpQ™qÔÎÞ€¯›ª8^çXþZ¨ñˆø—k¡è‘¬+Œ >@¨ˆ)_î0¢ðãK4üLÞHc1)íɳv&²”;~®ê«NºÑ'ê}�å’·Ÿò‡¯,D‹šÉsÕÍA+§š9ÀsN^VolTN?“Œ\ЇPH7sÅê‰*¯Šþ6(¯œ˜9_¦Â•›*éšKzÙ÷FÔ'Aä4ˆ¡ }ðá­|9 ’ÁÚb6ñ:úA¾ÿÀ@A�°eëð tà[w?ôX^­ú{E.NÈ^Œ9}S*ÇÍÇAòs\“ìZøæn%=¿P‘Ú¨3Ÿš-8¾(o—ì,‘ÖûswbÈÓûã§B¤ª-¤B›r øív¤©°P|óš‡ç´Ý¨¢qp®¹ ûÞ]d#§D:ãÑÑyWêTˆ)ûoAÿ2›mÿ/®Àj/ng]PPIZ™.¿bñL7-•çûÍÝPÂPŽæ™?õP|ʤ’'ýoE¶|€ÐÐsup>XkẢù¬Üˆ·g<¿È¦Rû _äOÔµuañ·J àêi‹)Ôu’G6Ò–=• G¢7>ê‰òÊçö©Qâ¹Ð;î…�qˆóýl¾0˜ÅAEÈŽ<ïÓÌÃÃ<”•½nz ]1­#ò�O)¬51A9™-‹3ùœS£²­/ó_Qùv¾„1æxõm£½oì˜)ÖOhÃZXˆŒóX¡õo ¿Ø£$ß»=Ž/ÙËWä(É¿˜üÝÉhm3('æ“&Ñ„Q¹ í`ÔíJmË›…Î럀ÓÝw¿Èâ­ý¨”H p“rÞšµÖ@HÕ²ñäg„‚ØcÎ=Îy’P›F&Û“DM]PI|9åÀ>£x´ê”Ùö}}s- ½ã=Šº°LBùJ¼£©;¨sþ-„S,â ·x‰§áI\Ø­ÀMô¼í´WÑ‚0Ùi…ƒBTÚ ³·kiŒ²t¨Ë%4d“˜* H´ìιÎqÀÉ'Nd*Õu¯£4•Ä‹ˆI$Ù•2$,†m –7ýQLŸƒ®CÞ›½¢ JÂ>62IYYÖê—3d�2qƒù d¾jk’ç2ýÏ4¥üñ2Á‹¦§{;=wØŽÕÕå¿C‹3(>rð йN£4°òòp‘˜HFÂëóÐì¦èy#¯‡ó0 !Å´Úð÷ךîÇàXWãìªõó!×w¤;Á`nÅ:ºƒ‘ öí>>‰Y©xøvû\Nlð‰8*OõŽBÛ°ú@9Wl·îmëÁVÓÊ€â“<”\`¡Ð"þäßÉÞDfñIä¢gzŽ¥ šz9ºšO»E/g1«Ë´Ù>xÃ".püïÌ"Å–·Þâe_`´c^À3ªVîœ}üGAß »Fc Ë…„çò&\®um JÜðÖˆ˜W„¶‘]yA“–4Õx³×½äô>n=Àõ"²£f«–¬ü-¼~?]_{%„P‘Ƈ¦$2@lGd^wÜà|J(]%ä_({ îñÀDÐÌ#+ã"“É^¦Jøð¹8è Jv ¬mŤ¼VΘî®ÓÏ‹ ±Àñ—!ðB‘ô8´YÁ³tØ{¿þ¢ñåÚ ÅKh&!{èÂý=Ó¼�TÓ bì ­°Ã™È¶ÏµÉ>!6 Ú®¤<=É€·­1;ÔÊïP€ù6]TK~.ûbuÛ7¨øñ±Ñ� òÃŒWͺÚ×í¾ï}WU_!5ƒ¤®†ëtí‚´m°Á _ТüEÑZr ^‹¥å,Æ™ö”ÓZ§ÝŽé¬à5„Në|<žvFO¡@.ÉÚi^b—\N²ÀuJ1Þ‰ÂÓå‘§'³8íÌ�Ê;·ɣ&«äJB™«2Úá! õÛVÆ,Êãü•!”kf,!ˆnþާÙ<CêìÞh‘`³.šRªc‹íQóc¾üQÚY®§d¦ÌuѵC0W&á_«ågiŠ3´(¯Ò˜d•'ÄüÆôƒ*ÔÙ*ÑgÛõdDö[gÍj€êc¸LsoÐ]ª“L°Ì*×Ùo²¬¤›È2ö§ ÊÓz|§øhežD%š›…–– ]œüÀ~ý³âùph³„7UÈLîÇSñ¹4þSÃ1²¦ÄDã±aܵª2æý …É^¨³k|eeOu-P3òI‹V'féŒOÞõwsUgàõŽãA}ºo'Œ{èœxmZ2<GCµ¼úáKs]i½jrv[šïÕv盩¬¡ÊÄ.M!3dž¢‰ÓñETPå´0<x²Ý¶£JG‹OÀU3‚dTcÅÚ}’qĨ&\—ÓNõeµ ¢ÍŸSfñëã2¡bæKQTÁʲîð!ï$m…ºªÐ|± hòX¾íþXÄecy}¡uþ h¢¬ç‚™ákç}:j)yMP[9›:(× R&}-¬_H.Ö¬äPe= m‡•€—ØtSê½þQõã(«¤ö?(×O¸úî'4(Ö*Êx\ãqÅ€è¹ùSõU·“T)TbµÍ`×n{qùˆõÇäùª QšË¹RqbL|EØŽ|4¯€£b½°eðNÁw�Œ\©ëT—I¤O~‘Œ‘{3÷_ŒûgíÞ‘b ɰa -cŽª(Ws×:ìTnq­‡LX;Zù ,¸aºsR4¼{:V2bùt˜²Ýó£@É‹¾¸˜Ù\>±„ŒŠì¬{¸(hàÆÛàr4Æ<S ˜‹÷¯ïþÚO }pÜ»‚z]4Œ´K3¼÷Öè®M7ë&3ÔÌ þb«D»X’Qï$XÜ·æ-Kh ]ZÎÜë!µHR|è:ž`Œà·Ö —Ÿi0G‰ÚÝxØï¯çÖqØÊÊxy05]ÞYüÉýS ]üoÇôÈØhKZNg¼„ Õ†®¼ˆŠ^&£Èxÿ ¯kêÑa1òŽ@ÛNÖý¿×ÆÙîI-áåTbð¹>G‡#Ì\;¬þU³Rp:À¸‚ÇYñ=fi £á™¦kXØ—È�þ€ôï%£6ô ýx>AÍ€2$¯w-$°ò­ÒW²‡ÄFvŽä tPAmo}Õþ]Ê|ð³FÆÞFÄxŒ}È ¼´;ìœ+•>­’ÓQ˜±Ì»õ#ô½ØFÀ"+ÝP¡ —mгmúÌÿnÎWL¼=°±%}Œ­ÃÊ }ø_Í *Sµ,ê™ÇΪ}h–ÿñ›Œû{7]Ðã`ì‚ tïÿt"z€Ì¹P¯§ÞˆœŸdO-–ÅTéqA¬Ý*Gú—íä“*PŠcÀ÷¢ ZWö³›TO 1޶öÛo”6DˆuÒÌ‹á0<Ë|ÇBÓ´Îãmt9£OhÖ*è_%—ÄI4–œ÷«ø…ú’2~Rd¸õ`5R\E_V÷Ç)kÎc°ðoÖÉ2¢âÞ”¶ºr^ˆ5<H†wi\�”B¦ú)ªÍÃM—c(‚yàÿAoVÜMUEìÚœÇH"ÁF±$C†OA˜Ò¨.½û%Ã<'z8„Ðgå;ýëe¦š<ýC•‘<D„ØÓ¨Ú™ŠÉôxÚ'w™¬PÔª_:UPúçrŠ#ÎÉN^¥F)u& JêDSmÐÛ\SeØä'IEÌ}wñléàŒ:â®_óùå÷Ù R¸µgĺEr Ø8æ°>¶hž$TÓŒËȇ %REúgìV0%‚]sÕ¥f5T7eÖ1a<VS¯6H?ÈüѬ%<󯇫l÷¹Reé^8éÔ4&ºuä;{’…› ˜dX¦’¤€Æí_G ¡ êŠàòÝÝðüAq›Ã4õãßv}¥lrº°lVm)ô»àšE3Ôûß¾ËõúÌ©a—ÎQÎ(ÿ(¡Ê†ÇîG ^~*ê,Å<³¡Œ¸•1úÿ‘14ù†–©>bKèÄ›‡DËUš2ƒµ®‘¹ì¹!ÄÔNÔ÷¼o¿aÜÞ›Z§îÒM´òÞ°\ÿ<"¿†ênjʩoÊòÆÂÙRß“Ñ]…LÌ_ùmÇUN¡úÑ€UÜ€ÞÐ<¦² ð¾m›'qØEÖ´ÉÝ/k¶-¤Þêß­îè.ð¿ƒÞ`fD“‹š‘RôJ¦[Î:Ïy¸pæòãàrÉÒþèUÏê2ØÔðgè¬zóåIv…ú4JþÁpžûLÂÏ‘U¥Nyë³($b¶èu.l×õ.T´ßmà,ã=6·o”\¥Ö7o@þoÁ£ùYȃ­h;a¥Ý`ã> ʦŠàùTˆO^SªŒáÚ5ðIÊ,ÜxGú#Ó~¨8´.™g*¶o¦k‰Q/ò ¯|t Ú›O±«>»RlÉ%ÜT µ|Í`Š0Y¤ˆä¤¬ŠT>!·ŽZQzåBl5y¨Á[/t !pÖ¨¿ž RÚÕ=Æ¢“U5ZGº„Ï\�$Xi±±J¼ñìTÆn¶pçQÃôÍl¡‘ÉJÊAýPIípÞHCqPÔ©«c¡?ÿ ²ÝL€çVäšÃ]ˆXîÌjå?“];;VáÛѺdJ:ÜÌ~‹»¬ÿtd¨)} ébɇn¾äº÷…< ,_®Ø³«åñÂ.îvXS»!¼.©–öA>Éj1ªò¹—µÍaµÿ€Û¬¾«}Ä<éw±dlòÌð,ï­?—û¦íÉwŠÞY3V“ðu*NÆ o ˆO%N‹ç ­ÙªœÖ¿Ö!3!­‹©`ìU Ñ¿ÏsùÜ0»§_üjÚs‰¡Av9]^vc¢B®4}S€#àÿXŠÏmx43ÈwM °sÛ{òÛ·zºKR/ÈHO "ú]_§èvWÚlŒÅ[ÿyÕ.iâS«ZTT„wS �W«ÓBáíyÞÑ™ùê4øeHGNÝ+¡¸ôoëN×6ÎD8ÎùÿÛÛ­™gAK ±»­(Pç^t¡‚⫯D+›\ÁX[¢O4:õÔ¡·cç?Pâ/i‡´”«w:}<Öú_zÉýVM„[®H÷@Tôj„/WÞ‡<øÑÛÀûçPkxR]oÒ›Bδ¿D]ßøùÛDU°õ|@°¶g/ÀYi`:†™ÉæÍÖÊ”U<جƒl‡³Š'‰fŠi`S²Åzí¨ìuN¿ÏÝËää.{³‹vLª7¤9qßäRÃQ™ÔïŒvj:)©õÍqÏÉ:S$p?@ØpÆ=Åæ8l‘ΘF‘yd£NaËš3«Y_ý,uŽ­ö¸¨·âr¢De´ÃƒÿxcÂkÁFub|Ð<m1žç. ¨µÍ½E†©¸å^G\t‹‹ b"¯ÒiZ ÙÜBkƒÒÆ‚œÍb·Å…“¼eÞÒ2 Û^ØÀ:I€68¨~kÛœó  Z•Vzغ«YOÙoŽø�1Q€¸‘}«lªFx%ÔÒ¤â¤+ì}b]R·—RÇùkf…R9[ó/1õ)QNè €€Òí¸ú1&ìWK vâøoRˆ7 ášþ­hIpÜIßò5”¼ÎyфډZ#!¶vå;„ø„« _ÆT'ê,¿pˆÂúerüÏhû¢§ë=Ó÷òíüzõˆ¢°ƒ€©'_m`ž¶Ä#'>ù·oÕOõº’òl`ˆ×ÇÙ ˜ê ä0è¾mù}©´ˆo"¢“ªö| .9t¥IRçRHšö}ØtžÞ—<@Ì‘+Îã7áZܦ.NYu3¸iËò²Í‚mO‡\ž&{¾™˜2°¹�W’Uå6rè:á¨9"tr ]¯´œ a+ÖÕÓqurùóÅ`ðþlÐBGHªHU - ßHñ‘ÂD–@ ÂÈIzé«O/ õ‘+ýx2Ô1ï,-–ʸÿT7cóÙ<˜ÀÙ’Ÿ¶BÒj”³Ii7õ IRù½ÑËû¼øIŠÑ»Äú—§8^@x¹ô;öêRÞr`Ò'ý×ýfÀ©µI4ø|)óE%;.ègmä�õRcq‘A—¼¯Í(#¯„«¬±›Îê>»Žuä_À“Yv àËñR–Þû§ ©/¬í‰åo…Þñ=ÞÙÙ½K¦O‘'[•ÐÒôQÿ%™ÿs¥V®Ù8+B•[ÿ ˜¤1¬ÇíJо€X[ËO3ÆÜ™½ã¦qªwés—3ìÏÐ*dòz îŽ1«µ[ÐÛ’ð­eK5ó]ýÝLï3¶ Ê·Òe +ŒßÐ�½šÆñ–çE‚€'\G˶ýÊà¾=?õÑpƒmæŽ qkê­q“í ƒsÙbaÝh'5 Z ——ßjü!˜=:Љ¼î+-U¥Üà“O: *tIK^Ñyѽ‘'IxêdPJŽûº‰…sIÒmdþˆÝb™aš¦Vê<&-WÛ§ô‹Ê‘öééŸN2‰ˆMŸ¸¼°ð…1HjòH–rÏQu˜4ø^¾Î2 os4#“ÚÍžÃ-]ŒõÎJ%Øe$‘j_;ÿ0wEZ êO@Z”SY¸ IGݸ ÖAœ¹ë¸°Â¤VÚÑï£Èõ/,=][óû•È›¹S¨í6ªÇÍ0É~^ÀšÉ'.wZ£_M@ øäóœ4ë0X0‹V,áW=ŒC^¾ÿ€Žÿ{né‘é´Zçj/’˜÷;uðG¶Ð>’êì’t𙆷Eò1xôÃIŒrEç¨0לÁÙ4„âÔ˜aF`†Ãî°Çø6uq&ÔeP=pË8j÷ï);ˆª¢)h³ÏR_ï ¸iCÿåöú&ÿfÛÝeö÷—ÛÚoðëâû{’¾Þø1ü;ªþ…꿇t`êC7§ R·g@O¯Æ~HÖÿ@Å{@<†cåÊ`*óú¹T9sÚDz£>ñø=š”+˼‡½‘ú¾‚EÈq2.<Ä–ëfÓ›àÉxj¬H­®Ûã4©*õ?·Ï:—½é÷H|bð¼‘NXª˜ ³âÅ“T¥spLúa¾ó êc×ú»²ûÉyjUô5|¬š ZxÂHÌe ËpÉD+ŒÂŒúÛirÑAý¸9D8‘»¤šh@§Cÿct€�Îî—¤E‰pNwFX"íбPã³"ɸ •‡Í…÷ýS”È“zk:xd(àš=ß±®2mÁ1©ZX_ì%!c‹œ?íˆqÑ~ÀEæÂ>É1 7Y÷ÚBêÿ7u\JË 7ðTSR ­”êçMÀj¦›C&s~Q¸ò‚8Œõ~P&ú¸ÿ.:X" ‡5ƒÄH¢*-™ûÒ¤=,óŽs;„ífÌFÕÞSŸHcdA é]D¥ûi�ÇB–«fõ…x¦UltW¯>s(·¤á´’š/x›¯B8‹| Í·[ :ùZ%)®´±V²Mz'q·‹ÿ/@"ð)<&ͯSi‘{– ]{°Óí#x`9GÔZA5YX{Yýcmj¾½SÚâ½�aÐÙ€Ðð_[ù¶P/4´¶k-ïWÝ$juð„ú‹«»mº÷’2˜ÅÙ‡-À@ǽvR ”€H£uR(c­3-³Íälð‘xFÉ’Æ«ö‰;uIDÑÈÌñÅOA"ÿBOk´ñ<ŽxAª_~V~$‘,i2sí|·Ò¸OïÉ40uo‡}DÅCnÝLšpucn+ùFÙJÕï¶…¦°ÃRÙ³»|y¡ì´n\y6Zùªh`½“ni1\تÖÍøÑÙõJüˆà£è£Ðt¶×úÒ¦ ËB["}¥ce¹|³úÅ-¸n>¹0ƒæ¯]GïI™©4oW”&™wM0t%ùÀÑâpÈ,ƒ¸VÔR“çt,„rrIaÖ5«xÌ*:Ø+<JýZZü£ïi‚.h˜HÒ™4;†}0‹,›§Q¼èK”=N9ͨ<dçÅ›é›|*&»Ñ\BW{ddçuXê‹‚›¥Û·§9ÄuÚˆÊÌ¥ô×·¯Ät…ôÑÊ6h0?åoî&Á‰�JaŒ¨E@¥ãoø$³…ýªæt#Éô>Ó©©ñUÜ2z7C†rY‘ª%öý¹™<á5î ªÏWŒj[¤bJç6x½of#]ñ<9ñ8»t�™b´sTÄìîÙ·0Qmæ7~‡ý”'|á!äy­Û rL†·ècÔ>G£dÔòѹƒˆàj‡ ð—m3 Ùb¾¶ÌGd=ú¼¾—p3“î©_« @^¢x˜Iœ•é”ù†hOâ®Èc~1U4G¡„¿ß+>ÖÍŠ™¥9u3-ÜVøcñ«§}ÜÿTÑ¢‹u‰¯]søµ°á%: ¨M‹Ã¢6W|N4û—Î}íúüzÄ úBçÑì·˜Z}vŽ‹®Ä¡]ç Ñ„ú4T-˜ü1Y…$í.pÍ9ð2Â<A|Ýv9Wë>¢CýÈÞ »~Õ¢5�Ž-k_*ljýE!¯\o²âª#0ë–¨p‹y'¬˜Ä ¡/ÂTNuçùÚRê«Ê÷l7´lZËÇ8ƒ£æþL< P˜¾U÷W ‡”]®îí¦ñf‡â^„,r| …·€hc$EÓ;apî_m#ïP7•4ƒè9Õ<(ë_%½"nI¯ÅyŸŒwL“ïÎ(# ›nØÔ§½Ä‡5¨‡æu¡MÃ`˧©kR–ø”ÄîLGƒÝ‹Tí†]?ÜTu˜5Žz�V2cÀ²¢‘/YwöÝË×&6œeߣұ­‚}„ðõ"@•xÕ*ÀŸˆ+¿úÖ|�³VT)²&†p´ŽÍf¡eHÀ¼ R�Z¦OÔ6V7ñ£±„Y}Zª™Ôg€#”n[?Üeýó@{¼8K6öP+výz‰ˆ,]·÷sMò þAUÄäë–åQ£î‘èœLÀL4Áˆ3ð:‡á¤gsÅÁ±AQ°:ç@;Ï,?0cŒ7Uá:7 †E?€¼G.'åjãüз{‚;ÎôjSÄG©#Å%„›§²ŒðT8,*8êSAæ–©rúËbØ?kù#÷¶AíÜ©y{߽慛ÁÔ· )¥›ýy£˜Õµ\#9Gl怖òýs²chÈ=J·6Ž´&ñÌ^—ì¹rîÞŽ¤¸4®<¢áŽ;t4J–„×EX%Å t0<}•ÒœÖ=ñÕ¬úžy´Q|äá;mƒ%™Rš®÷gòÝyµUuÉòç3üÇò¼Â¾¥²J,ÏT¾#ð jzM‘®c2ËDõâ»… >å"3Œ²Xö—^d‡°í;Ë`}\ÔEò•Â…µ·ñâ‡çRùÈìd*ttI¢Bösc]ïÜ·¬¯ß¢}:×½áE .·• OåôêôÆ4·Ñ ±¢Åý7{úûÏcšíû3—žû(H›÷ ºi‚ÄÌáSNœ!–”ßXõUí†`–¢QòLÉ“³*/PÚŠ)zL|±ZÚ£i°kÊšŸìÿšh´,r Ùü¶)ö÷ÎæRnÒM…¨·Á²…goÒÆ^<> ísȯ8(Ï×ʃP$´#`9==9�wBѪËàÞB{B:g€*£‘¡ ~¤µhVoÒkrè]”¯é寺«ŽxdÎ×ø‘÷1Ø^ÒóN«àWÓ~ÞÆÙ.ãÄñîáàrÖ_,Ð'ÞÌ–pNúQcEÆûu6C y~Wg(ü‚Dû‹P*–òSÝÌCšçì¸Þfp%­4_MJ•"Õ]Å3x4ùäÕ ûi!­ùª0¹¥œc9û,س±V ®‹r^‡Kv±‰ï™öóxRÐk²h\™Ø©đ΃¯}‹ð¼Dv “3†o j5Ôý¥É¹Š4*ªg%ˆ_÷ÀýqµÚ¶p‚Ó â >fãþÒôÃÆÌf‚§ó“ðŽÉç/ñ/†èaõ´3&q~ÄÁûAšÕ—Iuz;Þã…_ÚŒîjeõÒ&VÒ¨‰„'?ía›µ'¹œ·‹û"<»š+´‹éÊ:´›#sîRîæà‚cËñJ‡ÃòFŒ!º7àe§<ôžÊ€¢©œlÀ= üâQP>‘9¾6p³öÙÃT¬&ÑR^ä  G~G4JÍIÄP¦Ã¼ŠïË`¾(¯°·B ‡u*ÓˆBC ¬Î, ^5uòÝÆæaÌ×_å&”¦ëƒÚh(^¦ËgeÊ—èZ#} ðPÜIPãÔJ;Ê1 ²þ—¹ˆ€Ÿ¬EìÙŽl¢³l¥Éð¿‘…MT‘€)¥fÿ&¥Ýggïm|ý¹ôÄ‚þÇöwí¹i‘k5²'‰îÑ1#µDÎ-Àæt¦Ý ÜÁÐuåºsø:dœ›në–•B‹!äð1Ë”[¿¹‚ÿZè|s"ÅË DvÎKdRv¨·kFíM.@—¾Æ¸"5rü¢s׈QhºvU}‚øæ¤<ܰ!¤ƒ&¼ñ–Þ¡TÊøh¶YÆíÁEû>˜*ì5¦`¿Ž¹î Ô÷•ékÒLíx¶àÁCb^,M'*äH"ºÌŸÒ¥vnTÓ4ïC'Be4s%%%¬É òJÓd“¢6-Ê÷lİnŒ‘F®:ÆÚn6ìWÅÿ˜w"&PâÚ–© =óÀ1Œ*s.@}ófrŸK ö[&äþ;hâ¸Àyt˜熕MJ ¶zƶÕGTümÌLÛ 9ÝXfîSÁ¨Œk5Ë™éHi‰5ŒAuǦF£Oûߢ7Õ(?óƒ¾îåÜz¹í<oŸÆ÷£?u )Z(«×r¢ž€•ÙÙ5bÈ5º`„|IΠäx»¶á›±gÆ÷8%à‚`8Ð8ÿcH=Bý¾_ŠÙFúŒKt $ Ù)jc:}8Ž©Ø^åqV/ï(ŸüØÝìnÕ,áÎŒ•GlS³Ž%`C˜©)29-üWž5Ã2<W·Z½?¥ð¤ˆ-%³¿/ŠpÛ-ë5™ß«ù—³wðžÞº®AíÈ÷È–¢UçÓRÌRw×`ÞlqHøNƒu§{a[n/œ–,¡KÉg*‚¦ÿK6,郌>AדP yW½"·Ü)äÇm‹æ>‰7¸{<DiMÕ,–´ç³‰qR‡†ßž‚ù8y©:²ðicÁý*‰4ÊlV•Z8GzòÜ÷(ˆ¹Tà ÎõíD¿Šµúú²6ÖvÞYvrc«á*ˆ|8ÄhV~góš3ëì;~Â8Ô;`¥1Þs›€VVªÎ-}ëÌÃe}èV@jaíäôOvr· îSÔs[‰h×–ŽM�MãróO·ß·$wÑ©°—$‰;yj2 H2 -U•xù÷–àÆ¶&JÓÌù3S,¥†çï{ßß¿ºÜç¥ â¿-§XE¿Ðõ8RñÀ;^ôÙpHv^¼ß}ýªCón)GÞæë1møàA> 5ÍuÇÊÊ-«~f‡ä}©Œ Šä~!âUsèrTõ>ÛÁX_é`%×of3ÉbNÂ>¹(ÿȾ! ЦyY¼†%B°&ô ïLÐóÞÕ½îòšúî¦5Ë~ì¼±_Ñ@l©€%ÁsÜÈI™e´T‰­«ó~Êi£ãöJ+ÿT¦à_¼oÙV ÏOR$1‡½ôÁÇú&J®éá]kÅŸ™XxCàîFˆ?4ÏyKú€ÃfSƒ³ëy”gHó jäá?ýrÂg½^ÃͰÜ6è>Ér@Pš [æ%£Wn>G¾wy‰mgü¢âW»HlÒµh¯\&»èO³zzf˜ ÈÄ©á)=…ïŒiHÆì6:ý–¾?îÍúÑòÁ<À!2¿â>å™¶Ñ›=ƒFÊÙ‰³hÏz·Ïn6<¬ˆz&(‘4ÈWò¯Ø6›–ýLÑ~ÿ<ŒDÍ5Iy|Ö,F™[|Í»ŠQ¼7I¾ôäÓ ©ûCdÏÁ(š¬ç èœ4M|ÄÅ'§…ì•ªé¸ ~v…rùÑvd8(Ç7ªîÔStÜ-a«¯ØÁª–r&`Æhÿ-8Œ Í§”™°ø‘ß±#ð*rï§±2¬¡‘óòC01<ë}­ðs«S¶Ù¹ú¥H÷ôõ”†Águ–o‹(Š÷ýÿvµÏî7óMxòø§‹±ªRúBÌž—m€øö0ã º,®ç_ƒ$<$ËGÚneOY¹×ÎtÐÖ±½ü’‹±6¦sÓøó~Ëéæ"n[2Ï+>œâ ÆßÒ¶—z}å"ù®Wa#¹)’RÛÝtð�hxÚbH£’¯¿çî­ECO)Do+·/Þ¥÷O߯ê‚­ÆŽ·ÃMT1ê>¤*Lw©¹Œí9^þ‰;ï#Ì=-~úübcƒÎ™ÈÜ —wÏà8°Z[^àö—CŒHò"2O’+Jñ»`ˆBuî)BÚe2'¹†† äÞ»¸Kø½\ÒÉåÞÝ÷ðŽ0ôúãøð»ê¹ãRÚì;ù"·¢|dfüëS¢Vê(17ñœFc`$œ|±t"›ù……cV/<‡tšïÐ4L=\6Ó\°CxeÓxWoÁfòçg„¯Þ™ J ß·u­{‘Â|îÁ­ú‘Á3´!‡èfèRˆÒ 6"|>÷üÔ„ÉÚ†¸¥ódõ«b%˜¹ÍAz«r×cüåw Ô<ö 4€»þöSßlßYaà”ÃPT‚m¦<‹¨ýdC;&*8Ý¥]…ÁÊUéb§oñ÷©3°¤——.þ™eTT¤°žùž®Bè¿ü…ØF׆™ùJdã~K«Ì�rZ®Y;dìµ%[ñÚÏóšâÄ®Õ m¾£`iÚªC¬T)è%:ÓúHÂÒͺ#HŽÉzv…Ê Û.lò¹üÚ^`æ…3k%ÖIh²NÌÖ·þDÏ^Ÿ],èU ~ò3Ûõ~[ÇÂ.½{¾àðÔÇh•ó%cªÑl´yÁüŠá½÷›œ~íÃS3~¡ýÊêËé¶kz>ÞzP,s Ô@¤õõ˜í«hyÛËm¸<3ˆŒóUØ$_ú+úÚ›fÙ’üÔÙhgLîO­Â¯Wi\M¤îuŠ NÀŒóÜ%(,¡"n£HJ ÍÇåÖ-ÏÜë3y{*×'ˆõo¡rè…è lúYt¶†õw$´‘u¯õð8E®?³èž/Nu!{søo¨�)œ1Å©}ÎoŽdÜ9qŽÿ„â[ÐØHóæªxê§™, ?¤‹L|·I¥.Å9Ñú³wëÚïõZJ™³‹r ¯Ù{zP~°­{¾ˆ›\´-ÊRA¤Ïú¿2…µ…áïüámµþûe÷é²oèö³<Z{UÉÒ¢J dÀ<BŒÕiü Cœä˜íµ\\IN_|Œ†—¯Å%OÁ8�{5äš”Ñnj-µÖ~9°ã+ úÿ~H3(K£L° ºJ¼Í3Ï…ûê[«V,“‰ê™§DÍowžä$Úi˜ 06J lU´4ÔÚ4¦J9ÞÉq,ª¶&yµ´êv§©a’ Õ_ek_#%BY¼&–Í�ÌìB9çØ•ñ±¾¬fš}t˜¥•ŠØý«HO‰³J!%»\ 7´ÖþT|]g³GÓLñ?2Éï;:ä„߉fÝÆv…ßšk¯70ãõ¹k°žô%HãSªIî{0Õb‡ð)op[qÁUžHd1ðpü˜9·î´™³Ÿ$JŒ–£´AŽLãÀßÓÁÑ€ì)!×òØŠèVHr¸ï‘®�€Üõ#”8ñ®¥~ºuõÑ/ä‡G R6k4%8WŽúWÎø6ê|\¢ û”ôË<VÏ^*·ÃÁMH/«‰è6#þ+P=‰IY,fIƒíï¤òåŒcýGk•ÝÑ´žtõ|üÓ8ÒlÕõ˜£s_Íð )¼K š]¤ L‚çó€2 eL>ïýÝéÉ~.W·}÷e瘿GÂŽ]Ú=´jnÃêÐÂ-ïtÀ¢&mK¹/³ ÷Â[¾yhØuñ(ï{”ôª £œú¾¢áÒÅ •ÿ3òëHïÌGÄ&åÿªeÐDÅ0"ð’‰Ëq,f=å_ÅœwQ*¡í4ÇIóg¼¦L"T L5îÛ zŠö³ÈØAß+Lzù;DÙGË‘95hn<†¿÷Ÿèç:5Ÿèg¢ÇóÁÞíº¿þäZµ‹ÔEíãíäjô-,„¬nÞ¸Ígβ Ù45ñï&c÷ˆ™²rE¿iï áÕ¡ôSÒßA„ö_‚f_y¡¢ŸG>| ÒÁÜŽæç2Ü •=)ÓÙèúïʸŸùK#ÆG÷Ö `Ùj0EÎÄè’Žgˆk&#B¬#FÛ´æ%AX]3%ŽÛÄBÆ¡:æº[ãNjÜp`¬p…߈ýJ Ò�âÁ†Aãc©»(Iƒ¹N»ü“KS̬ÊZÏë°6©t\ °ëcNv»µýØ:gø¡tl±P#àYèZËÕÎN–\j=ÖÈ8ÆvÞ!²¦?Ù3‰]üL¶£ÒU'[©EÞ)Ú» bôZpâð „/ÜÌÙòÓ¹Ñ ô7v|95r.+gÏ”¬DÇãÙM=/kLbÏ Å±YôÖ.kÍ;q¿{áHµMªï¨IãC€ÅµGBÁúÔ¦ôÍ´vl)°eÛ•%`Ÿêåó¿!Ùêá‰tdÃêÅ%±íd¬!KÆ'6Vo‘¿x  ÖBÒ/ä‰üëÝ¥-S ^bÿ|…_‡flAz:Þ˜ØKœ#­ža$ë«” ´ÇÏú[ðúM?× y(ž®Ê˜Îe=îRÊ•o-}Ö¥Õs¶Îq¸ 6< NŽ5%sî¾÷Š¿1Põ–)WɃD>¢º³tvê9ÁkØÔ35§G7 ¦Ûöe¨ŒÂÜtð4%Æ=dÂ1e>¯¼‰VW£ãø«¡ Çej—Û¸n¼n¨h/åVa^¤L÷Þ«?ׂh~T‡ Ûåxu$ØÞ=£¿1Ja݃’ˆN-—ßÜU÷6 ªì`^ãX¦ã¿©—ÌAïÍüs~³Êex.ÄËo—¯”�`pÞÔíWä¶^œ±iÕŒ1tn­j”Ü–“ZØ¥N‰h8åm ‡_›){øR¦œ±úWð Wp[XaL ´¹/:™¬'> C‹3(>rðkäcpØû<¶ÒÏlÎ}r åÝ™ù($ÍœY0MŸ8'Ö´ìœ8*[3(¸y]OÙOÂXéÈ\÷Œ È`ìì5•jŠ(ú{„ÿ Ž•ÆÛšµ7¶výu¨9‰´4C&ʤñ£NÜØÒÏ.Δޭmý­@Ìè#Î|‡CNžm|AÓV[Ãüðé/ 'Ô¾Œ§1ñÕ TÜ`ÉT&צ‰MàO±–F±ÂÞ‰gÁŠUr£ãÈ”©1 ã1ˆBÓqÐHœÔ—¢½†”š6`Ü�íÎ6àW—&wgŸ%Ýf”»R½¥½ ˜T,0s^“'uë«NX<]sY†I_ú¤;(D:d½ÈE_;}c™Ä7†‹à¢ ñ’¨Û?¦­­õÒÙ °ƒT°˜k3¸‹wê<šå´®„¡ ^M¡ ”]q¨Av9P[³WêV&'Ëâ”Â>¤·–¼Áì^<-r0k(´7#å½ïþÑs#(ªÔñe…@à{HëM.h\¢QòzYò$™© ²^Úä6íh=ÌìA­kôX¹Å@m7Éö/þ„8®x,>ß­Ha?Ûƒ4B€ÊÑ£Al±ñ»R±73–ç„nÒ}”Wrî˜[z/}):Oèiûw·gͬX¦`ÐÛQì{É\9îã/M`B¶]�‚ Yú¶P,¬•UÃGkÍa‘°(MÜã%å¼ë\£´È õºM“ùbRÖ ¥Ò%çÁoxŒÆ£Y¶j‚eÞõ%|ºfÞpŸ ">4;U)o�)x;Ü6ÝϰòEXž}Æ‘·Hä)ê-)ê‹¡V¢JÝiRS? ùFI]|2q°jŸxtôrétDou7v† “<…óèáë[,³<+aDkžgb›šŽ«ÑàP@ÕvF›qC¾Lís<¡•°d5HAÞ _Y= q—{!,$¸X=‘Ró¾Uñšn�­Ü‡[aW—ÄOåÉd»NX{¨Á_6h¨=}} PGF ´.¢§jÙ$ú±-…#˾ eØ—«Ÿb±«HùR&üË’*&TÝãýwžv÷MSc‹ L‡Sþu‹�„[Ålž 'ŽsK7î–èØ=WÚ<IÁÏvÎÀUÞÏ,*H.Ì–!™³ÐäŽÛANÒm¸ 2ý$ÙWë#KÇшøâ¡zh âŽSà=xaÕóð –i˜$d.YÇQÔ3·6 “©“P±‰0—°Gç_ì•=óöÑÉtª·Ž¦i7­¯Ú)j=ÝIûÄQ_àâÆEåõ² €9µGÂw¯æK,l;Î$ùíe=­ÿ$E3þpTˆé¶­Ýoþ{ �×¾¶­À™¨Ð.ÔŸ@- Do9†Çõ ]ÅJ¾WâʯK < G¿^^è; #SºÁ/y¼{éòQ£ßÍ•ÿgž=¾’wÏ:¯ÛîÝY„&ægª^ÍoOÇ¥¬EHknÃÒæñlÛšÛfáfA¾iq²ü‰Õ؇EI‹k²!uS÷q'»cwkNŸOÅX‡íõ¿·ˆBøæ¶-÷.ǪíßùÜ?ZzËh„ð~waÄnw®I¶!ÀçÀÆ¥s_Œà½w×'  %ðe#~Øï§ÃÚ$Bp÷ý·ë–âã§@ó»=íÿAÎZ–ºc€»C¦ªà*M©QAKà§Ñ tl¤5| “#×Þö¶,«:K˜žõCH دՑUv¥ >ûwó†±U XuµAr¬ˆ+¾‹Â[çaRI¿Ûʱ¬ÚµºØ8¹hô¹JYG¸õzeÜåO#Y Æ:ÛÕ$2ühË¿;Ï/#Bä£x¶Jø¤îª–ÁKÚÚ6¼‹´]w°<9!*„ÇÄJ¢ô1©V €±ÅUÄ(wF ÝŽ)ɇ!'¼é¬]øt`ã¹Ðþëd¨~©;Zf}uUöÇ¥´!Ý‚Bk»†ÔwÙÝQàUÊi–~ O}ð"®0ÄK-$£asœ Í.'v\¼Å®Mhf1M¼<,¢†§‹|3Φ˜ ‹Û¿ÑCáj5Ërxìâ¹_ü't4"º¿Ñ¾i> ù¡ »(o-éž µÒ±Ï~RÙ†ñ‡J[÷f¾Ä®@¨|¼´|Æ>‹lŒE4+ï=>Zâ}÷w”ìŠÀž£;€ˆšt’-±5,;͇:ÜÒ¡–íaù ‡,uÔ€ýÞªE­œ ·‘Ç~_Õß¶ª‰ª×ÛdÂBÐ!DëºVSün¢\Ñ1*ÌÓˆ—Ù ¥Õò"‘“ä’ÛY9ƒM­+ð•`ôûCÿˆÓ;)ëh4óˆìÉá ed~®oR‘‹úlÐÉæ ô÷ ‰ÊÉ’sZO,¾ìØ^CIªþ~¦Z“y×çÙ§©t•¼º˜åi Cù• sFD BÉ„„úȲö§+&Ìi½ê9øÔW²‡›1ÇѾ¿ù¡òª×vÖÆLbÈnÍlíóïf|õ×þb ¼›hŠO"ö"6Tö!ÆËH•W‡Ñ2ö½M;j¡‹¡­ÄÈà°{`¿ðoÖÉ2¢âÞ$¸j5›tƒåU2…û…MäHeZáaÉŠ#_æb¾Ìô-®Èx†rÞe iÝp öçÚ·\ ƒ[•'«wó~z]1m=CaÙëRÐc.Öª»ô1@WͰ‡ Å8q÷`âÜ×5!u¨»,t£ú8ïFÀËDklxâõéžQÎ|d¯t1, µŒpø»ƒö7¬˜º¸šµéú3ŸÎ“Xs`Áœ÷¢ Ž Æ(>Îí¶ÈVî#DÑx`†IY@y턤×ÊgGcòðµVÌQ7øT çð¶«‘æ¢i8¦›@LJÖf¼~ÂÐþYq"ĬUÇ„ˆÔSïªWJÒPr üŒ”&=3(®,¥™Ï(LbãómN¬§UšÖ¡m–‹pÛwjQ­¿â‡¤ªm2É+gs/NT¯qË/ÆXB /´.Êäœ>„ÇùÝcvR–¤¥dPóúƒ2 -ÿeý ±™V¾_- ™UìMHEï@·;yØDx‘…,= &‘læVÕ%xß4¡¤õ¶Ü9>!¢z&± jm̓Ôxp[êÂXO‘”}ÚPkÒgV"ÞÑ/²N.”BðŽÑlI�C�Ï?<E*±ƒµ¶‡V˜Z …¾aÔàRÎÙæàca0 IF V ­Ë­‚ÄY, Ó4¨%HÕº  õ=lþ\Ó88–¼\%×ì/$°ÃCž´ÿ ÿn/†á)}Ò‰/�ÜòS€pÐ ™°|ÝÔ{»Ï‚>A³7Ò)Þ± �¦·£‡žO"àÓfIL‘4ª†xeHKäúµx€*§¿íÌjî½D²`K 7N^sQ²í¸Lr¶cUúùɆV›rUf²çjy)¬„£W®™-J1)wù€„÷Ò.ŽyY’7İá›=䊴G~ö¶-µ¤Y%x+9›ið“Å0íäC̘6+¦]‚;Wî›å`þö¹âœÞ4šúQÊ"ùE®ƒŒº ¨ˆóMB˜ð-PÁ®@öõ¶7Øå³â\A±žŸ¬BZú  <Ìrò}â3%ÔÇïî¯öL‚!ÔŽçbéºVêèòÁƒ£Â!]BÔžPz¯PÝð)S §èg’G´3$¹×²p` Ûmr XŽm—[2YWØŒFòªR½r'fˆÿ2ïh$7µû?À¸ó3x²9¸ ~/`ÐZ©‡ø¤«:'%ëÅ(ÊÍrz,j{}̳‚´¯ê§ÕÍrNxYoÏ¥Ì-c{…vf†Mȶp¬ÕhIç"E¤˜JŠÈAê„ÏXŸ 7³64E6ñ·ï…Æ-¸àäеX=ø¯d ”FçS`²‘¸—½Öµõ.C‰Kô8j vÚsà¶,%>:±ZéúåÚÏ÷ ºÀ°‰Ðݯ©èU—V Ýoñ„Éä{Uæÿ ñµÆ0aÒ`dO‡ÿxì/ˆ1—üÌ›~ϻ푿{æ³X$ »Âçö”Èúûö>8ÆS-<åÙDòû>îfœG¸à 7šz®êZ»ü»á©¯›ª„F}tÚj´B‰ö§ :|¢ÍŒ «°I°ž -O¾QSïg€ÕÂŒ¹£¶1Ž »ä�ö8Kîxñ“Ê¿å$5ñN€` |¡¯AsøÈÆÎõî” \Œ(pA‰±ŒM© S߉wTõ¾¤¬Òþòà›Ú‚l MÑ^qçI§¼kÈöØõ­£G*€ÿi¶Ï²J“ŒˆÌžW“ñì<±(vkÂzÔ.ÇÚ¬•†ø²¼K9£k²ν³›¥¹•’áŒBCp#I¨_Ðc-.B°äß)N©.±EQ žËßá·ÁlyfÂ}<„&XÏ‹Ûfª[àI§P Ÿš KÕ•%ÍùÓt™æÔéþÂP”×´‡’_Úwøf1kaA­_´ÞS—b²°•é“çs–Ä©[x� Ïì‚ù6ûnO{¯…ª˜Õþ*&Èô˜ÓÉ0¼"ãÔ:öÜܯæzüv‚äf}cI#VJDP­hý,[ãõÔs–$¹3�¸ÒõîÎÊ£FÙ~@Tìôd\•`ã~®Ú| =»JÍë¨mVÍšE¬÷hÝ•BtŸÛ†¤}H[º‡å4ûÆòvo!˜uÔôDÑwYÖîïHþFZŸ¸'Ž·½p°ûùUÜ8‡^ ý—Ÿl§¹Wá¯)  åêB‰cžùXÜyBÊû§³3a£›t¹æ¾íŒA»Î¡;ûu À ?¯f¨4áâZ««ÝBùŸVÞÆT@µÖÞofO“|,!“ ?îãΠÿv9~ÇH±!¶(‘Hmo²n@ÓâîÚ+‘ËÂõ€«þqÍ6E7Åø«ih ûâj¨o–‹Ì¤_ŒMx/Y zŠ Ø®0fœá½²þÌ€ós»n!ü>^ÄÎÐWk‡gu¯±‰@Ú$zšvÎjL[9 t#Gâü]Ç;xZ°òòDÁ“rª‰ž7ÇÇS®)°»ŠOA–°c:d]p^ŒR’íÑ}àåÔÔ]qr¢’^‹ª xÞ±)©£Ã¿\ŸÑþ7o“¦ÁþqXÆ�’iÑãln6• I1f²›ü…s¼ÓSÜbX°Ê"hë²ÈÛ´à ­ßIcP"Y€t.xyLUVXÀˆFhÚJt-*ÿ)ž\V’Ñkw¯ÝÁš1¦²×#a‘Ó¤Q.I‚ÕØóbPÊ.£ƒ×Î&gï–×\Ú »#ü|˜5X`œÖí‡àßS€úOÊ*m1£(ò�àzø±_öá‹>u=»¿\ïÀ»÷ñÅi¨"ÌÏ1ÙP °jÔgF‹©>X—î5Ø”yµÕìµùrWÑAÛâ‹Zå^rnè’ ˜ –ó$–ËaÓhÐò÷¿%w¡ÙaœL»Ð*ÙßTé¹Uo¹ÁÁžêM‰)lü#ÏbÅ‚às%û±aÅä—(WÒ ÷` Ï –„%CãþzsxU—£h@z‡HE¯!Š÷&øî¥êr(¡Tm¯òò‰è¸Îªñ7‘e«&Aè„p±–kÝ«Q#ÏÇú™’P?,ÝHã÷OŠ—iç"¢ö¤1Ÿ-Ùâoã#7»¥3 -Ò6býðgUE€HbVñÆ~û€[ô‹tÚh÷‚92Áõ½½‰ˆÆ M«I3Ä‹EhÖªÖ, ü!F=a[‰d2€>ˉfÿ˜ÒñÃC,¦¡¥`øYvqOÐÉä´UBqÓf’U·7‘q;dŸsWhß”K4k¼ßÉEXožT¾)¬ðâÙþÆΉ CcÔkBúãa¤£ÄJ¦É42Nªõ°m6ÒÉÝÜ;£¹Jï…w,îÑ [Ræ\ê�cöåH—Î7Íg†Ø}ì·¢ç1З´rÒ:«_¸G¤Êå,©¨* ÿ”·l¶|P…‹Çp_ÑÊèÙAv©œXôåå uUÉÎø—]k5týKåD$€ÙÓŒ’^{EuR*�pÑU1Ó–™vì_çðôh¾Þ¿¿‡k·ÛÕv?ol¿·¬/ÛÚOíëKüëíë_øvã}½]€ø|0óLëÖáV6eö’í ü—&$Ù„MÖŒ² &äȯõNLJÍkÔp.¡¾¹mÙ½"�ËËÑ0³„€£2gÐ~J>…ÄB²€s:<º<KÒ¢)j(¥äB“.V8œîòÛv¤éãòJ• \¥¹sêYëa ·ÉÁþ˜{Æ¢}Ö¾˜ž RŠ@̳󵿒’ÁFœ…†# •£þ©ïé+ ¬~{‹)¤ÕÉE45!Øõ¾~³s!Dð|”Æ!ªF!îÚnâÚwUtHŽ`’u>î•bUg-ÇÇ#b øã~ãÜ;f5 Tâ}‹\6nq»Ø‹±)Ê€:PÃ6¦ïajЍgãzgV5B÷(ñT °Ðç'±‘IJÇÑ)/áùðmÄAëF®ÜPžL~Óû©Ùåµbž ‹Ÿ~‘P“§‚Á‹ yVjmíl Çš1ÝãWÁ¶3ËJxZÛ6ÿ”ÊE²5’‡ØµA\ze &NÉƬ¿¬ÍªnCÚmË¿MdÆPƨ‹´“¸{©$”xH-w(×D}EuËj»7x5l«Ž[ T=Ö‡8I:õp\Ž®^2�¤•§Z˱Ä:^³—› cÞ¥Q³²'£f­ÚÛàD â_˜`‘°—ö¥¿žtù…F‹ u2É»€|¶oTHNà3RòXÿl–Ÿr…†RRʪZDÙÇYË5fMïNt‰ªÂ1�‚ýL Ô§åþ,áÆ¿ÏÑ?²ôKuù«ß=’«ZBG¼æRz0=Ü€V~S*Ÿ Òñ‹{Ÿ’HÌ 8 Aª"Ý>3^ÓÚÄ  æ¸R åÚ”ÍOTÅü�ÑaÕ_<‰ñÝ  û04u»t™iVTjB¢çià[/dì±¥åeÛM­÷3Ö'Û×Ñà b¼ß£Œé>³ÜeÇ-|tQ+„ $É’NºÊä'%òÂQνl%ÝëJûÝV„›KºD¾BòùË­9ü‡šÑ4ʰ:ÈóÏ8$úÊ^â¼~÷žß¶”ÇÙÞ0ŒY&O¨“ßÚa*[vE´Êàë”î´cŸù9WXI¹ô5)V­5h§\Õ|®Ý´•Ä#ògÖ…b.×ïã<¥ÆfÉ´›„ZÇ‚º²W«£ pEÛ ¡Í@õ°ýÑÏõ{N¬åabŠ ªqNáÖC:ìE7¸ÒBk¾®œºÃèÞ92T)¢ õòöYSŸ€×4µŠö{‹Ò&FDZ5C•!ŸK N”ݑ7‚?Ý–-QFHi¸ïó|̾©ƒ`iÕz h–Bª$¤×6È«WihB¤~CÐÉÐ`ÙF8bªçIeê'³I%=ÚGDî„>Ôâ‡4·ðQ¾_`Ûà{.³J 8ûÒ––[¦XR(v‰ iàÙ-=ƒZ'ŸoÊ àÜÑJP9º-hù¹šdN0kO'83_íf€€Ø¤r‡.«5d¹ÿý|7o®ÄQ*ÿðÒ ÝoÅ>Õ'tÀ° £“¼R¢¦ÀûqïÆ´8y]¸tóî°0.¦­Ï‰a/ó´†CbTó©í²Ÿ‘·T8 U~V\úR®�×GÙ,¯ÎŸÞèU®±I¨öèƒ÷iŽ¥RË´Ò]Ά •xŸ›¤ÿËyUÒÕ¹ Lëê (©Û†yÈ¡êù–µ„ O9›5ÈË aYÒ…Õ&ˆX8i”´˜Ùò›³;ˆÌŸ³nA2ƒ×s–ãéf|È ø«=ãíµWh†öûÈ—ÿ#ÄÞäxÖN^ËfbSkþ«:XÁBÀB Rë¦{ yw?C1x?£| ühš^»^–º;É€±µ^Ëš]­b@î*¬ÂX¯"Ð>t™ñMÇSL¬Þyÿ&çUêtêz£ÓVth-̶ŒyýùÄ8çH3¥1]Y>솶âI{ü;Õ&6öD—/9¼'†¥Gy’šP •wÁʈ°±1v, dt­îxº ¦¿wΦ•‰çUßüMö÷\ð—§ó1¥~#ÝïÉÏü1žË‡µˆ4šô±ßÀ¢ÎÃø®ý beók¼Ôæ5ó–¥Yk°eSfçK×pj6í“á˜'û¾:Gl"Kæ_È*zÃXYVa/=ÇÆØú—jy.Wí“u~z­ ‘û$ßR­cÿÓžS$ˆÕÀ„¹L°ú&75¬H›Ý¬§&ˆ´Oèc7:¸šyîÍ]”"j€²8·õ.ÀíÙöZ ö…žÌˆçõ‹ò Muÿy^úGQ-GÊŸNjK†Í(Vž0`:χn¹™;›„ñŠ']6ˆ1æ&ýåýcó°Fv4¾™Â^b±‰/gWìc,Ÿ1q.·ÍÔZ¼ÃÓÇóýåÖ1bUÁÌx¦-jˆ2ff`íµ¡±ŒÇ_׫å ; -oj”…ݲ66#â@5áx{Øe…Øü^á ¸é‡«*¾âí MÑ›äSÓ_'ÀTÆ.tAóbÌK ÐÌc5l\•R{䷿űhÒ ´í¤—Ò’`êib‡å“KÙ3Š4>ƇáÊ"øe´e¼(wëÒ±tŒ9‹£Ða¯(–-{âç“徚Š9%Ÿº˜{ØøLü÷ÖÒÝ]×ÂÊîÊ�„ïÜb úJ8kì¡SE +ãÝœB0ËN~AzÉŽsVu9°–©!ó2(FÁ5¸ôËu‰±5ÎÜ ®yç:ë6¸j!áóO©ð?œ€¨,J ô…⟄Sro"ÅwM²ªKgÁ¾ ÃÅúÊô04Þûßü«¯#£<Jb’Lq×ÐxÅ·¬c ¨Ô_î‰ùI¤¸kï~¤YÎ4DóY‹+yXx,¦žÎB°µD‡d¶×6»öŽ’‘°âRÀâ�ÚÇO)矌¿üiôÓö-…[B-´v¶ò7K&“ò9ú ¦ñU¹A\Ù5.³×~$Í\”ŒiØzAbß\n êí–Ö;7¸Æ’MOAS2uÛf"!ÀFõÚS-tÝòuƒª=¡e]1Ÿ�7‡ÛAmÞûþr$öš!Ï÷lT€.,Ç"»-ñóR‡U™6[š¿Õ(ÃÕãØJšKŸ<~Š%²Œ¿Ž±‚AF§}~Ô'A<Ïêg˼ƒ]Q£(ÅÏMà,=G÷œD1„’¿‘§j:˜-ÍšGiœt@äRô ò¨< }w†£­LKà)8/—¬{ámÁ`Ä×2ÛRÇ­Ö >Ý‘Ï?kž (ØXá–‰óž&S–‰mF¶Çö6ì>ép§I…ê=z÷i€%­MßKsdÜ ¸¦Ú®`ü*T"¤Ï,„½e'¥rˆC Xi.¼ :%¼×oÚñ§²b†û<í¿—Ÿz9>BDär�-N)@[ÌòjN'ÓåÊ,Cú½º©Ê  °çq=À÷[ü"U6…vä‡úa.¼Öi5ÐSG™ÕNìY¾*xÀpàÍ ¨Ž[M.é&Nõü?§‘S„ÔÆÝS¿·€<6Z êDûޝH ²`¿Þiˆ¢ô‚«ÿgŠ˜åŠÒô=GƒïæuG+鈅ù Z«CñVhl0`“Ÿìµ¤¨ø ST…¼dÓòO*ò¿ÄPµ¿«ÆE蘤6$5=¥ÐùS­ ÿ{Ê8±©(Þ”¦´€Œ!m88€Bå ù7ØmÏ\ŸÖ•.=þÕU,׆Qs»ö 6‚d¦¬N˜€ÿ/H½øÉ'™˜ÖÚ”¿€ó΂åi_KÂ6¶Tz!gÇ¿‰[g]FDC¾‘BM6´PQ‡Ë!+îÓòîyÖ3ÁÈW±Ám¦ Ý2ÌI>ÐäR5®$iiËE0:%dBGó—ïNfÊ¿â\èÅÖ$ÙcçÝþ=çs‹ƒ÷Ú Yøû¶YšK3t‡Zà°˜r•±GµB´!]€§ÖYnZ2©Ÿ±§ó‘1*×s¦=u4Iâv“ëæóOÙÅõü]W€Öýã©@1ý)ðEâöù‘·Ï[ $ CoorúÇÔl1c÷OÌúW%5tâG•ô(!G$…õà"©XàááÏ~“‡—„OÑ«ô¿–~â·(Ñvsâ-èA€6ÊÉEÆ�ìð£ï¯]‹Ã<ùÇ£-YDIQ˜a“½8±îLqXÍì”`ûŽÔîˆn 1ò%çíb‰Î˜÷êîÖ;oµÓWn³¸ èžPÛŠÇ’ÐzÓÄX>ô ŒÁw‚CpY”_ U¢~èc!Vºc¡/_y‰‹Ê.e/æð ·¾ê" õÏ3û}(:…Côt´H¨LG”™ß&&y/Š6byÃóG9÷—A+añ¦^ÕTÙ]�£ñE ŸFC—Îïl[ÀMÌ'Ý¿2ß ª/9U¶ËKWªõöPíéÆa¨â#“Q–Ôpb0«ÎY4Ô4j¬Gi(Šª«š€ É_!3fÃðsÕQH5Ö77CØ̧»ºK,Ãs1Bª÷µ:Ä1þ�gðì-û¥ÚP5_…ªÕêý ‹“µ¥#DÖõKØ@¼i戤øIaK‚ $@€-J¯ÑÇÕaÅ‘ ´Œ6£Î ‹nBz–¥ATáúˆ³„@2–C´Ö”—X}‰…•€Ÿ‰M`ÃÆìÎ1Zd¸zpHýêB‡‡FΊ^zQ`MW6»$às_g05¹XþH¿wA'ðcP|¤ÇnµÌžT¹ÔÛªÁ?ªz}ö°Õ·ÂÜüt7Crh@‡SC¬góÒN–„-oáb+±¬Ò ýà¯*L#žÆõˆ4ŠK+,$ªi-.’гÚõÇ´á]¾QNûÅ×Wq¸ÖÃïö,•Eå#ö­-dôò/]1C ƒ àN<ØòÛø€Áþ“rJéE8yû8!>nΩß¶9õýv&·#!ï~ †l¤”Éœo(GØÀ,|ãP`0þéD…õ›&õ^ýgÏh¢|-z{ªïiÏÍÞÜ!Ò0mSøX1ç¬_”Xˆ'qˆÝ/Ø…™œ£Â*hÿ#™jÇr~±íP©uI#u‘Øp•öìŠW².Ew7¬7ͧVx›à2)¬%¼šM9ê› ^iPffÓ>˜‘©‡0eØqò»<™{ÿ3º“?hºuty<0†QV@xÅó–· ýzG ‚–øœ:»zøf]?bÁ´oî`Ómm‚¤™é é(U½jIÕÇ^tËMÑéÆº[™¤¾ÑØd�ÕVeo¯År“ÿ“ë©´ºÓ½aä§Bï›}„ŽAVÐTX¤ÅãÝ?4h¼CÐÒºiçM#[(þ7?à€9ÜZ QÖƒ3ÿU ¸Pñ²«Ü':A½À>õ"žþ‹pxæI²¹µôBk? ëàøGðRPÍ·áoéoiÄ¥“X¥AE½c„¬g9choä ^B{ªIë`ï¢*¦Ãó^£ºðú÷’übb¿ÑŒ—n‰Æ"EMÕ'Å“î–ñÉÇ’ê%rók’¾Y¼•We Úo6¿ºï˜ïPVíõQ›î¸ˆ¶w!iŒ²|1SbÇöñtƒù&ÑGË=î‘ÝñA�uÍB—’n%À ¤þµçÂXm³¸Íœ\ô'¦ªIüúË�gŒhÿ6?^îÄ@}G)3K73¦ žÈ/,è^j§ßÚ4¦¼~$d;WÃ;3; :ð~‡"ieóJ‚z‰¢!˜|²&½4ÿ~¨Ð²½ø¢/n_ûc ÈeÕ#œ6Ø’5फHŒj„ZžmÙ×tÝê"<ß‘ObQ~C¿é;¥š-Àÿÿm@s¥¿¬ Q £¡ó2ä<z«øØ_¶5w¶ÔDȲOû>l¼gѳÆop?ÔæHD糞pý¦Ý܈z[Á0ºj¥¼ƒïVlÝÞZ™AóŽDP7-ÞpG) ‰U^^1 žîøqýëöµ¿jÁx×TÉ@…¯^»o¶µ—T.þV³…ÕWw¦`ƨM+«DŸYÑ¿3ßÄSŸ)7³Ï3íñ!�Ë3a y¼e•’Hà±0x+ÒFÕC-à~’°-Ÿj:ž`©¢OÝXÝ1 ÃõÙëB.JÑ+¦%ˆïã›ãÑ\E•adLû½Ù«‚t¡Œ*¤Ç;dî*±MeXÇÓ¥·xHÏ {å¸ã*ûÚÝ×J—eØöß^ª8çÿ~Ÿ‚ÜÐ*Û“‹wV¡ Ú20·i_£šÃ‹É‡ìQ7 Šh¥A(”Wd0 °ûÞÓ;Ž6H[ `4ÎBVç( ~;%6§·ñb¯F%îÇ®­«æeΆA»J[ûÍð}¼`šeåêçqeµÄÓúH„) f¬íßÅ+•‹°«ŽYŽyUQ«W/Y¿ «:´åo©ŒÈ6øàáÛÌÂÇ-á§w©Ç®³ÇK÷óhщyhG>þ¾Ü´ÿp¹ÙßOmïvÏ&Ê5ôWDËí�å u/M2sèFÍ[JZ¿à†p•ß8 ¾çö]Œ®ÑÅ|3¼mN DÔÒ d‚Ù¯wÂk± ö‚Ó«v¯5 yû59ñ€&YKŒJ_ßÙTË5‘‰A(±A1ßt˜>'`-Òo ¹ •¾ÿ:ãSuñéJŽ›¾‹Îb–†¯i°s JÃYM˜\ƒ:õ)¡(E¯O?mèÞ„o˜Ë›êòñš«Öo¨Wã¾7öwš(3 ³4ñ·r;§ø^Db Ó÷¡¬[5|fÔ ð<5ì‚Û¿eM`=;ë/ðÉ(ÛMç-uœãmIç›IC±´á»¡|£·$Çi ¿k1þÀül{7¡ý%7ow}jûìÌ[•¯�«žZP–Ö‘ðÑÇ5ýÊ9†¬ÃÛ¼ò8Õ8Ñ1‰BúE’•'íÃ. FZ70ÙS °@‰’cy0¶ æAÉù–Ø––=0uùlT>'¶zYz†@|€³dNÉ.�ÖlÁ¥´P² XËØ†UQv²uÝ ·¬s“q…mõ”A / ¸ÂÅž£FôübyÌ¡%»±KˆQñ»}¢EaˆÇN$Ì*“5ÛÝС· $ߢ۶Ù£†}aZ4á+ôü‹[æŸ1[³ßbðÎ`˰®†7”ÿ<­N$>ïÊcu;ñÔœdÕê5ñp5�³†?ÐHäÊ©þÂåüTYWûùq†gr¼/Äíˆfàáá·Î…¾r :h9'»·‘üz‘kÈ ¦SkWR1KTìWŒÃnJ”ÇÉmø¾£yOeÉ�¸èšGX¸ÜTôšâ6ŒOTl)ßeĺhp%EHR€P'T‹•Áåarmu+6™ ofÕÛèïøfï"Ç:ìÙ[ô ö)`™ ª|½^ø&•Q$$µqÏíý:ª$iR??NΫ1ƒR3g”𡨗³*» ûc %G Ë‹]½ôsæä̬$ˆßˆ6)Äe|eÌ)›êÔØŸ¬;m8©Ïç 2i«ñü-þöoÈ_­ˆêõ¥Ö]‚Tr[VŠÞ•q`ãÀ,fÂGz¦A¾3“o×H#‚ityµŒ0.öÁËC }<úx˜\%dPzãB—>²8ù5ô,;D¥ÊÃ฼ï¿üÑs„Pä1R0éZ¿D“‹—$xÿjá#i&(Š•¬N½&¤/<ÔàhQÇ_\•:š1¯[dÈ.i0fgB¼°šÉuð X3ê46³ô.ê–¿1Þï”×M0K€®’@~$žŽÌ¥f±v£pmiDã´dr3S{1éš_VWöê<Æ$ÄÑ•7D@7 `émDjj€x{Œ¤3ÇŒ¼i¬gNi Æ¤€ÿA¼jv'rƒ ¢ÊˆP<«fÙz·ÉBÁþþ“€ðM �6êì@×?Àz7Dé&½y”õÑI®{|Áaq"ôØ9oœNWSM+ÔÃûæpÿTýI~&«žõîÌ|ïh{2ÇsÆ@>“ô—¦I.¸§I®j':v6ñÞãôvÆÎÜÄTý–+¨’wÎy@˜˜6g1°~ø‚;˜ ¢Tç•æø}Ä‹î[¢p}1ù–MßøL¹rë>ËÕ½V7G1ë“Ì"¥_¼6‡ˆM¦òâ©ßÿ"\å+rÚeïcë•¿³á‹ŠÖ„¹´ery÷V1Ͳç¹ù+‘„KÜ5Ë·’‹ë5ŠnŠCF&JÈÇ›nÔÆmï×Hµµ¸ÂóßR !ÁUbŸUi˜G¸‡SɆâMÔiT@¼iù¬‘…¥WC�¿(†6'P*NÂË)Çà‡óTQÊ™•¸SÆNÛQÈee#ûÑÓ‘G…6ÊiؠѨäL5WEØÂ&S'õãüI6ôõ¦[ŒÀœ”ݾ¥o¨r½ÇTÝŒ$\_ûpY©áÊë¯hº/? þû¨A§x\m¬¬3£ƒ/aŠ¢BFI%%ðRQP„&Øm—ÇdäÐ>sq ú—òrÖ´OQHoÒð:OïÑëçÔ-‰˜ lÀó]gx*Õf©ÅÌöe&ò<ÍÕX7—û�ZúØ[¶öæè&oã Ü5pùošñ»íõå6/¶6ù³ùarÜ~pfÇwÉswëÐPÒ 8( ÒUò휫ìe<=ø Gy¦"iÁG­‚Àžð{WQ¯¾ò^`�¸Û`»sÚ¨:K{öº¡9<1-Ô3uîöÉð æK,l;ÎÀÇŒ/K² ×ÀóÇÛk»›!I¡ˆÝ Ý­3ÅË\ C4x=Üìï“ôZvŸ=°qËÃ;å%»Æ‹àúÝB?ËØZý{ õƒö0ƒÑùwÒ¼®¼ÏjïÒcŠM�¹醦-íc‡ÄodüÔ%”S^Ô3Aü¤U¼ÑS“;»/ >ñB5åˆNÀLÿrPسuXÁ.ãnfŒk¥5ÓcZþŠqz~î«Îb5wå´¥ 9#Õ?nä.¼rÕôzYvÞ"eq(b±`Þ]3Ì5ðžn5qÆSZ§v×¶©mé³v¡'µ¼¯¶ÃFC`§†sÀÊK±S®9fcýèSS’Å:T›•>°e–Ô¶ ”w>8nÖ[uZ€*ô‚äGÁ¤§êbó«lX%½k2ç'w`è|ÕÔÆSÔNø”%+#É[¨Øö,¬2½—ðŠâ8êÀYosÖJz&G.^9Œaà»t .üÈo} ÒýÕ1«]úCˆ«xØ‘'‡ Ÿu…áIZñ‹ð|ñ¢jµ”/3 `…ÀM×=ÔP ž ¥û‚0®w´Z‹X)»Àîh#ºÖLŒŠS3Z¤úbVÇo”¸9±diríPK=ÈŽ„ ð©{{kéLÎinf&ÙGö ±9~Ì÷HÉ}³:Nòˆ{û2”d%0Ì=®{7ÚÚ0QßÉX—Üðmã@è“3ZE÷M]ÇK‘8ÕÎNŠÊ#´í“h“ä°c1% Ðó,ûéÉš·«aä(•«sßa¯ ç¿x7S#ÏF-ãÏ~5 òˇb«Ú lUáq ŠaH B„‹ÃA§q¯~8܇†•þ'ê¯Ï}äQi<c�„¯<„*AU_P/êS\¨ŒwÇ¥àaR p­Ä—:Rç¢n*'œ™Èdõ²ž½%Vøá…Œ"9GMGÓÒerÕ‘†Öºi)uýN.›Y `0x<íQp¥‘<Õz¶LýZšnÎñWæí€:´¾àjŽ {—q‰¤´¬ìǺrÛjÏOQ‰2J¬3ÊýÍ6•λá,G…,É–ÅW!3H/}±ê;–Nq…Çx"¼â+øJ}2ñ¬éîÙŒå[ò†ÌÇIøQ3æ0'Íáy>X‡Ô&°0„ô�í7P wÕiÕês+ƒ�Çù3{'+^óÔè›ѵ1² àmÃXÚ¡nÿV`§X0ÕrV®;· †ð“°ÿ:ˆ8W¦Ý-œ›Ù*gÐcS®aþ›² 4¾õ‰$ýÁ»²@:Tµ®"‰aœ¡ú:ëòs×}_þ!awµ<ÄìùOIÿ~^óKÈœt߃Uaÿ:p_Ô fiÍxrŸÏéú®«¶ª¾.¼x¨(ïVºªž .T-³´÷žÊßúr.5õ…-ìüž)i9Èš¬!ØècÞñeDbL­wªVU½ 3†O6¥K+¶ÞùÈ\÷Å{•àe,DùÒ×ü!–z%µ«Ž¦¢oðCE×ʆ>ൻ/}CžÉ vÿŸêP×+2>09ƒr)έÚg–bÛ ¢ÖšòËV´ì'¯ïxhˆ*h̹ihr® G*‚é×m@6Ï3£ùÄÍŒ_¨¯ª–%™‘è‘Ñ5¢œ,zñÖ¿ÿ4jš‡¬«{óÁö8‚ûÎô+@‰«‹ËÊ à#°"‚Nõßo¶ÍÔ¢ç7F©ëúrC26O( ¯ŸZ˜ÅýßÕJz¨ÿUòaüœè8"öÓzû ê,Nò·yùmCÅë3yØR_ž_¨DIÆym’|ÂNŒ Kß>bÔ6 TŒÐÂ)ÄŒAóß #Hý‹RìÈâðü¿ê§æ'w–-}½´¿Àj¦c§¬WÄÛŠ„) £Kbß×£s!Q¼P(fáÙÕ‚Ä H’ÎG éà ¦'¼/G ·N(LøƒSÔæÜëdJGU©“Ë,à£0¦ûöä`×â×iEvü€ähò“ {g#?·³DÞaÊ ‘*L%W2ÃÎiRDÍN¿þ=>NëBà’ù‰óXMÀmdåȲ÷v?%K¬Rso·ÁLWÔI „]~û¨c„I@«‡O^cp,J ΙâY¿ÔjlämÔÃDÒM!ü‚“ ¬ŒŸûdIš|ä…¨B7(kM¾]žù«ó}ö¦ª&­gKÓTJÄΚòÒæÓ–>‡Ñ´}ßbÜp’OT­{Çè“ÖŒþ-Ã×Þõ-‚ùÓ7L̤j†³É-,J+[(¥¿€ Å[4AØ‘#çí›ÑÿfÒòMõÄeI.ÏE€Tn)l(]/¦‡Ü:#£]u_F)“2ã+Ƽ²Œ·»0_eY¸‡—Ià@NèÕ=:€Œ+Í<k¢­²ª¿;mÌØä+´æŒü¦ˆŠY:¥¨ÒB EË„Á6!ú·–äšäR~莢±$S2íB‰…˜êBžÞfg‡ùÅÞõ}”‹´™¹ìí‚¿M¾Þ‚ÕG„ú_ÝtÁÓºã(šn†MÈÓïtdꔈ&kyrî­Â=’tîåå]E®UŸ6Ä1Œcë!çë_q˳2cò¦æÙÃ;§:ø‰\•ßjl}¢1ÑàK›¹hÎW>•¹³Ð×RöÆ\·åšƒfL¸#è°Ì¥Û HY2fÙ™Í ÂÛ¬ÁM÷æOAº&<ï?™¼ÀŽªü2 sv¦j^°¡=srú‰yÖ¡ÞïSÛ× uJPÇŠƒž0Sè€æªLù*ôTÎ'SH£0lž>ú®Äÿ æB ÔM£u$ùƒŠ%aç¨èMSÔIÁh°è”bùÚFÌÞ] €”ajØÉ!}ÆÉÁm@ëïód¦m5}qÜŸ¸”‹—üØíúen'F§:I7}uÒ…-x€1•e+É_¶OÿAáýtÆŒÁÂh¾¡åçw õq¨Ž¦L*,xGBA¸\n`s9üp^茭ù¬P¤2±¹qò1éb3AÄC<�¦T ¡Î˜ýÐFpº¼Pኧh�i qN­Ò¨èŸ+j­æŒÊÜø\$eôƒÏ-âxŽªùŠÃûÙ_ ƒ:(¹ûÉÙì÷ž\1™í…ÿuÏ È€.FZ~Ù Äâ¤$ÑX-»h‘–K¤vÉf_¸e°|A÷RmÉKz}ŒÅ/í‘-ÛãN¨ =ƒÊkàµS0i„EU^}4oŽØW‘z-ªÎx}Ò®¸uPq…ì f—H8#V~Vè¹ÃèšÈoå$À%Ü&÷ò(®ÞŠYºh][AŠÒž"x8X/fF©P+Âø"¢ñMÑV]„ T:0:7vWòR$%¦ÙÊÕŒ^^I†I•V´;§ ñ"ðúºwñ×%ÜË ­2OÌ°ßÆceØÏÚ–±,XØÆÞö˜ª^;Ì^$àX ¼œî#G—Ñ‘å{.z–žÂ"5˜�Vmžë—$5Q›±ª+9§¿q|àðÞ½Ê/¤V,æ™înDTÕwW¼ùBo³ §E}êÆc/ñ<²BÌ5ÏŽnTy}Ͼ‰~kOp6ÉÚC !›¦9?BsHšl/:l¿~Ë2ýaÔLpc$½Næ±îˆˆ ƒñWv˜2¡Ú´$G~§kÚ¸éG%’zïò+‘<f½0C{ò¾VŠ‚ðµ#ój nÝm9c?ÛqðGNÒ“IÝñ‚y WO¨ žsfÜ݆M'ý:”õ¸ùK§ìy¯T½Ýãµæýu†Úñþ›¢=ÐÃéiϯ=äîHmµŽ+­à]|w½¤‚ö"A„òwpùjËÝ6öJ‡2)=3Wˆj¯*«I÷,fLYcfå¿ÓW‚t|•¹žÄ×ï_2Ö˜s½ãæ[Ýk¦l·.äŠúU2=®föP\–Ðë ?Ωl‘×Ç– Cíè.0EåYß[‘ÞJ¯±O4&¯¼ìä”RênI+[]¬mª}7DñËn#/xAB‘†ü;d)F•’…‹iÅèŠPºã§nˆL5¸åÓ7Ÿ%0cóÄŒ6¡µ·Ždk®ºà©Èö—ÁÒ³ROÏÃþ:’o¥1ƒèpQdz»Sçì––.i0BãVaLD}ê{8]ÖO¥¥·ô‡Ö©é]{jyS‚è¨ðZF “´×¶fqýË2v<{ ãÏz¦Ÿ¥×•”2¿hµ†ÔL™~}v¥ß¯±e[¿=‚÷â›®eƒíö¶µ‘äO¨pr½@³-R>6Q‹÷ý0¢’ú·TÞ Ö>S#¬{㳚ü×­å°WOåöéßðé[øhð×¼¾Ý=þ% þ5‹íÓÿpéøhð×påªæÒS½ÓÙ\Yê J=ÛÏñZy0áËCÚÃh(µ¦«y_åÛ‹5¯LÄ$»Þ#´`±â^¦Ã(ä±M€*¡ôØ–J(d­ êZ«ò&s)IyÄy Š»3Û/“òõ–Ö3ÜûGk}Öå‰t}«tÊ.DÌü Ú¼m¤vfâ—qXÏNÑ*žŸ`õvì\¥Sû Œã²+]8µ¹ô¸4_Z×õθÞê·‰¨&P B´*cäÂÐæ4 b@:oAH½ ÿGÃg™Üp®ÔôC¾PÖ ÉÖs¢2Z“¬-¡ÍÀþ°ËÈ£^WEóÞr‚«=ˆ\B ‹'ü€ ñËÓÄ5ÈG‰¬ô…ho‡…µÅ5Q…­¼ü|ö.‡æÕ½Á^בtãÐe9 Âam¿ÊÊPÖ¿ñ‚—$Mi£‰³…À ÷I{‘™ê [c‡¬‚„YAA4Rä¼CÓ%çÙ">uèòZaË+*•˜2t.+º9“�ƒ–ìºôò3{LÚPµ\·Ð6ˆÜ*½^“~\O„±hWMÞrFŽ —a%Õ¼j7®é~4®œД’žòôÎwÔ†¡cB)“¶ 1^, ­›kºF<ñÁÝÀ½÷Ûô¤ÿcì¿þ¾EæÆ…ì¨<>Úgfuh€/¢!^x¶=Ow õk‹‰ û"Na±æµæ§‚,›°_Ÿ ¿YKÛÇ $SÏÌÁµg{ØKt„0zøÞþ°´>],yºm84Xp%AZé1@ÏÛ"„mJ/ }`§q[¡Ù¾Ûƒ8ù–Û«Q±(LZ{öuö2vJºFÏÝê.ô¦&X 4v4„ôCž±Ê r;·:æU²K#óÖBT@+iÐÑD¹lt~§vNâ¹1˜¥3››,Ý4o¤ÅÉ©8u„'®¨{H;ág~äAÛï Ù'Ññ—+E‰ ˆ‚åf%åA ªƒcvŽÕ¼Y¿<e[étÞ.†$«~?ÓVÜ—üø !)“^’.1öÍ´øv-œY¸¨,~,v¤SòìÞ -pÌûÍèé'žjå{º¯c Dô„Μ¿»zgÙ¦G†dŽ{ÒtDªÑyoÅ.²³c«gS;ßdû˜-}ëýZTu«§¥€Q“Ã2OÔ¿ _ž8~;d D`àš„'™s?�|û{ì8ï3åq»ÿz¬Ì'•ÇÚ©ô¦ýÛ÷×Ò縌ԠN!IzÚï“<¯Knõ‚©AÒO¥ð¼‰7n.Rñ »Ý/G0¬¼ä‰ìÚOyö‚8¤> ±h^:+tæ_¹òoMÿ0jŽ‚Ù5ãJ:ÙÎZ‡›¬Öç 0>SOàýðõ¥¡Î¯W8œ,±É{h'®œ±æ*cˆ;Ž£'éWˆd¬gi'õØTèi•H Ókù‰èþ?™3ÂÎÈÌ…³žVbž‰„zê +?öSÒ`­#Ïd&wÓ¨S”×»oý+ý¼~ø’˜P(¨XC}´8×·”DiiÏ«P‚p·'b«×26ë.Ï6 …¨ãÛO»‡òW«ÒƒÚ ¼x~œ³”SAã*:;*72ŒJ³h=í¹ù9ÎZ@a<N @³÷0,ž- 7�Ï›릨¥œÒXÎÖ Î°Î©ò uÓi½+Êùj—%•Yòšõ;÷Yû<;'=¡s;Jµ² õì½aç ÃÎÛ·&^¢1Ú0§F‹$ÊÛ5[óÔà”O6OjrC¼s\ýJøSC„8Ê^Pì—ÔïðÀÜ›…?>Szi×Ͱ)G&«‰­oÓòEjÑÉßá(¼+ÅöÌÚGáÜÈ?¥ìü²îÕßT @Œš!ßÝcN?¾d…rnTŠ‚(!±íÐé~áTâ[fÑÕ_]é굃,óÒCôÌØÑnDxKƒ«D!è2>Ê—E·N‡“;?åuÝà…w}&íø?\AÁ– ² š9;¨…$‘´ÌÄǾmw—/Òò¦‘=Jœÿ�YÓÇ£èJ!?á€ê91¡ßÏÌ[ÀEÏZo(}Õ“}ÿbn ,¤;v××wih@¨ RX޵éÖyÉ'ö)‚`ZÃôH¶’ùÙ8çõƒ93ÚÄ—)}ëk=Jã‰ÿ>ÿ<ãòÞ;Šò¶n®DzTª°UÐ÷ðbò½Wʼjb2Àco&¤ÞqßÔp¡j¶åc¦K·ìîT_Açå'б¸ ¶c´4—fKígúPJçà8:¿­ö±åè-µú+ØKmªrz‡¨4LûÒ“R9tæ*„fB¢Á'Ÿv{EþÅíâøUlz>ý ɰÜÏ÷›QN•ºÉòÇajVÛ{vüĨž³c¤R„öª0³Æ‰}’…=½ôPæ£Í_A4`èº|;ÎtØ¡èÀ¨Žªƒ”®îÿ_Ñï}Oaœ+ö™ÆRyÜ:Fõ~ýú½ô8"Ò9 ÀÒ÷¦FZ¿ÙXL¶sc?ðoz¿¦4W:/¯ o¡¬Â'»¾‘õ¢ÉÍR”À}&�g²3h¹üb d»Í#Á¿¤Cò‡m&§o]t„hÚ èG‘Ž|–p‹;4÷Û\„¢­ŠÇ2Œpü…(uoÚñÇÛýwBaŒB1Ç?¤ÑH¶I¥‰=ù“¸Ò§áí©´m‚"°¾y¿x\`«ÿ1ù]r<Ç…‹8l–QEº,½{¾\>_(ÞÉaO+‹µ•ÅÒHÎ+} 9ãmÙÝÝÇ9s'ÿïï³ 1K ƒýÈ7tU£É&: Ù%0l½ð i@cöÞ‰½–NÊ™�¼Ôõ׫äÛéì7)Ê‚%‚¥ËÎß7‘b½²Z†|3ª,hg6íÒä6nÅøÍUÀkÉ¡‡N§OžSNN…Ý„×3)‰ÏêªÌ”ß0P&>ûÄ þ‹×h9Ÿzl<Å»vAÌ/_•¨Y‘«c7H1ÓnäÅ"`¦ÙWÄÉå¤<¡RÌX{,Ì«ÂZn§7o^°Òg»0ò{̃d[dÐaerJ4ά¸NªS·èQÄÈ25n4V_kZؾÉéÕ„õþ}õ²ÇÌÍZ~eº»r(Y: r ݼßx?†o!ŽAèMzªÓž¤á>»8% ‚¡ÐCÞ^MÙz\;U'$IæÓ±•2Ù¦Ô“÷6 ôøù/“É£ò¢Kþѯ«Ý4°!lïàø’ê?LaJ)òQç’¯N:U!GøËîVÖ¤û2h¯ûl"¬¼De’ƒ÷Õ@À¦µ½r·|Gþû™¶I²Ý¿Q׺¡¡ùI†ò|ˆ8޽çÖ e6t‡ñ_mp¸ ¸T#Mºáœ  Ëm(ªc%#l ›Â*“: {þ2^ü~¦º×¾¿ùb|ë_ׄ˜zq“¿xß ‘æÅ –ÜÃÙ‰˜²„zû^èhÏ((ÿ~Æ«md&¥õé³(Ò.…^¥º0Í@ÆaYo—H'ñ,*¦û¿‚ºßÜï±2¹—>9þ4ô‘ç ÷òNýž72—Гº:øv‹b,UYmóH(¸Âñä®ÍÉô ãuk)w6;@ µÿ;ïµ|-µqÓŽx‹Vî¹ÔùìB·6R!?׬¿Ú!\ÖË… “RDgO~G!bfg–@Ã:¾Å eðá·£„qM@±cüG…¢ˆõ^ölêîmáܳ‡èfh™}ÊyTrÍÂÒ9ë $UàJ$äïssó÷ª±nÍOÝ2?´ oc”M›DÆb5‘€i:¶pwétíìñoÿ6Jÿ;±8Õ²mšGñS FòÊ©7Ë+·!Pì�Åo£ ~ÚvK Xö‚cÀ—úƒný…ÚbCæ|Û˜§¿Š|…þ†4½‡…q (=Ézš[SËÊn%4ÜI|Ø@Î:±¢_•±–Qâ0jR±vµ7 e¬oyQÒÙJéUÁ‰i¹|v j8TŽpI˲’¦R³5A\˜æ{]]U¸ Ïñ{”±´‚¸ïm ÜÜåa䨢s„ǻ帀ѓ¸˜ø.Q)èg`kû«á׈ȩþ®Ô5JT›Êíœ~„’®c#QÚ¦N¡(þ„ÐõÿýÅÌ24Ù)¾`ÀÃÛØ÷Ï3rw|ñÖª¢{ÕXaÚÅÔÛõGå?„ Œ«çkÖœ;7n„M'ˆ)Oñ:ÿ!ÏÇáɉÒFg!°GõÀ2çfpš–*ݨØŒßðpÆú€÷š!ëg?ì'ˆ·P°Ó�é5¡> 5èʼÙ5Û8éGèRÙ(é¾*Ç™g;Šˆâj[TP'?\ŒûÁ®Cš¬Øg lÙnyB8ŽÍOù‘Ð�WÁUa£_¾¯#±NìrP{uZ0"cµ#\‹ÞG ìO«üýšC¸n`ž´E0 „4厂úi¹Èí²=]4ÆÞ¥¯ˆAì•v=Ïݾ3îè ¤SB=3á¤àX€ápt„`Ê9-É×hó†É8àB»>7ÁÆXx,§ÅßÏOР³—TXuMC{™,p_öÕ»2í! òOR¶Õý0|Íc| ´Ë»™EVŒBVº¿ð¯``ÐŒbpµvÚ|a>Ôwÿ'ˆ‰v¯vºcMÓ>‡«î+z¾|€Ô¢e„r(¯OŠ•÷k¥VÏNWìßçÝô=Ÿ«üa+ëBê6›ZÚPN“ž§„÷°£ÑËÈTi-WæãVÿyœt"üËw– aÜÇZí”ï‘!l I>œ/µ^C¿Ñò‹P¢mîŸKÔ­’0õIÛ =y‰mGvK‰9´fk+¾@_›LŒ>Ìú Rgý«=Ÿ²«ôöÌjóC5Öœ êÙr,x€J¡u['7½[. Àøgâ˜ã«™°¶¶ø-àX�¢ÏTvj;7¤©¨O ƒåÃFMîîŒ肜Ë6Àå7sBkUÉ€ä‰vÝ7›?Yc]&Ù cƒÌ¥Ç{ÇøJ>I˜Ê6¨\ÃF×i‰ˆ‡_R°ur¸åyM(‰ÑnB d\àÒå5׬¡¹6y(=¦`á)÷NªY—h$¹‡‘S¦É+é¬1«¢¬î¦$Þè_kRþš1i ‚ýÖ i‡ãî¹X8P$xÇÖJ¥¬Fè †lî¾þ– Ÿbž‚(L)•ØÄ ÓJ-zÔ šó»d·‹œÆFH© ºlZÉX»‰]}}«Ç-žD«÷mÙùr=è%ž ‘Ô¦¢…Ö�æ÷µU(Ëj \(B$Wn™ ³*òÓšKQˆ¼ £šn €b:Ö]}ë톘ôߥWGz>šœÀê"Ç#ùQÃç¤UÐýŠïDø­Ÿ÷§²¸Œa¬Ž¥tmâœ`ñgžý_˜!¿žóñâN¤;°Ž;yû½?ì†>[;}Øì¸2÷ÜÑÆ[ßêQIä°¼Y[ÉŒ_æ@wyñ`ªp4@Ë}Ñ6[nZOSdM.Ÿ°µø\põ¾J{Üe“”ç˜FK?„ïzãàÊ…‘<· ‚¡ *£¸ïw QË€}È•ÒýúÒs¾DÍì¶}bcWö,^âÚ*ìvÆt ½[ƒñÃhhD¯û8ø[½6õîÚëø®]<n˜6ô”¼Xoͽ |\˜=€ò†À}‹Ò9+láÏeôlÜl÷n#—rüÏÓzÁXp„´WàËVg΋ī(Í·ÊrF’œËGòBk¾ˆ&‘U$üA6àrŠvÆ^é4ˆ£0*{š#ŽbØE6ßÙžÿ>Š…øcQÇN{@†¨«7"“Â^É/1AèÀø~üiÀ ~´8SB«Â‘ãèáeÐWù¡©Fƒ…¡Ã3‘¨¼�çƒJa úöúumØo¯Úƒ+ÞC³zRÜT>o$•5µi¡›º½…îP4ÿÇkŸžºf#ãÜ\"wLÛ÷«Àh¨DêXPÕ„#¹…o@ɮƒ…¿´ÍRø—ÌÕa6#ƒDË;ÕÕw9uÉø²å9ÌT9VE§È!EVµY„>ò û[ÁÔCj< /ð®ÅãüÐ45x< Z%8>a«U`”Döc¢<ö÷ÞÖØ 9 ÌŒèòì)Ä0bô %j 5>ï+Ð×tq‹1:÷Æpˢ䶧ÆuiihêΩÕÙüd´Üîf¼â‚Y�©ùIo}ßÎR9=ò¾ª.úºâï·L/´’ú¶ƒö÷u}½»ÿC{öÔŸ·µ ûusöõVý½ßVš~ÛÓ”|•ŸÛÙç}ºK}¤ÐøzÓöô¹}½AŒ4ùõWííK¾ÝŽþV¿oC—Ûª×Û¡\Sç¾_Wa¿ðÔKí"“u~Þ¬o·ªŸá»»öÔÿ[Øß}º¹ü:j~ÝéŸV‹OªØöß—øl²Ä“\›`ôç¤?àìfvjñWyˆñžH¶EÔsŒ|— ]o»ŸÈg`¬nޙ»õŒ�,Lr Ïè¦ËÌúÀëàëVþw,B¯Bº6³šTÛ«9õ2g´pûrQø)–N9rù™"— ß ÷ZØåzj ´éÏw*×åá|FñøC³‹oDo˼@Ÿòd\w¬ÃPÊïq¬!ûd¯]­x’då³'ÔÙS¹z¦úk¿kcoÁÕöŠò¡mÒì}|#‹Ö]îV5øiYþŸ€IÐe0bv~Ÿ'§­À]Á‰„2k¾V$wP'¨:»2uJ4`–3ˆ{uËŒaAûûßöÒp¶nø¯´[GlºÔí"ØúZ6jÒòf ÉÆ÷ôyÝà«{=‹;åTý‹u€ ÌÑÂ2tQ&QêH0ƒg¡ß�ý{‚êWüt&•ï f«l¾¯ Ù“´-HAìVºt&â½å| À«Ø=\•íó *t¹.´á9{eý9~7š¤îÌà[c¹Så¨2œ×nöÍÙŸÓsZs\&¾èK“©ý BÍK0q%ý~ad|~ guv󎛣"zÓ‹îŒ&gü»âÒ©å*,`Èýã¹b-?`i„5sZýqY˜°{H˜¤Þ;0+ᥟágOP5|ûA‰G\ <;Sp-ÈüéMÖûêýOƒÙ^ ¾¬Ð/Në“¿^цo[t…ü�þwHšnÏFKJåÇã’¸ä™ÕÑNÒ%0aeÝD«aR›Ä½€u´xhŠÀ¯±N1ssÞÔ >Ÿ4 |Yc¯ç±»È„¯^šÓ ePÀBã×Oàʼ%é>&¼¼õoåXQ­Õawïþ‘¡S>1Áþú$;£oæ¹Ä-8|¿þiüh¹u(ä$”6B�y«}L¤ =ÐHº[4¤K©S8LÔûœ2;BõMª±ŽLG=#O𿢠e2D“°¶Æ¢e²•ç4Ø­³¨ÒÜÁ\ñÙ š6œd]Kõ}e%©áÌEÃ�ÞaF]èô�{Àé’n;ŽøœÇJhÌÎaT–ñKã>k+HX>u‚_Kܯ7Ë;>9¿Ò\n€ö¨_tåÈลŸ\RäK{œÃœª²4²?ø€ºÎÊäØùu‹haè?ÒžüJ>Šè½›¤íö­è¼ •hêPÒ_ÿRÂÁ^–‘P_<á=ómíTÚQA[Ìw/D9Z5y‚@Í+‚š’ËK)bðŸ„s"™;üu_öÀŒ&2ÄdÙäXVèyªŸgnWáŸÜ«WÙR—\¿w<Bfà_%{@[ V'Ž®ö;1Ä«$Ïèâ)<l]ZcØ¡‡Ð�ÛIa5;Àü©N2åUÂEÛmY\ÌÔII:Šq8o!„2–Ý�l•.ÉMYÏŒî¹I3U¿«â‰Ý;q’³Ä’´ÉE:’3ÙÃG”¦ø¢nFk•›q=ÉE:ôŠ/.°ïíûÛBgˆoi +ÍÙIìÐÔU2YæÊ‡´]+U¾­Y6s‰ôËøVI³ vÞ®_ðù´sÒŸ ’ôjIñ‘ù×ö²&.ÖÊž ý]7`˜ÀK$ÜTvŠÉW‹ãòž”w²‘“qÃøwBÀK·fCÄaïyÊæ›¶§ÇõêZÞ§ë϶(•óeV‰•<V™Uy≛µí…¢:]–4ÓF¾GÞÂv&'³Ø@ß“jIAõÎ’~À­f¹ÀPøF¬JL| 7¤‚8q­˜cù–2?«0ÆŒEŽÆxçô•Ë[f qJ‚äzXaÑçˆ^·÷uö»àbPÑ£þO_)Ý¥¼-ëùK=‰}œ ·«°§G‰Òô½òÔNÚ?Ã2«¼gùè/½N±#Ù^°åwe Rã2o)ƒföèÀ¼ëLÒÀR3JÛj b”%:Ö¼(ðç_Ä÷bÝá…¹™±²¯ù$+?ƵÑY·’BzþïìúEãÁR F�­“õó±w†nMAµÉá ÕsA:‰^†Õ=¾8ˆC2é• iš Göpà[ÂÒÞ?&TsN€oÒ‚ÀPÙÒ&ÀÒbæ<J\·—‘±“‘†Ä©84×™ðÜu00ê«‹Y<Ábí†SÒg¸¢^�f‰ZCCßÓµÜÍ£¸ý]ŽsHO�ÓAU¶J¿×dðè³— P/|ùÓ¾ˆs È„…ñF@qíø<`u€n€ŠÇ(>ÄâÉê†~?Œ—§oêZàÀþ;Ô*E$¿F£ ™‚Þ×´Û7ç솬 ƒÙDÒ–˜¹VCÿ^IЬbjÏj&F¶%Ž 1ûª4Fl›Õv4cæv‘wõ”Ë·â¡âѶ½hì‡�°2­‰ÂÈ”…•Le¡õùïÀéÎŒµŒÖtÓd ³7Xê« áÖÐ-ƒÎuï €£“X0¬1…䉶“.'ÈSé_Ç(þ^™ˆn@¥'ëý‚¿”î |(v î7’�Û‹vYPnµ‰S äËN�±úˆ¶"Òy¥™é|ZØ»öKÀòQg)=£ÄkÄu:h"ô<DqþPlZ°ð[„ÑêóªzÿoÄä‚ÔÊo™‡)t|žò²ëQl( N°Ìûq‰aº€Dp®ZåyIÐYfªa5Ï*:¬çŠxcÛèœÚY31í¥1 Å£ [v54¬úã+ÀÄ`rw|_ÐI3ÕBjxlDïx“ÏÁÃpgõãw•Ò(r½[42@‰Ëús$ÌNVíѳœ«î£ÌN‹¾¢+áJ…½~â(o³½Ä±æÀËi¢¶Ñ[õèœ Õl¶3= ›màk»Ô6!0#ð-hz¥KÎ"9Çÿ7ž›†¼šÛÎhOïáŠE„l‡Íæ%îË ¾&P{ž;½iTÝSmŒ®s·Ú)ßY© ‚òP Õ®@lÑnÆWô³Š‘�ÕmÉ'¢Öá'Ñ TØä˜ÛPç0f}Ÿ¾åä¬ìiqí³(`t@VÁ¦—~ÌziÛ“Iöƺ×a¾¾Ôí@*\eKA9Z{!Bc vÜË%KRÚ3/EFO­@ Ð촠¥öz?FM£Äsâ4bV¿¡;ƒ¼?(¸TŒ½7_ ¢übýâvä¢üàkñ¼ìC‡–(ñ®’þ1^9 —õ¨TuéJ-©‰À±{¤±=ä§¼Õ{A{W‚^±àœÉˆN‘šÁIˆ×¢‡B¨‹ZÀgОuèœT¶Ì7ªäeeüoÞÀëk/ÛIgdte¢j; H9.X=C` g–¿—ƒh‡eC"eW ë—Z,qU°æDö¯81ûîBÇñ0r_°±e ©v¬vI‰4€u®C^wØß–Îáó•ý¸p6›“ð‰ä—±ÿ„4Ø>j9™âçšßUÊþ¤NÆ®<›óÖ<¹3ÓcY6Dæ÷êË€Â5 ê»s¯‰—cv§¹tÎi— NË‚…ÙrþÊôz[—Ï¡¬žÄ0 i:JDVYÑ_l“f.Ÿ(=|ªÚd­Ñâ$¬þó·|„›ÚYçR‰–µ•Z-†ÀçÁ0ï%k§}z°B]5íçÕéU™ cõ˜m\§+Oˆ›T8§)°‚P,`‰ kÞñ$;õ+¬7>ŠtÀýS\¸+ «–D÷¥œW…#žïbñ}‚®ÊYða›€¤?1#|´b71ñ]rÐtF±~Ô.!ýçø*JÝZð3ù;n“ièÄ~ÍEùìƒéáôV<j,[„®˜!tÉ­¬ó’5Ãïz;�–ÛûJÿoõ‘£»Ñͺê\·J‘À9ÆÊJе£~ÍCËyø0bV0Á0¶Ðcy!¢´»~ðE¢1(à²Ý*™\ä/& Lâ«&S^ßL)õõ¾höú`ZÌËö6VÀñx \ 3ÿh–~ëO½·3»‘;˜Mü¾FÉ/²˜!E0Ã2¼=ÆDO"°g Y÷¢{ÐÓž†ð«®ÑÀBwcGv¥¡sf·£½ÚÞ:óÃÛô¿‰Ü€G”Cq2`c>tW–Xª£ÜwÅ~"vr¾üq‚E�É4kæŽÙîXà),öGºþ{ Í…ÍxBç½N³Ë_  åBk¡Œ&Ëï„Ìз¹ÑûÁ4yÃì3ó•u=’p ŒhZàÔhÄ&¾biîߨÖ`éäÎ…­ @+uÛVyO¨oï9Þ¼>L:!òúqåÀDqÒ^|4L5ÕE¾B!Í{ƒ>]tà!VЕ”*o,¾ Œg°èŽmZE]R4,ñlz£®†^ÙºN–ó÷þŸf yx)_ÀuFïJ§ÇG¬ø~h•c…Gn¸¾wÇ­í‘9ø÷7èÚ½*­1<à²Z Ñ€kíÚÒG,ÞçD(?2ù’WL±Þ#½ßÄ|T”`X´>üšÔgk3Š©,Ɇ²f´×4üëø²jÿCïâ{B².ÁÏ6 üÎ $š/KÀÌ­HE2Ea=°Ïvi§øÔGbaHÇZ¨2°»rº‚󀃱iyª«V¿tàïøŽûìœ[â¿:©p˜¡tþÞa(f~Ÿ<;ŠÊá(aŸ½Wß'܈¢Vú²%÷5|ŠåpH0Î\¶B€Ðö…ËsP¦çR«¼]‚EžœG*§ƒ#ÿt?¯üf¯qUÏøÞ†­Ú„†Z$„î–U6"©·UÓ ßè¡xþteÉÖ�­’ 3Ã~ÂТ<ÚäèÐÜztjòÎê½³Ê%„áXT5l-\ƒ–Àžx!Gú79PùòDJ')s†èÖ$`s²ƒŠÙhDdM3~Â4\ßh_Á]<²85o} – 3×t‹†Ê©n%ŒiÎþGzÍú\oj6ÐÞ8ÂÚMY‚ú¾e¬²Ýzx ìfGÌ DÐðí³4—QmŠã…ÊZÊк[B¡IøF÷œÚ«rGóc ýæÐY˜èEŸ7]¤b¢¬·|\uÈ×§baªÕ^Sìë+]shAâ…cÔ8$oE¢Cjžú-±ñ…¬÷Á…“ê¶và©mª!½T™|ZÎ|™5‰$ª‚bÛ!MÚ3qZ¢ ¢È}þ@dê{1¾š¸½Â3o½!ÊÔ2b?„ÎrĩјÉ[PuÕlìCõÒéEááõk$SÞ>ô³¤ÝDפ?‚!1ˆ²×æQÑk²æÞ‘<Þ=£6ÍŸÊhdLØU³µÏ@a–âǾ§òMX¶òÇ¥•eœŒ£³aaªv —ŠŒZL:ÔËwp¸= Qç\¤ľÿ%A„°7xª1Î{r+ÑÀžÂ«d_tnA(+k[ëPÏ=訹]à=áo‚?4oû:¹“ŒñJÀèßÝï*ÖùÀŠ1¨âù}H{_•вÏV¿W-C&²Pü¾˜ £ÇY°Š4دõ]ðYÄv¬ˆþƒ,‘ÿŠ'âJÃÁ„ŒÚÿDm’?oÙíÝNE¸Ãñù¶ó¹û~}¬?W©_ʸ»¦ˆì8–A4Î,£P„}þIÒß=T�‘ßê=x+~ ²¿®Y9÷\'vx‰qoøˆPzº^QÞ¥­RáænWˆ‚·õ àÒ…x‚˜üàÛßêÉ œª›Žx9ó5µ`帳8~ß‘?„œ´²aLÂ9jk¶e4ƾ`µ¡nk€u2»Pƒ{‘Ña-®X?å) ÛÉýa÷ìÈLI$²kM¹¿˜ä’4Ö¿@úw'lz™84°LM¶W#ˆ™^-Õc¸ÛÚÚÁZØ*ÿWPwñõÖHóßæÜ%Z¸ !@½?ðFÏá‚9WàGs¾-¢Ô‘(TnáDw6?6çñ°°ýØ�P:rfº(ìù¡?i-ÌíX‚+ø•C;Íת¬ˆ ú™EзÍC††lò;>8P}Tö(o(5K9+bc°€óǃƠÛ2îÜÛ‰–VÔЬpçfph=Ï;2r~Õ8™~lm÷^ŒÙv¾ª¸ækXy#'üPN‚ݽÑem2�”æ)~5–+Y¶wÄrЃ‹¿ŠìØ“ÿStC�pEõȈl]uBÛä2Šå^M�óúoª @)6 à‚ŽÆ¹9ø~A™*…ó‹©cîû… tûÝd}'[Ø(¹„’àÓSV°UÜjTi«²ô£o6wéaɈú\*³K˜³–7ÁóßÉã·UÓîUßk@ÿB'N)Pî=‡[¶üâ‘ ‘ '×2='üch ËÏQ qâqh,ÝkÑN +Nç˜ÓëCÕÈþ‘–Gnî¿jÒJs LŒd‚â)W†µ‡Ê§Ÿà15ÉIÂQ1í4HÅÑ â–;ñ”¬iž'@›ó«™3RúàΩ%PÓ -äXGÛÁloÁ±Y¼þ3_âœÅ‰X­œ:–WÌî¿:0ÚD±\§Šoeêž>Ohm¿„|™Ù,œÍ¼®lè:”ÀC=?êõO&²“ ?Ñÿ £ØlÌ¿bòž3MÄNNÿCX‹”ÿ‰ #‘«'Ò áõE$ EÔÏpò] TðóÐŽÿÜ/û‹?óY)æ÷&>M†Ô¯ö¿ÍŸZ·Ìt+nW/ÚVG‡d1b¸C‡i~ã0=ѹlè9×=Žïáz¿òÄ*å@óÃ-gÚ6äl¯éÑó÷³§2½üòvDÏÕ´œ Ãü%•B˜TTžh®J=Gííh)ÓZ3(Žpù l1],] _þxÁ†¡<kR¼:ÏÐWÍË’£YGµØ\ 9ó:¶bZíïrjÉfðɇÿIú§NÀ¯Væv»>$Bï¨|{ÇñÏwèÝÖä²Èö¹{`|&%xŒOHf øëʜ﬩1ßX«¸NÍ. qÅÊÅ$½ JÕ‘§£Õ Tr„Ë&gÆD,ŒQk wD›ÄRÆ\YâùÞ¢ÄÄvyÕí£t B%§© ®ÈÆN›ÈîûΊ"máÁ„ûÊb{zåa2›®"œ]PÙ—âÎgÉ’÷ëõiý~ç|•­ŸýÕóÚ¿‰B²ìsúÂ!<dÝ9àÌøÀ·Ñ•ÿOË–ÒN#Ø[-åY®Kx !‹bôCÇD-eŽ\f—ùr‡zWƒ x1h¹Ê½ävhS@|t~ñ°3œ‹ß±gÿ2ÙÖÒâó¼ÝVl­Å£FM–°)Ã*1£<°94ðsüE¯˜~:hÈ컽Xj‰Ï€ú³É§‘zlÞAas¯{;Í"`Ž™‘ˆ SÞádk^¦àv;—9dË'Ãõ:š×þ·—ˆjkÂŽP¾HöJ×T;ûä‚¥Ò£›ˆ^(¶ *‰'7&Ó#eÏ—!:mqóÒ{Àú›w/R æÜ4 ¸ì¨Ôz¸Yñ7¢8[!#jµ¥©>¢53£³óæ!…§Ãb­þaö©$”Vàgf¶§¨ÆÙݹ£ô"Ò¿v©U}º·‰X°3æÕÀp­ÜhQø,4ÔB®µü©ï£–…ôñ%’~Bd7ëzrÊðbóZØžËDFÏ„ÁØçâ1½-”uBÖI²g~nÿd|‰ˆwå"C­Ò²ƒE/·â8ˆý»%ÆÆ#=%uÀâØëè³óÌ4Ð\5GŽçòÂû5Ìñê°**'ÎÉåvÒ⊘• õg¹õpJ/têÊ›þáDìI·Æ,Žs"3ëç½ŠŠ‹e4´ê¼+Š)áo¡•ùlI¿!✵®Gñœ©Ù†Ì—Î »WÄÅôÿ$8˜ þÒfôHT… ß¾ú„&Y‘¸ú*ÊÆö:H•r¤5¯ç7ž“ü†îq®@[GÝ©¢Érýä¯<+NcDåÆl`ÄÅ’5µ»@Ë}ÌÅ“OS€[Æ-þ#•ð þ¸ ún"2Tè {”·ÌÈäaUG“#pÖkaˆ:>Cý•‘Sïò-î|œkö]C b¡f?>ù'‡šÜ�R)½Ç?@Ž äPÎЋç<b€r"úóZÂW4Þ$X=éûëWénÛ¹ä½àÝ@Ê`ïýªrj°³¼ßÆ^ '�"XÍ}‰Ý"uE×#3‰5÷áF ýœ´}`« ÅÌ‘SÕ̧ùòKUpZ-™M#Ù¤=¦%¡Ô{±–ÑÞµí‚ãÜs?º² œ›ÊÌÛ­dÛ1ËÞÚ0—s5Rõ„ô³mâ°òÏ\ Ï"/ˆæÄePÕ¹‹)å °dQµ€œY·ºÆÞt˜#9¹u�g®¡å%ZÔ Èt%¾,Ï›;ñ#ŸžÌ™gækg<+Ky³¬«ï Æ:ÀòBí„} 3¬É¼YïŽË…ƃˆjRr™2;Р寶pàYÄ1Ôú¯;f(ÜäŠrm§— 5�µ²ËÁÀH(HkºˆKc± ~ ×~‘qàf£žŸgë$N¸»L™â6qüH «Md5O7r¾y‹L0f6 ¯Ó©@gë]N7dG±#Ä,È7àJ‹u¹Í?•ì!|_à¢IB²©Ûðš­°•Ì«…KÎBic¿D×âµblaÑß l²šÀsÎ÷é¶ ûqÜ÷Ø2Î}H²Y$XjÑQDgœ2ö¼*ßx6†è³Hl:þH’‘VãSÇâ!Q•Õ4ß8Œ¦vZáí¢¢643}{¦)úG=ý'ˆÂy ?ñœ°­Àª¢Oþ‡¿Í\ÀG1û¾±pMô^tP’ÃzGÃ-÷aîJ­+úÖKŒBžµ½žƒààhcI¹,GËxt4‚94º�ΉáºÌû\�LäóYí¼@@ÙÒÍŠpeENp‡‰>{¼IˆÝ< º›ÈP}žŽëŒî' ™™ K&óÜoš{@3z¬3tºÿ›¤ú–ä“Ù¾~¨È´¡ £Äš(¾N¿7†¬Íê±âŒ]¾ÄOsµ[80ˆíÒ$wmαà•}“šƒ}Í'ûlz–ݳj³uäZX53Z~Phø¹Ó"œ¿ÃHò5¦N¢w�Hd“]÷Ñ¹ÍØÂ0øÏèkãÍ ÉA[ÆáCíAMà,¡/‘5O¹´äð¨!ܬxýŽÂ´ú_ô¡Š�®ϸ|Äñ E»\rÄ­Í=›*Þ€xccÑTù["oq(/È|)–Já7áæ´ô¼ýPTh³L_!5lj^‡îª]¡˜öŽœ¿Y¸±}.aEÔ¡À^„ ‰Ò ®ß¤y̯ð|±@‡¸´³âf7U\oq7~¿ÓÐÓ:€Æ¬„ïòpÇ6ŽÄFºÕ¦Õç2êoòaXŠY^NCÁ§£è[¡%-,rï– ™ö4%þH£Sv¾+lP·kuzÃ?°Íê‹ËºRŠ…Ùé}4Ðÿoß•C†læ!»2{²ë9á)LÓ¸ƒmöâÕæ9Š0Tr¿½©hXè6_Яd߆¿ì½ÑÕá«]Îcx)y$ý¿=iFâ5)5Áí…=S›ŠŒ ýL^‰¸€]œpds5Юÿ`î’aa¥‹ZüOñ¡€É«2A­ênÒ›Åbi½õ!¥¼‰i.¬°:QåÍGiÊ«‚âÙJW"RFžü« oš‚}[?KÚÆ:{â' n VõܽhÒ­É #ÑÓl. ·‘kXN[qÖR3H¹ׯÄ^Ð5x ⃚ži®âÊVù`Ï,ê1"ìT¥ËU­ÈΜØV£2ì†S–›Ô\¥œ+4Æ'¢Ñ\3Ï4k—dz9’I)'·G5ïà%ýV%½2Žþ$%Ï2K 3˜$T‹gÏÔ8„ºâ««2tÑYßêègo!ͪË2êÂ(¯ÉZ)`›ecIelè…6%ïN~êŠ1‚æ¼#üe‹ˆC†;໩P’óàè¢Ï= ¨hz-š’' "ä`A¿ÿL|=Rh2ùH ²£ G\A§¼šµ28‚Ô®ï$ü“€¢²Š –.¾ÎÁfêf~ŸàƒTLž—Wâgr³o6tͳÖò²;‚ÆÊM,PÏN§JõÏ›Á}[ùr¼el‡¡kó/ª¥éQ:qùÛñ*Ó"(c"‚ùâZŽ5³ç· ƒH‘g­ëá›äÌüÛåh|´$J!xL»_{]¤4«—Žç>Æh¾râÑŠ3ö{Ç>…’?g(t8èc mØš$Uø¨ùy¤jIý³¢ÖMAŽÚ"Œ‘ƒ?Omø6øÈœ¥ô—¸ ß\/ûtb7Ë/3=ª`6dä’p³Tm<É ÔuO!¸ ŸX)åõ–{ßÕî[dˆÔ,ÓV".-¬òá¥FD¥H‹P=Y?ú„ úŠ^º¾Ðw¦GüCˆûN�a°)» ›«Ž,¬`¬m4fUl¥%Ú&32vâilMhŸãtù¤}ŒDnº¶èMÈ’â 6 æ_A¨‚hGIÍyÂo{cV×ͱf8/É'auª”¸‡{ým§eÝôã“+Iü[býQòÁ+"ÈøÇª´° ±A[ß¿D—H�V§ÿ~°+>DDɹ®À9÷ÙÝLÏèOjH%ÉPN‡^jDwø~j­ oÙoL¿%Þ³t…!,MÚ0³ñûQèû²Ø XªôhÐ 408ÖŠø‡@¦ù{}{ráC)¸#6í|nRÅûµä£Poðÿ&ª4¸¯qhg;U‚Éxü_µ/FJ�Q¡ÎÕŸ©“PçlØÑuvm®¬'‘ç´øÃ3ºùA‘¶„X@³^#¾nÕ-& ½­JÚ‰ßj FþÌO… +;ß>Ã<w9$êÇkq5±*ë¼"ßå¡ðÒ7:ï¬Ïvs2 ‘®›DZô?1ÈÀˆ ã^ôEÿ?‘¸ðƒö€%©iÞÖøˆ˜!€± žØÜý[8¡ ub 1¨u…=¥#|1Ù˜ ™¦ï ÚÐrûîíÍò+øPªfõÈqÎwÇ×N¤ª{É}ÊSæv_ò÷OT§Ðœ¦&r~oøLH¿õÿN¯ã) \¯ýõ3WC=ŠüÆúƒ£!Ø‹‚œêðpïní³cB 44/Õ@%ÙO'Oèù8þÅ>JÒ?€ÊC;t÷m—oÅèL|Ô�¾8>ÅšþÖ“5^€3ZîpÄÊE!YnAÁ(e‡£¯øÓPËä"`ð¹´á—"kül;^"íÀ™Þ‰qKäH`ÿ€@³›Ü.mçó=ý„ “p€�¢îÛ Ópzs[žÏéBÕf7n_‘pädøÄßTÌ€BuýBY‚=Æåà{‰hŠds27†O–ÒbB$Qîlz'üÐjÓ[irà{^%:Áð¸4bY¸‚CX²(23àÒ´ŸKÒ —–0Ž·I»«X²žvs‰l Âà öoÛɸ”õ~X“q 4H%±ÐBüìê°GFæ`»sp•¾ 4ö2¾Ã£Hú2ý´x©Óî ûA»N~ãºÚEñZ¾ž\ÀÒ&AíšSÏÚ”€jÃöÊ—¿³zmÄm0{e'ŠVm‹7ˆ •´­H›ùÄ…Ít£Q G•µpjÁ¦Dñ@%M,œ™ð¦èfÅr‡ÁîcFßפ΅G.X“_¥—6]¥è�ÛñøJdÅcÕÇM¯ŒÒƒí<]½ºpUÑý©m7“Ssì¦ æ¢­<¥A@}U &¤/A»T¯Lvù„>Ï|'¹‹Å!Ñ;H¶ ä©raån2Ž 'í4¿>ÙÜ~?â#ôÄÔb²Ô4+Äo«´Ì®ÅÃʆ'ÜŸa[÷ÆcKö\Ñܹ—=áqrÊ0f-0•Ns jžã3DóîMnˆ9¦ihRãUþZéQ„Öa@«°7>"�û÷Á½×Á‘ý2¼]`ËTÅ-H‹è˜Ëç³ÔÒfb^ŒŽè»£ "®‚ÞîÞYW~˜fT`Êÿ_ftßÜË‘?øªz[$SÏ T‘üÜj2&–Jön—ï 6ÿDõIrHêϪ° 8añƒ9¡™8@sµ4›X¨}o–[;úºï)fY`\�:8gÇ€»¤±l쿈_Ç3ÕúdŠWvfÑ3i :Ê—6þÊ&6£CÈdöþù‘IÀÓçv@ÙMIß °vβ®-e K×$gÍup4Ù†úB=q} ܤ‹[ JªnVÛΩ]3'bPp_cã|tû«g²…«úZŽ{Á¬¸ÇÐ=Ȇä9] nÍÏC?i[¹ºâU$¼¦eÜ †°ÙãÈñnwå¦:é©ú솿Cý•ÀÃB)w?b@ÑMÚ/¾­ä_Yþˆ–ûðVoý},o‰a¾J‚ˆúëÔENM¦¹Ð‡kÉãE€£îUÖþæ\©AKn¾³ÍÅ‘±0ZÛp+ø1µ?ì ; ºs›qQÜÕW%?"Ô¦p¶‚n-: ¼”DÏÉâ½É/h‡Ež,üíDúi3º©:– 1Å`øE9 ŽhÞåFªßšÉ¡*྄f ÌHòqÂO­„UZü…"”Ç%;⡪k8&ýÌ/Q¶¡Ý_7áàƒ¿(ÞÍRç ”Ô¢ñ zZElsú阋c®ÊMë|­„šæÍ«¬ä§­ˆøÏ–“nÌÒAQ LŽïU ô„ô5ÂV!qkFï*óX+@×yÎáǸœ°WGéwa.CÒ‹“ý ó4Þ캩×X0óæ~ÔÙ [Ãët'A^Ó`‘ç$9¶½¤l‡«ïÅ^.¾¬wâ&D"Pë&FoõÜu?¶ÈÒgŽÞþ�Z’óã%g¬IšÀë>76£,±s·@ƒ¶K?RŒ)w|ìFÜ]Øqö.F|l'Yû‘d—ÍÛÚç¬ý8jVBHùééÉÃFJÛ"£êjKÅü6€—ä ðå™®ü—*Bð ¡5Ï�ªÍâæbøãàLi¡£ùq,àþýD_ ï+mãýNòàÒ“_Ç«¸ÉŸ7ÁÄ3vŒÁèG„ÕÁÓ1>JaÓÁä’vwN¹i›¤8}½J©1LáX­4‘WQ'6WÇGñϽþÓ3ÔƒDƒ‹úf5Ç�ÞÜ?A‡Èñ):½+ø…ÔGñÿsú(‡ ùkw¯W1·7l¯ß'è¼uDQžècÌ›µÙù§p\1{t FXDŸbô‘ù­æ;—;bDg(ê[”Ë[=„D•#>~ä§1?ú–ís?V&Nµ·å_Bš_œÖÅË®åw;.iáLpÈÖv38¯&FFù&{¹ >?ñ¸N¸åHE[DwÏ5ôé'ŒYc‹U¤ÍgLV…õa ÞËI(¼Æ_Ý1Ó' 4ib\¼U;ñe‹]Á[wé=¹¹…\ÕhÎCh6-ýakø¹b$VëxÊüf!ýنț|úÄIìßsÈ´UyË„ ë$ß~×&þæê5g«|F¥Ú yˆÛû6 ϺÁ/Á¤÷?j/bâ®þ)ÙŠvÅËž ÿƒ¿Ø•j/PhMF`ƒD‰²¢pê.žxm¾Z€’ÅeÍ{B¬=ûa†£ ¦•Å`•.c§KmñÝÿUјï ¶øš|XÞ|]#cC\Ì೜’ÒP™Å¤cþ”\ßé²ôù>þ¿>}5wÍ›d‚o4&ƒ£*ë߈ 5ÕŠËkÕršÎ5¼¼;jFíÓvu;º& Üú‘®ÚSŸD®ÄêæŸªOB}eÚ‘QBK]@ä”0.‹g¼Aðo¾-üã=öM5LX~©òGWËñœÿQÈÞÔ·Zò�Xµx‡…id ¾MWÇ!Íz‰-PGRÏ>°“šzéKšÇ‘¿CŸJB8ß�¤5°b¨c_ÃÑ´ªF •ʤt*Š°Öƒ³µ…?–ÅõS—,¼QKbéyÇ…)“HÍÀAÊd ŇÐ-B)’™ÉUì*Dº¤|ºå#U“_´-! hhÀs1”¤¢-+UwâÅb×KCxð,Q«þue¢«V¢�0h÷òPgÕ³)ÚÜ<ÕUß ù,x+ÀÆÆá·êÒö*õx@7ÅÏ<zʊ籋⬢±`/Ÿ’X›rT Ñø9˜²×KUáØ˜Ål]=KÃá·¹˜.„í âæRdyP\´SFiko@ÎãlЦæ)5,ó"ÍÏœý‘ØÔ"Ô[!!± È¬¶W!N¡=>ú½q‡TÞSéˆ.§/ã„yÀ-yEÖµ…Âí(o ™«xËŠcëÝ"íu,²TŒêÃëèj‡õj²ö@fl䜂)­•1FÛ^-{Э‰ó< -¹«ÌiEÕŸ¯8š|Ë=„NñMeÿ„›QU‘lsÂD¼1ŸAZ<µèe¤ìåR"uƤ#-(^Ûï8æ£'¦CèGŸ$Ù–ùtÒþälƸÁ1Źßí9`(‰Nš¯NËêõ$3q[ØÐç: ˜'¹MÓ±ÛÿöjoÞ¦‰æ£>¸Aê!d­9•`"ÚJ¦y:Ér_$ù+H,Ö0&ôå¹wú71HÂ'çЈ¿Í¦³°çs¹@xïìüçGÃlc:Ë4Œ.n@kt°¸ªê§Ë³L¥ð…1ó=¾Ê¥íÇE\g×¢ÂÏ4Ÿø“ŽÕ*\¸‘ê ¯¤H8L@5K× gÇßUyþ]Ãé— V0ZšµjÊM°8OÁ¼0‰1Î4›&íÀT%à ¯`ãvh IÕfõE•ÚæbQØ «j{x´j~^gJêxTÏ“x+1öëÉ׿;$Æ–öüH]–Ò›fÔÕ¥#ÇÏË ÁtTÓóÎ6ºy^^mnsÒzè…ÜY[Ñ&:‡´3ò~Xzˆ{{°¦žzuœ†|fi例ÔüééÔCFNÁzÍÖר�Xó¨|!ÎVcÐvC9!§gf9ªÕ—£†ÿ„¤);€p EMe`Þ•e²‡w^+&gDµ;dUŠ…øö/1>!z$ÀâwèJxߟKVnq´Àkµixxa½SÈ{&îѧáJ–ªRp-Ußö9¾ÍŒ!?2<©ë8Ý‘ ÞÝ9-ä +EÓ„SâÈÅbþî¤&HQ*®k¥jŽ,ŠÈÉ©�N\Jæ²tã£*í2—d~×5ûÝú„ï}=�hBž z Q«£>–:jœ™fOW?·î ÛH$êƒ(U†iÔÄ} GyÂ�Àþ Vˆ‡”�½ªÞàHÄøzª²NT߈6?¹–N/AbîÓ§±xU4‰u´ÊÉIÃ&aŒ.Ö3>' S ùÐ…ºMÛžXŸè¢‡¿íH$7íï@j6O}®¸¬U„žh s¾ºê[!Äö 2cŒám‡ Ub²®è á$S?JK¬À"Ôª¯”VtasÀ4íòùöŸ«J Oî¢àÂÀMd¤6'}’‡X~7¸Um²¶î‰]—±³à³YÜ_HJ¢sÝø9 n^™+í«¼oÉ_öõvñùŸŒ©~Ù ÁÍÊï–ªt£4ºÎ<ÒœiÜôÈ›¿¶‰4À‰"QóÀ‰ ™ÁkM"Jç)QW"s3\%@èVBÿO²'¸0b~‹ô¹³¸¢\›šQbÀPŠÇeRÓ‘X7aUdåÉSú¤<A¯‚ÿpQs'Ö%ô-ÓRÜQj°ÐÂó¨èì=mÚc˜õÈa‰jXN T¥˜-ó1{YúþVF_>âÞ÷nÚÐhhúF¡µù'+lÄeušÁn/ …Ì —˜ïç±3^eÊž³2Û!—P•I?6.ug,àØœc|²Î§ZzŸ×èàÓyç]—ó’íp¬zÆØK×ÞJ‘»Ïb£àÞ8þƒ PÁÐv;2H÷е ’ÔsD3è7 ˜|Þ}˜Çj‚0Ìb|¡ÙÌ9Úd˜ß=‡~›•´ã9|j#%¤¼1îaçH:.à¸vd„1[ðÔF\¤êiö9'H'u-aêý™:5DkÕ¦7Pö¸çGNVeÞ\˜ZÉêŠå ¸»×‰Bªì„ö‰RB þæXe7 äaÁ~‹·}ˆ¥õ’ù :ßò”‰Êº¨‘ Ñ…7ñÚV,¥#F­ÔRìé”í¶™@ÖòvªÁq3„¬Õ2”ùw€ýø³\0�w§m:ØÇ£ãK±ÆýºÇSìDÇɘΑ†…uƒÝ‰!æÀÖ—‘Q[Rïƒß*BøŠç×F–ãšÑqfmx°õ¼=Q}\˜ªÔ™(E=eÊjZh”Í¢Ìöß~µËj}$"ttq^=,/}¾P•² Sj}¥]ÍmVLЦON‘iÁƒ#Y;ëÏ·ÝQŽÍpÒµpa2Ž]æ¡¢º°'÷ûð6ýÉû¬Þþ—.üÕª¦!{ÁÂd™>àòÝ º4…I«„NÕ“âÅ#úP x¥wù‘ük?É•‹Že¬î] ´¹!åìûD§¥ ©O}N µ9ÿJl4A³~#ÿ ÷ya�šÞãž KÞ4!Ä3ÿ1c(ëý%Í8PdèDoïcœ w4Õ‘¦´©xé^ Táý5l…k!|‘²Ó.¤á’ç¿@•±ÆÕ¬ùXƒ^>`Œa-5rKÎñ RiZÇ0é~ƒ<šÞd/§JåFA<[qÒ©Cf´§F†LHeô—îÞ:ì¿¡9 H["Ã1BóÄ·9ÔæÄö1fS±¬zÿ9#/ìL·àŽ>Y=‰ºQæ÷úk·|)éìñÔtò)jbC”‰DXĪšË°×&Å3€­d!î׉ ¡:ç žŒð•ñ`#Qö)ž[üò¸ °…FKÜû ‰÷¶6 n*Ó—!³._{¨*ÈÇtzÊSêU HÔ¨VYÉ ¤oE€ýTh¢)ÚɆH¤¯|yá—Ôš~f:s(תˆ­<Mà!EëòŒ“þäaÚöÆ…7Gq¾ÜgE€æMbE¸D«\cîõâbS•Å%yÞû´*i"±‘ÿú3 ~`JÀ„§ÂXSF¹®iâ¼L0mÑL& ÔðQ¥Î ¶Â4¦J¯¥ÃpSW½ÓžXdºÒ¿îvÍ=@êàÆluÜmAjFÖä¦á¼yòóì;Èdœ‹ïœ‡œÒAÍË\êÖ–‚¹}gí9çƒ-›V¾È¬L𕻢Ïb!"atmÎW‘‹Ä;4yIL>éŸÈ£‘|ÂôÑËjÙÐlíñî½`œM·Çç—…±N+_ÅÉÆKs± À×ÒI8ÆW[¾æïò.œê+üÚrÛ%éMþ"-IÙ7Q÷ ÆåþH rÞ\¿8¤ò1¼Êgdä÷= #P–;–3yõ<UÆ6õÛKO$ÖåæÙlï lG–³Ãeh¿ .=ÌñV3öR—£ü É»Xžgªk³¦p]äÖPç¬Åt§63}Žy½+wÑË ÔÔñ³Û¶cýÁšy®’`Há>1 aFK¿ŒîÞY"¤Æø‘—Á©»ÍŽß' uÆCòk×Ä~;Rÿ 6M3ÐAóe‰•7OõÀZíqèrÇ6hPgÍULAzOf(ÒÜ´ñ莾íð ¬MÓ£¡ ¥â3©þ ¨±éÚP¿Ík朘Üù Ýù…{R4­ KWfÞŠ¶¤VìTYs#߉l'¾Ü|‚Ó‹6–OÇ þÓGszhSZ½†vCîÚì>E‰†ú°zCy?¦$è½ò¨ú˦3Ü‹â/²óM]³Ü]g jd~¯q|Ãö9þk™ûãËLÈ£¿T÷9M@ª]í\•ŠWç‹÷Ã|£ÏðéŒnýo$¦ØÁ6KÏÄ(¨Þß{ïèÑœÂ~Y>Ö;è9_ÔòÞy,•X|#`Úõr*þŠ^c`J›&/®õåõ!C|±ŽHž"4¥¨­ÓÐÆæ^%.k£õºÉ“¦5ø¦{0ƒï&jŸq~IH§2rhúIhw®ÁiNJ7bÂÕ")†ÅôNArX&Ø2×¹zv°gü8€SOŸ{é~oÆ JéE©¬Ç4"<j\g=Ìœø2mË2Ì9-Oy0]þ !¹óíñïÉ̶wú,H…ðC’1¬¶5ðŒ˜1W¢ÐŽE¡a×É_Á+}¤ ,ßE©¾Î®HÝd-€4ëÊPÐ[I†Ñ`>¤¨€}9ÛemÞ'–ﲯ¶µYždÞá®@§Jåbé@!s Ê·(F±âNKVí—IüõÓ:gN±^Åm¿(LW°9QŸ*~éR`ýû¹$DÅa{\¡)ÐÝ B~5ô \õ«¹«<d½urù çB%¬tñÒÔ½W&“!©Âye›ÖšwÛŒléB¯ï#·…ŸgTÿܽP¡¼ÑHùï hÜjy”MÚ40 Ô É>]²þ›q®éoùfŒ ÊéTÊ5.»Ã;AGô]Y‚¤ò©øþ“‚pð¬!xÖ8¨žàŽ×šú·f¨AæÜQ»BÿXámÏÿ(Á~âî7²©ÞÔ²édY½¥@#Úoh¥™ïDX" ”GQ”.9óêÄÝê „’ÚP½%‰ ÿ`Ÿ ÙGéeSyÿkm«`i'~–)ñ±lus ¼¸>k© ñ?Ó"k +Q²¯Úrqµ€¢•Îô »'†¥vŒª%(>â"\Nü˜À BØ1†c€þ¶µåS ‹?ÇXüi¥Œ†Mc¤Ô,¶¾fòß DÀÁ“§·ßŒmHJœ‚ /Å÷BU_/;còz.ÉEY±Ã;—BØ8Ž4õìT€;Ù•ÞJ’ž!y‚·ú²÷B½ìz§WšÛªÉ…ʻαG¨‘÷¹’b³ÎJ­ÕtA©í·žy¨X!3ÅMgª2zžÅÕ­c=k"éØü’tÉÔe AËõþkŒ©ÿ°ýÿ'ËÁÊ…aèZLÁ²líy?ðø" ô4¾²k^S§ aÝL’&#ÕþÒò"q‹4ª^”ît®=µªS9\Rüj8'þ3ž AƯ'üÿ- :ZÎVÊ ’iØ%3IQ¹|Ôæ×æÑ¨ùl4´_2?Nev5머òDèì±Ö!).Œµ{p ˜¯t¬’òwæ±ré.Gõ=Ô« øŒM& ,ë0gE%<§²ž¶o¡¬»ÇÀýÓ£ÖJf»…TÖ$cµZ±Ö­^+n6,ÑÌΆO((I¡Q'(ý˜hÏ5,‘VgžXc¤žÝk‘Óß-³½øc^AæÞ+5y\ÁéXfÕŽ¢ü_ŽˆþâÈåñVØXbÒâZâDÙF|ì7À§á¹ã›[{±8;V›ÅÞå¿Î ~kSûûj²ä °KÀ\\+¾Š™Á¼o›÷³�A_ow½‰Áx Ub24vøÒ–˜±$) ûI!›#ŠoÁx?ÿwσָÎÊ;Î`­bó:à’ÚІ÷œÐ<½LˆžpŠdߤOÄÔBýFHEªœÛÎÌG’?/f;y`Ìûf�I‡GˆmoÇ‚xxÕ—ž‹yrN>`µtU‹(87øRlO5ƒ+wSÓ,X÷Ë3ò ®AEŽ©æÆÜR„|žËöËßõGÂtž×7·”ÿ9q�±’ȽȺ2ÿ=iãÁzG$Ye_P)'I)ãVá:Í[/0L”Qã3EÓïý¹Ê°9rÍGü’Z‹;—ÊLU•©%ÄГ_z4p&É aÆ _ëá"¦QŠ“½ËÂûZ0ø{ñ <˜ÚŸË]1ðbFS²'Á$xÎQ°@‰í1~œ¬Ð~ õ“îE0‰¤*3ô¢–æ(ð*«áÖl •úKßNKqf­ô$ôãµeѱf7÷-ršæc†ïÛ«ÉHK›Ú0¥Þ`ƒ?íuþwÔ€”é?—q;šŠZ‰¬Ëϵ DŒP–Î.l³Ô}gÓ1iR ·xmí¼c)Í–¢=ùçÞžl)êA¸æ1G^ÕÏÓÊge´fÕ?¼ã©•Á¦{ãò(ŠIffaÖXh+%R1~Ø(åᘳK‹�´ÜjÑJ•a8 êã«ÊD=röÊ’¾eOáÁÔ 0@—öVK †âA­TkØÏÌçMG\«'©wðý»º.®Nß?IM~1³á% 7Ì^æf¹¿ô\†RªN=§ý7¯xCx¤“_Åú¹JÌæÍÀâž»xÒ©Fõ+RP€­¦^?²®µ— ­L*í/P| p!¼ˆÈë:¬éJ«rÍlîG®nÞ=mš »+Òw—Æ ‚9¾]Ž‹Ë—ÆÊjÇårÂoƒÌ1 ìÚ;4›Ï=ôð¿^=Q”ô t§£¦^D%[æòžO…5”"L—“·„À~Ëó*ž×¯ÍwØ4º¢K‹0©TX?²WƒgçIèÉöì½RmŽñ-jŽ3¶>?Jó–Û™Iwþà\!Ú¾P/,Ž»Ùͼ ûJI’‹ÍÑk$¤>lf>ÿ"s¥NSyž q‚øºSOtêÔüŠY, %×òÝ»ÔÆ2vÞÅ>¬NÞfË‚‘ÈŠ](ܨv?ìGæ[’‡L›Š3Š…½<bÇ Ÿ{ïns񮄱LèWj©XýˆPô­Ú?I+çÔeŽªß$ìše…j‹Cú”@ ßž�­òíQW&²œ²sxµ~¡¯‘”ã”îí7™ª|® Ç#’‰C°p¯ÛI‰ÙÝÊqå9‰É©Ÿ©óå0Ñö¨†B=7‰?uÝ'eÁ, òk¬`¯ 6{î/I”zTtâêß·Ï£s/Y�D»ï~ÁG°¿‡RDRÞÇt!OªKòq@ð«PÂÜ4â€ØP­§ŸâÖäptÏÑzc„=úž‡æ©ØKœ˜×`HEܹ$4ß2ˆ¾ÜÿøìÒîDü4µ 3µ|½tÎí”{Ðw{ah€uŠél§1byÌfqa¨Š,VV«6 O.€q]wR€ú¸ÖÆZyosh7©'˜Ë&Îh¨Ið4�Wiò«ÈRÄìYrѵòKŠf¿ ºÀ††;|Kl1-eèDNä°*žù‚èéCË�61 ϼŽÓþz©ÑÜâ¼™üA4“²}âšz[ס¹Õi ÜX¢rÞLðšû 8ÿSÀpkúŠ�:ðÇð\)çÉž]#¡¢X êéÎþ»wûÁƒDH ZiªÈš$Øõjˆ¯¯ºÖJ\Ÿ!áu犼—©öŠ6Þ/˜oÚ*Šå,{²ÅF]“oÌži,ò“’‘pïû)éyÙs4ûàÄ›îßxBe³~Ï£*¬}_öÃ0v}¸äÆš@?¦!)ýWw§ïV²RMôÖ®´âÕkĬeLé6sý TÃø©í`Œå.óIA…cZÅɾnBÁ4#6›t.Bè.ÀhÆQÆ…¯Æ›ŒS ˜7„iõ¯Ëiö–›¸L›´ÁFM»$äBÕ‡Õ¾y{Qg¢ÈŸ{<í—,šAû’V‰_õšÏðÍPÆ5¥£ó@!lÇ8™\ 9y£Sn^¿³›]ÙÞÖ ÖÀä`à§S΂ãsx¸¿ñât%–‚êq›’9žn1ð2dYøˆÚ]¬š9DC½õšÏýYüAR3dµs x7"^NçZ÷©Ð”¾ê%!¢ÉR\¾ôûh(ÒÖíñ߆¼Þ'휠3¢ÜzE~n5!Š©ãfÓº¾N ß c n@ÑÄpë5 õE±‡ÆÛgÇü]&rÍ2›6>j„¡ñPt/tx)–ªþ\:®œ/EîRv÷Eþj\֜ɋ ²I’,ž·÷7V)©ñ�DéøQó‘T©ÄÏ„-o&ñ Ì_s�Š‹ƒ±“Þ?IcȮͨ.3Œ:÷tàÆD€òíp [—«—ýx<\,gÝÖ“&ŠoUŒÅmêµ@Ñl)c†îÞ-ć!×qä³ß)CB/țʷYü<º7D=VògÌ gz{1  h«q›‹f—8 ¤õäPÜå™bÆå?_— ûPè$õá˜�Ò‡Ö í™%®J@ :œ ÒPG¯B ÃW2ÁUåòÙ쎮x¶‚¾(Ž®3`ñíß »ˆò÷Ü)ɘùã?ý$l­-I,”› =Qü}ÃÄ‚XHóB!©¹ò–ã/ÞâëÒG“ q}»“ìi&ÐÔç#.š "g°A*­É 6nRÚ£;¬Ý‹Kêz‹2 ° 𿺰ê‘oz¿Ì ïìEr–ˆjxÄ…Ór{¶†Šö‰Ý0ÎþÿÒþ»uâB°Qª÷zÁ¤à¨pÄ‚ê犿ñ0¬ÄNL[~Çá$¶ƒš¹). «Ä1þçGÅ2Ó޼‚¹™’²àN²uŒ‚¨òÈä;_%äå2_î°sn™ýbË©YÈÊ¥”{ ÒD¡Ñd|¢ÔˡË]$úz‘9XsGì_–KLÆßÅ_%uw‹ÜBòö˜£X«2Že YÀØL5‰Àø³‘ºZxãœúé‹Ñ×…m†²Â´ðsóÕiŒÎu7‘¡Æ–̦L£;¾}�&÷LÞ¹öGÚç[‰xŸ…ŠrWé$+L`¶˜†UùKÿk¢Ûïæwþ°®uÐÛ®ö¿­Ðè¶$SЪ]Ö|€«uåþzÚ%=¹¼I–~¯#âæŸŸb# rGoþë(öá¡nQr–¼\·õëùLþÐqH܈æÜ °±O›…îO™à®0ë*–³É§‰_gõµµÁ‘\ù 6ëôTøô9å*¼Òã7Ï+O)Ì8³-s&É)ãšd®aÂn;ãÄ’Vઠ˜sÜ¥uQdÃ烤ï%–çPÞžÒùŸuYO”]¾ƒUKï}ûý8ç`#!áÖõù\íÓuæ9”=(âåŽvgAFIRCt¥#Ï:VÆËÃE‰g ê4”¡«« c_)ßhÚ>JéªNÅ!Cn‰é®~–®ð½–zH“*ºvÞ`ÔÝ�Qÿ{Ñö‘\î4Ãå¿· jع«X ÝàR·œÀRˆt(@]ŠÃÛ÷‹›àÌ?ùìzҚɢ31Óö¥Ó,(/Z¿ZS.ô$õŠ5ž”ÅiUa[îv¿¡Mnïi�csk«ï•SãMÅ­éȤøC¹…o*z¡©×u÷¹„{İŒÔ_Õ½ Æv¡#÷ãEõ[ŸÌ n^\6¼Ð'¾5uGש¬¹T SE?™§·•]’iŽ}ØRùÏ•ˆf€>I6tÁAL’÷ÉEHÖ}þžáhmjÏ—óåuÚØnÆ ƒ@1\3ÃpÚ‡ˆcn–'¨2}˜¸=Ú¾ö·Q?Rxœ€K·¿L52Ì‘‘?¦ïóâb‰îÓ9yËõÕÛ-sfc¥}²ÙºŸA‰å]y¯·yQ†Vó‰q)h‹×tÞnTæsïïS9nõá N®÷¸¶ï´Wê_BxÎý‘¬¨Ž%VFŸ Vÿ9•ÕÖúCŽ"}^HÞÓ Ã�Aþ, ’ «l£Ü¤'⺳Ã&î26©ZŒ:l¬7¥ ®]O)ü:¬"ž}…ÑúÏñ!1›²˜ñK™I”?Áè:pÖ¡UÎŒú–à?lþ<š¡NLü„ÿ?Aô öîîè†óßB»j+Ý`›ÛôSktG†¯ý†‡>$ò“•¨¸³ =Ì1R™‹«e8Ìa %ŠüÖ7Ф�UÃÇvQëp3¯…Uõ2GÚg-Ÿ´ ÅMÓ¦î$ò(U‰º‰cMo¿7Bk„/”oGŸ¼¢™z¸SñCHÙC‚WN³W>‰Ð°¸\ñd’ü굜ˆ=>ÿZ×C©‡�*꓇Ñik¯›‡éÚTÄKÕ5F^=ˆ&n°)“†AˆÇ+>7n8R‚d.Åx‹þô]±CŒ­MŒÂëR›`1˜ó lò¼þF6øé*Ç@þæê‘äákèã0)(ÿvc áµ(y9få¥8tõÛ®9ùÚ‰8\ˆ@>5Ρ0ƒ“#Þßyl>U:`›Lâ£l¸Y$KKŽ">wÈò •XÞÁîóÏè®Êáh½á–— ¢Z1Ýþþ¢ã´b!@¯®ð›& kF„º­7‡g™=-}žûx+VYCO<ÁCvYOîýq“aVÕùæ°”G¥„Dg°IÒÇ6‹R]tÓåÁ^ >+ÿÆq7Ëô&F¤™«1û5SAYNB V`å"Ï€Ù:Ëí*î¤ì$¶K”Ã,ƒ`b.xë_çk¦Íc/¦ƒýëlT5’¼¦VÜv@eQ}%1á­„"´1”Ã⟘sÛµ¢ÝÆËÝ¿è7E`YCØÁè%dYoÂ3ã®>C÷rÒGÑÆNѶá´bÏ™ÿ}÷ÁïwCL©–!Yºíoƒõ}Ö Á¯¥àM?Èâ÷šB‰¬Eý¶íê~t°€ráo÷<F,Q«Ô©f+ B¶öp“Hdþ˜”ÛãÔ…Šƒ‹ƒR4Öÿ|=­=ü‰”M;ƒA#ó%ŽÒl&N{î„H ù²Q¢Ù¸AAªCnêS¶¿±Çm½gÆg«7v¨.+†{”®{½0Ž~dY+Ôbô¬t¼£)”Ÿ«¯¬ ÷¹6st|ŸdÚZåRÈÑp½ÿ~ÉA¹~ >&XÎ&Ü1,ßÛç.è=Üf©þ6ðcaƒ9·&í[Äë_‚è7´–~]˜¿¿ÁÄü mV«ò•Æ·õè£Ý/¼Åê¬ Oª³£ããè..È‹I=w6h±5ÍѪ«¦½ žĘ&á&VÊ_tr­Ey·5wÉн<%­ÇÆf±š#$vu€$ÎRO“ÀŸî']4%PÜ™e÷.£Ó eÆi–øGÓ)Òh"2(!_ÚÄ'ۋѯ¾ØÏ§þáKáâÎs6Å/Æý¸©œš…Á±×>Ï£¸'ø“3ëò³¤½Ù¤.gÂ$±zm1(yB™ê¦ª¡©Y¹£o«oÏ_-s·6%¬FøUû#EN%¥ûM³ÆA÷ñ§AÏØM!ðÞî2À ¯¯­zXÞ¿yŒ©óm v¯Õ’iejâ°wiæ¦ Cî@?ã/ØÇÈâê!Ô0•1c Ûˆêþ`òTПãUw&•? �$[²Ëð±›óê­¦ÿ$áçjh¼?ÿ%™… %±NâÙôœ§M«¾ð" ñ²\ø÷ðZºk•Í€V0dÖoFt¡¡À¡½œMõbû"ô jžùŒö$8d1uL:NÒ7S -s ,R�7Å x ÆŠãÃâŒi’&aS “-g[ä ûk]¨ñ‡3¦Oè¡?¨1uëŠzoæ™Y°OmÎËpÇ¡§ë[g­@ÃIÔmYßMã‰lÉ#?5ÀÄ»Ü ¢|°zÏKš‘#›­!`Ò»Kp<6B*lî ÈØHÿ,wI¡ÿ]û/Í\5Ì1‚ë‘hï0½}ÓMnßÎ\5ÃyDo²fx£MÇiih´ óׯ ÷nÛiûnWhV;„b÷&Å*:+W‘ÈÜ®[),¶Ãýâ’ª†Ôìl_dÝ5·_ù#H^fÞý7Ì?‰HÜg¡5šÀ"~_Ê©H±ÕÎZëü… };Çãzd^Yç%^åÈ¥è|Lýdd+ ¦¿­�ÿ4 —d­žÏ—ã܈žû7P`Jr0çÙŒñB艙µ±öPTëé_Ñõym\±Ñha]ÏiëãÅ.OÞ²G™3"%î¯úµºÄâ`JUL¶CïkÿŒæ)ÑÿpAïšÀ¼Ï…G/VŸz·Õ‚ $f’tã`ìˆ%e ü;K }ïÙp!Æu  - K<[’rû9¸?u›ö‰Ñt©g€-Ãa˜ËçG“ïžzå h +Ë�Ëæ@mîÝ ÇR|¯þ¥9%ŸaÙ$Éëf LR>ï2ï·"—Wq0-âÞÑ­Ã?Òß#üz ¢P3Ö5£ü|+ :™¶½dbäÐ ¯‡Õ#¦«ÁÞèCû_ºûÂǤ˜•éàcû¾°B™Gk£×ÜùY °¹´Üé‰ÎÝ×Dj4rR¸ref%š‹,sZ†‚çQ)d!<œ;h¥Ë=Oõ‡ò¾ª÷ööÿžßi'íé—ö÷¡ü;„þ oÛ¨ÿ[ÚîýºËûz¹~Þˆï«P?mëÊ|ú7ööÃßn™ßi$ùú¥ûzo¾Þ¢ÿC£-}Zyû{fï·e?‡V¯ÛÐÝöë=öè_õWß·²_øjUö’_VÉ~Þ²o·«á»Ûöé‡íì§¾Ýaþ5?nôO«JgÕk€ö¾†$”íHoÒ¦ök(Y÷Ä»Uºðî'Ç“ÛÏôn—~vw±~ËÖý¤¤F¬½b{'ªP3_Ͱ7~ðD‚Ξi7.y(0œd»»œÄ7Š© ±íû»$jO Ô½Ñ¬å|èÖüÁ�ΰ³ÕÇ•ŒÉÚŸRתº‘�›u5/Ð50ÖýáU%zÅtí\)¯™³º7H*µ±ÁöÕ)>+¨î{¯fB›®ÊQÏÀÊ콂‹n÷òÉ÷ вâãœE¯£O…ü<^•?Ñ&PéI΢’"•–A¯ï9ûñÌÐEá¢ðm”r¥/†|\¬}p†^²“ÏWQMlª6~w™'¿&Á‰P!µ@þ"r}>ÜeŽó½‰pðfþÜ%¢†¯-®'JÛcP§ã5ìLž=6?¢tŸÄK38„S«‚ÓƒPq­7sLd‘‰¨ïÙòÏ98¯—Rò6•°Tå Ù¿fˆ³z±™ üEK7€ÉµS<[:NøÝ…·&Hð]wÂiö âbŽ~ùÀÓN+L"Ò)UúÕ«)—CÖ¥qqÙfÊØ·3­Ö †c5Ô˜§  J³ßzÁMHˆ!ÝšßðÜ$Á<ÊÖ[ _ç9Ú  ¾3�×½–G›Ã—Ê_²,ýûdf¹´ubŠÙ“FÞ‹‡ Ðâ F-Eû]/½¶ Ï~NìÊF‹#7BíŸ 0?­7³JÆä8ú†Cù™3ëºá7£H1±¾Uåå�c÷:bô?O@àü.�hEæѤqÚ#3…8ÖIÖЩiƒÛ¸›_ñ”…±pˆ_„m-ö¯ ÓŽÌ<dl÷žšõ!ô{eƒ§¦à¶`‰ Ïœ´×EoÜÃÛj§ÛæN–Ý·ÐR•ê[-²gd”c®x êjö9âb†1Ï‘ºü6Üé7¸ç‰H.¶Ò« ¡§‘Ûà×1‚ija"Ö¢ÜÚöŠwh\`+W«yPæ™ql<ûæ‹ê rõ›9;Aý&þíOƒÏœ€š9í95Åxψ^ë5²a+Èû#×kÿ=KRíª•‹×µœ <èfÝ›!¢ÌŸ„Ìuwíp{ž*MÂËoÂjB1(4;Ÿì4òðfûg.»mÝ1�CÑeÀã'“>òçŠK–%7D~ÛâHGÄ_ ­R]b§‡G’¡)ïHi*à€}¼y¶+�Køµ`Ÿ:ê#÷Û¯iéœSª‘ ('ŠÎ«<Z<\Czà™@˾âºÚw69‹F_—´A^ö;cB§øŒOÞvƒàȘåò1Ô®@%§;wôŃö~ÇCh%[§¬z3”ö]ͱ šc>bZë{•P=¯É¡¿Ä/éûŒ°¤oP1õ„ƒŠÐCkzÿõ˜å̧ ·m®”å9& ‘f‚璘òG%�õŠÂ‰úGwÄ7|}f­Ö?+ý¥âõÝ6’á@FÝ¡©Â9ÊWyÓþÿO½YxGCâ­[#ƹœ¦ê( Ç*ÔŒ^Ðð­?ÒŒ_#vši¼LˆHÐvÿ3=‚³od߯GÛšˆ…Õ=á!CÁÀ³hý£§|{«]ª“™pmbŒ‡ošF£ƒÇp¦kÈáM O=+äLÕIµš¤ñ²Z ÈRÁJNÖt-»êW­®’sM¨& îT HØo¬‹1@é*þzEznõ@p4X¿øVI³ vÞ®_ðùX©€NrùE@g(E… ý`8ÞŸ¿õùskFï^ßû#š–:˜+Z¯ÂK8G€µÏ,uè­+Ï ‰!ïÑÛo´qE` G¾”¨PÁì/>8Pa,+s®î?¡aÞ×î~@s˜^•åÌ„R¸ùÆHVÍý1ñjúE¢1ÜíCÃ~�ÙlŸWÍÄ™ÛBÇoqwÇÚË�‡ÃnpqXt—ŒQ,ǀ˭ú§wÈ#á¯QC#eRørŽŽ3Ž#þØ÷= ëÚ­âHb ^B/µFr(»¬™ê’îMù$_£ºç°ýÍ!i!´öpƒé_f]1–C×¹‘¦\£Gíë\^ĦRÁÎÍ©­ïkð:šëؽè:uF®p±'° é!ÙíÅžr&Eú‹/óýÄÔ’5\%ÿG€M!«z`ˆU1œì L6én‚Õ¢o•µ²qäân¬èˆ–ªTN#½¤âµEì­F•‘ÅK¶¢>g+Ψûô}—Qغ«`ΫÎÎ_Nj&s ç#ýzmè*°ØÙ:&¹á ªßƆîS»í/VôÇ}övâíi˜»‘f [nŠ#ùtZæe1© qdß”•uÔÛzƒ9ú[ª+Û: ´TcÏ'a¾òžîüG`äPûܧəÆN’Y3ˆ¡°¡•ÎN$Ò´“ûy«'Ìࣇ„mU¯u†-5UÔt`ŒJ2È ¹.Ä#y³Ï=œgv4ýè‰]”—ù£ZSbg3Þ[„›Æÿ Òƒ®jËîç/¯á7ÐHx‘âõÓ2€¤æß0i”&”O„Žh•¹º+Q'Ÿ#ÃOÁIÈøu¢,Ã*ˆùÌQ:5h(J1&{ÔÎ5jk±3º5÷Ax¿çYv$Ÿ r~›.p:3vzË¥·uK³¿’SIѦ6HžsÞÇÕ-Çë‚©ðz‹9%œì( •øy?ù›Ôóu6D—›Î+9uÏœ%ò1å@kÀ(™&¤ÉÒg_ïX½ Ž]bò'ýÛ¾¸›Žê–±ÈL§²Þ�Ó³Fa =Hèõ52l:¹äíø댯jbûq©Ôo @¾ô`Ý,%@3Œj7ÏÑ`¶#h™â¦“¦CÁ¤àŒG§¸!ô¡÷íÉ Öï€wÏû’ÀÛÊ8Ñ ¬5̹ï2;k=Åg±ÿ|àC€œå.6_$x y¤u~Ærµüï-‹,X1í'ßOy-uhQ·-è#õ,“óÒˆ—ß§K[wSÀÅ®¾ú‹‰|xÊÕs_«É¨sNˆ«‚|M t° ü¨‡IûqŒqÛ–ɱ0Ï5€ô4 " â‡QšV‚ÝMrPº“až=õZ³’z®Ã9™Æý\Hÿæ¢ph1@J7â@:œX¿bM±wëµýèàÞ%oXû‹uÅXRqºúІ=ª@y±Æ5(»u„axuvó.“áüvªâËøÿaÀÿ?µÕP×^y[“åÉjÓï¾ûÈÐ&§nj%\põ·¥!áuím´”ºµ½¿ÀÝ<E91Ù¯LW`=ÁÑï+MåÖÜrBGŸ†+y|3í‹RÕœLÕ‹ Î`Í6IÍö—AºJ zþU‚§@Á’[娴YRG Lo†®L͹q Ôë®Â3>·Ib¾>!g<…ù“=.|iwá©t§-‰ c 2P¿öîÇËÑ#q¶)Ì=“ãuÜg´Tkj‰u‰ê-@%vUègÉîž*ÿÛ|a":“•>k‹h­wœ¡× À•ZI•ÞulÝåϧL‚�'£xïËV¡ChC­­™lê ž-)ôúF+ÝaŒ¹ÁQ¿Â?Ô'·.z‚åßG> o¦ÃU î«ËÅø34[˜4n¤-Ž^‹FR¬bŸ©0§ÎPïÕ[,1ùZ©\™·ýp]Ìê—ÿ.o¿®ÕÄòw†žˆ€µù±hp‡ZøŠ¹ÒOŤpl€Xž´ÑLÁ™ìé¾½ “óè º×Œ0 2fA'ª§?¦†ù4÷ûŠÓ\2cùÚ›,Ã�Õå'ž£â±îÖæ+%”ü_ù+LÂsòÎDd'£kôAÙïçHÑÚKqL ¡‚ŽÝjOÄîᔽ²µG»ÿ|7¯>¨ƒÚÁhVÊùÉÌAC’MDˬ¿½QÙ¸"2Ì¿ãwÓ›Šôsàã­5¤É'ìŸ:QªÍX’ŒTŸs‰uû’õ|òçÈ@oæFÚ„K©\ ¥‘½_%~Dèswz(™ÜHˆU¶>¾dü?Ýâhý¬Av' äÁW“´€ÀRí²ÙÒ¢û}îKAUU+Þq0d•yG'u4èþ)OVÕð%ÀFP ñJË…–¢Ÿ€7וÛùÓÀƒÒÃåý…{Þw!Ä44Ÿ6vº˜QÝñ/êúi ¹§ü”oF——Ý”tg±ä±¾=ƒêUO¨UU/n©`ocšhÊ Ú$û KE°a:E¥’ò¢óûg*Xÿ1ŽDá?&$wøº4yúy÷æ8Ò´¹-poV“€òâ�:üˆ±WeÐPÖèÝy:üû¿’Ÿ©u¡n…­H + wñP°{‚LÆ@þ?ôùÜμµãt?#³AáP,E»ö=÷[¨¶Ë£Ë&3æ'@#PØeRÐñR8mwØ2§Mªð¹A1gJztÿ\ìÜŸˆŽKÀW8U—šÜ¢y\{½Wºuúqº 878Q1ãèÌGr ,ÕÖ7|óm¼0/ßYòœLWâpЗ“9'Ê=¤8(0üøh/Ɇže`ê-Vš¹|„½8Ìžz;:ˆGeôH‹Ù OõßClê·ŽÉ‹tÓ) /¹YP’€Â¸{Lò`‰¿x’íd‹‡ƒ“tc´r’ôb²C¦ãÆŽÿBºuÂì ¡�F—ÞdG´wÈÆçùcðhH4_ç&v‰ñÒ­?ÄÎôg­ˆ'b@˜©oHÙJ©;I±uÂÌÝÐ6««ñÄApÒ>\Û‘à0njå Í©aêm®f`”Y©‡@,ú[(ö7S�U7«Å!bØí@[yW³½agÕl>>P¨ÙÖ_½\:Vòì:Çuº "«¡¼ÚFÜFîWÅX•ü„áTÖê(]îäþ$¨fuZáË2²· O[­Âá Lämvh§³Êñµ 2øVÕ°¼ºMÎÑ«fOe¯ýÓH}R·ÙÓ—PÖẽ(æÊY¬ÁåâáóxiÀö¼™Oé'ÅøÁ¾�ºžØÑ·åÃ?&Ç/©÷DõÍ'TTþ´%2§ßòô:,)Ágo…ê8¿±iyª«V¿tàïøŽûæäM9NnoÐg½K–Ý qž­†?{ +ã‚p0Î! »bã¢ÃľØ/¹+‹RçòªR0ãíª=­�k£ªwÁkùóo8Žñ†:EYYö…@%‚Ãq÷0oôôgŸ!:ðNÕ;T‰Û B¿P|k\šÛîåy�qèÚ”2mˆG¹VŒE¨}ܼ–Fn{_•w…HHGÐ@qÂÊ᳿çìê!ÜûĬÚí!ÂIvx]n W» ¡«s L†Š3¿â–&á꺨3fÚ)<þƒ~€`—©P¥ÌóYV1ü-홬±%v80»V¸ § +É(ü]Ô-ºohdµü‹”®öZ¯(Àjã‰e!ùnþnfÅÇbx ¾ÊÄ¥t[¯&˜åm‹Îï#÷÷A÷üÅf¶Ê�įáÍ¿›b'ÝÌËó€=€•þ÷´YV3®¾Î“ª%<ÂþŒê—¯Ë'h{åˆ{"›LY¼9xò9 4š}q2º¬¿ìZ5AŠb"ÞžCÎuüjzÓl`HYÁ¹Uù&þè¦÷äÒfÕ\_MÜUÖc¤Ò£« ÒÖI4ø +«PêñH€‡P7Åh“›$edw”2h«\ÿjQy [t,/:-Ž÷·Ø“ªï¦U'[ŸwM²Ù©S‚JD|E¹hæÝRò$ë #Ï+ûáGòMM ˆØQ¿|~èϨQ¶‚¼ üft@G4›‰è½“òðS@S.¹6³NеÁ7ûn±5ľA^e.ÿ5©³€DþXU$¹>¿7¯À[fUí°ž½Ç©û‹­*ñðØÎY³³Õ­9?Ö´ä çìl%`¯`<¢¶ÜߘÙMUº–\r&[²‚gìQ°«:Üîd  {‚"IÐXšêy3{z¾c1Òy#^ÊfC?N}¯ ­“9§7,èv_‹K’~âÂXÑ"|&î{ ‡ø¹N¢pá ÚÅÛl«d°Hrd[¢÷í¹«¥`ã,G%œø®æguaÜ-4~¶’%d}A²o~ÃÓ—Óä‡×ß½xK,×YÐÃXe 4Òb‡®p' —Ç1+c÷<i‘àP˜>†_õ-û@Õ²ô©7U©Ð?åX2-!ôùF–›Þd¾—ÖÓ =hÑÕve%ãyÿ&_k¬ÞÅØªòˆØõ³»‘+Õb#:mx´„Ìx‡Ùßxy"öͧňawSïÉ’d»;ªð«÷Ft^uáµägJïEÀ'ºÆÉ빌[ËmuMV^“ªPê%¯“â¸ý›Má_cÈSSò ë�ÐrÃ¥€@žÐ¾}¨’ÒµÖï>ó cÖ˜÷à–l¦¤‹x$^eSûÇ"ÌXƒ•xâäÁ“Û*Cú4p'Š&!ÖÔ³¨0“7×ÀÅÿMäØÔ?ü¾Þª7Ç‹«ù0]yõ%ç]|½'‚ŠÎËPêv½$˜æv$r=-Ó>0Q;zÜø:ø.æþ§wå>ì©ÆfiáÀʌg~‡¶úËný|ùÊ×P¬åâ·dƒQÐ3ó¦•u &îDÙ€)&åñ‚ŸïR×:† VË:£piì#xÞ8KÕÔ€qŸÏQ:$Bbfðzþ\Õ_‡ò Ó”¿ßhûÒ7úÄ~Bd”¢*ô´U®Yävr³CÓŸ7õ,ÓGg`·h¼hÝJBë}žM”wÔDJ¢¨ ²N󀞜yZ–³Ä>,_›éñ,üHc¦t ó!›s¤œI— 0ÀãÃwÑJ ,m0œ.¯-è"3{ÍŽiÕB¥”âîzûFÒEÀYõÿ*Z,v_õ«^?çÇwŸ¦—fG¬F™…‚s{kI¬ ¦Øè"sˆŸ*ÍŒçÖÀŽf&[BEWÑ+sìô|r·7*‹m¹è³†YžD!³&ç69g>àW(ãhå&"´ü(ª=ï^.?ÊnÀým!#õ’Mƒ#¡ Ó4 ”uÑ%}ª4ˆ|@Œ×IÀëÆ†Ú%GÔÍwò;²î9óÛi1¥Z]pk=JÏ«­rµ±¤ÍŒ áé ‡Â2wr¢ÈFRÈqËGù®q®>à–uŠr¿ OüïU¥î¬9Žá‘4²Ð‚ß¡ê4äUœ»¶¤Jrf˜]ôñ‡±úUxšMÂ7^‘ýÑ—®U”¨ÃõÚmÛÒÖD"kÀ•lñÿW£s·ªjl÷úû«`à‹qé-Hb †›ŸÂ¦°RƒÜ;ô?ÅÜ/û•Ò¿Ñ'¯Ò¡ÉJÞ \ - º_U(ñ®ã„ jÑΪ‡-1³ó}•r!,ñ §tµ26_*=Ç{@ì!N]Ÿ‰É™b T ΂·¤úù¿Ð²+Æþ]1áÉÇF î«W߀ËԚߺÁ}W¼0/ü êÀ?…Åó!ùŠû0§‚„êߺ5Ͳ&¿¹àlš�K¯•4‘ßn^:œaŒs‹v/”4ÉRÿvC5œÅ�/ʆÅ}1S4³ ½r¦K¿\ Å%íôBšVŽø<8°ŒJþdRIM5btùÖ‘lŒ©¢Œ YŽÙ4wÂ7ô+`“ÀÅX¬<E&4Ï r‡ jMò%³Ù’·ZÊÊ‘-Ò»¦§ÐÕÚ loz"eÿB´kQw8÷-PTµ2§ô®½;†ž²B¢£ 75OTá.è– ¼Ÿ:r6D¯ºƒ8¦° ”ßXŒ´ª—î|GÚ‰Cš±[ßÃ|°MÀMP#ÍäûžòÃ^¥´!öf‰jbsu÷’š™?—€X¯Ÿ£ù¬¢_ÿ»ÓKí .Ÿ0<îM »nFIÃGvÀÒÜÞú¢ˆ tK#>ÁS•;ëùB+@ ž"ô0öá4\Æß"i vdù]¦šÚ»–V†*XÞ‚;jjsüÎÔ•dý(›�‹d8»§]/çSây"ð+¡Hà"ú7ĹaSšˆ÷Hmw•8~"(s•¸µö”'—û§*dÇÕšbz*ÄOŸèÏY�­gy%R—0pYä­œœÄå(Øë‘YFÛNE•á›ZÐeÀƒxÊ’¬…¬õ—NѼ™®•¿ï4p>M4ÝLìÒ=¾VÝŽWt:#øª/‡ÏLA•œxÐ °É}Ö¦»ô‚Îùí¬jCî4#g¦/ÏàÈ=äÙêÀ´¼,—KV7ê�°B$õìõÑgç“‹([á¶Ñ¢²šµîàÙ=Ä’/êë=U~63©´8˜³Xÿ]¹í&ã7iÒš8üƒ…кºÝN#îI'É’A[דò¼¹|2zPDüÂ5Xqc¤oüþËå[ÐŨZïÃE7T”¤*ZÖlÌÌþÿoð?ïæ$³ë„=òý€;í…9–î±°Äùøe!ÍEú ¤Ìw8óÇpÊFE7¨¼º›¬ Ã8­,» ÊwêÏG€\uÞDãu=.Ù£¯ÀôÅ{†Nul!¡}œ&xhL¹i¢J±soæË’&cXçap[ΣýL*F¶(û›)ÑξóÁÜ š Yi“ÝvÓ.·`=&dö{ç©éb‹î<¸|h*HÀG Ôé8“÷¥iW|{W¶æÈ?×nm¢"Î2Õ?‡Ñe?Pç£Ã>ˆ>ù'‡Ÿ_f:X<¸³ˆ{—i}h 2 óöTUQa@Å~z:yð•’p'_î〠‰ÀœzÑJy4” ™|œ;ÖRà£Súl^[ª^¿¢Kp>-Èk•G‰?‰¼¯ûÜœi„«|ð«G6+ÜZЄ$Ø9K•Æs/¦Aa8›·>mNE“ ÔìdsÒf¢ðs *éÞ°+�ÆGÌ6*˜”Íã\Íë…MS;+ª3…PKB$œà·8pì„<¹.p¿‡þ£ÁÊíV)Ýqý”YÇ¡Z« =cp[ )xM0®8?ËbWGw¹Ÿ¸ðºÆ?GªÛ»tm= ã\ÇâKR‰Zªä¹h©·ÿ8n2ýáô®•]7¢ƒmÐih¾Iëœé(?› +ظèòGvä_Wõ¦Là×ÍÈ5?´°Hjƒa0'ûœjã4½ÑæuÖ„a«B¡¾åÃò‹Fîm‰WÔ\x¸Ž©ì®ÊâE¬oLî¾É,Ú7ôlKÇÑÒð«qö[º7~Ógg R°¸› ÅU¸¤|âqO…ù ¿ÙžUŠí¦vpÛ…Þ¯ þÐä¯a—‡Ô·¯;àÓþ)bø|Db„Åyä8jUÑI"¸÷ÆZ'[¡6Hx äøï¶q_dñ÷]3ÐÊ«²ôñI|zRš$uÞ¨o§ïË\»óî%=d‹§ÿ ¨ËÖ)¼@4,Dô³Úñœ°­C¥’j Î\sµÁü}¨¾ <áãaG2g}†»kè`\e›«ôŒ$]÷Ì›BÁ8Zw+—ú…Íü»¢­Ú®UA+ß’}EWQžáΧÎëăào¬2—)1ÈÈ×'¶gú£ a§kòecöÖo”K“ž5±œÖ¸õ»"°÷û¨×“ve¯9H?t¢¯9ÖòHT ‡â¶øx3´‡*~ûúÅqè<ÿqá4ÿ­ãôAPÃ>²Ÿ0‡v#œk,"Øoy·;2(#ùÛYÃfë`_ Âê\àîÄœ6ÁÜcÓïô Ü&Çž„Ë‹øžhüw{rA2±(Wô°ºôzöÝûÖlXC=?ù*’' Ò žÔØ’5¯ÿñý¼*Û÷`.Àú-©l‡NSK,@9ñwM¥R]ß¡Š�®Ò‚}‰ã@‹v ¸ä‰Z6#šz6U½�ðÆÇ¢©òÞ¡»JeÉM®ŽîÎmºš‘ÙéS[ìe�ç¬?«SÏI2cËØ[îÀ¸øÞ9×R„DÞ­n­À‹bà°–hÓ·ùv Á4,½`×DÛÁ^Yáµ5q¤Ê,h÷~)G>·Î^(WéŸÀ$2Üp–2=—�xДgT®ÿ)1|¢îœÞXîn Ø®We|“ |<`8ã¯rH«ÏØ'žÖ—mðQti m©þÁä+u€¡ÇëKQZ¢¶ ^ÓÒMfUßb\{üa„½~À·ÑB9;5ö¾¿®º]XšAsä$IY¿ô ¥¸ÿ.ù:(‡÷Q}‘À¸·».˜Š£þ(üTÇÀ»C.È„ôÛ°vºEqZMH«ñ÷öÏÌå…ÿ! ü˜qdqß§òºz¡‡*K¨2m˧0@–LŠÌQðÌÆ¦ŒÄImèlêöÃîÔéu(‰Kx'eŸñ¡€É«1Ù­g" UpÛ¦@ f£Ûgoœ…Û7ª…ÛÌ989°Ã›ç.§*%ƪ+Hw]¨O‘åòf.>˜†› §ºkEaRÕZ~å¼Ýy|^‡ÙÓO)áŒÞ„pTZ_Ðö2j‹¢&ú>faœî1¿S?l‚ Ò„; ÚócGâSf[á†xc]W‰Ú(ºê"mJzí–pj›W‰w+Z,Ä+øw “Œ¹§>Ôºp]xËHëœ(ŒÚm–"GE”NU`‘eú.ýÄ9áS@»¯1ÙR½Õ€¹7?E€ÛAjÆû`G'aE¼ÿ*5"Ï"L§YõNq:,1ÖGÚŠÚèå!FUPøÊýF“¢£d™&QÜÀ|w”ê»rcS4^Ùþ0ŸS@óßã–XZÊ/ #`d8tÝ|ÖÆrUñ¶‘–ÄQ~`jmªMæŽ\ˆ>s.™ /Ž‘öûÌœ©Tÿz8öƒ@ wmQÐ÷w÷ön�8[ÈcsúgÆÇY'[§_ÐÄ-Α- @«üÔÏ3èžDßvoо‡´eÿ‹Ù2^£;tlüšúž%È3câ=G ÙÌÒ£¬5A±¸Ëóÿ9Ì`ɹ¬1†±7ºì.yÄRTøÌA÷0z¤¿ÛúÜ"½×²›†n"ÿ"€_uüª‹—>R½c]%ç0°<Ígìª\ `V=XM€Ç@v…h%JÜÛd8¥< Á<ëŠb±TM3Ñé“Kèþ+ê®y½c òëuþZ¯(Bn­„¬fÞ[¯@N”ѹ)Çñd&Ê){UßJÞSÊxþÅy}ºêllă‘(-ÏÆZÖsv´tü»Í‰03I*g'C/0­—À¾sOóu©¬Î£„Tc4n+)K¨H±táÍð к 1`¨]zJk¯”¤é%ú0¥Üm?Û7µº!¡´y¨p/ÔH™q_‡ó«hYñ¨Ÿ§c3Î4gë¾ê `Ç[g_©Ci[¯^” °aÈ»Š¢Ç¶1*V Ü0ð-Â'ÝÆÙ·eÉQ€„ŠJcÊ´ÍÁÀÇÁ— JQ¹3§¿7²3_}ŠJZ/@œ>Q XF%QÚ«¤±¨øÞ½m*&Ø-OêqŒÖ—3§ø~j­ o„”ÏR[Öx–·×–VÉ0·F…†‡3 60o­´Ÿ+î®r©'LÄ´JE->Ò5ºm11Љ+ô äÅiµ9î|ïÔÓÄÑeè–õpYvéÑÙÈ/àñã–¬ôï$)gJJ6“üÕ Êv�Ù·ŸT û÷{{[–‰GX»m•ÆŠd5^æë˜²Å8LÊÀ'¼:4ù¸üYO‚‡ø*_¡•öÙ¥£ÔÄýpòÔèÿUð§BifÔJXyþØ>ºÇ—Ûoôu8ù¶sò¦¸TçFá¡¶Ô·:=YÓCš?.*eöEv–¾ÖÐV‰Àú {°Ôe§ÞÒùÜ¡Ç7O;8hÒ.SBqÝEŽQ^©V°òz} ®Øm`÷<h®‰Ø‘½[QøPªgÒ™™r‰ä<ûQèⱃénN¯ä\K-—Ýâ¶—=<Àù•§j¤VdâêüW(åë�x“`EeÍM-LN…^dÚpµÚ$óI&-œí ,1“ðÚRwMhŠÇN‰VìUí§©¥Þ²›>b¥Ì'cºøö ®– ãìŒõ=®X °WÈøJ!›Á</1{VéIKüËμ®n ‹K±>WÇù“ Úþ„ü%K¨qÙ›Sè:·ÛÔÈtØ™c¯`ƒÁ0a Gåæ–™OàÝ« ‘*ž˜!ÊM¦çêGùŒ*å¯{ž*×Â^áFˆýù²ë•ýÿ+¢mgK‡ð>ÿ"‘¢©¶dõøôé|!_êæu1üíº# 49”–Þ+?YÏ^² )†’ÐsWÅ3á=L©'yÙ£µp˜1ÝØ¬ÑT´ë^‹dßKpÿZ9¯«µDo¡ê5’BÕô{y¶!ýø‰á–!úéÁŒÆã&m«–£xøLéRº;nøâßç½³Jk®6¦¿©Ö‰ ¾÷òWN¿Æ&+­ŒP¬s7]¥í‡ø[Ç8=vw¦ìox€êõ¾pbQ]È!ÅŽj95¼'Õì0Z´2§¶þŸvUúL,Ö°ÏäÈ(ªêãүî›vÃ:ƒ`…þ£Nø ²Ìê­¥äÝë€?|tFC-Ù•1v²ÿJ#_NE¤: 6@:ƒÁ×ßy†ŸÀ«'㱫,'9[âG^îh?4FõÇ.´ìié.ÈL'VËâ¸g‡?AeÕ•K‰ÇÎS!.é:YC‹ôF“³ ˜¹}/Õc_L uññŠsom*�0ûβ% «³#AÆÅÜ壎Î5©lÍ‚µ~>_[Y.>ÛÒI¾â-âKe¨›?àqwº ‹nø¾“®_ö“§¸ÜÆ[š$NPhPÚ•—ÖKµ=@„2&Æ'DühZ\yŠõ”ä*æÍ^cý[êx[^ªÈI¶Ò;–{Qyߪ 8¤d6jj៫¨ïçL·Ìôrs2³{+öxü†pÄ}�h0aéü,÷§ãŸÁÐñeë  ¥Ÿ FG™%ï§ "ª8›þ!ƒsÊVæC?Qr²3íeÄÁ•‘d Ýã•dĺªÊ{òSgäÂ±èÆ˜¹ï;©»ý92´^cºB>ÜLJ:ÕïDDNí¶’hŒä–yÚÌI=ÕF”‚8€±Ì;¬„¾û»¢rxwT³K´õB/ogû6®TuãÖm xä9žhQKi¶s]ñ×ÄÉ.ÕœŽL^ï\a¤U0¥Edž‡85ËÍ™¦ f–VZYÎ}ûáò>®å…uldCšÝÓ¢aWùÁ êo¿¶ç´”èfd̩ۋYàC”zÆ\ êžÕEUÅCâíuš’~Ë+JÖžçÕs^‘äÑÍ$Xñãö"jÍ"ˆ°W¡˜â;q Щ9Ów fãÅaË@˜˜z#¥ U,inmäÕÄÜ[`ZöM~ôÍ«Ùöö‘ç1�¬þìü�­ üpŠ÷XRï-‘§Ãó|º¤ùÞx¹+Pu9HtqõÙ*¥Ò ƒ®4!åwÈa39M¦œÿTüø‹°- ´ä�„ãwçnþAÄ«k÷ó®¸Ç¦Uñs§Ì«ñ%.Âç÷S0Wåíßš’÷¯OèÕæ%ìÄxÚ;)ˆ]<.œ-az nIÏbà9Ê(Ç;}·úíZy© ¤ÚXä9ˆãßÍf„/×Ùø,Î7&¬ªøZOa˘((4aWð)+6¦D®è[ªÚ«_aæ}U7è¼úð·b¡<n©0ŽèE$ÌÜ÷uÅ,Ú«j3À,j˜Žóß:¿–ÒŒŸ”_ÜFþÂÄ‹mæàá\m"!·Í%w ¥èŸÕ>P|íëŒF'ÔßyýÁPø‚æ·ÜmBõòÍJ,{öÜ„ýËr|e’+äÙpôO†Û–œ&¾m¬o]YÑDßZVê`*Õ„×z|ßú$¦{¨uš’–ºõÃ(p®%Ò`ÐpŒlb†–õæ!Ðâ£ÑXËôàgý[29d漄SìÒ¸ Ø \™ ¢^Á hi(ñ 8ÞVuUç¨mì‚$Z|ܾ”ã—F¨éSzœ%bFÁN}ãôTo2„9g-àLi¡£ùq,àþñжfü©…ûä"u+fÄ¿ýÊv‡|(:ÜO$¤†{‹“’ë•”Ñ.DÕ%Õ§W®M¶Þù<}^ƒÜHVãiµR}iâ€(XÀëí°_†wØCM›¤É†hA7ÑFòmZ>-<ÂaCp…_È´Œ2êù[ñå¶q™]|;¼ú~ë $O‹>zÎ_CÓ­uëHq|)&õN@ Týo ‰«ÃÄ¿CDC‘>qWÁq¯³j|2N¬zu1òÆ\êQÅÐs èe¹¾‡÷§6E·#a°*Óì`Žwlé)]ÿ; :x ~Ï¿!œÁv³5rµ|n· ›z®gs¸m›'Z:Ôèù8ŸöÑ”®ŒÜd'Då·fvoüêUj(ªšÏfÚvµñš%Dì[ñ´ÑÑOоåÜÒ +D¤-—M+F9ˆÜ‹½Ò†?·Lóþ`ŽmÌï‹f‹äas¯}¥††m2ZxÄõá³ß†¼"ÆàJwÃ|Ý,›Ü±8 ö24þe8Z0 ­Ír쯈ï)v¾£¢&iV=’vjVN^­³øµº}Uµy|¬u³0q@¡@ä–ÚòÍWwº›ló-ÓGýû†뎼ìV2Bßê)§Þ?oeüŽqêΔCP†°�^ 4y_Ó3í—é‡$L &Ip="bÓ$ÛŒc/p0<¯øDœù¤Q‚5w2Љ dw -<ä-ïWqÊ쇅àó3ƒpl¹:œ-)™‰Š¿­¼$¯/BÙx--+Ø`Ç2; è·ëu. œ ˜œ™ñqê¶Žü1ú*hBô±’tSwݤFø P,f¢6P…OQèPÄr¦njÃíOP¢Ë:&·liMßSŠF2Ì�'ÈÆÍÈ8©.»t…èW£'41Àb%Oà©Þ¼¨º‚„î¬d”õAo7 ®®Ô3SÚ Å„Ï!N™ åm*°O6kN„|ê܉»ùëá ù*‰ÕÞ/U‰ƒSjûÏÄŠJWô½V?‡²µ¦Ç“ ½uä«•2¥Ò1h A·ù„òk”½Q±Ü8(|ôÒžëK7NãõáÓ¥€QfæZ’u÷B$|3iL72©Qd¶ŸM½VÑòГ»Ÿxú3ÏØ5 rZÁŠð«zß3KÔ<䈤,¸ê(ZTM$dct<MÜÁå²ÿ&úpØ‘�gA«i˜f6û\€é˜"ä(ÍøVÜ{ÃΔ­o#Ör`}ᯅG$ßÑiº=wRêW—»‘™Oõ†·MMó;ë4+É#Zþ"ÐNöü"ØÙùÔ&™"NYºežö棧²7sÛZÚhèLéÚ#aŸÓq";/Äã?bN×ÊóŠÐˆÜ_qoR>B Vêª~K–üSUåqøx–vææûˆ"xí’Õ×ò¤¬“²†Hþ^¶ ÙÀ/«¡¡)DtNêGˆ• %Íâ3O¸A)»©É©Gµ9\˜ÄdðOÈr¥;¨gI,xõƒ!ªÉŒÆjŒ-:¯9A'ˆM€9؉ùš’Zþ¶Üm7"/Â߃˜øWC`Hq9Þ¦\Çç!�ªx5Ë׫vKÀ[݉ 59_÷õïØä*:ï™A—2 \ªè¥à9©<ïÆ¥ÄšU¨– xìOBxI4‘`I[hlJ}ϩĶ+´ûÀ5*iÔcİh´ôÒCõœs×.Õ¨®ãõZšÝ*t[>ÓÁ2놱éQæêUá—EøgÑlÞdË'/ÿ9dÍÕQ„o¥³7?Ÿ -HBIã·0 L9º-n }û®|aÖò/»÷éO:bÆñ'2çÉù¯È{&1y<u•äÝÓ Ra²K[mqYµ '¾c&ù ŒaL>;tryÑ€ÖLÚµ²½9W5ùò¥ìë$UH6Ü_TàZGY¼K.TÞc–ÛaKP)€ŒRŽƒj´0žÒ/o»âÖ­Ì/ñ�ôXBÑ1a™þp~rÑHØ ƒ¥Õ€˜0#èÑóŇoÈœÚJUÊBÇ<¼Ìù½î-Ö‘ìKš†WXšhFªW€:¢±R…sŸû’¶¸\­Eb ïdƦ.Á£ºà*/­‡ d=˹Ì)72Ln“žcY÷b˜QøÍ÷UL#)9 ²ëÕÒ6ìUœxy—‚ôñ/ f°ÿZd>!l͘20ÎCs(#˜Kdw¿#@Ë«ºfSFøØ‘A'Í*èÿY?²™ù+cI5U¤ÿ5µy¹á¤§ß^ÿv¿üߟKW;{/ñ,«›ÖÿT¬Þ££ À\h³Â4NŽÓ ™Þ1@1ÞÖZÙÒ˜ggm¡ˆéU$ailKÿ ³ºD1tˆ˜ŽA{™-göÊMØ»˜£UhæúC¥ãDé¨4ë:˜WÏnV´ #Ï`›èM×,–H˜cóâ‚b{ÿ#Ì”‚þXÇK3ÊûDýˆŒF�)J,ä·{k^ýýŸ™hJD‹s º8Ìéᳩ}08�`ôtømYœDóì.¿Ÿ ;þžËã²Æñà¡’FûV%ºæ{28ñˆRIëˆ ˜ˆÈ»õÕÉÅâTó?Ý(i¤ «Å¡”OÜÀ©TÚû m˪ïù[3ŠÐé¿vá§WÄ£±ö<–& ä9úêQsáK!¥7ù96>¸k›8  ¸ì„Ã=i"mÙ1wˆy5\”ÞK'ó{ôöCèP¤O½íÌÿ]žÔXbâˆPM¡IÎÑ‘Sߢ°“ <¡T‹dù²€ãÛcõóñWü]qÛjÎ’ ÍCQa¶‰4À±mÝ£tö7úâÇCKú[ X½PoL0r`uq/ò ɨ¤gß…í38Ü fá®j#Xz…Sh¤7cåÛù( uh¢Ù÷ΔÒèÑ•Sz!/nraŠ Ú·ã&Êÿ;hA&Õ/qàCaæK%ƒÉrù*1–yàèÍÖláK~r‹Fۈ˙Ò4Ý k2 n㡳E ¬’gÜá)åÝ”}Pl²H»í81¹­Ó>I!¡éÏ‘vUÑQ¸Œ%øp>#Ä«·­5ô/˜ÞämÛ§Ôñ³­ºðöÚ’òS€žŸ+—¶ƒ¸EÄJñyZ+te•Ðê4â¦àX…'Íóß ôbÃL˜AžÁtÇN¿•çc\ æž[´Úä€ü>Ní¯ÃXŸm¦<bäë@±ÕçwPP܃!·íâô©¾?«àEÌI(ÿ\‡þ^¤ÊÃ^ª-‡!`N×°Ñæ*íïE|!Êå•á‹ù—r=xÉè*ÆŸeÚã<òè|s¢/¡HÅï(îšÃ“à@+pÅô¡y¿à ¶Ù§Ÿlå1S@)†Dhàï6 wû¦ëÂe ¬ÁWݰdËœ¹Àù¶ =g‘ÇåÁÐèíï‹B=˜ ò©ÚK‘ù; Vw¡Š CþþÌÐàÊ. Yæp‰*fäj"ª1Ïÿ%Cé™n‰ša,Èï§>V| XY¢Õϼˆ×fÓÝ­ ýË÷AK£ÒÁÉ™23ª+–Z}I=†‚ŽÇ£ÂçG_íý‹C«*Vü‚“O›z|Ã|Z§ÛVÖû%ë+ µ±Êýî *D/F0*rÓ`³×{Ši<ÞG¾¸©ÌÛ£6Þ¦.Úrl—<ÑàP×;4&ÀÉ"únàØ Ä»„HQÍEû·¾Œ/+Øy­6‘p—V|½19gŸ¯¥çÉ•‹ŽfÔUHëàs –¤1pNÚ_ÉLxG!,¨zé6t©”“ÆD¥@ûÇÂNÄÁ°?íáøŠ¯¢9Ä_/‘èoT¥?šB%´?[À<ˆi˜¦YÈQó(4ÍÕøreÿx_œu!.¨¯‹›~}xÔ§µjJó ì)§1d뛫0’4+âc(ŽX6o('ª|<c>ómHawyßsÛÜš–wh(@R`ÇrFf€®Ó¿èØ: Üï9ÁåïT«cî«Ô‡&RGŠØ–†¥Pzà¸v $ ¦—Źeê 8Èç•"meÒvœö::N®£fÅx°Çe$•ïPçÞ§¨M8]Ä»5¬Äþ.g0Zw5N÷fi 74áèmmðûT»¥Ä#£ÇÑj?#ôyîYhS¹r§}ÈãâÝöD&®ÂÕrfÔ%“†ñø£Õq­Ç#b¶·Ç àô›A€ëC­"«ÂxYçÕ°û¦©’€P¢"•\l1½ý-Þ ;Qn`áÕÈ�€›¼�°ÑÞ°<½"¸!*Šä¹¸:›l(t´i.tLjkü{~†T/Üq4¼UPpì#†gú 1þhÌŠ·ãΨÆtú{NbOšQòñÓ5·Pî-—¡˜­P*1 ÍO÷ÔUÏ'M 5voW²›{B¥v%Q…7¶¿âñƒrXc~ÔxÅŸo% –‚¹}f‰Ÿçƒ-›V¾È¬L𕻢Ïb!"atmÎW‘*ö¾Ù>ÓʃÎ쫌¶ýÙ ¦yÃ÷:½HAèü»|“÷æ“mѹeþtS‰©ÉYí(°•e–µhû‹ê ‘Eg{†wE^U`äFǤ–í€è°7íB‚Çè!«ÃÔ²µü[àä>k®Þ<žŒò¬ž•&vÙxg ·˜gZ¬`øÞ¥•iïM0k¯Ë}'P„7°9·ü½ƒ„j7«ë‹xŒ;)2HR (ª1‘šÕ«Š'Unæ?á(iœüþZß›‘ܧµ#•gM$À!<À(›'Ô¨ÔðK.ÂDÃy[÷~Õ`ª. Qô=ØBʶºÓ1ìʱ®VÙ»ãE”ŽöHO>G$¦õJÅÞæM·ÔCâc#TÃ¥®.1cú„«rú$k]ç9šf¾]¯3œKí:¤îúÞS¾@×Ï-ÕÖ×ã Lž8ë¬MÓØþuôÊ*ùRT5U­v‰­ªVI¢óÔ3~ZDÃu(�Kjdrm»>û,©®øRLd’UvtU!ù×Lïë‘9’¦ vf~G 9ü¹ß !A SÜ'lìí–eê°Øâ©¯”ÓŒSîWÛŽMäÇX‡L¾OóÀ¤f“Sl×U*8¬™/Ðs[mÆYPý¬ãÖÂ>'L¹t¸s™û»¯g2ì`h{Ïœ)ß®i3ÇX¾?¥Žz3{ï©®õ Í: éx�üp•/ .1´8Q0$è™JÝWÓXËM ;Ë3ÄvtêšçÑ‘‹×w“¨ nÞŽI«ï—6’àZ·ö¾¸F#hÔI÷‚\¿!'ìê¤g8æÉ=0ø 7Š¥d‹‡m€üæpÊ †æ·¸¶7\êXýköO³kžðC»Ç[±0æ_=‡ÀE6è»ËÊx ]³Ý¥ƒ-ÀÆÎJ³ñ ¥R£kL.Æh†»¿1–KY¬¿­]ÜÂa1‡Á6È~vÃø‡–8NOg8”çîã”ø>aíÖ´M½n›m²,M<9Ì1œ> ZFVb±"Î?J 1‘»IÏaò¥i“ü´<Ó½lc¾rßÃ〨iJÜq<RÁ''®ÂÑ_@bȽ%öòϬ}~´¿¯ÃýºðY‰ï<hްAFôü*] Rû»¬˜·ö ¯žX#@ÎÝ¡9आ®ó_$Ïò…W Ndî剷mïÜîà1�ÍêUå?Vh&«Žf!Ctw‘ì׌MÄzCBíZ±®nDçÇ#&GÂ=£B¯'3|V,ÝvV"rX€#BŒ©xyž–5Sâ÷ÕŽžÕtlþ¦wªÓ`¼Å¼”á¡[QOm(@c׎sÔÏk†z•¾Èv^ùña”J𩵑Üá…8/ÒÓQ/¯öAÕ½«Nžvà%èÔºV&rßÌM ?@jÊ·¦$bØN̵Vú\Oéóeï±2«"“*óú¨‹Ò†´2=Xm˰0?¾@›$¤§ê7PéLÈ¿…æØ ú=TÕD<•â*Ö÷eL�ðíÊ=2S?e,¯}*Ä3aÞUx À7k€âì´f9‚·�Lÿ…*áÆÕ#DP€Y¡äùÇàݘíŸoN€›~ªøqz2©Î“k‘ä÷NXLÂMâo;^–dÓ¹]çâTª)6<¤G¦¾ŠS0f™ÇQ:Z³c±ºWT¾©Æd´=Ž6›lq§NX«ˆ]ˆý%p¾¼%Âôt~P®ƒåmåé)àØ þË!2º PÍxÕ¨´ƒÎl§ …ð?™("§? eç ‘õz:ç1‹kqE_#�ZÁ‚Ä”Q ú vDptTÒó“ðo}·XÔ³í.åY KöYVŒo—šwÄú*‘1är¢S-«°LݰÈËm'{úzY©5ï@w……HîAå4!Q[tèÔ¢7½ßSÄð¢3Äà…êÜ•ê[ 'K,»'ÁàKûä Á&œóÇÖFQbO³ˆêØ…)îJx}á'kZm�]fÚ€G”_wó›åŸpW†SˆýJû2ç†V*™â :Ës²É÷°0/ÑÛy2,ð-¤Ê0´lXúL¥íq¾øŒM& ,ë0gE%<§²ž¶o¡¬»Ç{¥$!;¸z¡Â½h0‚ƒ èjBiZ™Ë F4�kÅ´{HWi­Š_›3\‹D)„ÈHÍ÷ð©P”7¯‚òxÔdÃÉS´»ù,sHet‰st®‚Ü7cÊå Õ/¨ú‡D·‰U[r“oî,7’}I¯^týèξ‡Ö€ÃX¼ݶ=Aé¹tg·"Ú§ìYB©ª/M›Å<ÿ1þ½CQnꢊ<3ׯ%4å·¤ ÔOØÖ» ô V´]UïÛ\Ã,Šæ‰?«‰MpDÆyùiíÔ¸öéþ´¨¨[&"**W„ù,Þˆ¯‹K5¼5¹ã9¥@K’>Q{c" c¡]1Ý6Âq4“'Ír“4ÅR_C\^žÕS¯–/Ù©¸@2—“zõ–:²Ë#$½?YÇøRlO5‚¸zv¤¡=>Õ—‚k¼HY ìzkˆ AÛ”—3«‰1ŸD!ÍÝ¥ŽgÅP¯:áâoûÄÌR•1K`ÜŽº8åIÓ°n©Í,ºŒ›,i}è–ñeòÊ­IŠÂ5ñŒf#G7»Ø«Œoÿg¤œ¹xA5¯ö6ž>Iv²ßè’_§M£mÈòÇ›:aXx‚ÈWÆ¥#“ÀÐ`#|$1)€ �GÍ»ñPÿû(”ÔŇyÐaT¬[Á_&t…‹îžUóÍØŠ/1Cq»&Á1º¯âzBŸïEº~F>[‘*5‹"ÕÌÀH°ÓVˆYÁ¦®fžma³å„Psûg¨ÖBíÛÜÉ:µ·{)Nâ·yôm½v@m Ã\ŸP¦, Kcy\{ ‰³þú9ëÆjKÀÄ7²#0Ê‹_¹Í$úzpœgÙq’Oê@¨WÿoøUÕc«ìs× G»@˾…Õlê¯(_Kîë(².‘Fåûm4¹ÇÙV¥þF…#~ëþoÇåu†&Ê“ñÚ¸¬Wö'ý"È4î “-|Ã5ÂIµIé›ßëé[Ï`ˆd,U áÈçôlˆêQøv䯼ô‘9~$;û OœÀ7õï–²v{Úµ«QËj ’€ZZ)ò)âôУý'@ª›³\'¯“4 £ãúj)·©æÚçÍW‹Ö€·ýÜð›Ú(‡PèÄsÚ¸ÌDn åç"R‹1ÔÁØ:–õ¦]âg-Q4Ô⎘̯‰G=‡¤sPùúSà¢j—n§¥N?q“yjM»®ò‹Ø‚Ð`W¹–¸/íËuÕºqÊÆL~äÝÙWÒ,©‚©cú‡(,õml'‹ ØEùÊmsH¼aª¦ÀÉQìŽÄˆênœüóÍ)1ma¡Yý#Nñ.ß•5‘œ~¾ÄÖEe³¬ì¼�†榼çæwµžž”¿)d'¾,hpÝN«V¡Iw­'õùªGYë¡m¿¿" çù¶ÃWî£äZ笌"ŽccõN;«pdé\ßu­©;БËú*ÙT÷飛êûKªË{NE­C̱°`ªép)²SâÞÛ4©ùèTP§d‡ºñù(ô3Ê­Èà;,ð`>›¦`å ©>í/_½CqѲSHµí8£f6⛦ěyO—asÃö b¼ ör¢‡fnùfGаûϡͣë)?°|×ý ñw›ÑtŒ ŸIÑž¦!³.¸(Wi¾P¢ ޤ`$` è61`‚ãõ'~ìÅ­Q¸ß^® ßýHQÞŽ°g2µâµÎè=ÿ†1?™Ä–ŸŸWTït>º þÁ< ó³Ã*мa¡:àë½ãg¾›”,B [Î m+’Òd¨…æÌÆÉjmëz÷¿+QsJg6ºÚåab ×D ó¤ÕX.â1st\É#•­ŒÖ{=¨-Ã)EôÍÝ­•ê‡Qël/%ôˆÄ >¬è㮩ŠÅ±Œ¯þÝ&¤ŽN, ~!´‚+…6¢C6I¶ ¹>±Jl×zL¹DÉœ~PZA:{0¬R"‚‰”ðÅæÑÛ…B½Ý Âx)ä„‘¸`„…"0^˜<µûqÞ%s‚9šüö•.ÎrƒC`T%Z¡Û«&òc‘¹mbˆ?ÔÚÕ(E¬E|å3‚Ô-�3ENy:ŸáGÄf¤]Cu™I¼x.,µÿšË‚J»¥^¡°Z€ýÞ‹ÓÜ®äÜb”¹l”Oþü,/hÙ4p´ÆDì§ãôDئ-âA­.IZ ‰áïpX4á0íù› êùß-œ®ÄF %…§édà °·¢¶Šµ<:Á×1]Ðz5wŒÄþŠ,áß x„W¯ž«…÷Øs°ãgÁ)<`¦åw™DV0ØéUÏ õ×,=–*ûu½1Šƒ@Ü…·d\L# 0",UÕ?= 42š¯0ùµ-tô¯%ý†d´ûê|ýÛùÄèÏ—vw']¬Å{7ãþGôïU®á^™Mœ * °J<lÈœÜæn¼LIz)ÐV^|Õ)—í-·±c”cý_ ž¿ayà]‰¢kÐ`¿•cGž×#mxÎ;8(¼1adöþi£þ‘{¡/ÌŒ”ŸßLßægÙÙRFž°…ÿ~iá¾}Þ§s=œµØfN°…šC'¯E7ìðnLã§ôcÐj~µX–C!ÑOu ¨í¼Ì€@.š=U†}°e‚ΨoÁŸ:9Q°Ûñ®I\–Yßywñ˜SÙýuõJÐbÅ|mvÜ »M¬Im¦¦äJ°«ÁàÁ_MØAQ€¿ýCÜh:ùCR›'÷lÿ1!óÿ`н™_c 6Œ-x0rãÂ4!­˜¥MŽ’‘º>o¥8È5 _&/çI€ùípöþýñl‰ÍôYÂ)æâ²ªIÕ­È”´T=<ÚàçŠyOüUŒiU%v#Ú3é(‰€·ÀåÀÜW‘ÄbùÄ9Š!d<þ¶t¦¢š>ÒõAÃ$H°äl?ÿÿÿ)~Û<å;… †Þ+~ûê|þ´bø¶ôЋ—¢ÕŒ »€wF±j9ŠsÀˆîœP½.é䱡<„~¤ªêág¯Ü½ÙÑÕ5|5i–ùØbÀªÅÓ ¥àÐ$®[àÎ.(O¢pæx= ‰ŒDܚݲÁزþê/9 =hdç‹Ë_´¾}‘RÃVzq)JkÁè´^%:ä:±8 ø~©^ ÜR‘™<ÇŽ!¦~7ÛáÙpøØ˜÷·#Fù„éìš_6¤`¶ØÄ[oŸàÒ‘Xv;8‹Ñ—½yÕR§¹ÃExš2RÛIUcù‘eÁ˜>§,B¼)N_@G¹åñ#B´øbf•ùêy¾l&Üp Ž @Ø €%´ìæòqEש¯Q½/{"ÊÅt´lð^d"z&PÜPüâšï/6ª0‹º¼ñgßÑÅI)°7Wrö™Nå¥ÂK¾¸™ >²¨3à¶£2@*ÑÁa´Õ"ˆ×D|´Mv¬øá6ÅoB„¢ ªV·y6a®Þ6Uµ–_Wà–CZ“ËA“©Pd‹Ù4.¸=÷ÿH¹ “Ÿ) TµŸ†Ëˆ°-;ú0ë[e´Ìd4€ ÑŽ›¢H[é¨B|ÙíX ÑB,Ò8º…pl@’¸›pö”©¤ÚfѪ£BØ’<+e\Áú*³ú¸ñláq(Ë«•‹D›áç-<‘)¶Y+T Y9§‹À‰ÒœNÅxZÒ*j¬pä²’ÿ6xcø/(\°d‰˜ÍP9Å}ÓþŠBÜ5` §À™>-å…Æ«Â¶%jÁg¤vWà1ž‚I#ªA(éè}†Þ¹?˜•@ÿŽçÁþÿ#šÀi ƒyÉœ²ñÓîkúg2 ·F½˜$!6éÒþ¸HVÆ9Åô86`ŠÄ%†Ôºð½g¸ æïÅÑ€A$Š™uH¥ËëäóÙß5/¬öyªCß—gGÎ:ašôœÞ 325fß\ûó²Ë}¦8¿€W V“Tç»a:Ôlu8«²>¸‡OÍÁ“kÁ+NѤ‘y Õšÿ70¿„Øþ¨Í‘R{Ø^…3æ�?ËÉç^oûDOoÊtN’ÌÐG^ÌhqܾÆî­{íØ.ß¼ "{–d›Ýÿ'ÁÊåב‘•𢶺ýë‹R«*pâ8wm\ bæºYî‰0ï¸B-¬³‘ÓxjèžÙ2»ó/a(¦Š)ä  £¶:Ÿ~ò£lâ_ ADuÛµ:‹Ó¡šŒ«ñ=8-åRåöa){5~ŒN”ˆÓjêÀ½¨´<àÂ5ÈDMpºÜ^'#C”‰éùb£3UKûª"ó›‚:yzWÔUõ 7cny5ˆÇ\F€4•ú³ŒÁÊÌÿ%Plð:j5*!eÑOK–} /惋1¬$9q4Fùí‘ß\ˆzôÑQ¨Â¨&ÂÁ$´ì4ÕBåBÁ²û\¨çÇSHDc›«ˆ¾…Öß,çzh!E–U61X'wžÛæ}è&Ò£gŽ'ÂÛX°¬°Ǩµ_0ÄsüKG™\X‚£X‡{S~È“édY5öAÏìœGPX¿=bÌóI]Ÿ�û~U#—ÙzdÉ©sE$ù²"ð\C降ò7-è;û wnŸQ±|™~$ P ïð¡÷ùüýÔFÑn~£¼úòøéÎÕ–0µwA–ë§+x†×ûÌ|Â"ùþÆrsàquŸXÖ{i(¥·>­ÈÕˆJ Bb.ý,´8:´B¶™6ýSºÄ´Ú‚×{ÍV²CíTïå u‹½QIpû‘~ÿ4ËqA õ€C˜WA¬t-ËÈ%Wp[çØåûTy·"é‡JЮG¥$Õz-˜…ê-„Ldá ¬Ô]îêÿ;ñ4¥¥mé©Xð/=á'ÏÖ·SªÉ¹lØ\˜èjN“Hf¤W@ ›eFA õ7Ùêÿ4/X‘ûº2ÆüFaoø(büîÍk"tçZM«šK8jú]h‰¹ ><³ïUã,²/¦ Ÿ� 15zK/¢_IÃp<ŸS+ÇÚš‹Õw¶It‚Åk´ùVÝ$ÞÒÚÉ^ÏkÍ&u‘xÓ|>QËâ\Ú{ÜUH+y †Œk%åÉ……pÓdü¶1°;s&pƒÙÒ΂օd4ï˜Ó„öŒ|0.‚R¼WŽÉúOéB‘Ûš7¼k¬ªw !;ýjÙékûl‚ß¾‘fc0|!‰õxR\ ù¦ô;”‡ç—Úáä³%ƒq )™-þ0”…h²É€&œ–•}d……wFͼÿAN.ýì‘o9¨&l™ádÕ '±–¨BeTÑ"…_øI_!žìEÔQ}féÃÞßyl>U:`›Lâ£l¸Y$KKŽ"á¬XÐtó-Ÿ¢‹OX€«|/ùƒÖ/2At#•ü]4ÉT%¬‹ü+ëq­hªlµÔ¾ §°Ù3K¥-Ñ|øŽ¥¤ š@PzHéÓAua%>BJ”*tsïc}—¸‡±vP)LÍ AwSþ–ÝW­˜°CØ,½Mƒ(ç<©60êCÍõ¼€M,�øºi—-ZÁ 5ŽãÍhg7Og T&§y­¼|¶Ù«ðD­ß¸XÀ¯WÊÑžo¨P‰ö6fžgàLdª·vðÅ iŶ˜¤vÁ¸|J €5N hlÑjvE,Âmëwó5©ÐfAUÞ\þ!Ÿl’غ8%Ä []Ž¥mwý5þ~ b=ˆ‰Uà,îfšÀ>{Š }õ\ò¯ÅJžà‰pÓ;ÔZ¾«qóÁx*ò†Ú¤x•C=mÏüAo™a,óxÊQ�WB1sÝ8ãR\¡ß5aº5€¾w¥¤üÊI° UÕ‰ë¬|{””/†�¤ºc­Ò9î1¬j²Ö(ݾxD<ë$ƒKoÔõ:Ü’Œn]ÇÁ»*sC—åãÖLšoY–ŸŸG¿õS‚©å ¨sj»ï$o¡ Ê ÿj±cù~ð¦R¿"=>+3ëcÇ=#íªÁ2':ŠÍ¿‰ ïrPÇXzލ`ykU®)0 K$òÛׯðµÿ7€鵨«3ûf>þ*3M£Îšá‡h´±.´Íˆ•*r_묿¢Ð"cö=ú#X<Ø¥%ˆ'h}F@˜ÙCY.±–†l’ÖÍœÀÚÔz‰m�Í_ Ó+ÃÀa@LˆmÖÖŸB¤Ÿ¹ÈE‘Äþ<Y'~eøöžºÂå¾»Ö£4ÎKƤ²Ê&1Ó¤8¹ù/ÄÍü–hñЍƒ¥¿Š±áãO?ç¤KË£^Úgtž<Ξ,'^e„}VµGÚ©è)…™Êã”(}‡ ‡ÀåÃí \C‹¤D¯L]AÖ+âß(â·²îÜdö°’˜1[ïIؠ꺫‡»1"Žûm-Àý·XÙ6Ìñísƒ%ÓüŽkê^È“AþœÈ™~Ó ø—|TwT Љ&ê(íU–‡"¸¢r*»ÝN¡­¦x*¢Þ¶U"‡C±J[£('VMAH ÜšÀL{%Íuƒe¯]Ð=ÂÍ$>ëÙ,.[rM>]–˨_Œ¢m±J7Íóz”ݯm›é»Êœ[9zJCô%ú«1»ê.ƒJÕÅZNIyñùädŠzÔ®žä{”<P5¼±Ì¹š¤ñ zÂ(¦t÷,׫FßÉOÝ/ÃOä^tÔeÿN—/·9÷y¶ŸÄˆ20Ë• æ�ü FNeSjŠ@1ð0û�Â@�3\{³µB^i"c•ư=«`br4Zk…+ŠqgÉ¢)8ø£Û¼wb�{¢³Ï'{ßï0½}ÓMmuÌÒž¦¥¯> ›ŸØM{÷ŒVcCÚcõv³ê,ðù'üܘÞeB…_lj ¡E`€åXEhÛñI)»]’Mÿ@X¹Óë1ÿaœå|’j,DÖññoŠþ:΢ÈxµpØîÂ/†À;øüGùpxÉÐZ펻cަѭ¯ÞæáRÏcK2îc§š]þQÝ……߀Hô1<YÇXà'9Y²0f}³3œ]ÙHéÏÜùÔv´à-çC6$½KÑ(?©ªþ(aó|¾ù=]ðŠsZ6—–ÿ<œ1“|¾{*€·™æ)®“XM§·%i‡ÆÌºú'§S`9YÂùuHÈÕzd” ¿J¤@YA~ ¡´éêɵ˜èGGOO =(ããˆÿ ü;K }ïÙp!Æu  - K‡ râåB‡·¸sÂsÜ ¶ üЉçƒZÁ—°!7³ÁøË;œÝÚ×H:3Þ=LŽ/ÜÖl9-m¶jµ¼‹1ïŒG„«XÍÂÞ ç8¨„Lg£#nÐJ¦lqõD»´!ô~”é‰å­Ýíró¦Ï%5†® j˜ Øps­ Hò·Pºìr#Z�SB n=C±Ø/¥ç­>¼k± ¿Ž[(~“çþ«¢}JFmº¿Þª¢ôãÿ\¬–Û+¢Eg@‘°F’ž‚ñý¶oíì«ýº}D?·¨ÛÞïíí«öïûußööGßV…~Þ‹—Õ»7ÕeßU™Æ|úWööóþÝ7ý¤SçëÏíêöôóûw÷¾­~ý½œ÷Õ£¿·¢%õnµõ[WÕfpþÛööyþÝGý¤·lÿ7¬ÿmê{öí¿ûu^ú»�ï«A?nø/«m¯ª»¾ªÀöÔ]ˆ€îÏÒ'd]Ç£fâ%”ÂtÎʧsÁ¯ÒçâC?ËE6¹=f¹�„cnö-¥*FÅØ»¡ã©¾nKúŒYÞ#¹–ˆbÉÒ%¯å4I^ Æ« "§Tþ$Ò>G„ÅÄȕǾȆ5gc 8œÊ8Z53MçF”ÓÌsp_Õ®"²ÑÜ/€f2§™ÆË¾rYün¸¢ç¨:Rdob¾µ¨ê>7œŒq²ü–T¨¤……Xè­ä@’ÑÒÆ´›a~æJmó„Þ•õîøòÖ”�2£ y5s-b˜†µ­™²¢œ£¿a¾ÓˆŽñùÂN³xŸÌØÔQ¼Óœ,×dÐĘ••PjPuL³5W悟٥'F 5"¶Ø®Ùa"EiLZ‰®¡˜Ç3 ÷¨È6Qÿ+[2~ìÌÅ%0.œq}# ƦU,ÇÅ2’©µ#Ñá\’­·dðצu¡ o]‰#÷°ä~4§´¯£¹]U¯úEÂNaH8èKaÝUí:#3 ÊF­½çþsÂ6Ë ²tn£wÚ(I;´çÕ©t\bƒ6}nô×$–½Öš$ö Ú p€ØÿGø�FåòÐ(%LÉÜý7 ¥¿ÿ!¯ð>”š@¿~¹Ïp´ K63OZEÝ{u¡çVèYê 5¤Âcû~f{CI?ÃýfÈäœÔî»fY¼þ¢ßÀ#¥`$f·*5‚¦¯™Ë¥$Ї©J’9lÜÀaÞ”@g£_`(J2EÞ?;‚;S‰7VÏ×ûpT—g ûÈ `ÕYƒ·Ræu*„Pƒ ?V!6ñÈ$vB<½œ{ëÌFqN9{èúÍ—xŸÑ^€ísí^ JÓ6ªYðòTâkÿt:Ân�¸ù©~§Þ÷þÊi Ä5ÌEÓdI^Šn‰C/z­³&ô)’Æ}Fæk $G�^Ù×UvÓ²Éb5óê¿®Ú:`myÑMøcž€;«ìà³ûz÷jºk\e$ç¶ú÷Db•ÈÃ{Yçÿ6ÉÜÆ)ÐQ+|e >i_oÔ=²!‡³Â2yË”éM1F½0b™ª?d­$AE¢1»Ÿl´h6IçãÇ´3ÇØ ÓqÊãpPð‰íÏrçœ=WïKcäÖ¢8–å:lïÇÈé¤@»Ó;ïÌ"Û¼oj<ð Ë@ª/qõ°H™_AØ«w ¯¤©Zú'X=ž >Û–¦5°IŒÜ÷&6ÞðŠ“²,‹ê}°*1{³l2õV°jãEoúèDW1�ôºbøAKß‘ƒ™ªg&Ðó�PªìÕµ=ª¯†¼e˜â´š}Ž]<DøU˜žuT+áïB¶!UÕ•6¥dZ¸9åœ/)Æó©‹œqÜ\}ˤ"þ+\p©­w¬í9|.½NMˆ²ð²lß–î™ÿ+²Žš+Œºd~ IÓ>&TÚ gسjj¼xœÑ—tôojEZb´ºúëqSzÞsØ{ÝØMÍàZ%¸*.T¾âviLêÔ8dâÞÝVvÁQæ$ä¿Ä‰6æÏ þIôèIe×ñno‹v¶›µ5¢^¾Î*®uÞ8 FÔM,½56c%æø 0°ÿv´ß«}¨Žp*dz¨”Öëÿˆ6t6•ûo5{%˜Ã$sL{AX %×¹Ág÷îê…x¦I~_¤ð.Ùrà­Œ]â§!^|£+dƒ�#€\' $ÀÅ2Fôް$‰>µSÆ,,'Œ…~‰Ïq`…tøS8;+|êò­¼H¯©Ÿ 2”ôÆÂì{F(©Ãñ†×òÆ_«éÔŽÔ»š*¬·—|§X(û³Ûà,€þ™ÎQœÎòßDôzÿU‹H49MkñFîDØ ¦¤�"ŒÛ^™ €èeY 2íÅ=‘k8ì):®Øñë±)ŽZÊôÖƒaêÈþCû¯ ô1~ gÞ‰î{ƒQûãlmÑ•JEã,Âr˜é B.ã­Pð¯ï#&2�$%£Iþ\Éb÷aë^Úr‚W¥ —Õ‰òNýäVÅ�Ú˜êºÒ¿Ðö3Ë–œ‰ö+ÃX߹ȷ¿î_»Þ/ÊVq)%û<¦R€ÔMØàÁ¢éFh‰r0å£&‰$xŸm-ÀÚ>_€¹";Õ´ÇÆ~'”¾p?` 1’o”øšÕÖ]¾Ó?-÷Ëëd—b…›p³¬BOÒy)û‘'ÆÀ$34tÔ^7vŨ©3=(Ϭ–Ùƒ€2ûÖ¬“ª×*XЀ|5§ì 8ñvYsN§\ÉÔ¸÷Ÿ×�Éû«Ú´òBµMeÍ~÷=oD‹¶ BBö|—;lÉœ¢d oë.¯?¤ŽDŸš„udjˆPÄÈÖ<b¹ž~‰½‘hŽk\™JØE¦ò³0ç–NÀ¦¢#{ÎÕÊìdøß|«\ŒófǾü¼zˆ·ÙØ9’EÚ.ÊncãŒWô†öyl“mŠß –}˦¨òÅ’DÔf1ë8�’.<-Ø!­ûj/.>Àü—)r÷­G—äc€o{Q‚Ú&7¾°6˜ÊמòvèÖþÄt{£Ï�È*ÏõT{F)ÂÞ[&æZŒÇlræCÊ›€™D­`],™³¯EÚ1ÚÊsížk¸l›<à`sXÊçJÆ?*ˆù!ËP?‹ßÅy•â¢-„nej-ÆI¨ÄªWÓ’¡ºf8´7JD“‡šD(Í—Â }屨ë…teÀäÒ1Ö51Íð-»,6ÖÎ&Jš ›÷'c.Sø•¼²«&KöZ[à„Çneø¤V!+"¬]òbmGM”½BÁqhæس’Œ¦4`)<¯§k‘ýq6Ùi-j5;|ÿ ©#5…ü!‹—\àù¼Œ]å¢ K/£$®/†2tÞM›&¤¿O(1¸è´‡G$mzÙ£‹íÆ nÆ$X#EºIééoç¥$g`ž]ËH`М6”ÞÂÃÖèLsxñSd­Òey< 0þXÛ\*¶; 6á£ÃA4©mc^î. nªáå9W`ò‹WäéãÌ¹èž ‚ÿFHµˆbWKãß–˜hÝG„-£¥rÆN¶°=H<åYSžÀ&9X-£.›?]òM5’÷¢‹¹GP3)r‰Ù4ä¥N‚6xø…h‘jcs¦UØÍÔ1bÙÓÊZ3É·£"ÚpVî K ãíW÷v7{j˜€—wT˜Ù#ä!AKÛéT."‚ö÷¥Ÿ-ÿUKß×õ´¨éìmn{yœ‘“HpÃûøôª¾‚æ*‘‚|•Ë“e$Žø×zÁc]¡»9mE‘nSµèeô¤…øÐµ¨dö8<š7‰(>7&v q𠌋L¨?uYÏŸ#ÕÛ| o´Æm½ŠF,Ó ö§iŸà¿$w\ÒG ,Üõº’ÅÈÍC»d„¥žw|ÙݰÖÐ8-ÄŠÚÎ$%ïCá÷S&é[%—ƒ×9'×׳Ԋ'›Ýk<¦æ@][&´j}5E|Öú}uípF¡ì5o‚ ¦OAX-êò{ìPJجˆà 2êkž‹ép¶Öý7Ì{.¤«®U¾37Ó!¦y Þ¦çp=Ò Ü[=Ü «åŽùUÐ=o¼-¬ÅkM¯ÉS.Y¯?³gbÒ *áKä騮‡Fºv|†Ÿè ª]"œï*ü’ߨŠó3£9œRVÞèÝGsb$H¥ÿNeÇÔd'm¶9Pý]¶ÙÕÖÂ/U>`®1ûÕàE„½½±X¡å9\&Xo Þû5S®Ì€•N‡Õ‹……A.Myär+Òr*h­~ïÏsùWm#Ê)a¹ø B Õ«&¼*÷#{kYWáh£¸-EãÑ ¥ÈŒÂªxˆÜåo;Ï«†J™H#½Á1ûha+TEIC1Žÿ1 žx„ý¡ß4å_9Ýã&;0ðçF^`þ]c-ÄóF‘ò†]¿SŽß"¾½¹}åò>©Úî ]ÜÌÈ=Ûem›¶p¥ öøH…‡‡ä÷ÈÇ`.Ö4p7…o]”®gºª9èkä¡Ò”»‹® âie/—1 ÆÅs '™A4û3±r™,@€ã,éW ð&d2«Â„Uø†â©±)yÉîíªí@‘"pw>ñ•<{KÆ¢$`géÅñº§ Äœ&­mO‡ºlèîâ8øßI!uT\}ûw4"ñ!öìŠeë±Ç3(¿Ø‚NX" J7D Uʈ‹“Ũ>;S˜®,ÂÀ]q FÏr¼=£x„qzâËì­¦éKe4”­~$ë5DƒYa2š�ÙÅéÛÞ4È"¶\ÃH+S‡/mÔQ8Ç7"#b;ÿ Õ ñ'Üí5 $øi‰ ì{Ø ð²JÆ”úÕERü‘Ðê´ÉôŸs}eØtHÄ;c°\9ƒfÜ=(J9·W}¿.²yAtú&Xl,×>=z5ƈ²ÕÐo>üó*³ •ÞjBvÜ­íp3ƒí³R°0¹ƒ§ˆ]>Êí`¡‡ž·'HZ“ôðÛÒWàÕù,ä«â@„ÝÏ»Aü·—â;,.9Ìâv÷±£+™PØ—¤Ç·Q‡^.]´ÔËü0{Üò°‡É…½®p•­$m(\[÷)BõD+¯”%¡#CÉß<êQRNÛáù|«pIp¾bJ¿ZY &©êëtŠHV°wkÀRf(žq4çßqUµ’×&íÂÏR¥®‘ÎÂaŸçÛ|е·[ ¾ÅŸfë¤ ÖÜ2”Køš Më}Ð0Þ·¤cü´ëzíh¼_ø–XŠï'k@©Ô¤®Á·Z RzŽdj8ç¼ Í¼‚Òmÿ@·‚Üd‘Ô@5‚¥PqäG@ÁPO#ð â¡Ð·¾n$ XŠJ²óy¨OðÝ Žw¨^†5oÔ•Ž@žñJ°’ ‡jMÉ.ø$Ž ¡åL ‰ÔÄQ”½›Ä–µpNmà:BÈ Ø`þÉý›lŽU¼8j)ñvy’‡ŒWYàaÿdRïÖº"‡—¦êíN›PRé;Œ=Lþ0ÏjûÒÚ©€ê–BGä%BªŸ¦(ì$²A…!LöSg¦„çí³rÎ@³U’ÿMJ#úåRFÑ„)¼¾"á\Æí…‹—£k[(r%€OÑÊW ÿ_yŠ�¨Fæ~ºŒßDÈ<tHCçs O†.- wq[tŸHHÎÂ>¨X^p»âW/¾O& xKïý-#ûFüã¿öòjªKˆ-¶Ös¬D5û€ãÜB“¯P¼SCA)lCa+Ú~nðŽð‹ ¥÷SˆâOÆÁu…µvK‚¦"¤Âå:¬½0Ç œI§±Zð|VãíÅxÍ1“Hˆu"f}Ø8³tð¢K ßýÀíQÝWØ>0xö„é’4Ž „1*ù½íÈóž¥i£³Š”}ߪ·­®ÑZr&.š?Í¿6c™2Þ€�qá%íOÞš±(HâC4CËU6¿ˆ)€´ˆNkb×ú( 9á%”|KË-V„àUH&–òŸO,h7 ./µ¾þS9ÏCk1N›õzEuЏCí×°Ÿœ– ?ÿGÅ•™ÿð/å{åî!p<’%Ù½ )ʺ*g´…ô®ÉGzÆP+äŸõßfPšLL®Ãñ|P¯ û¶à‚õH¼²QÏóæ8È‚1’]&E}^’ˉ÷@[ü–¶à>ˆ@½|m¾uua8N–ºï“ü ú)Šh ˹+3!)N‘ÚÊæk†E¥«.4Ü¢[qjYÀÑFƒ?n ,ßn/·›§Z“RÎÑó2Ë!n_Ý Áþ^Ÿ9±&²úsÑXÚ¶KûÎíÙ-›h¸kãÇœ˜Q÷³ÿP ”?î)U]á*]õ!ñwKAÄÔü/eñŠÕ_‘8M¥K)HB˜¬JŸzƒÞNL-}"ÛÑØÄÂß¼šÙÑU76À.IÉÍØ_7ÄÙhÂË»š•ù°ßxäy±i}g�—§lG•ürÆÙÞëL~ÏJjY40ɵ6fM‹Èb{÷ {Í’Þ|`U仩+\Zz�H¹:Éõ'Ë®=ùƒ;3@®ù?ö~ä˶ñ몙_qŸÍVÐrí¬ƒ¦IãXµˆ¸ yøgáúF£oÛ’„ç¡ÇÊÌe49S­„ Puµfegê(ÍÿU—<6 ½H1À¯Y"ž"ž”G.³24i_p<.Ä\.KÔJµøÔ³íØ6õѲâTbˆl·z¢[…¼tésõ: ÿ.LŸÅuª=‰ëÈ)ê‚ æ L®L º·c´5:-']Wö•²AùsàrfÛ½g`µ†×„ÀS…|+6@ûšöÇÑöÍ™£ /lЏô:RÒ „¨îÈD,úrüÉÀmA7Äôq“hòþð?<¡‡ÌW¡nè*x>ÊyWMNŸ?….õ@”´>°ÄûeÄž³×\ÈôxÊe‡´h·¶ÌßGÇ_úq&™‚ˆN?²m‚nqröAëç¬AZ·ÎÀÜYÙœ?`ü˜“JJ8ƒÍAÓj¤§ƒÈº®#Õÿ:Ó îÙCŠ­ƒ~kŸ×f—ÿZX™4K`#2æ½ÂާÃYñr¹–wöx3ŸQ¢µw¬cÂãBÿWëgºªWpÆ`‹Ñ,‰küŠNZ±wrnqdS–øºÔdIjçÓˆÛ­HÛõž�ï“…ù¥.V¾k‹-ÜrÙT+Õ…föÙ”ºfpì½×ºîk0ø¢nîÑIWk]Œð[ÃD¿u 8„_/<Ò•ž>çM¦€ƒE#4ÝŸüv BäÓ7Œ.ö1l>Hò2Áâ`Ú•9Ó$;&K£hÜ»º/¼Z¹Ïîz tÔ܉ö^¦6šÍèßÛO>D\:Ébê1íjÊSw•IS³R’ljóàf­ÔŽª{ý¥É[šÞ…6gð¯÷?ֲ̡1<ÂHÑÒÝuV#úhèìB;ÏO$‹vÅU2¡‚Dð¦Õà0?³�Ac\ ‘.”%{˜ð‘O´oXɳÆaŒj½$Øå%ƒÐÆe¹Žy×§ˆ6¤xCmÜ>ª,×Ö`îº9„4E—²2kñQ ·OµÕ)ˆßê…S ùYúÜÀóXÕ¬¯+ÖÔ¯Áâ<ÊeÏ|iúVvè¡ÿ°H^iýG2ÒÊOè5Z b ÷î,;'qôåvO"x–-=vBà°‚WÁØm‰Û úüîØvqñ?Ü/û•Ò¿Ñ3VÓÇ-*faW·$~ƒýËc¶¢vÔÚÒ—ËXcp6¼„nú=Ÿs®ÛÿH²ç>ñú³¶«dR 4¬×$²#º4¾n+|ÌÞ\ýĘ1!vÑ­±^Ï"`ÛF¡†€\ƒ ឈÿwIÛ0ñ¯‡®‘¨x­¨1ܲgDˆ¹§–¢8Xº¡þpqÈÛþ¾;7í@ dŽHã~e!Õ (¢C–°™CBÃõ4 Úÿz¥˜ 4éÁ€cßtëû½uê×Ò ‡ïäê^Øk³ß3 õ½¤Qò}àG0<í,Äåêäî"ƒE8’/¨ÜïäÚÿv¥Ägɹ€©_á¼·öbSÞ¢ æãòp4€t$‹f b`•]+uJG¯†U–)_äŸuµi`­÷ZÒ]J°M BbïxÞÄ×" ±ó·!}?êÚ«#9y’yö},Rëƒ`þÓV†Ä™éÔòôÉH«Ž»ö$Øãdx $½x«$gœHüŠ;;à{/ ´øÈr•çù{ü¯Ï�CèÂæWA†\O¯<ŵ TB i„ër#aeé+a3¦­À¯”›ýH©>z@.ç�Ïä—Û®œæ4jê``}Éaœj‡å''屡Π¸ï:µ†&$‚ïëÝ]Èèv·Ëõ F3„&’Œ»°¬Ž &#†ba “{Šàrr™&êÖžBçÛ5!ÆûÍŒkUEç4lj©ê. ü:ãžjð*$px%/½¹)Àé[Šá(¿“¾ã–_Œ„êSfy£¢o Vm‡Ãl½¾$÷>¤úu• Tæv¹è×®)©ðû+/f³cÅ¢ôU†0ø ‡Å‡€z•c¡u³O:àn…À@¬†a¶üuÅCbzé¸=™Ïjž¦Å¤r/ŽóôÁξ° ‘’5ŸMÔ¸èÎ϶]Ï3Ŀφþõó eE}΢»Utçm4Z(‰Ê”"ý·z@|T=»tÒÉkú; c]†–¥Þ1KÒŒ¿mW@Ÿ¦Bô|¹À"ÉÞ><»oè”ü”ÇU]µCÀ‘WÂ^b3ÁÇ7«Lw|à!ìvGåÆ+”tþàòMªšLˆÄ}š3 îO ðd\¶š°Û•Ý 5ö9£…GÊÊí> }ô„¸Å?·ðä$²•`·•öiÞÉKe˜{exÆêFílžÑ$eÆ} 9[uZ z4h˜!½È€"È-S—¤ðª0àÆìòfùsb`ùdn,&¿ÇÖ ‚'_!gAW®Zž¨‰‰¦”®‰v¾ÚtÒ®{Gï.ø3þ~kä¸"uKªû;p°•§nè–:grßs"šÄq€Qn¥\º3¶JEˆÂÅ]÷éÄÝú¼æ»ì‰N*Ásv6û4úlˆ6&}6qª¿BìB2è/[?™æ‚mêsA*°xo ‹¢`ô_ЍxÒáP2ˆOµöšp™ƒêx¨ÐZ¿Jæéo¿Þ6‰¨9þ÷™ØD%ÖÝ/ º[ͪ¯x½Ú[ À Äì}ÐxÙã­q<ôÇëý z'‘’sà1βê?Óõ¦ ÷¹­bעO¦ö8òâ#|%+Ô¢ \½þ¦’5lQ¾ùüh nÍäÆ{§Zæ^'<DqáØ<7Gñœ°ô«¶ ߬+Öuü§îë?¦ÚíõÜ`#7A-ìðÌûg&ïlì[ýŽÝ¡ ;ÞÒÄ"ޏ¿ŠYû¥4ÑCûö÷¹;k7¢)þá”X G„RŒ «8WÂÜÅDÀ€u¼i£Vy•;’ÕÅhÊqùã16®xF‡Æ˜DÔèT&¦Ÿ<—ø–J'‘cÐE?j냽åÙH'ÿC1gÒl®Ý¨ä¼w5+¡†½uTÖi)’yÜxÄâ2à3ɦ³œéîÀ/õáŠ�žæ¸Ú™Š~ÛêrÄì®nù3ÇÔ|ÉÓâð[×R^¼úîqà×iønxöÀJ( B>cv×@HódéÚP2§DÇŸT™:�M±¬rùOŠï”àÖÓI�úPŠÕÕ‹â\S—3ê÷&m9kCx {J¿ÎǺãChó±Àš«ÊŒ›„a—[ ©Ðæ#5`Ë™1D³OåÐá—ttRlpNNቄà.® ±)óÂŽ¥óº¾þ4)L„×`g· &þ°é¨ARÜg'ö¾ÀúWÈ‹©w8½ìq•À®D¸Î}<¶0£È§ØF!Ãf ¶ Û¥�ËÜ•†./ˆ ¥8<¨7„$d× î<zvÅ<§hG!îök„ªŒgBÙ`YïfZœ’aƒS´ÚRÔ›Üa°R<bІ¡š9¡?zØ•œV^rHf>ý©ßGTZP!žeÊ+-|*ñ•Ês•-ñ¡€É«1Ù­gƒ>ƒú•çzäNEçW�’¬zÁž\:-¥Ohmw嬇÷sO¼4é×—¸/hGîÝ÷˜þ~ ízc±½@¼±áÉ2fÓ,ç hZJ•Õªz$æ]ÙëÓÛmÞmjÿ,òÞ“:ãò<Ÿ03—4>O뺔§‚\ÙE‹ö ÚXb¿¡p¥’Ñ62½x[¿x5±†û'\xc/Sw® +û“(jtßrðfÂö‹ú<up>²u¦±ü`ƒñ•AWp¬fV d²¿òQRßÏÉPS¼”-ʯhÛ¶ÞpM)ît,Çý«7ÅQ$Â%ÝR)È‹f J­Nàv#áÍ£±Õ¦€*˜²ËºÔ=^è³6l¼•|ÕÄ ˆd ¼¯*е_Ó¢ô‚’ãATÝNð0üÿ�ZZ«2ß?a¯^ m£Òb¶ŸÆê‹ƒP§#ûu¾$:›ùIÏrØäkäªa8Ç™6- Ò$ÈF;Jƒš}N}õ£Gü <Zª€5Éá=¸R ¥Ì­ì» ñRœßJ¬‹?ŽÛ„˜¬ÀÑŠØ­2ÚE&Iùç¯2å[ßR™5úPæ ]ßëÿ6°‘$±8¤ø™‚gý­´Û%çsê•U7nT†»_Z+”†³Žcð1qÊás�‰ ïîëöËŠ¡ì_¶×ÐÐ{U±µKxµ¹WÈ{ ³÷Ô„¹È•ÆÝýÅ‘½å•KÈË#Oïì ØšÑYºfÈ{§·Iµ×é%Ç'Jéãpo(Ý©$³Á[ÙéeŽöd4W¸àžbŽB|eÉ0;„ß-'}OyÿcÉv=LÐcJ胪Ýr.];õ¯\X¯±•/.ŠËw:ß-Î4ÿHA¡ù>[>9’Bˆ,„’ŒF@"Ù²ùv¯0Íl¡w7’Ðû@Q:×Âõª#ж!þ‘É®Î@®Óy fà :æ{Íd ŸHÑ ¡ZЮa‹çœî™×–ïpA……™fÁ§*Ï(8{%Y2¶³#W‚\;B/h=w´g§* Å“ä éh@R\h n9°Ö!¾öJ0ëöíÝ•§ 5Fà`†ï@�ï7Ü\^ÿ6[xÉŠGM bóÈ'6¥÷Ƶt›ðF'9gt6ÅÀ>– ]´8Q¤ý¼Le©?øƒc »ürd;{O"­^ÇBäô™êM}äÖ`öÑŠÕÞ¡<†×8çÝaï„àöÞŽ0sB ÙÀc"9G?×͉“ø/ß>³£ æ{Ð*Жªsià}UqÅ‘ölœÄ•=\R)PדzÖ.úØF¢6h??Jj>Ÿ¾(}“<à6^G”wÿU‡¥ø¼ ôyÞ…p̼"\@vø¶6x)4BŽ+¹ ótéÓÎbAµ!âÝê"…C%G…UêFÿVßçî¿‘éðö[žËCM@C{®ÙDioùLx”u³Ag1òǘZI¢“¹ÌòSa¶.g›6!¿÷†¦Õæ»LråTû —‹mÄ1‰rK#s|.^ûøi9ä|ºð ògÓú"X‚¾<ˆ,›šw¹”?øZÔ*Íë 4>­2~,HTÿYRY‹C®¯“®˜ô‹Í—=I}ø¥ºÝúî)Svä)n'\ÁÍïÅP 9;mŒIåC}6çïê…9טím|À_áî€>ãqæ´I?:bß9ûpÑLÛ÷pàžÿ4Çúõ}Ÿ˜wâ2îðg ïê—Ùþô Єç ×Ú“St©¤3#a뽄çÍJ=8I&Uj£¿£H]qˆ¢]•üx–x÷æàûûo®oÙäx“uAZ^éW³Ì`æž6m®ËE€žO5Ó=_§…ÁõФ6¿èÃ_‰…™£cQ¼P½¿§[Û7NÍr!§TpeÐÂG(Ä䉗tªAtK@ÆdÇš˜u7@Ê~pð}Áù°Çþ™ûv •~vdt°Ï{¾K…íyÓGßa¸ù©cØe!OUÒþý+ ¬‰©}ì¬â6‘ĪŸß¯ïOB+ŸÇ¹BΗ‘ï}7neO‘§]áö€ZûërGÑæÑâYV==d£çºø¸\¦º„Ú ŠW|T°¾¡¬NHR·¾ëßéSN3ß&eã°Àèâ²ù» ÅàBÇHUhxÝ£D Ч¥^iV|/†Éä]æÓ¨û;¸‡)r6ê_¸™N 1úŒ€9hæF>+ñ0 KÈÀ+õ E­ßŽ@Hc5Ö¥¤ã ì ÿY4¶øæ%?-Ê5V‡¢K™’©;4L˜hdÑÄâ6-Q7ÊCò¼ËŸØöí ¤vrv¨AJæÃ‹õ´4ËØ@ÅEÅ—†oÆÕÉR×M±aïàÐi¶\™IÛ!ï·]:Ã9+Ø1ZdÔe·‰ë¥úëÂ<zë^‘ 4}´ˆ¤ÆH�ñoær†CyHG;|8¹)-žøNx»§AŽhnØ–CÖ¸+D²AL±12‚ê-I4� ÏÃ׃Ž<T�|k‡Ûýè¢q XBw‡VÈ|€”X×jjõ²úv‘}®œgŠ¿>Ób‚PŠ$ËŠ©�ýq„¥«´”õ j™§ç^6êåþ/ ÉåÎh,ô×ɵØl{#Tïâ¹3ï'Ò¡\ÄÅ (ý‘ððˆ€ív ÍIw³ø¯ôI{Q?|™ïˆòäÆ%3Àÿy{ƒ:½ ÐÀ˜Y‹©Â.¡¨×MËcÙ1õ ú¼Aa[éxÇpé…*ïkcJL+aiœŸ7A[ïxÙþúÙtëËRBçj¥Á„5ÒÊAél*³~QüON«¿‡ïSî™…ÉŽ42Oê?§Å*q…×vü·,›½èõ|gÓ¼¬¸I˜cÇq…gøÞy¤´¡ ™ubhs«*¸xoVÔøk[ÚÞ,*Ü/Fˆ.'9å9§œc„þVاóv:ÁR—bµ; §†ž’¹Äs½áѱߊ¢D^COJ\Yý4Ù4^g—çØ<ã ¸â^`¯Øo„LìÌ‚¥ÝóÒ¹Û …Á!NqÉÇ¢õÀÉ ÒN#ªg¢\özøP¢îèhN4¥â–ö™—õìV9GsCsÞábEÝ¥ Wô³KA; Ü2¾Æ–Dêî¸EZ,°ÇÄç*¾ÇOÄp¤ªØ^ýïïv*3°ôÆã˜Ä§”äûv5MN-Ôk þÌ[ךçoUËò¸~\>Ö|Äõ'V â/©L Tÿ$ý€sùˆlv„,ìgײbãf»(ôÄüÞWÝÂ\X‡ÿl½"'DU{Þ¾z‡9@ùADn¾Æm0“Žd™3+*Ù×÷ý¥´Oó¶+•›PŒ‚lí5¡¦ç¸[án»ÁGóÄ@—Æ´hzø£é*éõ–Ê>H‰Ïß,ÇØú78ý/K@›Ä¼é¥Èˆ`”JÃþå„%ØxÌqŒvª©“ùv÷ø{D ª3;jÎÐ ^ÎÂ|›‹þå�6ö²½_±¸¨LK9tM¸Á+g,=SÄ1qÙeibg@ê×›0½¼Tƒƒ‰³5lVöøÆ/ñ$ KçIL@#ÄñÅG¸K†¦\·´µ*Z¥H°º)~Ë.3óz™²ôÎ [ Ï®ÝbfeT֬ж.J²AÌŒ/ÜûÙ°Þi¸¤dpQn5sŸªêNþgI#¿0€¸Árl ?xkS3ηeh`‘àR•‹ Öü€I¿# tZÑ~Á‘óÍ HœC:ul^gáÀ0 Ë)[2†Ú¿Þ£ÛP7?ÝpØÅ™aHƒÀ¢ÄÈë�¹À?àL_{² ¹â0t6?Á"¾òÅÖºð@ཚ!xü¯BÒßÚCíâ3(RoüUzìÈèך¸~`1ÛäØQö˜D !nTäHÁG L;Qt êK(Žó@Öòš*Ëš°4ù½?\Œá‹ñÝ?¦¨*ìæ©,cú)–²%aœ@XêÕ¨‹„è\õ99ª"ý3 '§t à‡8+›ö<(iKµòaãO6�§ÑZŽ7{g‚ëêñö S¢3µîªÜg„ræÛ­Zù÷7oT6ð¶©³úÞC„1¼åáÄžÁÑLîy€c ø)Wd²×ÐÛ QлÛ«Y^ªqzûEg-¸N4y`H¼ê졎D·z<Èq~-Ñë˜bì¯\ý4Ø9ž!˜âId¼â þºH{÷%Ðü‚'£”Qèë„*b2nZ¢AÄkdø´ÿeLrGlmE³A›èò¡e¢»ÂùóÙ¹fJÇDÃf�zω^£" >ü_(õÆcĈԬ\È¢E•Mo7ÿ„é‰K×<Ö´ºX=]“- Ô ¶³ëý<ÚßIѬR— ¤ÔÁG·,Íõ9ß@„¾/|á8 ɲžìíH²þMC+„Ïæ£Õ¼Àt -ñÌ0IÉÝ[îU§j¢H¨ÑXa”Q`8o /Ì_Òá0Љ)òâYúz% Ëæg_öÁzÁö ¬,£†ùéocqŽ=œzìýÕù½÷ŸW¶TöwŒ/Ïz5üd7aN«p_/¸P»¤dNâî©EÁyß‚ž„eœÑÁÙÙåœÎý¶:»²ô]Ý•×òð«ÇàÊ'ÿ*Å ¬jd‘´ÇI€Ë?ÙÏô´˜2™»š90ãIJ/DkàwΕ=Þ˜¯=ÈöKшnð 4ÖLÃE´ 9?ªƒ"mé ŸBîºKÙÇ*…¼Òt:�\KûZA›ºí±�†³W0’(tÐ`^ûl³¹M™3õ/å^‘õ˜Ø–Zzõ» ÀOÕHá¹ø Û%ò6ü`ë}ðªodŽÝCg"2§fvµò[äeþ]ÎJxºî�(Æ“‘73ž©ªçIzÀ_ðemµi£XeÛþ¿5n»ëDOWS1’°W¹ÞeuQ|Å µþ¦GÆñè%Y2o %Da/ÿãŒ.R»ÕïØ5 rZÁb„‹¦iÉg?n$ `k£�‹ûéj£ªîÝ¢ÁQ ”ÊÇz¥ŒZ©úåEÌ+:ChþFî\”\?,Y+=0û<&)Vñîɼ>AÌÎ4çŸÒiß ±-®¶‰‰ÊŸm?šu{Æv/$áüŽ0× ô[1a5ÓPh§uuºC4¾¶]† F¥òøI¯úŒ^|TUÑçfúÐ,á0Út2TáÎ…è|ZÙö ÍÅBïìwœŸõgý›ÍáÄyóÏJŸ&æÝ][÷¶û_¨”ÕÆ0‚÷BVH %çäýÉU‰›ÁËÃ<;9œ'YŒi|ê,ïnh@>oäK¾uyH¤b—@KB¼@hß¿“H$—%Ì «…8ò“—¾'®·MލdÀ=Ý2¯@«Óƈ Ú.ƒ X 67KJ M.ú†Ú‚<š½s/ ”T`eD<×ì1>¯+àòÑË÷¶÷ì‚XÔ\ Lg ‡Gã¥jŽxs‰lå¬õY1ÂÌÄ/ê†xpœîÝ6Ÿ™ã§Ô¸BðUñmùÈk yG¹Orÿ}­G³‡÷§.pújÍ@ýQ¹LÒ×SgH)þ\!2«:í Q-ˆÎ‘ØÄ‘Š};F¦$^yñ)ïË;€süåTÿ‚æRüu—ʹó `¯]é<þ‰t©ø6žw Üð¿_>`»X–òÛW-I9¤_ïµÛ–·Ùo½¬©@^�:ô«¦ÅŠž‡bйŸÖ™êoH—u¾Ä½…çqÁZަŽ[¡Þm¾u…¿å%4·èmO NÐ8•¬i´BÎGlüºžØ]ÇDní¦å·§»ú¸sÌ‹u¹da*ÏeäO ã¥äK'³FµDáãw¦ì¬va®›N€Uo»2KY¡üK‘j A ´ þÏ|@œä,;¾1kF9eãÒê"×D9®¾_(PhGA–›zaàÌÿ|ðuS:¢±ya#bîÑ- pú1Õ·è©>¨4æšÀð"kÏÕƒ¶g›±Íçöóÿå‹ QŸÈþtÞ-@=©gæ¾]N]¿ ÿl(YÀÔŠßáÆ A7•êG ’%Ú"ÜQ: =ªÑl6à SOJ†bÁ°"n‚êî)áW¯"µ0£âN:ðfÃjÉ}£0/öƒ[–ÔíÚi,ç—™Zq8®<ÞcD€lÆÖ™MÓi<ïë`ñÚ3m›BŠ6ŸÑA ¨êýEÙ7bŽ­“Áh8,ú+ñoòEx÷ž])ãPPè»( m‡7©ØÔJÝh$EÝ—í@)öµ4/†×ík€òÎPþ‹×Û”ŸùPšË©»ºòÔâH`tWAðö<ËÅïÔ3º&œw4²Ñ£qŒ|¶ÁWk¦bŒR´§Á3iþ”F¯è@å/Ò½þü0ä³}~à¶$A_¶þ\×­Â0 ]´’€;ê0äVyÑO> iX {x« 9WñyÜæ= ”¼µß¶r1nËÐzäsk?¸¼�=*·™}ŠYcIÉ9tØŸ—øƒÇâaò­½Ì›c%¤Fw s#Ï$`�p`ûKt4”üДp'¬Úʤ FMÞåzg/a(©èîÑI|óæPõ ?–x ¹ÚëÝxYrýߢ€ÕdŠ »ZGó)þíáÖîì—ÿZéªãÑŸç^€Þvu÷§ s .­@?W-ý¾Ùò1.Ó©Ç>¹Çá‡-¶µå‚„˜V[w }:M&5¢pË>¿¡è¥›zªÒ 9Q᪬ Ÿÿ­OoŸÖ}ÞqÖT*¸(À1÷¡U\9x÷¤˜À È u–.'‹vSNA³“/µîN|·]µ%ã4HQÁVì7óÔ5ìÈ•dqS$òörv3fóxJ;ˆ» åˆÝF*„ßhþQï:QË 3Ÿa¤VkÚJ9/ô»7æg¢õùž@“·Õ4íÈãýb`ÎÜò+c¤=¤¥UŒ+èƒ$ÏþúÐq¼’qÁwIlY`z= Œhþ›,¯´NúzQ¼n%| J¶°ýL§T/‹8br"7ÅDxãUY Ð’XØ4uîŽDÈÖy€Š%Ž›IÁøŒã¶ MÏ;£ADÖ5SÑœ¯žd+³¹÷X�‰¤â‰¿É•‹ŽfÔUHëàs –¤1pNÚ_ÉúGQû*¼vm¬çýàU}*á}ÿa2ÑW?ãðÌ×ÈË\$´@$ “Æ»õ²³˜Éµ÷G¼›G[ñžÇ¥"E„el çËö~—7G· uÄTu=G|{…iÃ-PÄîÕIƒ› Û&]ïo+¼Q!p™öeøK"&†v’„L%ü!Éáx…¥<+} T8”p�?6¢A3ˆ¨— ±Š5—Ûù ‘¶'a—Áè±yÇÁ³e·2°ý†ñCÄúä5+ïñ·W¯[–®V‡ úra^Â*¬aÆðSKð0Ö 4E×$ú·p¼×›H6äUceON­ö„{²E³roîöëÿb·ƒîˆnU½d‡Ó}C¸ü‚jv°ÆÖNáS:yƒ±$®uA°ÛI쪎¾ÃÄ«œQW Ãd’æŸÚºTÔº46þš9J[ø| øÏsûÊ^jFÅÁПy©uAˆJžkä©Z[p+ö‘˜'F¢O¨½OrsfÃ+·Á^œ �—L0V®Pú¶ñL V¯*E™TÈy¨%jß¶ö§X²U:r“ïtŠšÚ?6»S&Ø]tàÕ€A¾ç„c°ýëâoiQbô:6~Žu”ï“+©–€å'ö³Çâ|ƒ ôç6Aˆÿa |'D^ò[÷�rß;½âŽ%—`qÁÚxMǙʇéë|¨©Æ)ÁÔ€þq@{ŸÝË5@Ò¬Jë+¢™=à#Í}«òT/²³\;ÐÌ˼ÝôáW¿¬0üÞçWÍTg^ÌÖø®½n_¯ʼn¾sw€RA*:'¥;N΢)Åãì@†f€dZliV,œ*‘Ä‹gâfŠ0Î]wgK*=’[GÑ[î¬MÓØþuôÊ*ùRT-1 ¦ý¹R…fЫu0.²È�7¾¾žB!Vo}”"òÚæ#HÿE9Á-ÀÆ�!:xŸŸ îe$+‚*×3ÄÀèÛâaOþüÔè%i`¥®Xç”îŽ`ÓÊŸ£ß|FµI©X /¿¢+RÞFP—.œð›3È÷¢dÏ-–œ·…—TGY°Ìõ‘¥¿ØB£ýnçÑe`Bʬ¦£‹±ÈÑ¢t‡hÖ.™‰Jø#×Q i,__ ëå/÷ÚbËG,ÖX¿¨%ø¡ï™-¬†è-Ÿ�hÂåhˆ“ñ´{½ì]Qï>Íù.Øi.½ÈYn³U‡Ìn�Îض·b\"ᦑßEÓOç+ˆ-œüçŠh×\FiYÎc‹Dj“­‰Âª¯›â|xžkþ“-8$‡vg¾.Ì^L†iU»�—Žú’ÁâPYMŸm›ÃxÈø\Œ>9‰•’VŽå4T±Ç`.Ò=ZæÓT줒@ð[Áž qjÝž‘ý4@ãò¥i“ûÕãmûWî¹U½Äc3æ"¯pR™ãMÈ`a‡ä¹zlÊŒ+Òé§I¨û­Î×ü@“}m$›eŠFÓ8g^”‡ÚC[¼£æt³Š©t‚Ë]¸ÿ5Û© !w{lLH¹¢ØC!ÉógÅ•¥ˆÉî‘Ôð”gºvô?PÒEÔàJëêο J$‘ ˜ŒÝ3u}Ꞹ%Cp¼;9YÈÕ·§½®C°Üƒ&Ír ˆÃÐ?#ÎÍ­*¡ dpBTe¼í˜òè(À%j`h͇Žç¶CœËŸÉêù³Šµ™§ð›µÃ­d;(Èñt’hÓ˜!ªÌüÍŠZ`PÇbþt|)¯q=e‘ö(ï+V|æG|ðÔ“)®¹tYÔ§�#<-{\ÀésݰYd«Õ÷¢2ɱÔ¸‹BY1àÌIPÐÙ[‹´6abRñÀ¯·éö ‘qE(ɉ2Ù)±•4÷ ÇÄN¼öBL^À÷£F] F_ù e.Æ€`ˆz“\´›WÝÄœ f ±¾`õM¤YLˆ'‹×9ZÙ~9¥wçAÙz¥™•¥p+Fú¯l”.¾{r¥*¶àP‚ÿvT¯Ròܵ#Kæé-e‡¸äŽˆ¦s6ÞQªÁrš‹K6A®â3H´ªL$‘÷°&2ZØù�z}Lf#:ÁJXᤂ×ò?¸¯’IÁñªÿÌKC‡rsTœû.¬é[ËêmÈDÚß+‡³®º½gÎ+„4býΚš·Öç[VX5�Õª£®ýcg™¾J (ÜÛp´Ã)ž„ŒóÝ,Üz!áoZ£®D—ì©qÄ©´”R\Ñeo–=!6 °EÒâ>CAâ%ó‡«È1Ëó’¤RðÀù`—Wæ\*,ØÍ3ˆ¸¼:ã?†Mx±�¢àào3ñIë©’¶ú!.˜ÑàŠ¥Ž3K/¬ÑN‘ÍÍØ_?ƒ¢n‹0¬”Øû´B*Á!ÐÒŒ€"ù1ëC"‹ÎÔÌŠ£9‚>ú 8#Bì#Ù§p.Ö3" M_‘fºV~)÷àZTóýÀœRŽ Ovº§÷5±‹í,qr¤áøÊ·EBöæÆ×nGzZ7­vÒôèI Ýó7ˆç¿ÚÄS—cò'Í6#ø¡c YMú™$xŽÓ8Íãƒu¡‚xWL~Ô7‚yÊ‹S¨¶4÷r•zû¥pœK÷m¹q"SÒ| |-NMÌ\a‹ ÙØY«œûŒ8­¨˜®üuíD¡¾ Ì3–×ó�Çã´j‡YQãGà[wÛO“i¯õSL]…€Oßú…)ú¤ÙîL»îð¾k¨$a u[´MØF ò!17u߱ߛ³#bõÝ|±¾ê*ÒÏŸ“ÞU¤™ªU¢œ‰f�¨Ñ$.⡃h*ËDv¡,;y)Ÿ•tßÍ%�*`‚AŸò€Ÿ“VfŽÙ>=!çï3)ÿCM…^˜0Ô¼v¿€†ëÀ²PÌôZ3 a„š¨=ÕVϼ“™Ù¼™îCŽœ+’dl5ëÜÂÌ¿øW­Õ°³[É@ØK±ceé�$à*`™Ö½s¡n«p —3áT?åSZ:z¸¿ŸB×Åp[Ÿîî]ßáIå>| ÕhÙ7Q Kˆ !E·œ¬6žø¨†‹ŠãCX{^¢�o¼™ïâ©<RÑ' ÇF½°™ Ÿl'ÆgþÆÚ:ÅJåô5ß”:q…“`ÀÏaº¢úàÏù H*ƒõj½¶4À 9IÜ~Æ™J"`¼cLB­™</'§K‹TŽ-BÍq•f„túΰåÕÕ|±vÔÐ.›͇¦¶*íË/v,b¦5I»áeæX­i®òh‹E¨´£È¨ Ld(ŠNã_j-¡ ˜Â¤PÛv‚Áoåâ\áîÀ#X_H¸Ç"óÉ Šn}–Ö™›hâ• Bîá€ØÀ¶Õ÷`ò«Äo¾Y}0Òâ’%{}# 2ôÞ²X¼Áp÷>â\Pé¼~ýÀžd˜™­sþÓxpœ¶€Ý¹›Ó~3 øˆ5[Y%a—BÿXNí› Ýf#WÀLD¶¤¯vX\ÕÀ_¡hÖ<á2; w>‡…æ4fƒŠºOÚî‹ÏÑ n'p¢ï£ô”,ãxŽ!,ëî„s ó;Ü’¡zã?íødWßE[ÊçÓΛÍ,š<P¢÷‰úGšybKÚ|'VAáQtö?uÔ1¬dǶ˜Ê(—yP¿d(ñ?ĪÏÊœ«€Öþp‘*Oº¢ÍUÈŽ‘¯ûOûõ F’®ýÓÕ€¯µX_'jжֲë€[¶b›ŽgÊ)§ëLn3ûxö¸¶I‰7Þc½Ñ:…[Öùö–áXôàx“6ð²¬]/£ Þ_j¸äó M­ˆ…Òüt¶˜ž4L¾ÒNÕkÇ;7W¹]ƒÿ)†OðáþVòAÍ¡ð ¸sµèðc­¶¢æ6€g$qQ9ãQ½që"þÆ¿•¿²£ÒìZê[úáˇù[ Ðn/O_ü‘DãJ¤OeÀ.hÒ„ÔÚ×xÓiˆÀ¾}ðܱ”è1»‰£üh£ˆÓÑ9)  LvNà‰;„ØP“s’‚“‘¥”j'¡Æ|»œ:Ë/IèûÓÈVé4!Љ%dI3Q,Ômİ7$©øõÙS“ÔÆBž´7éÏä'ãKqòœ€—Q†V5ŒlIœ֩½/C8QÞÌãïþ$˜zsk²È©ñLý/ÔDâ¤áf;�ßÊ좂?Æœ¢}k9®›·IˆMñøÃKßÑOÓÅ»¶­£’膢5îÓƒ%#¯»Ï9óæüȳ½Õ£‡)Æ¢ŸbW-ç7y›3Ý‚"„Á¼xOl2RÈ^#“«ÌRÍ_µtº);nM¤÷ÍÜÙ|a:ö êÔ¤ûû}/4*ׂ¡"[õ•”á‰×"7”3x\§’'¤òú$…ýgŸ¡ëx •¯ U½žü•ŠçJ®�–r]o¹eD þø"<>Çž@¬ì{ȶÇ>5\™ËØ…Œ2sœ?E©¡v‘ïâ®>Y¿pô’Ñ�ÅôE$U¦öñØY|‡ŽÅ-gÞHy5†üÕü€ïÇôTX@0û* ý¾Y¿.: î¡*a†+…5¿±Û±Û¦HÅø@½-n¦©m @/$ï§3ŸL Ø©#SìlÆkõfyvœ³×èýËͲ(÷âNûKŠ›ýƒtšÎK¯Ü…`?3+X„ˆŽªß^ªìivøzÀö¦ Ö>õù€9d_4S$äÉäåÕv,å¦hLåŒõy>]Äi ÿ~Ý™ˆÏ:Ë*N5 ü‚ID �ã>v°E”ÿoF%Ò˜ù½‘tÆ>v@ö²ìù­½ŽCfÀ8ñ+èÃqO²CaX*ð0îÀ<E" qݱöþ¬¿{ I&¸‘_U�4¤½I}µ=^¶Hêh¦Ø‘ÔLAþ 6¶=Yƒvƒ”òK¥©Š{^'<3X2§ÔÉhÅ îų˜@óêŸåùÐzÄ §fMìÐ(…×õcFXj)jüî?‰»DþWRg“¦6ŠI´«Ç ˜„…)(âŠß7dà^EdÈŠª4TÆfg`XÑq-‡’hmŠ{"1ÐW­òë.9êÂ:f!µ{¿´SlŒs xy®\ Á²oöGù&ã§hò~ÈÕ‰HØÛ|§G~çôošæ6�¨dµÐÌæq±`†B3‡©mX¨¼B†zÏÚvÀÖŠˆÕVmn¿M.ºÎ…ä\ÈÚæ[,*l•w:mü,¢Ä}{Œ¢þL¯CÁq‡á†´!蟇OGæ z4í/,ݾ쳂§…þ}4½ÿ–º«6 «Ð±Læ+=Û"MÃÚŽpyŒv£\}TA¤}‰ËЗwÈÝ‚ûLŸÞ¶èüoš]!S¨7ˆÞ«f)ÜL ^²iá|<«ÅÓÛ_9tÈ=þÓ|(5>FrœŽVÄX�™‹Ð¨¤ÿg1|›}Ê÷xzÿ#Æ–†ö=‡E€x Äò2¦Kç7”“zÎ>õ”’¯OŽ"ÝHî,m\;ƒÇ³RZ¤Kvú®å×^ï¬è)¯À¿ÎÓÏy*¾‘—ŸÓoZO¡Ê®ø5@m„t@ÝË­?›Ô$þ ¾H·~­Á®"iC‹d@[׋ãŸo�ÿ<çÎøÞAœœˆ*ÜW0ŸŸ– ¨šÀT�lê«/vpˆ<XÅ*Qåïû(1RK áˆÇq€_ëCûÍ3®Ëi½¸SI–~ÿ×/1œ”ÐÐßú– Ì.ˆGÁõPc}ÕõD<RS˜L^€…z’!y9[;2÷ìJÓà—fÁ]+Ëh¿`ÏÔJ,W»Ùwß;° <¨¤H?¥i(ìU»ûüš´W£7Tmñ¡!Wèܱ1^Ítþ*c?bç%_ùò˜†ôèG°•Ó$×>%µÊ ´êg…UÈÀèY¼™Ù(½]ΪeùÝ<iVqŒûh‰(L8Þ‚6€èFÙ·ÆY�—¥ôÙZÑSOÏé—¯Œ´?ÑÅI1LëÍG’ZˆY "¨a‰—E[î'"}˜ýöR]z\œÏ. ŽImfç‚¡¡Ó[ÁrSHéÚ轊Θˀ<SÐJ;*=²Íû¼ò:Î'^ÍeEáÉ®ÐDé—…kŸìET»‡s[ÏdÆ6hLëªå²¬Û[„å> dÞVZºïénè¹-õ UY%Ê£¦ïðH|�¾prDû,ÏTm^Û’f,X œï´`3ÔÐ s Gñü͘@ï¯Ž×øÁt¡¤:¼s%ÝDoèæñ$¤/J5R‡÷ª1ûzD²*ï*EôùK]cŽ)–BXaÒª÷a|¼æ<>»‡:÷L×ùø©yIÜéEª<ZÀÞ¬ÁL)üW¢+µ¤jX#L¾J£˜fRáºH·¢|@ÃDK óú©ºÍqbgtñ…SèÖ"H�?St›³]ëDéa*yzÒ:7è% “«ÙÁû@—Ú«¥eHØs–ö.óÁˆÎ½öò~æP9<B}#ü²ªùåHöè]õÀ©|¯h.µjUÜaÚ^I€È�þ9È$ƒNfB3½ý»ösª ¾è~FúðÃødºêê©üŸF‘†³)Ö­½~$ñBx+öXSë«8¤QýÜ<…ŸU-°-s-ãL²ýÐNºUó„« r'Uø¥¾ç¿´áÝïØxá5rž‹æ+ wr}óV4b…èY.´Ñâp.¨eZTd‘¦ 7ßnq¾#¨¤¿à¾Q}4Üf0b¯ª = ³œ©5¼ÐÓô«Ø ÓœécÊRXGbEæì<e #´ôZqW}ŒJ7M>¸PªíÈÚZ?•&¨�¶â€8·=ã®ÄTüjÕg;\h—áXL=)xÎØíE³¤pŒ9šI¼>Ÿö¢Y¼6‘GŽ¿/-͵`j.¡ŽIà°ÛØ^„<\“ð}K%q±Èiâ|UÀf/ðÒÃï®Ã-+·ijýض7)&çl ¾"£8Çßw÷_BÈàêÓc¬9rêƒJµ»¡í>Êà/š°PC9¸þ£Ó{½S »ŠòÒ|YéEkÿWÆé.  Á—e©Õð‰ò¨ÉJÜŠ­Ós=†ã/?䯦š’ñN­­y€™@¿Іuò^!¤¤Wêéâ!ãÂ6ä„ö³÷º€EϨ©‰TcàÖMŽY†¬œ©û´„g)›ù˜äØ™æw<¦…°4 ]6?ç$Îa±ÑÕ2ú×v»))œRC°9ç@¢¹›½Reòdùú–&„3'ÌOq£Y¹g©äÒ< ïà-ÍŒí™>æ`ÝO0ùGwz/Rw¿Ø¦m{\‡”½$ZWà}(•¸Œô˜²cÆr‘³wmW’zù¤ÁX|ie>'ÙÒ3S‘´YFÕtèŠÃŸÖSpüºÒá4É«ªÔ·ñ4¥©~ùòœÏcªØ,ʬU¸“Ð1˜7ﻎa"O-;ÓРf(  ü‘£-ÙššÖ¹ž $?çSºsY)Iù–é\«Ó%„WJÛmIÁÐðβh^5%QÙ«P‘ ñ½µlE¡QˆÑ·…#^vÇíÁUk5r‡4ô“d¬3xùGÂ7q±T#ÌÝ7.�¤Û¡JpWÒ„XSõ­Üá£æ“ê²¹$êÚCš¦:+òé£íœ(¡ÕÌÕêë?ŠÚç¾À£5=õUÚµ«Ù"\x„….3ÌG[î¾RCƒ'4R0s÷¼S´)N *³¯¨ ;Ÿ¹^9Àµd¯ €@k ’ô%>m›Ôøñâø”3®"elž—Ã!õÁ¤>,SóÏš‹5Drþ0My�ÔJÂM‚/Ëì›·±Àˆ´{6b²þ®ÈùGáåP‹�8óëo¡üy'÷Ÿb½ðªÐÂ<'·t"îò²™†)Q”¸dͼB…J »?´{ÁQ_.×bm?‡” 9Qö¹í„ZšÂ¦þ SXÕÙs§ò!æÛktð8¸²·?sÛ!à3ç]U!ì;“©|»±#šßM‰^C3 ¹#ú£šŠüD’ã‘Åï6x»Âu´LPZ… ¿[Îã·Ñv`¨Ý Y‰LäWëUÖÞslÞGÈm«ûZFCCÔ FûújíȤB.š È…e ûoÍI´¥¢{·¡(AÃ3¾WšÅ‘u²Q‘¤øù°3- !˜ƒH µH»ì½Ä’DuÁIJ*$ý\>“<Ç}e_n-#�̓©@Í<™D å9K†“m ¸¥N+ârÕgÜ?*èp5´cÔ Jx·©"³ù{DXÇûø– 0+\uöªR놡Øñ?%˜&m¡Ó3alçÊ…`·ƒ©rNŸBw…†[u¨¤†SJh^¤üºîø8i}šStÀÌŸ³¶3|Ä™Œ–3qÔsLÿ73ÆB> pàÐe*¤29aEƒÎE à ±B»á¼FýÙÏ­Âeò ’hW3\äÚwÍû‚óVƺ®npbÄC¨BR}‹ƒQ) Ïp8TY:¿ÒGl#Ü$Ž’-!ÏÔu+fÛi ¼¸^ÓËcÀÈVáIŽôFC³}‘/ö§5¡œŸÈÑ¢>ë: 0ø˜œ -*ÌìÇ¥gS`%m ã¢[b8ûRR¬0tW(=–_ti 6ç"ìé²AÊ@G±pA­1q1Z„‰w¹¥%\IÙ(Е*§â ”ÅüµíÈŸëEežo—…½Ï\I”1‰þÓ5é·8ûË)(4»†á9™$§c^Uª‘X§4ƒTû~ñ‹vÉgÉz^–•Ъ4ªŒôDÈxÞ‘8³œ•.£ñ¦‘C/¦N„¾áì¹JÒE7øŠ—›í %ç}×�Å<OÊ"ê\�Am’Ø®µI/_g™Fý¥ ò«yªs¿ç&Í5ÖÖ±pHX<P_xÒ®|~¬ÌûWQþ¯í4}rǯ‘øOÒ)V]`X&ßzŠÃ@ˆ‰ï0½}ÓMo5º{ ï>mTý!:'jf¤qM„0©ŽÈ;ƹ{ 7šÆþ»_hM`ÒZ É™¥7ø ¯LØ|l›É!<>nÈLÍ™›«öº;CEñØåyR¼’O-ƒzÑ¥*Smœ—|4Úª>œ«³i"‘f Ô¾mM2Aœ²%øØ”3ÏÑû]úð®†ßDbÁ‘˜šûéãO¾c²C#1èA€íyÏ¡úíÊ[nWÁ Ø¡Çx‘™©‘ój KY+‚ä4ÜÍðAÐ`ê¡Ï/øp”éd¹ð}™sàÑI‹èø„gè“Ú!À>ÛICÌú^¸'ª1EL‡íúZ€×q¸6ÿ,OjÊq¡µ’ÙÀ«Ä•í¬Úž=Ò3HË4„Êøû2Z­±ñüIyŸXsç†r!ÔR;lê4¦øâ"« óL;âg­Ï:Šc=‹´ :Ÿò¾ªJú¯ûlÛí#ýº"þ™ü7s~Ûûö‹÷Ú#ü5ÉûlØ|—;í¡y/ŸEþ­"ÿB;үÕu?†´ÿòïÛ sç1£äûu¡ûmoÛHþÚs‹öÓŸ¶ÿwÛh_i%õ_oá®Ãkõõ_Ô)ϨCøj‹öØSêw¾ÚòPSÿn”žÛ“ñž³,dÔ+[ì†öØZ°¾nÕ¶eåÁÎî:&oÞ -h,xê 8êM|)ñÖ4ùmS“øˆãfÞÕ³ 9´â¸^©m™)MÊvÒ½Lµ˜ÎÖ¯2„a®´=^d¢Ì„œûØ\Ñ€áÝÒl¹å²§º\DLQMqñg˵*4,n™T‹'Qÿ9ðª ñ”lfSk'”xMÊï%üT¤J©à¢ÁŽ]&>÷ynð°N‰-ž¦­lUÆ sPç¿–CØÌËF:¹Ì)ô?Æ+Â(Ut¿‹Ëý˜þÑž …§îãOÁ]¤‰ñM}ÿú„L FPR$ÇöÆYh,zÿ1o;)…³ºð) ).±¼÷{‘ -‚eŽòò Â;RÅÑʦä~ /8Bþ¹rÄñ�RºÎÍ\2RC‡³\¹ýÖñ7S&¤a†Iu× %»}õ(—}d E‘5µ®<˜ã%ŠÕ?©ö×§¾ Ó-ªûõ@—r„ÿ]ÏP1Ëøç‚õDe<À�V¸ñ­å‘éOì:piRüŠTøy9²n :äN(ÄÉŽ3o·ph@®d¬×/ühɈî\¯¥ŽS{@´Ÿ‚Èh<“€LËH_Ÿã•é°‚“ây’¦@Áªæ) —Ï!X–“‰g\wÚœŽì•ª³“f­ÎâéU#sÙÇÔpýn…\‚H :lnž; {Ô'˜C³ßožÃ…HmA¥nÍ Ç+°¦g |ùêá!·˜âB uÝ·šr…q_øUóÿ¾I7ê…ìÆá…C(«„5á)fÔL½-Òo«ãÅ}_ôË®4<!`Á©¥nët.*ëÌÔÕpfîi¸™~}➎ßyõ:—Ý2ök´Œ}òæ§é÷ýü&´.¾QŸˆï¸xÙ!dÓ LÕ½N2‹ߨ@@õ/Ö¼íßVi‰A{6ì&Ú«øÛ×c”÷Âx¶,#Ø[ÝéA­e´Ì § šp?\¥žMÝÅD’Ÿ®)±g—ðU)ÂÖam½ïÞk%kÈycœbÿn˜I<ý÷†Äþ/AqÅ 7ãkºÖ³·7¯{(ß�±ÄÉg/zh|�rpì.2/eëáûí;ç3ë'lÈóݘtçpp;£:›iR¬YŒ\o?2¼ÅÀ`~ݧ›¡›Å±k*w¶S!D'‘V•!í¢>í•z°u»¸ˆÁ®¾†Ùý±Ÿ´R§Tckÿ„;ÖõŒmÜBçq ÓlEà93 °Õ“XNèb">7¹y©_þ}\&NNp%X»ß .¾4M’v`×þ6Ü7"ÖOÕYG½ ‚ðÎ -ùy5ïrÓµÕ†…&$�ðz5sP�S‰ˆÿe  ‡4ü6†ã’)>k–!ë†çÐ{öWù8µ»›ð¬�óB†g‡Ïh„e‹—öYìY¢Ÿ÷÷†5 :ïàüÖ qc_˨¿N2ß ü:­._LÛ©Ý(Ê1zå¼i%âíŠ7zÔñl±ó�ß½–b„ÏôC{«Åä\à-æË«£¨¨¥dì[ÇP–"2˜ÒF“|B¡j¶“4é‚`/‰FÔ°^L„ýEׄ9æá£|X<2H ˜–4‚Àx™ôޢɑ€Ö*­_0¡æŽc±sàØàµ]-&Ѭ>“›C¤Ià©c“®ýªtP¹¡Éi–bJïx¬²ñÑÚÔr;)¬M¦Ÿe¾¿©º1¶“÷€Ó¤®w—„œ„¸Úm.ö˜0NÜ\6h_/&U7»æ-ÛW$Q`Ev³ÙÍ÷Pbâô´Ù úoŒ¼¸® {V¨P2!a#âI)¸ûî­5)S~²¤»£÷òù›˜S-ü[í|$ZÎû ¯ð2­Š‰M¿uµ9Ä>Â!Úij”ËQÎR/éå0K×»–Ð�³c"„·´’¾š_ì[+@ÎG+¦a”q³å«»‡Bðà}¡틌g“L/d¯u«wø¹x 懬ß.ÅATQN‡ÃÉø lœˆɳáyWbs' LïªöV I<Èkàd®<“„¼‹9xH Œ}lêù6(N\/ÙOi£÷„D£D3¨¬"´7áˆ=©c²`âš’ rXí°AŽw’>869tPLÅà£Xk Fµæ@}ÃŒõ`O¢júòE¤öÿ$¸â%†³BNîóBeÕ-*EõºAís‰-Éd¤£Ú¿#ü½5XnÈ=•ñv Ç6b1a ùãIG $„i'ŸC ¦+E9Ì Ò/üôÒ]ãµ)²Ùpo¿ÅÝäÓ£”“Dmû¥ï¤’eÙƒü-O 7Œ0›]¢¡±xiÙ +—Ån–¡feÕyƒƒp2k(ØÂÅÛ—¬‹é!ÇâlØ~ŸFiÂÃóVÉ›±ÚOƒ™üT~¨ÉÿN* 8¡‚«œ\ ž™fG�Os°¸ç!E„–p(Méû‹ÇW¸o‹Nl–dù©…u[à÷}:ÿ;ç±j ÄŸ ²¯É—Ë罦»}tEbâ évU%;é÷ Èó ÍOÿXãOxÂËÅqì½Yo®z¦b*˜íreF¹Æ$·Î²™»U>ý›ð)÷ßT-áÀîšÑh™i«h˃gÕÝïŠ2oâ+^õQ;3C¯ÙâÕ]ó\ªNÚþ±t‡âØúÅé¯ 4"ÅSŽõâB?Öbq0ñ‹A½íÍæóè#¦/­…µX+�ùø¸ª�A…”Aµ¿…owý¿Ù<qÃ’ZÏã”aˆ?ŒÚ©Í?Ú eÄãÑÖÚÌŽÀ÷Ð ‘tYÛîÿ$Úz¿Ùõ_—‚-×ôš¥J»ËÂ'.ˆÒ0@Ý9›Ç1me™E jlˆ¨ôoäNæ¯F”?fô"{Òþôh¹í¨½¾‹ØÒðÕ‚LwÞwò’dþŒÉW™tZ]¼œ%+­Xd·)ÁÃܤ×b írËi½àŠîC­æöu&sÛWÈ6“f<Çüg8hXÉ‹N©ÞBSæj© £Š7xøM mæóXvKÔÿ3u'üeÓSˆ*še¶áBä†P* Ø6–¶AÏÜÈ"©qÀVKÑ86Õ“ ÆvFª»›8WU3ê_ˆ¸4U¸dy±,Q³Ü D—OúXGïÙ9"bÆZìY4â†øföÈu¡„>Ü{£c1:A@¡‹Fb‚€à/BІZ,ÒA“Ø•+éV©¬e€ž`a,§s§Ì·êLhúfèYU# r‡ìçNÍÄÙ¯VØycµ—tcjmÛã$ÅnS­ÅÚ|Õñ}Ûa�‡[ÊNASvõ‡Vf Ü.ß”"•+£ñ'9 D×wªŸ0�¶{ÿXÿöµ0»›}¹Þ͆[ÛÃ|9$öÒ*f¾¦~=#Ƕ¢ vX}в„.ê9ø7e5¨Lx£pºÀÙŸâ­És'·…KNãSpñCÖ‘ÖEÈ;º"ÕÆ¿ð.¢–ïÂÖäl¸ õ)“`¨YY§ \Ê�öòå{òŠŽòá†0hWn°Æêò“ëà¿ý"Õ#@¡×ÓéðÂ…±@=ÄùäÅ~„6BgºúýkGr?�¬mð@‚ óºYŒlªVÓϲ¦ 5Ï/‹Ô)Û$hLԞ̞ðob6²ë-3Œ\d³ýBøZŸä;$X}ù“û­ÂÔ~_`¯‰»ì¹i¡¦'¨ÇÃ¨íš žsöz~eWöLŽà$.è“rf‡Ó›ôâ•Ô©¢wp 5±ç÷Ú„³3Y8Ò’Äåñ¸ 𵄷øô18ð„½ éø›ßD¾¯ÑF*ÁÓ±1º¶ŒK ÒM4qWÝàæµõø%IŸ—Dˆ‰zð‹Ñ6½ÀGé“ä–’èžðòPSÿ@D{þnWAo©LKh‹c@º„¢ä> V~ ¦KÊ�K>u†B÷àºAË4Ór¼ á¡&lÚöD7MKLѸUHAX¤. âDXVoq®Æñ ckyf«]QÚ8i{ô 岉¯)¯û+¾�ò9l¿ï<[Ô~ÈÒïe’¡ä%@À¿äª 'Р׳µxVÇú^™<OMÇ®.:Ù5e(¾`·[§gŠýÓ̱†�˜Zã{¢>Àçw]ÔŽwo–ˆà÷C¾"ÁSê’ ‰¾þX^Žhs/³ñ¡'ûq„¡'_E…R°m›Á9˜ÒÈy™b—‰3ÿ<s;‹.±q|ttc~к;÷ëüèq€áM/§ˆ æ ©öE5”’°:ÞAnÆ~^xÛ,Ä'^„ ìÄ19ظJ«l`?mä}{¬C;š[°ôª™îáfxÅŸ±üvO ÄŒ¡Ï_Qæ·’£èÏÞ·†j¬Oqëy@õË„ qS3—ŒÔÄ­nn¼‚¡¥5ÐAÌ>¨•yØ­0�Rx_Y4Bëø¡y²â ˆN+$;÷ø—Èíÿ0ÂaŒ7ÑkJÈÑö€dãÐŽ½DýT“‘Á^€G—.C ù•È\£tôßåw'TP°^d¼ú¬!»ƒ>wœ P1çC7g—Ž >–Ži}~Ö'‰©i¹+pBO¯ e`I&FÞž„ç¤(f®þ‘#>xšÌúMÐÜ·Ó¥B·°›olËq¥0ÊmqÕì{]NŠ84¹ñ:d%fž‡/–çF… Ÿ§BŠ©>R÷!ÄT׉­mƒ‹-¹¨(ªÁmDŸ‘vPÛÎâsøSb‹³òxól¶á6E  $cfø‡¹8%…†œƒ?¡mÛ©6èrb_âáðOõ·0Þ�¸A9Å ñy޲Âk‚Zà«%Œ¶› T®z:Uì;>ýFN«×8ùHpkhÇ”_¹¸7AOë\“…(ÓsjyJX™<•í½©pÛBÀuí0ÎËvD á©§yT› 4`_šBKÙ£ú ÷מª°Øwä*.<§Ç˜ž¶¶(Ú[aJžä°cìÔ½û¶`•^ZåĀ둵˜á?P5âžìb­IÝ™|�÷!“bRؾl?*ãñ1Í Î·"Áûq/‘—±eډͮ»Šé*“½ŒQB)¢m¿ì‚ò+ñ|±=žl?Ø ÙžøÇÞpYœººþÅví7¼žÎwAH:\M))waÈ5X‹Ò3¾&':iàá<óPåI´ÈPõÞ$-£1cvGØu¾Ïë«ùlóüÔ¬tÿ…ÖVG;‘ŸËÁñFÄÝLÉ7–SŸžô0Wø0–ˆk'jÛŽè@ bé€ÔyêK³Í6<Ü®L ´yÉÿ¸C){ZÍÂÂß–œ\Å´øýés˜ùÂ×÷”[e²ÚÁ0á˜_T:uÛsü BœPVš¥ïcebU*<•^“*+ùì+M°U{)¸IyÛÉ…>pžÖuwYGÖM;vz- ¶ŒK\vúUY¹×±÷Š$9ÎÒ5¹•\ â³ûÛíÛž5?š»6̽l‡d�¼Æ=:×�h°)sÞH ˦Ûdln›?i±5t¥\µµžÈ†n†We<q·caÆE¡oì'ËÁŒ?i2 4]Ú§à] Yö”P™H´Á ·É=ÿ,äTöÆaf ˆéŠÜ9ï�óU.Jr„o¡†{k¥lÙêpyP¨´BBú!xF'HÄcб|0xwâ`D”é1î°ÈTóã¼ò~í´&ŠiÌÅÐoDŸ(U~þÖ.v¢Æ°¸QÙÇ)O– ¼°8߸(_xi;£ÅÇ ‚À¢ã¬¾`/ ‚ÕúU±ÌªÆî¼÷•Y´éë$ú¿ÊîP#cšÐ(÷B£x˜[ÜwqÖ®UЦ”ÏKOlZ¦å|{3¨‹-'šfî¸=€†½#Õî+öiä±ÐÝ$Æí'¬ª GÕî¶ìÖI@³Ì„Q*¼ÂïÿcЄÿ„ñÞ­õ®êSdG¿bî Ñó_Éd/žéÜΘ•w}Œ$qïĈñ¨òÑ×·ÑÀô l¬½½`«rî¶vÞJ/?w8LP ¦ýVGĵÎTPHÆ'dýcHbpù¢w£î¯n΃G¾@M•Mÿ~Ó·7Ÿù810º4zŠÂ‚_e©+ÞrÌïT¼O‰~ÆõƒB¡Ä‰SM¤ø‰tºÐ¹Í—ñßM(] ¹( ?ê¥5ê[f:)ZƤÀ&2€_éÕ™füìUï=IHÆÔà³€R�3QÄœÂÒF³âûhŒÝ"×vý5_G6ÎmñÙ 4ºM)uë´óÂÜE³÷–*lÖ¦ý€©H±÷£Y¡\*¤ô¿1 ž…Ü#g ÓkG=-p›ewÐgª”µåç=lZù}1¨Ÿ*JvrËÈ(òoqºˆ1nŒó]eècL6blÁºÓ,$x–ºîÇ€‹.%QE3)LÝþàƒVuÔ̯!zøæ™:+Å@õ1ý$a^{ð�\½-¤Ê×buõ‘1â1Ý«ÛXjyAºÜ0¤C„Ÿ.¿q¥žä5¡sÌý‰³V$§£]vߤø”ªv<mávjšËè½Y¹¶£Í¿ã”V:ú¿®Ë‰ ·OÂ]ªiÒÄ1zAå¶m\*•]¼xÿ:ýËVŸC8©Œ‚Nø´ß4âÓ;µhdùügÛÓþO6Q>¬pfs’„‘'ü°‘ÛØ, ¦+ð•UÇú5›ýEéknAªÎ|!¹Pÿ&aˆBZŒ©ƒ|·ðUþß…l™¢¦{)Ij˜uîW=•“­l„´¸ ùĆI“j¡pìhÚµZ»GÉ )2Œ‚\ÏÇCõ\Zº ±UŸ×—,µÁBðÚ}ÄÔ<r2p‚J–usb+»Öô.Ê®vt¨*råéѦµ+ÇÑX~"X^£²â±µôµÓ ©b÷‘àKK2U^r9Á6¥Î¶Q8»@r‰œ\Ôp^Øß}~èªSRŸ“[¼«×?õ³Lîµ]pÇw„E“ÇB|¦ é Ìb) _jÖOä<¼MKàCǬi%(Æá‘ K·ÕÕ3ômÉϬò»àò/Á~vr>ƒ´&í°ƒNÓÓ8|í(¦×Ÿí›à±-éfÜ¢¢€}¢é%áµð𙛦*ÀÛ8 1¸¬ö´x*‰SghíÝ*8ܽŸ«ÔkkQ…&JåÍ©·šU(W{WÅV“•ý‰HáÔ¯Ÿ Ò-òú¢©fÛÐb3r]ï[¤?3×A&÷¤HÇ6ô²œ ڤݙí0 ÷䋈â"l{\}£v—¶ãÏhQl(Ô›E¯ Gñ‡Y¦BB˜†ç,º,\ìë Û"‚w?ߘëàÀ éH? xûa†“Otïm±¦SQù_x°âƒ_Û9ühÄ F�& ŠZ/ „À Ì= :ô¬âMü W [K45a.+]¦ò*6ð™8„ÄZÑ’Š YçBsw2‚ãÉÒÒ#èëýüW)3~»Þdvàû†Ò�[7V4Ä®ðüúá àáÖ¸ÊVûïE_�+j×¶½ÉÊÏ—ý|„¡kE*è×Q?Åu_Ñá' ƒY'åÞU¦ý¤õ.õŸ? вìÇÉÖPgeŸ·t0<µ …ûÎò¥¢È—×Ë¥܆  o žür]“yã.``¬ÔâYlÛ1÷"ìÕò[wp¶ì,Ýùé ËüÐ §è‘Kd¸Ì’èŸr36F7Cóv'’Ä$yQÑ ¼2Mt» c[$6ks‚[cÅB æhŠÄ¼Ÿ$zh²,_“î{€ˆÙ3ÑÇ?$ˆ¾Å ^a^Àí¾@d™¼cQWÂ( 3]jz áØFÉ˸*£*ƒ¨‘á%Þµ³%x”zxâåÊ»¢5~&jˆç¨Æ‚²;!€*)CGµß òŽ52àïC$ÁY‡DŒ+åÔ.è%0WðoQpÖ3[Áö ú”NÅŠ“d!7S¢NjHx°6´ )È¥‡eâsûT¦ |%¯Í<´•eŸ[Cš\>8{\þS/$ƒvà‰e<ôæ#Ï}¢æÇEv_$ òc‚îKb'}õ411ïÿ4B¤ØoŽ1ÊU׫PÂ{#£2Ûücè÷õG¿ŒLˆdüy"ùÉÊÍbEÀ~P çê-ãŒÅ ÿs�òž‹lõ‰¢ ™EÉ‹)x"°±uLÒ3 ó@6È­ü±âL#ÄBaˆðÈ÷òµMí>øòîeQ¹(SÆÖØõ©!qyêÎ0ù3A�›´{� Ø\oø¶9Þù˜þ¹:På~õÓV4PböôžþèVK0<Çܱ?ÈÞ£ È‘ùì:;…YN" ·N$<÷)8(~R2س3ìc»O„î(„`J^ÅÑÏÞïhá³y¦ñ'õf�˜ÄÝbëùø°ØÍˆ¾pɰãòãÆzŽrkÂÝ?ô|T6ýN„ÍHÀ›ÀHÜË#éûW猣лq”Rì2­RJS ßWp;ò‹ÐÁªEZ©HlâìTªP ú1ó«"cä7›%º^7Ž_øT34n2¡G<Šð�ª®0W±£Åtl†./`Ë âE?C8›àX´`wÛ°&LŒ7Öo`…4!žà€ _~"tw‘6CR%eHë×5^äf2|–kG÷§"0Æh“‰MA rÓ*¤=buµ–„Þý;s�˜òUºgèðÌr¯ m�¼à­Ó1W½ó Š¿§³hõQPÑQÊèó1C%'›ÈE™éè¸!Hùwï!ÆŒÈã:Öì”ÌYR€k“»±ɸ_¾SpÒ×;>=´íŒ-KFMí†^¬üNk>VÂ2ü4õíC3S ýŠr4׋6BúŒx¡øJB ÃiŽÔJäÚ]PVîé@IP;oý¸éjÊvž?“_Ò_õ¢ð—oÒá8†T# @M¿èÇò$CÃú…ÇÁÑ Ú"€#A,Ò¿Æ‚L–ѪÉê!†×¾ƒ!Š–‚IÒjàÀ'[ÓiÞ|žô¬e%Mjö¥XÏÈbÉœ;¨¹)×ê9ì¦^<ÉO/ué¾?;ÈÆèº,ð܎éùcש^‰Ù']î1a¤¢ÆÉ›ÃÙÉÆŽq::ç>Î`¶Ò\µÇØ@uiÆõ r ÁK¼oUaÚ›r‘M˜xzbÂèÿ€…Õšƒ)Ç®•(JñÌ×)”]¼áâ­w½ÿ¢€M:¼ Цm¹¨ìö/ã:ȯÓ_Ø1ª²å9ò+99ß‘â”ãüꥮwL Ë}ÅíGCRpÚà¥Çý|D7"ê·«˜ Û4ßG¨-—KüÇ¥Žé<‡ðÇ/…낲¬ÐµRßåÊÅÊC©ÝßEVä¬rËJè’8÷¾G­‚ýº¦ä¥]n*? (-qìcg…ÿr�¿ZÄ!^k[Œ¨X© ÔDÏbfnÎ^S`á¼¾«gÒ, ò/:ïéF²‚`{±}]rÖö=•4)Y’¤Îûzˆ,ážæAyDÀŒ`QîrÀܹ‚%‰Æ~ ÛÛCo &V?˜óûK I6¨›ù‚¥Â¥™™ª•¡_Šì¦æàÓrásñD› Ï9Í·Rí±aÕ ke ^NU3à{ÿƒ/Á´s •â5g&7$õs©='YÄæ'€ÜÕëyÿ<ä¸×Ïùkôþ˜¶lgsÌÜ'ïD®< .V-ŽTª'ˆeÑÌÉOIxÉ(ªE§Î8PQë*AtØN¨"A:‹»e5êþÌ{,Ë:õ$Ö³fšZþòs„eÝí¡âµf«dõ³zgy¼ËMQt:ÁÀg?†:t›<MìIÚÛ>Wt{ŠpÅ”‹ÀôæìdÊŸoq{í&LAZD¡ØPxo&…ºqë:q{.8̃g½&†ÁFCw ›n}ÏÊa9š3ίƒé"˜B‰9ÇMYj®;O·/<Rl(LKŸ#-ƒ„ò¬Ö7P.Kê4*XÛiD9ñ¼–Š{œ˜©V`ùwÉp«„ÀÇ(‚â¤`Ÿ#<¼õ‚¥ ƒÔZ*GaÓäVŸä£ïK‰¡£ÚDøü©u#•ðe÷$pn»A‹71ðÒbÏšÁ á ÓžÜ<c,ñjŠÿW µùˆ]ûÞ9EgÆ÷/×<°BNxªs ¦L°ÜSÿ+01AU µ‰B´Y¦AÉ2€Èº3Á©ü×ycÛyN­}<Ö$–NCF`²Ÿ5‘Üq¼î”‚ÙZØ—9ÌïÑØ²é>ømê_QAþ²„<�,·µ‡ÉllJ@#&¯þ¶=ý´yúel²ÎÑO±hS«‡Z º•øp&›ø€Ž3EèGáùÏäÓb .Èã”V:êý+¸Þd™€¿Í¥tñΓ éJð6@+Ü¢QÔ¯bÆóæ(ð7’Ì_Î?€ß‰@‹§‘ÒˆT†ÆË®ïO°?IBHÎwÃ#=ÆZµ»3Ÿç]O:×$'“úÌô,¡%‹5 Û Yw·j=Ÿ³Ç†PÅkç~Gªi1’pþëˆ.ÀUû óÕC?t,û?@}5â®—týOSÆÁÁôÚô ôR&”­â‚q°+OJáªEP¿6±c_Âc¨Ãp·¬_ÜH s*á?Qº¨ò¤åG³uìö¯¼E<Ò<ˆß©ÓX7¿Ñ49å„x„ï&UÆ iŽEÊx¶=³|% p¬Åv?™¼¯RÏ!‡F 2ù÷bðø³jIÇ}¾ÿO0©™(ŒÀ…i0ºÏìb+»¼ï‚óÜÝ¿3n@zˆª´,XëH"ÖèŒ\;KþÏR*z˜ƒ_2Û”{?ô÷«¼àWÉ…þh%¶7]­Ük7‚ “é£Ý8ÜñL;âºà{rîwF“Õa7‘«Åî“w¢ ŽËí50ÞVR ãŒÌ0Q˜’';;¯J¶ÞŒ …¢ÙÜÝ Ç2±…ó‡Dw–n¯‚)<R :QkÚý)M6=U¬³{µ7Œ·Áœ®ßBëŸÙèr^™=Uyh¾ºqƒ)MºùÊ”pf²gØ=¹ûø”@÷ƒa:å x¬ E„óØÅ»æïc-áøÅa kl î ?sẺ٠…]Ç3>Ä«›>{‘”_,6ÃU‚ŠÈR>Ùfñäõ°3óDÊß®[ÿ{IˆT)û¼’¦”ÛǶ±9 Ç/º¹}*¤}œ1LFH8§F!>~˜c.£A’ )}ã~‹xxÊÏuv¯ë….ÌêÆÚÍãlS ‘xꥻk^„Ó)gK�ïÜ÷ÐCacV@øôÀƾÊ8lñovlÔE7’½8…D›rƒtZ©Û·âl]åé K$ pùûBݤD@,Öé+Œö?Æv+u™ùQP RH¡¢Gáׇâ@_ÿÙ������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/qt/kid3.ico����������������������������������������������������������������������0000664�0000000�0000000�00001261536�12246035113�0015702�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �( �V���€€��� �(�~ �00��� �¨%��¦(� ��� �¨��NN���� �h��ö^�(���������� ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+++ 444X333444™444™333444g333(������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������333333ˆ333ð333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333á333›222W666����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������222R333í333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ý222Ê333†444@+++��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������444Y333ü333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ó333´333n333(����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������5550333õ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ã222222W111��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+++333×333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ý444Ë333†333A+++����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������444b333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ455ÿBRUÿDVYÿ6::ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ô333µ222p111*����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������333Í333ÿ333ÿ333ÿ333ÿ333ÿ333ÿI`eÿ|ÌÚÿ…Þîÿ…Þîÿ‚Ùèÿp²½ÿYƒŠÿCTWÿ455ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ã444ž444X111��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������444'333ÿ333ÿ333ÿ333ÿ333ÿ333ÿCTWÿƒÚéÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ~ÐÞÿi£­ÿRtzÿ<EGÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ý333Í333‡222B$$$����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������333333ÿ333ÿ333ÿ333ÿ333ÿ333ÿh¢¬ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿwÁÏÿa“œÿKdiÿ6::ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333õ333µ444q555+����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������333Û333ÿ333ÿ333ÿ333ÿ333ÿ7;<ÿ‚×çÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿƒÚéÿp²½ÿZ„ŒÿDUYÿ455ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ä333Ÿ333Z111��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������4446333ÿ333ÿ333ÿ333ÿ333ÿ333ÿQqwÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ~ÐÞÿi£®ÿSt{ÿ<EGÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333þ333Î333ˆ222B@@@����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������333333ÿ333ÿ333ÿ333ÿ333ÿ333ÿm­¹ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿxÂÐÿa“œÿKdiÿ7;<ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333õ444·444q333-������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������UUU333ç333ÿ333ÿ333ÿ333ÿ333ÿ:BCÿ„Üëÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿƒÚéÿq³¿ÿZ„ŒÿDVZÿ455ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333å333Ÿ222[...��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������444E333ÿ333ÿ333ÿ333ÿ333ÿ333ÿW~…ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿÑßÿi£®ÿSu{ÿ<FHÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333þ333Î444Š555D@@@����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������333Ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿtºÇÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿxÃÑÿb•žÿKejÿ7;<ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333õ333¸333r333-������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������@@@333ñ333ÿ333ÿ333ÿ333ÿ333ÿ?KNÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿƒÚéÿq´Àÿ[…ÿDVYÿ455ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ç333¡222[555��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������333U333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ\ˆÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ€Òáÿi¤¯ÿSv|ÿ=GIÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333þ333Ï444Š444E@@@����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������222±333ÿ333ÿ333ÿ333ÿ333ÿ333ÿyÄÒÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿƒÚéÿi£®ÿRtzÿG^bÿDUYÿEY\ÿLhmÿYƒŠÿj¥°ÿ‚×çÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿxÃÑÿc–ŸÿLglÿ7;<ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ö333¹333t222.�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������999333ù333ÿ333ÿ333ÿ333ÿ333ÿDUYÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿo±¼ÿ@NPÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ7;<ÿQqwÿv¿Ìÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿƒÛêÿq´Àÿ[†ŽÿDVYÿ455ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ç222¢222\555��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������222f333ÿ333ÿ333ÿ333ÿ333ÿ333ÿa’›ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ„ÝíÿUy€ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ455ÿPpvÿzÇÔÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ€Òáÿj¥°ÿSv|ÿ>IKÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333þ333Ñ333‹333F999 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������222À333ÿ333ÿ333ÿ333ÿ333ÿ455ÿ|ÌÛÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ\ˆÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ588ÿc— ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿyÅÓÿc–ŸÿLglÿ7;<ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333÷333¹222u111/�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������555333ý333ÿ333ÿ333ÿ333ÿ333ÿH_dÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿyÆÓÿ567ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿNjpÿ€Ôãÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ„Üëÿq´Àÿ[‡ÿEX[ÿ455ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ç333¤444^555��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������222u333ÿ333ÿ333ÿ333ÿ333ÿ333ÿeœ¦ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿTw}ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ@ORÿ}ÍÜÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ€Òáÿk§²ÿTw}ÿ>IKÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333Ò333Œ333F333 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������333Ï333ÿ333ÿ333ÿ333ÿ333ÿ689ÿ€Óâÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿÒáÿ699ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ?KNÿ{É×ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿyÅÒÿb–ŸÿLhmÿ7<=ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333÷222»222u4441�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������111*333ÿ333ÿ333ÿ333ÿ333ÿ333ÿMjoÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿp²½ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ?MOÿ€Ôãÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ„Üëÿr¶Âÿ[‡ÿEY\ÿ455ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333é333¤333_333��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������222„333ÿ333ÿ333ÿ333ÿ333ÿ333ÿj¦±ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿc— ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿKejÿ„Üìÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿÒáÿk§²ÿTx~ÿ=IKÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333Ò222Ž222H333 ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������333Ý333ÿ333ÿ333ÿ333ÿ333ÿ7<=ÿƒÙéÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿZ…ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ^Ž–ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿyÆÓÿc˜¡ÿLhmÿ8=>ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333÷444¼444v4441�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������1119333ÿ333ÿ333ÿ333ÿ333ÿ333ÿRtzÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿV|ƒÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ578ÿwÀÍÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ„Üëÿr·Ãÿ\ˆÿFZ]ÿ455ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ê333¥222`///��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������222“333ÿ333ÿ333ÿ333ÿ333ÿ333ÿo°¼ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ\‰‘ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿEY]ÿ„Ýíÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ€Ôãÿj§±ÿTyÿ>JLÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333Ó222Ž444I333 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������@@@333é333ÿ333ÿ333ÿ333ÿ333ÿ;CDÿ„Üìÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿd˜¢ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿeœ¦ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿyÆÓÿd˜¢ÿMjoÿ8=>ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ø333½333x3332�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������222H333ÿ333ÿ333ÿ333ÿ333ÿ333ÿW~…ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿxÂÏÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ?LOÿ„Ýíÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ„Üëÿr·Äÿ\ˆÿFZ]ÿ456ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ê333¦222`777��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������222¢333ÿ333ÿ333ÿ333ÿ333ÿ333ÿtºÇÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ„Ýíÿ;CDÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿn®ºÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ€Ôãÿk§²ÿTyÿ?KNÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333Õ444444J... ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������333 333ò333ÿ333ÿ333ÿ333ÿ333ÿ?KNÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿQqwÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿQrxÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Òèÿ…Æâÿ…Åáÿ…Çâÿ…Ðçÿ…Þíÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿzÇÕÿd˜¢ÿMjoÿ8=>ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ù333½333y2223�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������222W333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ\ˆÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿk§²ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ<EFÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þíÿ…Âßÿ…§Ñÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¥Ðÿ…¶Ùÿ…Çâÿ…Ùìÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ„Üìÿr·Äÿ\‰‘ÿFZ^ÿ456ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ê444¨444b777��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������222±333ÿ333ÿ333ÿ333ÿ333ÿ333ÿyÄÒÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ„Üëÿ=GIÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿwÁÏÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Öéÿ…­Ôÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¢Îÿ…®Ôÿ…¿Þÿ…Ñèÿ…Ýîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ€Ôãÿl©´ÿUy€ÿ?KNÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333×333444J+++ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������999333ù333ÿ333ÿ333ÿ333ÿ333ÿDUYÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿa’›ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿc–Ÿÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Ùëÿ…©Óÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¦Ñÿ…·Úÿ…Éãÿ…Ùìÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ{ÈÖÿd™£ÿMjoÿ9?@ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ù333¿333y5555�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������222f333ÿ333ÿ333ÿ333ÿ333ÿ333ÿa’›ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ‚Ùèÿ?LNÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿW†ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…¯Öÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¢Ïÿ…¯Öÿ…Áßÿ…Òèÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ„Üìÿs¸Åÿ]Š’ÿF[_ÿ456ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ì444¨444c555��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������222À333ÿ333ÿ333ÿ333ÿ333ÿ455ÿ|ÌÛÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿo±¼ÿ444ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿOmsÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Îæÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…§Ñÿ…¹Úÿ…Ëäÿ…Üíÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿÕäÿl©´ÿV{‚ÿ?LOÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333×333’222L+++ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������555333ý333ÿ333ÿ333ÿ333ÿ333ÿH_dÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿTx~ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ?LNÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…³×ÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¢Ïÿ…±×ÿ…Ãàÿ…Ôéÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿzÈÖÿdš¤ÿMjoÿ8?@ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ù444Á222z5555�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������222u333ÿ333ÿ333ÿ333ÿ333ÿ333ÿe¦ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ‚×çÿAQTÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ;CEÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…¦Ðÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…©Óÿ…»Üÿ…Ìåÿ…Üíÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ„Üìÿs¹Æÿ]Š’ÿG]aÿ456ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333í333©333d111��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������333Ï333ÿ333ÿ333ÿ333ÿ333ÿ588ÿÕäÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿzÇÕÿ9@Bÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Ùìÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¤Ïÿ…²×ÿ…Åáÿ…Öêÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿÖåÿlªµÿU{‚ÿ?LNÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333×444…000 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������111*333ÿ333ÿ333ÿ333ÿ333ÿ333ÿNkpÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿqµÁÿ7;<ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ[‡Žÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…×ëÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…«Óÿ…½Üÿ…Ïåÿ…Ýíÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿzÈÖÿe›¥ÿNlqÿ8?@ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ú444”333��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������222„333ÿ333ÿ333ÿ333ÿ333ÿ333ÿk§²ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿs¹Æÿ8>?ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ444ÿ€Óâÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…£Ïÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¥Ðÿ…µØÿ…Æâÿ…Øëÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ„Üìÿt»Èÿ^Œ”ÿG]aÿ567ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333Ú777���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������333Þ333ÿ333ÿ333ÿ333ÿ333ÿ8>?ÿ‚Ùèÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿwÀÍÿ?LOÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿDVYÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…®Õÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…­Ôÿ…¾Þÿ…Ðçÿ…Ýíÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿÖåÿlªµÿU{‚ÿ@NPÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333Ð333 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������333<333ÿ333ÿ333ÿ333ÿ333ÿ333ÿSu{ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿÖåÿQrxÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿg¡«ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Çâÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¦Ðÿ…·Ùÿ…Èãÿ…Úëÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ{ÊØÿe›¥ÿOnsÿ8?@ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ222˜��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������333–333ÿ333ÿ333ÿ333ÿ333ÿ333ÿp²½ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿk§²ÿ?KNÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ589ÿÖåÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Ýíÿ…¨Ñÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¢Ïÿ…¯Õÿ…ÀÞÿ…Òèÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ„Üìÿt»Èÿ]Œ”ÿG]aÿ578ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333þ222)������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������333333ë333ÿ333ÿ333ÿ333ÿ333ÿ<EFÿ„Üìÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿƒÚéÿc˜¡ÿ=HJÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿNkpÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Ñçÿ…¤Ïÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…§Ñÿ…¸Úÿ…Êäÿ…Úìÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿÖåÿm¬·ÿTyÿ699ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ƒ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������333K333ÿ333ÿ333ÿ333ÿ333ÿ333ÿW†ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ„Üìÿq³¿ÿTyÿDUYÿ6::ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿj¦±ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Ìäÿ…¤Ïÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Ïÿ…°×ÿ…Âàÿ…Ôéÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿv¾Ëÿ7;<ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333½������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������333¥333ÿ333ÿ333ÿ333ÿ333ÿ333ÿt¼Èÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ„ÝíÿÒáÿ>JLÿ333ÿ333ÿ333ÿ333ÿ333ÿ7;<ÿ‚×çÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…×êÿ…³Øÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…©Òÿ…ºÛÿ…Ìåÿ…Ûíÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿUzÿ333ÿ333ÿ333ÿ333ÿ333ÿ333í��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������... 333ô333ÿ333ÿ333ÿ333ÿ333ÿ?MOÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿt¼Èÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿPouÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Óèÿ…¿Þÿ…­Õÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…£Ïÿ…²×ÿ…Äàÿ…Öéÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ^Ž–ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������333Z333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ\‰‘ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿYˆÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿlªµÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Øëÿ…Çâÿ…µØÿ…¥Ðÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…ªÒÿ…¾Ýÿ…Õêÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿQrxÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ê��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������333´333ÿ333ÿ333ÿ333ÿ333ÿ333ÿyÆÓÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ„Ýíÿ=HJÿ333ÿ333ÿ333ÿ333ÿ333ÿ8>?ÿ‚Ùèÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þíÿ…Ïæÿ…½Üÿ…¬Ôÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¢Ïÿ…¿Þÿ…Üíÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ‚Ùèÿ8>?ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333½����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������333333ú333ÿ333ÿ333ÿ333ÿ333ÿDWZÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿs¸Äÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿRtzÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…×êÿ…Åáÿ…´Øÿ…¤Ïÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¯Õÿ…Ûíÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿk§²ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333~����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������333i333ÿ333ÿ333ÿ333ÿ333ÿ333ÿa“œÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿW~…ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿn®ºÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Üíÿ…Íæÿ…¼Üÿ…©Óÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…´Øÿ…Ýíÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿNkpÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ111*����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������333Ã333ÿ333ÿ333ÿ333ÿ333ÿ455ÿ}ÎÝÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ„Üìÿ<EFÿ333ÿ333ÿ333ÿ333ÿ333ÿ9@BÿƒÛêÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Öéÿ…Ãàÿ…²Öÿ…£Ïÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…Éãÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿÕäÿ588ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333Ï����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������111333þ333ÿ333ÿ333ÿ333ÿ333ÿIaeÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿq³¿ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿTx~ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Üíÿ…Ëäÿ…ºÛÿ…§Ñÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…­Ôÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿf§ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ222u����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������333x333ÿ333ÿ333ÿ333ÿ333ÿ333ÿf§ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿUy€ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿp²½ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Óèÿ…Áßÿ…°Öÿ…¡Ïÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…Ôéÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿIaeÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ý555����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������333Ò333ÿ333ÿ333ÿ333ÿ333ÿ588ÿÕäÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ„Üëÿ:ACÿ333ÿ333ÿ333ÿ333ÿ333ÿ;CDÿ„Üìÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Úìÿ…Éãÿ…¸Úÿ…¦Ðÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…Êãÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ}ÎÝÿ455ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333Ã����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������333-333ÿ333ÿ333ÿ333ÿ333ÿ333ÿNkpÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿo°¼ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿV|ƒÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Ýîÿ…Ñèÿ…ÀÞÿ…®Ôÿ…¢Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…Ãàÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿa“œÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333i����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������333‡333ÿ333ÿ333ÿ333ÿ333ÿ333ÿk§²ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿRtzÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿr·Ãÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿÖåÿTx~ÿn­¹ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Ùëÿ…Èâÿ…¶Ùÿ…¦Ðÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…Èãÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿDWZÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ú333���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������333à333ÿ333ÿ333ÿ333ÿ333ÿ8>?ÿ‚Ùèÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿƒÚéÿ9?@ÿ333ÿ333ÿ333ÿ333ÿ333ÿ<FHÿ„Ýíÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿOmrÿ333ÿU{‚ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Ýíÿ…Ðæÿ…¾Ýÿ…­Ôÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…Ñèÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿzÇÔÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333´����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������333<333ÿ333ÿ333ÿ333ÿ333ÿ333ÿSu{ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿl«¶ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿW†ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿb–Ÿÿ333ÿ333ÿ`‘šÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Øëÿ…Æâÿ…´Øÿ…£Ïÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…§Ñÿ…Ýíÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ^Œ”ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333Z����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������333–333ÿ333ÿ333ÿ333ÿ333ÿ333ÿp²½ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿQqwÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿt»Èÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿt¼Èÿ333ÿ333ÿ9ABÿÖåÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Ýíÿ…Îåÿ…¼Üÿ…«Óÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…Áßÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿAPRÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ô... ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������333333ë333ÿ333ÿ333ÿ333ÿ333ÿ<EFÿ„Üìÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿƒÙéÿ7;<ÿ333ÿ333ÿ333ÿ333ÿ333ÿ>JLÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ€Óâÿ<GIÿ333ÿ333ÿi£®ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Öéÿ…Äàÿ…³×ÿ…£Ïÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…©Óÿ…Úìÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿv¾Ëÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333¥����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������333K333ÿ333ÿ333ÿ333ÿ333ÿ333ÿW†ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿj§±ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿZ„Œÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ„ÜìÿJchÿ333ÿ333ÿJbgÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Ûíÿ…Íåÿ…ºÛÿ…©Òÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¦Ñÿ…Öéÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿY‚‰ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333K����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������333¥333ÿ333ÿ333ÿ333ÿ333ÿ333ÿt¼Èÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿNlqÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿu¾Ëÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿW†ÿ333ÿ333ÿ577ÿyÆÓÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Ôéÿ…Âàÿ…°×ÿ…¢Ïÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¯Õÿ…Ôéÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ„Üìÿ<FHÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ë333������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������... 333ô333ÿ333ÿ333ÿ333ÿ333ÿ@OQÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿÖæÿ6::ÿ333ÿ333ÿ333ÿ333ÿ333ÿ@NPÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿc–Ÿÿ333ÿ333ÿ333ÿ]Š’ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Úìÿ…Êäÿ…¹Úÿ…¨Ñÿ…¡Îÿ…¡Îÿ…¡Îÿ…¢Ïÿ…°×ÿ…Åáÿ…Ýîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿq´Àÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333–����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������333Z333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ^Œ”ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿi£­ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ\‰‘ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿh¡«ÿ344ÿ444ÿ444ÿ?LOÿ‚Ùèÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Øëÿ…Õéÿ…Õéÿ…Ûìÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿTx~ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ222=����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������333´333ÿ333ÿ333ÿ333ÿ333ÿ444ÿzÇÕÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿMinÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿxÃÑÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿi£®ÿ466ÿ444ÿ333ÿ455ÿs¹Æÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿƒÚéÿ8?@ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333â���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������333333ú333ÿ333ÿ333ÿ333ÿ333ÿFZ]ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ€Ôãÿ689ÿ333ÿ333ÿ333ÿ333ÿ333ÿBRUÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿf§ÿ344ÿ344ÿ333ÿ333ÿ\‰‘ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿlªµÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ444Š����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������333j333ÿ333ÿ333ÿ333ÿ333ÿ333ÿc–Ÿÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿfž¨ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ^•ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ„Ýíÿ\‰‘ÿ344ÿ344ÿ333ÿ344ÿEY\ÿ„Üìÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿOnsÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ5550����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������444Æ333ÿ333ÿ333ÿ333ÿ333ÿ456ÿ~ÏÝÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿJdhÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿzÇÔÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿÖåÿLglÿ333ÿ444ÿ333ÿ444ÿ7:;ÿzÇÔÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿÖæÿ6::ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333Õ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������555"333þ333ÿ333ÿ333ÿ333ÿ333ÿKdhÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿÑàÿ567ÿ333ÿ333ÿ333ÿ333ÿ333ÿDVYÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿq´Àÿ>JMÿ333ÿ333ÿ333ÿ333ÿ333ÿf§ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿh¢¬ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ444{����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������444{333ÿ333ÿ333ÿ333ÿ333ÿ333ÿg ªÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿdš¤ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ`‘šÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ€ÓâÿTw}ÿ344ÿ455ÿ333ÿ333ÿ333ÿ333ÿU{‚ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿKejÿ333ÿ333ÿ333ÿ333ÿ333ÿ333þ555"����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������333Õ333ÿ333ÿ333ÿ333ÿ333ÿ6::ÿÖåÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿI`eÿ333ÿ333ÿ333ÿ333ÿ333ÿ444ÿ|ËÙÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ}ÎÝÿYƒŠÿ6:;ÿ444ÿ333ÿ333ÿ333ÿ333ÿ334ÿG]aÿƒÚéÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿÑßÿ567ÿ333ÿ333ÿ333ÿ333ÿ333ÿ444Æ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������5550333ÿ333ÿ333ÿ333ÿ333ÿ333ÿOnsÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ}ÎÝÿ455ÿ333ÿ333ÿ333ÿ333ÿ333ÿEX[ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿzÇÕÿfž¨ÿMjoÿ577ÿ344ÿ333ÿ333ÿ333ÿ333ÿ333ÿ455ÿ<FHÿ~ÐÞÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿc˜¡ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ444l����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������444Š333ÿ333ÿ333ÿ333ÿ333ÿ333ÿlªµÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿb–Ÿÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ:ABÿCTWÿI`eÿG\`ÿAPSÿ7:;ÿ333ÿ333ÿ455ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ344ÿ7;<ÿv¾Ëÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿF[_ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333û...���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������222ä333ÿ333ÿ333ÿ333ÿ333ÿ8?@ÿƒÚéÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿF[_ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ445ÿ344ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ455ÿ689ÿq´Àÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ{É×ÿ444ÿ333ÿ333ÿ333ÿ333ÿ333ÿ444·���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������j333ÿ333ÿ333ÿ333ÿ333ÿ333ÿTx~ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ|ÌÚÿ455ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ444ÿ567ÿn­¹ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿzÇÔÿa“œÿSv|ÿI`eÿDUYÿAPSÿFZ]ÿNkpÿ[…ÿn¯»ÿƒÚéÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ^Ž–ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333_����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������P���y«333ÿ333ÿ333ÿ333ÿ333ÿ333ÿq³¿ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿa’›ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ455ÿ466ÿk§²ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ€Ôãÿ^Ž–ÿAPSÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ8=>ÿPpvÿtºÇÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿAQTÿ333ÿ333ÿ333ÿ333ÿ333ÿ333÷777��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������S���z���z���ç333ÿ333ÿ333ÿ333ÿ333ÿ588ÿ„Üìÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿDWZÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ445ÿ6:;ÿn¯»ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿƒÙéÿ`‘šÿ8>?ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿLglÿwÁÏÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿwÀÍÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333«����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������V���z���z���’×���þÿ333ÿ333ÿ333ÿ333ÿ333ÿ699ÿƒÚéÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿyÅÓÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ344ÿ;CEÿu¾Ëÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿo±¼ÿ=GIÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ456ÿUzÿ‚Ùèÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿZƒ‹ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ222Q��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������X���z���z���—Ý���ÿ���ÿ���ÿÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿeœ¦ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿo°¼ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ344ÿ333ÿF[_ÿ|ÌÚÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿc˜¡ÿ456ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿEY]ÿ~ÏÞÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ„Ýíÿ>JLÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ï$$$��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���\���z���z���œâ���ÿ���ÿ���ÿ���ÿ���ÿÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ689ÿa’›ÿ€Òáÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ{ÈÖÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ455ÿ456ÿ]Œ”ÿ„Üìÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ]Š’ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ=HJÿ}ÎÝÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿr·Äÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333œ�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#���^���z���{���£ç���ÿ���ÿ���ÿ���ÿ���ÿÿ9.ÿPBÿ96)ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ455ÿDWZÿ[†Žÿq´ÀÿƒÛêÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿQqwÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ455ÿ333ÿH^bÿv¾Ëÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿk§²ÿ444ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ñ333¿333¦333Ÿ222±333Ú333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿEX[ÿƒÚéÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿU{‚ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ222B���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������&���b���z���|���¨ì���ÿ���ÿ���ÿ���ÿ���ÿÿ>3ÿRCÿSDÿSDÿI?ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ7;<ÿLglÿc–ŸÿyÄÒÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ‚Øèÿ]Œ”ÿ8=>ÿ333ÿ344ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ344ÿ444ÿ333ÿEY\ÿm­¹ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿv¿Ìÿ6::ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333û333¡222H$$$��������������������666&444v333á333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿRsyÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ„Üëÿ:BDÿ333ÿ333ÿ333ÿ333ÿ333ÿ333å���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������)���d���z���}���­ð���ÿ���ÿ���ÿ���ÿ�ÿÿC7ÿTEÿUEÿUFÿUFÿUFÿVFÿA;"ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ=HJÿSv|ÿi¤¯ÿ€Òáÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿm­¸ÿJdhÿ455ÿ333ÿ333ÿ333ÿ333ÿ333ÿ?LNÿW†ÿxÃÑÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿÖåÿ>JMÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333É111*����������������������������������������$$$222z333ü333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿd˜¢ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿm­¹ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������,���f���z������³ô���ÿ���ÿ���ÿ���ÿ�ÿÿG:ÿVFÿWGÿWGÿWGÿXHÿYIÿYIÿYIÿXIÿ<8(ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ455ÿDVYÿZ…ÿq´ÀÿƒÚéÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿÖæÿr·Ãÿk§²ÿj§±ÿq´Àÿ~ÏÝÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ\‰‘ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333¦@@@����������������������������������������������������222G333ï333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ8?@ÿÒáÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿQqwÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ2223������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������.���h���z������¹÷���ÿ���ÿ���ÿ���ÿ�ÿ#ÿM?ÿYIÿYIÿYIÿYIÿZJÿZJÿ[Jÿ[Kÿ[Kÿ\Kÿ]Lÿ[JÿB<"ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ7;<ÿLglÿc–ŸÿxÃÑÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ~ÏÝÿ7;<ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ222“���������������������������������������������������������������222)333ï333ÿ333ÿ333ÿ333ÿ333ÿ333ÿY‚‰ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ‚×çÿ7;<ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333Ø��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������1���k���z���„���¿ù���ÿ���ÿ���ÿ���ÿ�ÿ( ÿQBÿZJÿ[Jÿ[Kÿ[Kÿ\Kÿ]Lÿ]Lÿ]Lÿ^Mÿ^Mÿ^Mÿ_Nÿ`Nÿ`Nÿ`NÿVHÿA;%ÿ332ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ=GIÿSv|ÿi£®ÿ€Òáÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ]‹“ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÂUUU��������������������������������������������������������������������444I333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ6::ÿ‚×çÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿi£®ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ444€������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������4���m���z���ˆ���Äû���ÿ���ÿ���ÿ���ÿÿ-$ÿUFÿ\Kÿ]Lÿ]Lÿ]Lÿ^Mÿ^Mÿ^Mÿ_Nÿ`Nÿ`Nÿ`OÿaOÿbPÿbPÿbPÿcQÿcQÿdRÿdRÿ`O ÿSGÿF?"ÿ86.ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ455ÿDVYÿ[…ÿq´ÀÿƒÚéÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿDVZÿ333ÿ333ÿ333ÿ333ÿ333ÿ333þ4446����������������������������������������������������������������������������333´333ÿ333ÿ333ÿ333ÿ333ÿ333ÿk¨³ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿLglÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ444'��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������c���z���Œ���Ê�ý���ÿ���ÿ���ÿ���ÿÿ2)ÿYIÿ^Mÿ^Mÿ_Nÿ`Nÿ`Nÿ`OÿaOÿbPÿbPÿbPÿcQÿcQÿcQÿdRÿdRÿdRÿeSÿfSÿfSÿfTÿgTÿhUÿhUÿhUÿiVÿ_P ÿC;ÿ+*%ÿ111ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ7;<ÿLfkÿb•žÿxÃÑÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ|ÌÛÿ444ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333«��������������������������������������������������������������������������������555+333ý333ÿ333ÿ333ÿ333ÿ333ÿRsyÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ€Òáÿ578ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333Ì�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������!���q���‚���Ë���þ���ÿ���ÿ���ÿ���ÿ ÿ7-ÿ\Kÿ`Nÿ`OÿaOÿbPÿbPÿbPÿcQÿcQÿdRÿdRÿdRÿeSÿfSÿfSÿfTÿgTÿhUÿhUÿhUÿiVÿiVÿiVÿiVÿjWÿjWÿkWÿkXÿdQÿ-%ÿ�ÿÿÿ841ÿB?<ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ<FHÿSv|ÿi£®ÿÑàÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿg ªÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ111C������������������������������������������������������������������������������������333¾333ÿ333ÿ333ÿ333ÿ333ÿ<EGÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿdš¤ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333r������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������?���y���£��ò���ÿ���ÿ���ÿ���ÿ ÿ<2ÿ_NÿbPÿcQÿcQÿdRÿdRÿdRÿeSÿfSÿfSÿfSÿfTÿgTÿhUÿhUÿhUÿiVÿiVÿiVÿjWÿjWÿkWÿkXÿkXÿlXÿmYÿmYÿmYÿnZÿo[ÿH;ÿ ÿ���ÿ���ÿÿpbTÿobSÿ`TIÿmbVÿndZÿCA>ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ455ÿDVYÿZ„Œÿq´ÀÿƒÚéÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿV{‚ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ð333������������������������������������������������������������������������������������333o333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ|ÌÛÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿH_dÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ý111��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������c���…���Ñ���ÿ���ÿ���ÿ���ÿ ÿA5ÿcQÿdRÿdRÿeSÿfSÿfSÿfTÿgTÿhUÿhUÿhUÿiVÿiVÿiVÿjWÿjWÿkWÿkXÿkXÿlXÿmYÿmYÿmYÿmYÿnZÿo[ÿo[ÿo[ÿp\ÿp\ÿq\ÿdRÿ%ÿ���ÿ���ÿÿQG<ÿ‚qaÿbVIÿwiYÿ· ‰ÿÒ¸ÿÒ¸ÿ·¡‹ÿŒ}nÿ`YQÿ987ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ7;<ÿKejÿb”ÿxÃÑÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿJbfÿ333ÿ333ÿ333ÿ333ÿ333ÿ444·����������������������������Uªª`Ÿ›=b Ÿ”a¡ž a¡œlc¥œ������������������������������������4441333ÿ333ÿ333ÿ333ÿ333ÿ333ÿp²¾ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ|ÌÛÿ455ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333½����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������7���v���¨���ô���ÿ���ÿ���ÿ�ÿ>2ÿeSÿfTÿgTÿhUÿhUÿhUÿiVÿiVÿiVÿiVÿjWÿjWÿkWÿkXÿkXÿlXÿmYÿmYÿmYÿnZÿo[ÿo[ÿo[ÿp\ÿp\ÿq\ÿq]ÿr]ÿr]ÿs^ÿs^ÿs^ÿs^ÿH;ÿÿ���ÿ���ÿ*% ÿ}m]ÿi\OÿfYMÿ•ƒpÿÑ·œÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÁ©ÿPH?ÿÿ)))ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ<EGÿSu{ÿi£®ÿÑßÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿCSVÿ333ÿ333ÿ333ÿ333ÿ333ÿ333¥������������������������_ š+a¡Úa¡ÿa¡ÿa¡ÿa¡ÿa¡öb¢c��������������������������������777333ÿ333ÿ333ÿ333ÿ333ÿ333ÿi£­ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿa’›ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ444c������������������������������������������������������������������������������������������������������������������������������������������������������������������������������]���†���Ö���ÿ���ÿ���ÿ���ÿ#ÿ\KÿhUÿiVÿiVÿiVÿjWÿjWÿkWÿkXÿkXÿlXÿmYÿmYÿmYÿnZÿo[ÿo[ÿo[ÿp\ÿp\ÿp\ÿq\ÿq]ÿr]ÿr]ÿs^ÿs^ÿs^ÿt_ÿt_ÿt_ÿu`ÿv`ÿv`ÿgTÿ$ÿ���ÿ���ÿÿYNCÿo_ÿbVIÿzk[ÿ½¦ŽÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ‘mÿÿ���ÿ���ÿEA<ÿ›‡ÿpjdÿDCAÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ455ÿDVZÿZ„Œÿq³¿ÿƒÚéÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿAQTÿ333ÿ333ÿ333ÿ333ÿ333ÿ333›��������������������Z¥–a¡žça¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡þb S����������������������������999333ÿ333ÿ333ÿ333ÿ333ÿ333ÿgŸ©ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿDUYÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ø000�����������������������������������������������������������������������������������������������������������������������������������������������������������������������*���s���¨���õ���ÿ���ÿ���ÿ ÿH;ÿjWÿkWÿkXÿkXÿlXÿmYÿmYÿmYÿmYÿnZÿo[ÿo[ÿo[ÿp\ÿp\ÿq\ÿq]ÿr]ÿr]ÿs^ÿs^ÿs^ÿt_ÿt_ÿt_ÿu`ÿv`ÿv`ÿvaÿvaÿwaÿxbÿxbÿxbÿycÿI;ÿÿ���ÿ���ÿ-'"ÿ~n^ÿgZMÿg[MÿŠuÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ¼¥ŒÿKB8ÿ���ÿ���ÿÿ·©ÿÔĵÿÔĵÿÔĵÿ¹¬Ÿÿ„|ÿa\Xÿ998ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ7;<ÿKdiÿa“œÿxÃÑÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿDVZÿ333ÿ333ÿ333ÿ333ÿ333ÿ222§��������������������a¢ða¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¢ðb£'������������������������000 333ÿ333ÿ333ÿ333ÿ333ÿ333ÿj¦±ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿyÄÒÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333®����������������������������������������������������������������������������������������������������������������������������������������������������������������������I������Î���ÿ���ÿ���ÿ���ÿ.%ÿdRÿmYÿmYÿmYÿnZÿo[ÿo[ÿo[ÿp\ÿp\ÿq\ÿq]ÿr]ÿr]ÿs^ÿs^ÿs^ÿs^ÿt_ÿt_ÿt_ÿu`ÿv`ÿv`ÿvaÿwaÿxbÿxbÿxbÿycÿycÿzdÿzdÿzdÿ{eÿ|eÿiVÿ#ÿ���ÿ���ÿ ÿ]QEÿ{l\ÿbVIÿ{l]ÿë’ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ‚raÿ ÿ���ÿ���ÿb[TÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÏÀ±ÿªž“ÿwpÿROLÿ554ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ<EGÿRtzÿi£®ÿ~ÐÞÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿMjoÿ333ÿ333ÿ333ÿ333ÿ333ÿ333Ç����������������a¢ž?a¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡žÔf™™ ��������������������333A333ÿ333ÿ333ÿ333ÿ333ÿ333ÿtºÇÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ\ˆÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ444T������������������������������������������������������������������������������������������������������������������������������������������������������������������_���‹���å���ÿ���ÿ���ÿ ÿUEÿo[ÿo[ÿp\ÿp\ÿp\ÿq\ÿq]ÿr]ÿr]ÿs^ÿs^ÿs^ÿt_ÿt_ÿt_ÿu`ÿv`ÿv`ÿvaÿwaÿxbÿxbÿxbÿycÿycÿzdÿzdÿzdÿzdÿ{eÿ|eÿ|fÿ|fÿ}fÿ~gÿ~gÿ}fÿI<ÿÿ���ÿ���ÿ0*$ÿ€o`ÿeYKÿh[Mÿ ŒwÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ°š„ÿ=6.ÿ���ÿ���ÿ ÿô§ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿǸªÿœ’ˆÿpjdÿDCAÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ455ÿDUYÿZ„Œÿp²¾ÿƒÚéÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ[†Žÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ü999������������a¢ž¿a¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ž¥��������������������333‹333ÿ333ÿ333ÿ333ÿ333ÿ455ÿÖåÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ?KNÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ñ@@@����������������������������������������������������������������������������������������������������������������������������������������������������������� ���d���’���ô���ÿ���ÿ���ÿ/&ÿq]ÿq]ÿr]ÿr]ÿs^ÿs^ÿs^ÿt_ÿt_ÿt_ÿu`ÿv`ÿv`ÿv`ÿvaÿwaÿxbÿxbÿxbÿycÿycÿzdÿzdÿzdÿ{eÿ|eÿ|fÿ|fÿ}fÿ~gÿ~gÿ~gÿhÿhÿhÿ€iÿ€iÿ€iÿlXÿ$ÿ���ÿ���ÿ ÿaUHÿzk[ÿbVIÿ{l\ÿª‘ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿϵ›ÿseVÿÿ���ÿ���ÿvlcÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ¹¬Ÿÿ„|ÿb]Yÿ998ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ7;;ÿKdiÿa’›ÿxÂÐÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿm­¹ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ222a������������a¡éa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿ`¡œb���������������333Û333ÿ333ÿ333ÿ333ÿ333ÿBSVÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿtºÇÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333¡����������������������������������������������������������������������������������������������������������������������������������������������������������� ���i���‘���ô���ÿ���ÿ�ÿPAÿzcÿs^ÿt_ÿt_ÿt_ÿu`ÿv`ÿv`ÿvaÿwaÿxbÿxbÿxbÿycÿycÿzdÿzdÿzdÿ{eÿ|eÿ|fÿ|fÿ}fÿ}fÿ~gÿ~gÿ~gÿhÿhÿhÿ€iÿ€iÿiÿjÿ‚jÿ‚jÿƒkÿƒkÿƒkÿPAÿÿ���ÿ���ÿ3,&ÿ‚sbÿeYKÿfYMÿ‹vÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ¨“~ÿ2,%ÿ���ÿ���ÿÿ˼®ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÐÀ²ÿªž“ÿ€xqÿSOLÿ554ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ<EGÿSt{ÿi£®ÿ~ÐÞÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ‚Øèÿ699ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333×@@@����[¤¤a¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ó`ŸŸ%��������444Y333ÿ333ÿ333ÿ333ÿ333ÿ333ÿZ…ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿW~…ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ222H����������������������������������������������������������������������������������������������������������������������������������������������������������d������ô���ÿ���ÿ�ÿnZÿzdÿv`ÿvaÿwaÿxbÿxbÿxbÿycÿycÿzdÿzdÿzdÿzdÿ{eÿ|eÿ|fÿ|fÿ}fÿ~gÿ~gÿ~gÿhÿhÿhÿ€iÿ€iÿiÿjÿ‚jÿ‚jÿƒkÿƒkÿƒkÿƒkÿ„lÿ…mÿ…mÿ…mÿ†nÿwaÿ-$ÿ���ÿ���ÿ ÿ_THÿ{l\ÿbVIÿyjZÿª‘ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿж›ÿqcTÿÿ���ÿ���ÿ‚vkÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿǸªÿ’‰ÿqkeÿDCAÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ455ÿDUYÿZ„Œÿp²½ÿƒÚéÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿMinÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ222e��������a¡åa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¢Êf™™$$$333ß333ÿ333ÿ333ÿ333ÿ333ÿ333ÿt»Èÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ„Üìÿ;CDÿ333ÿ333ÿ333ÿ333ÿ333ÿ333é@@@�������������������������������������������������������������������������������������������������������������������������������������������������������U���€���ë���ÿ���ÿ�ÿr]ÿzdÿxbÿycÿycÿzdÿzdÿzdÿ{eÿ|eÿ|fÿ|fÿ}fÿ~gÿ~gÿ~gÿhÿhÿhÿ€iÿ€iÿ€iÿiÿjÿ‚jÿ‚jÿƒkÿƒkÿƒkÿ„lÿ…mÿ…mÿ…mÿ†nÿ‡nÿ‡nÿ‡oÿˆoÿˆoÿ‰pÿ‰pÿ]Lÿ ÿ���ÿ���ÿ+& ÿ|l]ÿfZLÿeYKÿ™†sÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿª•ÿ5/(ÿ���ÿ���ÿ ÿ̼­ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ¹¬Ÿÿ„|ÿb]Yÿ:98ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ6::ÿKdiÿa“œÿwÁÏÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿh¢¬ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ë555"����b¡€a¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿb¡u333333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ>JLÿ„Ýíÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿp±½ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ222“�������������������������������������������������������������������������������������������������������������������������������������������������������8���z���Î���ÿ���ÿ�ÿq\ÿ|fÿ{eÿ|eÿ|fÿ|fÿ}fÿ}fÿ~gÿ~gÿ~gÿhÿhÿhÿ€iÿ€iÿiÿjÿ‚jÿ‚jÿƒkÿƒkÿƒkÿ„lÿ…mÿ…mÿ…mÿ†nÿ‡nÿ‡nÿ‡nÿ‡oÿˆoÿˆoÿ‰pÿ‰pÿ‰pÿŠqÿŠqÿŠqÿ‹rÿ‚kÿ5+ÿ���ÿ���ÿÿTI>ÿ}m^ÿbVIÿugXÿº£‹ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿзœÿufWÿÿ���ÿ���ÿxmbÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ¶†ÿŽxdÿn_SÿLF@ÿ444ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ<EGÿRtzÿi£­ÿ~ÐÞÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ„ÜìÿAPRÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ;CCøZ’àa¡©a¡Øa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿ]–“ç333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿi£­ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿSu{ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ1119������������������������������������������������������������������������������������������������������������������������������������������������������w���©���ÿ���ÿ���ÿTEÿ~gÿ~gÿ~gÿ~gÿhÿhÿhÿ€iÿ€iÿiÿjÿ‚jÿ‚jÿƒkÿƒkÿƒkÿƒkÿ„lÿ…mÿ…mÿ…mÿ†nÿ‡nÿ‡nÿ‡oÿˆoÿˆoÿ‰pÿ‰pÿ‰pÿŠqÿŠqÿŠqÿ‹rÿŒrÿŒsÿŒsÿsÿŽtÿŽtÿŽtÿhUÿÿ���ÿ���ÿ ÿwhXÿh[NÿcWJÿ~kÿÑ·œÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ­˜ÿ92*ÿ���ÿ���ÿ ÿȸ©ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ;¯ÿ¯¡•ÿ–‹€ÿƒypÿzqhÿ|sjÿŒwÿ ”ˆÿŶ¨ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ̹¨ÿ®uÿ®uÿ®uÿ®uÿ¤ˆoÿ„p^ÿbVLÿ@=:ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ455ÿCUXÿZƒ‹ÿp²½ÿ‚Ùèÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿlªµÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ7<<ÿV‡„ÿa¡õb¡’a¡žb ¯a¢ž~`Ÿœ¥a¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿ`Ÿ›ÿ344ÿ333ÿ333ÿ333ÿ333ÿ333ÿDVZÿ„Ýíÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ‚Ùèÿ8>?ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333Ý������������������������������������������������������������������������������������������������������������������������������������������������������^����õ���ÿ���ÿ.&ÿ‚jÿ€iÿ€iÿ€iÿiÿjÿ‚jÿ‚jÿƒkÿƒkÿƒkÿ„lÿ…mÿ…mÿ…mÿ†nÿ‡nÿ‡nÿ‡oÿˆoÿˆoÿ‰pÿ‰pÿ‰pÿŠqÿŠqÿŠqÿŠqÿ‹rÿŒrÿŒsÿŒsÿsÿŽtÿŽtÿŽtÿuÿuÿvÿvÿ‘wÿsÿB7ÿ���ÿ���ÿÿE=4ÿq`ÿbVIÿoaSÿ²œ…ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÑ·œÿyjZÿÿ���ÿ���ÿocYÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ¿±£ÿ‚xÿme]ÿjbZÿjbZÿjbZÿjbZÿjbZÿjbZÿjbZÿjb[ÿˆ~tÿô¦ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ¹Ÿ‡ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿšjÿxgXÿVNFÿ987ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ6::ÿKdiÿa“œÿxÂÏÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿOntÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ455ÿGdbÿ\—“Ý€¿€��������jª• a¡Úa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿZ‘ÿ333ÿ333ÿ333ÿ333ÿ333ÿ6::ÿv¿Ìÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿk§²ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ222„���������������������������������������������������������������������������������������������������������������������������������������������������'���z���À���ÿ���ÿ ÿ|eÿ‚jÿƒkÿƒkÿƒkÿ„lÿ…mÿ…mÿ…mÿ†nÿ‡nÿ‡nÿ‡nÿ‡oÿˆoÿˆoÿ‰pÿ‰pÿ‰pÿŠqÿŠqÿŠqÿ‹rÿŒrÿŒsÿŒsÿsÿŽtÿŽtÿŽtÿuÿuÿvÿvÿ‘wÿ‘wÿ‘wÿ’wÿ’xÿ’xÿ“xÿ”yÿt_ÿÿ���ÿ���ÿÿnaSÿnaRÿbVIÿ„tcÿδšÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ³†ÿ>7.ÿ���ÿ���ÿÿ±¡ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ;¯ÿ“ˆ~ÿjb[ÿjbZÿjbZÿVPIÿ>95ÿ*'$ÿ#!ÿ%# ÿ40,ÿRLFÿjbZÿjbZÿme]ÿ«ž’ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ̹¨ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ«Žsÿxdÿn_SÿLFAÿ444ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ<FGÿRtzÿh¢¬ÿ~ÐÞÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ‚×çÿDUYÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ466ÿ6::Â222f111/333U‡‡Ba¡œûa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿKljÿ333ÿ333ÿ333ÿ333ÿ444ÿi¤¯ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿNkpÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ111*��������������������������������������������������������������������������������������������������������������������������������������������������e���ƒ�ú���ÿ���ÿTEÿ…mÿ…mÿ…mÿ†nÿ‡nÿ‡nÿ‡oÿˆoÿˆoÿ‰pÿ‰pÿ‰pÿŠqÿŠqÿŠqÿ‹rÿŒrÿŒsÿŒsÿsÿsÿŽtÿŽtÿŽtÿuÿuÿvÿvÿ‘wÿ‘wÿ’wÿ’xÿ’xÿ“xÿ”yÿ”yÿ”yÿ•zÿ•zÿ•zÿ–{ÿ—{ÿUEÿ�ÿ���ÿ���ÿ:3,ÿ‚raÿbVIÿi]Oÿ©”~ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ‰xfÿ ÿ���ÿ���ÿYNDÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÀ²¤ÿwnfÿjbZÿh`Xÿ@;6ÿ ÿ���ÿ���ÿÿÿ�ÿ���ÿ���ÿÿQKEÿjbZÿjbZÿ ”‰ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿº¡Šÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ¤‰pÿ„p^ÿbVLÿ@=:ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ455ÿCTWÿYƒŠÿp²½ÿƒÙéÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ|ÌÚÿ;DEÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿE_]ÿ`Ÿœÿa¡ÿa¡ÿa¡ÿa¡ÿYŠÿ>NMÿ@RQÿ8>=ÿ333ÿ333ÿ\ˆÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿÕäÿ588ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333Ï���������������������������������������������������������������������������������������������������������������������������������������������������#���z���¼���ÿ���ÿÿ…mÿˆoÿˆoÿ‰pÿ‰pÿ‰pÿŠqÿŠqÿŠqÿŠqÿ‹rÿŒrÿŒsÿŒsÿsÿŽtÿŽtÿŽtÿuÿuÿvÿvÿ‘wÿ‘wÿ’wÿ’xÿ’xÿ“xÿ”yÿ”yÿ”yÿ”yÿ•zÿ•zÿ•zÿ–{ÿ—{ÿ—{ÿ—|ÿ˜|ÿ˜|ÿ™}ÿŠpÿ1)ÿ���ÿ���ÿ ÿdXJÿugXÿbVIÿ{k[ÿĬ“ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿª‘ÿPG<ÿ���ÿ���ÿ���ÿ°ŸÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ²¤˜ÿne^ÿjbZÿRLFÿÿ���ÿ ÿ9)ÿ[B*ÿlN1ÿlN1ÿhK/ÿT=&ÿ/"ÿÿÿ;72ÿjbZÿjbZÿ¥˜ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÍ»«ÿ®vÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ›kÿyhYÿWNFÿ987ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ6::ÿJdhÿa“œÿwÁÎÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿt»Çÿ;CDÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ<HGÿQzxÿZŒÿU„ÿW‰†ÿ^š–ÿa¡ÿa¡ÿa œÿOusÿW}ƒÿ„Ýíÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿf§ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ222u���������������������������������������������������������������������������������������������������������������������������������������������������X���{ó���ÿ���ÿSCÿŠqÿŠqÿŠqÿ‹rÿŒrÿŒsÿŒsÿsÿŽtÿŽtÿŽtÿuÿuÿvÿvÿvÿ‘wÿ‘wÿ’wÿ’xÿ’xÿ“xÿ”yÿ”yÿ”yÿ•zÿ•zÿ•zÿ–{ÿ—{ÿ—{ÿ—|ÿ˜|ÿ˜|ÿ™}ÿ™}ÿ™}ÿš~ÿ›ÿ›ÿ›ÿ›ÿq]ÿÿ���ÿ���ÿ'"ÿ|l\ÿdXKÿcWJÿ–„pÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ›ˆtÿ!ÿ���ÿ���ÿ2*$ÿÓ³ÿÔĵÿÔĵÿÔĵÿÔĵÿª‘ÿjb[ÿjbZÿ940ÿÿÿB0ÿjM0ÿlN1ÿlN1ÿlN1ÿlN1ÿnQ4ÿ¯ŸÿrV:ÿaF,ÿÿ���ÿ2/+ÿjbZÿkc\ÿº¬ŸÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ¼£Œÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ«Žsÿyeÿn_SÿLF@ÿ444ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ<EFÿRtzÿh¢¬ÿ~ÐÞÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ|ËÙÿBSVÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ>NMÿa œÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿn·ºÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿIaeÿ333ÿ333ÿ333ÿ333ÿ333ÿ333þ555��������������������������������������������������������������������������������������������������������������������������������������������������z���›���ÿ���ÿ�ÿ„lÿsÿsÿŽtÿŽtÿŽtÿuÿuÿvÿvÿ‘wÿ‘wÿ’wÿ’xÿ’xÿ“xÿ”yÿ”yÿ”yÿ•zÿ•zÿ•zÿ–{ÿ—{ÿ—{ÿ—{ÿ—|ÿ˜|ÿ˜|ÿ™}ÿ™}ÿ™}ÿš~ÿ›ÿ›ÿ›ÿœ€ÿ€ÿ€ÿÿžÿžÿ€ÿOAÿ�ÿ���ÿ���ÿH?6ÿo^ÿbVIÿqcTÿµŸ‡ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÍ´™ÿg[Mÿÿ���ÿ���ÿ€sÿÔĵÿÔĵÿÔĵÿÔĵÿ©œÿjb[ÿiaYÿ)&#ÿ���ÿÿcG-ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿɾ³ÿÿÿÿÿÛÔÍÿrU9ÿkM0ÿ$ÿ���ÿ>:5ÿjbZÿyogÿÒ³ÿÔĵÿÔĵÿÔĵÿϽ­ÿ¯‘vÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ¥‰pÿ„p^ÿcWLÿA=:ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ444ÿCTWÿY‚‰ÿo±¼ÿƒÙéÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ‚×çÿ\ˆÿ7;<ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿX‹‡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿyÉÓÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ}ÎÝÿ455ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333Ã���������������������������������������������������������������������������������������������������������������������������������������������������9���zÏ���ÿ���ÿ3*ÿuÿvÿvÿ‘wÿ‘wÿ’wÿ’xÿ’xÿ“xÿ”yÿ”yÿ”yÿ”yÿ•zÿ•zÿ•zÿ–{ÿ—{ÿ—{ÿ—|ÿ˜|ÿ˜|ÿ™}ÿ™}ÿ™}ÿš~ÿ›ÿ›ÿ›ÿœ€ÿ€ÿ€ÿÿžÿžÿžÿŸ‚ÿŸ‚ÿŸ‚ÿ ƒÿ ƒÿ¡„ÿŠpÿ,$ÿ���ÿ���ÿ ÿk]Oÿm`RÿbVIÿƒsbÿδšÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ¬—ÿ4.'ÿ���ÿ���ÿ ÿ˹¨ÿÔĵÿÔĵÿÔĵÿ´¦™ÿkc\ÿjbZÿ'$!ÿ���ÿ2$ÿkM0ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿ“}gÿÿÿÿÿÿÿÿÿÿÿÿÿÍùÿmO2ÿiL0ÿ ÿÿ[TMÿjbZÿ¦šŽÿÔĵÿÔĵÿÔĵÿ½¥ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ›kÿzhYÿWNFÿ987ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ6::ÿKdhÿa“œÿwÀÍÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ}ÎÝÿV|ƒÿ588ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ466ÿ`Ÿœÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿi¯¯ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿa“œÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333i���������������������������������������������������������������������������������������������������������������������������������������������������c���~û���ÿ���ÿgTÿ’xÿ’xÿ“xÿ”yÿ”yÿ”yÿ•zÿ•zÿ•zÿ–{ÿ—{ÿ—{ÿ—|ÿ˜|ÿ˜|ÿ™}ÿ™}ÿ™}ÿš~ÿš~ÿ›ÿ›ÿ›ÿœ€ÿ€ÿ€ÿÿžÿžÿŸ‚ÿŸ‚ÿŸ‚ÿ ƒÿ ƒÿ¡„ÿ¡„ÿ¢„ÿ¢…ÿ¢…ÿ£…ÿ¤†ÿ¤†ÿq]ÿ ÿ���ÿ���ÿ.)"ÿ|m]ÿbVIÿgZMÿ£zÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ€o`ÿ ÿ���ÿ���ÿdWKÿÔĵÿÔĵÿÔĵÿõ§ÿof^ÿjbZÿ2.*ÿ���ÿ8)ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿÙÑÊÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ®žŽÿlN1ÿZA)ÿÿ!ÿjbZÿvmeÿÓôÿÔĵÿμ«ÿ¯‘vÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ«Žsÿyeÿn`SÿLF@ÿ444ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ;DEÿRtzÿh¡«ÿ~ÏÝÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ~ÏÝÿd™£ÿKdhÿ689ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ@NOÿa œÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿ{ÎØÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿDWZÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ú333����������������������������������������������������������������������������������������������������������������������������������������������� ���z���¢���ÿ���ÿ ÿ“xÿ•zÿ•zÿ–{ÿ—{ÿ—{ÿ—{ÿ—|ÿ˜|ÿ˜|ÿ™}ÿ™}ÿ™}ÿš~ÿ›ÿ›ÿ›ÿœ€ÿ€ÿ€ÿÿžÿžÿŸ‚ÿŸ‚ÿŸ‚ÿ ƒÿ ƒÿ¡„ÿ¡„ÿ¡„ÿ¢„ÿ¢…ÿ¢…ÿ£…ÿ¤†ÿ¤†ÿ¤†ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ ƒÿG:ÿ���ÿ���ÿÿQH=ÿxjZÿbVIÿugXÿ½¥ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÁ©ÿLC9ÿ���ÿ���ÿÿµ¡ÿÔĵÿÔĵÿÐÀ±ÿ|sjÿjbZÿA<7ÿ���ÿ3%ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿqT8ÿûúùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúùùÿƒjQÿlN1ÿ.!ÿ���ÿVOIÿjbZÿ´§šÿÔĵÿ¸ž‡ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿmZKÿÿÿ///ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ444ÿCTWÿY‚‰ÿo±¼ÿƒÙéÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ‚ÙèÿwÀÍÿlªµÿgŸ©ÿdš¤ÿj¥°ÿr¶Âÿ~ÏÝÿ…Þîÿj±²ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿl´¶ÿ…Þîÿ…Þîÿ…ÞîÿzÇÕÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333´���������������������������������������������������������������������������������������������������������������������������������������������������+���z�Æ���ÿ���ÿ?4ÿ—|ÿ˜|ÿ˜|ÿ™}ÿ™}ÿ™}ÿš~ÿ›ÿ›ÿ›ÿœ€ÿ€ÿ€ÿÿÿžÿžÿŸ‚ÿŸ‚ÿŸ‚ÿ ƒÿ ƒÿ¡„ÿ¡„ÿ¢„ÿ¢…ÿ¢…ÿ£…ÿ¤†ÿ¤†ÿ¤†ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ‡nÿ%ÿ���ÿ���ÿÿqcTÿgZMÿbVIÿˆwfÿзœÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿŸ‹wÿ$ ÿ���ÿ���ÿ/(!ÿÑÀ±ÿÔĵÿÔĵÿšŽƒÿjbZÿWPJÿÿ%ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿ‰qZÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÕÍÅÿlN1ÿaF,ÿÿ*&#ÿjbZÿŒwÿɵ¢ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿvbOÿ���ÿ���ÿÿ‰yiÿ˜Ž…ÿpppÿ===ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ6::ÿJcgÿa’›ÿwÀÍÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿyÊÓÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¢žÿ€×äÿ…Þîÿ…Þîÿ^Œ”ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333Z���������������������������������������������������������������������������������������������������������������������������������������������������J���zì���ÿ���ÿiVÿ›ÿ›ÿ›ÿ›ÿœ€ÿ€ÿ€ÿÿžÿžÿŸ‚ÿŸ‚ÿŸ‚ÿ ƒÿ ƒÿ¡„ÿ¡„ÿ¢„ÿ¢…ÿ¢…ÿ£…ÿ¤†ÿ¤†ÿ¤†ÿ¤†ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿmYÿÿ���ÿ���ÿ5/(ÿ~o^ÿbVIÿh[Mÿ¦’|ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿugXÿÿ���ÿ���ÿ€pcÿÔĵÿÔĵÿ¾°£ÿkc\ÿh`Xÿÿ ÿgJ/ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿ”~iÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‘zeÿlN1ÿ$ÿÿg_Xÿme]ÿ°”|ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ{fSÿ���ÿ���ÿÿ™†sÿâÑÀÿÿÿÿÿúúúÿ¼²§ÿndYÿ210ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ;CDÿRsyÿh¡«ÿ~ÏÞÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Ýíÿd¦¤ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿs¾Äÿ…Þîÿ…ÞîÿAPRÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ô... ���������������������������������������������������������������������������������������������������������������������������������������������������j���„���þ���ÿ���ÿ”yÿŸ‚ÿžÿžÿŸ‚ÿŸ‚ÿŸ‚ÿ ƒÿ ƒÿ¡„ÿ¡„ÿ¡„ÿ¢„ÿ¢…ÿ¢…ÿ£…ÿ¤†ÿ¤†ÿ¤†ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¡„ÿG:ÿ���ÿ���ÿÿXMBÿvhXÿbVIÿvgXÿ¿§ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ¼¥ŒÿG>5ÿ���ÿ���ÿÿ¿«šÿÔĵÿÓôÿ‚xoÿjbZÿ40,ÿÿX@(ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿ™„pÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÑÈ¿ÿlN1ÿJ6"ÿ���ÿLGAÿaVLÿ“ycÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ{fSÿ���ÿ���ÿÿ™†sÿáϽÿÿÿÿÿÿÿÿÿêÞÑÿµŸ‡ÿ5/(ÿÿÿ""!ð000ñ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ444ÿBSVÿY‚‰ÿp±½ÿƒÙéÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿuÃÊÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿf©¨ÿ…Þîÿv¾Ëÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333¥������������������������������������������������������������������������������������������������������������������������������������������������������z���¥���ÿ���ÿÿ¥‡ÿ¢„ÿ ƒÿ¡„ÿ¡„ÿ¢„ÿ¢…ÿ¢…ÿ£…ÿ¤†ÿ¤†ÿ¤†ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿsÿ-%ÿ���ÿ���ÿÿpbTÿgZMÿbVIÿ‰xfÿÑ·œÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ›ˆtÿÿ���ÿ���ÿ:0(ÿÒÁ²ÿÔĵÿ°£–ÿjbZÿYRLÿÿ5&ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿ—mÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþýÿ|aGÿhK/ÿÿ1-)ÿXJ=ÿ~hTÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ{fSÿ���ÿ���ÿÿ™†sÿßͺÿÿÿÿÿÿÿÿÿìâ×ÿµŸ‡ÿ5/(ÿ���ÿ���ÿÞ���‰Q444l444²333ó333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ6::ÿJbfÿ`‘šÿwÀÍÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿƒÜìÿd¦£ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿ~ÒßÿY‚‰ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333K���������������������������������������������������������������������������������������������������������������������������������������������������#���z���À���ÿ���ÿC7ÿ¥‡ÿ¤†ÿ¤†ÿ¤†ÿ¤†ÿ¤†ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿxbÿÿ���ÿ���ÿ-'"ÿxiZÿbVIÿh[Mÿ¥{ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿзœÿoaRÿÿ���ÿ���ÿˆwhÿÔĵÿÓôÿ{riÿjbZÿÿ ÿhK/ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿxaÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ«š‰ÿlN1ÿ ÿÿWH:ÿkYHÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ{fSÿ���ÿ���ÿÿ™†sÿßͺÿÿÿÿÿÿÿÿÿìâ×ÿ´ž‡ÿ1,%ÿ���ÿ���ÿÜ���‡���+��������333111>333ƒ222Ê333ü333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ;CDÿQrxÿh¡«ÿ}ÎÝÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿk±³ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿr¾Äÿ<FHÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ë333���������������������������������������������������������������������������������������������������������������������������������������������������5���zÚ���ÿ���ÿaOÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿYIÿ�ÿ���ÿ���ÿH?6ÿvgXÿbVIÿufWÿ¹¢‹ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ· ‰ÿ@80ÿ���ÿ���ÿ ÿ­›ÿÔĵÿ³¥™ÿjbZÿPJDÿ���ÿD1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿ‚iQÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ×ÎÆÿlN1ÿ2$ÿÿWH:ÿ\L>ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿq^Lÿ���ÿ���ÿÿ–ƒpÿâÑ¿ÿÿÿÿÿÿÿÿÿìâ×ÿ¯šƒÿ.)#ÿ���ÿ���ÿ×���…���+������������������������999333U333›333ß333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ444ÿCSVÿY‚‰ÿo°¼ÿƒÙéÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ~ÑÝÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿf¨¨ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333–�������������������������������������������������������������������������������������������������������������������������������������������������������H���zî���ÿ���ÿzdÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ—|ÿ8.ÿ���ÿ���ÿ ÿg[Mÿg[MÿbVIÿ‚raÿÍ´™ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ›ˆtÿÿ���ÿ���ÿB7-ÿÒÁ²ÿÔĵÿ„zqÿjbZÿÿ ÿjM0ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿuX=ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿø÷õÿnP3ÿE2ÿ���ÿPB5ÿWH:ÿ§Špÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿhVFÿ���ÿ���ÿÿ›ˆtÿâÑÀÿÿÿÿÿÿÿÿÿìáÖÿ¯šƒÿ,&!ÿ���ÿ���ÿÖ���„���%����������������������������������������444'444l222±333ó333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ699ÿJbfÿ_™ÿwÀÍÿ„Ýíÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ„Þîÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿ\ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ222=�������������������������������������������������������������������������������������������������������������������������������������������������������Z���{ý���ÿ���ÿ“xÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿiÿÿ���ÿ���ÿ$ ÿtfWÿbVIÿeXKÿŠuÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿyj[ÿÿ���ÿ���ÿ‡vgÿÔĵÿ½®¡ÿjbZÿYRKÿ���ÿ>-ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿø÷õÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ…lTÿT=&ÿ���ÿE9.ÿWH:ÿž‚jÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ^N?ÿ���ÿ���ÿÿ¡xÿäÔÄÿÿÿÿÿÿÿÿÿêÞÑÿª•ÿ(#ÿ���ÿ���ÿ���Ñ������$����������������������������������������������������@@@111>333‚<IHû344ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ;CDÿQqwÿg ªÿ}ÎÝÿ…Þîÿ…Þîÿr¾Ãÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿF^^ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333â����������������������������������������������������������������������������������������������������������������������������������������������������������l���ˆ���ÿ���ÿ�ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿiUÿ�ÿ���ÿ���ÿ;4,ÿxiZÿbVIÿpbTÿ²œ…ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿë’ÿNE:ÿ���ÿ���ÿÿ¼¨–ÿÔĵÿƒyÿjbZÿ(%#ÿÿgJ/ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿèãßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ›‡sÿ_E+ÿ���ÿ?4*ÿWH:ÿ—}fÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿUF9ÿ���ÿ���ÿ&!ÿ¦‘|ÿåÕÅÿÿÿÿÿÿÿÿÿêÞÑÿª•ÿ'"ÿ���ÿ���ÿ���Ñ������ ������������������������������������������������������������`ŸŸa¡ÿ^š–ÿR}zÿE_]ÿ9AAÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ444ÿCSVÿY‚‰ÿn¯¹ÿe¨§ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿ[‘ÿ344ÿ333ÿ333ÿ333ÿ333ÿ333ÿ444Š�����������������������������������������������������������������������������������������������������������������������������������������������������������s������ÿ���ÿ ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¢„ÿE8ÿ���ÿ���ÿÿ]QFÿoaSÿbVIÿ|m]ÿĬ“ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ©”~ÿ-'!ÿ���ÿ���ÿ)"ÿп¯ÿȸªÿkc\ÿ`XQÿÿ4%ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿØÐÈÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿª™ˆÿfJ/ÿ���ÿ7.%ÿWH:ÿ”{dÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿK>2ÿ���ÿ���ÿ,&!ÿª•ÿçÙÊÿÿÿÿÿÿÿÿÿêÞÑÿ¥{ÿ!ÿ���ÿ���ÿ���Î���~�������������������������������������������������������������������a¡òa¡ÿa¡ÿa¡ÿa¡ÿ[‘ŽÿNsqÿAUTÿ477ý333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ:CBÿZ‹ÿ` ÿa¡ÿa œÿR{zÿ698ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333þ111*�����������������������������������������������������������������������������������������������������������������������������������������������������������v���‘���ÿ���ÿÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ‘wÿ0'ÿ���ÿ���ÿÿpbTÿcWJÿbVIÿ‹zhÿÑ·œÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿŒ{iÿ ÿ���ÿ���ÿj\NÿÔĵÿ£—‹ÿjbZÿ951ÿ�ÿaF,ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿǼ±ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ±¡’ÿlN1ÿ���ÿ3*"ÿWH:ÿ‘xbÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿB6,ÿ���ÿ���ÿ2,%ÿ°š„ÿéÝÐÿÿÿÿÿÿÿÿÿçÙËÿ¢Žyÿÿ���ÿ���ÿ���Ì���~�������������������������������������������������������������������a¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿ`Ÿœÿ@ROW333‚333È333ü333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ6::ÿNtqÿ^š–ÿXŒ‰ÿMqoÿ7==ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333¯���������������������������������������������������������������������������������������������������������������������������������������������������������������z���™���ÿ���ÿÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿiÿÿ���ÿ���ÿ)%ÿteVÿbVIÿh[Mÿ¢ŽyÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÍ´™ÿ`THÿ���ÿ���ÿ���ÿ¦”ƒÿÔĵÿ~tkÿjbZÿÿ ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿ·¨šÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¸ªœÿlN1ÿ���ÿ3*"ÿWH:ÿwaÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ8.&ÿ���ÿ���ÿ81*ÿ¶ŸˆÿíãØÿÿÿÿÿÿÿÿÿäÔÃÿš‡sÿÿ���ÿ���ÿ���Ê���z�������������������������������������������������������������������a¢ž†a¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿ` ”��������999444S444™333ß333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ9A@ÿ]—”ÿa¡ÿa¡ÿa¡ÿa¡ÿ]—“ÿ:EDÿ333ÿ333ÿ333ÿ333ê111��������������������������������������������������������������������������������������������������������������������������������������������������������������z���˜���ÿ���ÿÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿo[ÿÿ���ÿ���ÿ;4,ÿugWÿbVIÿseVÿ´ž‡ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ¶Ÿˆÿ<5-ÿ���ÿ���ÿ ÿ˸§ÿÆ·©ÿjbZÿZSLÿ���ÿB/ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿ¦”ƒÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ½°£ÿhK/ÿ���ÿ4+"ÿWH:ÿ•|dÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ#ÿ���ÿ���ÿ@80ÿ¼¥ŒÿðèßÿÿÿÿÿÿÿÿÿàϽÿ‘lÿ ÿ���ÿ���ÿ���¿���z��� ����������������������������������������������������������������`£›Ea¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿb¡žî€€€��������������������000%222k333°333ñ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿQywÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿX‹‡ÿ333ÿ333ÿ333ü555D�������������������������������������������������������������������������������������������������������������������������������������������������������������������s���Œ���ÿ���ÿ �ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿQBÿ���ÿ���ÿÿXMBÿl_QÿbVIÿ}n]ÿÆ®”ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿœ‰uÿÿ���ÿ���ÿJ>4ÿÓõÿ¨›ÿjbZÿ:61ÿ���ÿbG,ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿ˜ƒnÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿº¬Ÿÿ^D+ÿ���ÿ:0'ÿWH:ÿœiÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ ÿ���ÿ���ÿMD:ÿÆ®”ÿôíæÿÿÿÿÿÿÿÿÿÝʶÿ‡veÿÿ���ÿ���ÿ���³���w��������������������������������������������������������������������f™™a¡ëa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿb¢p��������������������������������@@@222=333‚333Ç333ü333ÿ333ÿ333ÿ333ÿ:CCÿ` œÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿ9BBÿ333ø444S�����������������������������������������������������������������������������������������������������������������������������������������������������������������������g���~���ÿ���ÿ���ÿžÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ™}ÿ8.ÿ���ÿ���ÿ ÿi\OÿcWJÿbVIÿŒ{iÿÑ·œÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ…udÿÿ���ÿ���ÿ„seÿÔĵÿ‰uÿjbZÿÿ ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿv`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ®ÿV>'ÿ���ÿ@5+ÿWH:ÿ¢†mÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿªrÿ�ÿ���ÿ���ÿZODÿзœÿ÷óîÿÿÿÿÿÿÿÿÿÙÄ­ÿteVÿ���ÿ���ÿþ���§���l������������������������������������������������������������������������a¡ž˜a¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡¶������������������������������������������������---444S444™333ß333ÿOwtÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿBXVÉ444'���������������������������������������������������������������������������������������������������������������������������������������������������������������������������Z���zú���ÿ���ÿsÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿˆoÿ$ÿ���ÿ���ÿ!ÿpbTÿbVIÿh[NÿŸ‹wÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿϵ›ÿ_SGÿ���ÿ���ÿ���ÿ²ŸÿÓôÿqiaÿh`Xÿÿ3%ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿ„kSÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ£~ÿN8#ÿ���ÿH;0ÿWH:ÿªrÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ—}eÿ���ÿ���ÿ���ÿpaSÿÒ¸ÿüúøÿÿÿÿÿÿÿÿÿÖ¿¦ÿ`UHÿ���ÿ���ÿû���¢���a������������������������������������������������������������������������b¢4a¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ñUªª������������������������������������������������������������222$a¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡žO�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������J���zé���ÿ���ÿp\ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿvaÿ ÿ���ÿ���ÿ4.'ÿseVÿbVIÿqcUÿ®˜‚ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ»¤ŒÿB:1ÿ���ÿ���ÿ ÿÍ»«ÿ³¥ÿjbZÿRLFÿ���ÿM7#ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿ}cIÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ–lÿD1ÿ���ÿOB5ÿYJ<ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿƒlXÿ���ÿ���ÿÿ…ucÿÔ¼¢ÿÿÿÿÿÿÿÿÿþþýÿжœÿRH>ÿ���ÿ���ÿö���œ���U����������������������������������������������������������������������������a¡žÇa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ýb ������������������������������������������������������������a¡žTa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡÷€¿€�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������.���zÅ���ÿ���ÿJ<ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿaPÿ�ÿ���ÿ���ÿJA7ÿobSÿbVIÿzk[ÿ¼¥ŒÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ¥{ÿ(#ÿ���ÿ���ÿB8/ÿÔĵÿ¬Ÿ“ÿjbZÿ;62ÿ�ÿeI.ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿ}cIÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿgNÿ7(ÿÿVG9ÿaPAÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿp]Kÿ���ÿ���ÿ ÿ‘lÿÚÄ®ÿÿÿÿÿÿÿÿÿùõñÿŬ“ÿG>5ÿ���ÿ���ÿð���—���K����������������������������������������������������������������������������b¢`a¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡×������������������������������������������������������������m’’a¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿb ¯��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������z���¡���ÿ���ÿ"ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¤†ÿH;ÿ���ÿ���ÿÿdWJÿfZLÿbVIÿƒsbÿ˲˜ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿŽ}kÿ ÿ���ÿ���ÿzm`ÿÔĵÿšŽƒÿjbZÿ'$!ÿ ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿ|aGÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷õôÿmO2ÿ%ÿ ÿWH:ÿlZIÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ[K=ÿ���ÿ���ÿÿœ‰uÿßͺÿÿÿÿÿÿÿÿÿóìäÿ»¤Œÿ=5-ÿ���ÿ���ÿê���‘���>����������������������������������������������������������������������������€€€a žºa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿb S��������������������������������������������������������Uªªa¡þa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿ`¢œX���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������n���ƒ���þ���ÿ�ÿ‘wÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿš~ÿ8.ÿ���ÿ���ÿÿl_QÿbVIÿcWJÿ’€nÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿk]Pÿ���ÿ���ÿ���ÿª™ŠÿÔĵÿˆ~tÿjbZÿÿ!ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿuY>ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÚÓËÿlN1ÿ ÿÿWH:ÿ|fSÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ=3)ÿ���ÿ���ÿ*%ÿ¨“~ÿåÖÆÿÿÿÿÿÿÿÿÿíâ×ÿ¬—ÿ,'!ÿ���ÿ���ÿà���‹���3��������������������������������������������������������������������������������^¡¡a¡®a¡ÿa¡ÿa¡ÿa¡öa¡½a Œa¡ža¡žW€€€��������������������������������������������a œa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡äUªª���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������P���zè���ÿ���ÿWGÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿŽtÿ*"ÿ���ÿ���ÿ ÿoaSÿbVIÿk]OÿŸ‹wÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿĬ“ÿMC9ÿ���ÿ���ÿÿ̺ªÿÔĵÿzqhÿjbZÿÿ3%ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿ€fMÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¾±¤ÿfI.ÿ�ÿ/'ÿWH:ÿ‹s^ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ ÿ���ÿ���ÿ>6.ÿ¸¡‰ÿêÞÒÿÿÿÿÿÿÿÿÿçÙËÿŠvÿÿ���ÿ���ÿÑ���‚���"����������������������������������������������������������������������������������������`ŸŸ0b¢cb¢`a¡Óa¡ÿa¡ÿa¡ÿa¡ÿa¡Æažž������������������������������������f™™ a¡úa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa žn�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������)���z±���ÿ���ÿÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿjÿÿ���ÿ���ÿ-'"ÿqcUÿbVIÿrdUÿª•ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ´ž‡ÿ92+ÿ���ÿ���ÿ.'!ÿÔĵÿÔĵÿof^ÿc\Tÿ���ÿ@.ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿ‹s\ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ—‚nÿN9$ÿ���ÿC7,ÿWH:ÿžƒjÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ«Žsÿÿ���ÿ���ÿPG<ÿȯ–ÿòêâÿÿÿÿÿÿÿÿÿâÑ¿ÿŽ|jÿ ÿ���ÿ���ÿ���Ã���{���������������������������������������������������������������������������������������������������` œa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿ�ÿÿ��������������������cœœ€€€����b¡9a¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿUªª����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������s���ƒý���ÿ���ÿ|eÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿv`ÿ ÿ���ÿ���ÿ:2+ÿqcTÿbVIÿyj[ÿ· ‰ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ¤{ÿ&!ÿ���ÿ���ÿ]SJÿÔĵÿÏ¿±ÿjbZÿYRKÿ���ÿL7"ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿ–lÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúùøÿqT8ÿ2$ÿÿTF8ÿ\L>ÿ­uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ“ycÿ���ÿ���ÿ���ÿj]OÿÒ¸ÿùöòÿÿÿÿÿÿÿÿÿÚÅ®ÿseVÿ���ÿ���ÿ���ÿ���¯���u������������������������������������������������������������������������������������������������jª• a¡ýa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa žn����^¡¡b Sa¡žÇa¡êa¡ÿa¡òa¡ž¥a¢ž?a¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿb¢`���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������K���zÚ���ÿ���ÿ0'ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿhTÿ�ÿ���ÿ���ÿMC9ÿk^PÿbVIÿq`ÿª‘ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ“nÿÿ���ÿ���ÿˆ{pÿÔĵÿɺ¬ÿjbZÿQKEÿ���ÿU=&ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿ¦”ƒÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÔËÂÿlN1ÿ ÿÿWH:ÿs_Mÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿuaNÿ���ÿ���ÿÿ‰xfÿÔ»¡ÿÿÿÿÿÿÿÿÿþýüÿжÿWL@ÿ���ÿ���ÿû���¢���b������������������������������������������������������������������������������������������������b¢4a¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¢žüa¢èa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡äa¡ôa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡žd����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������z���—���ÿ���ÿ�ÿŠqÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿN@ÿ���ÿ���ÿÿaUHÿdXKÿbVIÿˆwfÿδšÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿp_ÿÿ���ÿ���ÿ¬ÿÔĵÿõ§ÿjbZÿLF@ÿ���ÿZA)ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿ¿²¥ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¦“‚ÿZA)ÿ���ÿ6-$ÿWH:ÿŽu_ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿWH;ÿ���ÿ���ÿÿ™†sÿÛÆ±ÿÿÿÿÿÿÿÿÿöñìÿÁ©ÿC;3ÿ���ÿ���ÿò���˜���O������������������������������������������������������������������������������������������������jª• a¡ùa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡Ûa¡ýa¡ÿa¡ÿa¡ÿa¡ÿa¡þa œy���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������_���zç���ÿ���ÿ:/ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿŸ‚ÿ=2ÿ���ÿ���ÿ ÿi\OÿbVIÿeYLÿ‘lÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿзœÿ^SFÿ���ÿ���ÿÿɹ¬ÿÔĵÿ¾°£ÿjbZÿKE@ÿ���ÿ_E+ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿÙÑÊÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿø÷õÿtX<ÿ7(ÿÿQC6ÿYI<ÿ©‹qÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ9/'ÿ���ÿ���ÿ-'!ÿª•ÿãÒÂÿÿÿÿÿÿÿÿÿîäÚÿ°š„ÿ1,%ÿ���ÿ���ÿå���Ž���;����������������������������������������������������������������������������������������������������a¡¾a¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡Öc œ>a¡Îa¡üa¡ýa¡œÏb F�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������&���z���¢���ÿ���ÿ�ÿ‡nÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ•zÿ2)ÿ���ÿ���ÿÿnaRÿbVIÿk^Qÿš‡sÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿª‘ÿJA7ÿ���ÿ���ÿ!ÿÔĵÿÔĵÿ»­ ÿjbZÿKE@ÿ���ÿ_E+ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿóðîÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿź®ÿlN1ÿ ÿÿWH:ÿo\Kÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ ÿ���ÿ���ÿA90ÿº£‹ÿëàÔÿÿÿÿÿÿÿÿÿæ×Èÿ¡xÿÿ���ÿ���ÿÕ���…���(����������������������������������������������������������������������������������������������������_ œCa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡å��������jª• jª• ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������h���{ê���ÿ���ÿ1(ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿuÿ*#ÿ���ÿ���ÿ% ÿoaSÿbVIÿqcUÿ¢ŽyÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ³†ÿ70)ÿ���ÿ���ÿF?9ÿÔĵÿÔĵÿ»­ ÿjbZÿKF@ÿ���ÿ_E+ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿƒjQÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŠr[ÿZA)ÿ���ÿ4+"ÿWH:ÿ‹s]ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿš€hÿ���ÿ���ÿ���ÿ[PDÿÍ´™ÿõïéÿÿÿÿÿÿÿÿÿÞʶÿ{iÿ ÿ���ÿ���ÿ���È���|�����������������������������������������������������������������������������������������������������������` Ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡³�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������*���z���›���ÿ���ÿ���ÿkXÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿˆoÿ"ÿ���ÿ���ÿ,'!ÿpbTÿbVIÿwhYÿ«–€ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ¨“~ÿ)$ÿ���ÿ���ÿg]TÿÔĵÿÔĵÿ¾°£ÿjbZÿSLFÿ���ÿY@(ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿ±¡’ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÝÖÏÿlN1ÿ+ÿÿSE7ÿZK=ÿ§Špÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿr^Mÿ���ÿ���ÿÿƒraÿÓ¹ŸÿþýüÿÿÿÿÿþþþÿÔ»¢ÿfZMÿ���ÿ���ÿ�þ���¯���w�����������������������������������������������������������������������������������������������������������Z¥–a¡åa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡üc¡œ6���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������c���zâ���ÿ���ÿ ÿ“xÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿiÿÿ���ÿ���ÿ3-'ÿoaSÿbVIÿ|m]ÿ´†ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿœ‰uÿÿ���ÿ���ÿ~sgÿÔĵÿÔĵÿǸªÿjbZÿZSMÿ���ÿP:%ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿÜÕÏÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ“}gÿaF,ÿÿ(!ÿWH:ÿ|gTÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿJ>2ÿ���ÿ���ÿÿ›ˆtÿÛÆ±ÿÿÿÿÿÿÿÿÿöñëÿª‘ÿF>5ÿ���ÿ���ÿ÷���ž���`����������������������������������������������������������������������������������������������������������������`£œPa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿb¡žî`¢œX�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������"���z���ý���ÿ���ÿ$ÿ¡„ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿzdÿÿ���ÿ���ÿ:3,ÿqcTÿbVIÿqaÿ¼¥ŒÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ’€mÿ ÿ���ÿ���ÿ”…yÿÔĵÿÔĵÿÏ¿±ÿjbZÿd]Uÿ���ÿC0ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿy]CÿýýýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÛÔÍÿlN1ÿ5&ÿÿNA4ÿWH;ÿ¢†mÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ#ÿ���ÿ���ÿ70)ÿ±›„ÿåÕÅÿÿÿÿÿÿÿÿÿìàÕÿ¬—ÿ.("ÿ���ÿ���ÿç������E��������������������������������������������������������������������������������������������������������������������b §a¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡üa¡Å`¡r]¢¢���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������V���z©���ÿ���ÿ���ÿ?3ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿr]ÿ ÿ���ÿ���ÿF>4ÿnaRÿbVIÿ‡veÿÁ©ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ…vdÿÿ���ÿ���ÿ¨˜ŠÿÔĵÿÔĵÿÔĵÿsjbÿjbZÿÿ3%ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿ³¤•ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþÿxaÿhK/ÿÿ ÿWH:ÿs_Mÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ¦‰oÿÿ���ÿ���ÿQG<ÿǯ•ÿîåÚÿÿÿÿÿÿÿÿÿáоÿ—„qÿÿ���ÿ���ÿÑ���ƒ���*��������������������������������������������������������������������������������������������������������������������f¦™a¡êa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡õf™™��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���k���zÇ���ÿ���ÿ���ÿ^Mÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿkWÿ�ÿ���ÿ���ÿSH>ÿk^PÿbVIÿ‹ygÿÆ®”ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿpbSÿ���ÿ���ÿ���ÿº©šÿÔĵÿÔĵÿÔĵÿ‚xoÿjbZÿÿ ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿoR6ÿñîëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿȼ±ÿlN1ÿ7'ÿÿI=1ÿWH:ÿ›€hÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿkWÿ���ÿ���ÿ���ÿseVÿÒ¸ÿúöóÿÿÿÿÿÿÿÿÿ׿§ÿyj[ÿÿ���ÿ���ÿ���¼���z���������������������������������������������������������������������������������������������������������������������������b¢xa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡œ£��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������u���|â���ÿ���ÿ�ÿ|eÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ\Kÿ���ÿ���ÿÿaUHÿgZMÿeXKÿŒ{iÿ˲˜ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿзœÿZODÿ���ÿ���ÿ�ÿ˹©ÿÔĵÿÔĵÿÔĵÿ–‹€ÿjbZÿ/+(ÿÿkM0ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿ¢}ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿóðîÿw[@ÿaF,ÿÿ& ÿWH:ÿmZIÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿUF9ÿ���ÿ���ÿÿ“€nÿØÁªÿÿÿÿÿÿÿÿÿùöòÿ˲˜ÿRH>ÿ���ÿ���ÿü���¤���m��������������������������������������������������������������������������������������������������������������������������������`¢¬a¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡í_  #bžž"€€€�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+���z���‚ô���ÿ���ÿ ÿ’wÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿVFÿ���ÿ���ÿÿfZMÿcWJÿh[NÿŒ{iÿзœÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿȯ–ÿMD:ÿ���ÿ���ÿ ÿÑÀ±ÿÔĵÿÔĵÿÔĵÿ¯¡•ÿjbZÿHB=ÿ���ÿS<%ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿnQ4ÿæáÜÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŸ‹xÿlN1ÿ"ÿ ÿSF;ÿWH;ÿœiÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ"ÿ���ÿ���ÿ1+%ÿ¬—ÿãÔÃÿÿÿÿÿÿÿÿÿìâ×ÿ¶Ÿˆÿ92*ÿ���ÿ���ÿï���—���R��������������������������������������������������������������������������������������������������������������������������������€€€`¡ra¡éa¡ÿa¡úa¡žÔa Õa¡ûa¡ÿa¡òb¡…m’’���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������C���z���ü���ÿ���ÿ ÿŸ‚ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿN@ÿ���ÿ���ÿ ÿi\OÿbVIÿl_Qÿ~lÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿª‘ÿF>4ÿ���ÿ���ÿ#ÿÔĵÿÔĵÿÔĵÿÔĵÿ˼­ÿjb[ÿd\Uÿÿ0#ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿ¬›‹ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¶ªÿlN1ÿE2 ÿ���ÿGB<ÿZL@ÿvbOÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿœiÿ�ÿ���ÿ���ÿSI>ÿÆ®”ÿïæÜÿÿÿÿÿÿÿÿÿàλÿœ‰uÿÿ���ÿ���ÿÜ���‰���7��������������������������������������������������������������������������������������������������������������������������������������������f£™`ŸŸa¡ça¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡¸€¿€��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Y���z¦���ÿ���ÿ���ÿ:/ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿM?ÿ���ÿ���ÿ ÿi\OÿbVIÿoaSÿ”‚oÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ¼¥Œÿ@80ÿ���ÿ���ÿ0(!ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ‚xoÿjbZÿÿÿiL0ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿz_Dÿ÷õôÿÿÿÿÿÿÿÿÿÿÿÿÿÞ×ÑÿoR6ÿ[B*ÿÿ# ÿjbZÿbSFÿ¦‰oÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿjXGÿ���ÿ���ÿÿp_ÿÒ¸ÿúøõÿÿÿÿÿþýüÿÔ»¢ÿyj[ÿÿ���ÿ���ÿ���Á���|���������������������������������������������������������������������������������������������������������������������������������������������������a¡ž“a¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿ` —��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���j���zÄ���ÿ���ÿ���ÿ[Jÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿG:ÿ���ÿ���ÿ ÿl^PÿbVIÿrdUÿ”‚oÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ· ‰ÿ:3+ÿ���ÿ���ÿ;2*ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ§šÿjbZÿGB<ÿ���ÿC1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿËÀµÿÿÿÿÿÿÿÿÿÿÿÿÿéäßÿ{`FÿcH-ÿ ÿ ÿc\TÿjbZÿ•kÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ:0'ÿ���ÿ���ÿ"ÿŸ‹wÿÚÅ®ÿÿÿÿÿÿÿÿÿôíæÿŬ“ÿKB8ÿ���ÿ���ÿü���¥���s���������������������������������������������������������������������������������������������������������������������������������������������������a¡žÔa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ãf™™ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������u���{ß���ÿ���ÿ�ÿxbÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿD7ÿ���ÿ���ÿÿl_QÿbVIÿteWÿ˜…rÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ²œ…ÿ4.'ÿ���ÿ���ÿH=5ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ;¯ÿne^ÿh`Yÿ ÿ ÿiL0ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿ—‚nÿþþþÿÿÿÿÿÿÿÿÿéåàÿ{`FÿhK/ÿ ÿÿUNHÿjbZÿ„zÿ¸†ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ«sÿ ÿ���ÿ���ÿC;2ÿ»¤ŒÿèÛÍÿÿÿÿÿÿÿÿÿçÙÊÿ¨“~ÿ*%ÿ���ÿ���ÿê���”���T����������������������������������������������������������������������������������������������������������������������������������������������������a¡Öa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¢ž{�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������*���z���ò���ÿ���ÿ ÿŽtÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¤†ÿA5ÿ���ÿ���ÿÿm_RÿbVIÿvgXÿ˜…rÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ­˜ÿ.("ÿ���ÿ���ÿRF;ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ”‰~ÿjbZÿE@;ÿ���ÿ3%ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿ{`FÿóñïÿÿÿÿÿÿÿÿÿÜÕÎÿ{`FÿfJ/ÿ ÿÿJD>ÿjbZÿ}sjÿÏ¿±ÿ¹Ÿˆÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ‚kWÿ���ÿ���ÿ���ÿi]OÿзœÿõðêÿÿÿÿÿÿÿÿÿÙÄ­ÿŠygÿ ÿ���ÿ���ÿÏ������0����������������������������������������������������������������������������������������������������������������������������������������������������b¡a¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ñ]¢¢�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������C���z���ü���ÿ���ÿÿÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ ƒÿ>3ÿ���ÿ���ÿÿnaSÿbVIÿxiYÿœˆtÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ¨“~ÿ(#ÿ���ÿ���ÿ`SGÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿǸªÿme]ÿjbZÿÿÿN8#ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿoR6ÿߨÒÿÿÿÿÿýüüÿ·¨šÿnQ4ÿ^D*ÿ ÿÿE?:ÿjbZÿuldÿȹ«ÿÔĵÿ»¢‹ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿM@4ÿ���ÿ���ÿÿ’€mÿÖ¾¥ÿÿþþÿÿÿÿÿøóïÿÍ´™ÿ]QEÿ���ÿ���ÿþ���°���z��� ����������������������������������������������������������������������������������������������������������������������������������������������������a¡žLa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿ` ”����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Y���z¢�ÿ���ÿ���ÿ6,ÿ¤†ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ ƒÿ>3ÿ���ÿ���ÿÿpbTÿbVIÿyj[ÿœ‰uÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ¥{ÿ"ÿ���ÿ���ÿiZMÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿœ‘…ÿjbZÿaZSÿ ÿÿP:%ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿÊ¿µÿíéåÿÁ´¨ÿhOÿlN1ÿE2ÿÿÿID>ÿjbZÿsjbÿ³¥ÿÔĵÿÔĵÿ½¥ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ­tÿ ÿ���ÿ���ÿ81*ÿ¯šƒÿäÔÃÿÿÿÿÿÿÿÿÿéÜÎÿ´†ÿ93+ÿ���ÿ���ÿô���š���c������������������������������������������������������������������������������������������������������������������������������������������������������������b ¯a¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡öd¦›������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���j���zÀ���ÿ���ÿ���ÿTEÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ ƒÿ>3ÿ���ÿ���ÿÿrcUÿbVIÿ{l\ÿœˆtÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ¥{ÿ"ÿ���ÿ���ÿueXÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÒ³ÿ€vmÿjbZÿ[TMÿ ÿÿ1#ÿeI.ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿQ:%ÿ ÿ���ÿÿWPJÿjbZÿuldÿô¦ÿÔĵÿÔĵÿÔĵÿ¿¨“ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿkWÿ���ÿ���ÿ���ÿcVJÿÍ´™ÿòêâÿÿÿÿÿÿÿÿÿÚÄ®ÿ™†sÿÿ���ÿ���ÿÜ���‰���A������������������������������������������������������������������������������������������������������������������������������������������������������������b£'a¡ûa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¢žƒ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������u���{Ý���ÿ���ÿ�ÿt_ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¢…ÿC7ÿ���ÿ���ÿÿqcUÿbVIÿ{l\ÿ˜…rÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ¥{ÿ"ÿ���ÿ���ÿn`SÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ˼­ÿyogÿjbZÿb[Sÿÿ���ÿÿ!ÿ9)ÿA/ÿ@.ÿ1#ÿ ÿ���ÿÿ51-ÿg_XÿjbZÿ€vmÿɺ¬ÿÔĵÿÔĵÿÔĵÿÔĵÿ«—ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿD8-ÿ���ÿ���ÿÿ“€nÿÕ½¤ÿþýýÿÿÿÿÿøôïÿÑ·œÿn`Rÿÿ���ÿ���ÿ���¾���{�������������������������������������������������������������������������������������������������������������������������������������������������������������������b¡•a¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡êUªª�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������)���z���ð���ÿ���ÿ �ÿŒrÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿF9ÿ���ÿ���ÿÿpbTÿcWJÿ{l\ÿ˜…rÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ¥{ÿ"ÿ���ÿ���ÿl^QÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿȹ«ÿ|sjÿjbZÿjbZÿIC>ÿÿÿ���ÿ���ÿ���ÿÿÿ951ÿb[SÿjbZÿjb[ÿ•ŠÿÑÁ²ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÄ®šÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿªrÿ ÿ���ÿ���ÿ>6.ÿ´ž‡ÿãÔÃÿÿÿÿÿÿÿÿÿéÜÎÿ· ‰ÿ=6.ÿ���ÿ���ÿù���¡���p�������������������������������������������������������������������������������������������������������������������������������������������������������������������f£™a¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿ_ œC���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������B���z���‹û���ÿ���ÿÿ›ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿM?ÿ���ÿ���ÿ ÿl_QÿeYKÿ{l\ÿ–ƒpÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ¥{ÿ"ÿ���ÿ���ÿi[NÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿο°ÿŽƒyÿjbZÿjbZÿjbZÿd\UÿSMGÿNHBÿUOHÿg_XÿjbZÿjbZÿjbZÿ~tkÿ¹«žÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿƱžÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿydQÿ���ÿ���ÿ���ÿj]Pÿж›ÿóìåÿÿÿÿÿÿÿÿÿÙ«ÿ”‚oÿÿ���ÿ���ÿß���‹���I������������������������������������������������������������������������������������������������������������������������������������������������������������������������b¡’a¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿb¢s������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Y���zŸ�ÿ���ÿ���ÿ1(ÿ¤†ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿTEÿ���ÿ���ÿ ÿj]Oÿg[Mÿ{l\ÿ”‚oÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ¥{ÿ(#ÿ���ÿ���ÿfWJÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿº¬Ÿÿ‚xÿme]ÿjbZÿjbZÿjbZÿjbZÿjbZÿph`ÿ„zÿ´§šÿÓôÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÈ´¢ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ;1(ÿ���ÿ���ÿÿ—„qÿÖ¿¦ÿþþþÿÿÿÿÿôîèÿÍ´™ÿ_TGÿ���ÿ���ÿ�ÿ���¹���{���������������������������������������������������������������������������������������������������������������������������������������������������������������������������b£/a¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡œ_������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���i���z¼���ÿ���ÿ���ÿPBÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿYIÿ���ÿ���ÿ ÿg[Mÿj]Oÿ{l\ÿ~lÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ¥{ÿ(#ÿ���ÿ���ÿeVIÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿο°ÿ»­ ÿ¬Ÿ“ÿª‘ÿ®¡”ÿÀ²¤ÿÒ´ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿË·¦ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ¢†mÿÿ���ÿ���ÿC;2ÿ¹¢‹ÿæ×ÈÿÿÿÿÿÿÿÿÿãÓÂÿ¯™ƒÿ4-&ÿ���ÿ���ÿõ���›���l�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������a¡œ°a¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ø`ŸŸ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������u���zÙ���ÿ���ÿ�ÿo[ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ`Oÿ���ÿ���ÿÿdXKÿpbTÿzk[ÿŒ{iÿзœÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿª•ÿ*$ÿ���ÿ���ÿ`QCÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿͺ©ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ`O@ÿ���ÿ���ÿÿvgXÿÑ·œÿ÷òîÿÿÿÿÿýûúÿÔ»¢ÿŠygÿ ÿ���ÿ���ÿ×���…���C��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������d¤›a¡ía¡ÿa¡ÿa¡ÿa¡ÿa¡ÿ`¡œw�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������)���z���î���ÿ���ÿ�ÿˆoÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿiVÿ���ÿ���ÿÿ_THÿugXÿxiYÿŒ{iÿ˲˜ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ®˜‚ÿ2,%ÿ���ÿ���ÿXI<ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿϽ­ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ­tÿÿ���ÿ���ÿ-("ÿ¥{ÿÜDzÿÿÿÿÿÿÿÿÿîãÙÿȯ–ÿRH>ÿ���ÿ���ÿþ���°���z���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������d¤›`¢¬a¡ña¡ÿa¡àb V���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������A���z���‰ú���ÿ���ÿÿ™}ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿmYÿÿ���ÿ���ÿQG<ÿ{l\ÿugXÿŒ{iÿÆ®”ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ´ž‡ÿ92+ÿ���ÿ���ÿH;0ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÒÁ²ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ~hTÿ���ÿ���ÿ���ÿ^RFÿ˲—ÿíãÙÿÿÿÿÿÿÿÿÿÜȳÿ¦‘|ÿ)$ÿ���ÿ���ÿ  ÿ"87ç'B@¯b£/����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������^¡›.`¡Áa¡ÿa¡ýa¢Ò`¢ŸJ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������X���zœþ���ÿ���ÿ-%ÿ£…ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿv`ÿ ÿ���ÿ���ÿA90ÿp`ÿseVÿŒ{iÿÀ¨ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ¼¥ŒÿA90ÿ���ÿ���ÿ;1(ÿÑÀ±ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ¯’wÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ5,$ÿ���ÿ���ÿÿ•ƒoÿÔ»¢ÿýüúÿÿÿÿÿõðêÿÑ·œÿwhYÿÿ���ÿ���ÿ ÿ1PNÿJ{xÿa¡ÿa¡â` —b¢<������������������������������������������������������������������������������������������������������������������������������������������������������������������������a£ž2a¡øa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡œ_��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������i���z¸���ÿ���ÿ���ÿJ=ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿhÿÿ���ÿ���ÿ70)ÿp`ÿqcUÿŒ{iÿº£‹ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿª‘ÿI@6ÿ���ÿ���ÿ/' ÿÍ»ªÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ³—~ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿšgÿÿ���ÿ���ÿI@6ÿ½¥ÿäÔÄÿÿÿÿÿÿÿÿÿãÒÁÿ´†ÿ=6.ÿ���ÿ���ÿÿ#97ÿ3USÿ\™•ÿa¡ÿa¡ÿa¡ÿa¡þa¡¦Z¥–�������������������������������������������������������������������������������������������������������������������������������������������������������������ÿÿa¡Îa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¢ðf™™��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������u���z×���ÿ���ÿ���ÿkXÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ‡oÿ$ÿ���ÿ���ÿ.)"ÿp`ÿoaSÿŒ{iÿ±›„ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿɰ–ÿRH=ÿ���ÿ���ÿ#ÿɵ¢ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ·œ„ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿRD7ÿ���ÿ���ÿ ÿpaÿÒ¸ÿøóïÿÿÿÿÿûøõÿÓºŸÿˆwfÿ ÿ���ÿ���ÿÿ-JHÿAmjÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿb §����������������m’’����������������������������������������������������������������������������������������������������������������������������������������` —a¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿ_ œC���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������(���z���}í���ÿ���ÿ�ÿ…mÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿvÿ/&ÿ���ÿ���ÿ&!ÿ}n]ÿn`RÿŒ{iÿ¨“~ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿж›ÿZOCÿ���ÿ���ÿÿů›ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿº¡Šÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ£‡nÿ ÿ���ÿ���ÿ71)ÿ¬—ÿß̸ÿÿÿÿÿÿÿÿÿèÛÍÿª‘ÿKB8ÿ���ÿ���ÿÿ21ÿ3TRÿU‰ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿ^¡š&[¤¤b¡•`¡æa¡ýa¡ÿb¡žªa¡œ_^¡¡��������������������������������������������������������������������������������������������������������������������b¢ha¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡žT�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������@���z���‡ù���ÿ���ÿÿ—|ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ™}ÿ8.ÿ���ÿ���ÿÿufXÿsdVÿŒ{iÿž‹vÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿi]Oÿ���ÿ���ÿÿ¾¨“ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ¾¦ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ[K=ÿ���ÿ���ÿÿqcTÿж›ÿòëäÿÿÿÿÿýüüÿÖ¿§ÿ˜…qÿÿ���ÿ���ÿÿ*ECÿ:`^ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa žaa¡Öa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡Öb¡’b N]¢¢��������������������������������������������������������������������������������������������`ŸŸ a¡ìa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿ`ŸŸ(����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������W���z™þ���ÿ���ÿ)!ÿ¢„ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¡„ÿC7ÿ���ÿ���ÿÿl_QÿxjZÿ‹zhÿ•ƒoÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿƒrbÿÿ���ÿ���ÿ©”€ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÁ«–ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ¦‰oÿ ÿ���ÿ���ÿ2,%ÿ¨“~ÿÙÄ­ÿÿÿÿÿÿÿÿÿìáÖÿ˲—ÿ[PDÿ���ÿ���ÿÿ*)ÿ2SQÿM€|ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡Èa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡Ð`¢›R�ÿÿ����������������������������������������������������������������������������b¢pa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿb¡Ñ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������i���zµ���ÿ���ÿ���ÿG:ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿRCÿ���ÿ���ÿÿ_THÿq`ÿ‡weÿ{iÿзœÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ’€mÿ ÿ���ÿ���ÿ|jÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿŰÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ`O@ÿ���ÿ���ÿÿnaSÿϵ›ÿîäÙÿÿÿÿÿþþýÿ×Á©ÿ£zÿ&!ÿ���ÿ���ÿ ÿ'@>ÿ4WUÿ^œ˜ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿb¡žîa¡Úa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡¾Uªª��������f£™`¢›R`¡œob [b V`Ÿ›@_ ›3_ œCb¡žDa žia¡žTb¢4jª• ��������a žaa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿb¢`����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������t���zÒ���ÿ���ÿ���ÿfTÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿkWÿ�ÿ���ÿ���ÿH?5ÿŠygÿƒsbÿŒ{iÿĬ“ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿŸ‹wÿ"ÿ���ÿ���ÿp`QÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿɵ¢ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ¨‹qÿ ÿ���ÿ���ÿ/)#ÿ¥{ÿØÁ©ÿþþýÿÿÿÿÿîäÚÿϵ›ÿl_Qÿÿ���ÿ���ÿÐ''©<d`Àa¡åa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡þ`¢œ]a¡àa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿb¡}����a žqa¡öa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ë`¢œU�ÿÿa¡êa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿ�ÿÿ�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������(���z���}ê���ÿ���ÿ�ÿjÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿzdÿÿ���ÿ���ÿ5/(ÿ‰xfÿq`ÿŒ{iÿ¶ŸˆÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ¬—ÿ1+$ÿ���ÿ���ÿNA5ÿÒÁ²ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ̹¨ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿdSCÿ���ÿ���ÿÿi\OÿÍ´™ÿíâ×ÿÿÿÿÿÿþþÿÙìÿ¦‘|ÿ-'!ÿ���ÿ���ÿù���¡���y����ÿÿ_ ›3a¢ž~a¡ÿa¡ÿa¡ú`¡É_ œC����a¡®a¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡Î`¢œXa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡üa¡ž\a¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ã^¡¡�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������?���z���…ù���ÿ���ÿÿ•zÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿˆoÿ&ÿ���ÿ���ÿ'"ÿ~n^ÿ‰xfÿŒ{iÿ¨“~ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿº£‹ÿA91ÿ���ÿ���ÿ3*"ÿË·¦ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿп¯ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ¥‰oÿ ÿ���ÿ���ÿ,&!ÿ¢Žyÿ×À¨ÿþýýÿÿÿÿÿïæÝÿÍ´™ÿgZMÿÿ���ÿ���ÿÒ���‚���R��������������������€€€Uªª������������c¡œ6a¡üa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ïa¡œÜa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿb¡Ùa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡å`ŸŸ(��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������W���z–þ���ÿ���ÿ&ÿ¡„ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ–{ÿ7-ÿ���ÿ���ÿÿqcTÿ~lÿŒ{iÿ›ˆtÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿȯ–ÿRH=ÿ���ÿ���ÿ ÿÄ®šÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ¯’wÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿTE8ÿ���ÿ���ÿÿm`RÿÍ´™ÿìáÖÿÿÿÿÿþþýÿØÂ«ÿ¢Žyÿ(#ÿ���ÿ���ÿù���Ÿ���z�����������������������������������������������a žYa èa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa Õa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ôa¡Ûa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿažž����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������h���z°���ÿ���ÿ���ÿA5ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ£…ÿH;ÿ���ÿ���ÿÿdXKÿ•ƒpÿŒ{iÿŽ}kÿÑ·œÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿrdVÿ���ÿ���ÿ���ÿª•ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ´˜ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿšgÿÿ���ÿ���ÿ81*ÿ«–€ÿØÁªÿþýüÿÿÿÿÿìâ×ÿ˲˜ÿbVIÿÿ���ÿ���ÿÑ���€���W����������������������������������������������������`ŸŸa œta¡»a¡÷a¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿb¢`a¡ûa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ë_ œCa¡þa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡öa œy€¿€������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������s���z Ï���ÿ���ÿ���ÿbPÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿdRÿ�ÿ���ÿ���ÿMC:ÿ•ƒoÿ“€nÿŒ{iÿª‘ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ~lÿ ÿ���ÿ���ÿm]ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ¸ž‡ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ?4*ÿ���ÿ���ÿ ÿ}n]ÿÑ·œÿðçÞÿÿÿÿÿýüûÿÖ¿§ÿŸ‹wÿ$ÿ���ÿ���ÿù���œ���z�������������������������������������������������������������������`ŸŸb¡žDb¡žˆb¡žÌa¡ýa¡ÿa¡ÿa¡ÿa¡ÿa¡þa Œ����`¡Áa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡«����_ œCb¡Ùa¡ÿa¡ÿa¡çb¡žˆb���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������'���z���|è���ÿ���ÿ�ÿ~gÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿu`ÿÿ���ÿ���ÿ2,&ÿ‡weÿž‹vÿŒ{iÿ¯šƒÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ¦‘|ÿ)$ÿ���ÿ���ÿNA5ÿÓ´ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ½¥ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿŠr]ÿ�ÿ���ÿ���ÿF>4ÿ¸¡‰ÿÛDZÿÿÿÿÿÿÿÿÿéÝÐÿɰ–ÿ]QFÿÿ���ÿ���ÿÏ������Z������������������������������������������������������������������������������������^¡¡b¡Qa¡ÿa¡ž b¡…b¡9��������c¢œ,a¡÷a¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ò`ŸŸ ������������`ŸŸ^¢™�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������>���z���ƒ÷���ÿ���ÿ ÿ“xÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿŠqÿ(!ÿ���ÿ���ÿÿufWÿ¨“}ÿŒ{iÿœ‰uÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ¹¢‹ÿB:1ÿ���ÿ���ÿ(!ÿ˸¦ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ«—ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ­uÿ)"ÿ���ÿ���ÿÿŒzhÿÒ¸ÿôíçÿÿÿÿÿûù÷ÿÕ¼£ÿ–ƒpÿÿ���ÿ���ÿö���š���z���������������������������������������������������������������������������������������������������������������������������c¢œ,a œÂa¡ýa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¢ž¿`ŸŸ(�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������V���z”ý���ÿ���ÿ"ÿŸ‚ÿ¥‡ÿ¥‡ÿ¥‡ÿŸ‚ÿ@5ÿ���ÿ���ÿÿbVIÿ¬—€ÿ’€mÿŽ|jÿϵ›ÿÒ¸ÿÒ¸ÿÒ¸ÿÍ´™ÿZODÿ���ÿ���ÿÿ»¥‘ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿDzŸÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿhVFÿ���ÿ���ÿ���ÿVL@ÿª‘ÿàμÿÿÿÿÿÿÿÿÿãÒÂÿ»¤ŒÿLC9ÿ���ÿ���ÿÿÇ���~���U������������������������������������������������������������������������������������������������������������������������������������`ŸŸc¡œ1b¡I`¢œUb ^b žfb žfa žYb Na¡žT`ŸŸ%����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������h���z­���ÿ���ÿ���ÿ=2ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿcQÿ�ÿ���ÿ���ÿB:1ÿ‰uÿª•ÿŒ{iÿ½¦ŽÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿƒtbÿÿ���ÿ���ÿ…tdÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ̸§ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿœiÿ ÿ���ÿ���ÿ,'!ÿœˆtÿÔ»¡ÿúöóÿÿÿÿÿõïéÿзœÿ|l]ÿ ÿ���ÿ���ÿ ï���•���x�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������s���z Ì���ÿ���ÿ���ÿ]Lÿ¥‡ÿ¥‡ÿ¥‡ÿ|fÿÿ���ÿ���ÿ+% ÿ‚raÿÀ¨ÿŒ{iÿ¨“~ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ£zÿ&!ÿ���ÿ���ÿB8.ÿÒÁ²ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿп¯ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ5,$ÿ���ÿ���ÿ ÿvgXÿÍ´™ÿêÞÑÿÿÿÿÿÿþþÿÛÆ±ÿ¦’|ÿ4-'ÿ���ÿ���ÿþ´���{���H���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������&���z���{ æ���ÿ���ÿ�ÿzdÿ¥‡ÿ¥‡ÿ‘wÿ1(ÿ���ÿ���ÿÿn`Rÿª‘ÿ–ƒpÿ’€mÿзœÿÒ¸ÿÒ¸ÿÒ¸ÿ¿§ÿH@6ÿ���ÿ���ÿ ÿƱžÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ°“xÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿs_Mÿ���ÿ���ÿ���ÿNE:ÿ¸¡‰ÿÙíÿþþýÿÿÿÿÿíâ×ÿȯ–ÿbUIÿÿ���ÿ���ÿ â���ˆ���s��� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������=���z���‚ö���ÿ���ÿ ÿvÿ¥‡ÿ£…ÿL>ÿ���ÿ���ÿÿQG<ÿ°š„ÿ°š„ÿŒ{iÿ¾§ŽÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿsdVÿÿ���ÿ���ÿ›‡vÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ¶š‚ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿžƒjÿ ÿ���ÿ���ÿ&!ÿ–ƒpÿÒ¸ÿôíæÿÿÿÿÿúøõÿÕ½¤ÿ~lÿÿ���ÿ���ÿ û ���z���:���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������U���z‘ý���ÿ���ÿÿŸ‚ÿ¥‡ÿq]ÿ ÿ���ÿ���ÿ-'"ÿŒ{iÿÍ´™ÿ~kÿ¦’|ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ™†sÿÿ���ÿ���ÿSF:ÿÒÁ²ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ»¢Œÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ¬Žtÿ/' ÿ���ÿ���ÿÿpbTÿ˲˜ÿåÖÇÿÿÿÿÿÿÿÿÿàλÿ±›„ÿF=4ÿ���ÿ���ÿÿ Ë������k���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������g���z©���ÿ���ÿ���ÿ8.ÿ¤†ÿŽtÿ-%ÿ���ÿ���ÿ ÿi\Oÿ˲—ÿª•ÿ~lÿзœÿÒ¸ÿÒ¸ÿÒ¸ÿ· ‰ÿ?7/ÿ���ÿ���ÿ ÿï›ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÁª•ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿYJ<ÿ���ÿ���ÿ���ÿJA7ÿ²œ…ÿÙ«ÿýüûÿÿÿÿÿïæÝÿÆ®”ÿgZMÿÿ���ÿ���ÿ ï���’���z���"��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������s���z É���ÿ���ÿ���ÿWGÿ¤†ÿSDÿ�ÿ���ÿ���ÿE<3ÿ¶Ÿˆÿɰ–ÿ{iÿ¸¢ŠÿÒ¸ÿÒ¸ÿÒ¸ÿÑ·œÿseWÿÿ���ÿ���ÿ„rdÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿƲŸÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿƒlXÿÿ���ÿ���ÿ-'!ÿ•ƒoÿÓ¹žÿõïéÿÿÿÿÿûù÷ÿÕ½¤ÿ‡veÿÿ���ÿ���ÿý¯���z���O�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������%���z���{ ä���ÿ���ÿ�ÿv`ÿycÿÿ���ÿ���ÿ% ÿŒ{iÿÒ¸ÿ¤{ÿœˆtÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ¤{ÿ(#ÿ���ÿ���ÿ-%ÿμ«ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿͺ©ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿŸ„kÿ ÿ���ÿ���ÿÿ|m]ÿδšÿçÙËÿÿÿÿÿÿÿÿÿàλÿ¤{ÿ92*ÿ���ÿ���ÿÿ Ô���„���p��� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������=���z���€õ���ÿ���ÿ �ÿ€iÿ6,ÿ���ÿ���ÿÿcWJÿϵ›ÿĬ“ÿŒ{iÿª‘ÿÒ¸ÿÒ¸ÿÒ¸ÿɰ–ÿWLAÿ���ÿ���ÿ�ÿ©•ƒÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÒ³ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿªrÿ,%ÿ���ÿ���ÿÿcWJÿª‘ÿÜÈ´ÿþþýÿÿÿÿÿìáÖÿ¾§ŽÿYOCÿÿ���ÿ���ÿ ñ���”���z���-�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������T���zŽý���ÿ���ÿÿZJÿ�ÿ���ÿ���ÿ70)ÿ®˜‚ÿÒ¸ÿ£zÿ¦‘|ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿlÿ ÿ���ÿ���ÿMA6ÿÑÀ±ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ²–}ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ­uÿ>3*ÿ���ÿ���ÿ���ÿI@6ÿ­˜ÿÖ¾¥ÿú÷õÿÿÿÿÿ÷òîÿзžÿyj[ÿ ÿ���ÿ���ÿ ý­���z���Y��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������f���z¦�ÿ���ÿ���ÿ)!ÿ ÿ���ÿ���ÿÿxiZÿÒ¸ÿɰ–ÿ~lÿ˲˜ÿÒ¸ÿÒ¸ÿÒ¸ÿ»¤ŒÿD;2ÿ���ÿ���ÿÿ®šˆÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ¹Ÿˆÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿRD7ÿ���ÿ���ÿ���ÿ/*#ÿ”‚oÿÓ¹žÿóìäÿÿÿÿÿýüûÿÙÄ­ÿ~kÿ'"ÿ���ÿ���ÿÿ Ë���€���s���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������s���z Ä���ÿ���ÿ���ÿ&ÿ���ÿ���ÿ���ÿC;2ÿÆ­”ÿÒ¸ÿ­˜ÿª•ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ‰ygÿ ÿ���ÿ���ÿE;1ÿÑÀ±ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÀ¨“ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿgUEÿ���ÿ���ÿ���ÿÿƒsbÿδšÿéÝÐÿÿÿÿÿÿÿÿÿâÒÁÿ¡xÿ;3,ÿ���ÿ���ÿ���ÿ è���Œ���z���*�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������$���z���z á���ÿ���ÿ�ÿ ÿ���ÿ���ÿÿŒziÿÒ¸ÿϵ›ÿ•ƒoÿʱ—ÿÒ¸ÿÒ¸ÿÒ¸ÿ¾§ŽÿH@6ÿ���ÿ���ÿÿ§“ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿDzŸÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ}gTÿÿ���ÿ���ÿÿxiYÿʱ—ÿâÑÀÿÿÿÿÿÿÿÿÿëßÓÿ±›„ÿMD:ÿÿ���ÿ���ÿ ùŸ���z���J�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<���z���ô���ÿ���ÿ�ÿ���ÿ���ÿÿG>5ÿʱ—ÿÒ¸ÿ· ‰ÿ§’}ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ‘lÿÿ���ÿ���ÿ7.&ÿÍ»«ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿμ«ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿzeRÿÿ���ÿ���ÿ ÿm_QÿĬ“ÿÞʶÿþýýÿÿÿÿÿóìäÿ¿¨ÿ`UHÿÿ���ÿ���ÿþ·���|���f��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������S���z‹ü���ÿ���ÿ�ÿ���ÿ���ÿÿ~kÿÒ¸ÿÑ·œÿŸ‹wÿȯ–ÿÒ¸ÿÒ¸ÿÒ¸ÿª‘ÿPG<ÿ���ÿ���ÿ���ÿpaÿÓõÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÓõÿ¯’wÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿwbPÿÿ���ÿ���ÿÿ`THÿ½¥ÿØÂ«ÿüúøÿÿÿÿÿùõñÿ̵žÿrcUÿ ÿ���ÿ���ÿÿ Î���€���v���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������f���z£ÿ���ÿ���ÿ���ÿ���ÿ���ÿA91ÿÆ­”ÿÒ¸ÿʱ—ÿ¦’|ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ Œwÿ'#ÿ���ÿ���ÿ ÿ·£’ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ¶›ƒÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿt`Nÿÿ���ÿ���ÿÿZOCÿ³†ÿØÁªÿú÷ôÿÿÿÿÿúøõÿÓ¾§ÿqaÿÿ���ÿ���ÿÿ à���‡���z���2��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������s���z Â���ÿ���ÿ���ÿ���ÿ���ÿ ÿ…udÿÒ¸ÿÒ¸ÿ¸¢Šÿ½¥ÿÒ¸ÿÒ¸ÿÒ¸ÿж›ÿ{k\ÿ ÿ���ÿ���ÿ5-%ÿͺ«ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ½¤Žÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿlYHÿÿ���ÿ���ÿÿ\QEÿ´ž‡ÿ×À¨ÿùöòÿÿÿÿÿüûùÿÖÁ«ÿ„tbÿ!ÿ���ÿ���ÿ���ÿ í�����z���H�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#���y���z Þ���ÿ���ÿ���ÿ���ÿ���ÿ3-&ÿ³†ÿÒ¸ÿÒ¸ÿ«–€ÿϵ›ÿÒ¸ÿÒ¸ÿÒ¸ÿÁ©ÿSI>ÿ���ÿ���ÿ���ÿo_QÿÒÁ²ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÄ®šÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ¬ŽtÿL?3ÿ���ÿ���ÿ���ÿÿ^RFÿ¶Ÿˆÿ×À§ÿùõñÿÿÿÿÿýüüÿÛÇ´ÿ†udÿ$ÿ���ÿ���ÿ���ÿ ÷���z���Z��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������;���z���}ò���ÿ���ÿ���ÿ���ÿÿeXKÿ˲˜ÿÒ¸ÿÍ´™ÿ±›„ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ¦‘|ÿ1+$ÿ���ÿ���ÿÿ“€oÿÓõÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ˸¦ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ¢†mÿ/' ÿ���ÿ���ÿ���ÿ ÿ`THÿ· ‰ÿÙìÿùöòÿÿÿÿÿýüûÿÜȵÿŠygÿ&!ÿ���ÿ���ÿ���ÿ ü«���z���i����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������S���zŠü���ÿ���ÿ���ÿ���ÿÿ}jÿÒ¸ÿÒ¸ÿª‘ÿŬ“ÿÒ¸ÿÒ¸ÿÒ¸ÿÑ·œÿ†udÿÿ���ÿ���ÿ ÿ |ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÒÁ²ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿwaÿÿ���ÿ���ÿ���ÿÿl^Pÿ»¤ŒÿÜDzÿûù÷ÿÿÿÿÿýûúÿÛdzÿ‹zhÿ)%ÿ���ÿ���ÿ���ÿ ý ´���|���s�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������e���z ÿ���ÿ���ÿ���ÿ���ÿ93+ÿ¨“~ÿÒ¸ÿÒ¸ÿª‘ÿδšÿÒ¸ÿÒ¸ÿÒ¸ÿʱ—ÿvgXÿÿ���ÿ���ÿ ÿ›ˆxÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ³—}ÿ®uÿ®uÿ®uÿ®uÿ®uÿ«Žsÿ[L=ÿÿ���ÿ���ÿ���ÿ&"ÿ~n_ÿÆ®”ÿàλÿýüûÿÿÿÿÿûù÷ÿ×Ä®ÿ‹zhÿ,& ÿ���ÿ���ÿ���ÿ ý º���|���w��� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������s���z ½���ÿ���ÿ���ÿ���ÿÿ[PDÿ»¤ŒÿÒ¸ÿÒ¸ÿë’ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿª‘ÿm_Qÿ ÿ���ÿ���ÿÿ‡wiÿÓ³ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿº¡Šÿ®uÿ®uÿ®uÿ®uÿw`ÿ#ÿ���ÿ���ÿ���ÿÿ;4,ÿ‘mÿϵ›ÿéÜÏÿÿÿÿÿÿÿÿÿ÷óîÿϺ¤ÿ}n^ÿ"ÿ���ÿ���ÿ���ÿ þ ½���|���w���&���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#���y���z Û���ÿ���ÿ���ÿ���ÿ ÿoaSÿĬ“ÿÒ¸ÿÒ¸ÿʱ—ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÀ¨ÿj]Oÿ ÿ���ÿ���ÿ���ÿYNDÿÌ»¬ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿí™ÿ®uÿ®uÿ¤ˆnÿUF9ÿÿ���ÿ���ÿ���ÿ ÿVK@ÿ¦‘|ÿÖ¿¦ÿôíæÿÿÿÿÿÿÿÿÿñêáÿÄ­–ÿobTÿÿ���ÿ���ÿ���ÿ þ Â���}���y���)�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������:���z���} ò���ÿ���ÿ���ÿ���ÿÿ|l\ÿʱ—ÿÒ¸ÿзœÿзœÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿª‘ÿqcUÿÿ���ÿ���ÿ���ÿÿ¥•‡ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ˸§ÿ¥‰oÿYI<ÿ ÿ���ÿ���ÿ���ÿÿ0*$ÿo_ÿª‘ÿáоÿûùöÿÿÿÿÿþýüÿæØÊÿ¹¢ŠÿbUIÿ ÿ���ÿ���ÿ���ÿ þ Ã���}���z���-�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������R���z‡ü���ÿ���ÿ���ÿ���ÿ ÿq`ÿ̳™ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿȯ–ÿ‚qaÿ+& ÿ���ÿ���ÿ���ÿ���ÿH@:ÿ³¥—ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ̽°ÿ¥œ”ÿ ÿ���ÿ���ÿ���ÿ���ÿ ÿYMBÿ¥{ÿÖÀ¨ÿòêãÿÿÿÿÿÿÿÿÿöñìÿÙÄ­ÿª•ÿSI?ÿÿ���ÿ���ÿ���ÿ ý »���|���z���1��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������d���zþ���ÿ���ÿ���ÿ���ÿ% ÿ„tcÿ˲˜ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿзœÿž‹vÿKB8ÿ ÿ���ÿ���ÿ���ÿ���ÿ@95ÿ•‰ÿǹ¬ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿʼ¯ÿ±§žÿˆ‚ÿHHGÿÿ���ÿ���ÿ���ÿ ÿF>4ÿ}jÿȲ›ÿêÞÒÿýüûÿÿÿÿÿûøõÿäÕÅÿε›ÿ“€nÿ<5-ÿÿ���ÿ���ÿÿ û ®���z���z���4����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������r���z »���ÿ���ÿ���ÿ���ÿ���ÿ!ÿ}n^ÿȯ–ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ¿§ÿ}n]ÿ1+%ÿÿ���ÿ���ÿ���ÿ���ÿÿ?;8ÿrjdÿ—…ÿ®£™ÿµª¡ÿ¶«¡ÿ´© ÿ­¤œÿŸ˜‘ÿˆ‚}ÿ^\Zÿ888ÿÿÿ���ÿ���ÿ���ÿ ÿE=4ÿŒ|iÿƱšÿêÞÒÿýüúÿÿÿÿÿùõòÿçÙÊÿÔ» ÿ¾§ŽÿugXÿ!ÿ���ÿ���ÿ���ÿÿ÷ Ÿ���z���x���,���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������"���y���z Ø���ÿ���ÿ���ÿ���ÿ���ÿÿrdUÿÀ¨ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ²œ…ÿseVÿ4-'ÿÿ���ÿ���ÿ���ÿ���ÿ���ÿ���ÿ���ÿÿÿÿÿ���ÿ���ÿ���ÿ���ÿ���ÿ���ÿÿ#ÿPF<ÿ‘‚pÿÕÆ¶ÿòëäÿüúøÿõïèÿìàÕÿÝÉ´ÿÒ¸ÿж›ÿ ŒwÿPF;ÿ ÿ���ÿ���ÿ���ÿÿñ “���z���t���!�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������9���z���|ò���ÿ���ÿ���ÿ���ÿ���ÿ ÿ^RFÿ°š„ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÑ·œÿ²œ…ÿ‡weÿKB8ÿ(#ÿÿ���ÿ���ÿ���ÿ���ÿ���ÿ���ÿ���ÿ���ÿ���ÿ���ÿ���ÿ ÿ&!ÿI@7ÿugXÿŸ‹wÿĬ“ÿÒ¸ÿÒ¸ÿÓº ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ»¤ŒÿxiZÿ)#ÿ���ÿ���ÿ���ÿ���ÿ ÿÞ…���z���o����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Q���z†û���ÿ ÿ���ÿ���ÿ���ÿÿ@80ÿ”‚oÿ̳™ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿǯ•ÿª•ÿ‘lÿj]PÿLC9ÿ>7.ÿ1+%ÿ,&!ÿ&!ÿ*%ÿ2,%ÿ=5-ÿMD:ÿh[NÿŽ|jÿ¤{ÿ¿§ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿŬ“ÿ‘€mÿD;3ÿÿ���ÿ���ÿ���ÿÿ ý ¿���{���z���h�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������z���z2'Ñ3*ÿ ÿÿ���ÿ���ÿ���ÿÿl_Qÿ´†ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ˲˜ÿÁ©ÿ¸¢Šÿ¶Ÿˆÿ²œ…ÿ²œ…ÿ¸¢ŠÿÀ¨ÿ˲˜ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÆ®”ÿ™†sÿTJ?ÿÿ���ÿ���ÿ���ÿ���ÿ ÿó œ���z���z���T�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������g���z��{/%¿9.!ÿÿÿ���ÿ���ÿ���ÿÿ81*ÿ‡vdÿº£‹ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ¿§ÿ”‚oÿXMAÿÿ���ÿ���ÿ���ÿ���ÿÿþ#Öƒ���z���x���6������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���^���z���z' ¯>2$ý(!ÿ ÿ���ÿ���ÿ���ÿ���ÿ ÿ@80ÿƒsbÿ¯šƒÿϵ›ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ˲—ÿª•ÿƒsbÿND:ÿÿ���ÿ���ÿ���ÿ���ÿÿ ÿ$ñ ¡���z���z���m���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������R���z���z™A4$ï4*ÿÿÿ���ÿ���ÿ���ÿ���ÿ ÿ5.'ÿeXKÿ—„qÿ´†ÿÍ´™ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿª‘ÿ©”~ÿŠygÿ\PEÿ5.'ÿ ÿ���ÿ���ÿ���ÿ���ÿÿ ÿ"ý"Å}���z���z���O��� ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������C���z���z ƒ6+Ð?3$ÿ,$ÿ ÿÿ���ÿ���ÿ���ÿ���ÿÿÿ70)ÿYNBÿ„sbÿœˆtÿ¯™ƒÿ½¥ÿȯ–ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿ˲—ÿª‘ÿ´†ÿ¢Žyÿ~lÿpbTÿKB8ÿ/)#ÿ ÿ���ÿ���ÿ���ÿ���ÿ���ÿÿ ÿ"þ)!Ú ���z���z���k���"���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������&���q���z���zŸ@3$ì;0"ÿ( ÿ ÿÿ���ÿ���ÿ���ÿ���ÿ���ÿ���ÿÿÿ.)#ÿ=6.ÿKB8ÿSI>ÿ^RGÿbUIÿ_SGÿUK@ÿLB8ÿ@80ÿ4-'ÿ ÿ ÿ���ÿ���ÿ���ÿ���ÿ���ÿ���ÿ���ÿÿ ÿ%þ*"Û ’���z���z���x���C�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������W���z���z}(!±B4&ô;/"ÿ+"ÿ ÿ ÿ�ÿ���ÿ���ÿ���ÿ���ÿ���ÿ���ÿ���ÿ���ÿ���ÿ���ÿ���ÿ���ÿ���ÿ���ÿ���ÿ���ÿ���ÿ���ÿ���ÿ���ÿ���ÿ���ÿÿ ÿÿ,#ü,"Ø ’���z���z���z���U������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+���n���z���z~' ¯>2$ê?3$ÿ2)ÿ$ÿ ÿ ÿÿ���ÿ���ÿ���ÿ���ÿ���ÿ���ÿ���ÿ���ÿ���ÿ���ÿ���ÿ���ÿ���ÿ���ÿ���ÿÿÿ ÿÿ+#ÿ0&ë&· ƒ���z���z���z���W���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������;���s���z���z���z˜4*ÉA4%ó>2#ÿ5+ÿ-%ÿ!ÿ ÿ ÿ ÿ ÿÿÿÿÿÿ ÿ ÿ ÿ ÿÿ)!ÿ1'þ2(é-&Å•���z���z���z���y���V�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������7���l���z���z���z{–,$¸7- ÔA4$ð@4%ÿ<0"ÿ8- ÿ5+ÿ3*ÿ2(ÿ1(ÿ3)ÿ3)ÿ5+ÿ8- ÿ:/"û8-!ç3)Ç"¥ „���z���z���z���z���g���9��� ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#���Q���v���z���z���z���z���z€ ‘!¢$©' ¯,#µ' ¯' ¯"¥™ ‹{���z���z���z���z���z���j���G�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#���G���b���x���z���z���z���z���z���z���z���z���z���z���z���z���z���z���n���N���/������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������)���9���G���O���U���Y���V���Q���L���@���4���!���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÃÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ�?ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿü�ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿø��ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿð��ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿà���ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿà���ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÀ����ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÀ����ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€����ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€�����ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€�����ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ�������?ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ�������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ��������?ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ��������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ���������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ���������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿü����������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿü����������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿü�����������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿø�����������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿø�����������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿø������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿð������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿð�������������?ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿà�������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿà��������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿà��������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÀ���������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÀ���������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÀ����������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€����������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€����������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€�����������������ÿÿÿÿÿÿÿÿÿÿÿÿÿ������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿ�������������������?ÿÿÿÿÿÿÿÿÿÿÿÿ�������������������ÿÿÿÿÿÿÿÿÿÿÿþ��������������������?ÿÿÿÿÿÿÿÿÿÿþ��������������������ÿÿÿÿÿÿÿÿÿÿþ���������������������ÿÿÿÿÿÿÿÿÿü���������������������ÿÿÿÿÿÿÿÿÿü���������������������ÿÿÿÿÿÿÿÿÿø����������������������ÿÿÿÿÿÿÿÿÿø����������������������ÿÿÿÿÿÿÿÿø����������������������?ÿÿÿÿÿÿÿÿð����������������������?ÿÿÿÿÿÿÿÿð����������������������ÿÿÿÿÿÿÿÿð����������������������ÿÿÿÿÿÿÿÿà����������������������ÿÿÿÿÿÿÿÿà����������������������ÿÿÿÿÿÿÿÿà����������������������ÿÿÿÿÿÿÿÿÀ����������������������ÿÿÿÿÿÿÿÿÀ����������������������?ÿÿÿÿÿÿÿÿÀ����������������������?ÿÿÿÿÿÿÿÿ€����������������������?ÿÿÿÿÿÿÿÿ€����������������������ÿÿÿÿÿÿÿÿ€����������������������ÿÿÿÿÿÿÿÿ�����������������������ÿÿÿÿÿÿÿÿ�����������������������ÿÿÿÿÿÿÿÿþ�����������������������ÿÿÿÿÿÿÿÿþ�����������������������ÿÿÿÿÿÿÿÿþ����������������������ÿÿÿÿÿÿÿÿü����������������������ÿÿÿÿÿÿÿÿü����������������������ÿÿÿÿÿÿÿÿü����������������������ÿÿÿÿÿÿÿÿø����������������������ÿÿÿÿÿÿÿÿø����������������������ÿÿÿÿÿÿÿÿø����������������������ÿÿÿÿÿÿÿÿð����������������������ÿÿÿÿÿÿÿÿð����������������������ÿÿÿÿÿÿÿÿð����������������������ÿÿÿÿÿÿÿÿà����������������������ÿÿÿÿÿÿÿÿà����������������������ÿÿÿÿÿÿÿÿà����������������������ÿÿÿÿÿÿÿÿÀ����������������������ÿÿÿÿÿÿÿÿÀ����������������������?ÿÿÿÿÿÿÿÿ€����������������������?ÿÿÿÿÿÿÿÿ€����������������������?ÿÿÿÿÿÿÿÿ€����������������������ÿÿÿÿÿÿÿÿ�����������������������ÿÿÿÿÿÿÿþ�����������������������ÿÿÿÿÿÿÿø�����������������������ÿÿÿÿÿÿÿÿà�����������������������ÿÿÿÿÿÿÿÿ€�����������������������ÿÿÿÿÿÿÿþ�����������������������ÿÿÿÿÿÿÿø������������������ÿ���ÿÿÿÿÿÿÿà������������������ÿÀ��ÿÿÿÿÿÿÿ€������������������ÿà��ÿÿÿÿÿÿü�������������������ÿð��ÿÿÿÿÿÿð�������������������?ÿø��ÿÿÿÿÿÿÀ�������������������ÿø��ÿÿÿÿÿÿ��������������������ÿü��ÿÿÿÿÿü��������������������ÿÿü��ÿÿÿÿÿø��������������������ÿÿþ��ÿÿÿÿÿà��������������������ÿŸþ��ÿÿÿÿÿÀ��������������������þþ��ÿÿÿÿÿ���������������������üþ��ÿÿÿÿþ���������������������øþ��ÿÿÿÿü���������������������ø�þ��?ÿÿÿÿð���������������������ð�|��?ÿÿÿÿà���������������������ð�|��?ÿÿÿÿÀ���������������������p�<��ÿÿÿÿ€���������������������p���ÿÿÿÿ����������������������0���ÿÿÿÿ���������������������������ÿÿÿÿþ�����������������������@���ÿÿÿÿü����������������������à���ÿÿÿÿü�����������������������ð��ÿÿÿÿø��������������������������ÿÿÿÿø��������������������������ÿÿÿÿø��������������������������ÿÿÿÿð��������������������������ÿÿÿÿð��������������������������ÿÿÿÿð��������������������������ÿÿÿÿà��������������������������ÿÿÿÿà��������������������������ÿÿÿÿà��������������������������ÿÿÿÿÀ��������������������������ÿÿÿÿÀ�������������������������ÿÿÿÿÀ������������������à������ÿÿÿÿÀ������������������þ������ÿÿÿÿÀ������������������ÿà�����?ÿÿÿÿÀ������������������ÿü�����?ÿÿÿÿ€������������������ÿþ�����?ÿÿÿÿ€������������������ÿþ�����ÿÿÿÿ€������������������ÿþ�€���ÿÿÿÿ€������������������ÿþ�x���ÿÿÿÿÿ€������������������ÿÿ�€�ÿÿÿÿÿ€������������������ÿÿ�ð�ÿÿÿÿÿÀ������������������ÿÿ�?ÿ�ÿÿÿÿÿÀ������������������ÿÿ€?ÿàÿÿÿÿÿÀ������������������ÿÿ€?ÿàÿÿÿÿÿÀ������������������ÿÿÀ?ÿÀÿÿÿÿÿÀ������������������ÿÿÀÿ€ÿÿÿÿÿÀ������������������ÿÿàÿ�ÿÿÿÿÿà������������������ÿÿþÿ�?ÿÿÿÿÿà������������������ÿÿüÿ�?ÿÿÿÿÿà������������������ÿÿüÁ�ÿÿÿÿÿð������������������ÿÿü���ÿÿÿÿÿÿð������������������ÿÿü��ÿÿÿÿÿÿø������������������ÿÿü��Cÿÿÿÿÿÿø������������������ÿÿþ��ÿÿÿÿÿÿü������������������ÿÿþ��ÿÿÿÿÿÿü������������������ÿÿÿ��ÿÿÿÿÿÿÿþ������������������ÿÿÿ€ÿÿÿÿÿÿÿþ������������������ÿÿÿ€ÿÿÿÿÿÿÿÿ������������������ÿÿÿÀÿÿÿÿÿÿÿÿ€�����������������?ÿÿÿàÿÿÿÿÿÿÿÿÀ�����������������?ÿÿÿà?ÿÿÿÿÿÿÿÿÀ�����������������?ÿÿÿøÿÿÿÿÿÿÿÿà�����������������?ÿÿÿÿÿÿÿÿÿÿÿÿð�����������������ÿÿÿþ�ÿÿÿÿÿÿÿÿø�����������������ÿÿÿþ�ÿÿÿÿÿÿÿÿü�����������������ÿÿÿþ�ÿÿÿÿÿÿÿÿü�����������������ÿÿÿþ�ÿÿÿÿÿÿÿþ�����������������ÿÿÿÿÿ�?ÿÿÿÿÿÿÿÿ�����������������ÿÿÿÿÿ�?ÿÿÿÿÿÿÿÿ€����������������ÿÿÿÿÿ€ÿÿÿÿÿÿÿÿÀ���������������ÿÿÿÿÿ€ÿÿÿÿÿÿÿÿà���������������ÿÿÿÿÿÀÿÿÿÿÿÿÿÿà���������������ÿÿÿÿÿÀÿÿÿÿÿÿÿÿð���������������ÿÿÿÿÿàÿÿÿÿÿÿÿÿø���������������ÿÿÿÿÿàÿÿÿÿÿÿÿÿü���������������ÿÿÿÿÿð?ÿÿÿÿÿÿÿÿþ���������������ÿÿÿÿÿøÿÿÿÿÿÿÿÿþ���������������ÿÿÿÿÿü?ÿÿÿÿÿÿÿÿÿ����������������ÿÿÿÿøÿÿÿÿÿÿÿÿÿ€���������������ÿÿÿÿðÿÿÿÿÿÿÿÿÿÀ���������������ÿÿÿÿàÿÿÿÿÿÿÿÿÿà��������������� ÿÿÿàÿÿÿÿÿÿÿÿÿà���������������ÿÿÿ€ÿÿÿÿÿÿÿÿÿð�����������������?ÿÿ�ÿÿÿÿÿÿÿÿÿø�����������������ÿþ�ÿÿÿÿÿÿÿÿÿü����������������� �ÿÿÿÿÿÿÿÿÿþ��������������|0���?ÿÿÿÿÿÿÿÿÿþ��������������ø�����ÿÿÿÿÿÿÿÿÿÿ��������������ÿü�����ÿÿÿÿÿÿÿÿÿÿÿ€�������������ÿÿ��ÿÿÿÿÿÿÿÿÿÿÿÀ������������ÿÿà�ÿÿÿÿÿÿÿÿÿÿÿà������������ÿÿþ<�ÿÿÿÿÿÿÿÿÿÿÿÿà������������ÿÿÿþ�ÿÿÿÿÿÿÿÿÿÿÿÿð������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿø������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿü������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ������������?ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€�����������?ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÀ�����������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿà�����������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿà�����������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿð����������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿø����������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿü����������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ����������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ����������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ����������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€���������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÀ���������?ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿà���������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿð���������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿð��������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿø��������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿü��������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ��������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ��������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ��������?ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€�������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÀ�������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿà������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿð������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿð������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿü������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ������?ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€����ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÀ����ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿð����ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿü����ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÀ��ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿø��ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ(���€������� ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������999111C333<222$��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������$$$444”333û333ÿ333ÿ333ÿ333ß444™444S999������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+++ 444Ë333ÿ333ÿ333ÿ444þ333ÿ333ÿ333ÿ333ÿ333ü333Ç333‚222=@@@��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������444…333ÿ333ÿ333ÿ588ÿ;CDÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ò333°444l666&����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������333 333ò333ÿ333ÿOnsÿÖåÿ„Üìÿu¼Éÿ^Ž–ÿH_dÿ689ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ß333š444T999������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������222W333ÿ333ÿ455ÿ|ÌÛÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ‚×çÿn­¹ÿW~…ÿ@ORÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ü333É333ƒ111>333��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������222±333ÿ333ÿIaeÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ|ËÚÿf§ÿPouÿ:ACÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ó222±444l444'����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������999333ù333ÿ333ÿf§ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ„Ýíÿu½Êÿ_˜ÿH_dÿ588ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333à333›222V999������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������222f333ÿ333ÿ588ÿÕäÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ‚×çÿm­¹ÿW~…ÿAQTÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ý222Ê222„111>+++��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������222À333ÿ333ÿNlqÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿwÁÎÿg ªÿe¦ÿs¸Åÿ„Üìÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ}ÍÜÿfž¨ÿPpvÿ:BDÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ó333³333m333(����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������555333ý333ÿ333ÿlªµÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿyÆÓÿBSVÿ333ÿ333ÿ333ÿ333ÿ<FHÿ`‘šÿ‚Øèÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ„Ýíÿu½Êÿ_˜ÿH`dÿ588ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333â333›222W666������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������222u333ÿ333ÿ8?@ÿƒÚéÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿƒÙéÿ@ORÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ@NPÿv¿Ìÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ‚×çÿn¯»ÿX€‡ÿAQTÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ý222Ê333†444@+++��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������333Ï333ÿ333ÿTx~ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿc–Ÿÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ7;<ÿp²¾ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ}ÎÝÿgŸ©ÿPpvÿ;CDÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ó333µ333n333(����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������111*333ÿ333ÿ333ÿq´Àÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿNlqÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ6::ÿu¼Éÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ„Ýíÿu¾Ëÿ_˜ÿJbfÿ588ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ã222222W111������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������222„333ÿ333ÿ<FHÿ„Üìÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿH_cÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ>JLÿ‚×çÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ‚Øèÿn¯»ÿYˆÿBRUÿ444ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ý333Ì333†222B+++�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������333Ý333ÿ333ÿY‚‰ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿKejÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ\ˆÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ}ÎÝÿg ªÿQqwÿ;CDÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ô333µ222p111*����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������1119333ÿ333ÿ333ÿv¾Ëÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿW†ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ8>?ÿÖåÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ„Ýíÿv¿Ìÿ_™ÿJbfÿ699ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ã333Ÿ444Y111������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������222“333ÿ333ÿAPRÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿq´Àÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿdš¤ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Êãÿ…ºÛÿ…´Ùÿ…£Ïÿ…Ïåÿ…Ýíÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿƒÙéÿo°¼ÿY‚‰ÿBSVÿ444ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ý333Í333‡222B$$$����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������333333ë333ÿ333ÿ^Œ”ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿDWZÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿOnsÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þíÿ…´Øÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…£Ïÿ…´Ùÿ…Æâÿ…Øëÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ}ÎÝÿh¡«ÿQrxÿ;CDÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333õ222¶444q444,����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������333K333ÿ333ÿ444ÿzÇÕÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿp±½ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿBSVÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Áßÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…­Ôÿ…¾Ýÿ…Ðçÿ…Ýíÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿwÀÍÿa’›ÿJcgÿ6::ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ä333Ÿ333Z111������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������333¥333ÿ333ÿFZ]ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿQqwÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ7:;ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ªÓÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¦Ðÿ…¶Ùÿ…Èãÿ…Ùëÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿƒÙéÿp±½ÿY‚‰ÿCTWÿ444ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333þ333Î222‰111C@@@����������������������������������������������������������������������������������������������������������������������������������������������������������������... 333ô333ÿ333ÿc–Ÿÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ‚×çÿFZ]ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Üíÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¢Îÿ…®Õÿ…ÀÞÿ…Ñèÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ~ÏÝÿh¡«ÿRtzÿ<EFÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333í333›555��������������������������������������������������������������������������������������������������������������������������������������������������������333Z333ÿ333ÿ456ÿ~ÏÝÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿÕäÿH_dÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿTx~ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…§Ñÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…§Ñÿ…¸Úÿ…Éãÿ…Úìÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿwÀÍÿa“œÿKdhÿ6::ÿ333ÿ333ÿ333ÿ333ÿ344þ333ÿ333ï3337����������������������������������������������������������������������������������������������������������������������������������������������������333´333ÿ333ÿKdhÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ„Üëÿ]Š’ÿ699ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ444ÿ|ÌÛÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…½Üÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Ïÿ…°Öÿ…Âßÿ…Óèÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿƒÙéÿp²½ÿYƒŠÿCTWÿ455ÿ333ÿ333ÿ333ÿ333á�����������������������������������������������������������������������������������������������������������������������������������������������333333ú333ÿ333ÿg ªÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ|ËÙÿZƒ‹ÿ?LOÿ444ÿ333ÿ333ÿ333ÿG]`ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Üíÿ…¯Öÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¨Òÿ…ºÛÿ…Ëäÿ…Ûíÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ~ÐÞÿ\ˆÿ444ÿ333ÿ333ÿ333<��������������������������������������������������������������������������������������������������������������������������������������������333i333ÿ333ÿ6::ÿÖåÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ~ÏÞÿ588ÿ333ÿ333ÿc˜¡ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Ýîÿ…Çâÿ…¬Ôÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…£Ïÿ…²Öÿ…Ãßÿ…Öéÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿEY]ÿ333ÿ333ÿ333s��������������������������������������������������������������������������������������������������������������������������������������������333Ã333ÿ333ÿOnsÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿh¡«ÿ333ÿ333ÿ456ÿ~ÐÞÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Öêÿ…Åáÿ…³Øÿ…¤Ïÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…«Óÿ…Ëäÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ„Üìÿ;CDÿ333ÿ333ÿ444]����������������������������������������������������������������������������������������������������������������������������������������111333þ333ÿ333ÿlªµÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿLfkÿ333ÿ333ÿIaeÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Üíÿ…Íæÿ…»Üÿ…©Óÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…ºÛÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿq´Àÿ333ÿ333ÿ333ÿ111*����������������������������������������������������������������������������������������������������������������������������������������333x333ÿ333ÿ8?@ÿƒÚéÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ€Óâÿ578ÿ333ÿ333ÿeœ¦ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Õéÿ…Ãàÿ…²Öÿ…£Ïÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…Íåÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿTx~ÿ333ÿ333ÿ333Ò��������������������������������������������������������������������������������������������������������������������������������������������333Ò333ÿ333ÿTx~ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿf§ÿ333ÿ333ÿ578ÿÒáÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Üíÿ…Ëäÿ…ºÛÿ…§Ñÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…»Üÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿƒÚéÿ8?@ÿ333ÿ333ÿ333x����������������������������������������������������������������������������������������������������������������������������������������333-333ÿ333ÿ333ÿq´Àÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿJbfÿ333ÿ333ÿKejÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ„Ýíÿt»Çÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Óèÿ…Áßÿ…°Öÿ…¡Ïÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…´Ùÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿlªµÿ333ÿ333ÿ333þ111����������������������������������������������������������������������������������������������������������������������������������������333‡333ÿ333ÿ<FHÿ„Üìÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿÑßÿ456ÿ333ÿ333ÿg ªÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿTx~ÿDWZÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Úìÿ…Éãÿ…¸Úÿ…¦Ðÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…ÀÞÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿOnsÿ333ÿ333ÿ333Ã�������������������������������������������������������������������������������������������������������������������������������������������333à333ÿ333ÿY‚‰ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿd˜¢ÿ333ÿ333ÿ588ÿÕäÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿn®¹ÿ333ÿi¤®ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Ýîÿ…Ñèÿ…ÀÞÿ…®Ôÿ…¢Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¥Ðÿ…Øëÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿÖåÿ6::ÿ333ÿ333ÿ333i����������������������������������������������������������������������������������������������������������������������������������������333<333ÿ333ÿ333ÿv¾Ëÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿH_cÿ333ÿ333ÿNjpÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿyÆÓÿ456ÿBSUÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Ùëÿ…Èâÿ…¶Ùÿ…¦Ðÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¥Ñÿ…Ìåÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿg ªÿ333ÿ333ÿ333ú333����������������������������������������������������������������������������������������������������������������������������������������333–333ÿ333ÿAPRÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ}ÍÜÿ455ÿ333ÿ333ÿi¤¯ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ~ÐÞÿ<EGÿ689ÿ{ÈÖÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Ýíÿ…Éãÿ…µÙÿ…¼Üÿ…Åáÿ…Ùëÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿKdhÿ333ÿ333ÿ333´����������������������������������������������������������������������������������������������������������������������������������������333333ë333ÿ333ÿ^Œ”ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿb”ÿ333ÿ333ÿ7;;ÿÖæÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿzÈÖÿ>JMÿ699ÿ`˜ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿÑßÿ567ÿ333ÿ333ÿ444]����������������������������������������������������������������������������������������������������������������������������������������333K333ÿ333ÿ444ÿzÇÕÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿEY]ÿ333ÿ333ÿOnsÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿsºÇÿ;DEÿ455ÿEY]ÿ„Ýíÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿc˜¡ÿ333ÿ333ÿ333õ;;; ����������������������������������������������������������������������������������������������������������������������������������������333¥333ÿ333ÿFZ]ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ{ÊØÿ444ÿ333ÿ333ÿk§²ÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿƒÛêÿ[†Žÿ:ABÿ333ÿ;DFÿ}ÎÝÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿF[_ÿ333ÿ333ÿ444¨����������������������������������������������������������������������������������������������������������������������������������������... 333ô333ÿ333ÿc˜¡ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ`‘šÿ333ÿ333ÿ6;<ÿ‚Ùèÿ…Þîÿ…ÞîÿyÆÓÿ\‰’ÿ;EFÿ6:;ÿ333ÿ9@Bÿs¹Æÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ{É×ÿ444ÿ333ÿ333ÿ444N����������������������������������������������������������������������������������������������������������������������������������������111]333ÿ333ÿ567ÿÑßÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿAOSÿ333ÿ333ÿ69:ÿ<EGÿ>JMÿ=HJÿ;CDÿ445ÿ333ÿ333ÿ9@AÿgŸ©ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ^Ž–ÿ333ÿ333ÿ333í+++��������������������������������������������������������������������������������������������������������������������������������������7'''Æ333ÿ333ÿJdhÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿzÇÔÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ9?@ÿc— ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ„Üëÿj¥°ÿPpvÿCTWÿ<FGÿ@ORÿLglÿd˜¢ÿ€Óâÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿAQTÿ333ÿ333ÿ444™����������������������������������������������������������������������������������������������������������������������������������:���v Æ%%$þ333ÿ333ÿ[†Žÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ>JLÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ9ABÿfž©ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿr¶ÂÿAPRÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ9@Bÿe¦ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿwÀÍÿ333ÿ333ÿ333ÿ555?��������������������������������������������������������������������������������������������������������������������������<���z Êý���ÿ#!ÿ333ÿ333ÿ>JMÿxÂÐÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿV|ƒÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ577ÿ=GIÿr·Äÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿb•žÿ455ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿRtzÿ‚Ùèÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿZƒ‹ÿ333ÿ333ÿ333â������������������������������������������������������������������������������������������������������������������ ���@��� Ðý���ÿÿ<1ÿL@ÿ333ÿ333ÿ333ÿ333ÿAQTÿW†ÿn®ºÿ‚×çÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿyÄÒÿ9ABÿ577ÿ333ÿ333ÿ333ÿ689ÿ:BCÿV|‚ÿÕäÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿd˜¢ÿ333ÿ333ÿ333ÿ333þ222»333r333U444g333©333ö333ÿ333ÿ333ÿPpvÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ„Ýíÿ=HJÿ333ÿ333ÿ444Š����������������������������������������������������������������������������������������������������������� ���C���„Ôþ��ÿÿB6ÿUFÿWGÿWGÿC<ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ588ÿH`dÿ_˜ÿu½Êÿ„Ýíÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿp³¿ÿV|ƒÿLfkÿPpvÿgŸ©ÿÖåÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿzÈÖÿ689ÿ333ÿ333ÿ333á555:��������������������333444Á333ÿ333ÿ333ÿi£®ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿr¶Âÿ333ÿ333ÿ333ÿ5550��������������������������������������������������������������������������������������������������� ���F���ŠÚþ�ÿÿI;ÿZJÿ[Jÿ[Kÿ]Lÿ]Lÿ^MÿMCÿ:7,ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ:BDÿPpvÿfž¨ÿ}ÍÜÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿUy€ÿ333ÿ333ÿ333ñ333-����������������������������777333Ó333ÿ333ÿ@OQÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿUy€ÿ333ÿ333ÿ333Õ��������������������������������������������������������������������������������������������������E��‘ß�ÿ�ÿ#ÿPAÿ^Mÿ^Mÿ`Nÿ`OÿbPÿbPÿcQÿdRÿeSÿeSÿYLÿLCÿ53)ÿ222ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿAQTÿW~…ÿm­¹ÿ‚×çÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ„Üìÿ:BCÿ333ÿ333ÿ333t������������������������������������4446333þ333ÿ333ÿwÁÎÿ…Þîÿ…Þîÿ…ÞîÿƒÛêÿ9@Bÿ333ÿ333ÿ444{����������������������������������������������������������������������������������������������pÙ���ÿ�ÿ+#ÿVFÿbPÿcQÿdRÿdRÿfSÿfTÿhUÿhUÿiVÿiVÿjWÿkXÿlXÿcPÿ ÿ ÿXPFÿKF@ÿEB?ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ588ÿI`eÿ_˜ÿu½Êÿ„Ýíÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿtºÇÿ333ÿ333ÿ333ù333 ����������������������������������������444Á333ÿ333ÿ^•ÿ…Þîÿ…Þîÿ…Þîÿn­¹ÿ333ÿ333ÿ333ÿ222$���������������������������������������������������������������������������������������0��¤ù�ÿ.&ÿ\KÿfSÿgTÿhUÿiVÿiVÿjWÿkXÿkXÿmYÿmYÿo[ÿo[ÿp\ÿq]ÿr]ÿ2)ÿ���ÿ;4,ÿm`Rÿ£zÿÒ¸ÿº¤ÿ~oÿ853ÿ111ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ:ACÿPpvÿf§ÿ|ÌÛÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿg ªÿ333ÿ333ÿ333Ý������������a¢žBa žÔa¡Ú`¢ŸZ����������������333š333ÿ333ÿRtzÿ…Þîÿ…Þîÿ…ÞîÿQqwÿ333ÿ333ÿ333É����������������������������������������������������������������������������������� ���]Õ���ÿÿWGÿjWÿkWÿkXÿmYÿmYÿo[ÿo[ÿp\ÿq\ÿr]ÿs^ÿs^ÿt_ÿu`ÿv`ÿwaÿhTÿ ÿ ÿseUÿufXÿ˲˜ÿÒ¸ÿÒ¸ÿ–„pÿÿIC=ÿ¬ •ÿyrÿURNÿ444ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿAPSÿW~…ÿm­¹ÿ‚×çÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿeœ¦ÿ333ÿ333ÿ333×��������b¡Aa¡ÿa¡ÿa¡ÿa¡ÿ`¡œb������������444333ÿ333ÿOmrÿ…Þîÿ…Þîÿ‚×çÿ7;<ÿ333ÿ333ÿ333o����������������������������������������������������������������������������������ô�ÿB6ÿlYÿo[ÿp\ÿq\ÿr]ÿr]ÿs^ÿt_ÿt_ÿv`ÿvaÿxbÿxbÿycÿzdÿ{eÿ|fÿ|fÿ4+ÿ���ÿA80ÿl_Qÿ¦‘|ÿÒ¸ÿÒ¸ÿɰ–ÿ0*$ÿÿ¸©œÿÔĵÿÔĵÿÔĵÿɺ¬ÿž”ŠÿrlfÿHFDÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ588ÿH_dÿ_Ž—ÿu½Êÿ„Ýíÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿn­¹ÿ333ÿ333ÿ333ð�������b¡žîa¡ÿa¡ÿa¡ÿa¡ÿa¢ð_  #��������333¯333ÿ333ÿYˆÿ…Þîÿ…Þîÿi£®ÿ333ÿ333ÿ333ü555���������������������������������������������������������������������������!��£�þ ÿbPÿs^ÿt_ÿt_ÿu`ÿv`ÿwaÿxbÿycÿzdÿzdÿ|eÿ|fÿ~gÿ~gÿhÿhÿ€iÿjÿo[ÿ ÿ ÿufWÿugXÿÍ´™ÿÒ¸ÿÒ¸ÿ€paÿÿ]ULÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ¼¯¢ÿ‡~ÿd_Zÿ;::ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ:ACÿPouÿf§ÿ|ËÚÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿÖåÿ689ÿ333ÿ333ÿ222G�ÿÿa¡õa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa ÍUªª111333ò333ÿ333ÿn®ºÿ…Þîÿ…ÞîÿLglÿ333ÿ333ÿ333º�����������������������������������������������������������������������������˜�þÿxbÿxbÿxbÿycÿzdÿ{eÿ|fÿ}fÿ~gÿhÿhÿ€iÿjÿ‚jÿƒkÿƒkÿ…mÿ…mÿ‡nÿ‡oÿB6ÿ���ÿ@80ÿk^Qÿ¨“~ÿÒ¸ÿÒ¸ÿǯ•ÿ)$ÿÿ¿¯ ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÒ³ÿ¬ •ÿ‚zsÿURNÿ444ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿAPSÿW~…ÿn­¹ÿ‚×çÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿKejÿ333ÿ333ÿ333Ó333 a¡Ða¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿb¡…333Ÿ333ÿ333ÿ8>?ÿƒÚéÿ…Þîÿ€Òáÿ578ÿ333ÿ333ÿ222`��������������������������������������������������������������������������sùÿ|eÿ|fÿ~gÿ~gÿhÿ€iÿiÿ‚jÿƒkÿƒkÿ…mÿ…mÿ†nÿ‡nÿˆoÿ‰pÿ‰pÿŠqÿ‹rÿŒsÿƒkÿÿ ÿnaSÿqcUÿ˲˜ÿÒ¸ÿÒ¸ÿ…tcÿÿ]TKÿÔĵÿÔĵÿÔĵÿÔĵÿÓôÿ¾°£ÿ«ž’ÿ²¤˜ÿÆ·©ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÒ³ÿ©Žvÿ†r_ÿdXMÿC?<ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ689ÿH_dÿ_Ž—ÿu½Êÿ„Üìÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿn®ºÿ333ÿ333ÿ333ÿ?RQú_œ˜Õa¢ž¿`¢Èa¡òa¡ÿa¡ÿa¡ÿ_œ˜ò333ÿ333ÿ333ÿZ„Œÿ…Þîÿ…Þîÿd™£ÿ333ÿ333ÿ333÷777�����������������������������������������������������������������������7Ó�ÿp\ÿjÿ‚jÿƒkÿ„lÿ…mÿ†nÿ‡nÿˆoÿ‰pÿ‰pÿŠqÿŠqÿŒrÿŒsÿŽtÿŽtÿuÿvÿ‘wÿ’xÿUFÿ���ÿ0*%ÿo`Sÿ—…qÿÒ¸ÿÒ¸ÿʱ—ÿ-'!ÿÿº«œÿÔĵÿÔĵÿÓôÿ­ ”ÿxofÿ\UNÿID>ÿOICÿf^Wÿƒyÿ˼­ÿÔĵÿÔĵÿÔĵÿÔĵÿ«—ÿ®uÿ®uÿ®uÿ®uÿœƒlÿziYÿYPGÿ976ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ:ACÿOntÿf§ÿ|ËÚÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿNlqÿ333ÿ333ÿ333ÿ8@?ÿD^\¹222)]”‘Xa¡ÿa¡ÿa¡ÿYŠÿ333ÿ333ÿ?LOÿÖåÿ…Þîÿ…ÞîÿG]aÿ333ÿ333ÿ333«����������������������������������������������������������������������� ���†�ÿ<1ÿ‡nÿ‡oÿˆoÿ‰pÿŠqÿŠqÿŒrÿŒsÿsÿŽtÿuÿvÿ‘wÿ’wÿ’xÿ”yÿ”yÿ•zÿ–{ÿ—{ÿ”yÿ"ÿÿfZMÿh[NÿÁ©ÿÒ¸ÿÒ¸ÿ—…qÿÿG>6ÿÔĵÿÔĵÿ̼®ÿ„zqÿPJEÿÿÿ0#ÿ*ÿ ÿ=71ÿzqhÿ˼­ÿÔĵÿÔĵÿÒ³ÿ±”zÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ¬tÿyeÿoaSÿNGAÿ443ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ@ORÿW~…ÿm­¸ÿ‚×çÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ‚×çÿIaeÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿE]\ÿ[‘ŽÿZÿS}ÿLpnÿBVXÿyÆÓÿ…Þîÿ…Þîÿ|ËÙÿ455ÿ333ÿ333ÿ222Q�����������������������������������������������������������������������4 Î�ÿ{eÿŒsÿsÿŽtÿuÿuÿvÿ‘wÿ’xÿ“xÿ”yÿ•zÿ•zÿ—{ÿ—|ÿ˜|ÿ™}ÿ™}ÿ›ÿ›ÿ€ÿ~gÿÿÿteVÿƒsbÿÒ¸ÿÒ¸ÿзœÿC;3ÿ���ÿ¡‘ƒÿÔĵÿÊ»¬ÿyphÿ82-ÿ ÿ_E+ÿlN1ÿlN1ÿ¶¨™ÿƒkTÿ!ÿ61+ÿ‹€wÿÔĵÿÔĵÿí™ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ¦Šqÿ†r`ÿdXMÿC?<ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ689ÿH_dÿ^Ž–ÿu¼Éÿ„Üìÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ„ÜëÿY‚‰ÿ455ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿU„ÿa¡ÿa¡ÿe¦¤ÿƒÚéÿ…Þîÿ…Þîÿ_˜ÿ333ÿ333ÿ333ï$$$�����������������������������������������������������������������������dù(!ÿ‘wÿ’xÿ’xÿ”yÿ”yÿ•zÿ–{ÿ—{ÿ˜|ÿ™}ÿ™}ÿ›ÿ›ÿ€ÿ€ÿžÿŸ‚ÿŸ‚ÿ ƒÿ¡„ÿ¢…ÿF9ÿ���ÿD;3ÿg[Mÿ°›ƒÿÒ¸ÿÒ¸ÿ³†ÿ ÿÿÑÀ±ÿÒ³ÿ‚xoÿ2,'ÿ3%ÿlN1ÿlN1ÿlN1ÿ‘zeÿÿÿÿÿïìèÿz_Eÿ ÿXQKÿ´§šÿÒ³ÿ²–|ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿœ‚kÿziYÿYPGÿ765ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ:ACÿOntÿf§ÿ|ÌÚÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿwÁÎÿQrxÿ8?@ÿ333ÿ333ÿ333ÿ6:9ÿ` ÿa¡ÿa¡ÿa¡ÿtÂÉÿ…Þîÿ…ÞîÿBSVÿ333ÿ333ÿ333œ����������������������������������������������������������������������� ˜���ÿaOÿ—{ÿ—|ÿ˜|ÿ™}ÿš~ÿ›ÿœ€ÿ€ÿžÿŸ‚ÿŸ‚ÿ ƒÿ¡„ÿ¢„ÿ¢…ÿ¤†ÿ¤†ÿ¥‡ÿ¥‡ÿ¥‡ÿ€ÿÿÿk^PÿobSÿ˲˜ÿÒ¸ÿÒ¸ÿk]Pÿ���ÿ{m`ÿÔĵÿšŽƒÿE?9ÿ.!ÿlN1ÿlN1ÿlN1ÿlN1ÿ½°£ÿÿÿÿÿÿÿÿÿÎĺÿYA)ÿ#ÿ†|rÿÁª•ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿŽv_ÿ ÿB=7ÿ^\[ÿ555ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿAPRÿW~…ÿm­¸ÿ‚×çÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ„Ýíÿ}ÎÝÿwÀÍÿ|ÌÚÿ„Üëÿh­®ÿa¡ÿa¡ÿa¡ÿe¨¥ÿ„ÝìÿwÁÏÿ333ÿ333ÿ333ÿ222B�����������������������������������������������������������������������, Æ���ÿtÿ€ÿÿžÿŸ‚ÿ ƒÿ¡„ÿ¢„ÿ¢…ÿ£…ÿ¤†ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ|fÿÿ"ÿobTÿŠygÿÒ¸ÿÒ¸ÿ̳™ÿ-("ÿÿ½¬œÿ³¥ÿ_XQÿÿkM0ÿlN1ÿlN1ÿlN1ÿlN1ÿËÁ¶ÿÿÿÿÿÿÿÿÿÿÿÿÿ‹s]ÿÿcZSÿ¨‹rÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿw`ÿ���ÿND:ÿòêâÿçáÛÿXPFÿÿ///ö333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ578ÿH_dÿ^•ÿu¼Éÿ„Üìÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿxÉÑÿa¡ÿa¡ÿa¡ÿa¡ÿyÊÔÿ[‡ÿ333ÿ333ÿ333ç��������������������������������������������������������������������������I êÿ¤†ÿ¢…ÿ£…ÿ¤†ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿI<ÿ���ÿF=4ÿdXKÿ®™‚ÿÒ¸ÿÒ¸ÿ©”~ÿÿ2+$ÿÓôÿƒypÿ ÿZA)ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿƺ¯ÿÿÿÿÿÿÿÿÿÿÿÿÿĸ¬ÿ<+ÿD;3ÿ”{dÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿw`ÿ���ÿMD:ÿïæÝÿõïéÿh[Nÿ���ÿ¹ 7222k333°333ñ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ:ACÿOnsÿe¦ÿ|ÌÚÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þìÿa¡ÿa¡ÿa¡ÿa¡ÿo¸¼ÿ?KNÿ333ÿ333ÿ333���������������������������������������������������������������������������]ù6,ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¡„ÿ$ÿÿi[Nÿm`Rÿɰ–ÿÒ¸ÿÒ¸ÿdWKÿ���ÿreÿ·©œÿOIDÿ, ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿ¼® ÿÿÿÿÿÿÿÿÿÿÿÿÿðíêÿS<%ÿ/&ÿ…nYÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿt_ÿ���ÿPF<ÿòêâÿôîèÿeYLÿ���ÿ���²�����������@@@333<333‚444Æ333ü333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿAPRÿW†ÿm­¸ÿÖæÿ…Þîÿ…Þîÿp¼Àÿa¡ÿa¡ÿa¡ÿe§§ÿ333ÿ333ÿ333ÿ4446���������������������������������������������������������������������������r�ÿPAÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿŒsÿ ÿÿoaSÿ†udÿÒ¸ÿÒ¸ÿϵ›ÿ0*$ÿ�ÿ»©™ÿˆ}tÿÿ_E+ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿ®ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿqX@ÿ!ÿ€jVÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ„mYÿ���ÿZOCÿôîèÿôîèÿ]REÿ���ÿ���­���������������������������---IheE_]ÿ9A@ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ578ÿH_dÿ^•ÿp·Àÿb£Ÿÿa¡ÿa¡ÿU€€ÿ333ÿ333ÿ333Û�������������������������������������������������������������������������������„���ÿZJÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿdRÿ���ÿ6/(ÿfZLÿ¥{ÿÒ¸ÿÒ¸ÿ¶Ÿˆÿ ÿ% ÿó¥ÿ[TNÿ!ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿžŠwÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿˆr]ÿÿ{fSÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿzeRÿ���ÿfYLÿöñìÿôíæÿZODÿ���ÿ���ª�������������������������������`¡œba¡ÿa¡ÿZ‘ŽÿNsqÿ355³333ð333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿCYYÿ^™—ÿUƒÿ455ÿ333ÿ333ÿ444l�������������������������������������������������������������������������������Š���ÿaOÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ>3ÿ���ÿRH=ÿgZMÿ¼¥ŒÿÒ¸ÿÒ¸ÿ€paÿ���ÿk^Tÿ¤—Œÿ61,ÿJ5"ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿŽwaÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‘{fÿÿxcQÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿlZIÿ���ÿtfVÿùõñÿðèßÿKA8ÿ���ÿ���¢�������������������������������`ŸŸ0a¡ÿa¡ÿa¡ÿa¡ÿa¡ž\@@@444;333‚444Æ333ü333ÿ333ÿ333ÿ333ÿT‚ÿa¡ÿa¡ÿPzwÿ333ÿ333É@@@�������������������������������������������������������������������������������z���ÿSDÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¢„ÿ"ÿÿi[NÿqcTÿ̳™ÿÒ¸ÿÒ¸ÿJA8ÿ���ÿ¦•‡ÿ‚xoÿÿhK/ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿhOÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‰t`ÿÿ|fSÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿZK=ÿ���ÿŠyhÿûùöÿíãØÿ:2+ÿ���ÿ�����������������������������������€€€a¡øa¡ÿa¡ÿa¡ÿ`¡æ����������������000444S333—333Þ?ONÿa¡ÿa¡ÿa¡ÿa œÿ8==¤333 �����������������������������������������������������������������������������������fý@4ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ’xÿ ÿÿn_Rÿ„tbÿÒ¸ÿÒ¸ÿ˲˜ÿ'#ÿÿǵ¥ÿg_XÿÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿ}cIÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿt_Kÿ%ÿ‚lXÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿF:/ÿ���ÿ§“~ÿþþýÿèÜÎÿ+& ÿý���~������������������������������������a œya¡ÿa¡ÿa¡ÿa¡ù€€€����������������������������^›—¼a¡ÿa¡ÿa¡ÿa¡ÿ`ŸŸ���������������������������������������������������������������������������������������M ìÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿycÿ���ÿ+% ÿi]Oÿž‹vÿÒ¸ÿÒ¸ÿµŸ‡ÿ ÿ/("ÿ¾¯¢ÿPJDÿ1#ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿw[@ÿÿÿÿÿÿÿÿÿÿÿÿÿýüüÿQ<)ÿ/&ÿ‹s]ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ2*"ÿ ÿ¿­™ÿÿÿÿÿÞÏ¿ÿ ÿû���n������������������������������������d¦›a¡ëa¡ÿa¡ÿa¢ž¿����������������������������b¡a¡ÿa¡ÿa¡ÿa¡ÿa žº�������������������������������������������������������������������������������������������0 Ç���ÿŽtÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ[Kÿ���ÿ@8/ÿeYLÿ´ž‡ÿÒ¸ÿÒ¸ÿ}jÿ���ÿh]Rÿ«ž’ÿ;50ÿJ6"ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿuY>ÿÿÿÿÿÿÿÿÿÿÿÿÿåàÛÿ2$ÿA6+ÿ›€hÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿÿÿÓñÿÿÿÿÿͼªÿ ÿõ���Y����������������������������������������c¢œ,b¡ša¡¾`¡Éa¡œµ_ ›3�����������������ÿÿa¡üa¡ÿa¡ÿa¡ÿa¡ÿb¡Q������������������������������������������������������������������������������������������� ��‰�ÿOAÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿB5ÿ���ÿRH=ÿk^Qÿ¿§ÿÒ¸ÿÒ¸ÿdXKÿ���ÿ—ˆ{ÿ¢–Šÿ.("ÿV>'ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿ€fMÿÿÿÿÿÿÿÿÿÿÿÿÿÀ³¦ÿÿSE8ÿ«sÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ¦Špÿÿ.("ÿå×Èÿÿÿÿÿ³¡Žÿÿê���@��������������������������������������������€€€`¡æa¡ÿa¡ÿa¡ÿ^¡¡^¢™a žqa œ|`Ÿ›@a¡ÿa¡ÿa¡ÿa¡ÿa¡¸�ÿÿ�����������������������������������������������������������������������������������������������W ë ÿ›ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ+#ÿÿdWJÿqcUÿʱ—ÿÒ¸ÿÒ¸ÿB:2ÿ���ÿ¹ªœÿ™ƒÿ% ÿ_E+ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿzdÿÿÿÿÿÿÿÿÿÿÿÿÿ|gÿ ÿkYHÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ‰r\ÿ���ÿI@7ÿìáÖÿüûùÿ‚scÿ���ÿ×���,��������������������������������������������cœœa¡þa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¢ða¡éa¡ÿa¡ÿb¡žî^¡›.���������������������������������������������������������������������������������������������������›���ÿRCÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿŸ‚ÿÿ ÿj\Oÿzk\ÿÑ·œÿÒ¸ÿÍ´™ÿ)$ÿ ÿÒ´ÿ—Œÿ"ÿfI.ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿª™ˆÿÿÿÿÿÿÿÿÿïìèÿK8#ÿ0'ÿ‡p[ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿjXHÿ���ÿl_Qÿóìåÿôîèÿ_SGÿ���ÿº���������������������������������������������������`¡Áa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿ`¡œwb¢sb¢`jª• �����������������������������������������������������������������������������������������������������������[ ëÿ—{ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ˜|ÿÿÿj]Pÿ‰yfÿÒ¸ÿÒ¸ÿ¿§ÿÿ.)%ÿÔĵÿ›…ÿ& ÿ`E+ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿÎĺÿÿÿÿÿÿÿÿÿµ¥—ÿÿM@4ÿ£‡nÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿD8-ÿÿ‰wÿýüúÿëàÔÿ:3+ÿ���ÿ������������������������������������������������������`ŸŸ-a úa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡üc¡œ1�����������������������������������������������������������������������������������������������������������������������›���ÿ1(ÿ¤†ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ‘wÿ ÿÿi\Oÿ™‡sÿÒ¸ÿÒ¸ÿ´†ÿ ÿME>ÿÔĵÿ ”‰ÿ-'!ÿ\B*ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿoR6ÿöõóÿÿÿÿÿöôòÿfN7ÿÿs_Nÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿÿÿÉ·£ÿÿÿÿÿØÈ¸ÿÿü���w��������������������������������������������������������a¢ž~a¡ÿa¡ÿa¡ÿa¡ÿa¡þa¡ÿb¡Q�������������������������������������������������������������������������������������������������������������������������������R È���ÿQBÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿŠqÿÿ% ÿgZMÿ¥{ÿÒ¸ÿÒ¸ÿ¤{ÿ���ÿe[RÿÔĵÿªž’ÿA;7ÿJ5!ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿ¢}ÿÿÿÿÿÿÿÿÿ¯Ÿÿ%ÿG:/ÿ›€hÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ¢†mÿ�ÿ/*#ÿäÕÆÿÿÿÿÿ¶¢Žÿÿï���N��������������������������������������������������������f™™b œêa¡ÿa¡ÿa¡ÿa¡œd������������������������������������������������������������������������������������������������������������������������������������������f ä��ÿp\ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ}fÿ���ÿ0*$ÿeXKÿª•ÿÒ¸ÿÒ¸ÿˆxfÿ���ÿ}peÿÔĵÿ¾°£ÿWPJÿ0"ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿmO2ÿãÞØÿÿÿÿÿäÞÙÿV>(ÿ& ÿs_Mÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿt`Nÿ���ÿYMBÿðçÞÿøõñÿ{l\ÿ���ÿÖ���/������������������������������������������������������������c£œ$a¡Ða¡ùa¡Öa¡ž`¢œZ������������������������������������������������������������������������������������������������������������������������������������������xöÿ‰pÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿt_ÿ���ÿ70)ÿfYMÿ®˜‚ÿÒ¸ÿÒ¸ÿxiZÿ���ÿtÿÔĵÿÑÁ²ÿph`ÿÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿlN1ÿ£~ÿÿÿÿÿöõóÿgOÿÿ_VMÿ¤ˆnÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿB6,ÿÿ—…qÿú÷õÿëßÓÿF>5ÿ���ÿ®�����������������������������������������������������������������������`ŸŸ0a¡öa¡ÿa¡ÿa¡ž���������������������������������������������������������������������������������������������������������������������������������������"Œýÿš~ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿr]ÿ���ÿ71)ÿi\Oÿ±›„ÿÒ¸ÿÒ¸ÿl^Qÿ���ÿ—ˆzÿÔĵÿÔĵÿ“ˆ~ÿ0*%ÿH4!ÿlN1ÿlN1ÿlN1ÿlN1ÿx\Aÿòïìÿùø÷ÿ“}hÿ#ÿRKDÿ˜‰zÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ­uÿ ÿÿÊ·£ÿÿÿÿÿÓÀ­ÿÿý���������������������������������������������������������������������������a¡œla¡ÿa¡ÿa¡ÿa¡÷c¥œ���������������������������������������������������������������������������������������������������������������������������������������9¦�ÿ.%ÿ£…ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿkXÿ���ÿ=6.ÿk^QÿµŸ‡ÿÒ¸ÿÒ¸ÿaUIÿ���ÿ¡‘ƒÿÔĵÿÔĵÿõ§ÿf_Wÿ ÿbG-ÿlN1ÿlN1ÿnP3ÿ×ÎÆÿíéåÿŠs]ÿ"ÿB<7ÿ‚xÿƱžÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ‹s^ÿ���ÿ?7/ÿéÜÎÿüúøÿŸ‹xÿÿî���O������������������������������������������������������������������������b¡9a¡ÿa¡ÿa¡ÿa¡ÿ`¢¤�������������������������������������������������������������������������������������������������������������������������������������������R Å���ÿK=ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿhVÿ���ÿ@7/ÿoaSÿ¶ŸˆÿÒ¸ÿÒ¸ÿYNBÿ���ÿ§–‡ÿÔĵÿÔĵÿÔĵÿž’‡ÿMGBÿ ÿX?(ÿlN1ÿ‚hPÿŸ‹xÿQ<)ÿ ÿID=ÿ†|rÿÐÀ±ÿɵ£ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿPB6ÿ���ÿ€p`ÿ÷òîÿîåÚÿ]RFÿ���ÿÈ���)����������������������������������������������������������������������������b¢·a¡ÿa¡ÿa¡ÿa¡ý`ŸŸ ������������������������������������������������������������������������������������������������������������������������������������������d á���ÿlXÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿr]ÿ���ÿ>7.ÿoaSÿ³†ÿÒ¸ÿÒ¸ÿYNBÿ���ÿ¨˜‰ÿÔĵÿÔĵÿÔĵÿÒ´ÿŽƒyÿUNHÿÿ ÿ ÿ ÿ'"ÿ`XRÿ–Š€ÿÒ³ÿÔĵÿ˸§ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ­tÿ ÿÿıœÿÿÿÿÿÜ˹ÿ)$ÿþ���”�������������������������������������������������������������������������������^¡›.a¡ÿa¡ÿa¡ÿa¡ÿb¡…����������������������������������������������������������������������������������������������������������������������������������������������v õÿ…mÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿycÿ���ÿ81*ÿobSÿ¯šƒÿÒ¸ÿÒ¸ÿZOCÿ���ÿ¦–‡ÿÔĵÿÔĵÿÔĵÿÔĵÿÒ´ÿ¥™ÿwnfÿf_Wÿ`XQÿld\ÿ‹€wÿ¿±£ÿÔĵÿÔĵÿÔĵÿÍ»«ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ„mYÿ���ÿB:1ÿçÙËÿüúøÿ¤‘|ÿÿò���[������������������������������������������������������������������������������������a¡œµa¡ÿa¡ÿa¡ÿa¡©�����������������������������������������������������������������������������������������������������������������������������������������������"Šýÿ—|ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ€hÿ���ÿ2,%ÿrdUÿ¬—ÿÒ¸ÿÒ¸ÿ^RFÿ���ÿ¤’ƒÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÒ´ÿɺ¬ÿô¦ÿÑÁ²ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿо®ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿB6,ÿÿŒ{iÿ÷òîÿëßÓÿUJ?ÿ���ÿÇ���,������������������������������������������������������������������������������������b¡9a¡öa¡ÿa¡þb V���������������������������������������������������������������������������������������������������������������������������������������������������8¤ÿ)"ÿ¢…ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿŒrÿÿ.("ÿwhXÿ©”~ÿÒ¸ÿÒ¸ÿk]Pÿ���ÿŸŽÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÒ³ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ¡…lÿÿ"ÿÒÀ­ÿÿÿÿÿ͸£ÿÿþ ª7[[����������������������������������������������������������������������������������������a£ž:a¡åa¡œµ^¡¡�������������������������������������������������������������������������������������������������������������������������������������������������������Q Á���ÿG:ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ”yÿ ÿ!ÿ{l\ÿ¤{ÿÒ¸ÿÒ¸ÿ{k\ÿ���ÿ€sÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ¯’wÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ_N@ÿ���ÿeXKÿïæÝÿóìåÿ‡veÿÿÿBnkÿa¡õa¡ž­`ŸŸ��������������������������������������������������������������������������������a¡œ°a¡ÿa¡ÿa¢Ò�ÿÿ������������������������������������������������������������������������������������������������������������������������������������������������������d ß���ÿgTÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿÿÿÿ|m^ÿœ‰uÿÒ¸ÿÒ¸ÿŒ{hÿ���ÿ~pbÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ³—~ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ«Žsÿÿ ÿ»¨’ÿþýýÿÝÍ»ÿ3-&ÿÿ#75ÿVŒÿa¡ÿa¡ÿa¡ä`ŸŸb¢c`¡Áa¡œlf™™ ��������������������������������������������������������`¢¤a¡ÿa¡ÿa¡ÿa¡ÿ_  #����������������������������������������������������������������������������������������������������������������������������������������������������������t ó�ÿ‚jÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¤†ÿÿ ÿzk[ÿ“nÿÒ¸ÿÒ¸ÿª•~ÿÿgZNÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ·œ„ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿo\Kÿ���ÿND:ÿçÚÌÿùöóÿ yÿÿ ÿ8][ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡œµa¡ÿa¡ÿa¡ÿa¡÷a¡»`¡‡^¢™������������������������������������`ŸŸa¢Êa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿUªª�����������������������������������������������������������������������������������������������������������������������������������������������������������!ˆüÿ•zÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ2*ÿÿtfWÿ‰xfÿϵ›ÿÒ¸ÿº£‹ÿ ÿH=5ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿº¡Šÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ­tÿÿ ÿ¯š†ÿûøöÿãÔÃÿE<4ÿ���ÿ èM}îa¡ÿa¡ÿa¡ÿa¡œÏa¡ãa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ê`ŸŸ `¢œ]a ž²a¡œÏb¡žªb¡žªb¡Ùa œ„f¦™`¡Éa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¢ž†�������������������������������������������������������������������������������������������������������������������������������������������������������������������7  ÿ%ÿ¡„ÿ¥‡ÿ¥‡ÿ¥‡ÿOAÿ���ÿ\QEÿŠxgÿÁ©ÿÒ¸ÿǯ•ÿÿ&ÿÑÀ±ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ¾¦‘ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿr^Mÿ���ÿI@6ÿæØÉÿùöòÿ«–ÿ ÿú���z+++`ŸŸ5a¡œÏb¢pcœœa¡œ£a¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡œµa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡Åa¡þa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿUªª�����������������������������������������������������������������������������������������������������������������������������������������������������������������������P ¾���ÿA5ÿ¥‡ÿ¥‡ÿ¥‡ÿq]ÿ���ÿ>7.ÿ“nÿ´†ÿÒ¸ÿÒ¸ÿ6/(ÿÿɵ¤ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿí™ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ©Œrÿÿ ÿ±‡ÿüúøÿâÒÁÿE<3ÿ���ÿ È���7��������������������€¿€a œ„a¡ãa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿ`¡Áa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡õa¡»a¡ÿa¡ÿa¡ýa¡ž“`ŸŸ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������d Ü���ÿbPÿ¥‡ÿ¥‡ÿŽtÿÿ"ÿ˜†rÿ¢ŽyÿÒ¸ÿÒ¸ÿ\QEÿ���ÿ«™ˆÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÈ´¢ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ]M?ÿ���ÿ[ODÿéÜÎÿùõñÿ£{ÿÿú���|�����������������������������������`ŸŸ-a Ía¡ßa¡žÇa¡œla£ž2a¡÷a¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡ÿa¡œ¨f™™b¢sa œa¤ž*������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������s ñ�ÿ~gÿ¥‡ÿŸ‚ÿÿ ÿ|iÿ’€mÿÑ·œÿÒ¸ÿ{jÿ���ÿ|l^ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÍ»«ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿœiÿ ÿÿ«•ÿýûúÿÝ̺ÿ<4,ÿ���ÿ Â���7������������������������������������������������������������_ ›3a œ‰b §b¡žªb¡žªa¡žÇ`¢¤`ŸŸ�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������!‡ü ÿ“xÿ¥‡ÿ>3ÿ���ÿbVIÿ¡xÿë’ÿÒ¸ÿ¸¡‰ÿ ÿ8/'ÿÓ´ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÓ³ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ7-%ÿÿ~o_ÿïæÜÿñêáÿ…udÿÿ ô���o��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������7žÿ"ÿ ƒÿnZÿ���ÿ4.'ÿ°š„ÿ«–€ÿÒ¸ÿж›ÿ1+%ÿÿƳ¢ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ²–}ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿt`Nÿ���ÿ?7/ÿÚȶÿýüúÿɳÿÿÿ§���(�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������P »���ÿ=2ÿ”yÿ ÿ ÿ£Ž{ÿœ‰uÿÑ·œÿÒ¸ÿfYMÿ���ÿŒ|mÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ·…ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ›€hÿ ÿÿ¶ ‹ÿú÷óÿæØÉÿWMBÿ���ÿ ã���]��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������c Ú���ÿ]Lÿ6,ÿ���ÿeYLÿ³†ÿ¾§ŽÿÒ¸ÿ¯šƒÿ ÿ2*#ÿÒÁ²ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ½¤Žÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ«Žsÿ(!ÿÿ…udÿìáÕÿöñëÿ’nÿÿ û…���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������r ð�ÿUFÿÿ'"ÿÄ«“ÿ¦’|ÿÒ¸ÿзœÿ<5-ÿ��ÿ¯ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ«—ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿJ=2ÿ���ÿXNBÿßμÿüúøÿűœÿÿÿ ±���<�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������!…û ÿÿÿ‡veÿ¶Ÿˆÿǯ•ÿÒ¸ÿ~lÿÿD:3ÿÓ³ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÈ´¢ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿfUEÿ���ÿ92+ÿѽ¨ÿýüúÿÚÉ·ÿ?7/ÿ���ÿ Ý���c������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������7šþÿ���ÿ/)#ÿϵšÿ¯šƒÿÒ¸ÿ̳™ÿ6/(ÿ��ÿ¨–‡ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿϽ­ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿo\Kÿÿ*%ÿÁ¬•ÿùõòÿéÝÑÿ[PDÿ�ÿ õ��|��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������O ¸���ÿÿÿŽ|jÿë’ÿŬ“ÿÒ¸ÿ™‡rÿ ÿ#ÿ̺«ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ°“yÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿlYHÿÿÿ°œ‡ÿôíæÿòêâÿyk[ÿÿ ü’���.������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������c Ö���ÿÿ$ÿÆ®”ÿ½¥ÿÑ·œÿÑ·œÿYOCÿ���ÿaUJÿÓõÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ·…ÿ®uÿ®uÿ®uÿ®uÿ­uÿ]M>ÿ��ÿÿ¯›†ÿôîçÿóìäÿƒtdÿ ÿþ ©���I��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������q ï��ÿ�ÿgYLÿзœÿë’ÿÒ¸ÿĬ“ÿ0*$ÿ���ÿ‚tgÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ¾¦‘ÿ®uÿ®uÿ®uÿ§Špÿ<2)ÿ���ÿ$ ÿ³ž‰ÿõðêÿôíçÿŠyiÿ ÿÿ Â���Z������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������„ûÿ ÿ™†rÿϵ›ÿ̳™ÿÒ¸ÿ· ‰ÿ$ ÿÿ‡ylÿÓõÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿŰÿ®uÿ®uÿƒlXÿÿ��ÿ<5-ÿŰ›ÿ÷òíÿòëãÿsdÿ ÿÿ È���g��� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������6—þÿÿ· ˆÿж›ÿÒ¸ÿÒ¸ÿ±›…ÿ+& ÿ���ÿ]QHÿʹ«ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÍ»«ÿ”{dÿ?5+ÿ��ÿ ÿbVJÿÚʹÿüúøÿèÜÐÿfZMÿÿÿ Î���i��� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������N ¶�ÿ���ÿ,&!ÿº£‹ÿÒ¸ÿÒ¸ÿÒ¸ÿë’ÿJA7ÿÿÿzodÿÀ±¤ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿÑ´ÿ´©žÿvoiÿÿÿ3-&ÿ¯žÿñêáÿ÷óîÿͼ©ÿI@7ÿÿþ Å���k�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������b ×���ÿ���ÿ+& ÿ¹¢ŠÿÒ¸ÿÒ¸ÿÒ¸ÿϵ›ÿ’€mÿ+& ÿÿÿ%# ÿXQLÿh`Yÿjb\ÿLGCÿ%$$ÿÿ ÿ60)ÿ­ ’ÿêßÕÿñéáÿáϽÿ—„qÿ!ÿ���ÿ þ ³���d��� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������q óÿ�ÿÿ—…qÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿʱ—ÿž‹vÿUK@ÿ1+%ÿÿÿÿ)$ÿG>5ÿpbTÿ²›„ÿж›ÿÒ¸ÿÒ¸ÿ¸¡ŠÿQG=ÿ ÿÿ ø œ���[��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������w/&Å"ÿÿ ÿPG<ÿ´ž‡ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿȯ–ÿŬ“ÿ˲—ÿÑ·œÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿº£‹ÿh[Nÿÿ���ÿÿá‚���A�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������p' ¯/%ü ÿ���ÿ ÿRH>ÿžŠvÿȯ–ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿÒ¸ÿĬ“ÿžŠvÿMD:ÿÿ���ÿÿ ø©���m���#��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������_ 6+ä"ÿ ÿ���ÿÿ"ÿC:2ÿdXKÿ‡veÿ}kÿ}kÿ†udÿg[MÿB:2ÿÿÿ���ÿ ÿû!Ä���G�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������:��v!¢6+ç%ÿ ÿÿ���ÿ���ÿ���ÿ���ÿ���ÿ���ÿ���ÿÿ ÿ ÿ!ñ$º���Q�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���D���u ‘,&¾2)ä/&ú$ÿÿÿÿ#ÿ+#ù.$â' ¾ ”���s���I���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������.���V���q���z ˆ Œ• ‘‚���z���l���M���*������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ������)���)���'������ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿà?ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÀÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€�ÿÿÿÿÿÿÿÿÿÿÿÿÿ€�ÿÿÿÿÿÿÿÿÿÿÿÿÿ€��ÿÿÿÿÿÿÿÿÿÿÿÿ���ÿÿÿÿÿÿÿÿÿÿÿÿ����ÿÿÿÿÿÿÿÿÿÿÿÿ����ÿÿÿÿÿÿÿÿÿÿþ����ÿÿÿÿÿÿÿÿÿÿþ�����ÿÿÿÿÿÿÿÿÿþ�����ÿÿÿÿÿÿÿÿÿü������?ÿÿÿÿÿÿÿÿü������ÿÿÿÿÿÿÿÿø�������?ÿÿÿÿÿÿÿø�������ÿÿÿÿÿÿÿø��������ÿÿÿÿÿÿð��������ÿÿÿÿÿÿð���������ÿÿÿÿÿÿð���������ÿÿÿÿÿà����������ÿÿÿÿÿà����������ÿÿÿÿà����������ÿÿÿÿÀ����������ÿÿÿÿÀ����������ÿÿÿÿÀ����������ÿÿÿÿ€����������ÿÿÿÿ€����������ÿÿÿÿ€����������ÿÿÿÿ�����������ÿÿÿÿ�����������ÿÿÿþ�����������ÿÿÿþ�����������ÿÿÿþ�����������ÿÿÿü�����������ÿÿÿü�����������?ÿÿÿü�����������?ÿÿÿø�����������?ÿÿÿø�����������ÿÿÿø�����������ÿÿÿð�����������ÿÿÿà�����������ÿÿÿÿ€�����������ÿÿÿþ�����������ÿÿÿð���������?€ÿÿÿÀ���������Àÿÿÿ����������ÿàÿÿþ����������ÿàÿÿø����������óàÿÿð����������áàÿÿÀ����������Ààÿÿ€����������À`ÿÿ�����������@�ÿÿ�������������ÿþ����������� �ÿü�������������ÿü�������������ÿü�������������ÿø�������������?ÿø�������������?ÿø������������?ÿø������������ÿø���������à��ÿð���������ð��ÿÿð���������ðà�ÿÿø���������ð~ÿÿø���������?øƒÿÿø���������?øÿÿø���������?ü?ÿÿø���������?þÿÿü���������?þ�ÿÿü���������?þ�ÿÿÿþ���������?ÿ�ÿÿÿþ���������ÿÿÿÿÿ���������ÿ‡ÿÿÿÿ€��������ÿÃÿÿÿÿÀ��������ÿðÿÿÿÿÀ��������ÿðÿÿÿÿà��������ÿÿðÿÿÿð��������ÿÿðÿÿÿø��������ÿÿø?ÿÿÿü�������ÿÿø?ÿÿÿü�������ÿÿüÿÿÿþ�������ÿÿþÿÿÿÿ��������ÿü?ÿÿÿÿ€�������7ÿø?ÿÿÿÿÀ��������ð?ÿÿÿÿÀ��������0 ?ÿÿÿÿà������À��ÿÿÿÿð������à��ÿÿÿÿÿø������ü`ÿÿÿÿÿü������ÿð?ÿÿÿÿÿü������?ÿÿÿÿÿÿÿÿþ������?ÿÿÿÿÿÿÿÿÿ������ÿÿÿÿÿÿÿÿÿ€�����ÿÿÿÿÿÿÿÿÿÀ�����ÿÿÿÿÿÿÿÿÿÿÀ����ÿÿÿÿÿÿÿÿÿÿà����ÿÿÿÿÿÿÿÿÿÿð����ÿÿÿÿÿÿÿÿÿÿø����ÿÿÿÿÿÿÿÿÿÿü����ÿÿÿÿÿÿÿÿÿÿü����ÿÿÿÿÿÿÿÿÿÿþ����?ÿÿÿÿÿÿÿÿÿÿÿ����ÿÿÿÿÿÿÿÿÿÿÿ€���ÿÿÿÿÿÿÿÿÿÿÿÿÀ��ÿÿÿÿÿÿÿÿÿÿÿÿà��ÿÿÿÿÿÿÿÿÿÿÿÿð��ÿÿÿÿÿÿÿÿÿÿÿÿø��?ÿÿÿÿÿÿÿÿÿÿÿÿþ��ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿø?ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ(���0���`���� ������$����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������444I333s111>@@@����������������������������������������������������������������������������������������������������������������������������������������������������������������������������222a578úBUXö567ü577÷589·444l444'����������������������������������������������������������������������������������������������������������������������������������������������������������������6:;Ù^•ÿ…Þîÿ‚×çÿm­¹ÿV}†þ>LNö333ÿ69;ç466333U999��������������������������������������������������������������������������������������������������������������������������������������������333-333ÿ{É×ÿ…Þîÿ…Þîÿ~ÐÞÿÕäÿ…Þîÿ|ÌÛÿf§ÿOotû8>?ù333ý59:Ó222„111>+++����������������������������������������������������������������������������������������������������������������������������333‡EWZø…Þîÿ…Þîÿb•Ÿÿ444ÿ466ÿTyÿƒÚéÿ…Þîÿ…Þîÿ„Ýíÿu½Êÿ_˜ÿF]aø567ü577÷69:¹444l333(���������������������������������������������������������������������������������������������������������������689çd™£ÿ…Þîÿ…Þîÿ;DFÿ333ÿ333ÿ689ÿLfkÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ‚×çÿn¯»ÿW~‡þ?LOö333ÿ699é557ž222W666��������������������������������������������������������������������������������������������333<455ý€Òáÿ…Þîÿ…ÞîÿBSVÿ333ÿ333ÿ333ÿ69:ÿq´Àÿ…Þîÿ…Þîÿ…Þîÿ…Ýíÿ…Òçÿ…Øëÿ…Þîÿ…Þîÿ…Þîÿ}ÎÝÿgŸ©ÿPouû9?@ø344þ6::Ó333†444@+++����������������������������������������������������������������������������466˜Kdhù…Þîÿ…Þîÿ…Þîÿfž¨ÿ699ÿ333ÿ333ÿ444ÿUz€ÿ…Þîÿ…Þîÿ…Þîÿ…µÙÿ…¡Îÿ…¢Îÿ…®Ôÿ…ÀÞÿ…Ñèÿ…Ýîÿ…Þîÿ…Þîÿ„Ýíÿu¾Ëÿ_˜ÿH^bø567ü577÷589»333n333(������������������������������������������������������������333577ñi£®ÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿTx~ÿ445ÿ467ÿ344ÿOlqÿ…Þîÿ…Þîÿ…Þîÿ…¥Ðÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¦Ñÿ…¸Úÿ…Éãÿ…Ùìÿ…Þîÿ…Þîÿ…ÞîÿƒÙéÿn¯»ÿXˆÿ?MPö334ÿ689é222Q����������������������������������������������������333K589ú‚×çÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿn¯»ÿPpvÿ333ÿyÆÓÿ…Þîÿ…Þîÿ…Þîÿ…Åáÿ…¤Ïÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¢Ïÿ…°Öÿ…Áßÿ…Óèÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ}ÎÝÿ^Œ”ÿ577ô333������������������������������������������������588©Oouû…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ]Š’ÿDVYÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Üíÿ…Íåÿ…»Üÿ…©Óÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…­Ôÿ…×ëÿ…Þîÿ…Þîÿ…Þîÿj§±ÿ567ô�����������������������������������������������... 456øm­¹ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ@ORÿ`‘šÿ…Þîÿ…Þîÿ…Þîÿ~ÐÞÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Õêÿ…Ãàÿ…²Öÿ…¢Ïÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…Ãàÿ…Þîÿ…Þîÿ…ÞîÿMimú557Ÿ������������������������������������������������333Z8>?ù„Üëÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿwÁÎÿ444ÿ|ËÙÿ…Þîÿ…Þîÿb•žÿb–Ÿÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Üíÿ…Ëäÿ…ºÛÿ…§Ñÿ…¡Îÿ…¤Ðÿ…Òçÿ…Þîÿ…ÞîÿÕäÿ567ü222B������������������������������������������������5::ºU{ý…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ[‡ÿFZ^ÿ…Þîÿ…Þîÿo°¼ÿKejÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Õéÿ…Ùìÿ…Þîÿ…Þîÿ…Þîÿf§ÿ588ì�����������������������������������������������333455ür·Äÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ<DFÿ\‰‘ÿr¶ÂÿW†ÿDVZÿ}ÎÜÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿG^bø244Ž�����������������������������������������������$!À9@Aÿ„Ýíÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿt¹Æÿ333ÿ577ÿ333ÿ@ORÿvÀÍÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿp²¾ÿTw}ýPouûh¢¬ÿ„Ýíÿ…Þîÿ…Þîÿ}ÎÝÿ334þ2223��������������������������������������� ¤ø1* ÿ566ÿUzÿlªµÿÖåÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿu¼Éÿ=HIÿ=IKÿQqwÿ{ÊØÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿg «þ455ü356¤444“567õTy€ù…Þîÿ…Þîÿa“œÿ69:à����������������������������������� ®!ø<1ÿ]Lÿ`NÿUHÿB>)ÿ465ÿ566ÿJ\^ÿ]‹“ÿtºÇÿ„Üìÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿÖåÿ6:;ù222L��������666!466øu¼Éÿ…ÞîÿBSV÷333~���������������������������� S"óM?ÿhUÿjWÿmYÿo[ÿr]ÿ9.ÿSH<ÿ§”ÿGA;ÿOOMÿ555ÿ:>?ÿPlpÿe›¥ÿzÈÖÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿr·Ãÿ455û���a¡œ£b£€����7;<Ë_™ÿyÆÓÿ333ÿ222$������������������������ ”7-üo[ÿu`ÿxbÿzdÿ|fÿ~gÿzdÿ"ÿœˆtÿ²›„ÿ^ULÿÔĵÿË»­ÿ¢—ÿvojÿGHGÿ333ÿ@IJÿU{ÿl©´ÿÖåÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ{ÊØÿ333þRwwKa¡ÿ`¢žþb F69:éj¥°ÿ]Š’ÿ69;Ñ������������������������ S:0ü€iÿ‚jÿ…mÿ‡nÿ‰pÿ‹rÿŽtÿJ=ÿMD7ÿʱ—ÿMB7ÿ¼¬ŸÿÆ·©ÿzqhÿˆ}rÿ̼®ÿÔĵÿª“€ÿ|jZÿZRJÿ;;:ÿ455ÿIZ]ÿ]Š’ÿs¹Æÿ„Üìÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿQqwù<FF÷Qzv©`ž›íUƒå?MOöƒÛêÿ>JL÷333o�����������������������"âiÿuÿ’wÿ”yÿ–{ÿ˜|ÿ›ÿ€ÿ%ÿ~kÿÁª‘ÿNF?ÿ¶¨›ÿH9*ÿbI2ÿ‚sÿbUJÿͺ©ÿ®uÿ®uÿ®uÿ­uÿ“{fÿrbUÿOLGÿ344ÿ;??ÿLhlÿdš¤ÿzÈÖÿ…Þîÿ…ÞîÿÕäÿQsyÿ699û8>=ý]™—þn¶»ÿv¾Ëÿ344þ111�����������������������4+ ù€ÿŸ‚ÿ¡„ÿ£…ÿ¥‡ÿ¥‡ÿ¥‡ÿˆoÿ,'ÿ´‡ÿpbSÿ˜Š~ÿG9,ÿkM0ÿŒt]ÿýýýÿgWFÿ§zÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ@7.ÿ­¥ÿ/.,ü59:å333ÿ=GI÷V{ýl©´ÿÖåÿ„ÜìÿƒÛêÿh­¬ÿa¡ÿZ‚‰þ799Ä������������������������LN@ þ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿJ= ÿVL?ÿж›ÿ<3+ÿ|odÿZB*ÿlN1ÿ…lTÿÿÿÿÿ¢–‹ÿŠr\ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ@5+ÿãØÌÿ% ù���000 222e>MMß6;;þ344ýDX[ö]Š’ÿj­²ÿb¡žÿ<FGø444c������������������������ XUF ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ)#ÿ†udÿǯ•ÿOG?ÿG9+ÿlN1ÿlN1ÿx]Bÿÿÿÿÿ·­¢ÿxcPÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ=3*ÿèÝÑÿ!÷��� ��������b¡a œÿD^^Z333}6:;Ê=MMýZŒÿ=IIÕ$$$���������������������������5C6 ý¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¤†ÿ%ÿž‹wÿ–„pÿxmcÿH6%ÿlN1ÿlN1ÿqT8ÿÿÿÿÿ¤šÿt^ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ90'ÿéÝÒÿõ�����������a£ž:a¡ÿ`¢›R��������b¡Öa¡ÿ\™™2������������������������������� ("ñ¡„ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿvÿ*%ÿµž‡ÿdWJÿ”‡}ÿJ7#ÿlN1ÿlN1ÿz_Dÿÿÿÿÿuh[ÿ‚jÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ­uÿ6.&ÿÞÔËÿê����������������`¢œZa¡ÿa¢½b¡¢a¡òa¡ž“������������������������������������ •YIÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿvaÿ91&ÿŬ“ÿC90ÿ¯¡•ÿM8$ÿlN1ÿlN1ÿ—‚nÿêæâÿC6(ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ•{dÿg\Sÿµ­£ÿ Â����������������d¦›a¡÷a¡ÿa¡ÿ`ŸŸ������������������������������������������� " à€iÿ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿgUÿA8/ÿ̳™ÿ:1)ÿô¨ÿK8&ÿlN1ÿlN1ÿÐÆ½ÿ‡ykÿƒkXÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿm[Iÿ¬¢—ÿrjaÿ }��������������������cŸœMa Ê`£›E���������������������������������������������������%ó”yÿ¥‡ÿ¥‡ÿ¥‡ÿ\JÿLB8ÿж›ÿ>6.ÿÓôÿTG;ÿiL/ÿ•jÿ¦™ŒÿcYMÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ;1(ÿâ×Ëÿ% ü���)������������������������a¡ž a¡ød¢›)������������������������������������������������ H-%ú¡„ÿ¥‡ÿ¥‡ÿ]LÿQF<ÿϵ›ÿE=4ÿÔĵÿ¹«žÿF;0ÿB6(ÿzmcÿÐÀ±ÿ°“yÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿ¨‹qÿ>5-ÿÙÏÅÿí���������������������������a¢ž7a¡ÿ` Ÿ���������������������������������������������������� ?4ý¥‡ÿ¥‡ÿkXÿE<1ÿ˲˜ÿ=4+ÿÔĵÿÔĵÿÓôÿÐÀ±ÿÔĵÿÔĵÿ³—~ÿ®uÿ®uÿ®uÿ®uÿ®uÿ®uÿkZIÿ¤™Žÿ|tiÿ &"Áb ����������������������������a¡ž“b¢c�������������������������������������������������������  ·\K ÿ¥‡ÿ~hÿ<4'ÿĬ“ÿ=5,ÿÒ³ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ¶›ƒÿ®uÿ®uÿ®uÿ®uÿ®uÿ­uÿ1)"ÿäØËÿ!ÿT‹ˆÿa œÂa¡ž a œ^¡›.Uªª��������`¡œwa¡ÿa¡é����������������������������������������������������������� # Ü|eÿžÿ/)ÿ¸¡ŠÿNC8ÿų¤ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ¹Ÿˆÿ®uÿ®uÿ®uÿ®uÿ®uÿwcQÿ‡|rÿ›‘‡ÿÎa¡žWa¡žLa¡âa¡ÿa¡Ûa¡äa¡ÿb¡žîa¡âa¡ÿb¢<���������������������������������������������������������������$ ñ‘wÿ.(ÿ°šƒÿwhYÿ£“„ÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿ¾¦ÿ®uÿ®uÿ®uÿ®uÿ©‹qÿ4+#ÿâÖÊÿ!ü/���������ÿÿb£/`ŸŸ(a œta¢™b¡•`ŸŸ]¢¢�������������������������������������������������������������������� D+$úB6ÿ‰wfÿÀ¨ÿi^TÿÔĵÿÔĵÿÔĵÿÔĵÿÔĵÿí™ÿ®uÿ®uÿ®uÿ®uÿSE8ÿµ¨œÿqh_ÿ°�������������������������������������������������������������������������������������������������������������������� z2)ýC:+ÿĬ“ÿI?5ÿÌ»¬ÿÔĵÿÔĵÿÔĵÿÔĵÿɵ¢ÿ®uÿ®uÿ®uÿvaOÿrg\ÿŹ­ÿð�������������������������������������������������������������������������������������������������������������������������� °("ÿ­˜ƒÿ©”~ÿzm`ÿÔĵÿÔĵÿÔĵÿÔĵÿо®ÿ®uÿ®uÿ‚lWÿVLBÿØÎÂÿ& ú A������������������������������������������������������������������������������������������������������������������������������� á;4%ÿ̲˜ÿhZLÿ”†xÿÔĵÿÔĵÿÔĵÿÔĵÿ°“zÿkYIÿj^SÿÛÐÄÿ,%üs��������������������������������������������������������������������������������������������������������������������������������������� ÷I?6ÿϵ›ÿn`QÿZPGÿž’†ÿ­ ”ÿ‘†|ÿZPFÿ°£—ÿ¾±¥ÿ*$û w������������������������������������������������������������������������������������������������������������������������������������������� B#ê6/'ÿ¯™ƒÿɰ–ÿ‘lÿ€p`ÿ¯™ƒÿ®˜‚ÿH>3ÿ% ïT������������������������������������������������������������������������������������������������������������������������������������������������������ $¸$ù)#ÿ91)ÿ91)ÿ-'ÿ("ó#¡������������������������������������������������������������������������������������������������������������������������������������������������������������������*& q,$–*"‡ L���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ÿÿÿÿÿÿ��ÿÿÿÿÿÿ��ÿÿÿÿÿÿ��ÿÿÿÿÿ��ÿþ�ÿÿ��ÿþ�ÿÿ��ÿü��ÿÿ��ÿü��ÿ��ÿü���ÿ��ÿø�����ÿø�����ÿø�����ÿð�����ÿð�����ÿð�����ÿà�����ÿà�����ÿÀ�����ÿ������ü�����ø��� ��à��� ��à������À������À������À��À?��À��Ì?��À��î��À��ð��À��ñÿ��à��ûÿ��ð��ùÿ��ø��üÿ��ü��ýÿ��ü���|ÿ��þ��ÿ��ÿ��çÿ��ÿ€�ÿÿ��ÿÀ�ÿÿ��ÿÀ�?ÿÿ��ÿà�ÿÿ��ÿð�ÿÿÿ��ÿøÿÿÿ��ÿüÿÿÿ��ÿÿŸÿÿÿ��ÿÿÿÿÿÿ��ÿÿÿÿÿÿ��ÿÿÿÿÿÿ��(��� ���@���� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������999 111>555��������������������������������������������������������������������������������������������������������������������<DE´Tw~ïDVYê;CEâ;EE£222L+++ ������������������������������������������������������������������������������������������������333?LPê…Þîÿ„ÜìÿÒáÿp±½ÿW|ƒñ>LNé;DEÕ:@B„5555UUU��������������������������������������������������������������������������������8<<n`˜÷…ÞîÿG\_ÿ7<=ÿOntÿ„Üìÿ…Þîÿ~ÏÝÿg «þMjoë;DEê<EH¿68;h000 ��������������������������������������������������������������������<DFÖ|ÌÛÿ…Þîÿ>KMÿ333ÿ6:;ÿa’šÿ…Þîÿ…Þîÿ…Üíÿ…Ùìÿ…ÞîÿwÀÍÿa‘™øEX\ê:BCä;DF¤444O;;; ������������������������������������������������111CUXë…Þîÿ…ÞîÿZ„Œÿ7<<ÿ333ÿ@MOÿ…Þîÿ…Þîÿ…«Óÿ…¡Îÿ…­Ôÿ…¾Ýÿ…Ðçÿ…ÝíÿƒÙéÿp²½ÿW~„ñ>JLê;DEÕ9@B‡333-������������������������������������9?A‚f›¥û…Þîÿ…Þîÿ„Ýíÿa“œÿ=IKÿSv|ÿ…Þîÿ…Þîÿ…´Øÿ…¡Îÿ…¡Îÿ…¡Îÿ…¡Îÿ…¦Ðÿ…¶Ùÿ…Èãÿ…Ùëÿ…Þîÿ~ÐÞÿh¢­þ@NPì3332��������������������������������;CDà€Óâÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿKdhÿyÆÓÿ…Þîÿ…Þîÿ…Þîÿ…Öéÿ…Äàÿ…³×ÿ…£Ïÿ…¡Îÿ…¡Îÿ…¡Îÿ…¢Îÿ…Æâÿ…Þîÿ…ÞîÿPotí444;����������������������������333-I`dê…Þîÿ…Þîÿ…Þîÿ…ÞîÿÑßÿFZ]ÿ…Þîÿ„Üìÿe›¦ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Ûíÿ…Íåÿ…ºÛÿ…©Òÿ…¡Îÿ…ÁÞÿ…ÞîÿƒÚéÿ<DFæ�������������������������������:CD•j§²þ…Þîÿ…Þîÿ…Þîÿ…Þîÿd™£ÿ_™ÿÖæÿTw}ÿ|ÊÙÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Ùëÿ…Þîÿ…Þîÿlª¶þ;CE˜���������������������������� 8>>ú‚Ùèÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ=HJÿF[_ÿBSVÿp³¾ÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿr·Ãÿb”œûxÂÐÿ…Þîÿ…ÞîÿKdhë5550�������������������� (,$ Ï6,üBD2ÿI][ÿc— ÿ{ÈÖÿ…Þîÿ…ÞîÿV~…ÿTxÿv¾Ëÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿg ©÷:BCÂ555a;DEÛu¾ËþÖåÿ;DEà�������������������/% ºG9üaPÿiVÿmYÿ:2ÿiaXÿDIGÿHPQÿ[x}ÿl©´ÿ€Ôãÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ…Þîÿ@MPèc”Œa žn4446OotëgŸ©ü:AC…���������������8- álYÿzdÿ}fÿ€iÿvaÿJ@.ÿ¤{ÿ™‹ÿÏ¿±ÿ©ž’ÿ|ytÿMQOÿJVVÿ^‡ÿr·Äÿ„Üìÿ…Þîÿ…Þîÿ…Þîÿ…ÞîÿH`cêT‚•a¡÷Gfe‰\‡òFX\ê333#������������+!  {fÿsÿ‘wÿ”yÿ—|ÿK?ÿ”‚nÿ^QEÿ›ÿVB/ÿk\OÿÅ´¦ÿ°“xÿ¤ˆoÿ„q_ÿ_\UÿDHGÿPegÿc— þ{ÈÖÿ…Þîÿ{ÈÖÿBSUüD]]çYŒŠùyÇÔÿ;CDÙ����������������E8â ƒÿ¤†ÿ¥‡ÿ¥‡ÿ¥‡ÿ?6ÿ¸¡Šÿxl`ÿT?*ÿgNÿâÜÖÿo]Mÿ®uÿ®uÿ®uÿ®uÿ¤ˆnÿ–އÿ751ï;EGÊ<FHêRuzùk¨³ÿ€Òáÿc¥¢ÿc—û8=?v����������������L> ñ¥‡ÿ¥‡ÿ¥‡ÿ¥‡ÿuÿOE5ÿ„rbÿbSFÿkM0ÿw[@ÿÿÿÿÿ`N=ÿ®uÿ®uÿ®uÿ®uÿœiÿ­£šÿ1*"ß����KxxU…‚ÿ?JJŸ;CFÜPwvóF^^ì999����������������E8 㥇ÿ¥‡ÿ¥‡ÿ¥‡ÿdR ÿvgXÿ^QEÿXE5ÿlN1ÿoR6ÿøöôÿ^I8ÿ®uÿ®uÿ®uÿ®uÿˆp\ÿ¼°¦ÿ+$×��������a¡ž¥a ži`ŸŸa¡ÿa ›a��������������������) i ÿ¥‡ÿ¥‡ÿ¥‡ÿJ>ÿžŠvÿpcVÿXD1ÿlN1ÿƒjQÿÈ¿·ÿ‰pZÿ®uÿ®uÿ®uÿ®uÿgVFÿ¾²§ÿ ¦��������^¡¡`¡žùa¡žñ`ŸœH���������������������������9-Üš}ÿ¥‡ÿ¥‡ÿG<ÿ©”ÿxk_ÿ^M=ÿlN1ÿµ¥—ÿm^Oÿ­tÿ®uÿ®uÿ®uÿ®uÿ]O@ÿ˜…þ K������������b£Sa¡œ�ÿÿ���������������������������� ��B5 ñ£…ÿ¥‡ÿJ>ÿ¨”~ÿ€rfÿ©œÿP@.ÿn_Pÿµ£‘ÿ®uÿ®uÿ®uÿ®uÿ«sÿŠ€wÿWPFö���������������Uªªa¡ùb S�������������������������������� ATD ú¥‡ÿL?ÿ¬—ÿ{m`ÿÔĵÿÔĵÿÔĵÿ˸¦ÿ®uÿ®uÿ®uÿ®uÿt`Mÿº­¡ÿ12,ð^¡š&f™™������������`¡b¡Q������������������������������������)!}lX þYJÿž‹vÿugZÿÔĵÿÔĵÿÔĵÿμ«ÿ®uÿ®uÿ®uÿ­uÿgZQÿ|riýFni¬` a¡ÿ` —a¢Êb¡•a¡ÿ`¢ŸJ����������������������������������������2(´kX ÿi\KÿgXKÿÓ´ÿÔĵÿÔĵÿÓ³ÿ®uÿ®uÿ®uÿs_Mÿ»¯¢ÿ/&Û��������`ŸŸ ba¢ž~b£/]¢¢ �����������������������������������������������8,ÛTH+ÿ‰tÿ®ŽÿÔĵÿÔĵÿÔĵÿ²–}ÿ®uÿ•|dÿ‰|qÿZQHú%���������������������������������������������������������������������������� ��4*÷¤yÿufWÿÏ¿°ÿÔĵÿÔĵÿ¹Ÿˆÿ˜~fÿ}rfÿ‰~tý&������������������������������������������������������������������������������������# ;3, þ´œ†ÿl^Sÿ¦™Œÿ¶¨›ÿ†whÿ›€ÿ}shý0)!•��������������������������������������������������������������������������������������������j:1(õl_Pÿlÿ‰xfÿvgVÿI=2ò(!f���������������������������������������������������������������������������������������������������� -#“.'Í-&Ê)!{��� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ÿÿÿÿÿÿÿÿÿÁÿÿÿÀÿÿÀÿÿ€�?ÿ€�ÿ��ÿ��ÿ��þ��þ��ø��Cà��ãÀ��€��€��€�€�߀�?À�¿à�¿ð�ÿø��?ø�ÿü�?ÿþ�?ÿÿ�ÿÿÿÿÿÇÿÿÿÿÿÿÿÿÿÿ(������ ���� ��������������������������������������������999@@@����������������������������������������������������333Tx~Õa‘›ÝUy€·H_c‹::@0����������������������������������������CSV\yÅÓüEX[ÿZ„ŒÿƒÚéÿp³¾õZ†ŽÑQqw©DX]q777������������������������Qqw®…Þîÿ]‹“ÿH^bÿ…Þîÿ…¨Òÿ…«Óÿ…½ÜÿÆÜÿh «êX‰ÄJfjg������������... b“œÞ…ÞîÿƒÛêÿd˜¢ÿ|ÍÛÿ…Üíÿ…Ìåÿ…»Üÿ…©Óÿ…²Öÿ„ÜìÿMhm›������������<HCœ~ÏÝÿ…Þîÿfœ§ÿZ„‹ÿq´Àÿ…Þîÿ…Þîÿ…ÞîÿxÅÓþ„Ûêÿq³¾ô=LL6����;+APAâSJþSf]ÿfˆŒÿi ©ÿtºÇÿ…Þîÿ…Þîÿ…Þîÿo±¼ïJ[]hW~…ÂX†É����-#3lY ü‡nÿ}e ÿtfSÿŒ}nÿ‚wÿwxpÿn‚ÿg‘—ÿyÇÕþwÂÐ÷W„„Ú_‘—ÚIbg’����O@›¥‡ÿ¥‡ÿm[#ÿ~m]ÿmR9ÿ¬Ÿ’ÿ®uÿ®uÿ˜ˆwÿ?A=»RvzÇY‰Ë[Žì:BB#����D4u›€ ÿ¥‡ÿp_4ÿq_Lÿw[@ÿ ‘ƒÿ®uÿ®uÿ”„uÿ$b^¡¡a¡œ›a ›n�����������aNؤ†ÿve>ÿˆxhÿ|hTÿ•€lÿ®uÿ«Žsÿviý ������a œqm’’������������q\ïyg9ÿ¬œÿÔĵÿ¿§‘ÿ®uÿŒv`ÿwqfía ži`¢ŸJa¢žƒ`ŸŸ����������������:->k[,ý¢€ÿÔĵÿ«—ÿ¦Špÿ~qý;/&Wf™™^¢™Uªª ������������������������E<‰„s_þ³£•ÿ´¢‘ÿ’rÿdXK©��������������������������������������������E;/gYHék]LäMB6w������������������������������������������������������������������������������������������������ÿÿ��ø��ø��ð��ð���à��À ��€�����€7��€?��À7��à��à��ñÿ��ÿÿ��������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/qt/kid3.qrc����������������������������������������������������������������������0000664�0000000�0000000�00000001175�12246035113�0015703�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE RCC><RCC version="1.0"> <qresource> <file>images/application-exit.png</file> <file>images/preferences-system.png</file> <file>images/document-export.png</file> <file>images/document-import.png</file> <file>images/document-open.png</file> <file>images/document-open-recent.png</file> <file>images/document-revert.png</file> <file>images/document-save.png</file> <file>images/edit-select-all.png</file> <file>images/go-next.png</file> <file>images/go-previous.png</file> <file>images/help-contents.png</file> <file>images/view-media-playlist.png</file> </qresource> </RCC> ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/qt/kid3mainwindow.cpp������������������������������������������������������������0000664�0000000�0000000�00000070474�12246035113�0020005�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \file kid3mainwindow.cpp * Kid3 main window. * * \b Project: Kid3 * \author Urs Fleisch * \date 9 Jan 2003 * * Copyright (C) 2003-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "kid3mainwindow.h" #include <QMessageBox> #include <QCloseEvent> #include <QIcon> #include <QToolBar> #include <QApplication> #include <QMenuBar> #include <QMenu> #include <QAction> #include <QStyle> #include <QStatusBar> #include <QSessionManager> #include "config.h" #include "qtcompatmac.h" #include "recentfilesmenu.h" #include "shortcutsmodel.h" #include "mainwindowconfig.h" #include "kid3form.h" #include "kid3application.h" #include "configdialog.h" #include "guiconfig.h" #include "tagconfig.h" #include "fileconfig.h" #include "contexthelp.h" #include "serverimporter.h" #include "servertrackimporter.h" #include "platformtools.h" #include "loadtranslation.h" #include "fileproxymodel.h" /** * Constructor. * * @param parent parent widget */ Kid3MainWindow::Kid3MainWindow(QWidget* parent) : QMainWindow(parent), BaseMainWindow(this, m_platformTools = new PlatformTools), m_shortcutsModel(new ShortcutsModel(this)) { #if !defined Q_OS_WIN32 && defined CFG_DATAROOTDIR QString dataRootDir(QLatin1String(CFG_DATAROOTDIR)); Utils::prependApplicationDirPathIfRelative(dataRootDir); QPixmap icon; if (icon.load(dataRootDir + #ifndef Q_OS_MAC QLatin1String("/icons/hicolor/48x48/apps/kid3-qt.png") #else QLatin1String("/kid3.png") #endif )) { setWindowIcon(icon); } #endif readFontAndStyleOptions(); init(); connect(qApp, SIGNAL(commitDataRequest(QSessionManager&)), this, SLOT(onCommitDataRequest(QSessionManager&))); } /** * Destructor. */ Kid3MainWindow::~Kid3MainWindow() { delete m_platformTools; } /** Only defined for generation of translation files */ #define MAIN_TOOLBAR_FOR_PO QT_TRANSLATE_NOOP("@default", "Main Toolbar") /** * Init menu and toolbar actions. */ void Kid3MainWindow::initActions() { QToolBar* toolBar = new QToolBar(this); toolBar->setObjectName(QLatin1String("MainToolbar")); #if defined Q_OS_MAC && QT_VERSION >= 0x050000 toolBar->setStyleSheet(QLatin1String("QToolButton { border: 0; }")); #endif QMenuBar* menubar = menuBar(); QString menuTitle(tr("&File")); QMenu* fileMenu = menubar->addMenu(menuTitle); QAction* fileOpen = new QAction(this); fileOpen->setStatusTip(tr("Opens a directory")); fileOpen->setText(tr("&Open...")); fileOpen->setShortcut(QKeySequence::Open); fileOpen->setIcon(QCM_QIcon_fromTheme("document-open")); fileOpen->setObjectName(QLatin1String("file_open")); m_shortcutsModel->registerAction(fileOpen, menuTitle); connect(fileOpen, SIGNAL(triggered()), impl(), SLOT(slotFileOpen())); fileMenu->addAction(fileOpen); toolBar->addAction(fileOpen); m_fileOpenRecent = new RecentFilesMenu(fileMenu); connect(m_fileOpenRecent, SIGNAL(loadFile(QString)), this, SLOT(slotFileOpenRecentDirectory(QString))); m_fileOpenRecent->setStatusTip(tr("Opens a recently used directory")); m_fileOpenRecent->setTitle(tr("Open &Recent")); m_fileOpenRecent->setIcon(QCM_QIcon_fromTheme("document-open-recent")); fileMenu->addMenu(m_fileOpenRecent); QAction* fileOpenDirectory = new QAction(this); fileOpenDirectory->setStatusTip(tr("Opens a directory")); fileOpenDirectory->setText(tr("O&pen Directory...")); fileOpenDirectory->setShortcut(Qt::CTRL + Qt::Key_D); fileOpenDirectory->setIcon(QCM_QIcon_fromTheme("document-open")); fileOpenDirectory->setObjectName(QLatin1String("open_directory")); m_shortcutsModel->registerAction(fileOpenDirectory, menuTitle); connect(fileOpenDirectory, SIGNAL(triggered()), impl(), SLOT(slotFileOpenDirectory())); fileMenu->addAction(fileOpenDirectory); fileMenu->addSeparator(); QAction* fileSave = new QAction(this); fileSave->setStatusTip(tr("Saves the changed files")); fileSave->setText(tr("&Save")); fileSave->setShortcut(QKeySequence::Save); fileSave->setIcon(QCM_QIcon_fromTheme("document-save")); fileSave->setObjectName(QLatin1String("file_save")); m_shortcutsModel->registerAction(fileSave, menuTitle); connect(fileSave, SIGNAL(triggered()), impl(), SLOT(slotFileSave())); fileMenu->addAction(fileSave); toolBar->addAction(fileSave); QAction* fileRevert = new QAction(this); fileRevert->setStatusTip( tr("Reverts the changes of all or the selected files")); fileRevert->setText(tr("Re&vert")); fileRevert->setIcon(QCM_QIcon_fromTheme("document-revert")); fileRevert->setObjectName(QLatin1String("file_revert")); m_shortcutsModel->registerAction(fileRevert, menuTitle); connect(fileRevert, SIGNAL(triggered()), app(), SLOT(revertFileModifications())); fileMenu->addAction(fileRevert); toolBar->addAction(fileRevert); fileMenu->addSeparator(); QAction* fileImport = new QAction(this); fileImport->setData(-1); fileImport->setStatusTip(tr("Import from file or clipboard")); fileImport->setText(tr("&Import...")); fileImport->setIcon(QCM_QIcon_fromTheme("document-import")); fileImport->setObjectName(QLatin1String("import")); m_shortcutsModel->registerAction(fileImport, menuTitle); connect(fileImport, SIGNAL(triggered()), impl(), SLOT(slotImport())); fileMenu->addAction(fileImport); int importerIdx = 0; foreach (const ServerImporter* si, app()->getServerImporters()) { QString serverName(QCoreApplication::translate("@default", si->name())); QString actionName = QString::fromLatin1(si->name()).toLower().remove(QLatin1Char(' ')); int dotPos = actionName.indexOf(QLatin1Char('.')); if (dotPos != -1) actionName.truncate(dotPos); actionName = QLatin1String("import_") + actionName; QAction* fileImportServer = new QAction(this); fileImportServer->setData(importerIdx); fileImportServer->setStatusTip(tr("Import from %1").arg(serverName)); fileImportServer->setText(tr("Import from %1...").arg(serverName)); fileImportServer->setObjectName(actionName); m_shortcutsModel->registerAction(fileImportServer, menuTitle); connect(fileImportServer, SIGNAL(triggered()), impl(), SLOT(slotImport())); fileMenu->addAction(fileImportServer); ++importerIdx; } foreach (const ServerTrackImporter* si, app()->getServerTrackImporters()) { QString serverName(QCoreApplication::translate("@default", si->name())); QString actionName = QString::fromLatin1(si->name()).toLower().remove(QLatin1Char(' ')); int dotPos = actionName.indexOf(QLatin1Char('.')); if (dotPos != -1) actionName.truncate(dotPos); actionName = QLatin1String("import_") + actionName; QAction* fileImportServer = new QAction(this); fileImportServer->setData(importerIdx); fileImportServer->setStatusTip(tr("Import from %1").arg(serverName)); fileImportServer->setText(tr("Import from %1...").arg(serverName)); fileImportServer->setObjectName(actionName); m_shortcutsModel->registerAction(fileImportServer, menuTitle); connect(fileImportServer, SIGNAL(triggered()), impl(), SLOT(slotImport())); fileMenu->addAction(fileImportServer); ++importerIdx; } QAction* fileBatchImport = new QAction(this); fileBatchImport->setStatusTip(tr("Automatic import")); fileBatchImport->setText(tr("Automatic I&mport...")); fileBatchImport->setObjectName(QLatin1String("batch_import")); m_shortcutsModel->registerAction(fileBatchImport, menuTitle); connect(fileBatchImport, SIGNAL(triggered()), impl(), SLOT(slotBatchImport())); fileMenu->addAction(fileBatchImport); QAction* fileBrowseCoverArt = new QAction(this); fileBrowseCoverArt->setStatusTip(tr("Browse album cover artwork")); fileBrowseCoverArt->setText(tr("&Browse Cover Art...")); fileBrowseCoverArt->setObjectName(QLatin1String("browse_cover_art")); m_shortcutsModel->registerAction(fileBrowseCoverArt, menuTitle); connect(fileBrowseCoverArt, SIGNAL(triggered()), impl(), SLOT(slotBrowseCoverArt())); fileMenu->addAction(fileBrowseCoverArt); QAction* fileExport = new QAction(this); fileExport->setStatusTip(tr("Export to file or clipboard")); fileExport->setText(tr("&Export...")); fileExport->setIcon(QCM_QIcon_fromTheme("document-export")); fileExport->setObjectName(QLatin1String("export")); m_shortcutsModel->registerAction(fileExport, menuTitle); connect(fileExport, SIGNAL(triggered()), impl(), SLOT(slotExport())); fileMenu->addAction(fileExport); QAction* fileCreatePlaylist = new QAction(this); fileCreatePlaylist->setStatusTip(tr("Create M3U Playlist")); fileCreatePlaylist->setText(tr("&Create Playlist...")); fileCreatePlaylist->setIcon(QIcon(QLatin1String(":/images/view-media-playlist.png"))); fileCreatePlaylist->setObjectName(QLatin1String("create_playlist")); m_shortcutsModel->registerAction(fileCreatePlaylist, menuTitle); connect(fileCreatePlaylist, SIGNAL(triggered()), impl(), SLOT(slotPlaylistDialog())); fileMenu->addAction(fileCreatePlaylist); toolBar->addAction(fileCreatePlaylist); fileMenu->addSeparator(); QAction* fileQuit = new QAction(this); fileQuit->setStatusTip(tr("Quits the application")); fileQuit->setText(tr("&Quit")); fileQuit->setShortcut(Qt::CTRL + Qt::Key_Q); fileQuit->setIcon(QCM_QIcon_fromTheme("application-exit")); fileQuit->setObjectName(QLatin1String("file_quit")); m_shortcutsModel->registerAction(fileQuit, menuTitle); connect(fileQuit, SIGNAL(triggered()), impl(), SLOT(slotFileQuit())); fileMenu->addAction(fileQuit); menuTitle = tr("&Edit"); QMenu* editMenu = menubar->addMenu(menuTitle); QAction* editSelectAll = new QAction(this); editSelectAll->setStatusTip(tr("Select all files")); editSelectAll->setText(tr("Select &All")); editSelectAll->setShortcut(Qt::ALT + Qt::Key_A); editSelectAll->setIcon(QCM_QIcon_fromTheme("edit-select-all")); editSelectAll->setObjectName(QLatin1String("edit_select_all")); m_shortcutsModel->registerAction(editSelectAll, menuTitle); connect(editSelectAll, SIGNAL(triggered()), form(), SLOT(selectAllFiles())); editMenu->addAction(editSelectAll); QAction* editDeselect = new QAction(this); editDeselect->setStatusTip(tr("Deselect all files")); editDeselect->setText(tr("Dese&lect")); editDeselect->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_A); editDeselect->setObjectName(QLatin1String("edit_deselect")); m_shortcutsModel->registerAction(editDeselect, menuTitle); connect(editDeselect, SIGNAL(triggered()), form(), SLOT(deselectAllFiles())); editMenu->addAction(editDeselect); QAction* editSelectAllInDir = new QAction(this); editSelectAllInDir->setStatusTip(tr("Select all files in the current directory")); editSelectAllInDir->setText(tr("Select All in &Directory")); editSelectAllInDir->setObjectName(QLatin1String("select_all_in_directory")); m_shortcutsModel->registerAction(editSelectAllInDir, menuTitle); connect(editSelectAllInDir, SIGNAL(triggered()), form(), SLOT(selectAllInDirectory())); editMenu->addAction(editSelectAllInDir); QAction* editPreviousFile = new QAction(this); editPreviousFile->setStatusTip(tr("Select previous file")); editPreviousFile->setText(tr("&Previous File")); editPreviousFile->setShortcut(Qt::ALT + Qt::Key_Up); editPreviousFile->setIcon(QCM_QIcon_fromTheme("go-previous")); editPreviousFile->setObjectName(QLatin1String("previous_file")); m_shortcutsModel->registerAction(editPreviousFile, menuTitle); connect(editPreviousFile, SIGNAL(triggered()), form(), SLOT(previousFile())); editMenu->addAction(editPreviousFile); toolBar->addAction(editPreviousFile); QAction* editNextFile = new QAction(this); editNextFile->setStatusTip(tr("Select next file")); editNextFile->setText(tr("&Next File")); editNextFile->setShortcut(Qt::ALT + Qt::Key_Down); editNextFile->setIcon(QCM_QIcon_fromTheme("go-next")); editNextFile->setObjectName(QLatin1String("next_file")); m_shortcutsModel->registerAction(editNextFile, menuTitle); connect(editNextFile, SIGNAL(triggered()), form(), SLOT(nextFile())); editMenu->addAction(editNextFile); toolBar->addAction(editNextFile); menuTitle = tr("&Tools"); QMenu* toolsMenu = menubar->addMenu(menuTitle); QAction* toolsApplyFilenameFormat = new QAction(this); toolsApplyFilenameFormat->setStatusTip(tr("Apply Filename Format")); toolsApplyFilenameFormat->setText(tr("Apply &Filename Format")); toolsApplyFilenameFormat->setObjectName(QLatin1String("apply_filename_format")); m_shortcutsModel->registerAction(toolsApplyFilenameFormat, menuTitle); connect(toolsApplyFilenameFormat, SIGNAL(triggered()), app(), SLOT(applyFilenameFormat())); toolsMenu->addAction(toolsApplyFilenameFormat); QAction* toolsApplyId3Format = new QAction(this); toolsApplyId3Format->setStatusTip(tr("Apply Tag Format")); toolsApplyId3Format->setText(tr("Apply &Tag Format")); toolsApplyId3Format->setObjectName(QLatin1String("apply_id3_format")); m_shortcutsModel->registerAction(toolsApplyId3Format, menuTitle); connect(toolsApplyId3Format, SIGNAL(triggered()), app(), SLOT(applyId3Format())); toolsMenu->addAction(toolsApplyId3Format); QAction* toolsApplyTextEncoding = new QAction(this); toolsApplyTextEncoding->setStatusTip(tr("Apply Text Encoding")); toolsApplyTextEncoding->setText(tr("Apply Text &Encoding")); toolsApplyTextEncoding->setObjectName(QLatin1String("apply_text_encoding")); m_shortcutsModel->registerAction(toolsApplyTextEncoding, menuTitle); connect(toolsApplyTextEncoding, SIGNAL(triggered()), app(), SLOT(applyTextEncoding())); toolsMenu->addAction(toolsApplyTextEncoding); QAction* toolsRenameDirectory = new QAction(this); toolsRenameDirectory->setStatusTip(tr("Rename Directory")); toolsRenameDirectory->setText(tr("&Rename Directory...")); toolsRenameDirectory->setObjectName(QLatin1String("rename_directory")); m_shortcutsModel->registerAction(toolsRenameDirectory, menuTitle); connect(toolsRenameDirectory, SIGNAL(triggered()), impl(), SLOT(slotRenameDirectory())); toolsMenu->addAction(toolsRenameDirectory); QAction* toolsNumberTracks = new QAction(this); toolsNumberTracks->setStatusTip(tr("Number Tracks")); toolsNumberTracks->setText(tr("&Number Tracks...")); toolsNumberTracks->setObjectName(QLatin1String("number_tracks")); m_shortcutsModel->registerAction(toolsNumberTracks, menuTitle); connect(toolsNumberTracks, SIGNAL(triggered()), impl(), SLOT(slotNumberTracks())); toolsMenu->addAction(toolsNumberTracks); QAction* toolsFilter = new QAction(this); toolsFilter->setStatusTip(tr("Filter")); toolsFilter->setText(tr("F&ilter...")); toolsFilter->setObjectName(QLatin1String("filter")); m_shortcutsModel->registerAction(toolsFilter, menuTitle); connect(toolsFilter, SIGNAL(triggered()), impl(), SLOT(slotFilter())); toolsMenu->addAction(toolsFilter); const TagConfig& tagCfg = TagConfig::instance(); if (tagCfg.taggedFileFeatures() & TaggedFile::TF_ID3v24) { QAction* toolsConvertToId3v24 = new QAction(this); toolsConvertToId3v24->setStatusTip(tr("Convert ID3v2.3 to ID3v2.4")); toolsConvertToId3v24->setText(tr("Convert ID3v2.3 to ID3v2.&4")); toolsConvertToId3v24->setObjectName(QLatin1String("convert_to_id3v24")); m_shortcutsModel->registerAction(toolsConvertToId3v24, menuTitle); connect(toolsConvertToId3v24, SIGNAL(triggered()), app(), SLOT(convertToId3v24())); toolsMenu->addAction(toolsConvertToId3v24); if (tagCfg.taggedFileFeatures() & TaggedFile::TF_ID3v23) { QAction* toolsConvertToId3v23 = new QAction(this); toolsConvertToId3v23->setStatusTip(tr("Convert ID3v2.4 to ID3v2.3")); toolsConvertToId3v23->setText(tr("Convert ID3v2.4 to ID3v2.&3")); toolsConvertToId3v23->setObjectName(QLatin1String("convert_to_id3v23")); m_shortcutsModel->registerAction(toolsConvertToId3v23, menuTitle); connect(toolsConvertToId3v23, SIGNAL(triggered()), app(), SLOT(convertToId3v23())); toolsMenu->addAction(toolsConvertToId3v23); } } #if defined HAVE_PHONON || QT_VERSION >= 0x050000 QAction* toolsPlay = new QAction(this); toolsPlay->setStatusTip(tr("Play")); toolsPlay->setText(tr("&Play")); toolsPlay->setIcon(QIcon(style()->standardIcon(QStyle::SP_MediaPlay))); toolsPlay->setObjectName(QLatin1String("play")); m_shortcutsModel->registerAction(toolsPlay, menuTitle); connect(toolsPlay, SIGNAL(triggered()), app(), SLOT(playAudio())); toolsMenu->addAction(toolsPlay); toolBar->addAction(toolsPlay); #endif menuTitle = tr("&Settings"); QMenu* settingsMenu = menubar->addMenu(menuTitle); m_viewToolBar = toolBar->toggleViewAction(); if (m_viewToolBar) { m_viewToolBar->setStatusTip(tr("Enables/disables the toolbar")); m_viewToolBar->setText(tr("Show &Toolbar")); m_viewToolBar->setObjectName(QLatin1String("options_configure_toolbars")); m_shortcutsModel->registerAction(m_viewToolBar, menuTitle); } if (MainWindowConfig::instance().m_hideToolBar) toolBar->hide(); m_viewToolBar->setChecked(!MainWindowConfig::instance().m_hideToolBar); settingsMenu->addAction(m_viewToolBar); m_viewStatusBar = new QAction(this); m_viewStatusBar->setStatusTip(tr("Enables/disables the statusbar")); m_viewStatusBar->setText(tr("Show St&atusbar")); m_viewStatusBar->setCheckable(true); m_viewStatusBar->setObjectName(QLatin1String("options_show_statusbar")); m_shortcutsModel->registerAction(m_viewStatusBar, menuTitle); connect(m_viewStatusBar, SIGNAL(triggered()), this, SLOT(slotViewStatusBar())); settingsMenu->addAction(m_viewStatusBar); m_settingsShowHidePicture = new QAction(this); m_settingsShowHidePicture->setStatusTip(tr("Show Picture")); m_settingsShowHidePicture->setText(tr("Show &Picture")); m_settingsShowHidePicture->setCheckable(true); m_settingsShowHidePicture->setObjectName(QLatin1String("hide_picture")); m_shortcutsModel->registerAction(m_settingsShowHidePicture, menuTitle); connect(m_settingsShowHidePicture, SIGNAL(triggered()), impl(), SLOT(slotSettingsShowHidePicture())); settingsMenu->addAction(m_settingsShowHidePicture); m_settingsAutoHideTags = new QAction(this); m_settingsAutoHideTags->setStatusTip(tr("Auto Hide Tags")); m_settingsAutoHideTags->setText(tr("Auto &Hide Tags")); m_settingsAutoHideTags->setCheckable(true); m_settingsAutoHideTags->setObjectName(QLatin1String("auto_hide_tags")); m_shortcutsModel->registerAction(m_settingsAutoHideTags, menuTitle); connect(m_settingsAutoHideTags, SIGNAL(triggered()), impl(), SLOT(slotSettingsAutoHideTags())); settingsMenu->addAction(m_settingsAutoHideTags); QAction* settingsConfigure = new QAction(this); settingsConfigure->setStatusTip(tr("Configure Kid3")); settingsConfigure->setText(tr("&Configure Kid3...")); settingsConfigure->setIcon(QCM_QIcon_fromTheme("preferences-system")); #if QT_VERSION >= 0x040600 settingsConfigure->setShortcut(QKeySequence::Preferences); #endif settingsConfigure->setObjectName(QLatin1String("options_configure")); m_shortcutsModel->registerAction(settingsConfigure, menuTitle); connect(settingsConfigure, SIGNAL(triggered()), this, SLOT(slotSettingsConfigure())); settingsMenu->addSeparator(); settingsMenu->addAction(settingsConfigure); toolBar->addAction(settingsConfigure); menuTitle = tr("&Help"); QMenu* helpMenu = menubar->addMenu(menuTitle); QAction* helpHandbook = new QAction(this); helpHandbook->setStatusTip(tr("Kid3 Handbook")); helpHandbook->setText(tr("Kid3 &Handbook")); helpHandbook->setIcon(QCM_QIcon_fromTheme("help-contents")); helpHandbook->setShortcut(QKeySequence::HelpContents); helpHandbook->setObjectName(QLatin1String("help_contents")); m_shortcutsModel->registerAction(helpHandbook, menuTitle); connect(helpHandbook, SIGNAL(triggered()), this, SLOT(slotHelpHandbook())); helpMenu->addAction(helpHandbook); QAction* helpAbout = new QAction(this); helpAbout->setStatusTip(tr("About Kid3")); helpAbout->setText(tr("&About Kid3")); helpAbout->setObjectName(QLatin1String("help_about_app")); m_shortcutsModel->registerAction(helpAbout, menuTitle); connect(helpAbout, SIGNAL(triggered()), this, SLOT(slotHelpAbout())); helpMenu->addAction(helpAbout); QAction* helpAboutQt = new QAction(this); helpAboutQt->setStatusTip(tr("About Qt")); helpAboutQt->setText(tr("About &Qt")); helpAboutQt->setObjectName(QLatin1String("help_about_qt")); m_shortcutsModel->registerAction(helpAboutQt, menuTitle); connect(helpAboutQt, SIGNAL(triggered()), this, SLOT(slotHelpAboutQt())); helpMenu->addAction(helpAboutQt); addToolBar(toolBar); updateWindowCaption(); initFormActions(); } /** * Init actions of form. */ void Kid3MainWindow::initFormActions() { QString ctx(tr("Filename")); initAction(tr("From Tag 1"), QLatin1String("filename_from_v1"), form(), SLOT(fnFromID3V1()), ctx); initAction(tr("From Tag 2"), QLatin1String("filename_from_v2"), form(), SLOT(fnFromID3V2()), ctx); initAction(tr("Focus"), QLatin1String("filename_focus"), form(), SLOT(setFocusFilename()), ctx); ctx = tr("Tag 1"); initAction(tr("From Filename"), QLatin1String("v1_from_filename"), app(), SLOT(getTagsFromFilenameV1()), ctx); initAction(tr("From Tag 2"), QLatin1String("v1_from_v2"), app(), SLOT(copyV2ToV1()), ctx); initAction(tr("Copy"), QLatin1String("v1_copy"), app(), SLOT(copyTagsV1()), ctx); initAction(tr("Paste"), QLatin1String("v1_paste"), app(), SLOT(pasteTagsV1()), ctx); initAction(tr("Remove"), QLatin1String("v1_remove"), app(), SLOT(removeTagsV1()), ctx); initAction(tr("Focus"), QLatin1String("v1_focus"), form(), SLOT(setFocusV1()), ctx); ctx = tr("Tag 2"); initAction(tr("From Filename"), QLatin1String("v2_from_filename"), app(), SLOT(getTagsFromFilenameV2()), ctx); initAction(tr("From Tag 1"), QLatin1String("v2_from_v1"), app(), SLOT(copyV1ToV2()), ctx); initAction(tr("Copy"), QLatin1String("v2_copy"), app(), SLOT(copyTagsV2()), ctx); initAction(tr("Paste"), QLatin1String("v2_paste"), app(), SLOT(pasteTagsV2()), ctx); initAction(tr("Remove"), QLatin1String("v2_remove"), app(), SLOT(removeTagsV2()), ctx); initAction(tr("Edit"), QLatin1String("frames_edit"), form(), SLOT(editFrame()), ctx); initAction(tr("Add"), QLatin1String("frames_add"), form(), SLOT(addFrame()), ctx); initAction(tr("Delete"), QLatin1String("frames_delete"), form(), SLOT(deleteFrame()), ctx); initAction(tr("Focus"), QLatin1String("v2_focus"), form(), SLOT(setFocusV2()), ctx); ctx = tr("File List"); initAction(tr("Focus"), QLatin1String("filelist_focus"), form(), SLOT(setFocusFileList()), ctx); ctx = tr("Directory List"); initAction(tr("Focus"), QLatin1String("dirlist_focus"), form(), SLOT(setFocusDirList()), ctx); } /** * Init action of form. */ void Kid3MainWindow::initAction(const QString& text, const QString& name, const QObject* receiver, const char* slot, const QString& context) { QAction* action = new QAction(form()); action->setStatusTip(text); action->setText(text); action->setObjectName(name); m_shortcutsModel->registerAction(action, context); connect(action, SIGNAL(triggered()), receiver, slot); addAction(action); } /** * Add directory to recent files list. * * @param dirName path to directory */ void Kid3MainWindow::addDirectoryToRecentFiles(const QString& dirName) { m_fileOpenRecent->addDirectory(dirName); } /** * Read settings from the configuration. */ void Kid3MainWindow::readConfig() { const MainWindowConfig& mainWindowConfig = MainWindowConfig::instance(); if (mainWindowConfig.m_hideStatusBar) statusBar()->hide(); m_viewStatusBar->setChecked(!mainWindowConfig.m_hideStatusBar); m_settingsShowHidePicture->setChecked(!GuiConfig::instance().m_hidePicture); m_settingsAutoHideTags->setChecked(GuiConfig::instance().m_autoHideTags); m_fileOpenRecent->loadEntries(app()->getSettings()); m_shortcutsModel->readFromConfig(app()->getSettings()); if (!mainWindowConfig.m_geometry.isEmpty()) { restoreGeometry(mainWindowConfig.m_geometry); } else { resize(1000, 900); } if (!mainWindowConfig.m_windowState.isEmpty()) { restoreState(mainWindowConfig.m_windowState); } } /** * Store geometry and recent files in settings. */ void Kid3MainWindow::saveConfig() { MainWindowConfig& mainWindowConfig = MainWindowConfig::instance(); m_fileOpenRecent->saveEntries(app()->getSettings()); m_shortcutsModel->writeToConfig(app()->getSettings()); mainWindowConfig.m_hideToolBar = !m_viewToolBar->isChecked(); mainWindowConfig.m_geometry = saveGeometry(); mainWindowConfig.m_windowState = saveState(); mainWindowConfig.writeToConfig(app()->getSettings()); } /** * Set main window caption. * * @param caption caption without application name * @param modified true if any file is modified */ void Kid3MainWindow::setWindowCaption(const QString& caption, bool modified) { QString cap(caption); if (modified) { cap += tr(" [modified]"); } if (!cap.isEmpty()) { cap += QLatin1String(" - "); } cap += QLatin1String("Kid3"); setWindowTitle(cap); } /** * Get action for Settings/Auto Hide Tags. * @return action. */ QAction* Kid3MainWindow::autoHideTagsAction() { return m_settingsAutoHideTags; } /** * Get action for Settings/Hide Picture. * @return action. */ QAction* Kid3MainWindow::showHidePictureAction() { return m_settingsShowHidePicture; } /** * Window is closed. * * @param ce close event */ void Kid3MainWindow::closeEvent(QCloseEvent* ce) { if (queryBeforeClosing()) { ce->accept(); } else { ce->ignore(); } } /** * Read font and style options. */ void Kid3MainWindow::readFontAndStyleOptions() { const MainWindowConfig& mainWindowConfig = MainWindowConfig::instance(); if (mainWindowConfig.m_useFont && !mainWindowConfig.m_fontFamily.isEmpty() && mainWindowConfig.m_fontSize > 0) { QApplication::setFont(QFont(mainWindowConfig.m_fontFamily, mainWindowConfig.m_fontSize)); } if (!mainWindowConfig.m_style.isEmpty()) { QApplication::setStyle(mainWindowConfig.m_style); } } /** * Open recent directory. * * @param dir directory to open */ void Kid3MainWindow::slotFileOpenRecentDirectory(const QString& dir) { openRecentDirectory(dir); } /** * Turn status bar on or off. */ void Kid3MainWindow::slotViewStatusBar() { MainWindowConfig::instance().m_hideStatusBar = !m_viewStatusBar->isChecked(); slotStatusMsg(tr("Toggle the statusbar...")); if (MainWindowConfig::instance().m_hideStatusBar) { statusBar()->hide(); } else { statusBar()->show(); } slotStatusMsg(tr("Ready.")); } /** * Display handbook. */ void Kid3MainWindow::slotHelpHandbook() { ContextHelp::displayHelp(); } /** * Display "About" dialog. */ void Kid3MainWindow::slotHelpAbout() { QMessageBox::about( this, QLatin1String("Kid3"), QLatin1String("Kid3 " VERSION "\n(c) 2003-" RELEASE_YEAR " Urs Fleisch\nufleisch@users.sourceforge.net")); } /** * Display "About Qt" dialog. */ void Kid3MainWindow::slotHelpAboutQt() { QMessageBox::aboutQt(this, QLatin1String("Kid3")); } /** * Preferences. */ void Kid3MainWindow::slotSettingsConfigure() { QString caption(tr("Configure - Kid3")); ConfigDialog* dialog = new ConfigDialog(this, caption, m_shortcutsModel); dialog->setConfig(); if (dialog->exec() == QDialog::Accepted) { dialog->getConfig(); impl()->applyChangedConfiguration(); } } /** * Called when session manager wants application to commit its data. * @param manager session manager */ void Kid3MainWindow::onCommitDataRequest(QSessionManager& manager) { // Make sure that current file is saved even if "load last opened file" // is not enabled. FileConfig& fileCfg = FileConfig::instance(); if (!fileCfg.m_loadLastOpenedFile) { fileCfg.m_lastOpenedFile = app()->getFileProxyModel()->filePath(app()->currentOrRootIndex()); } // Ask user if there are unsaved data. if (manager.allowsInteraction()) { if (queryBeforeClosing()) { manager.release(); } else { manager.cancel(); } } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/qt/kid3mainwindow.h��������������������������������������������������������������0000664�0000000�0000000�00000007052�12246035113�0017442�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \file kid3mainwindow.h * Kid3 main window. * * \b Project: Kid3 * \author Urs Fleisch * \date 9 Jan 2003 * * Copyright (C) 2003-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #ifndef KID3MAINWINDOW_H #define KID3MAINWINDOW_H #include <QMainWindow> #include "basemainwindow.h" class QAction; class QSessionManager; class RecentFilesMenu; class ShortcutsModel; class IPlatformTools; /** * Kid3 main window. */ class Kid3MainWindow : public QMainWindow, public BaseMainWindow { Q_OBJECT public: /** * Constructor. * * @param parent parent widget */ explicit Kid3MainWindow(QWidget* parent = 0); /** * Destructor. */ virtual ~Kid3MainWindow(); /** * Init menu and toolbar actions. */ virtual void initActions(); /** * Add directory to recent files list. * * @param dirName path to directory */ virtual void addDirectoryToRecentFiles(const QString& dirName); /** * Read settings from the configuration. */ virtual void readConfig(); /** * Store geometry and recent files in settings. */ virtual void saveConfig(); /** * Get action for Settings/Auto Hide Tags. * @return action. */ virtual QAction* autoHideTagsAction(); /** * Get action for Settings/Hide Picture. * @return action. */ virtual QAction* showHidePictureAction(); /** * Set main window caption. * * @param caption caption without application name * @param modified true if any file is modified */ virtual void setWindowCaption(const QString& caption, bool modified); protected: /** * Window is closed. * * @param ce close event */ virtual void closeEvent(QCloseEvent* ce); private slots: /** * Open recent directory. * * @param dir directory to open */ void slotFileOpenRecentDirectory(const QString& dir); /** * Turn status bar on or off. */ void slotViewStatusBar(); /** * Preferences. */ void slotSettingsConfigure(); /** * Display handbook. */ void slotHelpHandbook(); /** * Display "About" dialog. */ void slotHelpAbout(); /** * Display "About Qt" dialog. */ void slotHelpAboutQt(); /** * Called when session manager wants application to commit its data. * @param manager session manager */ void onCommitDataRequest(QSessionManager& manager); private: /** * Read font and style options. */ void readFontAndStyleOptions(); /** * Init actions of form. */ void initFormActions(); /** * Init action of form. */ void initAction(const QString& text, const QString& name, const QObject* receiver, const char* slot, const QString& context); IPlatformTools* m_platformTools; RecentFilesMenu* m_fileOpenRecent; ShortcutsModel* m_shortcutsModel; QAction* m_viewToolBar; QAction* m_viewStatusBar; QAction* m_settingsAutoHideTags; QAction* m_settingsShowHidePicture; }; #endif /* KID3MAINWINDOW_H */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/qt/kid3win.rc��������������������������������������������������������������������0000664�0000000�0000000�00000000074�12246035113�0016235�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������IDI_ICON1 ICON DISCARDABLE "kid3.ico" ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/qt/mainqt.cpp��������������������������������������������������������������������0000664�0000000�0000000�00000005302�12246035113�0016333�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \file mainqt.cpp * Main program. * * \b Project: Kid3 * \author Urs Fleisch * \date 9 Jan 2003 * * Copyright (C) 2003-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <QFile> #include <QApplication> #include <QLibraryInfo> #include <QLocale> #include <QTranslator> #include <QDir> #include "fileconfig.h" #include "loadtranslation.h" #include "kid3mainwindow.h" /** * QApplication subclass with adapted session management. */ class Kid3QtApplication : public QApplication { public: /** * Constructor. * @param argc number of arguments (including command) * @param argv arguments */ Kid3QtApplication(int& argc, char** argv) : QApplication(argc, argv) {} /** * Called when session manager wants application to commit all its data. * * This method is reimplemented to avoid closing all top level widgets and * make restoring with the KDE window manager working. * * @param manager session manager */ virtual void commitData(QSessionManager& manager) { emit commitDataRequest(manager); } }; /** * Main program. * * @param argc number of arguments including command name * @param argv arguments, argv[0] is command name * * @return exit code of application. */ int main(int argc, char* argv[]) { Q_INIT_RESOURCE(kid3); Kid3QtApplication app(argc, argv); app.setApplicationName(QLatin1String("Kid3")); Utils::loadTranslation(); #ifdef Q_OS_MAC QDir dir(QApplication::applicationDirPath()); dir.cdUp(); dir.cd(QLatin1String("PlugIns")); QApplication::setLibraryPaths(QStringList(dir.absolutePath())); #endif Kid3MainWindow* kid3 = new Kid3MainWindow; kid3->setAttribute(Qt::WA_DeleteOnClose); kid3->show(); if (argc > 1) { kid3->confirmedOpenDirectory(QApplication::arguments().mid(1)); } else if ((FileConfig::instance().m_loadLastOpenedFile || app.isSessionRestored()) && !FileConfig::instance().m_lastOpenedFile.isEmpty()) { kid3->confirmedOpenDirectory(QStringList() << FileConfig::instance().m_lastOpenedFile); } return app.exec(); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/qt/mainwindowconfig.cpp����������������������������������������������������������0000664�0000000�0000000�00000006067�12246035113�0020415�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \file mainwindowconfig.cpp * Main window configuration. * * \b Project: Kid3 * \author Urs Fleisch * \date 08 Apr 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "mainwindowconfig.h" int MainWindowConfig::s_index = -1; /** * Constructor. */ MainWindowConfig::MainWindowConfig() : StoredConfig<MainWindowConfig>(QLatin1String("MainWindow")), m_hideToolBar(false), m_hideStatusBar(false), m_useFont(false), m_fontSize(-1), m_dontUseNativeDialogs( #if defined Q_OS_WIN32 || defined Q_OS_MAC false #else true #endif ) { } /** * Destructor. */ MainWindowConfig::~MainWindowConfig() { } /** * Persist configuration. * * @param config configuration */ void MainWindowConfig::writeToConfig(ISettings* config) const { config->beginGroup(m_group); config->setValue(QLatin1String("HideToolBar"), QVariant(m_hideToolBar)); config->setValue(QLatin1String("HideStatusBar"), QVariant(m_hideStatusBar)); config->setValue(QLatin1String("Geometry"), m_geometry); config->setValue(QLatin1String("WindowState"), m_windowState); config->setValue(QLatin1String("UseFont"), QVariant(m_useFont)); config->setValue(QLatin1String("FontFamily"), QVariant(m_fontFamily)); config->setValue(QLatin1String("FontSize"), QVariant(m_fontSize)); config->setValue(QLatin1String("Style"), QVariant(m_style)); config->setValue(QLatin1String("DontUseNativeDialogs"), QVariant(m_dontUseNativeDialogs)); config->endGroup(); } /** * Read persisted configuration. * * @param config configuration */ void MainWindowConfig::readFromConfig(ISettings* config) { config->beginGroup(m_group); m_hideToolBar = config->value(QLatin1String("HideToolBar"), m_hideToolBar).toBool(); m_hideStatusBar = config->value(QLatin1String("HideStatusBar"), m_hideStatusBar).toBool(); m_geometry = config->value(QLatin1String("Geometry"), m_geometry).toByteArray(); m_windowState = config->value(QLatin1String("WindowState"), m_windowState).toByteArray(); m_useFont = config->value(QLatin1String("UseFont"), m_useFont).toBool(); m_fontFamily = config->value(QLatin1String("FontFamily"), m_fontFamily).toString(); m_fontSize = config->value(QLatin1String("FontSize"), -1).toInt(); m_style = config->value(QLatin1String("Style"), m_style).toString(); m_dontUseNativeDialogs = config->value(QLatin1String("DontUseNativeDialogs"), m_dontUseNativeDialogs).toBool(); config->endGroup(); } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/qt/mainwindowconfig.h������������������������������������������������������������0000664�0000000�0000000�00000003777�12246035113�0020067�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \file mainwindowconfig.h * Main window configuration. * * \b Project: Kid3 * \author Urs Fleisch * \date 08 Apr 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #ifndef MAINWINDOWCONFIG_H #define MAINWINDOWCONFIG_H #include "generalconfig.h" /** * Main window configuration. */ class MainWindowConfig : public StoredConfig<MainWindowConfig> { public: /** * Constructor. */ MainWindowConfig(); /** * Destructor. */ virtual ~MainWindowConfig(); /** * Persist configuration. * * @param config configuration */ virtual void writeToConfig(ISettings* config) const; /** * Read persisted configuration. * * @param config configuration */ virtual void readFromConfig(ISettings* config); /** true to hide toolbar */ bool m_hideToolBar; /** true to hide statusbar */ bool m_hideStatusBar; /** mainwindow geometry */ QByteArray m_geometry; /** mainwindow state */ QByteArray m_windowState; /** true if custom application font is used */ bool m_useFont; /** custom application font family */ QString m_fontFamily; /** custom application font size */ int m_fontSize; /** custom application style, empty if not used */ QString m_style; /** Don't use the native file dialog if true */ bool m_dontUseNativeDialogs; /** Index in configuration storage */ static int s_index; }; #endif �kid3-3.0.2/src/app/qt/messagedialog.cpp�������������������������������������������������������������0000664�0000000�0000000�00000012555�12246035113�0017656�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \file messagedialog.cpp * Message dialog. * * \b Project: Kid3 * \author Urs Fleisch * \date 18 Aug 2011 * * Copyright (C) 2011-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "messagedialog.h" #include <QLabel> #include <QTextEdit> #include <QDialogButtonBox> #include <QVBoxLayout> #include <QStyle> #include <QIcon> #include <QPushButton> /** * Constructor. * * @param parent parent widget */ MessageDialog::MessageDialog(QWidget* parent) : QDialog(parent) { setObjectName(QLatin1String("MessageDialog")); QVBoxLayout* vlayout = new QVBoxLayout(this); QHBoxLayout* hlayout = new QHBoxLayout; m_iconLabel = new QLabel; m_iconLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); hlayout->addWidget(m_iconLabel); m_textLabel = new QLabel; m_textLabel->setWordWrap(true); m_textLabel->setMinimumSize(50, 50); hlayout->addWidget(m_textLabel); vlayout->addLayout(hlayout); m_textEdit = new QTextEdit; m_textEdit->setFocusPolicy(Qt::NoFocus); m_textEdit->setReadOnly(true); m_textEdit->hide(); vlayout->addWidget(m_textEdit); m_buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok); m_buttonBox->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); connect(m_buttonBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT(buttonClicked(QAbstractButton*))); vlayout->addWidget(m_buttonBox); } /** * Destructor. */ MessageDialog::~MessageDialog() {} /** * Set the text to be displayed. * * @param text message text. */ void MessageDialog::setText(const QString &text) { m_textLabel->setText(text); } /** * Set the informative text. * This text can be large and is displayed in a text edit. * * @param text message text. */ void MessageDialog::setInformativeText(const QString& text) { m_textEdit->setText(text); QStringList lines = text.split(QLatin1Char('\n')); QFontMetrics fm(m_textEdit->fontMetrics()); int maxWidth = 0; foreach (const QString& line, lines) { int lineWidth = fm.width(line); if (maxWidth < lineWidth) { maxWidth = lineWidth; } } maxWidth += fm.width(QLatin1String("WW")); // some space for the borders if (maxWidth <= 1000) { m_textEdit->setMinimumWidth(maxWidth); m_textEdit->setWordWrapMode(QTextOption::NoWrap); } else { m_textEdit->setWordWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); } if (text.isEmpty()) { m_textEdit->hide(); } else { m_textEdit->show(); } } /** * Set the message box's icon. * * @param icon icon to be displayed */ void MessageDialog::setIcon(QMessageBox::Icon icon) { QStyle::StandardPixmap sp = QStyle::SP_MessageBoxQuestion; bool hasIcon = true; switch (icon) { case QMessageBox::Question: sp = QStyle::SP_MessageBoxQuestion; break; case QMessageBox::Information: sp = QStyle::SP_MessageBoxInformation; break; case QMessageBox::Warning: sp = QStyle::SP_MessageBoxWarning; break; case QMessageBox::Critical: sp = QStyle::SP_MessageBoxCritical; break; case QMessageBox::NoIcon: default: hasIcon = false; } if (hasIcon) { QStyle* widgetStyle = style(); int iconSize = widgetStyle->pixelMetric( QStyle::PM_MessageBoxIconSize, 0, this); m_iconLabel->setPixmap(widgetStyle->standardIcon(sp, 0, this). pixmap(iconSize, iconSize)); } else { m_iconLabel->setPixmap(QPixmap()); } } /** * Set buttons to be displayed. * * @param buttons buttons to be displayed */ void MessageDialog::setStandardButtons(QMessageBox::StandardButtons buttons) { m_buttonBox->setStandardButtons( QDialogButtonBox::StandardButtons(static_cast<int>(buttons))); } /** * Set default button. * * @param button button which gets default focus */ void MessageDialog::setDefaultButton(QMessageBox::StandardButton button) { m_buttonBox->button(static_cast<QDialogButtonBox::StandardButton>(button))-> setDefault(true); } /** * Called when a button is clicked. * * @param button button which was clicked */ void MessageDialog::buttonClicked(QAbstractButton* button) { done(m_buttonBox->standardButton(button)); } /** * Display a modal dialog with a list of items. * * @param parent parent widget * @param title dialog title * @param text dialog text * @param list list of items * @param buttons buttons shown * * @return QMessageBox::StandardButton code of pressed button. */ int MessageDialog::warningList( QWidget* parent, const QString& title, const QString& text, const QStringList& list, QMessageBox::StandardButtons buttons) { MessageDialog dialog(parent); dialog.setWindowTitle(title); dialog.setText(text); dialog.setInformativeText(list.join(QLatin1String("\n"))); dialog.setIcon(QMessageBox::Warning); dialog.setStandardButtons(buttons); return dialog.exec(); } ���������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/qt/messagedialog.h���������������������������������������������������������������0000664�0000000�0000000�00000005535�12246035113�0017323�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \file messagedialog.h * Message dialog. * * \b Project: Kid3 * \author Urs Fleisch * \date 18 Aug 2011 * * Copyright (C) 2011 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #ifndef MESSAGEDIALOG_H #define MESSAGEDIALOG_H #include <QDialog> #include <QMessageBox> class QLabel; class QTextEdit; class QDialogButtonBox; /** * Message dialog. * Drop-in replacement for QMessageBox, but suitable for large texts. */ class MessageDialog : public QDialog { Q_OBJECT public: /** * Constructor. * * @param parent parent widget */ explicit MessageDialog(QWidget* parent = 0); /** * Destructor. */ virtual ~MessageDialog(); /** * Set the text to be displayed. * * @param text message text. */ void setText(const QString& text); /** * Set the informative text. * This text can be large and is displayed in a text edit. * * @param text message text. */ void setInformativeText(const QString& text); /** * Set the message box's icon. * * @param icon icon to be displayed */ void setIcon(QMessageBox::Icon icon); /** * Set buttons to be displayed. * * @param buttons buttons to be displayed */ void setStandardButtons(QMessageBox::StandardButtons buttons); /** * Set default button. * * @param button button which gets default focus */ void setDefaultButton(QMessageBox::StandardButton button); /** * Display a modal dialog with a list of items. * * @param parent parent widget * @param title dialog title * @param text dialog text * @param list list of items * @param buttons buttons shown * * @return QMessageBox::Ok or QMessageBox::Cancel. */ static int warningList(QWidget* parent, const QString& title, const QString& text, const QStringList& list, QMessageBox::StandardButtons buttons = QMessageBox::Ok | QMessageBox::Cancel); private slots: /** * Called when a button is clicked. * * @param button button which was clicked */ void buttonClicked(QAbstractButton* button); private: QLabel* m_iconLabel; QLabel* m_textLabel; QTextEdit* m_textEdit; QDialogButtonBox* m_buttonBox; }; #endif // MESSAGEDIALOG_H �������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/qt/platformtools.cpp�������������������������������������������������������������0000664�0000000�0000000�00000017073�12246035113�0017757�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \file platformtools.cpp * Platform specific tools. * * \b Project: Kid3 * \author Urs Fleisch * \date 30 Mar 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "platformtools.h" #include <QFileInfo> #include <QFileDialog> #include <QIcon> #include <QSettings> #include <QCoreApplication> #include "config.h" #include "browserdialog.h" #include "messagedialog.h" #include "kid3settings.h" #include "mainwindowconfig.h" /** * Constructor. */ PlatformTools::PlatformTools() : m_helpBrowser(0) { } /** * Destructor. */ PlatformTools::~PlatformTools() { if (m_helpBrowser) { // Without close() the application will not quit when the main window is // closed but the help browser is still open. m_helpBrowser->close(); delete m_helpBrowser; } } /** * Get application settings. * @return settings instance. */ ISettings* PlatformTools::applicationSettings() { return CorePlatformTools::applicationSettings(); } /** * Move file or directory to trash. * * @param path path to file or directory * * @return true if ok. */ bool PlatformTools::moveToTrash(const QString& path) const { return CorePlatformTools::moveToTrash(path); } /** * Display help for a topic. * * @param anchor anchor in help document */ void PlatformTools::displayHelp(const QString& anchor) { if (!m_helpBrowser) { QString caption(QCoreApplication::translate("@default", QT_TRANSLATE_NOOP("@default", "Kid3 Handbook"))); m_helpBrowser = new BrowserDialog(0, caption); } m_helpBrowser->goToAnchor(anchor); m_helpBrowser->setModal(!anchor.isEmpty()); if (m_helpBrowser->isHidden()) { m_helpBrowser->show(); } } /** * Get a themed icon by name. * @param name name of icon * @return icon. */ QIcon PlatformTools::iconFromTheme(const QString& name) const { #if QT_VERSION >= 0x040600 return QIcon::fromTheme(name, QIcon(QLatin1String(":/images/") + name + QLatin1String(".png"))); #else return QIcon(QLatin1String(":/images/") + name + QLatin1String(".png")); #endif } /** * Construct a name filter string suitable for file dialogs. * @param nameFilters list of description, filter pairs, e.g. * [("Images", "*.jpg *.jpeg *.png"), ("All Files", "*")]. * @return name filter string. */ QString PlatformTools::fileDialogNameFilter( const QList<QPair<QString, QString> >& nameFilters) const { return CorePlatformTools::fileDialogNameFilter(nameFilters); } /** * Get file pattern part of m_nameFilter. * @param nameFilter name filter string * @return file patterns, e.g. "*.mp3". */ QString PlatformTools::getNameFilterPatterns(const QString& nameFilter) const { return CorePlatformTools::getNameFilterPatterns(nameFilter); } /** * Display error dialog with item list. * @param parent parent widget * @param text text * @param strlist list of items * @param caption caption */ void PlatformTools::errorList(QWidget* parent, const QString& text, const QStringList& strlist, const QString& caption) { MessageDialog::warningList(parent, caption, text, strlist, QMessageBox::Ok); } /** * Display warning dialog with yes, no, cancel buttons. * @param parent parent widget * @param text text * @param caption caption * @return QMessageBox::Yes, QMessageBox::No or QMessageBox::Cancel. */ int PlatformTools::warningYesNoCancel(QWidget* parent, const QString& text, const QString& caption) { return QMessageBox::warning(parent, caption, text, QMessageBox::Yes | QMessageBox::Default, QMessageBox::No, QMessageBox::Cancel | QMessageBox::Escape); } /** * Display dialog to select an existing file. * @param parent parent widget * @param caption caption * @param dir directory * @param filter filter * @param selectedFilter the selected filter is returned here * @return selected file, empty if canceled. */ QString PlatformTools::getOpenFileName(QWidget* parent, const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter) { return QFileDialog::getOpenFileName( parent, caption, dir, filter, selectedFilter, MainWindowConfig::instance().m_dontUseNativeDialogs ? QFileDialog::DontUseNativeDialog : QFileDialog::Options(0)); } /** * Display dialog to select existing files. * @param parent parent widget * @param caption caption * @param dir directory * @param filter filter * @param selectedFilter the selected filter is returned here * @return selected files, empty if canceled. */ QStringList PlatformTools::getOpenFileNames(QWidget* parent, const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter) { return QFileDialog::getOpenFileNames( parent, caption, dir, filter, selectedFilter, MainWindowConfig::instance().m_dontUseNativeDialogs ? QFileDialog::DontUseNativeDialog : QFileDialog::Options(0)); } /** * Display dialog to select a file to save. * @param parent parent widget * @param caption caption * @param dir directory * @param filter filter * @param selectedFilter the selected filter is returned here * @return selected file, empty if canceled. */ QString PlatformTools::getSaveFileName(QWidget* parent, const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter) { return QFileDialog::getSaveFileName( parent, caption, dir, filter, selectedFilter, MainWindowConfig::instance().m_dontUseNativeDialogs ? QFileDialog::DontUseNativeDialog : QFileDialog::Options(0)); } /** * Display dialog to select an existing directory. * @param parent parent widget * @param caption caption * @param startDir start directory * @return selected directory, empty if canceled. */ QString PlatformTools::getExistingDirectory(QWidget* parent, const QString& caption, const QString& startDir) { return QFileDialog::getExistingDirectory(parent, caption, startDir, MainWindowConfig::instance().m_dontUseNativeDialogs ? QFileDialog::ShowDirsOnly | QFileDialog::DontUseNativeDialog : QFileDialog::ShowDirsOnly); } /** * Display warning dialog. * @param parent parent widget * @param text text * @param details detailed message * @param caption caption */ void PlatformTools::warningDialog(QWidget* parent, const QString& text, const QString& details, const QString& caption) { MessageDialog dialog(parent); dialog.setWindowTitle(caption); dialog.setText(text); dialog.setInformativeText(details); dialog.setIcon(QMessageBox::Warning); dialog.exec(); } /** * Display warning dialog with options to continue or cancel. * @param parent parent widget * @param text text * @param strlist list of items * @param caption caption * @return true if continue was selected. */ bool PlatformTools::warningContinueCancelList(QWidget* parent, const QString& text, const QStringList& strlist, const QString& caption) { return MessageDialog::warningList(parent, caption, text, strlist) == QMessageBox::Ok; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/qt/platformtools.h���������������������������������������������������������������0000664�0000000�0000000�00000012364�12246035113�0017422�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \file platformtools.h * Platform specific tools. * * \b Project: Kid3 * \author Urs Fleisch * \date 30 Mar 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #ifndef PLATFORMTOOLS_H #define PLATFORMTOOLS_H #include "iplatformtools.h" #include "coreplatformtools.h" class QSettings; class BrowserDialog; /** * Platform specific tools. */ class PlatformTools : public IPlatformTools, private CorePlatformTools { public: /** * Constructor. */ PlatformTools(); /** * Destructor. */ virtual ~PlatformTools(); /** * Get application settings. * @return settings instance. */ virtual ISettings* applicationSettings(); /** * Move file or directory to trash. * * @param path path to file or directory * * @return true if ok. */ virtual bool moveToTrash(const QString& path) const; /** * Display help for a topic. * * @param anchor anchor in help document */ virtual void displayHelp(const QString& anchor); /** * Get a themed icon by name. * @param name name of icon * @return icon. */ virtual QIcon iconFromTheme(const QString& name) const; /** * Construct a name filter string suitable for file dialogs. * @param nameFilters list of description, filter pairs, e.g. * [("Images", "*.jpg *.jpeg *.png"), ("All Files", "*")]. * @return name filter string. */ virtual QString fileDialogNameFilter( const QList<QPair<QString, QString> >& nameFilters) const; /** * Get file pattern part of m_nameFilter. * @param nameFilter name filter string * @return file patterns, e.g. "*.mp3". */ virtual QString getNameFilterPatterns(const QString& nameFilter) const; /** * Display error dialog with item list. * @param parent parent widget * @param text text * @param strlist list of items * @param caption caption */ virtual void errorList(QWidget* parent, const QString& text, const QStringList& strlist, const QString& caption); /** * Display warning dialog with yes, no, cancel buttons. * @param parent parent widget * @param text text * @param caption caption * @return QMessageBox::Yes, QMessageBox::No or QMessageBox::Cancel. */ virtual int warningYesNoCancel(QWidget* parent, const QString& text, const QString& caption); /** * Display dialog to select an existing file. * @param parent parent widget * @param caption caption * @param dir directory * @param filter filter * @param selectedFilter the selected filter is returned here * @return selected file, empty if canceled. */ virtual QString getOpenFileName(QWidget* parent, const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter); /** * Display dialog to select existing files. * @param parent parent widget * @param caption caption * @param dir directory * @param filter filter * @param selectedFilter the selected filter is returned here * @return selected files, empty if canceled. */ virtual QStringList getOpenFileNames(QWidget* parent, const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter); /** * Display dialog to select a file to save. * @param parent parent widget * @param caption caption * @param dir directory * @param filter filter * @param selectedFilter the selected filter is returned here * @return selected file, empty if canceled. */ virtual QString getSaveFileName(QWidget* parent, const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter); /** * Display dialog to select an existing directory. * @param parent parent widget * @param caption caption * @param startDir start directory * @return selected directory, empty if canceled. */ virtual QString getExistingDirectory(QWidget* parent, const QString& caption, const QString& startDir); /** * Display warning dialog. * @param parent parent widget * @param text text * @param details detailed message * @param caption caption */ virtual void warningDialog(QWidget* parent, const QString& text, const QString& details, const QString& caption); /** * Display warning dialog with options to continue or cancel. * @param parent parent widget * @param text text * @param strlist list of items * @param caption caption * @return true if continue was selected. */ virtual bool warningContinueCancelList(QWidget* parent, const QString& text, const QStringList& strlist, const QString& caption); private: BrowserDialog* m_helpBrowser; }; #endif // PLATFORMTOOLS_H ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/qt/qt.conf�����������������������������������������������������������������������0000664�0000000�0000000�00000000037�12246035113�0015631�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������[Paths] Libraries = Frameworks �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/qt/recentfilesmenu.cpp�����������������������������������������������������������0000664�0000000�0000000�00000006743�12246035113�0020244�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \file recentfilesmenu.cpp * Menu to open recent files. * * \b Project: Kid3 * \author Urs Fleisch * \date 15 Aug 2010 * * Copyright (C) 2010-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "recentfilesmenu.h" #include <QAction> #include <QDir> static const int MAX_RECENT_FILES = 10; /** * Constructor. * * @param parent parent widget */ RecentFilesMenu::RecentFilesMenu(QWidget* parent) : QMenu(parent) { setObjectName(QLatin1String("RecentFilesMenu")); } /** * Destructor. */ RecentFilesMenu::~RecentFilesMenu() { } /** * Add directory to list of recent files. * * @param dir path to directory */ void RecentFilesMenu::addDirectory(const QString& dir) { QString path = QDir(dir).canonicalPath(); if (path.isNull()) return; // first remove the path if it already exists int pathIdx = m_files.indexOf(path); if (pathIdx != -1) { m_files.removeAt(pathIdx); } m_files.prepend(path); if (m_files.size() > MAX_RECENT_FILES) { m_files.removeLast(); } updateRecentFileActions(); } /** * Saves the current recent files entries to a given configuration. * * @param config configuration settings */ void RecentFilesMenu::saveEntries(ISettings* config) { config->beginGroup(QLatin1String("RecentFiles")); config->setValue(QLatin1String("Files"), QVariant(m_files)); config->endGroup(); } /** * Loads the recent files entries from a given configuration. * * @param config configuration settings */ void RecentFilesMenu::loadEntries(ISettings* config) { config->beginGroup(QLatin1String("RecentFiles")); m_files = config->value(QLatin1String("Files"), m_files).toStringList(); config->endGroup(); while (m_files.size() > MAX_RECENT_FILES) { m_files.removeLast(); } updateRecentFileActions(); } /** * Update the recent file actions. */ void RecentFilesMenu::updateRecentFileActions() { int i = 0; clear(); for (QStringList::const_iterator it = m_files.begin(); it != m_files.end(); ++it) { QAction* act = new QAction(this); act->setText(QString(QLatin1String("&%1 %2")).arg(++i).arg(*it)); act->setData(*it); connect(act, SIGNAL(triggered()), this, SLOT(openRecentFile())); this->addAction(act); } if (i > 0) { addSeparator(); QAction* clearListAction = new QAction(this); clearListAction->setText(tr("&Clear List")); connect(clearListAction, SIGNAL(triggered()), this, SLOT(clearList())); this->addAction(clearListAction); setEnabled(true); } else { setEnabled(false); } } /** * Emit a load file signal when a recent file has to be loaded. */ void RecentFilesMenu::openRecentFile() { QAction* action = qobject_cast<QAction*>(sender()); if (action) { emit loadFile(action->data().toString()); } } /** * Clear the list of recent files. */ void RecentFilesMenu::clearList() { m_files.clear(); updateRecentFileActions(); } �����������������������������kid3-3.0.2/src/app/qt/recentfilesmenu.h�������������������������������������������������������������0000664�0000000�0000000�00000004212�12246035113�0017676�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \file recentfilesmenu.h * Menu to open recent files. * * \b Project: Kid3 * \author Urs Fleisch * \date 15-Aug-2010 * * Copyright (C) 2010-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #ifndef RECENTFILESMENU_H #define RECENTFILESMENU_H #include <QStringList> #include <QMenu> #include "generalconfig.h" /** * Menu to open recent files. */ class RecentFilesMenu : public QMenu { Q_OBJECT public: /** * Constructor. * * @param parent parent widget */ explicit RecentFilesMenu(QWidget* parent); /** * Destructor. */ virtual ~RecentFilesMenu(); /** * Add directory to list of recent files. * * @param dir path to directory */ void addDirectory(const QString& dir); /** * Saves the current recent files entries to a given configuration. * * @param config configuration settings */ void saveEntries(ISettings* config); /** * Loads the recent files entries from a given configuration. * * @param config configuration settings */ void loadEntries(ISettings* config); signals: /** * Emitted when a recent file has to be loaded. * Parameter: path to file or directory */ void loadFile(const QString&); private slots: /** * Emit a load file signal when a recent file has to be loaded. */ void openRecentFile(); /** * Clear the list of recent files. */ void clearList(); private: /** * Update the recent file actions. */ void updateRecentFileActions(); QStringList m_files; }; #endif // RECENTFILESMENU_H ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/qt/shortcutsdelegate.cpp���������������������������������������������������������0000664�0000000�0000000�00000016523�12246035113�0020602�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \file shortcutsdelegate.cpp * Keyboard shortcuts item delegate. * * \b Project: Kid3 * \author Urs Fleisch * \date 29 Dec 2011 * * Copyright (C) 2011-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "shortcutsdelegate.h" #include <QToolButton> #include <QLineEdit> #include <QHBoxLayout> #include <QKeyEvent> /** * Constructor. * @param parent parent object */ ShortcutsDelegate::ShortcutsDelegate(QObject* parent) : QItemDelegate(parent), m_resetFlag(false) { } /** * Destructor. */ ShortcutsDelegate::~ShortcutsDelegate() { } /** * Create an editor to edit the cells contents. * @param parent parent widget * @param option style * @param index index of item * @return editor widget */ QWidget* ShortcutsDelegate::createEditor( QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const { QWidget* editor = QItemDelegate::createEditor(parent, option, index); if (QLineEdit* le = qobject_cast<QLineEdit*>(editor)) { editor = new ShortcutsDelegateEditor(le, parent); connect(editor, SIGNAL(clearClicked()), this, SLOT(clearAndCloseEditor())); connect(editor, SIGNAL(resetClicked()), this, SLOT(resetToDefault())); connect(editor, SIGNAL(valueEntered()), this, SLOT(commitAndCloseEditor())); } return editor; } /** * Reset editor to default value. */ void ShortcutsDelegate::resetToDefault() { if (ShortcutsDelegateEditor* editor = qobject_cast<ShortcutsDelegateEditor*>(sender())) { m_resetFlag = true; emit commitData(editor); emit closeEditor(editor); } } /** * Commit editor value and close editor. */ void ShortcutsDelegate::commitAndCloseEditor() { if (ShortcutsDelegateEditor* editor = qobject_cast<ShortcutsDelegateEditor*>(sender())) { emit commitData(editor); emit closeEditor(editor); } } /** * Clear editor value and close editor. */ void ShortcutsDelegate::clearAndCloseEditor() { if (ShortcutsDelegateEditor* editor = qobject_cast<ShortcutsDelegateEditor*>(sender())) { editor->getLineEdit()->clear(); emit commitData(editor); emit closeEditor(editor); } } /** * Set data to be edited by the editor. * @param editor editor widget * @param index index of item */ void ShortcutsDelegate::setEditorData( QWidget* editor, const QModelIndex& index) const { if (ShortcutsDelegateEditor* compoundWidget = qobject_cast<ShortcutsDelegateEditor*>(editor)) { QItemDelegate::setEditorData(compoundWidget->getLineEdit(), index); } } /** * Set model data supplied by editor. * @param editor editor widget * @param model model * @param index index of item */ void ShortcutsDelegate::setModelData( QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const { if (ShortcutsDelegateEditor* compoundWidget = qobject_cast<ShortcutsDelegateEditor*>(editor)) { if (m_resetFlag) { m_resetFlag = false; model->setData(index, QVariant(), Qt::EditRole); } else { QItemDelegate::setModelData(compoundWidget->getLineEdit(), model, index); } } } /** * Updates the geometry of the @a editor for the item with the given * @a index, according to the rectangle specified in the @a option. * @param editor editor widget * @param option style * @param index index of item */ void ShortcutsDelegate::updateEditorGeometry( QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex&) const { // If this method is not overridden, the ShortcutsDelegateEditor // is displayed as a thin horizontal line, see also // http://stackoverflow.com/questions/5457154/qstyleditemdelegate-how-does-updateeditorgeometry-works QRect rect(option.rect); QSize sizeHint(editor->sizeHint()); if (rect.width() < sizeHint.width()) { rect.setWidth(sizeHint.width()); } if (rect.height() < sizeHint.height()) { int yAdj = (rect.height() - sizeHint.height()) / 2; rect.setHeight(sizeHint.height()); rect.translate(0, yAdj); } editor->setGeometry(rect); } /** * Constructor. * * @param parent parent widget */ ShortcutsDelegateEditor::ShortcutsDelegateEditor( QLineEdit* lineEdit, QWidget* parent) : QFrame(parent), m_lineEdit(lineEdit) { QHBoxLayout* hlayout = new QHBoxLayout(this); hlayout->setContentsMargins(0, 0, 0, 0); hlayout->addWidget(m_lineEdit, 0, Qt::AlignLeft); QToolButton* clearButton = new QToolButton(this); clearButton->setText(tr("Clear")); connect(clearButton, SIGNAL(clicked()), this, SIGNAL(clearClicked())); hlayout->addWidget(clearButton); QToolButton* resetButton = new QToolButton(this); resetButton->setText(tr("Reset")); connect(resetButton, SIGNAL(clicked()), this, SIGNAL(resetClicked())); hlayout->addWidget(resetButton); setFocusProxy(m_lineEdit); m_lineEdit->setReadOnly(true); m_lineEdit->installEventFilter(this); } /** * Destructor. */ ShortcutsDelegateEditor::~ShortcutsDelegateEditor() { } bool ShortcutsDelegateEditor::event(QEvent* ev) { QEvent::Type eventType = ev->type(); if (eventType != QEvent::ShortcutOverride && eventType != QEvent::KeyPress && eventType != QEvent::KeyRelease) return QFrame::event(ev); if (eventType == QEvent::ShortcutOverride) { QKeyEvent* keyEvent = static_cast<QKeyEvent*>(ev); int keyCode = 0; // Check modifiers pressed if (keyEvent->modifiers() & Qt::ControlModifier) keyCode |= Qt::ControlModifier; if (keyEvent->modifiers() & Qt::AltModifier) keyCode |= Qt::AltModifier; if (keyEvent->modifiers() & Qt::ShiftModifier) keyCode |= Qt::ShiftModifier; if (keyEvent->modifiers() & Qt::MetaModifier) keyCode |= Qt::MetaModifier; switch (keyEvent->key()) { // These keys can't be used case Qt::Key_Shift: case Qt::Key_Control: case Qt::Key_Meta: case Qt::Key_Alt: case Qt::Key_AltGr: case Qt::Key_Super_L: case Qt::Key_Super_R: case Qt::Key_Menu: case Qt::Key_Hyper_L: case Qt::Key_Hyper_R: case Qt::Key_Help: case Qt::Key_Direction_L: case Qt::Key_Direction_R: break; default: keyCode |= keyEvent->key(); } QString keyString = QKeySequence(keyCode).toString(); if (!keyString.endsWith(QLatin1Char('+'))) { m_lineEdit->setText(keyString); emit valueEntered(); } } return true; } /** * Filters events if this object has been installed as an event filter for * the @a watched object. * @param watched watched object * @param ev event * @return true to stop further event handling */ bool ShortcutsDelegateEditor::eventFilter(QObject* watched, QEvent* ev) { QEvent::Type eventType = ev->type(); if (eventType == QEvent::KeyPress || eventType == QEvent::KeyRelease || eventType == QEvent::ShortcutOverride) return event(ev); else return QFrame::eventFilter(watched, ev); } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/qt/shortcutsdelegate.h�����������������������������������������������������������0000664�0000000�0000000�00000007616�12246035113�0020252�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \file shortcutsdelegate.h * Keyboard shortcuts item delegate. * * \b Project: Kid3 * \author Urs Fleisch * \date 29 Dec 2011 * * Copyright (C) 2011 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #ifndef SHORTCUTSDELEGATE_H #define SHORTCUTSDELEGATE_H #include <QItemDelegate> #include "config.h" /** * Item delegate to edit and reset keyboard shortcuts. */ class ShortcutsDelegate : public QItemDelegate { Q_OBJECT public: /** * Constructor. * @param parent parent object */ explicit ShortcutsDelegate(QObject* parent = 0); /** * Destructor. */ virtual ~ShortcutsDelegate(); /** * Create an editor to edit the cells contents. * @param parent parent widget * @param option style * @param index index of item * @return editor widget */ virtual QWidget* createEditor( QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const; /** * Set data to be edited by the editor. * @param editor editor widget * @param index index of item */ virtual void setEditorData(QWidget* editor, const QModelIndex& index) const; /** * Set model data supplied by editor. * @param editor editor widget * @param model model * @param index index of item */ virtual void setModelData( QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const; /** * Updates the geometry of the @a editor for the item with the given * @a index, according to the rectangle specified in the @a option. * @param editor editor widget * @param option style * @param index index of item */ virtual void updateEditorGeometry( QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const; private slots: void clearAndCloseEditor(); void resetToDefault(); void commitAndCloseEditor(); private: mutable bool m_resetFlag; }; /** * Editor widget for delegate with buttons to clear and reset the value. * * The editor consists of a line edit to edit the value and buttons to clear and * reset the value to the default. */ class ShortcutsDelegateEditor : public QFrame { Q_OBJECT public: /** * Constructor. * * @param lineEdit widget used to edit value * @param parent parent widget */ explicit ShortcutsDelegateEditor(QLineEdit* lineEdit, QWidget* parent = 0); /** * Destructor. */ virtual ~ShortcutsDelegateEditor(); /*! * Get edit widget. * @return line edit widget */ QLineEdit* getLineEdit() { return m_lineEdit; } /** * Filters events if this object has been installed as an event filter for * the @a watched object. * @param watched watched object * @param ev event * @return true to stop further event handling */ virtual bool eventFilter(QObject* watched, QEvent* ev); signals: /** * Emitted when a shortcut has been entered. */ void valueEntered(); /** * Emitted when the clear button is clicked. */ void clearClicked(); /** * Emitted when the reset button is clicked. */ void resetClicked(); protected: /** * Receive events. * @param ev event * @return true if event was recognized and processed */ virtual bool event(QEvent* ev); private: QLineEdit* m_lineEdit; }; #endif // SHORTCUTSDELEGATE_H ������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/qt/shortcutsmodel.cpp������������������������������������������������������������0000664�0000000�0000000�00000026134�12246035113�0020127�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \file shortcutsmodel.cpp * Keyboard shortcuts configuration tree model. * * \b Project: Kid3 * \author Urs Fleisch * \date 29 Dec 2011 * * Copyright (C) 2011-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "shortcutsmodel.h" #include "isettings.h" #include <QAction> namespace { static const int TopLevelId = -1; bool isTopLevelItem(const QModelIndex& index) { return quintptr(index.internalId()) == quintptr(TopLevelId); } } /** * Constructor. * @param parent parent widget */ ShortcutsModel::ShortcutsModel(QObject* parent) : QAbstractItemModel(parent) { setObjectName(QLatin1String("ShortcutsModel")); } /** * Destructor. */ ShortcutsModel::~ShortcutsModel() { } /** * Get item flags for index. * @param index model index * @return item flags */ Qt::ItemFlags ShortcutsModel::flags(const QModelIndex& index) const { Qt::ItemFlags itemFlags = QAbstractItemModel::flags(index); if (index.isValid() && index.column() == ShortcutColumn) { itemFlags |= Qt::ItemIsEditable; } return itemFlags; } /** * Get group if a model index is a valid index for a group item. * @param index index to check * @return group if index is for a group item else 0 */ const ShortcutsModel::ShortcutGroup* ShortcutsModel::shortcutGroupForIndex( const QModelIndex& index) const { if (index.column() == 0 && index.row() >= 0 && index.row() < m_shortcutGroups.size() && isTopLevelItem(index)) { return &m_shortcutGroups.at(index.row()); } return 0; } /** * Get data for a given role. * @param index model index * @param role item data role * @return data for role */ QVariant ShortcutsModel::data(const QModelIndex& index, int role) const { if (index.isValid()) { QModelIndex parentIndex = index.parent(); if (parentIndex.isValid()) { if (const ShortcutGroup* group = shortcutGroupForIndex(parentIndex)) { if (index.row() >= 0 && index.row() < group->size()) { const ShortcutItem& shortcutItem = group->at(index.row()); if (index.column() == ActionColumn) { if (role == Qt::DisplayRole) { return shortcutItem.actionText(); } else if (role == Qt::FontRole) { if (shortcutItem.isCustomShortcutActive()) { QFont font; font.setBold(true); return font; } } } else if (index.column() == ShortcutColumn) { if (role == Qt::DisplayRole || role == Qt::EditRole) { return shortcutItem.activeShortcut(); } else if (role == Qt::ToolTipRole) { return tr("Press F2 or double click to edit cell contents."); } } } } } else { if (const ShortcutGroup* group = shortcutGroupForIndex(index)) { if (role == Qt::DisplayRole) { return group->context(); } } } } return QVariant(); } /** * Set data for a given role. * @param index model index * @param value data value * @param role item data role * @return true if successful */ bool ShortcutsModel::setData(const QModelIndex& index, const QVariant& value, int role) { if (index.isValid() && index.column() == ShortcutColumn && role == Qt::EditRole) { QModelIndex parentIndex = index.parent(); if (parentIndex.isValid()) { if (ShortcutGroup* group = const_cast<ShortcutGroup*>(shortcutGroupForIndex(parentIndex))) { if (index.row() >= 0 && index.row() < group->size()) { ShortcutItem si((*group)[index.row()]); si.setCustomShortcut(value.toString()); QString keyString(si.activeShortcut()); if (!keyString.isEmpty()) { foreach (const ShortcutGroup& g, m_shortcutGroups) { foreach (const ShortcutItem& i, g) { if (i.activeShortcut() == keyString && si.action() != i.action()) { emit shortcutAlreadyUsed(keyString, g.context(), i.action()); return false; } } } } (*group)[index.row()].setCustomShortcut(value.toString()); emit dataChanged(index.sibling(index.row(), ActionColumn), index); emit shortcutSet(keyString, group->context(), si.action()); return true; } } } } return false; } /** * Get data for header section. * @param section column or row * @param orientation horizontal or vertical * @param role item data role * @return header data for role */ QVariant ShortcutsModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole) return QVariant(); if (orientation == Qt::Horizontal) { if (section == ActionColumn) { return tr("Action"); } else if (section == ShortcutColumn) { return tr("Shortcut"); } } return section + 1; } /** * Get number of rows. * @param parent parent model index * @return number of rows, if parent is valid number of children */ int ShortcutsModel::rowCount(const QModelIndex& parent) const { if (parent.isValid()) { if (const ShortcutGroup* group = shortcutGroupForIndex(parent)) { return group->size(); } return 0; } else { return m_shortcutGroups.size(); } } /** * Get number of columns. * @param parent parent model index * @return number of columns for children of given @a parent */ int ShortcutsModel::columnCount(const QModelIndex& parent) const { Q_UNUSED(parent) return NumColumns; } /** * Get model index of item. * @param row row of item * @param column column of item * @param parent index of parent item * @return model index of item */ QModelIndex ShortcutsModel::index(int row, int column, const QModelIndex& parent) const { if (parent.isValid()) { const ShortcutGroup* group; if ((group = shortcutGroupForIndex(parent)) != 0 && column >= 0 && column < NumColumns && row >= 0 && row <= group->size()) { return createIndex(row, column, parent.row()); } } else { if (column == 0 && row >= 0 && row < m_shortcutGroups.size()) { return createIndex(row, column, TopLevelId); } } return QModelIndex(); } /** * Get parent of item. * @param index model index of item * @return model index of parent item */ QModelIndex ShortcutsModel::parent(const QModelIndex& index) const { int id = index.internalId(); if (id >= 0 && id < m_shortcutGroups.size()) { return createIndex(id, 0, TopLevelId); } return QModelIndex(); } /** * Register an action. * * @param action action to be added to model * @param context context of action */ void ShortcutsModel::registerAction(QAction* action, const QString& context) { ShortcutItem item(action); ShortcutGroup group(context); QList<ShortcutGroup>::iterator it; for (it = m_shortcutGroups.begin(); it != m_shortcutGroups.end(); ++it) { if (it->context() == group.context()) { it->append(item); break; } } if (it == m_shortcutGroups.end()) { group.append(item); m_shortcutGroups.append(group); } } /** * Assign the shortcuts which have been changed to their actions. * * @return true if there was at least one shortcut changed */ bool ShortcutsModel::assignChangedShortcuts() { bool changed = false; for (QList<ShortcutGroup>::iterator git = m_shortcutGroups.begin(); git != m_shortcutGroups.end(); ++git) { for (ShortcutGroup::iterator iit = git->begin(); iit != git->end(); ++iit) { if (iit->isCustomShortcutChanged()) { iit->assignCustomShortcut(); changed = true; } } } return changed; } /** * Forget about all changed shortcuts. */ void ShortcutsModel::discardChangedShortcuts() { for (QList<ShortcutGroup>::iterator git = m_shortcutGroups.begin(); git != m_shortcutGroups.end(); ++git) { for (ShortcutGroup::iterator iit = git->begin(); iit != git->end(); ++iit) { iit->revertCustomShortcut(); } } } /** * Save the shortcuts to a given configuration. * * @param config configuration settings */ void ShortcutsModel::writeToConfig(ISettings* config) const { config->beginGroup(QLatin1String("Shortcuts")); config->remove(QLatin1String("")); for (QList<ShortcutGroup>::const_iterator git = m_shortcutGroups.constBegin(); git != m_shortcutGroups.constEnd(); ++git) { for (ShortcutGroup::const_iterator iit = git->constBegin(); iit != git->constEnd(); ++iit) { QString actionName(iit->action() ? iit->action()->objectName() : QLatin1String("")); if (!actionName.isEmpty()) { if (!iit->customShortcut().isEmpty()) { config->setValue(actionName, iit->customShortcut()); } } else { qWarning("Action %s does not have an object name", qPrintable(iit->actionText())); } } } config->endGroup(); } /** * Read the shortcuts from a given configuration. * * @param config configuration settings */ void ShortcutsModel::readFromConfig(ISettings* config) { config->beginGroup(QLatin1String("Shortcuts")); for (QList<ShortcutGroup>::iterator git = m_shortcutGroups.begin(); git != m_shortcutGroups.end(); ++git) { for (ShortcutGroup::iterator iit = git->begin(); iit != git->end(); ++iit) { QString actionName(iit->action() ? iit->action()->objectName() : QLatin1String("")); if (!actionName.isEmpty() && config->contains(actionName)) { QString keyStr(config->value(actionName, QString()).toString()); iit->setCustomShortcut(keyStr); iit->assignCustomShortcut(); } } } config->endGroup(); } ShortcutsModel::ShortcutItem::ShortcutItem(QAction* act) : m_action(act), m_defaultShortcut(m_action->shortcut().toString()) { } void ShortcutsModel::ShortcutItem::setCustomShortcut(const QString& shortcut) { m_customShortcut = shortcut != m_defaultShortcut ? shortcut : QString(); } void ShortcutsModel::ShortcutItem::revertCustomShortcut() { m_customShortcut = m_oldCustomShortcut; } void ShortcutsModel::ShortcutItem::assignCustomShortcut() { m_action->setShortcut(QKeySequence(activeShortcut())); m_oldCustomShortcut = m_customShortcut; } QString ShortcutsModel::ShortcutItem::actionText() const { return m_action ? m_action->text().remove(QLatin1Char('&')) : QLatin1String(""); } ShortcutsModel::ShortcutGroup::ShortcutGroup(const QString& ctx) : m_context(ctx) { m_context.remove(QLatin1Char('&')); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/app/qt/shortcutsmodel.h��������������������������������������������������������������0000664�0000000�0000000�00000014476�12246035113�0017602�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \file shortcutsmodel.h * Keyboard shortcuts configuration tree model. * * \b Project: Kid3 * \author Urs Fleisch * \date 29 Dec 2011 * * Copyright (C) 2011 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #ifndef SHORTCUTSMODEL_H #define SHORTCUTSMODEL_H #include <QAbstractItemModel> #include <QList> class QAction; class ISettings; /** * Keyboard shortcuts configuration tree model. * * The model is hierarchical with two levels: The keyboard shortcuts have * columns with the text of the action and the key sequences and have context * parent items, which describe the menu or section in the GUI where the * action can be found. The model can be used in a QTreeView, to edit the * @ref ShortcutColumn, a ShortcutsDelegate can be used. */ class ShortcutsModel : public QAbstractItemModel { Q_OBJECT public: /** * Columns in model. */ enum Columns { ActionColumn, /**< Action text */ ShortcutColumn, /**< Shortcut key sequence */ NumColumns /**< Number of columns */ }; /** * Constructor. * @param parent parent widget */ explicit ShortcutsModel(QObject* parent = 0); /** * Destructor. */ virtual ~ShortcutsModel(); /** * Get item flags for index. * @param index model index * @return item flags */ virtual Qt::ItemFlags flags(const QModelIndex& index) const; /** * Get data for a given role. * @param index model index * @param role item data role * @return data for role */ virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; /** * Set data for a given role. * @param index model index * @param value data value * @param role item data role * @return true if successful */ virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole); /** * Get data for header section. * @param section column or row * @param orientation horizontal or vertical * @param role item data role * @return header data for role */ virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; /** * Get number of rows. * @param parent parent model index * @return number of rows, if parent is valid number of children */ virtual int rowCount(const QModelIndex& parent=QModelIndex()) const; /** * Get number of columns. * @param parent parent model index * @return number of columns for children of given \a parent */ virtual int columnCount(const QModelIndex& parent = QModelIndex()) const; /** * Get model index of item. * @param row row of item * @param column column of item * @param parent index of parent item * @return model index of item */ virtual QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const; /** * Get parent of item. * @param index model index of item * @return model index of parent item */ virtual QModelIndex parent(const QModelIndex& index) const; /** * Register an action. * * @param action action to be added to model * @param context context of action */ void registerAction(QAction* action, const QString& context); /** * Assign the shortcuts which have been changed to their actions. * * @return true if there was at least one shortcut changed */ bool assignChangedShortcuts(); /** * Save the shortcuts to a given configuration. * * @param config configuration settings */ void writeToConfig(ISettings* config) const; /** * Read the shortcuts from a given configuration. * * @param config configuration settings */ void readFromConfig(ISettings* config); public slots: /** * Forget about all changed shortcuts. */ void discardChangedShortcuts(); signals: /** * Emitted if a keyboard shortcut is already used. * @param key string representation of key sequence * @param context context of action * @param action action using @a key */ void shortcutAlreadyUsed(const QString& key, const QString& context, const QAction* action); /** * Emitted if a keyboard shortcut is set. * Can be used to clear a previously displayed "already used" warning. * @param key string representation of key sequence * @param context context of action * @param action action using @a key */ void shortcutSet(const QString& key, const QString& context, const QAction* action); private: class ShortcutItem { public: explicit ShortcutItem(QAction* act); QAction* action() { return m_action; } const QAction* action() const { return m_action; } QString customShortcut() const { return m_customShortcut; } void setCustomShortcut(const QString& shortcut); void revertCustomShortcut(); void assignCustomShortcut(); bool isCustomShortcutChanged() const { return m_customShortcut != m_oldCustomShortcut; } QString activeShortcut() const { return m_customShortcut.isNull() ? m_defaultShortcut : m_customShortcut; } bool isCustomShortcutActive() const { return !m_customShortcut.isNull(); } QString actionText() const; private: QAction* m_action; QString m_defaultShortcut; QString m_customShortcut; QString m_oldCustomShortcut; }; class ShortcutGroup : public QList<ShortcutItem> { public: explicit ShortcutGroup(const QString& ctx); QString context() const { return m_context; } private: QString m_context; }; const ShortcutGroup* shortcutGroupForIndex(const QModelIndex& index) const; QList<ShortcutGroup> m_shortcutGroups; }; #endif // SHORTCUTSMODEL_H ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/core/��������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�12246035113�0014062�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/core/CMakeLists.txt������������������������������������������������������������������0000664�0000000�0000000�00000002072�12246035113�0016623�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������include_directories(utils config tags export import model) set(core_SRCS) set(core_MOC_HDRS) foreach (_subdir utils config tags export import model) include(${_subdir}/Sources.cmake) set(core_SRCS ${core_SRCS} ${${_subdir}_SRCS}) set(core_MOC_HDRS ${core_MOC_HDRS} ${${_subdir}_MOC_HDRS}) endforeach() add_definitions(-DKID3_BUILD_CORE_LIB) if (NOT BUILD_SHARED_LIBS) add_definitions(${QT_EXECUTABLE_COMPILE_FLAGS}) endif (NOT BUILD_SHARED_LIBS) if (WITH_GCC_PCH) add_definitions(${GCC_PCH_COMPILE_FLAGS}) endif (WITH_GCC_PCH) qt4_wrap_cpp(core_GEN_MOC_SRCS ${core_MOC_HDRS}) add_library(kid3-core ${core_SRCS} ${core_GEN_MOC_SRCS}) set_target_properties(kid3-core PROPERTIES VERSION ${KID3_VERSION}) target_link_libraries(kid3-core ${BASIC_LIBRARIES} ${PLUGIN_LIBRARIES}) if (BUILD_SHARED_LIBS) install(TARGETS kid3-core LIBRARY DESTINATION ${WITH_LIBDIR} RUNTIME DESTINATION ${WITH_BINDIR}) endif (BUILD_SHARED_LIBS) if (BUILD_KDE_APP) install(FILES model/net.sourceforge.Kid3.xml DESTINATION "share/dbus-1/interfaces") endif (BUILD_KDE_APP) ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/core/config/�������������������������������������������������������������������������0000775�0000000�0000000�00000000000�12246035113�0015327�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/core/config/Sources.cmake������������������������������������������������������������0000664�0000000�0000000�00000001165�12246035113�0017757�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������set(config_SRCS config/batchimportconfig.cpp config/batchimportprofile.cpp config/batchimportsourcesmodel.cpp config/configstore.cpp config/filterconfig.cpp config/formatconfig.cpp config/generalconfig.cpp config/importconfig.cpp config/exportconfig.cpp config/tagconfig.cpp config/fileconfig.cpp config/rendirconfig.cpp config/numbertracksconfig.cpp config/useractionsconfig.cpp config/guiconfig.cpp config/networkconfig.cpp config/playlistconfig.cpp config/serverimporterconfig.cpp config/isettings.cpp config/kid3settings.cpp ) set(config_MOC_HDRS config/batchimportsourcesmodel.h ) �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/core/config/batchimportconfig.cpp����������������������������������������������������0000664�0000000�0000000�00000011161�12246035113�0021535�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \file batchimportconfig.cpp * Configuration for batch import. * * \b Project: Kid3 * \author Urs Fleisch * \date 3 Jan 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "batchimportconfig.h" #include <QString> #include "batchimportprofile.h" #include "config.h" int BatchImportConfig::s_index = -1; /** * Constructor. */ BatchImportConfig::BatchImportConfig() : StoredConfig<BatchImportConfig>(QLatin1String("BatchImport")), m_importDest(TrackData::TagV2), m_profileIdx(0) { /** * Preset profile expressions. */ m_profileNames << QLatin1String("All") << QLatin1String("MusicBrainz") << QLatin1String("Discogs") << QLatin1String("Cover Art") << QLatin1String("Custom Profile"); m_profileSources << QLatin1String("MusicBrainz Release:75:SAC;Discogs:75:SAC;Amazon:75:SAC;gnudb.org:75:S;TrackType.org:75:S") << QLatin1String("MusicBrainz Release:75:SAC") << QLatin1String("Discogs:75:SAC") << QLatin1String("Amazon:75:C;Discogs:75:C;MusicBrainz Release:75:C") << QLatin1String(""); } /** * Destructor. */ BatchImportConfig::~BatchImportConfig() { } /** * Persist configuration. * * @param config configuration */ void BatchImportConfig::writeToConfig(ISettings* config) const { config->beginGroup(m_group); config->setValue(QLatin1String("ImportDestination"), QVariant(m_importDest)); config->setValue(QLatin1String("ProfileNames"), QVariant(m_profileNames)); config->setValue(QLatin1String("ProfileSources"), QVariant(m_profileSources)); config->setValue(QLatin1String("ProfileIdx"), QVariant(m_profileIdx)); config->setValue(QLatin1String("WindowGeometry"), QVariant(m_windowGeometry)); config->endGroup(); } /** * Read persisted configuration. * * @param config configuration */ void BatchImportConfig::readFromConfig(ISettings* config) { QStringList names, sources; config->beginGroup(m_group); m_importDest = TrackData::tagVersionCast( config->value(QLatin1String("ImportDestination"), m_importDest).toInt()); names = config->value(QLatin1String("ProfileNames"), m_profileNames).toStringList(); sources = config->value(QLatin1String("ProfileSources"), m_profileSources).toStringList(); m_profileIdx = config->value(QLatin1String("ProfileIdx"), m_profileIdx).toInt(); m_windowGeometry = config->value(QLatin1String("WindowGeometry"), m_windowGeometry).toByteArray(); config->endGroup(); // KConfig seems to strip empty entries from the end of the string lists, // so we have to append them again. unsigned numNames = names.size(); while (static_cast<unsigned>(sources.size()) < numNames) sources.append(QLatin1String("")); /* Use defaults if no configuration found */ QStringList::const_iterator namesIt, sourcesIt; for (namesIt = names.begin(), sourcesIt = sources.begin(); namesIt != names.end() && sourcesIt != sources.end(); ++namesIt, ++sourcesIt) { int idx = m_profileNames.indexOf(*namesIt); if (idx >= 0) { m_profileSources[idx] = *sourcesIt; } else if (!(*namesIt).isEmpty()) { m_profileNames.append(*namesIt); m_profileSources.append(*sourcesIt); } } if (m_profileIdx >= static_cast<int>(m_profileNames.size())) m_profileIdx = 0; } /** * Get a batch import profile. * * @param name name of profile * @param profile the profile will be returned here * @return true if profile with @a name found. */ bool BatchImportConfig::getProfileByName(const QString& name, BatchImportProfile& profile) const { for (QStringList::const_iterator namesIt = m_profileNames.constBegin(), sourcesIt = m_profileSources.constBegin(); namesIt != m_profileNames.constEnd() && sourcesIt != m_profileSources.constEnd(); ++namesIt, ++sourcesIt) { if (name == *namesIt) { profile.setName(*namesIt); profile.setSourcesFromString(*sourcesIt); return true; } } return false; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/core/config/batchimportconfig.h������������������������������������������������������0000664�0000000�0000000�00000004223�12246035113�0021203�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \file batchimportconfig.h * Configuration for batch import. * * \b Project: Kid3 * \author Urs Fleisch * \date 3 Jan 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #ifndef BATCHIMPORTCONFIG_H #define BATCHIMPORTCONFIG_H #include <QStringList> #include "config.h" #include "generalconfig.h" #include "trackdata.h" #include "kid3api.h" class BatchImportProfile; /** * Filter configuration. */ class KID3_CORE_EXPORT BatchImportConfig : public StoredConfig<BatchImportConfig> { public: /** * Constructor. */ BatchImportConfig(); /** * Destructor. */ virtual ~BatchImportConfig(); /** * Persist configuration. * * @param config KDE configuration */ virtual void writeToConfig(ISettings* config) const; /** * Read persisted configuration. * * @param config KDE configuration */ virtual void readFromConfig(ISettings* config); /** * Get a batch import profile. * * @param name name of profile * @param profile the profile will be returned here * @return true if profile with @a name found. */ bool getProfileByName(const QString& name, BatchImportProfile& profile) const; /** tag version to import */ TrackData::TagVersion m_importDest; /** Names of profiles */ QStringList m_profileNames; /** Profile import sources */ QStringList m_profileSources; /** Selected profile */ int m_profileIdx; /** Window geometry */ QByteArray m_windowGeometry; /** Index in configuration storage */ static int s_index; }; #endif �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/core/config/batchimportprofile.cpp���������������������������������������������������0000664�0000000�0000000�00000005117�12246035113�0021734�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \file batchimportprofile.cpp * Profile containing a name list for source for batch import. * * \b Project: Kid3 * \author Urs Fleisch * \date 2 Jan 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "batchimportprofile.h" #include <QStringList> /** * Constructor. */ BatchImportProfile::BatchImportProfile() { } /** * Destructor. */ BatchImportProfile::~BatchImportProfile() { } /** * Restore batch import sources from serialized string. * @param str string representation of import sources */ void BatchImportProfile::setSourcesFromString(const QString& str) { m_sources.clear(); if (!str.isEmpty()) { QStringList srcStrs = str.split(QLatin1Char(';')); foreach (const QString& srcStr, srcStrs) { QStringList propStrs = srcStr.split(QLatin1Char(':')); Source src; if (propStrs.size() > 0) src.setName(propStrs.at(0)); if (propStrs.size() > 1) src.setRequiredAccuracy(propStrs.at(1).toInt()); if (propStrs.size() > 2) { const QString& enableStr = propStrs.at(2); src.enableStandardTags(enableStr.contains(QLatin1Char('S'))); src.enableAdditionalTags(enableStr.contains(QLatin1Char('A'))); src.enableCoverArt(enableStr.contains(QLatin1Char('C'))); } m_sources.append(src); } } } /** * Serialize batch import sources as a string. * @return string representation of import sources. */ QString BatchImportProfile::getSourcesAsString() const { QStringList strs; foreach (const Source& src, m_sources) { QString enableStr; if (src.standardTagsEnabled()) enableStr += QLatin1Char('S'); if (src.additionalTagsEnabled()) enableStr += QLatin1Char('A'); if (src.coverArtEnabled()) enableStr += QLatin1Char('C'); strs.append(src.getName() + QLatin1Char(':') + QString::number(src.getRequiredAccuracy()) + QLatin1Char(':') + enableStr); } return strs.join(QLatin1String(";")); } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/core/config/batchimportprofile.h�����������������������������������������������������0000664�0000000�0000000�00000010724�12246035113�0021401�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \file batchimportprofile.h * Profile containing a name list for source for batch import. * * \b Project: Kid3 * \author Urs Fleisch * \date 2 Jan 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #ifndef BATCHIMPORTPROFILE_H #define BATCHIMPORTPROFILE_H #include <QString> #include <QList> #include "kid3api.h" /** * Profile containing a name list for source for batch import. */ class KID3_CORE_EXPORT BatchImportProfile { public: /** Events occurring during batch import. */ enum ImportEventType { ReadingDirectory, Started, SourceSelected, QueryingAlbumList, FetchingTrackList, TrackListReceived, FetchingCoverArt, CoverArtReceived, Finished, Aborted, Error }; /** * Properties of a source used during batch import. */ class Source { public: /** * Constructor. */ Source() : m_accuracy(0), m_standardTags(false), m_additionalTags(false), m_coverArt(false) { } /** * Destructor. */ ~Source() {} /** * Get name. * @return name. */ QString getName() const { return m_name; } /** * Set name. * @param name name */ void setName(const QString& name) { m_name = name; } /** * Get required accuracy. * An import will only be applied if at least the given accuracy is reached. * @return accuracy. */ int getRequiredAccuracy() const { return m_accuracy; } /** * Set required accuracy. * @param accuracy accuracy */ void setRequiredAccuracy(int accuracy) { m_accuracy = accuracy; } /** * Check if standard tags are fetched from this source. * @return true if standard tags are fetched. */ bool standardTagsEnabled() const { return m_standardTags; } /** * Enable fetching of standard tags from this source. * @param enable true to fetch standard tags */ void enableStandardTags(bool enable) { m_standardTags = enable; } /** * Check if additional tags are fetched from this source. * @return true if additional tags are fetched. */ bool additionalTagsEnabled() const { return m_additionalTags; } /** * Enable fetching of additional tags from this source. * @param enable true to fetch additional tags */ void enableAdditionalTags(bool enable) { m_additionalTags = enable; } /** * Check if cover art is fetched from this source. * @return true if cover art is fetched. */ bool coverArtEnabled() const { return m_coverArt; } /** * Enable fetching of cover art from this source. * @param enable true to fetch cover art */ void enableCoverArt(bool enable) { m_coverArt = enable; } private: QString m_name; int m_accuracy; bool m_standardTags; bool m_additionalTags; bool m_coverArt; }; /** * Constructor. */ BatchImportProfile(); /** * Destructor. */ ~BatchImportProfile(); /** * Get name. * @return name. */ QString getName() const { return m_name; } /** * Set name. * @param name name */ void setName(const QString& name) { m_name = name; } /** * Set import sources used by this batch. * @param sources import sources */ void setSources(const QList<Source>& sources) { m_sources = sources; } /** * Get import sources used by this batch. * @return sources. */ const QList<Source>& getSources() const { return m_sources; } /** * Restore batch import sources from serialized string. * @param str string representation of import sources */ void setSourcesFromString(const QString& str); /** * Serialize batch import sources as a string. * @return string representation of import sources. */ QString getSourcesAsString() const; private: QString m_name; QList<Source> m_sources; }; #endif // BATCHIMPORTPROFILE_H ��������������������������������������������kid3-3.0.2/src/core/config/batchimportsourcesmodel.cpp����������������������������������������������0000664�0000000�0000000�00000017056�12246035113�0023005�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \file batchimportsourcesmodel.cpp * Context menu commands configuration table model. * * \b Project: Kid3 * \author Urs Fleisch * \date 2 Jan 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "batchimportsourcesmodel.h" /** Column indices. */ enum ColumnIndex { CI_Name, CI_Accuracy, CI_StandardTags, CI_AdditionalTags, CI_CoverArt, CI_NumColumns }; /** * Constructor. * @param parent parent widget */ BatchImportSourcesModel::BatchImportSourcesModel(QObject* parent) : QAbstractTableModel(parent) { setObjectName(QLatin1String("BatchImportSourcesModel")); } /** * Destructor. */ BatchImportSourcesModel::~BatchImportSourcesModel() { } /** * Get item flags for index. * @param index model index * @return item flags */ Qt::ItemFlags BatchImportSourcesModel::flags(const QModelIndex& index) const { Qt::ItemFlags theFlags = QAbstractTableModel::flags(index); if (index.isValid()) { theFlags |= Qt::ItemIsSelectable | Qt::ItemIsEnabled; if (index.column() == CI_StandardTags || index.column() == CI_AdditionalTags || index.column() == CI_CoverArt) { theFlags |= Qt::ItemIsUserCheckable; } } return theFlags; } /** * Get data for a given role. * @param index model index * @param role item data role * @return data for role */ QVariant BatchImportSourcesModel::data(const QModelIndex& index, int role) const { if (!index.isValid() || index.row() < 0 || index.row() >= m_sources.size() || index.column() < 0 || index.column() >= CI_NumColumns) return QVariant(); const BatchImportProfile::Source& item = m_sources.at(index.row()); if (role == Qt::DisplayRole || role == Qt::EditRole) { switch (index.column()) { case CI_Name: return item.getName(); case CI_Accuracy: return item.getRequiredAccuracy(); default: ; } } if (role == Qt::CheckStateRole) { switch (index.column()) { case CI_StandardTags: return item.standardTagsEnabled() ? Qt::Checked : Qt::Unchecked; case CI_AdditionalTags: return item.additionalTagsEnabled() ? Qt::Checked : Qt::Unchecked; case CI_CoverArt: return item.coverArtEnabled() ? Qt::Checked : Qt::Unchecked; default: ; } } return QVariant(); } /** * Set data for a given role. * @param index model index * @param value data value * @param role item data role * @return true if successful */ bool BatchImportSourcesModel::setData(const QModelIndex& index, const QVariant& value, int role) { if (!index.isValid() || index.row() < 0 || index.row() >= m_sources.size() || index.column() < 0 || index.column() >= CI_NumColumns) return false; BatchImportProfile::Source& item = m_sources[index.row()]; if (role == Qt::EditRole) { switch (index.column()) { case CI_Name: item.setName(value.toString()); break; case CI_Accuracy: item.setRequiredAccuracy(value.toInt()); break; default: return false; } } else if (role == Qt::CheckStateRole) { switch (index.column()) { case CI_StandardTags: item.enableStandardTags(value.toInt() == Qt::Checked); break; case CI_AdditionalTags: item.enableAdditionalTags(value.toInt() == Qt::Checked); break; case CI_CoverArt: item.enableCoverArt(value.toInt() == Qt::Checked); break; default: return false; } } else { return false; } emit dataChanged(index, index); return true; } /** * Get data for header section. * @param section column or row * @param orientation horizontal or vertical * @param role item data role * @return header data for role */ QVariant BatchImportSourcesModel::headerData( int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole) return QVariant(); if (orientation == Qt::Horizontal) { switch (section) { case CI_Name: return tr("Server"); case CI_Accuracy: return tr("Accuracy"); case CI_StandardTags: return tr("Standard Tags"); case CI_AdditionalTags: return tr("Additional Tags"); case CI_CoverArt: return tr("Cover Art"); default: return section + 1; } } return section + 1; } /** * Get number of rows. * @param parent parent model index, invalid for table models * @return number of rows, * if parent is valid number of children (0 for table models) */ int BatchImportSourcesModel::rowCount(const QModelIndex& parent) const { return parent.isValid() ? 0 : m_sources.size(); } /** * Get number of columns. * @param parent parent model index, invalid for table models * @return number of columns, * if parent is valid number of children (0 for table models) */ int BatchImportSourcesModel::columnCount(const QModelIndex& parent) const { return parent.isValid() ? 0 : CI_NumColumns; } /** * Insert rows. * @param row rows are inserted before this row, if 0 at the begin, * if rowCount() at the end * @param count number of rows to insert * @return true if successful */ bool BatchImportSourcesModel::insertRows(int row, int count, const QModelIndex&) { beginInsertRows(QModelIndex(), row, row + count - 1); for (int i = 0; i < count; ++i) m_sources.insert(row, BatchImportProfile::Source()); endInsertRows(); return true; } /** * Remove rows. * @param row rows are removed starting with this row * @param count number of rows to remove * @return true if successful */ bool BatchImportSourcesModel::removeRows(int row, int count, const QModelIndex&) { beginRemoveRows(QModelIndex(), row, row + count - 1); for (int i = 0; i < count; ++i) m_sources.removeAt(row); endRemoveRows(); return true; } /** * Set batch import source of a given @a row. * @param row number of row to set * @param source batch import source */ void BatchImportSourcesModel::setBatchImportSource( int row, const BatchImportProfile::Source& source) { if (row >= 0 && row < m_sources.size()) { m_sources[row] = source; emit dataChanged(index(row, 0), index(row, CI_NumColumns - 1)); } } /** * Get batch import source of a given @a row. * @param row number of row to get * @param source the batch import source is returned here */ void BatchImportSourcesModel::getBatchImportSource( int row, BatchImportProfile::Source& source) { if (row >= 0 && row < m_sources.size()) { source = m_sources.at(row); } } /** * Set the model from the import sources. * @param sources batch import sources */ void BatchImportSourcesModel::setBatchImportSources( const QList<BatchImportProfile::Source>& sources) { #if QT_VERSION >= 0x040600 beginResetModel(); m_sources = sources; endResetModel(); #else m_sources = sources; reset(); #endif } /** * Get the import sources from the model. * @return batch import sources. */ QList<BatchImportProfile::Source> BatchImportSourcesModel::getBatchImportSources() const { return m_sources; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/core/config/batchimportsourcesmodel.h������������������������������������������������0000664�0000000�0000000�00000011245�12246035113�0022444�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \file batchimportsourcesmodel.h * Context menu commands configuration table model. * * \b Project: Kid3 * \author Urs Fleisch * \date 2 Jan 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #ifndef BATCHIMPORTSOURCESMODEL_H #define BATCHIMPORTSOURCESMODEL_H #include <QAbstractTableModel> #include <QHeaderView> #include "batchimportprofile.h" #include "kid3api.h" /** * Context menu commands configuration table model. */ class KID3_CORE_EXPORT BatchImportSourcesModel : public QAbstractTableModel { Q_OBJECT public: /** * Constructor. * @param parent parent widget */ explicit BatchImportSourcesModel(QObject* parent = 0); /** * Destructor. */ virtual ~BatchImportSourcesModel(); /** * Get item flags for index. * @param index model index * @return item flags */ virtual Qt::ItemFlags flags(const QModelIndex& index) const; /** * Get data for a given role. * @param index model index * @param role item data role * @return data for role */ virtual QVariant data(const QModelIndex& index, int role=Qt::DisplayRole) const; /** * Set data for a given role. * @param index model index * @param value data value * @param role item data role * @return true if successful */ virtual bool setData(const QModelIndex& index, const QVariant& value, int role=Qt::EditRole); /** * Get data for header section. * @param section column or row * @param orientation horizontal or vertical * @param role item data role * @return header data for role */ virtual QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const; /** * Set data for header section. * Not supported. * @return false */ virtual bool setHeaderData(int, Qt::Orientation, const QVariant&, int=Qt::EditRole) { return false; } /** * Get number of rows. * @param parent parent model index, invalid for table models * @return number of rows, * if parent is valid number of children (0 for table models) */ virtual int rowCount(const QModelIndex& parent=QModelIndex()) const; /** * Get number of columns. * @param parent parent model index, invalid for table models * @return number of columns, * if parent is valid number of children (0 for table models) */ virtual int columnCount(const QModelIndex& parent=QModelIndex()) const; /** * Insert rows. * @param row rows are inserted before this row, if 0 at the begin, * if rowCount() at the end * @param count number of rows to insert * @param parent parent model index, invalid for table models * @return true if successful */ virtual bool insertRows(int row, int count, const QModelIndex& parent=QModelIndex()); /** * Remove rows. * @param row rows are removed starting with this row * @param count number of rows to remove * @param parent parent model index, invalid for table models * @return true if successful */ virtual bool removeRows(int row, int count, const QModelIndex& parent=QModelIndex()); /** * Set batch import source of a given @a row. * @param row number of row to set * @param source batch import source */ void setBatchImportSource(int row, const BatchImportProfile::Source& source); /** * Get batch import source of a given @a row. * @param row number of row to get * @param source the batch import source is returned here */ void getBatchImportSource(int row, BatchImportProfile::Source& source); /** * Set the model from the import sources. * @param sources batch import sources */ void setBatchImportSources(const QList<BatchImportProfile::Source>& sources); /** * Get the import sources from the model. * @return batch import sources. */ QList<BatchImportProfile::Source> getBatchImportSources() const; private: QList<BatchImportProfile::Source> m_sources; }; #endif // BATCHIMPORTSOURCESMODEL_H �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/core/config/configstore.cpp����������������������������������������������������������0000664�0000000�0000000�00000003401�12246035113�0020353�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \file configstore.cpp * Configuration storage. * * \b Project: Kid3 * \author Urs Fleisch * \date 09 Jul 2011 * * Copyright (C) 2011-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "configstore.h" #include "generalconfig.h" ConfigStore* ConfigStore::s_self = 0; /** * Constructor. * @param config application settings */ ConfigStore::ConfigStore(ISettings* config) : m_config(config) { Q_ASSERT_X(!s_self, "ConfigStore", "there should be only one config store"); s_self = this; } /** * Destructor. */ ConfigStore::~ConfigStore() { qDeleteAll(m_configurations); } /** * Persist all added configurations. */ void ConfigStore::writeToConfig() { foreach (GeneralConfig* cfg, m_configurations) { cfg->writeToConfig(m_config); } } /** * Add a configuration. * The configuration will be read from the application settings. * * @param cfg configuration, ownership is taken * @return index of configuration. */ int ConfigStore::addConfiguration(GeneralConfig* cfg) { Q_ASSERT(cfg); int index = m_configurations.size(); m_configurations.append(cfg); cfg->readFromConfig(m_config); return index; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/core/config/configstore.h������������������������������������������������������������0000664�0000000�0000000�00000004010�12246035113�0020015�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \file configstore.h * Configuration storage. * * \b Project: Kid3 * \author Urs Fleisch * \date 09 Jul 2011 * * Copyright (C) 2011 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #ifndef CONFIGSTORE_H #define CONFIGSTORE_H #include <QList> #include "kid3api.h" class ISettings; class GeneralConfig; /** * Configuration storage. */ class KID3_CORE_EXPORT ConfigStore { public: /** * Constructor. * @param config application settings */ explicit ConfigStore(ISettings* config); /** * Destructor. */ ~ConfigStore(); /** * Persist all added configurations. */ void writeToConfig(); /** * Add a configuration. * The configuration will be read from the application settings. * * @param cfg configuration, ownership is taken * @return index of configuration. */ int addConfiguration(GeneralConfig* cfg); /** * Access to configuration. * @param index index of configuration * @return configuration, 0 if not found. */ GeneralConfig* configuration(int index) const { return m_configurations.at(index); } /** * Get a pointer to the application's config store instance. * @return config store, 0 if no instance has been allocated. */ static ConfigStore* instance() { return s_self; } private: ISettings* m_config; QList<GeneralConfig*> m_configurations; static ConfigStore* s_self; }; #endif // CONFIGSTORE_H ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kid3-3.0.2/src/core/config/exportconfig.cpp���������������������������������������������������������0000664�0000000�0000000�00000024245�12246035113�0020551�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \file exportconfig.cpp * Configuration for export dialog. * * \b Project: Kid3 * \author Urs Fleisch * \date 30 Jun 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "exportconfig.h" int ExportConfig::s_index = -1; /** * Constructor. */ ExportConfig::ExportConfig() : StoredConfig<ExportConfig>(QLatin1String("Export")), m_exportSrcV1(TrackData::TagV1), m_exportFormatIdx(0) { m_exportFormatNames.append(QLatin1String("CSV unquoted")); m_exportFormatHeaders.append(QLatin1String("")); m_exportFormatTracks.append(QLatin1String("%{track}\\t%{title}\\t%{artist}\\t%{album}\\t%{year}\\t%{genre}\\t%{comment}\\t%{duration}.00")); m_exportFormatTrailers.append(QLatin1String("")); m_exportFormatNames.append(QLatin1String("CSV quoted")); m_exportFormatHeaders.append(QLatin1String("")); m_exportFormatTracks.append(QLatin1String("\"%{track}\"\\t\"%{title}\"\\t\"%{artist}\"\\t\"%{album}\"\\t\"%{year}\"\\t\"%{genre}\"\\t\"%{comment}\"\\t\"%{duration}.00\"")); m_exportFormatTrailers.append(QLatin1String("")); m_exportFormatNames.append(QLatin1String("CSV more unquoted")); m_exportFormatHeaders.append(QLatin1String( "Track\\tTitle\\tArtist\\tAlbum\\tDate\\tGenre\\tComment\\tDuration\\t" "Album Artist\\tArranger\\tAuthor\\tBPM\\tComposer\\t" "Conductor\\tCopyright\\tDisc Number\\tEncoded-by\\tGrouping\\tISRC\\t" "Language\\tLyricist\\tLyrics\\tMedia\\tOriginal Album\\t" "Original Artist\\tOriginal Date\\tPart\\tPerformer\\t" "Publisher\\tRemixer\\tSubtitle\\tWebsite")); m_exportFormatTracks.append(QLatin1String( "%{track}\\t%{title}\\t%{artist}\\t%{album}\\t%{year}\\t%{genre}\\t%{comment}\\t" "%{duration}.00\\t" "%{album artist}\\t%{arranger}\\t%{author}\\t%{bpm}\\t%{composer}\\t" "%{conductor}\\t%{copyright}\\t%{disc number}\\t%{encoded-by}\\t%{grouping}\\t%{isrc}\\t" "%{language}\\t%{lyricist}\\t%{lyrics}\\t%{media}\\t%{original album}\\t" "%{original artist}\\t%{original date}\\t%{part}\\t%{performer}\\t" "%{publisher}\\t%{remixer}\\t%{subtitle}\\t%{website}")); m_exportFormatTrailers.append(QLatin1String("")); m_exportFormatNames.append(QLatin1String("CSV more quoted")); m_exportFormatHeaders.append(QLatin1String( "\"Track\"\\t\"Title\"\\t\"Artist\"\\t\"Album\"\\t\"Date\"\\t" "\"Genre\"\\t\"Comment\"\\t\"Duration\"\\t" "\"Album Artist\"\\t\"Arranger\"\\t\"Author\"\\t\"BPM\"\\t" "\"Composer\"\\t\"Conductor\"\\t\"Copyright\"\\t\"Disc Number\"\\t" "\"Encoded-by\"\\t\"Grouping\"\\t\"ISRC\"\\t\"Language\"\\t\"Lyricist\"\\t\"Lyrics\"\\t" "\"Media\"\\t\"Original Album\"\\t\"Original Artist\"\\t" "\"Original Date\"\\t\"Part\"\\t\"Performer\"\\t\"Publisher\"\\t" "\"Remixer\"\\t\"Subtitle\"\\t\"Website\"")); m_exportFormatTracks.append(QLatin1String( "\"%{track}\"\\t\"%{title}\"\\t\"%{artist}\"\\t\"%{album}\"\\t\"%{year}\"\\t" "\"%{genre}\"\\t\"%{comment}\"\\t\"%{duration}.00\"\\t" "\"%{album artist}\"\\t\"%{arranger}\"\\t\"%{author}\"\\t\"%{bpm}\"\\t" "\"%{composer}\"\\t\"%{conductor}\"\\t\"%{copyright}\"\\t\"%{disc number}\"\\t" "\"%{encoded-by}\"\\t\"%{grouping}\"\\t\"%{isrc}\"\\t\"%{language}\"\\t\"%{lyricist}\"\\t\"%{lyrics}\"\\t" "\"%{media}\"\\t\"%{original album}\"\\t\"%{original artist}\"\\t" "\"%{original date}\"\\t\"%{part}\"\\t\"%{performer}\"\\t\"%{publisher}\"\\t" "\"%{remixer}\"\\t\"%{subtitle}\"\\t\"%{website}\"")); m_exportFormatTrailers.append(QLatin1String("")); m_exportFormatNames.append(QLatin1String("Extended M3U")); m_exportFormatHeaders.append(QLatin1String("#EXTM3U")); m_exportFormatTracks.append(QLatin1String("#EXTINF:%{seconds},%{artist} - %{title}\\n%{filepath}")); m_exportFormatTrailers.append(QLatin1String("")); m_exportFormatNames.append(QLatin1String("Extended PLS")); m_exportFormatHeaders.append(QLatin1String("[playlist]")); m_exportFormatTracks.append(QLatin1String("File%{tracknumber}=%{filepath}\\nTitle%{tracknumber}=%{artist} - %{title}\\nLength%{tracknumber}=%{seconds}")); m_exportFormatTrailers.append(QLatin1String("NumberOfEntries=%{tracks}\\nVersion=2")); m_exportFormatNames.append(QLatin1String("HTML")); m_exportFormatHeaders.append(QLatin1String("<html>\\n <head>\\n <title>%h{artist} - %h{album}\\n \\n \\n

%h{artist} - %h{album}

\\n
")); m_exportFormatTracks.append(QLatin1String("
%h{track}. %h{title}
")); m_exportFormatTrailers.append(QLatin1String("
\\n \\n")); m_exportFormatNames.append(QLatin1String("Kover XML")); m_exportFormatHeaders.append(QLatin1String("\\n \\n <text><![CDATA[%{artist} ]]></text>\\n <text><![CDATA[%{album}]]></text>\\n \\n ")); m_exportFormatTracks.append(QLatin1String(" ")); m_exportFormatTrailers.append(QLatin1String(" \\n")); m_exportFormatNames.append(QLatin1String("Technical Details")); m_exportFormatHeaders.append(QLatin1String("File\\tBitrate\\tVBR\\tDuration\\tSamplerate\\tChannels\\tMode\\tCodec")); m_exportFormatTracks.append(QLatin1String("%{file}\\t%{bitrate}\\t%{vbr}\\t%{duration}\\t%{samplerate}\\t%{channels}\\t%{mode}\\t%{codec}")); m_exportFormatTrailers.append(QLatin1String("")); m_exportFormatNames.append(QLatin1String("Custom Format")); m_exportFormatHeaders.append(QLatin1String("")); m_exportFormatTracks.append(QLatin1String("")); m_exportFormatTrailers.append(QLatin1String("")); } /** * Destructor. */ ExportConfig::~ExportConfig() {} /** * Persist configuration. * * @param config configuration */ void ExportConfig::writeToConfig(ISettings* config) const { config->beginGroup(m_group); config->setValue(QLatin1String("ExportSourceV1"), QVariant(m_exportSrcV1 == TrackData::TagV1)); config->setValue(QLatin1String("ExportFormatNames"), QVariant(m_exportFormatNames)); config->setValue(QLatin1String("ExportFormatHeaders"), QVariant(m_exportFormatHeaders)); config->setValue(QLatin1String("ExportFormatTracks"), QVariant(m_exportFormatTracks)); config->setValue(QLatin1String("ExportFormatTrailers"), QVariant(m_exportFormatTrailers)); config->setValue(QLatin1String("ExportFormatIdx"), QVariant(m_exportFormatIdx)); config->setValue(QLatin1String("ExportWindowGeometry"), QVariant(m_exportWindowGeometry)); config->endGroup(); } /** * Read persisted configuration. * * @param config configuration */ void ExportConfig::readFromConfig(ISettings* config) { QStringList expNames, expHeaders, expTracks, expTrailers; config->beginGroup(m_group); m_exportSrcV1 = config->value(QLatin1String("ExportSourceV1"), m_exportSrcV1 == TrackData::TagV1).toBool() ? TrackData::TagV1 : TrackData::TagV2; expNames = config->value(QLatin1String("ExportFormatNames"), m_exportFormatNames).toStringList(); expHeaders = config->value(QLatin1String("ExportFormatHeaders"), m_exportFormatHeaders).toStringList(); expTracks = config->value(QLatin1String("ExportFormatTracks"), m_exportFormatTracks).toStringList(); expTrailers = config->value(QLatin1String("ExportFormatTrailers"), m_exportFormatTrailers).toStringList(); m_exportFormatIdx = config->value(QLatin1String("ExportFormatIdx"), m_exportFormatIdx).toInt(); m_exportWindowGeometry = config->value(QLatin1String("ExportWindowGeometry"), m_exportWindowGeometry).toByteArray(); config->endGroup(); // KConfig seems to strip empty entries from the end of the string lists, // so we have to append them again. unsigned numExpNames = expNames.size(); while (static_cast(expHeaders.size()) < numExpNames) expHeaders.append(QLatin1String("")); while (static_cast(expTracks.size()) < numExpNames) expTracks.append(QLatin1String("")); while (static_cast(expTrailers.size()) < numExpNames) expTrailers.append(QLatin1String("")); QStringList::const_iterator expNamesIt, expHeadersIt, expTracksIt, expTrailersIt; for (expNamesIt = expNames.begin(), expHeadersIt = expHeaders.begin(), expTracksIt = expTracks.begin(), expTrailersIt = expTrailers.begin(); expNamesIt != expNames.end() && expHeadersIt != expHeaders.end() && expTracksIt != expTracks.end() && expTrailersIt != expTrailers.end(); ++expNamesIt, ++expHeadersIt, ++expTracksIt, ++expTrailersIt) { int idx = m_exportFormatNames.indexOf(*expNamesIt); if (idx >= 0) { m_exportFormatHeaders[idx] = *expHeadersIt; m_exportFormatTracks[idx] = *expTracksIt; m_exportFormatTrailers[idx] = *expTrailersIt; } else if (!(*expNamesIt).isEmpty()) { m_exportFormatNames.append(*expNamesIt); m_exportFormatHeaders.append(*expHeadersIt); m_exportFormatTracks.append(*expTracksIt); m_exportFormatTrailers.append(*expTrailersIt); } } if (m_exportFormatIdx >= static_cast(m_exportFormatNames.size())) m_exportFormatIdx = 0; // Use HTML escaping for old HTML export format. int htmlIdx = m_exportFormatNames.indexOf(QLatin1String("HTML")); if (htmlIdx != -1) { if (m_exportFormatHeaders.at(htmlIdx) == QLatin1String("\\n \\n %{artist} - %{album}\\n \\n \\n

%{artist} - %{album}

\\n
")) { m_exportFormatHeaders[htmlIdx] = QLatin1String("\\n \\n %h{artist} - %h{album}\\n \\n \\n

%h{artist} - %h{album}

\\n
"); } if (m_exportFormatTracks.at(htmlIdx) == QLatin1String("
%{track}. %{title}
")) { m_exportFormatTracks[htmlIdx] = QLatin1String("
%h{track}. %h{title}
"); } } } kid3-3.0.2/src/core/config/exportconfig.h000066400000000000000000000040261224603511300202110ustar00rootroot00000000000000/** * \file exportconfig.h * Configuration for export dialog. * * \b Project: Kid3 * \author Urs Fleisch * \date 30 Jun 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef EXPORTCONFIG_H #define EXPORTCONFIG_H #include #include "generalconfig.h" #include "trackdata.h" /** * Export configuration. */ class KID3_CORE_EXPORT ExportConfig : public StoredConfig { public: /** * Constructor. */ ExportConfig(); /** * Destructor. */ virtual ~ExportConfig(); /** * Persist configuration. * * @param config configuration */ virtual void writeToConfig(ISettings* config) const; /** * Read persisted configuration. * * @param config configuration */ virtual void readFromConfig(ISettings* config); /** Tag1 to export ID3v1 tags, Tag2 for ID3v2 tags */ TrackData::TagVersion m_exportSrcV1; /** Names of export formats */ QStringList m_exportFormatNames; /** regexp describing header export format */ QStringList m_exportFormatHeaders; /** regexp describing track export format */ QStringList m_exportFormatTracks; /** regexp describing trailer export format */ QStringList m_exportFormatTrailers; /** selected export format */ int m_exportFormatIdx; /** export window geometry */ QByteArray m_exportWindowGeometry; /** Index in configuration storage */ static int s_index; }; #endif kid3-3.0.2/src/core/config/fileconfig.cpp000066400000000000000000000136041224603511300201440ustar00rootroot00000000000000/** * \file fileconfig.cpp * File related configuration. * * \b Project: Kid3 * \author Urs Fleisch * \date 29 Jun 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "fileconfig.h" int FileConfig::s_index = -1; /** Default to filename format list */ static const char* defaultToFilenameFormats[] = { "%{track} %{title}", "%{track}. %{title}", "%{track} - %{artist} - %{title}", "%{track}. %{artist} - %{title}", "%{artist} - %{track} - %{title}", "%{artist} - %{album} - %{track} - %{title}", "%{artist} - [%{year}] %{album} - %{track} - %{title}", "%{artist} - %{title}", "%{artist}-%{title}", "(%{artist}) %{title}", "%{artist}-%{title}-%{album}", 0 }; /** Default from filename format list */ static const char* defaultFromFilenameFormats[] = { "%{artist} - %{album}/%{track} %{title}", "%{artist} - %{album}/%{track}. %{title}", "%{artist} - [%{year}] %{album}/%{track} %{title}", "%{artist} - [%{year}] %{album}/%{track}. %{title}", "%{artist} - %{album} (%{year})/%{track} - %{title}", "%{artist}/%{album}/%{track} %{title}", "%{artist}/%{album}/%{track}. %{title}", "%{artist}/[%{year}] %{album}/%{track} %{title}", "%{artist}/[%{year}] %{album}/%{track}. %{title}", "%{album}/%{track} - %{artist} - %{title}", "%{album}/%{track}. %{artist} - %{title}", "%{album}/%{artist} - %{track} - %{title}", "[%{year}] %{album}/%{track} - %{artist} - %{title}", "%{artist} - %{album} - %{track} - %{title}", "%{artist} - [%{year}] %{album} - %{track} - %{title}", "%{album}/%{artist} - %{track} - %{title}", "[%{year}] %{album}/%{artist} - %{track} - %{title}", "%{album}/%{artist} - %{title}", "%{album}/%{artist}-%{title}", "%{album}/(%{artist}) %{title}", "%{artist}-%{title}-%{album}", 0 }; /** * Constructor. */ FileConfig::FileConfig() : StoredConfig(QLatin1String("Files")), m_preserveTime(false), m_markChanges(true), m_nameFilter(QLatin1String("")), m_formatText(QString::fromLatin1(defaultToFilenameFormats[0])), m_formatItem(0), m_formatFromFilenameText(QString::fromLatin1(defaultFromFilenameFormats[0])), m_formatFromFilenameItem(0), m_defaultCoverFileName(QLatin1String("folder.jpg")), m_loadLastOpenedFile(false) { } /** * Destructor. */ FileConfig::~FileConfig() {} /** * Persist configuration. * * @param config configuration */ void FileConfig::writeToConfig(ISettings* config) const { config->beginGroup(m_group); config->setValue(QLatin1String("NameFilter"), QVariant(m_nameFilter)); config->setValue(QLatin1String("FormatItem"), QVariant(m_formatItem)); config->setValue(QLatin1String("FormatItems"), QVariant(m_formatItems)); config->setValue(QLatin1String("FormatText"), QVariant(m_formatText)); config->setValue(QLatin1String("FormatFromFilenameItem"), QVariant(m_formatFromFilenameItem)); config->setValue(QLatin1String("FormatFromFilenameItems"), QVariant(m_formatFromFilenameItems)); config->setValue(QLatin1String("FormatFromFilenameText"), QVariant(m_formatFromFilenameText)); config->setValue(QLatin1String("PreserveTime"), QVariant(m_preserveTime)); config->setValue(QLatin1String("MarkChanges"), QVariant(m_markChanges)); config->setValue(QLatin1String("LoadLastOpenedFile"), QVariant(m_loadLastOpenedFile)); config->setValue(QLatin1String("LastOpenedFile"), QVariant(m_lastOpenedFile)); config->setValue(QLatin1String("DefaultCoverFileName"), QVariant(m_defaultCoverFileName)); config->endGroup(); } /** * Read persisted configuration. * * @param config configuration */ void FileConfig::readFromConfig(ISettings* config) { config->beginGroup(m_group); m_nameFilter = config->value(QLatin1String("NameFilter"), QLatin1String("")).toString(); m_formatItem = config->value(QLatin1String("FormatItem"), 0).toInt(); m_formatItems = config->value(QLatin1String("FormatItems"), m_formatItems).toStringList(); m_formatFromFilenameItem = config->value(QLatin1String("FormatFromFilenameItem"), 0).toInt(); m_formatFromFilenameItems = config->value(QLatin1String("FormatFromFilenameItems"), m_formatFromFilenameItems).toStringList(); m_preserveTime = config->value(QLatin1String("PreserveTime"), m_preserveTime).toBool(); m_markChanges = config->value(QLatin1String("MarkChanges"), m_markChanges).toBool(); m_formatText = config->value(QLatin1String("FormatText"), QString::fromLatin1(defaultToFilenameFormats[0])).toString(); m_formatFromFilenameText = config->value(QLatin1String("FormatFromFilenameText"), QString::fromLatin1(defaultFromFilenameFormats[0])).toString(); m_loadLastOpenedFile = config->value(QLatin1String("LoadLastOpenedFile"), m_loadLastOpenedFile).toBool(); m_lastOpenedFile = config->value(QLatin1String("LastOpenedFile"), m_lastOpenedFile).toString(); m_defaultCoverFileName = config->value(QLatin1String("DefaultCoverFileName"), m_defaultCoverFileName).toString(); config->endGroup(); if (m_formatItems.isEmpty()) { for (const char** sl = defaultToFilenameFormats; *sl != 0; ++sl) { m_formatItems += QString::fromLatin1(*sl); } } if (m_formatFromFilenameItems.isEmpty()) { for (const char** sl = defaultFromFilenameFormats; *sl != 0; ++sl) { m_formatFromFilenameItems += QString::fromLatin1(*sl); } } } kid3-3.0.2/src/core/config/fileconfig.h000066400000000000000000000044041224603511300176070ustar00rootroot00000000000000/** * \file fileconfig.h * File related configuration. * * \b Project: Kid3 * \author Urs Fleisch * \date 29 Jun 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef FILECONFIG_H #define FILECONFIG_H #include #include "generalconfig.h" #include "kid3api.h" /** * File related configuration. */ class KID3_CORE_EXPORT FileConfig : public StoredConfig { public: /** * Constructor. */ FileConfig(); /** * Destructor. */ virtual ~FileConfig(); /** * Persist configuration. * * @param config configuration */ virtual void writeToConfig(ISettings* config) const; /** * Read persisted configuration. * * @param config configuration */ virtual void readFromConfig(ISettings* config); /** true to preserve file time stamps */ bool m_preserveTime; /** true to mark changed fields */ bool m_markChanges; /** filter of file names to be opened */ QString m_nameFilter; /** filename format */ QString m_formatText; /** index of filename format selected */ int m_formatItem; /** filename formats */ QStringList m_formatItems; /** from filename format */ QString m_formatFromFilenameText; /** index of from filename format selected */ int m_formatFromFilenameItem; /** from filename formats */ QStringList m_formatFromFilenameItems; /** default file name to save cover art */ QString m_defaultCoverFileName; /** true to open last opened file on startup */ bool m_loadLastOpenedFile; /** path to last opened file */ QString m_lastOpenedFile; /** Index in configuration storage */ static int s_index; }; #endif kid3-3.0.2/src/core/config/filterconfig.cpp000066400000000000000000000121071224603511300205070ustar00rootroot00000000000000/** * \file filterconfig.cpp * Configuration for filter dialog. * * \b Project: Kid3 * \author Urs Fleisch * \date 16 Jan 2008 * * Copyright (C) 2008-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "filterconfig.h" #include #include "config.h" int FilterConfig::s_index = -1; /** * Constructor. */ FilterConfig::FilterConfig() : StoredConfig(QLatin1String("Filter")), m_filterIdx(0) { /** * Preset filter expressions. */ m_filterNames << QLatin1String("All") << QLatin1String("Filename Tag Mismatch") << QLatin1String("No Tag 1") << QLatin1String("No Tag 2") << QLatin1String("ID3v2.2.0 Tag") << QLatin1String("ID3v2.3.0 Tag") << QLatin1String("ID3v2.4.0 Tag") << QLatin1String("Tag 1 != Tag 2") << QLatin1String("Tag 1 == Tag 2") << QLatin1String("Incomplete") << QLatin1String("No Picture") << QLatin1String("Custom Filter"); m_filterExpressions << QLatin1String("") << QLatin1String("not (%{filepath} contains \"%{artist} - %{album}/%{track} %{title}\")") << QLatin1String("%{tag1} equals \"\"") << QLatin1String("%{tag2} equals \"\"") << QLatin1String("%{tag2} equals \"ID3v2.2.0\"") << QLatin1String("%{tag2} equals \"ID3v2.3.0\"") << QLatin1String("%{tag2} equals \"ID3v2.4.0\"") << QLatin1String("not (%1{title} equals %2{title} and %1{album} equals %2{album} and %1{artist} equals %2{artist} and %1{comment} equals %2{comment} and %1{year} equals %2{year} and %1{track} equals %2{track} and %1{genre} equals %2{genre})") << QLatin1String("%1{title} equals %2{title} and %1{album} equals %2{album} and %1{artist} equals %2{artist} and %1{comment} equals %2{comment} and %1{year} equals %2{year} and %1{track} equals %2{track} and %1{genre} equals %2{genre}") << QLatin1String("%{title} equals \"\" or %{artist} equals \"\" or %{album} equals \"\" or %{year} equals \"\" or %{tracknumber} equals \"\" or %{genre} equals \"\"") << QLatin1String("%{picture} equals \"\"") << QLatin1String(""); } /** * Destructor. */ FilterConfig::~FilterConfig() {} /** * Persist configuration. * * @param config configuration */ void FilterConfig::writeToConfig(ISettings* config) const { config->beginGroup(m_group); config->setValue(QLatin1String("FilterNames"), QVariant(m_filterNames)); config->setValue(QLatin1String("FilterExpressions"), QVariant(m_filterExpressions)); config->setValue(QLatin1String("FilterIdx"), QVariant(m_filterIdx)); config->setValue(QLatin1String("WindowGeometry"), QVariant(m_windowGeometry)); config->endGroup(); } /** * Read persisted configuration. * * @param config configuration */ void FilterConfig::readFromConfig(ISettings* config) { QStringList names, expressions; config->beginGroup(m_group); names = config->value(QLatin1String("FilterNames"), m_filterNames).toStringList(); expressions = config->value(QLatin1String("FilterExpressions"), m_filterExpressions).toStringList(); m_filterIdx = config->value(QLatin1String("FilterIdx"), m_filterIdx).toInt(); m_windowGeometry = config->value(QLatin1String("WindowGeometry"), m_windowGeometry).toByteArray(); config->endGroup(); // KConfig seems to strip empty entries from the end of the string lists, // so we have to append them again. unsigned numNames = names.size(); while (static_cast(expressions.size()) < numNames) expressions.append(QLatin1String("")); /* Use defaults if no configuration found */ QStringList::const_iterator namesIt, expressionsIt; for (namesIt = names.begin(), expressionsIt = expressions.begin(); namesIt != names.end() && expressionsIt != expressions.end(); ++namesIt, ++expressionsIt) { int idx = m_filterNames.indexOf(*namesIt); if (idx >= 0) { m_filterExpressions[idx] = *expressionsIt; } else if (!(*namesIt).isEmpty()) { m_filterNames.append(*namesIt); m_filterExpressions.append(*expressionsIt); } } if (m_filterIdx >= static_cast(m_filterNames.size())) m_filterIdx = 0; } /** * Set the filename format in the "Filename Tag Mismatch" filter. * * @param format filename format */ void FilterConfig::setFilenameFormat(const QString& format) { int idx = m_filterNames.indexOf(QLatin1String("Filename Tag Mismatch")); if (idx != -1) { m_filterExpressions[idx] = QLatin1String("not (%{filepath} contains \"") + format + QLatin1String("\")"); } } kid3-3.0.2/src/core/config/filterconfig.h000066400000000000000000000036371224603511300201640ustar00rootroot00000000000000/** * \file filterconfig.h * Configuration for filter dialog. * * \b Project: Kid3 * \author Urs Fleisch * \date 16 Jan 2008 * * Copyright (C) 2008 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef FILTERCONFIG_H #define FILTERCONFIG_H #include #include "config.h" #include "generalconfig.h" #include "kid3api.h" /** * Filter configuration. */ class KID3_CORE_EXPORT FilterConfig : public StoredConfig { public: /** * Constructor. */ FilterConfig(); /** * Destructor. */ virtual ~FilterConfig(); /** * Persist configuration. * * @param config KDE configuration */ virtual void writeToConfig(ISettings* config) const; /** * Read persisted configuration. * * @param config KDE configuration */ virtual void readFromConfig(ISettings* config); /** * Set the filename format in the "Filename Tag Mismatch" filter. * * @param format filename format */ void setFilenameFormat(const QString& format); /** Names of filter expressions */ QStringList m_filterNames; /** Filter expressions */ QStringList m_filterExpressions; /** Selected filter */ int m_filterIdx; /** Window geometry */ QByteArray m_windowGeometry; /** Index in configuration storage */ static int s_index; }; #endif kid3-3.0.2/src/core/config/formatconfig.cpp000066400000000000000000000260231224603511300205140ustar00rootroot00000000000000/** * \file formatconfig.cpp * Format configuration. * * \b Project: Kid3 * \author Urs Fleisch * \date 17 Sep 2003 * * Copyright (C) 2003-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "formatconfig.h" #include "config.h" #include #include #include #include "generalconfig.h" #include "frame.h" /** * Constructor. */ FormatConfig::FormatConfig(const QString& grp) : GeneralConfig(grp), m_formatWhileEditing(false), m_caseConversion(AllFirstLettersUppercase), m_strRepEnabled(false), m_locale(0), m_filenameFormatter(false) { m_strRepMap.clear(); } /** * Destructor. */ FormatConfig::~FormatConfig() { delete m_locale; } /** * Set specific properties for a filename format. * This will set default string conversions and not touch the file * extension when formatting. */ void FormatConfig::setAsFilenameFormatter() { m_filenameFormatter = true; m_caseConversion = NoChanges; m_localeName = QString(); delete m_locale; m_locale = 0; m_strRepEnabled = true; m_strRepMap[QLatin1String("/")] = QLatin1Char('-'); m_strRepMap[QLatin1String(":")] = QLatin1Char('-'); m_strRepMap[QLatin1String(".")] = QLatin1String(""); m_strRepMap[QLatin1String("?")] = QLatin1String(""); m_strRepMap[QLatin1String("*")] = QLatin1String(""); m_strRepMap[QLatin1String("\"")] = QLatin1String("''"); m_strRepMap[QLatin1String("<")] = QLatin1Char('-'); m_strRepMap[QLatin1String(">")] = QLatin1Char('-'); m_strRepMap[QLatin1String("|")] = QLatin1Char('-'); m_strRepMap[QChar(0xe4)] = QLatin1String("ae"); m_strRepMap[QChar(0xf6)] = QLatin1String("oe"); m_strRepMap[QChar(0xfc)] = QLatin1String("ue"); m_strRepMap[QChar(0xc4)] = QLatin1String("Ae"); m_strRepMap[QChar(0xd6)] = QLatin1String("Oe"); m_strRepMap[QChar(0xdc)] = QLatin1String("Ue"); m_strRepMap[QChar(0xdf)] = QLatin1String("ss"); m_strRepMap[QChar(0xc0)] = QLatin1Char('A'); m_strRepMap[QChar(0xc1)] = QLatin1Char('A'); m_strRepMap[QChar(0xc2)] = QLatin1Char('A'); m_strRepMap[QChar(0xc3)] = QLatin1Char('A'); m_strRepMap[QChar(0xc5)] = QLatin1Char('A'); m_strRepMap[QChar(0xc6)] = QLatin1String("AE"); m_strRepMap[QChar(0xc7)] = QLatin1Char('C'); m_strRepMap[QChar(0xc8)] = QLatin1Char('E'); m_strRepMap[QChar(0xc9)] = QLatin1Char('E'); m_strRepMap[QChar(0xca)] = QLatin1Char('E'); m_strRepMap[QChar(0xcb)] = QLatin1Char('E'); m_strRepMap[QChar(0xcc)] = QLatin1Char('I'); m_strRepMap[QChar(0xcd)] = QLatin1Char('I'); m_strRepMap[QChar(0xce)] = QLatin1Char('I'); m_strRepMap[QChar(0xcf)] = QLatin1Char('I'); m_strRepMap[QChar(0xd0)] = QLatin1Char('D'); m_strRepMap[QChar(0xd1)] = QLatin1Char('N'); m_strRepMap[QChar(0xd2)] = QLatin1Char('O'); m_strRepMap[QChar(0xd3)] = QLatin1Char('O'); m_strRepMap[QChar(0xd4)] = QLatin1Char('O'); m_strRepMap[QChar(0xd5)] = QLatin1Char('O'); m_strRepMap[QChar(0xd7)] = QLatin1Char('x'); m_strRepMap[QChar(0xd8)] = QLatin1Char('O'); m_strRepMap[QChar(0xd9)] = QLatin1Char('U'); m_strRepMap[QChar(0xda)] = QLatin1Char('U'); m_strRepMap[QChar(0xdb)] = QLatin1Char('U'); m_strRepMap[QChar(0xdd)] = QLatin1Char('Y'); m_strRepMap[QChar(0xe0)] = QLatin1Char('a'); m_strRepMap[QChar(0xe1)] = QLatin1Char('a'); m_strRepMap[QChar(0xe2)] = QLatin1Char('a'); m_strRepMap[QChar(0xe3)] = QLatin1Char('a'); m_strRepMap[QChar(0xe5)] = QLatin1Char('a'); m_strRepMap[QChar(0xe6)] = QLatin1String("ae"); m_strRepMap[QChar(0xe7)] = QLatin1Char('c'); m_strRepMap[QChar(0xe8)] = QLatin1Char('e'); m_strRepMap[QChar(0xe9)] = QLatin1Char('e'); m_strRepMap[QChar(0xea)] = QLatin1Char('e'); m_strRepMap[QChar(0xeb)] = QLatin1Char('e'); m_strRepMap[QChar(0xec)] = QLatin1Char('i'); m_strRepMap[QChar(0xed)] = QLatin1Char('i'); m_strRepMap[QChar(0xee)] = QLatin1Char('i'); m_strRepMap[QChar(0xef)] = QLatin1Char('i'); m_strRepMap[QChar(0xf0)] = QLatin1Char('d'); m_strRepMap[QChar(0xf1)] = QLatin1Char('n'); m_strRepMap[QChar(0xf2)] = QLatin1Char('o'); m_strRepMap[QChar(0xf3)] = QLatin1Char('o'); m_strRepMap[QChar(0xf4)] = QLatin1Char('o'); m_strRepMap[QChar(0xf5)] = QLatin1Char('o'); m_strRepMap[QChar(0xf8)] = QLatin1Char('o'); m_strRepMap[QChar(0xf9)] = QLatin1Char('u'); m_strRepMap[QChar(0xfa)] = QLatin1Char('u'); m_strRepMap[QChar(0xfb)] = QLatin1Char('u'); m_strRepMap[QChar(0xfd)] = QLatin1Char('y'); m_strRepMap[QChar(0xff)] = QLatin1Char('y'); } /** * Format a string using this configuration. * * @param str string to format */ void FormatConfig::formatString(QString& str) const { QString ext; int dotPos = -1; if (m_filenameFormatter) { /* Do not format the extension if it is a filename */ dotPos = str.lastIndexOf(QLatin1Char('.')); if (dotPos != -1) { ext = str.right(str.length() - dotPos); str = str.left(dotPos); } } if (m_caseConversion != NoChanges) { switch (m_caseConversion) { case AllLowercase: str = toLower(str); break; case AllUppercase: str = toUpper(str); break; case FirstLetterUppercase: str = toUpper(str.at(0)) + toLower(str.right(str.length() - 1)); break; case AllFirstLettersUppercase: { static const QString romanLetters(QLatin1String("IVXLCDM")); QString newstr; bool wordstart = true; const unsigned strLen = static_cast(str.length()); for (unsigned i = 0; i < strLen; ++i) { QChar ch = str.at(i); if (!ch.isLetterOrNumber() && ch != QLatin1Char('\'') && ch != QLatin1Char('`')) { wordstart = true; newstr.append(ch); } else if (wordstart) { wordstart = false; // Skip word if it is a roman number if (romanLetters.contains(ch)) { unsigned j = i + 1; while (j < strLen) { QChar c = str.at(j); if (!c.isLetterOrNumber()) { break; } else if (!romanLetters.contains(c)) { j = i; break; } ++j; } if (j > i) { newstr.append(str.midRef(i, j - i)); i = j - 1; continue; } } newstr.append(toUpper(ch)); } else { newstr.append(toLower(ch)); } } str = newstr; break; } default: ; } } if (m_strRepEnabled) { QMap::ConstIterator it; for (it = m_strRepMap.begin(); it != m_strRepMap.end(); ++it) { str.replace(it.key(), *it); } } /* append extension if it was removed */ if (dotPos != -1) { str.append(ext); } } /** Returns a lowercase copy of @a str. */ QString FormatConfig::toLower(const QString& str) const { #if QT_VERSION >= 0x040800 if (m_locale) return m_locale->toLower(str); #endif return str.toLower(); } /** Returns an uppercase copy of @a str. */ QString FormatConfig::toUpper(const QString& str) const { #if QT_VERSION >= 0x040800 if (m_locale) return m_locale->toUpper(str); #endif return str.toUpper(); } /** * Format frames using this configuration. * * @param frames frames */ void FormatConfig::formatFrames(FrameCollection& frames) const { for (FrameCollection::iterator it = frames.begin(); it != frames.end(); ++it) { Frame& frame = const_cast(*it); if (frame.getType() != Frame::FT_Genre) { QString value(frame.getValue()); if (!value.isEmpty()) { formatString(value); frame.setValueIfChanged(value); } } } } /** * Format frames if format while editing is switched on. * * @param frames frames */ void FormatConfig::formatFramesIfEnabled(FrameCollection& frames) const { if (m_formatWhileEditing) { formatFrames(frames); } } /** * Persist configuration. * * @param config configuration */ void FormatConfig::writeToConfig(ISettings* config) const { config->beginGroup(m_group); config->setValue(QLatin1String("FormatWhileEditing"), QVariant(m_formatWhileEditing)); config->setValue(QLatin1String("CaseConversion"), QVariant(m_caseConversion)); config->setValue(QLatin1String("LocaleName"), QVariant(m_localeName)); config->setValue(QLatin1String("StrRepEnabled"), QVariant(m_strRepEnabled)); config->setValue(QLatin1String("StrRepMapKeys"), QVariant(m_strRepMap.keys())); config->setValue(QLatin1String("StrRepMapValues"), QVariant(m_strRepMap.values())); config->endGroup(); } /** * Read persisted configuration. * * @param config configuration */ void FormatConfig::readFromConfig(ISettings* config) { config->beginGroup(m_group); m_formatWhileEditing = config->value(QLatin1String("FormatWhileEditing"), m_formatWhileEditing).toBool(); m_caseConversion = (CaseConversion)config->value(QLatin1String("CaseConversion"), (int)m_caseConversion).toInt(); m_localeName = config->value(QLatin1String("LocaleName"), m_localeName).toString(); m_strRepEnabled = config->value(QLatin1String("StrRepEnabled"), m_strRepEnabled).toBool(); QStringList keys = config->value(QLatin1String("StrRepMapKeys"), QStringList()).toStringList(); QStringList values = config->value(QLatin1String("StrRepMapValues"), QStringList()).toStringList(); if (!keys.empty() && !values.empty()) { QStringList::Iterator itk, itv; m_strRepMap.clear(); for (itk = keys.begin(), itv = values.begin(); itk != keys.end() && itv != values.end(); ++itk, ++itv) { m_strRepMap[*itk] = *itv; } } config->endGroup(); } /** * Set name of locale to use for string conversions. * @param localeName locale name */ void FormatConfig::setLocaleName(const QString& localeName) { if (localeName != m_localeName) { m_localeName = localeName; delete m_locale; m_locale = new QLocale(m_localeName); } } int FilenameFormatConfig::s_index = -1; /** * Constructor. */ FilenameFormatConfig::FilenameFormatConfig() : StoredConfig( QLatin1String("FilenameFormat")) { setAsFilenameFormatter(); } /** * Destructor. */ FilenameFormatConfig::~FilenameFormatConfig() { } int TagFormatConfig::s_index = -1; /** * Constructor. */ TagFormatConfig::TagFormatConfig() : StoredConfig(QLatin1String("TagFormat")) { } /** * Destructor. */ TagFormatConfig::~TagFormatConfig() { } kid3-3.0.2/src/core/config/formatconfig.h000066400000000000000000000101261224603511300201560ustar00rootroot00000000000000/** * \file formatconfig.h * Format configuration. * * \b Project: Kid3 * \author Urs Fleisch * \date 17 Sep 2003 * * Copyright (C) 2003-2012 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef FORMATCONFIG_H #define FORMATCONFIG_H #include "config.h" #include "generalconfig.h" #include #include "kid3api.h" class QString; class QLocale; class FrameCollection; /** * Format configuration. */ class KID3_CORE_EXPORT FormatConfig : public GeneralConfig { public: /** Case conversion options. */ enum CaseConversion { NoChanges, AllLowercase, AllUppercase, FirstLetterUppercase, AllFirstLettersUppercase, NumCaseConversions }; /** * Constructor. * * @param grp configuration group */ explicit FormatConfig(const QString& grp); /** * Destructor. */ virtual ~FormatConfig(); /** * Set specific properties for a filename format. * This will set default string conversions and not touch the file * extension when formatting. */ void setAsFilenameFormatter(); /** * Format a string using this configuration. * * @param str string to format */ void formatString(QString& str) const; /** * Format frames using this configuration. * * @param frames frames */ void formatFrames(FrameCollection& frames) const; /** * Format frames if format while editing is switched on. * * @param frames frames */ void formatFramesIfEnabled(FrameCollection& frames) const; /** * Persist configuration. * * @param config KDE configuration */ virtual void writeToConfig(ISettings* config) const; /** * Read persisted configuration. * * @param config KDE configuration */ virtual void readFromConfig(ISettings* config); /** * Get name of locale to use for string conversions. * @return locale name */ QString getLocaleName() const { return m_localeName; } /** * Set name of locale to use for string conversions. * @param localeName locale name */ void setLocaleName(const QString& localeName); /** true to enable formating in line edits */ bool m_formatWhileEditing; /** Case conversion option */ CaseConversion m_caseConversion; /** true if string replacement enabled */ bool m_strRepEnabled; /** Mapping for string replacement */ QMap m_strRepMap; private: /** Locale to use for case conversion */ QString m_localeName; /** Returns a lowercase copy of @a str. */ QString toLower(const QString& str) const; /** Returns an uppercase copy of @a str. */ QString toUpper(const QString& str) const; /** Locale to use for string conversions */ const QLocale* m_locale; /** true if it is a file formatter */ bool m_filenameFormatter; }; /** * FormatConfig subclass for stored filename format configuration instance. */ class KID3_CORE_EXPORT FilenameFormatConfig : public StoredConfig { public: /** * Constructor. */ FilenameFormatConfig(); /** * Destructor. */ virtual ~FilenameFormatConfig(); /** Index in configuration storage */ static int s_index; }; /** * FormatConfig subclass for stored tag format configuration instance. */ class KID3_CORE_EXPORT TagFormatConfig : public StoredConfig { public: /** * Constructor. */ TagFormatConfig(); /** * Destructor. */ virtual ~TagFormatConfig(); /** Index in configuration storage */ static int s_index; }; #endif kid3-3.0.2/src/core/config/generalconfig.cpp000066400000000000000000000020731224603511300206400ustar00rootroot00000000000000/** * \file generalconfig.cpp * General configuration. * * \b Project: Kid3 * \author Urs Fleisch * \date 17 Sep 2003 * * Copyright (C) 2003-2007 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "generalconfig.h" /** * Constructor. * Set default configuration. * * @param grp configuration group */ GeneralConfig::GeneralConfig(const QString& grp) : m_group(grp) {} /** * Destructor. */ GeneralConfig::~GeneralConfig() {} kid3-3.0.2/src/core/config/generalconfig.h000066400000000000000000000057341224603511300203140ustar00rootroot00000000000000/** * \file generalconfig.h * General configuration. * * \b Project: Kid3 * \author Urs Fleisch * \date 17 Sep 2003 * * Copyright (C) 2003-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef GENERALCONFIG_H #define GENERALCONFIG_H #include #include "isettings.h" #include "configstore.h" /** * Abstract base class for configurations. */ class KID3_CORE_EXPORT GeneralConfig { public: /** * Constructor. * Set default configuration. * * @param grp configuration group */ explicit GeneralConfig(const QString& grp); /** * Destructor. */ virtual ~GeneralConfig(); /** * Persist configuration. * * @param config KDE configuration */ virtual void writeToConfig(ISettings* config) const = 0; /** * Read persisted configuration. * * @param config KDE configuration */ virtual void readFromConfig(ISettings* config) = 0; protected: /** Configuration group. */ QString m_group; }; /** * Template to inject a static instance() method into a configuration class. * This is an application of the "curiously recurring template pattern" so that * the instance() method returns the type of the derived class. * A typical usage is * @code * class SpecializedConfig : public StoredConfig { * public: * explicit SpecializedConfig(const QString& grp) : StoredConfig(grp) { * (..) * } * }; * @endcode * * SpecializedConfig::instance() returns a reference to a stored instance * of this class. There can only be one such instance per class. * * @tparam Derived derived class * @tparam Base base class, default is GeneralConfig */ template class StoredConfig : public Base { public: /** * Constructor. * Set default configuration. * * @param grp configuration group */ explicit StoredConfig(const QString& grp) : Base(grp) {} /** * Get stored instance of class. * * @return instance. */ static Derived& instance(); }; template Derived& StoredConfig::instance() { Derived* obj = 0; ConfigStore* store = ConfigStore::instance(); if (Derived::s_index >= 0) { obj = static_cast(store->configuration(Derived::s_index)); } else { obj = new Derived; Derived::s_index = store->addConfiguration(obj); } return *obj; } #endif kid3-3.0.2/src/core/config/guiconfig.cpp000066400000000000000000000065151224603511300200140ustar00rootroot00000000000000/** * \file guiconfig.cpp * GUI related configuration. * * \b Project: Kid3 * \author Urs Fleisch * \date 29 Jun 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "guiconfig.h" int GuiConfig::s_index = -1; /** * Constructor. */ GuiConfig::GuiConfig() : StoredConfig(QLatin1String("GUI")), m_autoHideTags(true), m_hideFile(false), m_hideV1(false), m_hideV2(false), m_hidePicture(false), m_playOnDoubleClick(false) { } /** * Destructor. */ GuiConfig::~GuiConfig() {} /** * Persist configuration. * * @param config configuration */ void GuiConfig::writeToConfig(ISettings* config) const { config->beginGroup(m_group); config->setValue(QLatin1String("AutoHideTags"), QVariant(m_autoHideTags)); config->setValue(QLatin1String("HideFile"), QVariant(m_hideFile)); config->setValue(QLatin1String("HideV1"), QVariant(m_hideV1)); config->setValue(QLatin1String("HideV2"), QVariant(m_hideV2)); config->setValue(QLatin1String("HidePicture"), QVariant(m_hidePicture)); config->setValue(QLatin1String("PlayOnDoubleClick"), QVariant(m_playOnDoubleClick)); QList::const_iterator it; int i; for (it = m_splitterSizes.begin(), i = 0; it != m_splitterSizes.end(); ++it, ++i) { config->setValue(QLatin1String("SplitterSize") + QString::number(i), QVariant(*it)); } for (it = m_vSplitterSizes.begin(), i = 0; it != m_vSplitterSizes.end(); ++it, ++i) { config->setValue(QLatin1String("VSplitterSize") + QString::number(i), QVariant(*it)); } config->endGroup(); } /** * Read persisted configuration. * * @param config configuration */ void GuiConfig::readFromConfig(ISettings* config) { config->beginGroup(m_group); m_autoHideTags = config->value(QLatin1String("AutoHideTags"), m_autoHideTags).toBool(); m_hideFile = config->value(QLatin1String("HideFile"), m_hideFile).toBool(); m_hideV1 = config->value(QLatin1String("HideV1"), m_hideV1).toBool(); m_hideV2 = config->value(QLatin1String("HideV2"), m_hideV2).toBool(); m_hidePicture = config->value(QLatin1String("HidePicture"), m_hidePicture).toBool(); m_playOnDoubleClick = config->value(QLatin1String("PlayOnDoubleClick"), m_playOnDoubleClick).toBool(); m_splitterSizes.clear(); for (int i = 0; i < 5; ++i) { int val = config->value(QLatin1String("SplitterSize") + QString::number(i), -1).toInt(); if (val != -1) { m_splitterSizes.push_back(val); } else { break; } } m_vSplitterSizes.clear(); for (int j = 0; j < 5; ++j) { int val = config->value(QLatin1String("VSplitterSize") + QString::number(j), -1).toInt(); if (val != -1) { m_vSplitterSizes.push_back(val); } else { break; } } config->endGroup(); } kid3-3.0.2/src/core/config/guiconfig.h000066400000000000000000000037051224603511300174570ustar00rootroot00000000000000/** * \file guiconfig.h * GUI related configuration. * * \b Project: Kid3 * \author Urs Fleisch * \date 29 Jun 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef GUICONFIG_H #define GUICONFIG_H #include "generalconfig.h" #include "kid3api.h" /** * GUI related configuration. */ class KID3_CORE_EXPORT GuiConfig : public StoredConfig { public: /** * Constructor. */ GuiConfig(); /** * Destructor. */ virtual ~GuiConfig(); /** * Persist configuration. * * @param config configuration */ virtual void writeToConfig(ISettings* config) const; /** * Read persisted configuration. * * @param config configuration */ virtual void readFromConfig(ISettings* config); /** true to automatically hide unused tags */ bool m_autoHideTags; /** true to hide file controls */ bool m_hideFile; /** true to hide ID3v1.1 controls */ bool m_hideV1; /** true to hide ID3v2.3 controls */ bool m_hideV2; /** true to hide picture preview */ bool m_hidePicture; /** true to play file on double click */ bool m_playOnDoubleClick; /** size of splitter in main window */ QList m_splitterSizes; /** size of file/dirlist splitter */ QList m_vSplitterSizes; /** Index in configuration storage */ static int s_index; }; #endif kid3-3.0.2/src/core/config/importconfig.cpp000066400000000000000000000533501224603511300205410ustar00rootroot00000000000000/** * \file importconfig.cpp * Configuration for import dialog. * * \b Project: Kid3 * \author Urs Fleisch * \date 17 Sep 2003 * * Copyright (C) 2003-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "importconfig.h" #include #include "config.h" namespace { /** * Convert tag version to import destination value in configuration. * @param tagVersion tag version * @return value used in configuration, kept for backwards compatibility. */ inline int tagVersionToImportDestCfg(TrackData::TagVersion tagVersion) { return static_cast(tagVersion) - 1; } /** * Convert import destination value in configuration to tag version. * @param importDest value used in configuration, kept for backwards * compatibility. * @return tag version. */ inline TrackData::TagVersion importDestCfgToTagVersion(int importDest) { return TrackData::tagVersionCast(importDest + 1); } } int ImportConfig::s_index = -1; /** * Constructor. */ ImportConfig::ImportConfig() : StoredConfig(QLatin1String("Import")), m_importServer(0), m_importDest(TrackData::TagV1), m_importFormatIdx(0), m_enableTimeDifferenceCheck(true), m_maxTimeDifference(3), m_importVisibleColumns(0x2000000000ULL), m_importTagsIdx(0), m_pictureSourceIdx(0) { /** * Preset import format regular expressions. * The following codes are used before the () expressions. * %s %{title} title (song) * %l %{album} album * %a %{artist} artist * %c %{comment} comment * %y %{year} year * %t %{track} track, at least two digits * %T %{tracknumber} track number * %g %{genre} genre * %d %{duration} duration mm:ss * %D %{seconds} duration in seconds * %f %{file} file name * %p %{filepath} absolute file path * %u %{url} URL * %n %{tracks} number of tracks */ m_importFormatNames.append(QLatin1String("CSV unquoted")); m_importFormatHeaders.append(QLatin1String("")); m_importFormatTracks.append(QLatin1String("%{track}(\\d+)\\t%{title}([^\\r\\n\\t]*)\\t%{artist}([^\\r\\n\\t]*)\\t%{album}([^\\r\\n\\t]*)\\t%{year}(\\d+)\\t%{genre}([^\\r\\n\\t]*)\\t%{comment}([^\\r\\n\\t]*)\\t(?:\\d+:)?%{duration}(\\d+:\\d+)")); m_importFormatNames.append(QLatin1String("CSV quoted")); m_importFormatHeaders.append(QLatin1String("")); m_importFormatTracks.append(QLatin1String("\"?%{track}(\\d+)\"?\\t\"?%{title}([^\\r\\n\\t\"]*)\"?\\t\"?%{artist}([^\\r\\n\\t\"]*)\"?\\t\"?%{album}([^\\r\\n\\t\"]*)\"?\\t\"?%{year}(\\d+)\"?\\t\"?%{genre}([^\\r\\n\\t\"]*)\"?\\t\"?%{comment}([^\\r\\n\\t\"]*)\"?\\t\"?(?:\\d+:)?%{duration}(\\d+:\\d+)")); m_importFormatNames.append(QLatin1String("CSV more unquoted")); m_importFormatHeaders.append(QLatin1String("")); m_importFormatTracks.append(QLatin1String( "%{track}(\\d+)\\t%{title}([^\\r\\n\\t]*)\\t%{artist}([^\\r\\n\\t]*)\\t" "%{album}([^\\r\\n\\t]*)\\t%{year}(\\d+)\\t%{genre}([^\\r\\n\\t]*)\\" "t%{comment}([^\\r\\n\\t]*)\\t(?:\\d+:)?%{duration}(\\d+:\\d+)(?:\\.\\d+)?\\t" "%{album artist}([^\\r\\n\\t]*)\\t%{arranger}([^\\r\\n\\t]*)\\t" "%{author}([^\\r\\n\\t]*)\\t%{bpm}([^\\r\\n\\t]*)\\t" "%{composer}([^\\r\\n\\t]*)\\t%{conductor}([^\\r\\n\\t]*)\\t" "%{copyright}([^\\r\\n\\t]*)\\t%{disc number}([^\\r\\n\\t]*)\\t" "%{encoded-by}([^\\r\\n\\t]*)\\t%{grouping}([^\\r\\n\\t]*)\\t%{isrc}([^\\r\\n\\t]*)\\t" "%{language}([^\\r\\n\\t]*)\\t%{lyricist}([^\\r\\n\\t]*)\\t%{lyrics}([^\\r\\n\\t]*)\\t" "%{media}([^\\r\\n\\t]*)\\t%{original album}([^\\r\\n\\t]*)\\t" "%{original artist}([^\\r\\n\\t]*)\\t%{original date}([^\\r\\n\\t]*)\\t" "%{part}([^\\r\\n\\t]*)\\t%{performer}([^\\r\\n\\t]*)\\t" "%{publisher}([^\\r\\n\\t]*)\\t%{remixer}([^\\r\\n\\t]*)\\t" "%{subtitle}([^\\r\\n\\t]*)\\t%{website}([^\\r\\n\\t]*)")); m_importFormatNames.append(QLatin1String("CSV more quoted")); m_importFormatHeaders.append(QLatin1String("")); m_importFormatTracks.append(QLatin1String( "\"?%{track}(\\d+)\"?\\t\"?%{title}([^\\r\\n\\t\"]*)\"?\\t" "\"?%{artist}([^\\r\\n\\t\"]*)\"?\\t\"?%{album}([^\\r\\n\\t\"]*)\"?\\t" "\"?%{year}(\\d+)\"?\\t\"?%{genre}([^\\r\\n\\t\"]*)\"?\\t" "\"?%{comment}([^\\r\\n\\t\"]*)\"?\\t" "\"?(?:\\d+:)?%{duration}(\\d+:\\d+)(?:\\.\\d+)?\"?\\t" "\"?%{album artist}([^\\r\\n\\t\"]*)\"?\\t\"?%{arranger}([^\\r\\n\\t\"]*)\"?\\t" "\"?%{author}([^\\r\\n\\t\"]*)\"?\\t\"?%{bpm}([^\\r\\n\\t\"]*)\"?\\t" "\"?%{composer}([^\\r\\n\\t\"]*)\"?\\t\"?%{conductor}([^\\r\\n\\t\"]*)\"?\\t" "\"?%{copyright}([^\\r\\n\\t\"]*)\"?\\t\"?%{disc number}([^\\r\\n\\t\"]*)\"?\\t" "\"?%{encoded-by}([^\\r\\n\\t\"]*)\"?\\t\"?%{grouping}([^\\r\\n\\t\"]*)\"?\\t\"?%{isrc}([^\\r\\n\\t\"]*)\"?\\t" "\"?%{language}([^\\r\\n\\t\"]*)\"?\\t\"?%{lyricist}([^\\r\\n\\t\"]*)\"?\\t\"?%{lyrics}([^\\r\\n\\t\"]*)\"?\\t" "\"?%{media}([^\\r\\n\\t\"]*)\"?\\t\"?%{original album}([^\\r\\n\\t\"]*)\"?\\t" "\"?%{original artist}([^\\r\\n\\t\"]*)\"?\\t\"?%{original date}([^\\r\\n\\t\"]*)\"?\\t" "\"?%{part}([^\\r\\n\\t\"]*)\"?\\t\"?%{performer}([^\\r\\n\\t\"]*)\"?\\t" "\"?%{publisher}([^\\r\\n\\t\"]*)\"?\\t\"?%{remixer}([^\\r\\n\\t\"]*)\"?\\t" "\"?%{subtitle}([^\\r\\n\\t\"]*)\"?\\t\"?%{website}([^\\r\\n\\t\"]*)")); m_importFormatNames.append(QLatin1String("freedb HTML text")); m_importFormatHeaders.append(QLatin1String("%{artist}(\\S[^\\r\\n/]*\\S)\\s*/\\s*%{album}(\\S[^\\r\\n]*\\S)[\\r\\n]+\\s*tracks:\\s+\\d+.*year:\\s*%{year}(\\d+)?.*genre:\\s*%{genre}(\\S[^\\r\\n]*\\S)?[\\r\\n]")); m_importFormatTracks.append(QLatin1String("[\\r\\n]%{track}(\\d+)[\\.\\s]+%{duration}(\\d+:\\d+)\\s+%{title}(\\S[^\\r\\n]*\\S)")); m_importFormatNames.append(QLatin1String("freedb HTML source")); m_importFormatHeaders.append(QLatin1String("<[^>]+>%{artist}([^<\\s][^\\r\\n/]*\\S)\\s*/\\s*%{album}(\\S[^\\r\\n]*[^\\s>])<[^>]+>[\\r\\n]+\\s*tracks:\\s+\\d+.*year:\\s*%{year}(\\d+)?.*genre:\\s*%{genre}(\\S[^\\r\\n>]*\\S)?<[^>]+>[\\r\\n]")); m_importFormatTracks.append(QLatin1String("]*>\\s*%{track}(\\d+).]*>\\s*%{duration}(\\d+:\\d+)]*>(?:<[^>]+>)?%{title}([^<\\r\\n]+)")); m_importFormatNames.append(QLatin1String("Title")); m_importFormatHeaders.append(QLatin1String("")); m_importFormatTracks.append(QLatin1String("\\s*%{title}(\\S[^\\r\\n]*\\S)\\s*")); m_importFormatNames.append(QLatin1String("Track Title")); m_importFormatHeaders.append(QLatin1String("")); m_importFormatTracks.append(QLatin1String("\\s*%{track}(\\d+)[\\.\\s]+%{title}(\\S[^\\r\\n]*\\S)\\s*")); m_importFormatNames.append(QLatin1String("Track Title Time")); m_importFormatHeaders.append(QLatin1String("")); m_importFormatTracks.append(QLatin1String("\\s*%{track}(\\d+)[\\.\\s]+%{title}(\\S[^\\r\\n]*\\S)\\s+%{duration}(\\d+:\\d+)\\s*")); m_importFormatNames.append(QLatin1String("Custom Format")); m_importFormatHeaders.append(QLatin1String("")); m_importFormatTracks.append(QLatin1String("")); m_importTagsNames.append(QLatin1String("Artist to Album Artist")); m_importTagsSources.append(QLatin1String("%{artist}")); m_importTagsExtractions.append(QLatin1String("%{albumartist}(.+)")); m_importTagsNames.append(QLatin1String("Album Artist to Artist")); m_importTagsSources.append(QLatin1String("%{albumartist}")); m_importTagsExtractions.append(QLatin1String("%{artist}(.+)")); m_importTagsNames.append(QLatin1String("Artist to Composer")); m_importTagsSources.append(QLatin1String("%{artist}")); m_importTagsExtractions.append(QLatin1String("%{composer}(.+)")); m_importTagsNames.append(QLatin1String("Artist to Conductor")); m_importTagsSources.append(QLatin1String("%{artist}")); m_importTagsExtractions.append(QLatin1String("%{conductor}(.+)")); m_importTagsNames.append(QLatin1String("Track Number from Title")); m_importTagsSources.append(QLatin1String("%{title}")); m_importTagsExtractions.append(QLatin1String("\\s*%{track}(\\d+)[\\.\\s]+%{title}(\\S.*\\S)\\s*")); m_importTagsNames.append(QLatin1String("Track Number to Title")); m_importTagsSources.append(QLatin1String("%{track} %{title}")); m_importTagsExtractions.append(QLatin1String("%{title}(.+)")); m_importTagsNames.append(QLatin1String("Subtitle from Title")); m_importTagsSources.append(QLatin1String("%{title}")); m_importTagsExtractions.append(QLatin1String("%{subtitle}(.+) - ")); m_importTagsNames.append(QLatin1String("Title Annotation to Comment")); m_importTagsSources.append(QLatin1String("%{title}")); m_importTagsExtractions.append(QLatin1String("%{title}(.+) +\\(%{comment}((?:Bonus|Remix)[^)]*)\\)")); m_importTagsNames.append(QLatin1String("Custom Format")); m_importTagsSources.append(QLatin1String("")); m_importTagsExtractions.append(QLatin1String("")); m_pictureSourceNames.append(QLatin1String("Google Images")); m_pictureSourceUrls.append(QLatin1String("http://images.google.com/images?q=%u{artist}%20%u{album}")); m_pictureSourceNames.append(QLatin1String("Yahoo Images")); m_pictureSourceUrls.append(QLatin1String("http://images.search.yahoo.com/search/images?ei=UTF-8&p=%u{artist}%20%u{album}")); m_pictureSourceNames.append(QLatin1String("Amazon")); m_pictureSourceUrls.append(QLatin1String("http://www.amazon.com/s?search-alias=aps&field-keywords=%u{artist}+%u{album}")); m_pictureSourceNames.append(QLatin1String("Amazon.co.uk")); m_pictureSourceUrls.append(QLatin1String("http://www.amazon.co.uk/s?search-alias=aps&field-keywords=%u{artist}+%u{album}")); m_pictureSourceNames.append(QLatin1String("Amazon.de")); m_pictureSourceUrls.append(QLatin1String("http://www.amazon.de/s?search-alias=aps&field-keywords=%u{artist}+%u{album}")); m_pictureSourceNames.append(QLatin1String("Amazon.fr")); m_pictureSourceUrls.append(QLatin1String("http://www.amazon.fr/s?search-alias=aps&field-keywords=%u{artist}+%u{album}")); m_pictureSourceNames.append(QLatin1String("MusicBrainz")); m_pictureSourceUrls.append(QLatin1String("http://musicbrainz.org/search/textsearch.html?query=%u{artist}+%u{album}&type=release")); m_pictureSourceNames.append(QLatin1String("Discogs")); m_pictureSourceUrls.append(QLatin1String("http://www.discogs.com/search?q=%u{artist}+%u{album}")); m_pictureSourceNames.append(QLatin1String("CD Universe")); m_pictureSourceUrls.append(QLatin1String("http://www.cduniverse.com/sresult.asp?HT_Search_Info=%u{artist}+%u{album}")); m_pictureSourceNames.append(QLatin1String("Coveralia")); m_pictureSourceUrls.append(QLatin1String("http://www.coveralia.com/mostrar.php?bus=%u{artist}%20%u{album}&bust=2")); m_pictureSourceNames.append(QLatin1String("FreeCovers")); m_pictureSourceUrls.append(QLatin1String("http://www.freecovers.net/search.php?search=%u{artist}+%u{album}&cat=4")); m_pictureSourceNames.append(QLatin1String("CoverHunt")); m_pictureSourceUrls.append(QLatin1String("http://www.coverhunt.com/search/%u{artist}+%u{album}")); m_pictureSourceNames.append(QLatin1String("SlothRadio")); m_pictureSourceUrls.append(QLatin1String("http://www.slothradio.com/covers/?artist=%u{artist}&album=%u{album}")); m_pictureSourceNames.append(QLatin1String("Albumart")); m_pictureSourceUrls.append(QLatin1String("http://www.albumart.org/index.php?srchkey=%u{artist}+%u{album}&searchindex=Music")); m_pictureSourceNames.append(QLatin1String("Yalp!")); m_pictureSourceUrls.append(QLatin1String("http://search.yalp.alice.it/search/search.html?txtToSearch=%u{artist}%20%u{album}")); m_pictureSourceNames.append(QLatin1String("HMV")); m_pictureSourceUrls.append(QLatin1String("http://hmv.com/hmvweb/advancedSearch.do?searchType=2&artist=%u{artist}&title=%u{album}")); m_pictureSourceNames.append(QLatin1String("Custom Source")); m_pictureSourceUrls.append(QLatin1String("")); m_matchPictureUrlMap[QLatin1String("http://www.google.com/.*imgurl=([^&]+)&.*")] = QLatin1String("\\1"); m_matchPictureUrlMap[QLatin1String("http://images.search.yahoo.com/.*&imgurl=([^&]+)&.*")] = QLatin1String("http%3A%2F%2F\\1"); m_matchPictureUrlMap[QLatin1String("http://(?:www.)?amazon.(?:com|co.uk|de|fr).*/(?:dp|ASIN|images|product|-)/([A-Z0-9]+).*")] = QLatin1String("http://images.amazon.com/images/P/\\1.01._SCLZZZZZZZ_.jpg"); m_matchPictureUrlMap[QLatin1String("http://musicbrainz.org/misc/redirects/.*&asin=([A-Z0-9]+).*")] = QLatin1String("http://images.amazon.com/images/P/\\1.01._SCLZZZZZZZ_.jpg"); m_matchPictureUrlMap[QLatin1String("http://www.freecovers.net/view/(\\d+)/([0-9a-f]+)/.*")] = QLatin1String("http://www.freecovers.net/preview/\\1/\\2/big.jpg"); } /** * Destructor. */ ImportConfig::~ImportConfig() {} /** * Persist configuration. * * @param config configuration */ void ImportConfig::writeToConfig(ISettings* config) const { config->beginGroup(m_group); config->setValue(QLatin1String("ImportServer"), QVariant(m_importServer)); config->setValue(QLatin1String("ImportDestination"), QVariant(tagVersionToImportDestCfg(m_importDest))); config->setValue(QLatin1String("ImportFormatNames"), QVariant(m_importFormatNames)); config->setValue(QLatin1String("ImportFormatHeaders"), QVariant(m_importFormatHeaders)); config->setValue(QLatin1String("ImportFormatTracks"), QVariant(m_importFormatTracks)); config->setValue(QLatin1String("ImportFormatIdx"), QVariant(m_importFormatIdx)); config->setValue(QLatin1String("EnableTimeDifferenceCheck"), QVariant(m_enableTimeDifferenceCheck)); config->setValue(QLatin1String("MaxTimeDifference"), QVariant(m_maxTimeDifference)); config->setValue(QLatin1String("ImportVisibleColumns"), QVariant(m_importVisibleColumns)); config->setValue(QLatin1String("ImportWindowGeometry"), QVariant(m_importWindowGeometry)); config->setValue(QLatin1String("ImportTagsNames"), QVariant(m_importTagsNames)); config->setValue(QLatin1String("ImportTagsSources"), QVariant(m_importTagsSources)); config->setValue(QLatin1String("ImportTagsExtractions"), QVariant(m_importTagsExtractions)); config->setValue(QLatin1String("ImportTagsIdx"), QVariant(m_importTagsIdx)); config->setValue(QLatin1String("PictureSourceNames"), QVariant(m_pictureSourceNames)); config->setValue(QLatin1String("PictureSourceUrls"), QVariant(m_pictureSourceUrls)); config->setValue(QLatin1String("PictureSourceIdx"), QVariant(m_pictureSourceIdx)); config->setValue(QLatin1String("MatchPictureUrlMapKeys"), QVariant(m_matchPictureUrlMap.keys())); config->setValue(QLatin1String("MatchPictureUrlMapValues"), QVariant(m_matchPictureUrlMap.values())); config->setValue(QLatin1String("BrowseCoverArtWindowGeometry"), QVariant(m_browseCoverArtWindowGeometry)); config->setValue(QLatin1String("DisabledPlugins"), QVariant(m_disabledPlugins)); config->endGroup(); } /** * Read persisted configuration. * * @param config configuration */ void ImportConfig::readFromConfig(ISettings* config) { QStringList names, headers, tracks; QStringList tagsNames, tagsSources, tagsExtractions; QStringList picNames, picUrls; config->beginGroup(m_group); m_importServer = config->value(QLatin1String("ImportServer"), m_importServer).toInt(); m_importDest = importDestCfgToTagVersion( config->value(QLatin1String("ImportDestination"), tagVersionToImportDestCfg(m_importDest)).toInt()); names = config->value(QLatin1String("ImportFormatNames"), m_importFormatNames).toStringList(); headers = config->value(QLatin1String("ImportFormatHeaders"), m_importFormatHeaders).toStringList(); tracks = config->value(QLatin1String("ImportFormatTracks"), m_importFormatTracks).toStringList(); m_importFormatIdx = config->value(QLatin1String("ImportFormatIdx"), m_importFormatIdx).toInt(); m_enableTimeDifferenceCheck = config->value(QLatin1String("EnableTimeDifferenceCheck"), m_enableTimeDifferenceCheck).toBool(); m_maxTimeDifference = config->value(QLatin1String("MaxTimeDifference"), m_maxTimeDifference).toInt(); m_importVisibleColumns = config->value(QLatin1String("ImportVisibleColumns"), m_importVisibleColumns).toULongLong(); m_importWindowGeometry = config->value(QLatin1String("ImportWindowGeometry"), m_importWindowGeometry).toByteArray(); tagsNames = config->value(QLatin1String("ImportTagsNames"), m_importTagsNames).toStringList(); tagsSources = config->value(QLatin1String("ImportTagsSources"), m_importTagsSources).toStringList(); tagsExtractions = config->value(QLatin1String("ImportTagsExtractions"), m_importTagsExtractions).toStringList(); m_importTagsIdx = config->value(QLatin1String("ImportTagsIdx"), m_importTagsIdx).toInt(); picNames = config->value(QLatin1String("PictureSourceNames"), m_pictureSourceNames).toStringList(); picUrls = config->value(QLatin1String("PictureSourceUrls"), m_pictureSourceUrls).toStringList(); m_pictureSourceIdx = config->value( QLatin1String("PictureSourceIdx"), m_pictureSourceIdx).toInt(); QStringList keys = config->value(QLatin1String("MatchPictureUrlMapKeys"), QStringList()).toStringList(); QStringList values = config->value(QLatin1String("MatchPictureUrlMapValues"), QStringList()).toStringList(); if (!keys.empty() && !values.empty()) { QStringList::Iterator itk, itv; m_matchPictureUrlMap.clear(); for (itk = keys.begin(), itv = values.begin(); itk != keys.end() && itv != values.end(); ++itk, ++itv) { m_matchPictureUrlMap[*itk] = *itv; } } m_browseCoverArtWindowGeometry = config->value( QLatin1String("BrowseCoverArtWindowGeometry"), m_browseCoverArtWindowGeometry).toByteArray(); m_disabledPlugins = config->value(QLatin1String("DisabledPlugins"), m_disabledPlugins).toStringList(); config->endGroup(); // KConfig seems to strip empty entries from the end of the string lists, // so we have to append them again. unsigned numNames = names.size(); while (static_cast(headers.size()) < numNames) headers.append(QLatin1String("")); while (static_cast(tracks.size()) < numNames) tracks.append(QLatin1String("")); unsigned numPicNames = picNames.size(); while (static_cast(picUrls.size()) < numPicNames) picUrls.append(QLatin1String("")); /* Use defaults if no configuration found */ QStringList::const_iterator namesIt, headersIt, tracksIt; for (namesIt = names.begin(), headersIt = headers.begin(), tracksIt = tracks.begin(); namesIt != names.end() && headersIt != headers.end() && tracksIt != tracks.end(); ++namesIt, ++headersIt, ++tracksIt) { int idx = m_importFormatNames.indexOf(*namesIt); if (idx >= 0) { m_importFormatHeaders[idx] = *headersIt; m_importFormatTracks[idx] = *tracksIt; } else if (!(*namesIt).isEmpty()) { m_importFormatNames.append(*namesIt); m_importFormatHeaders.append(*headersIt); m_importFormatTracks.append(*tracksIt); } } QStringList::const_iterator tagsNamesIt, sourcesIt, extractionsIt; for (tagsNamesIt = tagsNames.begin(), sourcesIt = tagsSources.begin(), extractionsIt = tagsExtractions.begin(); tagsNamesIt != tagsNames.end() && sourcesIt != tagsSources.end() && extractionsIt != tagsExtractions.end(); ++tagsNamesIt, ++sourcesIt, ++extractionsIt) { int idx = m_importTagsNames.indexOf(*tagsNamesIt); if (idx >= 0) { m_importTagsSources[idx] = *sourcesIt; m_importTagsExtractions[idx] = *extractionsIt; } else if (!(*tagsNamesIt).isEmpty()) { m_importTagsNames.append(*tagsNamesIt); m_importTagsSources.append(*sourcesIt); m_importTagsExtractions.append(*extractionsIt); } } QStringList::const_iterator picNamesIt, picUrlsIt; for (picNamesIt = picNames.begin(), picUrlsIt = picUrls.begin(); picNamesIt != picNames.end() && picUrlsIt != picUrls.end(); ++picNamesIt, ++picUrlsIt) { int idx = m_pictureSourceNames.indexOf(*picNamesIt); if (idx >= 0) { m_pictureSourceUrls[idx] = *picUrlsIt; } else if (!(*picNamesIt).isEmpty()) { m_pictureSourceNames.append(*picNamesIt); m_pictureSourceUrls.append(*picUrlsIt); } } if (m_importFormatIdx >= static_cast(m_importFormatNames.size())) m_importFormatIdx = 0; if (m_importTagsIdx >= static_cast(m_importTagsNames.size())) m_importTagsIdx = 0; if (m_pictureSourceIdx >= static_cast(m_pictureSourceNames.size())) m_pictureSourceIdx = 0; // Replace mappings which do no longer work. if (m_pictureSourceUrls.removeOne(QLatin1String( "http://cdbaby.com/found?artist=%u{artist}&album=%u{album}"))) { m_pictureSourceNames.removeOne(QLatin1String("CD Baby")); } if (m_pictureSourceUrls.removeOne(QLatin1String( "http://www.jamendo.com/en/search/all/%u{artist}%20%u{album}"))) { m_pictureSourceNames.removeOne(QLatin1String("Jamendo")); } if (m_matchPictureUrlMap.remove(QLatin1String( "http://images.google.com/.*imgurl=([^&]+)&.*")) != 0) { m_matchPictureUrlMap[QLatin1String("http://www.google.com/.*imgurl=([^&]+)&.*")] = QLatin1String("\\1"); } if (m_matchPictureUrlMap.remove(QLatin1String( "http://rds.yahoo.com/.*&imgurl=([^&]+)&.*")) != 0) { m_matchPictureUrlMap[QLatin1String("http://images.search.yahoo.com/.*&imgurl=([^&]+)&.*")] = QLatin1String("http%3A%2F%2F\\1"); } m_matchPictureUrlMap.remove(QLatin1String( "http://rds.yahoo.com/.*%26imgurl=((?:[^%]|%(?!26))+).*")); m_matchPictureUrlMap.remove(QLatin1String( "http://cdbaby.com/cd/(\\w)(\\w)(\\w+)")); m_matchPictureUrlMap.remove(QLatin1String( "http://www.jamendo.com/en/album/(\\d+)")); } kid3-3.0.2/src/core/config/importconfig.h000066400000000000000000000067561224603511300202160ustar00rootroot00000000000000/** * \file importconfig.h * Configuration for import dialog. * * \b Project: Kid3 * \author Urs Fleisch * \date 17 Sep 2003 * * Copyright (C) 2003-2009 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef IMPORTCONFIG_H #define IMPORTCONFIG_H #include #include #include "config.h" #include "generalconfig.h" #include "trackdata.h" /** * Import configuration. */ class KID3_CORE_EXPORT ImportConfig : public StoredConfig { public: /** * Constructor. */ ImportConfig(); /** * Destructor. */ virtual ~ImportConfig(); /** * Persist configuration. * * @param config configuration */ virtual void writeToConfig(ISettings* config) const; /** * Read persisted configuration. * * @param config configuration */ virtual void readFromConfig(ISettings* config); /** * Access to list of available plugins. * @return available plugins. * @remark This information is not stored in the configuration, it is * determined when the plugins are loaded. */ QStringList& availablePlugins() { return m_availablePlugins; } /** * Get list of available plugins. * @return available plugins. */ QStringList getAvailablePlugins() const { return m_availablePlugins; } /** import server */ int m_importServer; /** tag version to import */ TrackData::TagVersion m_importDest; /** Names of import formats */ QStringList m_importFormatNames; /** regexp describing header import format */ QStringList m_importFormatHeaders; /** regexp describing track import format */ QStringList m_importFormatTracks; /** selected import format */ int m_importFormatIdx; /** check maximum allowable time difference */ bool m_enableTimeDifferenceCheck; /** maximum allowable time difference */ int m_maxTimeDifference; /** visible optional columns in import table */ quint64 m_importVisibleColumns; /** import window geometry */ QByteArray m_importWindowGeometry; /** Names of import tags formats */ QStringList m_importTagsNames; /** Expressions for tag import sources */ QStringList m_importTagsSources; /** regexp describing extraction from import tag sources */ QStringList m_importTagsExtractions; /** selected import tags format */ int m_importTagsIdx; /** names of picture sources */ QStringList m_pictureSourceNames; /** picture source URLs */ QStringList m_pictureSourceUrls; /** selected picture source */ int m_pictureSourceIdx; /** Browse cover art window geometry */ QByteArray m_browseCoverArtWindowGeometry; /** Mapping for picture URL matching */ QMap m_matchPictureUrlMap; /** Last directory used for import or export. */ QString m_importDir; /** Disabled plugins */ QStringList m_disabledPlugins; /** Index in configuration storage */ static int s_index; private: QStringList m_availablePlugins; }; #endif kid3-3.0.2/src/core/config/isettings.cpp000066400000000000000000000231611224603511300200470ustar00rootroot00000000000000/** * \file isettings.h * Interface for application settings. * * \b Project: Kid3 * \author Urs Fleisch * \date 07 Apr 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "isettings.h" #include #include /** * Destructor. */ ISettings::~ISettings() { } /** * Migrate from an old settings version. * Can be called from the constructor of derived classes to automatically * convert old settings. */ void ISettings::migrateOldSettings() { beginGroup(QLatin1String("Tags")); bool isOld = !contains(QLatin1String("MarkTruncations")); endGroup(); if (isOld) { bool migrated = false; static const struct { const char* oldKey; const char* newKey; QVariant::Type type; } mappings[] = { { "Id3Format/FormatWhileEditing", "TagFormat/FormatWhileEditing", QVariant::Bool }, { "Id3Format/CaseConversion", "TagFormat/CaseConversion", QVariant::Int }, { "Id3Format/LocaleName", "TagFormat/LocaleName", QVariant::String }, { "Id3Format/StrRepEnabled", "TagFormat/StrRepEnabled", QVariant::Bool }, { "Id3Format/StrRepMapKeys", "TagFormat/StrRepMapKeys", QVariant::StringList }, { "Id3Format/StrRepMapValues", "TagFormat/StrRepMapValues", QVariant::StringList }, { "General Options/HideToolBar", "MainWindow/HideToolBar", QVariant::Bool }, { "General Options/HideStatusBar", "MainWindow/HideStatusBar", QVariant::Bool }, { "General Options/Geometry", "MainWindow/Geometry", QVariant::ByteArray }, { "General Options/WindowState", "MainWindow/WindowState", QVariant::ByteArray }, { "General Options/UseFont", "MainWindow/UseFont", QVariant::Bool }, { "General Options/FontFamily", "MainWindow/FontFamily", QVariant::String }, { "General Options/FontSize", "MainWindow/FontSize", QVariant::Int }, { "General Options/Style", "MainWindow/Style", QVariant::String }, { "General Options/DontUseNativeDialogs", "MainWindow/DontUseNativeDialogs", QVariant::Bool }, { "General Options/MarkTruncations", "Tags/MarkTruncations", QVariant::Bool }, { "General Options/EnableTotalNumberOfTracks", "Tags/EnableTotalNumberOfTracks", QVariant::Bool }, { "General Options/GenreNotNumeric", "Tags/GenreNotNumeric", QVariant::Bool }, { "General Options/CommentName", "Tags/CommentName", QVariant::String }, { "General Options/PictureNameItem", "Tags/PictureNameItem", QVariant::Int }, { "General Options/CustomGenres", "Tags/CustomGenres", QVariant::StringList }, { "General Options/ID3v2Version", "Tags/ID3v2Version", QVariant::Int }, { "General Options/TextEncodingV1", "Tags/TextEncodingV1", QVariant::String }, { "General Options/TextEncoding", "Tags/TextEncoding", QVariant::Int }, { "General Options/QuickAccessFrames", "Tags/QuickAccessFrames", QVariant::UInt }, { "General Options/TrackNumberDigits", "Tags/TrackNumberDigits", QVariant::Int }, { "General Options/OnlyCustomGenres", "Tags/OnlyCustomGenres", QVariant::Bool }, { "General Options/NameFilter3", "Files/NameFilter", QVariant::String }, { "General Options/FormatItem", "Files/FormatItem", QVariant::Int }, { "General Options/FormatItems", "Files/FormatItems", QVariant::StringList }, { "General Options/FormatText2", "Files/FormatText", QVariant::String }, { "General Options/FormatFromFilenameItem", "Files/FormatFromFilenameItem", QVariant::Int }, { "General Options/FormatFromFilenameItems", "Files/FormatFromFilenameItems", QVariant::StringList }, { "General Options/FormatFromFilenameText", "Files/FormatFromFilenameText", QVariant::String }, { "General Options/PreserveTime", "Files/PreserveTime", QVariant::Bool }, { "General Options/MarkChanges", "Files/MarkChanges", QVariant::Bool }, { "General Options/LoadLastOpenedFile", "Files/LoadLastOpenedFile", QVariant::Bool }, { "General Options/LastOpenedFile", "Files/LastOpenedFile", QVariant::String }, { "General Options/DefaultCoverFileName", "Files/DefaultCoverFileName", QVariant::String }, { "General Options/DirFormatItem", "RenameDirectory/DirFormatItem", QVariant::Int }, { "General Options/DirFormatText", "RenameDirectory/DirFormatText", QVariant::String }, { "General Options/RenameDirectorySource", "RenameDirectory/RenameDirectorySource", QVariant::Int }, { "General Options/NumberTracksDestination", "NumberTracks/NumberTracksDestination", QVariant::Int }, { "General Options/NumberTracksStartNumber", "NumberTracks/NumberTracksStartNumber", QVariant::Int }, { "General Options/AutoHideTags", "GUI/AutoHideTags", QVariant::Bool }, { "General Options/HideFile", "GUI/HideFile", QVariant::Bool }, { "General Options/HideV1", "GUI/HideV1", QVariant::Bool }, { "General Options/HideV2", "GUI/HideV2", QVariant::Bool }, { "General Options/HidePicture", "GUI/HidePicture", QVariant::Bool }, { "General Options/PlayOnDoubleClick", "GUI/PlayOnDoubleClick", QVariant::Bool }, { "General Options/SplitterSize0", "GUI/SplitterSize0", QVariant::Int }, { "General Options/SplitterSize1", "GUI/SplitterSize1", QVariant::Int }, { "General Options/VSplitterSize0", "GUI/VSplitterSize0", QVariant::Int }, { "General Options/VSplitterSize1", "GUI/VSplitterSize1", QVariant::Int }, { "General Options/UseProxy", "Network/UseProxy", QVariant::Bool }, { "General Options/Proxy", "Network/Proxy", QVariant::String }, { "General Options/UseProxyAuthentication", "Network/UseProxyAuthentication", QVariant::Bool }, { "General Options/ProxyUserName", "Network/ProxyUserName", QVariant::String }, { "General Options/ProxyPassword", "Network/ProxyPassword", QVariant::String }, { "General Options/Browser", "Network/Browser", QVariant::String }, { "General Options/ImportServer", "Import/ImportServer", QVariant::Int }, { "General Options/ImportDestination", "Import/ImportDestination", QVariant::Int }, { "General Options/ImportFormatNames", "Import/ImportFormatNames", QVariant::StringList }, { "General Options/ImportFormatHeaders", "Import/ImportFormatHeaders", QVariant::StringList }, { "General Options/ImportFormatTracks", "Import/ImportFormatTracks", QVariant::StringList }, { "General Options/ImportFormatIdx", "Import/ImportFormatIdx", QVariant::Int }, { "General Options/EnableTimeDifferenceCheck", "Import/EnableTimeDifferenceCheck", QVariant::Bool }, { "General Options/MaxTimeDifference", "Import/MaxTimeDifference", QVariant::Int }, { "General Options/ImportVisibleColumns", "Import/ImportVisibleColumns", QVariant::ULongLong }, { "General Options/ImportWindowGeometry", "Import/ImportWindowGeometry", QVariant::ByteArray }, { "General Options/ImportTagsNames", "Import/ImportTagsNames", QVariant::StringList }, { "General Options/ImportTagsSources", "Import/ImportTagsSources", QVariant::StringList }, { "General Options/ImportTagsExtractions", "Import/ImportTagsExtractions", QVariant::StringList }, { "General Options/ImportTagsIdx", "Import/ImportTagsIdx", QVariant::Int }, { "General Options/PictureSourceNames", "Import/PictureSourceNames", QVariant::StringList }, { "General Options/PictureSourceUrls", "Import/PictureSourceUrls", QVariant::StringList }, { "General Options/PictureSourceIdx", "Import/PictureSourceIdx", QVariant::Int }, { "General Options/MatchPictureUrlMapKeys", "Import/MatchPictureUrlMapKeys", QVariant::StringList }, { "General Options/MatchPictureUrlMapValues", "Import/MatchPictureUrlMapValues", QVariant::StringList }, { "General Options/BrowseCoverArtWindowGeometry", "Import/BrowseCoverArtWindowGeometry", QVariant::ByteArray }, { "General Options/ExportSourceV1", "Export/ExportSourceV1", QVariant::Bool }, { "General Options/ExportFormatNames", "Export/ExportFormatNames", QVariant::StringList }, { "General Options/ExportFormatHeaders", "Export/ExportFormatHeaders", QVariant::StringList }, { "General Options/ExportFormatTracks", "Export/ExportFormatTracks", QVariant::StringList }, { "General Options/ExportFormatTrailers", "Export/ExportFormatTrailers", QVariant::StringList }, { "General Options/ExportFormatIdx", "Export/ExportFormatIdx", QVariant::Int }, { "General Options/ExportWindowGeometry", "Export/ExportWindowGeometry", QVariant::ByteArray } }; for (unsigned int i = 0; i < sizeof(mappings) / sizeof(mappings[0]); ++i) { QStringList groupKey = QString::fromLatin1(mappings[i].oldKey). split(QLatin1Char('/')); beginGroup(groupKey.at(0)); if (contains(groupKey.at(1))) { QVariant val = value(groupKey.at(1), QVariant(mappings[i].type)); remove(groupKey.at(1)); endGroup(); groupKey = QString::fromLatin1(mappings[i].newKey). split(QLatin1Char('/')); beginGroup(groupKey.at(0)); setValue(groupKey.at(1), val); migrated = true; } endGroup(); } if (migrated) { qDebug("Migrated old settings"); } } } kid3-3.0.2/src/core/config/isettings.h000066400000000000000000000044351224603511300175170ustar00rootroot00000000000000/** * \file isettings.h * Interface for application settings. * * \b Project: Kid3 * \author Urs Fleisch * \date 07 Apr 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef ISETTINGS_H #define ISETTINGS_H #include #include "kid3api.h" class QString; /** * Interface for application settings. */ class KID3_CORE_EXPORT ISettings { public: /** * Destructor. */ virtual ~ISettings() = 0; /** * Use settings subgroup. * @param prefix group name */ virtual void beginGroup(const QString& prefix) = 0; /** * Finnish using settings subgroup. */ virtual void endGroup() = 0; /** * Set value for setting. * @param key name of setting * @param value value for setting */ virtual void setValue(const QString& key, const QVariant& value) = 0; /** * Get value for setting. * @param key name of setting * @param defaultValue default value * @return value of setting as variant. */ virtual QVariant value(const QString& key, const QVariant& defaultValue) const = 0; /** * Remove setting. * @param key name of setting */ virtual void remove(const QString& key) = 0; /** * Check if setting exists. * @param key name of setting * @return true if setting exists. */ virtual bool contains(const QString& key) const = 0; /** * Write unsaved changes to permanent storage. */ virtual void sync() = 0; protected: /** * Migrate from an old settings version. * Can be called from the constructor of derived classes to automatically * convert old settings. */ void migrateOldSettings(); }; #endif // ISETTINGS_H kid3-3.0.2/src/core/config/kid3settings.cpp000066400000000000000000000060171224603511300204520ustar00rootroot00000000000000/** * \file kid3settings.cpp * Wrapper for Qt application settings. * * \b Project: Kid3 * \author Urs Fleisch * \date 07 Apr 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "kid3settings.h" #include #include namespace { void copyOldSettings(QSettings* config) { if (!config->contains(QLatin1String("Tags/MarkTruncations"))) { // Configuration missing or not in current format QSettings oldSettings( QSettings::UserScope, QLatin1String("kid3.sourceforge.net"), QLatin1String("Kid3")); if (oldSettings.contains(QLatin1String("/kid3/General Options/ExportFormatIdx"))) { oldSettings.beginGroup(QLatin1String("/kid3")); foreach (const QString& key, oldSettings.allKeys()) { QString newKey(key); newKey.replace(QLatin1String("Recent Files"), QLatin1String("RecentFiles")); config->setValue(newKey, oldSettings.value(key)); } qDebug("Copied old settings"); } } } } /** * Constructor. */ Kid3Settings::Kid3Settings(QSettings* config) : m_config(config) { copyOldSettings(m_config); migrateOldSettings(); } /** * Destructor. */ Kid3Settings::~Kid3Settings() { } /** * Use settings subgroup. * @param prefix group name */ void Kid3Settings::beginGroup(const QString& prefix) { m_config->beginGroup(prefix); } /** * Finnish using settings subgroup. */ void Kid3Settings::endGroup() { m_config->endGroup(); } /** * Set value for setting. * @param key name of setting * @param value value for setting */ void Kid3Settings::setValue(const QString& key, const QVariant& value) { m_config->setValue(key, value); } /** * Get value for setting. * @param key name of setting * @param defaultValue default value * @return value of setting as variant. */ QVariant Kid3Settings::value(const QString& key, const QVariant& defaultValue) const { return m_config->value(key, defaultValue); } /** * Remove setting. * @param key name of setting */ void Kid3Settings::remove(const QString& key) { m_config->remove(key); } /** * Check if setting exists. * @param key name of setting * @return true if setting exists. */ bool Kid3Settings::contains(const QString& key) const { return m_config->contains(key); } /** * Write unsaved changes to permanent storage. */ void Kid3Settings::sync() { m_config->sync(); } kid3-3.0.2/src/core/config/kid3settings.h000066400000000000000000000043311224603511300201140ustar00rootroot00000000000000/** * \file kid3settings.h * Wrapper for Qt application settings. * * \b Project: Kid3 * \author Urs Fleisch * \date 07 Apr 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KID3SETTINGS_H #define KID3SETTINGS_H #include "isettings.h" class QSettings; /** * Wrapper for KDE application settings. */ class KID3_CORE_EXPORT Kid3Settings : public ISettings { public: /** * Constructor. * @param config Qt settings */ explicit Kid3Settings(QSettings* config); /** * Destructor. */ virtual ~Kid3Settings(); /** * Use settings subgroup. * @param prefix group name */ virtual void beginGroup(const QString& prefix); /** * Finnish using settings subgroup. */ virtual void endGroup(); /** * Set value for setting. * @param key name of setting * @param value value for setting */ virtual void setValue(const QString& key, const QVariant& value); /** * Get value for setting. * @param key name of setting * @param defaultValue default value * @return value of setting as variant. */ virtual QVariant value(const QString& key, const QVariant& defaultValue) const; /** * Remove setting. * @param key name of setting */ virtual void remove(const QString& key); /** * Check if setting exists. * @param key name of setting * @return true if setting exists. */ virtual bool contains(const QString& key) const; /** * Write unsaved changes to permanent storage. */ virtual void sync(); private: QSettings* m_config; }; #endif // KID3SETTINGS_H kid3-3.0.2/src/core/config/networkconfig.cpp000066400000000000000000000056571224603511300207270ustar00rootroot00000000000000/** * \file networkconfig.cpp * Network related configuration. * * \b Project: Kid3 * \author Urs Fleisch * \date 29 Jun 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "networkconfig.h" #include namespace { /** Default value for web browser */ #ifdef Q_OS_MAC const char* const defaultBrowser = "open"; #else const char* const defaultBrowser = "xdg-open"; #endif } int NetworkConfig::s_index = -1; /** * Constructor. */ NetworkConfig::NetworkConfig() : StoredConfig(QLatin1String("Network")), m_useProxy(false), m_useProxyAuthentication(false) { } /** * Destructor. */ NetworkConfig::~NetworkConfig() {} /** * Persist configuration. * * @param config configuration */ void NetworkConfig::writeToConfig(ISettings* config) const { config->beginGroup(m_group); config->setValue(QLatin1String("UseProxy"), QVariant(m_useProxy)); config->setValue(QLatin1String("Proxy"), QVariant(m_proxy)); config->setValue(QLatin1String("UseProxyAuthentication"), QVariant(m_useProxyAuthentication)); config->setValue(QLatin1String("ProxyUserName"), QVariant(m_proxyUserName)); config->setValue(QLatin1String("ProxyPassword"), QVariant(m_proxyPassword)); config->setValue(QLatin1String("Browser"), QVariant(m_browser)); config->endGroup(); } /** * Read persisted configuration. * * @param config configuration */ void NetworkConfig::readFromConfig(ISettings* config) { config->beginGroup(m_group); m_useProxy = config->value(QLatin1String("UseProxy"), m_useProxy).toBool(); m_proxy = config->value(QLatin1String("Proxy"), m_proxy).toString(); m_useProxyAuthentication = config->value(QLatin1String("UseProxyAuthentication"), m_useProxyAuthentication).toBool(); m_proxyUserName = config->value(QLatin1String("ProxyUserName"), m_proxyUserName).toString(); m_proxyPassword = config->value(QLatin1String("ProxyPassword"), m_proxyPassword).toString(); #ifdef Q_OS_WIN32 m_browser = config->value(QLatin1String("Browser"), QString()).toString(); if (m_browser.isEmpty()) { m_browser = QString::fromLocal8Bit(qgetenv("ProgramFiles")); m_browser += QLatin1String("\\Internet Explorer\\IEXPLORE.EXE"); } #else m_browser = config->value(QLatin1String("Browser"), QString::fromLatin1(defaultBrowser)).toString(); #endif config->endGroup(); } kid3-3.0.2/src/core/config/networkconfig.h000066400000000000000000000034761224603511300203710ustar00rootroot00000000000000/** * \file networkconfig.h * Network related configuration. * * \b Project: Kid3 * \author Urs Fleisch * \date 29 Jun 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef NETWORKCONFIG_H #define NETWORKCONFIG_H #include "generalconfig.h" #include "kid3api.h" /** * Network related configuration. */ class KID3_CORE_EXPORT NetworkConfig : public StoredConfig { public: /** * Constructor. */ NetworkConfig(); /** * Destructor. */ virtual ~NetworkConfig(); /** * Persist configuration. * * @param config configuration */ virtual void writeToConfig(ISettings* config) const; /** * Read persisted configuration. * * @param config configuration */ virtual void readFromConfig(ISettings* config); /** true if proxy is used */ bool m_useProxy; /** proxy used for access */ QString m_proxy; /** true to use proxy authentication */ bool m_useProxyAuthentication; /** proxy user name */ QString m_proxyUserName; /** proxy password */ QString m_proxyPassword; /** web browser substituted for %b */ QString m_browser; /** Index in configuration storage */ static int s_index; }; #endif kid3-3.0.2/src/core/config/numbertracksconfig.cpp000066400000000000000000000052321224603511300217230ustar00rootroot00000000000000/** * \file numbertracksconfig.cpp * Configuration for track numbering. * * \b Project: Kid3 * \author Urs Fleisch * \date 29 Jun 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "numbertracksconfig.h" namespace { /** * Convert tag version to number tracks destination value in configuration. * @param tagVersion tag version * @return value used in configuration, kept for backwards compatibility. */ inline int tagVersionToNumberTracksDestCfg(TrackData::TagVersion tagVersion) { return static_cast(tagVersion) - 1; } /** * Convert number tracks destination value in configuration to tag version. * @param importDest value used in configuration, kept for backwards * compatibility. * @return tag version. */ inline TrackData::TagVersion numberTracksDestCfgToTagVersion(int importDest) { return TrackData::tagVersionCast(importDest + 1); } } int NumberTracksConfig::s_index = -1; /** * Constructor. */ NumberTracksConfig::NumberTracksConfig() : StoredConfig(QLatin1String("NumberTracks")), m_numberTracksDst(TrackData::TagV1), m_numberTracksStart(1) { } /** * Destructor. */ NumberTracksConfig::~NumberTracksConfig() {} /** * Persist configuration. * * @param config configuration */ void NumberTracksConfig::writeToConfig(ISettings* config) const { config->beginGroup(m_group); config->setValue(QLatin1String("NumberTracksDestination"), QVariant(tagVersionToNumberTracksDestCfg(m_numberTracksDst))); config->setValue(QLatin1String("NumberTracksStartNumber"), QVariant(m_numberTracksStart)); config->endGroup(); } /** * Read persisted configuration. * * @param config configuration */ void NumberTracksConfig::readFromConfig(ISettings* config) { config->beginGroup(m_group); m_numberTracksDst = numberTracksDestCfgToTagVersion( config->value(QLatin1String("NumberTracksDestination"), 0).toInt()); m_numberTracksStart = config->value(QLatin1String("NumberTracksStartNumber"), 1).toInt(); config->endGroup(); } kid3-3.0.2/src/core/config/numbertracksconfig.h000066400000000000000000000033131224603511300213660ustar00rootroot00000000000000/** * \file numbertracksconfig.h * Configuration for track numbering. * * \b Project: Kid3 * \author Urs Fleisch * \date 29 Jun 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef NUMBERTRACKSCONFIG_H #define NUMBERTRACKSCONFIG_H #include "generalconfig.h" #include "trackdata.h" #include "kid3api.h" /** * Configuration for track numbering. */ class KID3_CORE_EXPORT NumberTracksConfig : public StoredConfig { public: /** * Constructor. */ NumberTracksConfig(); /** * Destructor. */ virtual ~NumberTracksConfig(); /** * Persist configuration. * * @param config configuration */ virtual void writeToConfig(ISettings* config) const; /** * Read persisted configuration. * * @param config configuration */ virtual void readFromConfig(ISettings* config); /** number tracks in tags 1, tags 2, or both */ TrackData::TagVersion m_numberTracksDst; /** number tracks start number */ int m_numberTracksStart; /** Index in configuration storage */ static int s_index; }; #endif kid3-3.0.2/src/core/config/playlistconfig.cpp000066400000000000000000000072521224603511300210700ustar00rootroot00000000000000/** * \file playlistconfig.cpp * Configuration for playlist dialog. * * \b Project: Kid3 * \author Urs Fleisch * \date 16 Sep 2009 * * Copyright (C) 2009-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "playlistconfig.h" #include "config.h" int PlaylistConfig::s_index = -1; /** * Constructor. */ PlaylistConfig::PlaylistConfig() : StoredConfig(QLatin1String("Playlist")), m_useFileNameFormat(false), m_onlySelectedFiles(false), m_useSortTagField(false), m_useFullPath(false), m_writeInfo(false), m_location(PL_CurrentDirectory), m_format(PF_M3U), m_fileNameFormat(QLatin1String("%{artist} - %{album}")), m_sortTagField(QLatin1String("%{track.3}")), m_infoFormat(QLatin1String("%{artist} - %{title}")) { } /** * Destructor. */ PlaylistConfig::~PlaylistConfig() {} /** * Persist configuration. * * @param config configuration */ void PlaylistConfig::writeToConfig(ISettings* config) const { config->beginGroup(m_group); config->setValue(QLatin1String("UseFileNameFormat"), QVariant(m_useFileNameFormat)); config->setValue(QLatin1String("OnlySelectedFiles"), QVariant(m_onlySelectedFiles)); config->setValue(QLatin1String("UseSortTagField"), QVariant(m_useSortTagField)); config->setValue(QLatin1String("UseFullPath"), QVariant(m_useFullPath)); config->setValue(QLatin1String("WriteInfo"), QVariant(m_writeInfo)); config->setValue(QLatin1String("Location"), QVariant(static_cast(m_location))); config->setValue(QLatin1String("Format"), QVariant(static_cast(m_format))); config->setValue(QLatin1String("FileNameFormat"), QVariant(m_fileNameFormat)); config->setValue(QLatin1String("SortTagField"), QVariant(m_sortTagField)); config->setValue(QLatin1String("InfoFormat"), QVariant(m_infoFormat)); config->endGroup(); } /** * Read persisted configuration. * * @param config configuration */ void PlaylistConfig::readFromConfig(ISettings* config) { config->beginGroup(m_group); m_useFileNameFormat = config->value(QLatin1String("UseFileNameFormat"), m_useFileNameFormat).toBool(); m_onlySelectedFiles = config->value(QLatin1String("OnlySelectedFiles"), m_onlySelectedFiles).toBool(); m_useSortTagField = config->value(QLatin1String("UseSortTagField"), m_useSortTagField).toBool(); m_useFullPath = config->value(QLatin1String("UseFullPath"), m_useFullPath).toBool(); m_writeInfo = config->value(QLatin1String("WriteInfo"), m_writeInfo).toBool(); m_location = static_cast(config->value(QLatin1String("Location"), static_cast(m_location)).toInt()); m_format = static_cast(config->value(QLatin1String("Format"), static_cast(m_format)).toInt()); m_fileNameFormat = config->value(QLatin1String("FileNameFormat"), m_fileNameFormat).toString(); m_sortTagField = config->value(QLatin1String("SortTagField"), m_sortTagField).toString(); m_infoFormat = config->value(QLatin1String("InfoFormat"), m_infoFormat).toString(); config->endGroup(); } kid3-3.0.2/src/core/config/playlistconfig.h000066400000000000000000000052401224603511300205300ustar00rootroot00000000000000/** * \file playlistconfig.h * Configuration for playlist dialog. * * \b Project: Kid3 * \author Urs Fleisch * \date 16 Sep 2009 * * Copyright (C) 2009-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef PLAYLISTCONFIG_H #define PLAYLISTCONFIG_H #include #include "config.h" #include "generalconfig.h" #include "kid3api.h" /** * Playlist configuration. */ class KID3_CORE_EXPORT PlaylistConfig : public StoredConfig { public: /** * Playlist format. */ enum PlaylistFormat { PF_M3U, /**< M3U */ PF_PLS, /**< PLS */ PF_XSPF /**< XSPF */ }; /** * Location to create playlist. */ enum PlaylistLocation { PL_CurrentDirectory, /**< create in current directory */ PL_EveryDirectory, /**< create in every directory */ PL_TopLevelDirectory /**< create in top-level directory */ }; /** * Constructor. */ explicit PlaylistConfig(); /** * Destructor. */ virtual ~PlaylistConfig(); /** * Persist configuration. * * @param config KDE configuration */ virtual void writeToConfig(ISettings* config) const; /** * Read persisted configuration. * * @param config KDE configuration */ virtual void readFromConfig(ISettings* config); /** Use file name format if true, else directory name */ bool m_useFileNameFormat; /** Include only selected files if true, else all files */ bool m_onlySelectedFiles; /** Sort by tag field if true, else file name */ bool m_useSortTagField; /** Use full path for files in playlist if true, else relative path */ bool m_useFullPath; /** Write info format, else only list of files */ bool m_writeInfo; /** Playlist location */ PlaylistLocation m_location; /** Playlist format */ PlaylistFormat m_format; /** Playlist file name format */ QString m_fileNameFormat; /** Tag field used for sorting */ QString m_sortTagField; /** Format for additional information */ QString m_infoFormat; /** Index in configuration storage */ static int s_index; }; #endif // PLAYLISTCONFIG_H kid3-3.0.2/src/core/config/rendirconfig.cpp000066400000000000000000000063501224603511300205100ustar00rootroot00000000000000/** * \file rendirconfig.cpp * Configuration for directory renaming. * * \b Project: Kid3 * \author Urs Fleisch * \date 29 Jun 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "rendirconfig.h" /** Default directory format list */ static const char* dirFmt[] = { "%{artist} - %{album}", "%{artist} - [%{year}] %{album}", "%{artist} - %{album} (%{year})", "%{artist}/%{album}", "%{artist}/[%{year}] %{album}", "%{album}", "[%{year}] %{album}", 0 // end of StrList }; /** Default directory format list */ const char** RenDirConfig::s_defaultDirFmtList = &dirFmt[0]; namespace { /** * Convert tag version to rename directory value in configuration. * @param tagVersion tag version * @return value used in configuration, kept for backwards compatibility. */ inline int tagVersionToRenDirCfg(TrackData::TagVersion tagVersion) { int renDirSrc = static_cast(tagVersion); if (renDirSrc == 3) renDirSrc = 0; return renDirSrc; } /** * Convert rename directory value in configuration to tag version. * @param renDirSrc value used in configuration, kept for backwards * compatibility. * @return tag version. */ inline TrackData::TagVersion renDirCfgToTagVersion(int renDirSrc) { if (renDirSrc == 0) renDirSrc = 3; return TrackData::tagVersionCast(renDirSrc); } } int RenDirConfig::s_index = -1; /** * Constructor. */ RenDirConfig::RenDirConfig() : StoredConfig(QLatin1String("RenameDirectory")), m_dirFormatText(QString::fromLatin1(s_defaultDirFmtList[0])), m_dirFormatItem(0), m_renDirSrc(TrackData::TagV2V1) { } /** * Destructor. */ RenDirConfig::~RenDirConfig() {} /** * Persist configuration. * * @param config configuration */ void RenDirConfig::writeToConfig(ISettings* config) const { config->beginGroup(m_group); config->setValue(QLatin1String("DirFormatItem"), QVariant(m_dirFormatItem)); config->setValue(QLatin1String("DirFormatText"), QVariant(m_dirFormatText)); config->setValue(QLatin1String("RenameDirectorySource"), QVariant(tagVersionToRenDirCfg(m_renDirSrc))); config->endGroup(); } /** * Read persisted configuration. * * @param config configuration */ void RenDirConfig::readFromConfig(ISettings* config) { config->beginGroup(m_group); m_dirFormatItem = config->value(QLatin1String("DirFormatItem"), 0).toInt(); m_renDirSrc = renDirCfgToTagVersion(config->value(QLatin1String("RenameDirectorySource"), 0).toInt()); m_dirFormatText = config->value(QLatin1String("DirFormatText"), QString::fromLatin1(s_defaultDirFmtList[0])).toString(); config->endGroup(); } kid3-3.0.2/src/core/config/rendirconfig.h000066400000000000000000000034721224603511300201570ustar00rootroot00000000000000/** * \file rendirconfig.h * Configuration for directory renaming. * * \b Project: Kid3 * \author Urs Fleisch * \date 29 Jun 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef RENDIRCONFIG_H #define RENDIRCONFIG_H #include "generalconfig.h" #include "trackdata.h" #include "kid3api.h" /** * Configuration for directory renaming. */ class KID3_CORE_EXPORT RenDirConfig : public StoredConfig { public: /** * Constructor. */ RenDirConfig(); /** * Destructor. */ virtual ~RenDirConfig(); /** * Persist configuration. * * @param config configuration */ virtual void writeToConfig(ISettings* config) const; /** * Read persisted configuration. * * @param config configuration */ virtual void readFromConfig(ISettings* config); /** directory name format */ QString m_dirFormatText; /** index of directory name format selected */ int m_dirFormatItem; /** rename directory from tags 1, tags 2, or both */ TrackData::TagVersion m_renDirSrc; /** Default directory format list */ static const char** s_defaultDirFmtList; /** Index in configuration storage */ static int s_index; }; #endif kid3-3.0.2/src/core/config/serverimporterconfig.cpp000066400000000000000000000061171224603511300223160ustar00rootroot00000000000000/** * \file serverimporterconfig.cpp * Configuration for server import. * * \b Project: Kid3 * \author Urs Fleisch * \date 09 Oct 2006 * * Copyright (C) 2006-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "serverimporterconfig.h" #include /** * Constructor. * Set default configuration. * * @param grp configuration group */ ServerImporterConfig::ServerImporterConfig(const QString& grp) : GeneralConfig(grp), m_cgiPathUsed(true), m_additionalTagsUsed(false), m_standardTags(true), m_additionalTags(true), m_coverArt(true) { } /** * Constructor. * Used to create temporary configuration. */ ServerImporterConfig::ServerImporterConfig() : GeneralConfig(QLatin1String("Temporary")), m_cgiPathUsed(false), m_additionalTagsUsed(false), m_standardTags(false), m_additionalTags(false), m_coverArt(false) {} /** * Destructor. */ ServerImporterConfig::~ServerImporterConfig() {} /** * Persist configuration. * * @param config configuration */ void ServerImporterConfig::writeToConfig(ISettings* config) const { config->beginGroup(m_group); config->setValue(QLatin1String("Server"), QVariant(m_server)); if (m_cgiPathUsed) config->setValue(QLatin1String("CgiPath"), QVariant(m_cgiPath)); if (m_additionalTagsUsed) { config->setValue(QLatin1String("StandardTags"), QVariant(m_standardTags)); config->setValue(QLatin1String("AdditionalTags"), QVariant(m_additionalTags)); config->setValue(QLatin1String("CoverArt"), QVariant(m_coverArt)); } config->setValue(QLatin1String("WindowGeometry"), QVariant(m_windowGeometry)); config->endGroup(); } /** * Read persisted configuration. * * @param config configuration */ void ServerImporterConfig::readFromConfig(ISettings* config) { config->beginGroup(m_group); m_server = config->value(QLatin1String("Server"), m_server).toString(); if (m_cgiPathUsed) m_cgiPath = config->value(QLatin1String("CgiPath"), m_cgiPath).toString(); if (m_additionalTagsUsed) { m_standardTags = config->value(QLatin1String("StandardTags"), m_standardTags).toBool(); m_additionalTags = config->value(QLatin1String("AdditionalTags"), m_additionalTags).toBool(); m_coverArt = config->value(QLatin1String("CoverArt"), m_coverArt).toBool(); } m_windowGeometry = config->value(QLatin1String("WindowGeometry"), m_windowGeometry).toByteArray(); config->endGroup(); } kid3-3.0.2/src/core/config/serverimporterconfig.h000066400000000000000000000042071224603511300217610ustar00rootroot00000000000000/** * \file serverimporterconfig.h * Configuration for server import. * * \b Project: Kid3 * \author Urs Fleisch * \date 09 Oct 2006 * * Copyright (C) 2006-2007 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef SERVERIMPORTERCONFIG_H #define SERVERIMPORTERCONFIG_H #include "generalconfig.h" #include #include "kid3api.h" /** * Freedb configuration. */ class KID3_CORE_EXPORT ServerImporterConfig : public GeneralConfig { public: /** * Constructor. * Set default configuration. * * @param grp configuration group */ explicit ServerImporterConfig(const QString& grp); /** * Constructor. * Used to create temporary configuration. */ ServerImporterConfig(); /** * Destructor. */ virtual ~ServerImporterConfig(); /** * Persist configuration. * * @param config KDE configuration */ virtual void writeToConfig(ISettings* config) const; /** * Read persisted configuration. * * @param config KDE configuration */ virtual void readFromConfig(ISettings* config); /** server */ QString m_server; /** CGI path used for access */ QString m_cgiPath; /** window geometry */ QByteArray m_windowGeometry; /** true if CgiPath configuration is used */ bool m_cgiPathUsed; /** true if additional tags configuration is used */ bool m_additionalTagsUsed; /** standard tags imported */ bool m_standardTags; /** additional tags imported */ bool m_additionalTags; /** cover art imported */ bool m_coverArt; }; #endif kid3-3.0.2/src/core/config/tagconfig.cpp000066400000000000000000000124041224603511300177750ustar00rootroot00000000000000/** * \file tagconfig.cpp * Tag related configuration. * * \b Project: Kid3 * \author Urs Fleisch * \date 29 Jun 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "tagconfig.h" #include "taggedfile.h" #include "frame.h" namespace { /** Default value for comment name */ const char* const defaultCommentName = "COMMENT"; /** Default to filename format list */ const char* defaultPluginOrder[] = { "Id3libMetadata", "OggFlacMetadata", "TaglibMetadata", 0 }; } int TagConfig::s_index = -1; /** * Constructor. */ TagConfig::TagConfig() : StoredConfig(QLatin1String("Tags")), m_markTruncations(true), m_enableTotalNumberOfTracks(false), m_genreNotNumeric(false), m_commentName(QString::fromLatin1(defaultCommentName)), m_pictureNameItem(VP_METADATA_BLOCK_PICTURE), m_id3v2Version(ID3v2_3_0), m_textEncodingV1(QLatin1String("ISO-8859-1")), m_textEncoding(TE_ISO8859_1), m_quickAccessFrames(FrameCollection::DEFAULT_QUICK_ACCESS_FRAMES), m_trackNumberDigits(1), m_onlyCustomGenres(false), m_taggedFileFeatures(0) { } /** * Destructor. */ TagConfig::~TagConfig() {} /** * Persist configuration. * * @param config configuration */ void TagConfig::writeToConfig(ISettings* config) const { config->beginGroup(m_group); config->setValue(QLatin1String("MarkTruncations"), QVariant(m_markTruncations)); config->setValue(QLatin1String("EnableTotalNumberOfTracks"), QVariant(m_enableTotalNumberOfTracks)); config->setValue(QLatin1String("GenreNotNumeric"), QVariant(m_genreNotNumeric)); config->setValue(QLatin1String("CommentName"), QVariant(m_commentName)); config->setValue(QLatin1String("PictureNameItem"), QVariant(m_pictureNameItem)); config->setValue(QLatin1String("CustomGenres"), QVariant(m_customGenres)); config->setValue(QLatin1String("ID3v2Version"), QVariant(m_id3v2Version)); config->setValue(QLatin1String("TextEncodingV1"), QVariant(m_textEncodingV1)); config->setValue(QLatin1String("TextEncoding"), QVariant(m_textEncoding)); config->setValue(QLatin1String("QuickAccessFrames"), QVariant(m_quickAccessFrames)); config->setValue(QLatin1String("TrackNumberDigits"), QVariant(m_trackNumberDigits)); config->setValue(QLatin1String("OnlyCustomGenres"), QVariant(m_onlyCustomGenres)); config->setValue(QLatin1String("PluginOrder"), QVariant(m_pluginOrder)); config->setValue(QLatin1String("DisabledPlugins"), QVariant(m_disabledPlugins)); config->endGroup(); } /** * Read persisted configuration. * * @param config configuration */ void TagConfig::readFromConfig(ISettings* config) { config->beginGroup(m_group); m_markTruncations = config->value(QLatin1String("MarkTruncations"), m_markTruncations).toBool(); m_enableTotalNumberOfTracks = config->value(QLatin1String("EnableTotalNumberOfTracks"), m_enableTotalNumberOfTracks).toBool(); m_genreNotNumeric = config->value(QLatin1String("GenreNotNumeric"), m_genreNotNumeric).toBool(); m_commentName = config->value(QLatin1String("CommentName"), QString::fromLatin1(defaultCommentName)).toString(); m_pictureNameItem = config->value(QLatin1String("PictureNameItem"), VP_METADATA_BLOCK_PICTURE).toInt(); m_customGenres = config->value(QLatin1String("CustomGenres"), m_customGenres).toStringList(); m_id3v2Version = config->value(QLatin1String("ID3v2Version"), ID3v2_3_0).toInt(); m_textEncodingV1 = config->value(QLatin1String("TextEncodingV1"), QLatin1String("ISO-8859-1")).toString(); m_textEncoding = config->value(QLatin1String("TextEncoding"), TE_ISO8859_1).toInt(); m_quickAccessFrames = config->value(QLatin1String("QuickAccessFrames"), FrameCollection::DEFAULT_QUICK_ACCESS_FRAMES).toUInt(); m_trackNumberDigits = config->value(QLatin1String("TrackNumberDigits"), 1).toInt(); m_onlyCustomGenres = config->value(QLatin1String("OnlyCustomGenres"), m_onlyCustomGenres).toBool(); m_pluginOrder = config->value(QLatin1String("PluginOrder"), m_pluginOrder).toStringList(); m_disabledPlugins = config->value(QLatin1String("DisabledPlugins"), m_disabledPlugins).toStringList(); config->endGroup(); if (m_pluginOrder.isEmpty()) { for (const char** pn = defaultPluginOrder; *pn != 0; ++pn) { m_pluginOrder += QString::fromLatin1(*pn); } } } /** version used for new ID3v2 tags */ int TagConfig::id3v2Version() const { if (m_id3v2Version == ID3v2_3_0 && !(taggedFileFeatures() & TaggedFile::TF_ID3v23)) return ID3v2_4_0; if (m_id3v2Version == ID3v2_4_0 && !(taggedFileFeatures() & TaggedFile::TF_ID3v24)) return ID3v2_3_0; return m_id3v2Version; } kid3-3.0.2/src/core/config/tagconfig.h000066400000000000000000000162211224603511300174430ustar00rootroot00000000000000/** * \file tagconfig.h * Tag related configuration. * * \b Project: Kid3 * \author Urs Fleisch * \date 29 Jun 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef TAGCONFIG_H #define TAGCONFIG_H #include #include "generalconfig.h" #include "kid3api.h" /** * Tag related configuration. */ class KID3_CORE_EXPORT TagConfig : public StoredConfig { public: /** The ID3v2 version used for new tags. */ enum Id3v2Version { ID3v2_3_0 = 0, ID3v2_4_0 = 1 }; /** Encoding used for ID3v2 frames. */ enum TextEncoding { TE_ISO8859_1, TE_UTF16, TE_UTF8 }; /** Name for Vorbis picture. */ enum VorbisPictureName { VP_METADATA_BLOCK_PICTURE, VP_COVERART }; /** Available tag formats which can be queried at run time. */ enum TagFormatFlag { TF_ID3v2_3_0_ID3LIB = 1 << 0, TF_ID3v2_3_0_TAGLIB = 1 << 1, TF_ID3v2_4_0_TAGLIB = 1 << 2, TF_VORBIS_LIBOGG = 1 << 3 }; /** * Constructor. */ TagConfig(); /** * Destructor. */ virtual ~TagConfig(); /** * Persist configuration. * * @param config configuration */ virtual void writeToConfig(ISettings* config) const; /** * Read persisted configuration. * * @param config configuration */ virtual void readFromConfig(ISettings* config); /** * Get features provided by metadata plugins. * @return bit mask with TaggedFile::Feature flags set. * @remark This information is not stored in the configuration, it is * registered at initialization time using setTaggedFileFeatures(). */ int taggedFileFeatures() const { return m_taggedFileFeatures; } /** * Set features provided by metadata plugins. * @param taggedFileFeatures bit mask with TaggedFile::Feature flags set */ void setTaggedFileFeatures(int taggedFileFeatures) { m_taggedFileFeatures = taggedFileFeatures; } /** true to mark truncated ID3v1.1 fields */ bool markTruncations() const { return m_markTruncations; } /** Set true to mark truncated ID3v1.1 fields. */ void setMarkTruncations(bool markTruncations) { m_markTruncations = markTruncations; } /** true to write total number of tracks into track fields */ bool enableTotalNumberOfTracks() const { return m_enableTotalNumberOfTracks; } /** Set true to write total number of tracks into track fields. */ void setEnableTotalNumberOfTracks(bool enableTotalNumberOfTracks) { m_enableTotalNumberOfTracks = enableTotalNumberOfTracks; } /** true to write genres as text instead of numeric string */ bool genreNotNumeric() const { return m_genreNotNumeric; } /** Set true to write genres as text instead of numeric string. */ void setGenreNotNumeric(bool genreNotNumeric) { m_genreNotNumeric = genreNotNumeric; } /** field name used for Vorbis comment entries */ QString commentName() const { return m_commentName; } /** Set field name used for Vorbis comment entries. */ void setCommentName(const QString& commentName) { m_commentName = commentName; } /** index of field name used for Vorbis picture entries */ int pictureNameItem() const { return m_pictureNameItem; } /** Set index of field name used for Vorbis picture entries. */ void setPictureNameItem(int pictureNameItem) { m_pictureNameItem = pictureNameItem; } /** custom genres for ID3v2.3 */ QStringList customGenres() const { return m_customGenres; } /** Set custom genres for ID3v2.3. */ void setCustomGenres(const QStringList& customGenres) { m_customGenres = customGenres; } /** version used for new ID3v2 tags */ int id3v2Version() const; /** Set version used for new ID3v2 tags. */ void setId3v2Version(int id3v2Version) { m_id3v2Version = id3v2Version; } /** text encoding used for new ID3v1 tags */ QString textEncodingV1() const { return m_textEncodingV1; } /** Set text encoding used for new ID3v1 tags. */ void setTextEncodingV1(const QString& textEncodingV1) { m_textEncodingV1 = textEncodingV1; } /** text encoding used for new ID3v2 tags */ int textEncoding() const { return m_textEncoding; } /** Set text encoding used for new ID3v2 tags. */ void setTextEncoding(int textEncoding) { m_textEncoding = textEncoding; } /** frames which are displayed for Tag 2 even if not present */ quint64 quickAccessFrames() const { return m_quickAccessFrames; } /** Set frames which are displayed for Tag 2 even if not present. */ void setQuickAccessFrames(quint64 quickAccessFrames) { m_quickAccessFrames = quickAccessFrames; } /** number of digits in track number */ int trackNumberDigits() const { return m_trackNumberDigits; } /** Set number of digits in track number. */ void setTrackNumberDigits(int trackNumberDigits) { m_trackNumberDigits = trackNumberDigits; } /** true to show only custom genres in combo boxes */ bool onlyCustomGenres() const { return m_onlyCustomGenres; } /** Set true to show only custom genres in combo boxes. */ void setOnlyCustomGenres(bool onlyCustomGenres) { m_onlyCustomGenres = onlyCustomGenres; } /** The order in which meta data plugins are tried when opening a file */ QStringList pluginOrder() const { return m_pluginOrder; } /** Set the order in which meta data plugins are tried when opening a file. */ void setPluginOrder(const QStringList& pluginOrder) { m_pluginOrder = pluginOrder; } /** Disabled plugins */ QStringList disabledPlugins() const { return m_disabledPlugins; } /** Set list of disabled plugins. */ void setDisabledPlugins(const QStringList& disabledPlugins) { m_disabledPlugins = disabledPlugins; } /** * Access to list of available plugins. * @return available plugins. * @remark This information is not stored in the configuration, it is * determined when the plugins are loaded. */ QStringList& availablePlugins() { return m_availablePlugins; } /** * Get list of available plugins. * @return available plugins. */ QStringList getAvailablePlugins() const { return m_availablePlugins; } /** Index in configuration storage */ static int s_index; private: bool m_markTruncations; bool m_enableTotalNumberOfTracks; bool m_genreNotNumeric; QString m_commentName; int m_pictureNameItem; QStringList m_customGenres; int m_id3v2Version; QString m_textEncodingV1; int m_textEncoding; quint64 m_quickAccessFrames; int m_trackNumberDigits; bool m_onlyCustomGenres; QStringList m_pluginOrder; QStringList m_disabledPlugins; QStringList m_availablePlugins; int m_taggedFileFeatures; }; #endif kid3-3.0.2/src/core/config/useractionsconfig.cpp000066400000000000000000000133631224603511300215660ustar00rootroot00000000000000/** * \file useractionsconfig.cpp * User actions configuration. * * \b Project: Kid3 * \author Urs Fleisch * \date 29 Jun 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "useractionsconfig.h" #include #include int UserActionsConfig::s_index = -1; /** * Constructor. */ UserActionsConfig::UserActionsConfig() : StoredConfig(QLatin1String("MenuCommands")) { } /** * Destructor. */ UserActionsConfig::~UserActionsConfig() {} /** * Persist configuration. * * @param config configuration */ void UserActionsConfig::writeToConfig(ISettings* config) const { config->beginGroup(m_group); int cmdNr = 1; for (QList::const_iterator it = m_contextMenuCommands.begin(); it != m_contextMenuCommands.end(); ++it) { config->setValue(QString(QLatin1String("Command%1")).arg(cmdNr++), QVariant((*it).toStringList())); } // delete entries which are no longer used for (;;) { QStringList strList = config->value(QString(QLatin1String("Command%1")).arg(cmdNr), QStringList()).toStringList(); if (strList.empty()) { break; } config->remove(QString(QLatin1String("Command%1")).arg(cmdNr)); ++cmdNr; } config->endGroup(); } /** * Read persisted configuration. * * @param config configuration */ void UserActionsConfig::readFromConfig(ISettings* config) { config->beginGroup(m_group); m_contextMenuCommands.clear(); int cmdNr = 1; for (;;) { QStringList strList = config->value(QString(QLatin1String("Command%1")).arg(cmdNr), QStringList()).toStringList(); if (strList.empty()) { break; } m_contextMenuCommands.push_back(UserActionsConfig::MenuCommand(strList)); ++cmdNr; } config->endGroup(); if (cmdNr == 1) { #ifdef Q_OS_WIN32 QString prgDir = QString::fromLocal8Bit(qgetenv("ProgramFiles")); m_contextMenuCommands.push_back( UserActionsConfig::MenuCommand( QLatin1String("Windows Media Player"), QLatin1Char('"') + prgDir + QLatin1String("\\Windows Media Player\\wmplayer.exe\" %{files}"))); #elif !defined Q_OS_MAC m_contextMenuCommands.push_back( UserActionsConfig::MenuCommand(QLatin1String("Amarok"), QLatin1String("amarok %{files}"))); #endif m_contextMenuCommands.push_back( UserActionsConfig::MenuCommand(QLatin1String("Google Images"), QLatin1String("%{browser} http://images.google.com/images?q=%u{artist}%20%u{album}"))); m_contextMenuCommands.push_back( UserActionsConfig::MenuCommand(QLatin1String("Amazon"), QLatin1String("%{browser} http://www.amazon.com/s?search-alias=aps&field-keywords=%u{artist}+%u{album}"))); m_contextMenuCommands.push_back( UserActionsConfig::MenuCommand(QLatin1String("LyricWiki"), QLatin1String("%{browser} http://lyricwiki.org/%u{artist}:%u{title}"))); m_contextMenuCommands.push_back( UserActionsConfig::MenuCommand(QLatin1String("Lyrics.com"), QLatin1String("%{browser} http://www.lyrics.com/search.php?keyword=%u{artist}+%u{title}&what=all"))); m_contextMenuCommands.push_back( UserActionsConfig::MenuCommand(QLatin1String("AZLyrics"), QLatin1String("%{browser} http://search.azlyrics.com/search.php?q=%u{artist}+%u{title}"))); m_contextMenuCommands.push_back( UserActionsConfig::MenuCommand(QLatin1String("Dark Lyrics"), QLatin1String("%{browser} http://www.darklyrics.com/search?q=%u{album}"))); m_contextMenuCommands.push_back( UserActionsConfig::MenuCommand(QLatin1String("Metro Lyrics"), QLatin1String("%{browser} http://www.metrolyrics.com/search.php?category=artisttitle&search=%u{artist}+%u{title}"))); m_contextMenuCommands.push_back( UserActionsConfig::MenuCommand(QLatin1String("SongLyrics"), QLatin1String("%{browser} http://www.songlyrics.com/index.php?section=search&searchW=%u{artist}+%u{title}"))); } } /** * Constructor. * * @param name display name * @param cmd command string with argument codes * @param confirm true if confirmation required * @param showOutput true if output of command shall be shown */ UserActionsConfig::MenuCommand::MenuCommand(const QString& name, const QString& cmd, bool confirm, bool showOutput) : m_name(name), m_cmd(cmd), m_confirm(confirm), m_showOutput(showOutput) { } /** * Constructor. * * @param strList string list with encoded command */ UserActionsConfig::MenuCommand::MenuCommand(const QStringList& strList) { if (strList.size() == 3) { bool ok; uint flags = strList[2].toUInt(&ok); if (ok) { m_confirm = (flags & 1) != 0; m_showOutput = (flags & 2) != 0; m_name = strList[0]; m_cmd = strList[1]; } else { m_confirm = false; m_showOutput = false; } } } /** * Encode into string list. * * @return string list with encoded command. */ QStringList UserActionsConfig::MenuCommand::toStringList() const { QStringList strList; strList.push_back(m_name); strList.push_back(m_cmd); uint flags = (m_confirm ? 1 : 0) | (m_showOutput ? 2 : 0); strList.push_back(QString::number(flags)); return strList; } kid3-3.0.2/src/core/config/useractionsconfig.h000066400000000000000000000073201224603511300212270ustar00rootroot00000000000000/** * \file useractionsconfig.h * User actions configuration. * * \b Project: Kid3 * \author Urs Fleisch * \date 29 Jun 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef USERACTIONSCONFIG_H #define USERACTIONSCONFIG_H #include "generalconfig.h" #include "kid3api.h" /** * User actions configuration. */ class KID3_CORE_EXPORT UserActionsConfig : public StoredConfig { public: /** * External command in context menu. */ class MenuCommand { public: /** * Constructor. * * @param name display name * @param cmd command string with argument codes * @param confirm true if confirmation required * @param showOutput true if output of command shall be shown */ explicit MenuCommand(const QString& name = QString(), const QString& cmd = QString(), bool confirm = false, bool showOutput = false); /** * Constructor. * * @param strList string list with encoded command */ explicit MenuCommand(const QStringList& strList); /** * Encode into string list. * * @return string list with encoded command. */ QStringList toStringList() const; /** * Get the display name. * @return name. */ const QString& getName() const { return m_name; } /** * Set the display name. * @param name display name */ void setName(const QString& name) { m_name = name; } /** * Get the command string. * @return command string. */ const QString& getCommand() const { return m_cmd; } /** * Set the command string. * @param cmd command string. */ void setCommand(const QString& cmd) { m_cmd = cmd; } /** * Check if command must be confirmed. * @return true if command has to be confirmed. */ bool mustBeConfirmed() const { return m_confirm; } /** * Set if command must be confirmed. * @param confirm true if command has to be confirmed */ void setMustBeConfirmed(bool confirm) { m_confirm = confirm; } /** * Check if command output has to be shown. * @return true if command output has to be shown. */ bool outputShown() const { return m_showOutput; } /** * Set if command output has to be shown. * @param showOutput true if command output has to be shown */ void setOutputShown(bool showOutput) { m_showOutput = showOutput; } private: QString m_name; QString m_cmd; bool m_confirm; bool m_showOutput; }; /** * Constructor. */ UserActionsConfig(); /** * Destructor. */ virtual ~UserActionsConfig(); /** * Persist configuration. * * @param config configuration */ virtual void writeToConfig(ISettings* config) const; /** * Read persisted configuration. * * @param config configuration */ virtual void readFromConfig(ISettings* config); /** commands available in context menu */ QList m_contextMenuCommands; /** Index in configuration storage */ static int s_index; }; #endif kid3-3.0.2/src/core/export/000077500000000000000000000000001224603511300154035ustar00rootroot00000000000000kid3-3.0.2/src/core/export/Sources.cmake000066400000000000000000000001371224603511300200310ustar00rootroot00000000000000set(export_SRCS export/playlistcreator.cpp export/textexporter.cpp ) set(export_MOC_HDRS) kid3-3.0.2/src/core/export/playlistcreator.cpp000066400000000000000000000176001224603511300213340ustar00rootroot00000000000000/** * \file playlistcreator.cpp * Playlist creator. * * \b Project: Kid3 * \author Urs Fleisch * \date 21 Sep 2009 * * Copyright (C) 2009-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "playlistcreator.h" #include #include #include #include #include "playlistconfig.h" #include "taggedfile.h" #include "trackdata.h" #include "fileproxymodel.h" #include "config.h" /** * Constructor. * * @param topLevelDir top-level directory of playlist * @param cfg playlist configuration */ PlaylistCreator::PlaylistCreator(const QString& topLevelDir, const PlaylistConfig& cfg) : m_cfg(cfg) { if (m_cfg.m_location == PlaylistConfig::PL_TopLevelDirectory) { m_playlistDirName = topLevelDir; if (!m_playlistDirName.endsWith(QChar(QDir::separator()))) { m_playlistDirName += QDir::separator(); } } } /** * Write playlist containing added Entry elements. * * @return true if ok. */ bool PlaylistCreator::write() { bool ok = true; if (!m_playlistFileName.isEmpty()) { QFile file(m_playlistDirName + m_playlistFileName); ok = file.open(QIODevice::WriteOnly); if (ok) { QTextStream stream(&file); switch (m_cfg.m_format) { case PlaylistConfig::PF_M3U: if (m_cfg.m_writeInfo) { stream << "#EXTM3U\n"; } for (QMap::const_iterator it = m_entries.begin(); it != m_entries.end(); ++it) { if (m_cfg.m_writeInfo) { stream << QString(QLatin1String("#EXTINF:%1,%2\n")). arg((*it).duration).arg((*it).info); } stream << (*it).filePath << "\n"; } break; case PlaylistConfig::PF_PLS: { unsigned nr = 1; stream << "[playlist]\n"; stream << QString(QLatin1String("NumberOfEntries=%1\n")).arg(m_entries.size()); for (QMap::const_iterator it = m_entries.begin(); it != m_entries.end(); ++it) { stream << QString(QLatin1String("File%1=%2\n")).arg(nr).arg((*it).filePath); if (m_cfg.m_writeInfo) { stream << QString(QLatin1String("Title%1=%2\n")).arg(nr).arg((*it).info); stream << QString(QLatin1String("Length%1=%2\n")).arg(nr).arg((*it).duration); } ++nr; } stream << "Version=2\n"; } break; case PlaylistConfig::PF_XSPF: { stream << "\n"; QString line = QLatin1String("\n"); stream << line; stream << " \n"; for (QMap::const_iterator it = m_entries.begin(); it != m_entries.end(); ++it) { stream << " \n"; QUrl url((*it).filePath); if (m_cfg.m_useFullPath) { url.setScheme(QLatin1String("file")); } stream << QString(QLatin1String(" %1\n")).arg( QString::fromLatin1(url.toEncoded().data())); if (m_cfg.m_writeInfo) { // the info is already formatted in the case of XSPF stream << (*it).info; } stream << " \n"; } stream << " \n"; stream << "\n"; } break; } m_entries.clear(); m_playlistFileName = QLatin1String(""); file.close(); } } return ok; } /** * Constructor. * * @param index model index * @param ctr associated playlist creator */ PlaylistCreator::Item::Item(const QModelIndex& index, PlaylistCreator& ctr) : m_ctr(ctr), m_isDir(false), m_taggedFile(FileProxyModel::getTaggedFileOfIndex(index)), m_trackData(0) { if (m_taggedFile) { m_dirName = m_taggedFile->getDirname(); } else { m_dirName = FileProxyModel::getPathIfIndexOfDir(index); m_isDir = !m_dirName.isNull(); } QChar separator = QDir::separator(); if (!m_dirName.endsWith(separator)) { m_dirName += separator; } // fix double separators m_dirName.replace(QString(separator) + separator, separator); } /** * Destructor. */ PlaylistCreator::Item::~Item() { delete m_trackData; } /** * Format string using tags and properties of item. * * @param format format string * * @return string with percent codes replaced. */ QString PlaylistCreator::Item::formatString(const QString& format) { if (!m_trackData) { m_taggedFile->readTags(false); m_taggedFile = FileProxyModel::readWithId3V24IfId3V24(m_taggedFile); m_trackData = new ImportTrackData(*m_taggedFile, ImportTrackData::TagV2V1); } return m_trackData->formatString(format); } /** * Add item to playlist. * This operation will write a playlist if the configuration is set to write * a playlist in every directory and a new directory is entered. * * @return true if ok. */ bool PlaylistCreator::Item::add() { bool ok = true; if (m_ctr.m_cfg.m_location != PlaylistConfig::PL_TopLevelDirectory) { if (m_ctr.m_playlistDirName != m_dirName) { ok = m_ctr.write(); m_ctr.m_playlistDirName = m_dirName; } } if (m_ctr.m_playlistFileName.isEmpty()) { if (!m_ctr.m_cfg.m_useFileNameFormat) { m_ctr.m_playlistFileName = QDir(m_ctr.m_playlistDirName).dirName(); } else { m_ctr.m_playlistFileName = formatString(m_ctr.m_cfg.m_fileNameFormat); } switch (m_ctr.m_cfg.m_format) { case PlaylistConfig::PF_M3U: m_ctr.m_playlistFileName += QLatin1String(".m3u"); break; case PlaylistConfig::PF_PLS: m_ctr.m_playlistFileName += QLatin1String(".pls"); break; case PlaylistConfig::PF_XSPF: m_ctr.m_playlistFileName += QLatin1String(".xspf"); break; } } QString filePath = m_dirName + m_taggedFile->getFilename(); if (!m_ctr.m_cfg.m_useFullPath && filePath.startsWith(m_ctr.m_playlistDirName)) { filePath = filePath.mid(m_ctr.m_playlistDirName.length()); } QString sortKey; if (m_ctr.m_cfg.m_useSortTagField) { sortKey = formatString(m_ctr.m_cfg.m_sortTagField); } sortKey += filePath; PlaylistCreator::Entry entry; entry.filePath = filePath; if (m_ctr.m_cfg.m_writeInfo) { if (m_ctr.m_cfg.m_format != PlaylistConfig::PF_XSPF) { entry.info = formatString(m_ctr.m_cfg.m_infoFormat); } else { entry.info = formatString(QLatin1String( " %{title}\n" " %{artist}\n" " %{album}\n" " %{track.1}\n" " %{seconds}000\n")); } TaggedFile::DetailInfo detailInfo; m_taggedFile->getDetailInfo(detailInfo); entry.duration = detailInfo.duration; } else { entry.info = QString(); entry.duration = 0; } m_ctr.m_entries.insert(sortKey, entry); return ok; } kid3-3.0.2/src/core/export/playlistcreator.h000066400000000000000000000062171224603511300210030ustar00rootroot00000000000000/** * \file playlistcreator.h * Playlist creator. * * \b Project: Kid3 * \author Urs Fleisch * \date 21 Sep 2009 * * Copyright (C) 2009-2011 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef PLAYLISTCREATOR_H #define PLAYLISTCREATOR_H #include #include class QModelIndex; class TaggedFile; class ImportTrackData; class PlaylistConfig; /** * Playlist creator. * Creates playlists from added items according to a playlist configuration. */ class PlaylistCreator { public: /** * An item from the file list which can be added to a playlist. * The item will only be added to the playlist if add() is called. */ class Item { public: /** * Constructor. * * @param index model index * @param ctr associated playlist creator */ Item(const QModelIndex& index, PlaylistCreator& ctr); /** * Destructor. */ ~Item(); /** * Check if item is a directory. * @return true if item is directory. */ bool isDir() const { return m_isDir; } /** * Check if item is a tagged file. * @return true if item is file. */ bool isFile() const { return m_taggedFile != 0; } /** * Get the directory of the item. * @return directory path with trailing separator. */ QString getDirName() const { return m_dirName; } /** * Add item to playlist. * This operation will write a playlist if the configuration is set to write * a playlist in every directory and a new directory is entered. * * @return true if ok. */ bool add(); private: /** * Format string using tags and properties of item. * * @param format format string * * @return string with percent codes replaced. */ QString formatString(const QString& format); PlaylistCreator& m_ctr; bool m_isDir; TaggedFile* m_taggedFile; ImportTrackData* m_trackData; QString m_dirName; }; /** * Constructor. * * @param topLevelDir top-level directory of playlist * @param cfg playlist configuration */ PlaylistCreator(const QString& topLevelDir, const PlaylistConfig& cfg); /** * Write playlist containing added Entry elements. * * @return true if ok. */ bool write(); private: friend class Item; struct Entry { unsigned long duration; QString filePath; QString info; }; const PlaylistConfig& m_cfg; QString m_playlistDirName; QString m_playlistFileName; QMap m_entries; }; #endif // PLAYLISTCREATOR_H kid3-3.0.2/src/core/export/textexporter.cpp000066400000000000000000000065221224603511300206710ustar00rootroot00000000000000/** * \file textexporter.cpp * Export tags as text. * * \b Project: Kid3 * \author Urs Fleisch * \date 22 Jul 2011 * * Copyright (C) 2011-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "textexporter.h" #include #include #include #include #include #include #include "exportconfig.h" #include "importconfig.h" /** * Constructor. * @param parent parent object */ TextExporter::TextExporter(QObject* parent) : QObject(parent) { setObjectName(QLatin1String("TextExporter")); } /** * Destructor. */ TextExporter::~TextExporter() { } /** * Update text from tags. * * @param headerFormat header format * @param trackFormat track format * @param trailerFormat trailer format */ void TextExporter::updateText( const QString& headerFormat, const QString& trackFormat, const QString& trailerFormat) { m_text.clear(); unsigned numTracks = m_trackDataVector.size(); unsigned trackNr = 0; for (ImportTrackDataVector::const_iterator it = m_trackDataVector.begin(); it != m_trackDataVector.end(); ++it) { if (trackNr == 0 && !headerFormat.isEmpty()) { m_text.append((*it).formatString(headerFormat)); m_text.append(QLatin1Char('\n')); } if (!trackFormat.isEmpty()) { m_text.append((*it).formatString(trackFormat)); m_text.append(QLatin1Char('\n')); } if (trackNr == numTracks - 1 && !trailerFormat.isEmpty()) { m_text.append((*it).formatString(trailerFormat)); m_text.append(QLatin1Char('\n')); } ++trackNr; } } /** * Update text from tags using formats from the configuration. * * int fmtIdx index of format */ void TextExporter::updateTextUsingConfig(int fmtIdx) { const QStringList& headerFmts = ExportConfig::instance().m_exportFormatHeaders; const QStringList& trackFmts = ExportConfig::instance().m_exportFormatTracks; const QStringList& trailerFmts = ExportConfig::instance().m_exportFormatTrailers; if (fmtIdx < headerFmts.size() && fmtIdx < trackFmts.size() && fmtIdx < trailerFmts.size()) { updateText(headerFmts.at(fmtIdx), trackFmts.at(fmtIdx), trailerFmts.at(fmtIdx)); } } /** * Export to a file. * * @param fn file name * * @return true if ok. */ bool TextExporter::exportToFile(const QString& fn) { if (!fn.isEmpty()) { QFile file(fn); if (file.open(QIODevice::WriteOnly)) { ImportConfig::instance().m_importDir = QFileInfo(file).dir().path(); QTextStream stream(&file); stream << m_text; file.close(); return true; } } return false; } /** * Export to clipboard. */ void TextExporter::exportToClipboard() { QApplication::clipboard()->setText(m_text, QClipboard::Clipboard); } kid3-3.0.2/src/core/export/textexporter.h000066400000000000000000000046361224603511300203420ustar00rootroot00000000000000/** * \file textexporter.h * Export tags as text. * * \b Project: Kid3 * \author Urs Fleisch * \date 22 Jul 2011 * * Copyright (C) 2011 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef TEXTEXPORTER_H #define TEXTEXPORTER_H #include #include "trackdata.h" #include "kid3api.h" /** * Export text from tags. */ class KID3_CORE_EXPORT TextExporter : public QObject { public: /** * Constructor. * @param parent parent object */ explicit TextExporter(QObject* parent = 0); /** * Destructor. */ virtual ~TextExporter(); /** * Set data to be exported. * * @param trackDataVector data to export */ void setTrackData(const ImportTrackDataVector& trackDataVector) { m_trackDataVector = trackDataVector; } /** * Reread the tags in the track data. * @param tagVersion tag version */ void readTagsInTrackData(TrackData::TagVersion tagVersion) { m_trackDataVector.readTags(tagVersion); } /** * Get exported text. * @return exported text. */ QString getText() const { return m_text; } /** * Update text from tags. * * @param headerFormat header format * @param trackFormat track format * @param trailerFormat trailer format */ void updateText(const QString& headerFormat, const QString& trackFormat, const QString& trailerFormat); /** * Update text from tags using formats from the configuration. * * int fmtIdx index of format */ void updateTextUsingConfig(int fmtIdx); /** * Export to a file. * * @param fn file name * * @return true if ok. */ bool exportToFile(const QString& fn); /** * Export to clipboard. */ void exportToClipboard(); private: ImportTrackDataVector m_trackDataVector; QString m_text; }; #endif // TEXTEXPORTER_H kid3-3.0.2/src/core/import/000077500000000000000000000000001224603511300153745ustar00rootroot00000000000000kid3-3.0.2/src/core/import/Sources.cmake000066400000000000000000000007521224603511300200250ustar00rootroot00000000000000set(import_SRCS import/batchimporter.cpp import/httpclient.cpp import/importclient.cpp import/importparser.cpp import/iserverimporterfactory.cpp import/iservertrackimporterfactory.cpp import/jsonparser.cpp import/serverimporter.cpp import/servertrackimporter.cpp import/textimporter.cpp import/trackdatamatcher.cpp ) set(import_MOC_HDRS import/batchimporter.h import/httpclient.h import/importclient.h import/serverimporter.h import/servertrackimporter.h ) kid3-3.0.2/src/core/import/batchimporter.cpp000066400000000000000000000345351224603511300207550ustar00rootroot00000000000000/** * \file batchimporter.cpp * Batch importer. * * \b Project: Kid3 * \author Urs Fleisch * \date 3 Jan 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "batchimporter.h" #include "serverimporter.h" #include "trackdatamodel.h" #include "downloadclient.h" #include "pictureframe.h" #include "fileconfig.h" #include "formatconfig.h" /** * Flags to store types of data which have to be imported. */ enum DataFlags { StandardTags = 1, AdditionalTags = 2, CoverArt = 4 }; /** * Constructor. * @param netMgr network access manager */ BatchImporter::BatchImporter(QNetworkAccessManager* netMgr) : QObject(netMgr), m_downloadClient(new DownloadClient(netMgr)), m_currentImporter(0), m_trackDataModel(0), m_albumModel(0), m_albumListItem(0), m_tagVersion(TrackData::TagNone), m_state(Idle), m_trackListNr(-1), m_sourceNr(-1), m_albumNr(-1), m_requestedData(0), m_importedData(0) { connect(m_downloadClient, SIGNAL(downloadFinished(QByteArray,QString,QString)), this, SLOT(onImageDownloaded(QByteArray,QString,QString))); m_frameFilter.enableAll(); } /** * Destructor. */ BatchImporter::~BatchImporter() { } /** * Set importers. * @param importers available importers * @param trackDataModel track data model used by importers */ void BatchImporter::setImporters(QList importers, TrackDataModel* trackDataModel) { m_importers = importers; m_trackDataModel = trackDataModel; } /** * Start batch import. * @param trackLists list of track data vectors with album tracks * @param profile batch import profile * @param tagVersion import destination tag version */ void BatchImporter::start(const QList& trackLists, const BatchImportProfile& profile, TrackData::TagVersion tagVersion) { m_trackLists = trackLists; m_profile = profile; m_tagVersion = tagVersion; emit reportImportEvent(BatchImportProfile::Started, profile.getName()); m_trackListNr = -1; m_state = CheckNextTrackList; stateTransition(); } /** * Check if operation is aborted. * * @return true if aborted. */ bool BatchImporter::isAborted() const { return m_state == Aborted; } /** * Clear state which is reported by isAborted(). */ void BatchImporter::clearAborted() { if (m_state == Aborted) { m_state = Idle; stateTransition(); } } /** * Abort batch import. */ void BatchImporter::abort() { State oldState = m_state; m_state = Aborted; if (oldState == Idle) { stateTransition(); } else if (oldState == GettingCover) { m_downloadClient->cancelDownload(); stateTransition(); } } void BatchImporter::stateTransition() { switch (m_state) { case Idle: m_trackListNr = -1; break; case CheckNextTrackList: if (m_trackDataModel) { bool searchKeyFound = false; forever { ++m_trackListNr; if (m_trackListNr < 0 || m_trackListNr >= m_trackLists.size()) { break; } const ImportTrackDataVector& trackList = m_trackLists.at(m_trackListNr); if (!trackList.isEmpty()) { m_currentArtist = trackList.getArtist(); m_currentAlbum = trackList.getAlbum(); if (m_currentArtist.isEmpty() && m_currentAlbum.isEmpty()) { // No tags available, try to guess artist and album from file name if (TaggedFile* taggedFile = trackList.first().getTaggedFile()) { FrameCollection frames; taggedFile->getTagsFromFilename(frames, FileConfig::instance().m_formatFromFilenameText); m_currentArtist = frames.getArtist(); m_currentAlbum = frames.getAlbum(); } } if (!m_currentArtist.isEmpty() || !m_currentAlbum.isEmpty()) { m_trackDataModel->setTrackData(trackList); searchKeyFound = true; break; } } } if (searchKeyFound) { m_sourceNr = -1; m_importedData = 0; m_state = CheckNextSource; } else { emit reportImportEvent(BatchImportProfile::Finished, QString()); emit finished(); m_state = Idle; } stateTransition(); } break; case CheckNextSource: m_currentImporter = 0; forever { ++m_sourceNr; if (m_sourceNr < 0 || m_sourceNr >= m_profile.getSources().size()) { break; } const BatchImportProfile::Source& profileSource = m_profile.getSources().at(m_sourceNr); if ((m_currentImporter = getImporter(profileSource.getName())) != 0) { m_requestedData = 0; if (profileSource.standardTagsEnabled()) m_requestedData |= StandardTags; if (m_currentImporter->additionalTags()) { if (profileSource.additionalTagsEnabled()) m_requestedData |= AdditionalTags; if (profileSource.coverArtEnabled()) m_requestedData |= CoverArt; } break; } } if (m_currentImporter) { emit reportImportEvent(BatchImportProfile::SourceSelected, QString::fromLatin1(m_currentImporter->name())); m_state = GettingAlbumList; } else { m_state = CheckNextTrackList; } stateTransition(); break; case GettingAlbumList: if (m_currentImporter) { emit reportImportEvent(BatchImportProfile::QueryingAlbumList, m_currentArtist + QLatin1String(" - ") + m_currentAlbum); m_albumNr = -1; m_albumModel = 0; connect(m_currentImporter, SIGNAL(findFinished(QByteArray)), this, SLOT(onFindFinished(QByteArray))); connect(m_currentImporter, SIGNAL(progress(QString,int,int)), this, SLOT(onFindProgress(QString,int,int))); m_currentImporter->find(m_currentImporter->config(), m_currentArtist, m_currentAlbum); } break; case CheckNextAlbum: m_albumListItem = 0; forever { ++m_albumNr; if (!m_albumModel || m_albumNr < 0 || m_albumNr >= m_albumModel->rowCount()) { break; } if ((m_albumListItem = static_cast(m_albumModel->item(m_albumNr, 0))) != 0 && m_albumListItem->type() == AlbumListItem::Type) { break; } } if (m_albumListItem) { m_state = GettingTracks; } else { m_state = CheckNextSource; } stateTransition(); break; case GettingTracks: if (m_albumListItem && m_currentImporter) { emit reportImportEvent(BatchImportProfile::FetchingTrackList, m_albumListItem->text()); int pendingData = m_requestedData & ~m_importedData; // Also fetch standard tags, so that accuracy can be measured m_currentImporter->setStandardTags( pendingData & (StandardTags | AdditionalTags | CoverArt)); m_currentImporter->setAdditionalTags(pendingData & AdditionalTags); m_currentImporter->setCoverArt(pendingData & CoverArt); connect(m_currentImporter, SIGNAL(albumFinished(QByteArray)), this, SLOT(onAlbumFinished(QByteArray))); connect(m_currentImporter, SIGNAL(progress(QString,int,int)), this, SLOT(onAlbumProgress(QString,int,int))); m_currentImporter->getTrackList(m_currentImporter->config(), m_albumListItem->getCategory(), m_albumListItem->getId()); } break; case GettingCover: if (m_trackDataModel) { QString imgUrl; if (m_tagVersion & TrackData::TagV2) { QString coverArtUrl = m_trackDataModel->getTrackData().getCoverArtUrl(); if (!coverArtUrl.isEmpty()) { imgUrl = DownloadClient::getImageUrl(coverArtUrl); if (!imgUrl.isEmpty()) { emit reportImportEvent(BatchImportProfile::FetchingCoverArt, coverArtUrl); m_downloadClient->startDownload(imgUrl); } } } if (imgUrl.isEmpty()) { m_state = CheckIfDone; stateTransition(); } } break; case CheckIfDone: if (m_requestedData & ~m_importedData) { m_state = CheckNextAlbum; } else { m_state = CheckNextTrackList; } stateTransition(); break; case Aborted: emit reportImportEvent(BatchImportProfile::Aborted, QString()); break; } } void BatchImporter::onFindFinished(const QByteArray& searchStr) { disconnect(m_currentImporter, SIGNAL(findFinished(QByteArray)), this, SLOT(onFindFinished(QByteArray))); disconnect(m_currentImporter, SIGNAL(progress(QString,int,int)), this, SLOT(onFindProgress(QString,int,int))); if (m_state == Aborted) { stateTransition(); } else if (m_currentImporter) { m_currentImporter->parseFindResults(searchStr); m_albumModel = m_currentImporter->getAlbumListModel(); m_state = CheckNextAlbum; stateTransition(); } } void BatchImporter::onFindProgress(const QString& text, int step, int total) { if (step == -1 && total == -1) { disconnect(m_currentImporter, SIGNAL(findFinished(QByteArray)), this, SLOT(onFindFinished(QByteArray))); disconnect(m_currentImporter, SIGNAL(progress(QString,int,int)), this, SLOT(onFindProgress(QString,int,int))); emit reportImportEvent(BatchImportProfile::Error, text); m_state = CheckNextAlbum; stateTransition(); } } void BatchImporter::onAlbumFinished(const QByteArray& albumStr) { disconnect(m_currentImporter, SIGNAL(albumFinished(QByteArray)), this, SLOT(onAlbumFinished(QByteArray))); disconnect(m_currentImporter, SIGNAL(progress(QString,int,int)), this, SLOT(onAlbumProgress(QString,int,int))); if (m_state == Aborted) { stateTransition(); } else if (m_trackDataModel && m_currentImporter) { m_currentImporter->parseAlbumResults(albumStr); int accuracy = m_trackDataModel->calculateAccuracy(); emit reportImportEvent(BatchImportProfile::TrackListReceived, tr("Accuracy") + QLatin1Char(' ') + (accuracy >= 0 ? QString::number(accuracy) + QLatin1Char('%') : tr("Unknown"))); const BatchImportProfile::Source& profileSource = m_profile.getSources().at(m_sourceNr); if (accuracy >= profileSource.getRequiredAccuracy()) { if (m_requestedData & (StandardTags | AdditionalTags)) { // Set imported data in tags of files. ImportTrackDataVector trackDataVector(m_trackDataModel->getTrackData()); for (ImportTrackDataVector::iterator it = trackDataVector.begin(); it != trackDataVector.end(); ++it) { if (TaggedFile* taggedFile = it->getTaggedFile()) { taggedFile->readTags(false); it->removeDisabledFrames(m_frameFilter); TagFormatConfig::instance().formatFramesIfEnabled(*it); if (m_tagVersion & TrackData::TagV1) { taggedFile->setFramesV1(*it, false); } if (m_tagVersion & TrackData::TagV2) { taggedFile->setFramesV2(*it, false); } } } trackDataVector.setCoverArtUrl(QString()); m_trackLists[m_trackListNr] = trackDataVector; } else { // Revert imported data. ImportTrackDataVector trackDataVector(m_trackLists.at(m_trackListNr)); trackDataVector.setCoverArtUrl( m_trackDataModel->getTrackData().getCoverArtUrl()); m_trackDataModel->setTrackData(trackDataVector); } if (m_requestedData & StandardTags) m_importedData |= StandardTags; if (m_requestedData & AdditionalTags) m_importedData |= AdditionalTags; } else { // Accuracy not sufficient => Revert imported data, check next album. m_trackDataModel->setTrackData(m_trackLists.at(m_trackListNr)); } m_state = GettingCover; stateTransition(); } } void BatchImporter::onAlbumProgress(const QString& text, int step, int total) { if (step == -1 && total == -1) { disconnect(m_currentImporter, SIGNAL(albumFinished(QByteArray)), this, SLOT(onAlbumFinished(QByteArray))); disconnect(m_currentImporter, SIGNAL(progress(QString,int,int)), this, SLOT(onAlbumProgress(QString,int,int))); emit reportImportEvent(BatchImportProfile::Error, text); m_state = GettingCover; stateTransition(); } } void BatchImporter::onImageDownloaded(const QByteArray& data, const QString& mimeType, const QString& url) { if (m_state == Aborted) { stateTransition(); } else { if (data.size() >= 1024) { if (mimeType.startsWith(QLatin1String("image")) && m_trackDataModel) { emit reportImportEvent(BatchImportProfile::CoverArtReceived, url); PictureFrame frame(data, url, PictureFrame::PT_CoverFront, mimeType); ImportTrackDataVector trackDataVector(m_trackDataModel->getTrackData()); for (ImportTrackDataVector::iterator it = trackDataVector.begin(); it != trackDataVector.end(); ++it) { if (TaggedFile* taggedFile = it->getTaggedFile()) { taggedFile->readTags(false); taggedFile->addFrameV2(frame); } } m_importedData |= CoverArt; } } else { // Probably an invalid 1x1 picture from Amazon emit reportImportEvent(BatchImportProfile::CoverArtReceived, tr("Invalid File")); } m_state = CheckIfDone; stateTransition(); } } ServerImporter* BatchImporter::getImporter(const QString& name) { foreach (ServerImporter* importer, m_importers) { if (QString::fromLatin1(importer->name()) == name) { return importer; } } return 0; } kid3-3.0.2/src/core/import/batchimporter.h000066400000000000000000000102171224603511300204110ustar00rootroot00000000000000/** * \file batchimporter.h * Batch importer. * * \b Project: Kid3 * \author Urs Fleisch * \date 3 Jan 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef BATCHIMPORTER_H #define BATCHIMPORTER_H #include #include "trackdata.h" #include "batchimportprofile.h" #include "iabortable.h" class QNetworkAccessManager; class QStandardItemModel; class DownloadClient; class ServerImporter; class TrackDataModel; class AlbumListItem; /** * Batch importer. */ class BatchImporter : public QObject, public IAbortable { Q_OBJECT public: /** * Constructor. * @param netMgr network access manager */ explicit BatchImporter(QNetworkAccessManager* netMgr); /** * Destructor. */ virtual ~BatchImporter(); /** * Check if operation is aborted. * * @return true if aborted. */ virtual bool isAborted() const; /** * Clear state which is reported by isAborted(). */ virtual void clearAborted(); /** * Set importers. * @param importers available importers * @param trackDataModel track data model used by importers */ void setImporters(QList importers, TrackDataModel* trackDataModel); /** * Start batch import. * @param trackLists list of track data vectors with album tracks * @param profile batch import profile * @param tagVersion import destination tag version */ void start(const QList& trackLists, const BatchImportProfile& profile, TrackData::TagVersion tagVersion); /** * Set frame filter to be used when importing. * @param flt frame filter */ void setFrameFilter(const FrameFilter& flt) { m_frameFilter = flt; } /** * Emit a report event. * @param type type of event * @param text additional message */ void emitReportImportEvent(BatchImportProfile::ImportEventType type, const QString& text) { emit reportImportEvent(type, text); } signals: /** * Report event. * @param type type of event * @param text additional message */ void reportImportEvent(BatchImportProfile::ImportEventType type, const QString& text); /** * Emitted when the batch import is finished. */ void finished(); public slots: /** * Abort batch import. */ virtual void abort(); private slots: void onFindFinished(const QByteArray& searchStr); void onFindProgress(const QString& text, int step, int total); void onAlbumFinished(const QByteArray& albumStr); void onAlbumProgress(const QString& text, int step, int total); void onImageDownloaded(const QByteArray& data, const QString& mimeType, const QString& url); private: enum State { Idle, CheckNextTrackList, CheckNextSource, GettingAlbumList, CheckNextAlbum, GettingTracks, GettingCover, CheckIfDone, Aborted }; void stateTransition(); ServerImporter* getImporter(const QString& name); DownloadClient* m_downloadClient; QList m_importers; ServerImporter* m_currentImporter; TrackDataModel* m_trackDataModel; QStandardItemModel* m_albumModel; AlbumListItem* m_albumListItem; QList m_trackLists; BatchImportProfile m_profile; TrackData::TagVersion m_tagVersion; State m_state; int m_trackListNr; int m_sourceNr; int m_albumNr; int m_requestedData; int m_importedData; QString m_currentArtist; QString m_currentAlbum; FrameFilter m_frameFilter; }; #endif // BATCHIMPORTER_H kid3-3.0.2/src/core/import/httpclient.cpp000066400000000000000000000177051224603511300202700ustar00rootroot00000000000000/** * \file httpclient.cpp * Client to connect to HTTP server. * * \b Project: Kid3 * \author Urs Fleisch * \date 30 Dec 2008 * * Copyright (C) 2008-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "httpclient.h" #include #include #include #include #include "networkconfig.h" /** Time when last request was sent to server */ #if QT_VERSION >= 0x040700 QMap HttpClient::s_lastRequestTime; #else QMap HttpClient::s_lastRequestTime; #endif /** Minimum interval between two requests to server in ms */ QMap HttpClient::s_minimumRequestInterval; /** * Used to initialize the minimum interval for some servers * at static initialization time. * * Rate limit requests to servers, MusicBrainz and Discogs impose a limit of * one request per second * http://musicbrainz.org/doc/XML_Web_Service/Rate_Limiting#Source_IP_address * http://www.discogs.com/developers/accessing.html#rate-limiting */ static struct MinimumRequestIntervalInitializer { MinimumRequestIntervalInitializer() { HttpClient::s_minimumRequestInterval[QLatin1String("musicbrainz.org")] = 1000; HttpClient::s_minimumRequestInterval[QLatin1String("api.discogs.com")] = 1000; HttpClient::s_minimumRequestInterval[QLatin1String("www.amazon.com")] = 1000; HttpClient::s_minimumRequestInterval[QLatin1String("images.amazon.com")] = 1000; HttpClient::s_minimumRequestInterval[QLatin1String("www.gnudb.org")] = 1000; HttpClient::s_minimumRequestInterval[QLatin1String("gnudb.gnudb.org")] = 1000; HttpClient::s_minimumRequestInterval[QLatin1String("tracktype.org")] = 1000; HttpClient::s_minimumRequestInterval[QLatin1String("api.acoustid.org")] = 1000; } } minimumRequestIntervalInitializer; /** * Constructor. * * @param netMgr network access manager */ HttpClient::HttpClient(QNetworkAccessManager* netMgr) : QObject(netMgr), m_netMgr(netMgr), m_rcvBodyLen(0), m_requestTimer(new QTimer(this)) { setObjectName(QLatin1String("HttpClient")); m_requestTimer->setSingleShot(true); connect(m_requestTimer, SIGNAL(timeout()), this, SLOT(delayedSendRequest())); } /** * Destructor. */ HttpClient::~HttpClient() { if (m_reply) { m_reply->close(); m_reply->disconnect(); m_reply->deleteLater(); } } /** Only defined for generation of translation files */ #define DATA_RECEIVED_FOR_PO QT_TRANSLATE_NOOP("@default", "Data received: %1") /** * Called when the request is finished. */ void HttpClient::networkReplyFinished() { if (QNetworkReply* reply = qobject_cast(sender())) { QByteArray data(reply->readAll()); m_rcvBodyType = reply->header(QNetworkRequest::ContentTypeHeader).toString(); m_rcvBodyLen = reply->header(QNetworkRequest::ContentLengthHeader).toUInt(); emit bytesReceived(data); QString msg(tr("Ready.")); if (reply->error() != QNetworkReply::NoError) { msg = tr("Error"); msg += QLatin1String(": "); msg += reply->errorString(); } emitProgress(msg, data.size(), data.size()); reply->deleteLater(); } } /** * Called to report connection progress. * * @param received bytes received * @param total total bytes */ void HttpClient::networkReplyProgress(qint64 received, qint64 total) { emitProgress(tr("Data received: %1").arg(received), received, total); } /** * Called when an error occurred. * * @param code error code */ void HttpClient::networkReplyError(QNetworkReply::NetworkError) { if (QNetworkReply* reply = qobject_cast(sender())) { emitProgress(reply->errorString(), -1, -1); } } /** * Send a HTTP GET request. * * @param server host name * @param path path of the URL */ void HttpClient::sendRequest(const QString& server, const QString& path, const RawHeaderMap& headers) { QString host(server); if (host.endsWith(QLatin1String(":80"))) { host.chop(3); } qint64 msSinceLastRequest; int minimumRequestInterval; #if QT_VERSION >= 0x040700 QDateTime now = QDateTime::currentDateTime(); QDateTime lastRequestTime = s_lastRequestTime.value(host); #else QTime now = QTime::currentTime(); QTime lastRequestTime = s_lastRequestTime.value(host); #endif if (lastRequestTime.isValid() && (minimumRequestInterval = s_minimumRequestInterval.value(host)) > 0 && (msSinceLastRequest = lastRequestTime.msecsTo(now)) < minimumRequestInterval) { // Delay request to comply with minimum interval m_delayedSendRequestContext.server = server; m_delayedSendRequestContext.path = path; m_delayedSendRequestContext.headers = headers; m_requestTimer->start(minimumRequestInterval - msSinceLastRequest); return; } m_rcvBodyLen = 0; m_rcvBodyType = QLatin1String(""); QString proxy, username, password; int proxyPort = 0; QNetworkProxy::ProxyType proxyType = QNetworkProxy::NoProxy; if (NetworkConfig::instance().m_useProxy) { splitNamePort(NetworkConfig::instance().m_proxy, proxy, proxyPort); proxyType = QNetworkProxy::HttpProxy; } if (NetworkConfig::instance().m_useProxyAuthentication) { username = NetworkConfig::instance().m_proxyUserName; password = NetworkConfig::instance().m_proxyPassword; } m_netMgr->setProxy(QNetworkProxy(proxyType, proxy, proxyPort, username, password)); QUrl url; #if QT_VERSION >= 0x050000 url.setUrl(QLatin1String("http://") + host + path); #else url.setEncodedUrl((QLatin1String("http://") + host + path).toAscii()); #endif QNetworkRequest request(url); for (RawHeaderMap::const_iterator it = headers.constBegin(); it != headers.constEnd(); ++it) { request.setRawHeader(it.key(), it.value()); } QNetworkReply* reply = m_netMgr->get(request); m_reply = reply; connect(reply, SIGNAL(finished()), this, SLOT(networkReplyFinished())); connect(reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(networkReplyProgress(qint64,qint64))); connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(networkReplyError(QNetworkReply::NetworkError))); s_lastRequestTime[host] = now; emitProgress(tr("Request sent..."), 0, 0); } /** * Called to start delayed sendRequest(). */ void HttpClient::delayedSendRequest() { sendRequest(m_delayedSendRequestContext.server, m_delayedSendRequestContext.path, m_delayedSendRequestContext.headers); } /** * Abort request. */ void HttpClient::abort() { if (m_reply) { m_reply->abort(); } } /** * Emit a progress signal with step/total steps. * * @param text state text * @param step current step * @param totalSteps total number of steps */ void HttpClient::emitProgress(const QString& text, int step, int totalSteps) { emit progress(text, step, totalSteps); } /** * Extract name and port from string. * * @param namePort input string with "name:port" * @param name output string with "name" * @param port output integer with port */ void HttpClient::splitNamePort(const QString& namePort, QString& name, int& port) { int colPos = namePort.lastIndexOf(QLatin1Char(':')); if (colPos >= 0) { bool ok; port = namePort.mid(colPos + 1).toInt(&ok); if (!ok) port = 80; name = namePort.left(colPos); } else { name = namePort; port = 80; } } kid3-3.0.2/src/core/import/httpclient.h000066400000000000000000000114111224603511300177210ustar00rootroot00000000000000/** * \file httpclient.h * Client to connect to HTTP server. * * \b Project: Kid3 * \author Urs Fleisch * \date 30 Dec 2008 * * Copyright (C) 2008-2012 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef HTTPCLIENT_H #define HTTPCLIENT_H #include #include #include #include #include #include #include #include "kid3api.h" class QByteArray; class QNetworkAccessManager; /** * Client to connect to HTTP server. */ class KID3_CORE_EXPORT HttpClient : public QObject { Q_OBJECT public: /** Name-value map for raw HTTP headers. */ typedef QMap RawHeaderMap; /** * Constructor. * * @param netMgr network access manager */ explicit HttpClient(QNetworkAccessManager* netMgr); /** * Destructor. */ virtual ~HttpClient(); /** * Send a HTTP GET request. * * @param server host name * @param path path of the URL * @param headers optional raw headers to send */ void sendRequest(const QString& server, const QString& path, const RawHeaderMap& headers = RawHeaderMap()); /** * Abort request. */ void abort(); /** * Get content length. * @return size of body in bytes, 0 if unknown. */ unsigned long getContentLength() const { return m_rcvBodyLen; } /** * Get content type. * @return MIME type, empty if unknown. */ QString getContentType() const { return m_rcvBodyType; } /** * Extract name and port from string. * * @param namePort input string with "name:port" * @param name output string with "name" * @param port output integer with port */ static void splitNamePort(const QString& namePort, QString& name, int& port); signals: /** * Emitted to report progress. * Parameter: state text, bytes received, total bytes. */ void progress(const QString&, int, int); /** * Emitted when response received. * Parameter: bytes containing result of request */ void bytesReceived(const QByteArray&); private slots: /** * Called when the request is finished. */ void networkReplyFinished(); /** * Called to report connection progress. * * @param received bytes received * @param total total bytes */ void networkReplyProgress(qint64 received, qint64 total); /** * Called when an error occurred. * * @param code error code */ void networkReplyError(QNetworkReply::NetworkError); /** * Called to start delayed sendRequest(). */ void delayedSendRequest(); private: /** * Emit a progress signal with step/total steps. * * @param text state text * @param step current step * @param totalSteps total number of steps */ void emitProgress(const QString& text, int step, int totalSteps); /** * Emit a progress signal with bytes received/total bytes. * * @param text state text */ void emitProgress(const QString& text); /** * Read the available bytes. */ void readBytesAvailable(); /** * Get string with proxy or destination and port. * If a proxy is set, the proxy is returned, else the real destination. * * @param dst real destination * * @return "destinationname:port". */ static QString getProxyOrDest(const QString& dst); /** network access manager */ QNetworkAccessManager* m_netMgr; /** network reply if available, else 0 */ QPointer m_reply; /** content length of entitiy-body, 0 if not available */ unsigned long m_rcvBodyLen; /** content type */ QString m_rcvBodyType; /** Timer used to delay requests */ QTimer* m_requestTimer; /** Context for delayedSendRequest() */ struct { QString server; QString path; RawHeaderMap headers; } m_delayedSendRequestContext; friend struct MinimumRequestIntervalInitializer; /** Time when last request was sent to server */ #if QT_VERSION >= 0x040700 static QMap s_lastRequestTime; #else static QMap s_lastRequestTime; #endif /** Minimum interval between two requests to server in ms */ static QMap s_minimumRequestInterval; }; #endif kid3-3.0.2/src/core/import/importclient.cpp000066400000000000000000000056021224603511300206140ustar00rootroot00000000000000/** * \file importclient.cpp * Client to connect to server with import data. * * \b Project: Kid3 * \author Urs Fleisch * \date 09 Oct 2006 * * Copyright (C) 2006-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "importclient.h" #include #include #include #include "serverimporterconfig.h" /** * Constructor. * * @param netMgr network access manager */ ImportClient::ImportClient(QNetworkAccessManager* netMgr) : HttpClient(netMgr), m_requestType(RT_None) { setObjectName(QLatin1String("ImportClient")); connect(this, SIGNAL(bytesReceived(QByteArray)), this, SLOT(requestFinished(QByteArray))); } /** * Destructor. */ ImportClient::~ImportClient() { } /** * Find keyword on server. * * @param cfg import source configuration * @param artist artist to search * @param album album to search */ void ImportClient::find(const ServerImporterConfig* cfg, const QString& artist, const QString& album) { sendFindQuery(cfg, artist, album); m_requestType = RT_Find; } /** * Handle response when request is finished. * The data is sent to other objects via signals. * * @param rcvStr received data */ void ImportClient::requestFinished(const QByteArray& rcvStr) { switch (m_requestType) { case RT_Album: emit albumFinished(rcvStr); break; case RT_Find: emit findFinished(rcvStr); break; default: qWarning("Unknown import request type"); } } /** * Request track list from server. * * @param cfg import source configuration * @param cat category * @param id ID */ void ImportClient::getTrackList(const ServerImporterConfig* cfg, QString cat, QString id) { sendTrackListQuery(cfg, cat, id); m_requestType = RT_Album; } /** * Encode a query in an URL. * The query is percent-encoded with spaces collapsed and replaced by '+'. * * @param query query to encode * * @return encoded query. */ QString ImportClient::encodeUrlQuery(const QString& query) { QString result(query); result.replace(QRegExp(QLatin1String(" +")), QLatin1String(" ")); // collapse spaces result = QString::fromLatin1(QUrl::toPercentEncoding(result)); result.replace(QLatin1String("%20"), QLatin1String("+")); // replace spaces by '+' return result; } kid3-3.0.2/src/core/import/importclient.h000066400000000000000000000066201224603511300202620ustar00rootroot00000000000000/** * \file importclient.h * Client to connect to server with import data. * * \b Project: Kid3 * \author Urs Fleisch * \date 09 Oct 2006 * * Copyright (C) 2006-2011 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef IMPORTCLIENT_H #define IMPORTCLIENT_H #include "httpclient.h" #include "kid3api.h" class ServerImporterConfig; /** * Client to connect to server with import data. */ class KID3_CORE_EXPORT ImportClient : public HttpClient { Q_OBJECT public: /** * Constructor. * * @param netMgr network access manager */ explicit ImportClient(QNetworkAccessManager* netMgr); /** * Destructor. */ virtual ~ImportClient(); /** * Send a query command to search on the server. * This method has to be reimplemented for the specific search command. * * @param cfg import source configuration * @param artist artist to search * @param album album to search */ virtual void sendFindQuery( const ServerImporterConfig* cfg, const QString& artist, const QString& album) = 0; /** * Send a query command to fetch the track list * from the server. * This method has to be reimplemented for the specific server. * * @param cfg import source configuration * @param cat category * @param id ID */ virtual void sendTrackListQuery( const ServerImporterConfig* cfg, const QString& cat, const QString& id) = 0; /** * Find artist, album on server. * * @param cfg import source configuration * @param artist artist to search * @param album album to search */ void find(const ServerImporterConfig* cfg, const QString& artist, const QString& album); /** * Request track list from server. * * @param cfg import source configuration * @param cat category * @param id ID */ void getTrackList(const ServerImporterConfig* cfg, QString cat, QString id); /** * Encode a query in an URL. * The query is percent-encoded with spaces collapsed and replaced by '+'. * * @param query query to encode * * @return encoded query. */ static QString encodeUrlQuery(const QString& query); signals: /** * Emitted when find request finished. * Parameter: text containing result of find request */ void findFinished(const QByteArray&); /** * Emitted when album track data request finished. * Parameter: text containing result of album request */ void albumFinished(const QByteArray&); private slots: /** * Handle response when request is finished. * The data is sent to other objects via signals. * * @param rcvStr received data */ void requestFinished(const QByteArray& rcvStr); private: /** type of current request */ enum RequestType { RT_None, RT_Find, RT_Album } m_requestType; }; #endif kid3-3.0.2/src/core/import/importparser.cpp000066400000000000000000000142401224603511300206300ustar00rootroot00000000000000/** * \file importparser.cpp * Import parser. * * \b Project: Kid3 * \author Urs Fleisch * \date 17 Sep 2003 * * Copyright (C) 2003-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "importparser.h" #include #include "frame.h" #include "genres.h" /** * Constructor. */ ImportParser::ImportParser() : m_trackIncrEnabled(false), m_trackIncrNr(0) { } /** * Get help text for format codes supported by setFormat(). * * @return help text. */ QString ImportParser::getFormatToolTip() { QString str; str += QLatin1String("\n"); str += QLatin1String("\n"); str += QLatin1String("\n"); str += QLatin1String("\n"); str += QLatin1String("\n"); str += QLatin1String("\n"); str += QLatin1String("\n"); str += QLatin1String("\n"); str += QLatin1String("\n"); str += QLatin1String("
%s%{title}"); str += QCoreApplication::translate("@default", "Title"); str += QLatin1String("
%l%{album}"); str += QCoreApplication::translate("@default", "Album"); str += QLatin1String("
%a%{artist}"); str += QCoreApplication::translate("@default", "Artist"); str += QLatin1String("
%c%{comment}"); str += QCoreApplication::translate("@default", "Comment"); str += QLatin1String("
%y%{year}"); str += QCoreApplication::translate("@default", QT_TRANSLATE_NOOP("@default", "Year")); str += QLatin1String("
%t%{track}"); str += QCoreApplication::translate("@default", "Track"); str += QLatin1String("
%g%{genre}"); str += QCoreApplication::translate("@default", "Genre"); str += QLatin1String("
%d%{duration}"); str += QCoreApplication::translate("@default", QT_TRANSLATE_NOOP("@default", "Length")); str += QLatin1String("
\n"); return str; } /** * Set import format. * * @param fmt format regexp * @param enableTrackIncr enable automatic track increment if no %t is found */ void ImportParser::setFormat(const QString& fmt, bool enableTrackIncr) { static const struct { const char* from; const char* to; } codeToName[] = { { "%s", "%{title}" }, { "%l", "%{album}" }, { "%a", "%{artist}" }, { "%c", "%{comment}" }, { "%y", "%{date}" }, { "%t", "%{track number}" }, { "%g", "%{genre}" }, { "%d", "%{__duration}" }, { "%{year}", "%{date}" }, { "%{track}", "%{track number}" }, { "%{tracknumber}", "%{track number}" }, { "%{duration}", "%{__duration}" }, }; int percentIdx = 0, nr = 1, lastIdx = fmt.length() - 1; m_pattern = fmt; for (unsigned i = 0; i < sizeof(codeToName) / sizeof(codeToName[0]); ++i) { m_pattern.replace(QString::fromLatin1(codeToName[i].from), QString::fromLatin1(codeToName[i].to)); } m_codePos.clear(); while (((percentIdx = m_pattern.indexOf(QLatin1String("%{"), percentIdx)) >= 0) && (percentIdx < lastIdx)) { int closingBracePos = m_pattern.indexOf(QLatin1String("}("), percentIdx + 2); if (closingBracePos > percentIdx + 2) { QString code = m_pattern.mid(percentIdx + 2, closingBracePos - percentIdx - 2); m_codePos[code] = nr; percentIdx = closingBracePos + 2; ++nr; } else { percentIdx += 2; } } if (enableTrackIncr && !m_codePos.contains(QLatin1String("track number"))) { m_trackIncrEnabled = true; m_trackIncrNr = 1; } else { m_trackIncrEnabled = false; m_trackIncrNr = 0; } m_pattern.remove(QRegExp(QLatin1String("%\\{[^}]+\\}"))); m_re.setPattern(m_pattern); } /** * Get next tags in text buffer. * * @param text text buffer containing data from file or clipboard * @param frames frames for output * @param pos current position in buffer, will be updated to point * behind current match (to be used for next call) * @return true if tags found (pos is index behind match). */ bool ImportParser::getNextTags(const QString& text, FrameCollection& frames, int& pos) { int idx, oldpos = pos; if (m_pattern.isEmpty()) { m_trackDuration.clear(); return false; } if (!m_codePos.contains(QLatin1String("__duration"))) { m_trackDuration.clear(); } else if (pos == 0) { m_trackDuration.clear(); int dsp = 0; // "duration search pos" int lastDsp = dsp; while ((idx = m_re.indexIn(text, dsp)) != -1) { QString durationStr = m_re.cap(m_codePos[QLatin1String("__duration")]); int duration; QRegExp durationRe(QLatin1String("(\\d+):(\\d+)")); if (durationRe.indexIn(durationStr) != -1) { duration = durationRe.cap(1).toInt() * 60 + durationRe.cap(2).toInt(); } else { duration = durationStr.toInt(); } m_trackDuration.append(duration); dsp = idx + m_re.matchedLength(); if (dsp > lastDsp) { /* avoid endless loop */ lastDsp = dsp; } else { break; } } } if ((idx = m_re.indexIn(text, pos)) != -1) { for (QMap::iterator it = m_codePos.begin(); it != m_codePos.end(); ++it) { QString name = it.key(); QString str = m_re.cap(*it); if (!str.isEmpty() && !name.startsWith(QLatin1String("__"))) { frames.setValue(Frame::ExtendedType(name), str); } } if (m_trackIncrEnabled) { frames.setTrack(m_trackIncrNr++); } pos = idx + m_re.matchedLength(); if (pos > oldpos) { /* avoid endless loop */ return true; } } return false; } kid3-3.0.2/src/core/import/importparser.h000066400000000000000000000045301224603511300202760ustar00rootroot00000000000000/** * \file importparser.h * Import parser. * * \b Project: Kid3 * \author Urs Fleisch * \date 17 Sep 2003 * * Copyright (C) 2003-2007 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef IMPORTPARSER_H #define IMPORTPARSER_H #include #include #include #include #include "kid3api.h" class FrameCollection; /** * Import parser. */ class KID3_CORE_EXPORT ImportParser { public: /** * Constructor. */ ImportParser(); /** * Set import format. * * @param fmt format regexp * @param enableTrackIncr enable automatic track increment if no %t is found */ void setFormat(const QString& fmt, bool enableTrackIncr = false); /** * Get next tags in text buffer. * * @param text text buffer containing data from file or clipboard * @param frames frames for output * @param pos current position in buffer, will be updated to point * behind current match (to be used for next call) * @return true if tags found (pos is index behind match). */ bool getNextTags(const QString& text, FrameCollection& frames, int& pos); /** * Get list with track durations. * * @return list with track durations. */ QList getTrackDurations() const { return m_trackDuration; } /** * Get help text for format codes supported by setFormat(). * * @return help text. */ static QString getFormatToolTip(); private: /** track regexp pattern */ QString m_pattern; /** regexp object */ QRegExp m_re; /** true if automatic track number incrementing is used */ bool m_trackIncrEnabled; /** automatically incremented track number */ int m_trackIncrNr; QMap m_codePos; QList m_trackDuration; }; #endif kid3-3.0.2/src/core/import/iserverimporterfactory.cpp000066400000000000000000000022551224603511300227350ustar00rootroot00000000000000/** * \file iserverimporterfactory.cpp * Interface for server importer factory. * * \b Project: Kid3 * \author Urs Fleisch * \date 13 Jul 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "iserverimporterfactory.h" /** * Destructor. */ IServerImporterFactory::~IServerImporterFactory() { // Just defining "virtual ~IServerImporterFactory() {}" in the header file // will lead to unresolved symbols when building with shared libraries on // Windows and a class from another library inherits from this class. } kid3-3.0.2/src/core/import/iserverimporterfactory.h000066400000000000000000000037231224603511300224030ustar00rootroot00000000000000/** * \file iserverimporterfactory.h * Interface for server importer factory. * * \b Project: Kid3 * \author Urs Fleisch * \date 13 Jul 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef ISERVERIMPORTERFACTORY_H #define ISERVERIMPORTERFACTORY_H #include #include "kid3api.h" class QString; class QStringList; class QNetworkAccessManager; class TrackDataModel; class ServerImporter; /** * Interface for server importer factory. */ class KID3_CORE_EXPORT IServerImporterFactory { public: /** * Destructor. */ virtual ~IServerImporterFactory() = 0; /** * Get keys of available server importers. * @return list of keys. */ virtual QStringList serverImporterKeys() const = 0; /** * Create server importer. * @param key server importer key * @param netMgr network access manager * @param trackDataModel track data to be filled with imported values * @return server importer instance, 0 if key unknown. * @remarks The caller takes ownership of the returned instance. */ virtual ServerImporter* createServerImporter( const QString& key, QNetworkAccessManager* netMgr, TrackDataModel* trackDataModel) = 0; }; Q_DECLARE_INTERFACE(IServerImporterFactory, "net.sourceforge.kid3.IServerImporterFactory") #endif // ISERVERIMPORTERFACTORY_H kid3-3.0.2/src/core/import/iservertrackimporterfactory.cpp000066400000000000000000000023071224603511300237600ustar00rootroot00000000000000/** * \file iservertrackimporterfactory.cpp * Interface for server track importer factory. * * \b Project: Kid3 * \author Urs Fleisch * \date 21 Jul 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "iservertrackimporterfactory.h" /** * Destructor. */ IServerTrackImporterFactory::~IServerTrackImporterFactory() { // Just defining "virtual ~IServerImporterFactory() {}" in the header file // will lead to unresolved symbols when building with shared libraries on // Windows and a class from another library inherits from this class. } kid3-3.0.2/src/core/import/iservertrackimporterfactory.h000066400000000000000000000040331224603511300234230ustar00rootroot00000000000000/** * \file iservertrackimporterfactory.h * Interface for server track importer factory. * * \b Project: Kid3 * \author Urs Fleisch * \date 21 Jul 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef ISERVERTRACKIMPORTERFACTORY_H #define ISERVERTRACKIMPORTERFACTORY_H #include #include "kid3api.h" class QString; class QStringList; class QNetworkAccessManager; class TrackDataModel; class ServerTrackImporter; /** * Interface for server track importer factory. */ class KID3_CORE_EXPORT IServerTrackImporterFactory { public: /** * Destructor. */ virtual ~IServerTrackImporterFactory() = 0; /** * Get keys of available server importers. * @return list of keys. */ virtual QStringList serverTrackImporterKeys() const = 0; /** * Create server importer. * @param key server importer key * @param netMgr network access manager * @param trackDataModel track data to be filled with imported values * @return server importer instance, 0 if key unknown. * @remarks The caller takes ownership of the returned instance. */ virtual ServerTrackImporter* createServerTrackImporter( const QString& key, QNetworkAccessManager* netMgr, TrackDataModel* trackDataModel) = 0; }; Q_DECLARE_INTERFACE(IServerTrackImporterFactory, "net.sourceforge.kid3.IServerTrackImporterFactory") #endif // ISERVERTRACKIMPORTERFACTORY_H kid3-3.0.2/src/core/import/jsonparser.cpp000066400000000000000000000207151224603511300202730ustar00rootroot00000000000000/** * \file jsonparser.cpp * JSON serializer and deserializer. * * \b Project: Kid3 * \author Urs Fleisch * \date 09 Oct 2012 * * Copyright (C) 2012-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "jsonparser.h" #include #include #include #include #include /** * JSON deserializer. */ class JsonDeserializer { public: /** * Constructor. */ JsonDeserializer(); /** * Deserialize a JSON string to a string-variant map. * @param str string to deserialize * @param ok if not null, true is returned here on success * @return deserialized string-variant map */ QVariant deserialize(const QString& str, bool* ok = 0); private: void skipWhiteSpace(); bool requireDelimiter(const QString& delimiters); QString parseSymbol(); QString m_str; int m_len; int m_pos; }; namespace { QString stripQuotes(const QString& str) { return (str.startsWith(QLatin1Char('"')) && str.endsWith(QLatin1Char('"'))) ? str.mid(1, str.length() - 2) : str; } QVariant valueStringToVariant(const QString& value) { bool ok; if (value.startsWith(QLatin1Char('"')) && value.endsWith(QLatin1Char('"'))) { // The value is probably a string, but could also be a date/time. QString str(value.mid(1, value.length() - 2)); QDateTime dt(QDateTime::fromString(str, Qt::ISODate)); if (dt.isValid()) return dt; else return str; } // Return nested objects or arrays. if (value.startsWith(QLatin1Char('{')) || value.startsWith(QLatin1Char('['))) return JsonDeserializer().deserialize(value); if (value == QLatin1String("true")) return true; else if (value == QLatin1String("false")) return false; else if (value == QLatin1String("null")) return QVariant(); qlonglong num = value.toLongLong(&ok); if (ok) { if (num >= INT_MIN && num <= INT_MAX) return static_cast(num); return num; } double dbl = value.toDouble(&ok); if (ok) return dbl; return value; } QString variantToValueString(const QVariant& var) { QString value; QVariant::Type type = var.type(); if (!var.isValid()) { value = QLatin1String("null"); } else if (type == QVariant::List) { QVariantList lst(var.toList()); // Serialize into an array container "[...]". for (int i = 0; i < lst.size(); i++) { value += QString(QLatin1String("%1%2")).arg(value.isEmpty() ? QLatin1String("") : QLatin1String(", ")). arg(variantToValueString(lst.at(i))); } value = QString(QLatin1String("[%1]")).arg(value); } else if (type == QVariant::Map) { // Serialize into an object container "{...}". QVariantMap map(var.toMap()); for (QMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it) { value += QString(QLatin1String("%1\"%2\": %3")).arg(value.isEmpty() ? QLatin1String("") : QLatin1String(", ")). arg(it.key()).arg(variantToValueString(it.value())); } value = QString(QLatin1String("{%1}")).arg(value); } else { value = var.toString(); if (value.startsWith(QLatin1Char('{')) || value.startsWith(QLatin1Char('['))) { ; // keep value } else if (type == QVariant::String || type == QVariant::DateTime || type == QVariant::Date || type == QVariant::Time) { value = QLatin1Char('"') + value.replace(QLatin1Char('\\'), QLatin1String("\\\\")).replace(QLatin1Char('"'), QLatin1String("\\\"")) + QLatin1Char('"'); } } return value; } } JsonDeserializer::JsonDeserializer(): m_len(0), m_pos(0) { } QVariant JsonDeserializer::deserialize(const QString& str, bool* ok) { QVariant result; bool isOk = false; m_str = str; m_len = str.length(); m_pos = 0; if (requireDelimiter(QLatin1String("{"))) { // Deserialize from object container "{...}". QVariantMap map; isOk = true; while (m_pos < m_len) { QString key = parseSymbol(); if (key.isEmpty() || !requireDelimiter(QLatin1String(":"))) { isOk = false; break; } QString value = parseSymbol(); if (value.isEmpty() || !requireDelimiter(QLatin1String(",}"))) { isOk = false; break; } map.insert(stripQuotes(key), valueStringToVariant(value)); } result = map; } else if (requireDelimiter(QLatin1String("["))) { // Deserialize from array container "[...]". QVariantList lst; isOk = true; for (int i = 0; m_pos < m_len; i++) { QString value = parseSymbol(); if (value.isEmpty() || !requireDelimiter(QLatin1String(",]"))) { isOk = false; break; } lst.append(valueStringToVariant(value)); } result = lst; } if (ok) { *ok = isOk; } return result; } void JsonDeserializer::skipWhiteSpace() { QChar ch; while (m_pos < m_len && ((ch = m_str.at(m_pos)) == QLatin1Char(' ') || ch == QLatin1Char('\t') || ch == QLatin1Char('\r') || ch == QLatin1Char('\n'))) { ++m_pos; } } bool JsonDeserializer::requireDelimiter(const QString& delimiters) { skipWhiteSpace(); if (m_pos < m_len && delimiters.indexOf(m_str.at(m_pos)) != -1) { ++m_pos; skipWhiteSpace(); return true; } return false; } QString JsonDeserializer::parseSymbol() { QString result; skipWhiteSpace(); if (m_pos < m_len) { const QChar beginCh = m_str.at(m_pos); if (beginCh == QLatin1Char('"')) { // String, get symbol between double quotes respecting escaped quotes. int endPos; int searchPos = m_pos + 1; forever { endPos = m_str.indexOf(QLatin1Char('"'), searchPos); if (endPos < 1 || m_str.at(endPos - 1) != QLatin1Char('\\')) break; searchPos = endPos + 1; } if (endPos > m_pos) { result = m_str.mid(m_pos, ++endPos - m_pos). replace(QLatin1String("\\\""), QLatin1String("\"")).replace(QLatin1String("\\\\"), QLatin1String("\\")); m_pos = endPos; } } else if (beginCh == QLatin1Char('{') || beginCh == QLatin1Char('[')) { // Object or array, find end. Nesting is supported. const QChar endCh = beginCh == QLatin1Char('{') ? QLatin1Char('}') : QLatin1Char(']'); int nestingLevel = 0; bool insideString = false; QChar lastCh; int endPos = m_pos + 1; while (endPos < m_len) { const QChar ch = m_str.at(endPos); if (insideString) { if (ch == QLatin1Char('"') && lastCh != QLatin1Char('\\')) { insideString = false; } } else { if (ch == QLatin1Char('"')) { insideString = true; } else if (ch == beginCh) { ++nestingLevel; } else if (ch == endCh) { if (nestingLevel == 0) { break; } else { --nestingLevel; } } } lastCh = ch; ++endPos; } if (endPos < m_len) { result = m_str.mid(m_pos, ++endPos - m_pos); m_pos = endPos; } } else { // Probably number or symbol without whitespace. int startPos = m_pos; const QString endChars = QLatin1String(" \t\r\n:,}]"); while (m_pos < m_len && endChars.indexOf(m_str.at(m_pos)) == -1) { ++m_pos; } result = m_str.mid(startPos, m_pos - startPos); } } skipWhiteSpace(); return result; } /** * Serialize a variant as a JSON string. * @param var variant * @return JSON representation of @a var. */ QString JsonParser::serialize(const QVariant& var) { return variantToValueString(var); } /** * Deserialize a JSON string to a string-variant map. * @param str string to deserialize * @param ok if not null, true is returned here on success * @return deserialized string-variant map */ QVariant JsonParser::deserialize(const QString& str, bool* ok) { return JsonDeserializer().deserialize(str, ok); } kid3-3.0.2/src/core/import/jsonparser.h000066400000000000000000000032131224603511300177320ustar00rootroot00000000000000/** * \file jsonparser.h * JSON serializer and deserializer. * * \b Project: Kid3 * \author Urs Fleisch * \date 09 Oct 2012 * * Copyright (C) 2012 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef JSONPARSER_H #define JSONPARSER_H #include "kid3api.h" class QString; class QVariant; /** * Serialize and deserialize simple string-variant maps with JSON. * * The following types are serialized: null (represented by invalid QVariant), * bool, int, qlonglong, double, QString, QDateTime (ISO 8601 extended format * YYYY-MM-DDTHH:MM:SS). */ namespace JsonParser { /** * Deserialize a JSON string to a string-variant map. * @param str string to deserialize * @param ok if not null, true is returned here on success * @return deserialized string-variant map */ QVariant KID3_CORE_EXPORT deserialize(const QString& str, bool* ok = 0); /** * Serialize a variant as a JSON string. * @param var variant * @return JSON representation of @a var. */ QString KID3_CORE_EXPORT serialize(const QVariant& var); } #endif kid3-3.0.2/src/core/import/serverimporter.cpp000066400000000000000000000113471224603511300211760ustar00rootroot00000000000000/** * \file serverimporter.cpp * Generic baseclass to import from a server. * * \b Project: Kid3 * \author Urs Fleisch * \date 09 Oct 2006 * * Copyright (C) 2006-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "serverimporter.h" #include #include "serverimporterconfig.h" #include "importclient.h" #include "trackdata.h" /** * Constructor. * * @param netMgr network access manager * @param trackDataModel track data to be filled with imported values */ ServerImporter::ServerImporter(QNetworkAccessManager* netMgr, TrackDataModel* trackDataModel) : ImportClient(netMgr), m_albumListModel(new QStandardItemModel(this)), m_trackDataModel(trackDataModel), m_standardTagsEnabled(true), m_additionalTagsEnabled(false), m_coverArtEnabled(false) { setObjectName(QLatin1String("ServerImporter")); } /** * Destructor. */ ServerImporter::~ServerImporter() { } /** NULL-terminated array of server strings, 0 if not used */ const char** ServerImporter::serverList() const { return 0; } /** default server, 0 to disable */ const char* ServerImporter::defaultServer() const { return 0; } /** default CGI path, 0 to disable */ const char* ServerImporter::defaultCgiPath() const { return 0; } /** anchor to online help, 0 to disable */ const char* ServerImporter::helpAnchor() const { return 0; } /** configuration, 0 if not used */ ServerImporterConfig* ServerImporter::config() const { return 0; } /** additional tags option, false if not used */ bool ServerImporter::additionalTags() const { return false; } /** * Clear model data. */ void ServerImporter::clear() { m_albumListModel->clear(); } /** * Replace HTML entities in a string. * * @param str string with HTML entities (e.g. ") * * @return string with replaced HTML entities. */ QString ServerImporter::replaceHtmlEntities(QString str) { str.replace(QLatin1String("""), QLatin1String("\"")); str.replace(QLatin1String(" "), QLatin1String(" ")); str.replace(QLatin1String("<"), QLatin1String("<")); str.replace(QLatin1String(">"), QLatin1String(">")); str.replace(QLatin1String("&"), QLatin1String("&")); str.replace(QLatin1String("×"), QString(QChar(0xd7))); str.replace(QLatin1String("–"), QLatin1String("-")); QRegExp numEntityRe(QLatin1String("&#(\\d+);")); int pos = 0; while ((pos = numEntityRe.indexIn(str, pos)) != -1) { str.replace(pos, numEntityRe.matchedLength(), QChar(numEntityRe.cap(1).toInt())); pos += numEntityRe.matchedLength(); } return str; } /** * Replace HTML entities and remove HTML tags. * * @param str string containing HTML * * @return clean up string */ QString ServerImporter::removeHtml(QString str) { QRegExp htmlTagRe(QLatin1String("<[^>]+>")); return replaceHtmlEntities(str.remove(htmlTagRe)).trimmed(); } /** * Constructor. * @param text title * @param cat category * @param idStr ID */ AlbumListItem::AlbumListItem(const QString& text, const QString& cat, const QString& idStr) : QStandardItem(text) { setData(cat, Qt::UserRole + 1); setData(idStr, Qt::UserRole + 2); } /** * Destructor. */ AlbumListItem::~AlbumListItem() { } /** * Get type of item. * Used to distinguish items of this custom type from base class items. * @return AlbumListItem::Type. */ int AlbumListItem::type() const { return Type; } /** * Get category. * @return category. */ QString AlbumListItem::getCategory() const { return data(Qt::UserRole + 1).toString(); } /** * Get ID. * @return ID. */ QString AlbumListItem::getId() const { return data(Qt::UserRole + 2).toString(); } #ifndef QT_NO_DEBUG /** * Dump an album list. * @param albumModel album list model */ void AlbumListItem::dumpAlbumList(const QStandardItemModel* albumModel) { for (int row = 0; row < albumModel->rowCount(); ++row) { AlbumListItem* item = static_cast(albumModel->item(row, 0)); if (item && item->type() == AlbumListItem::Type) { qDebug("%s (%s, %s)", qPrintable(item->text()), qPrintable(item->getCategory()), qPrintable(item->getId())); } } } #endif kid3-3.0.2/src/core/import/serverimporter.h000066400000000000000000000127201224603511300206370ustar00rootroot00000000000000/** * \file serverimporter.h * Generic baseclass to import from a server. * * \b Project: Kid3 * \author Urs Fleisch * \date 09 Oct 2006 * * Copyright (C) 2006-2011 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef SERVERIMPORTER_H #define SERVERIMPORTER_H #include "importclient.h" #include #include class QStandardItemModel; class ServerImporterConfig; class ImportClient; class TrackDataModel; /** * Generic baseclass to import from an external source. */ class KID3_CORE_EXPORT ServerImporter : public ImportClient { Q_OBJECT public: /** * Constructor. * * @param netMgr network access manager * @param trackDataModel track data to be filled with imported values */ ServerImporter(QNetworkAccessManager* netMgr, TrackDataModel *trackDataModel); /** * Destructor. */ virtual ~ServerImporter(); /** * Name of import source. * @return name. */ virtual const char* name() const = 0; /** NULL-terminated array of server strings, 0 if not used */ virtual const char** serverList() const; /** default server, 0 to disable */ virtual const char* defaultServer() const; /** default CGI path, 0 to disable */ virtual const char* defaultCgiPath() const; /** anchor to online help, 0 to disable */ virtual const char* helpAnchor() const; /** configuration, 0 if not used */ virtual ServerImporterConfig* config() const; /** additional tags option, false if not used */ virtual bool additionalTags() const; /** * Parse result of find request and populate m_albumListBox with results. * This method has to be reimplemented for the specific result data. * * @param searchStr search data received */ virtual void parseFindResults(const QByteArray& searchStr) = 0; /** * Parse result of album request and populate m_trackDataModel with results. * This method has to be reimplemented for the specific result data. * * @param albumStr album data received */ virtual void parseAlbumResults(const QByteArray& albumStr) = 0; /** * Get model with album list. * * @return album list item model. */ QStandardItemModel* getAlbumListModel() const { return m_albumListModel; } /** * Clear model data. */ void clear(); /** * Get standard tags option. * * @return true if standard tags are enabled. */ bool getStandardTags() const { return m_standardTagsEnabled; } /** * Set standard tags option. * * @param enable true if standard tags are enabled */ void setStandardTags(bool enable) { m_standardTagsEnabled = enable; } /** * Get additional tags option. * * @return true if additional tags are enabled. */ bool getAdditionalTags() const { return m_additionalTagsEnabled; } /** * Set additional tags option. * * @param enable true if additional tags are enabled */ void setAdditionalTags(bool enable) { m_additionalTagsEnabled = enable; } /** * Get cover art option. * * @return true if cover art are enabled. */ bool getCoverArt() const { return m_coverArtEnabled; } /** * Set cover art option. * * @param enable true if cover art are enabled */ void setCoverArt(bool enable) { m_coverArtEnabled = enable; } /** * Replace HTML entities in a string. * * @param str string with HTML entities (e.g. ") * * @return string with replaced HTML entities. */ static QString replaceHtmlEntities(QString str); /** * Replace HTML entities and remove HTML tags. * * @param str string containing HTML * * @return clean up string */ static QString removeHtml(QString str); protected: QStandardItemModel* m_albumListModel; /**< albums to select */ TrackDataModel* m_trackDataModel; /**< model with tracks to import */ private: bool m_standardTagsEnabled; bool m_additionalTagsEnabled; bool m_coverArtEnabled; }; /** * QStandardItem subclass for album list. */ class KID3_CORE_EXPORT AlbumListItem : public QStandardItem { public: /** * Type returned by type(), extension of QStandardItem::ItemType. */ enum ItemType { Type = UserType + 1 }; /** * Constructor. * @param text title * @param cat category * @param idStr ID */ AlbumListItem(const QString& text, const QString& cat, const QString& idStr); /** * Destructor. */ virtual ~AlbumListItem(); /** * Get type of item. * Used to distinguish items of this custom type from base class items. * @return AlbumListItem::Type. */ virtual int type() const; /** * Get category. * @return category. */ QString getCategory() const; /** * Get ID. * @return ID. */ QString getId() const; #ifndef QT_NO_DEBUG /** * Dump an album list. * @param albumModel album list model */ static void dumpAlbumList(const QStandardItemModel* albumModel); #endif }; #endif kid3-3.0.2/src/core/import/servertrackimporter.cpp000066400000000000000000000040271224603511300222200ustar00rootroot00000000000000/** * \file servertrackimporter.cpp * Abstract base class for track imports from a server. * * \b Project: Kid3 * \author Urs Fleisch * \date 23 Jun 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "servertrackimporter.h" #include "httpclient.h" /** * Constructor. * * @param netMgr network access manager * @param trackDataModel track data to be filled with imported values, * is passed with filenames set */ ServerTrackImporter::ServerTrackImporter(QNetworkAccessManager* netMgr, TrackDataModel* trackDataModel) : QObject(netMgr), m_httpClient(new HttpClient(netMgr)), m_trackDataModel(trackDataModel) { } /** * Destructor. */ ServerTrackImporter::~ServerTrackImporter() { } /** NULL-terminated array of server strings, 0 if not used */ const char** ServerTrackImporter::serverList() const { return 0; } /** default server, 0 to disable */ const char* ServerTrackImporter::defaultServer() const { return 0; } /** anchor to online help, 0 to disable */ const char* ServerTrackImporter::helpAnchor() const { return 0; } /** configuration, 0 if not used */ ServerImporterConfig* ServerTrackImporter::config() const { return 0; } /** * Set configuration. * * @param cfg import server configuration, 0 if not used */ void ServerTrackImporter::setConfig(const ServerImporterConfig* cfg) { Q_UNUSED(cfg) } kid3-3.0.2/src/core/import/servertrackimporter.h000066400000000000000000000060301224603511300216610ustar00rootroot00000000000000/** * \file servertrackimporter.h * Abstract base class for track imports from a server. * * \b Project: Kid3 * \author Urs Fleisch * \date 23 Jun 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef SERVERTRACKIMPORTER_H #define SERVERTRACKIMPORTER_H #include #include "kid3api.h" class QNetworkAccessManager; class ImportTrackDataVector; class TrackDataModel; class ServerImporterConfig; class HttpClient; /** * Abstract base class for track imports from a server. */ class KID3_CORE_EXPORT ServerTrackImporter : public QObject { Q_OBJECT public: /** * Constructor. * * @param netMgr network access manager * @param trackDataModel track data to be filled with imported values, * is passed with filenames set */ ServerTrackImporter(QNetworkAccessManager* netMgr, TrackDataModel* trackDataModel); /** * Destructor. */ virtual ~ServerTrackImporter(); /** * Name of import source. * @return name. */ virtual const char* name() const = 0; /** NULL-terminated array of server strings, 0 if not used */ virtual const char** serverList() const; /** default server, 0 to disable */ virtual const char* defaultServer() const; /** anchor to online help, 0 to disable */ virtual const char* helpAnchor() const; /** configuration, 0 if not used */ virtual ServerImporterConfig* config() const; /** * Set configuration. * * @param cfg import server configuration, 0 if not used */ virtual void setConfig(const ServerImporterConfig* cfg); /** * Add the files in the file list. */ virtual void start() = 0; /** * Reset the client state. */ virtual void stop() = 0; signals: /** * Emitted when status of a file changed. * Parameter: index of file, status text */ void statusChanged(int, const QString&); /** * Emitted when results for a file are received. * Parameter index of file, track data list */ void resultsReceived(int, ImportTrackDataVector&); protected: /** * Access to HTTP client. * @return HTTP client. */ HttpClient* httpClient() { return m_httpClient; } /** * @brief Access to track data model. * @return track data model. */ TrackDataModel* trackDataModel() { return m_trackDataModel; } private: HttpClient* m_httpClient; TrackDataModel* m_trackDataModel; }; #endif // SERVERTRACKIMPORTER_H kid3-3.0.2/src/core/import/textimporter.cpp000066400000000000000000000126551224603511300206570ustar00rootroot00000000000000/** * \file textimporter.cpp * Import tags from text. * * \b Project: Kid3 * \author Urs Fleisch * \date 19 Jun 2011 * * Copyright (C) 2011 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "textimporter.h" #include "importparser.h" #include "trackdatamodel.h" /** * Constructor. * * @param trackDataModel track data to be filled with imported values */ TextImporter::TextImporter(TrackDataModel* trackDataModel) : m_headerParser(new ImportParser), m_trackParser(new ImportParser), m_trackDataModel(trackDataModel) { } /** * Destructor. */ TextImporter::~TextImporter() { delete m_headerParser; delete m_trackParser; } /** * Look for album specific information (artist, album, year, genre) in * a header. * * @param frames frames to put resulting values in, * fields which are not found are not touched. * * @return true if one or more field were found. */ bool TextImporter::parseHeader(FrameCollection& frames) { int pos = 0; m_headerParser->setFormat(m_headerFormat); return m_headerParser->getNextTags(m_text, frames, pos); } /** * Update track data list with imported tags. * * @param text text to import * @param headerFormat header format * @param trackFormat track format * * @return true if tags were found. */ bool TextImporter::updateTrackData( const QString& text, const QString& headerFormat, const QString& trackFormat) { m_text = text; m_headerFormat = headerFormat; m_trackFormat = trackFormat; FrameCollection framesHdr; (void)parseHeader(framesHdr); FrameCollection frames(framesHdr); bool start = true; ImportTrackDataVector trackDataVector(m_trackDataModel->getTrackData()); ImportTrackDataVector::iterator it = trackDataVector.begin(); bool atTrackDataListEnd = (it == trackDataVector.end()); while (getNextTags(frames, start)) { start = false; if (atTrackDataListEnd) { ImportTrackData trackData; trackData.setFrameCollection(frames); trackDataVector.push_back(trackData); } else { while (!atTrackDataListEnd && !it->isEnabled()) { ++it; atTrackDataListEnd = (it == trackDataVector.end()); } if (!atTrackDataListEnd) { (*it).setFrameCollection(frames); ++it; atTrackDataListEnd = (it == trackDataVector.end()); } } frames = framesHdr; } frames.clear(); while (!atTrackDataListEnd) { if (it->isEnabled()) { if ((*it).getFileDuration() == 0) { it = trackDataVector.erase(it); } else { (*it).setFrameCollection(frames); (*it).setImportDuration(0); ++it; } } else { ++it; } atTrackDataListEnd = (it == trackDataVector.end()); } if (!start) { /* start is false => tags were found */ QList trackDuration = getTrackDurations(); if (!trackDuration.isEmpty()) { it = trackDataVector.begin(); for (QList::const_iterator tdit = trackDuration.begin(); tdit != trackDuration.end(); ++tdit) { if (it != trackDataVector.end()) { if (it->isEnabled()) { (*it).setImportDuration(*tdit); } ++it; } else { break; } } } m_trackDataModel->setTrackData(trackDataVector); return true; } return false; } /** * Get next line as frames from imported file or clipboard. * * @param frames frames * @param start true to start with the first line, false for all * other lines * * @return true if ok (result in st), * false if end of file reached. */ bool TextImporter::getNextTags(FrameCollection& frames, bool start) { static int pos = 0; if (start || pos == 0) { pos = 0; m_trackParser->setFormat(m_trackFormat, true); } return m_trackParser->getNextTags(m_text, frames, pos); } /** * Get list with track durations. * * @return list with track durations, * empty if no track durations found. */ QList TextImporter::getTrackDurations() { QList lst; if (m_headerParser) { lst = m_headerParser->getTrackDurations(); } else if (m_trackParser) { lst = m_trackParser->getTrackDurations(); } return lst; } /** * Import text from tags to other tags. * * @param sourceFormat format to create source text * @param extractionFormat regular expression to extract other tags * @param trackDataVector track data to process */ void TextImporter::importFromTags( const QString& sourceFormat, const QString& extractionFormat, ImportTrackDataVector& trackDataVector) { ImportParser parser; parser.setFormat(extractionFormat); for (ImportTrackDataVector::iterator it = trackDataVector.begin(); it != trackDataVector.end(); ++it) { if (it->isEnabled()) { QString text(it->formatString(sourceFormat)); int pos = 0; parser.getNextTags(text, *it, pos); } } } kid3-3.0.2/src/core/import/textimporter.h000066400000000000000000000062751224603511300203250ustar00rootroot00000000000000/** * \file textimporter.h * Import tags from text. * * \b Project: Kid3 * \author Urs Fleisch * \date 19 Jun 2011 * * Copyright (C) 2011 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef TEXTIMPORTER_H #define TEXTIMPORTER_H #include #include "kid3api.h" class ImportTrackDataVector; class ImportParser; class TrackDataModel; class FrameCollection; /** * Import tags from text. */ class KID3_CORE_EXPORT TextImporter { public: /** * Constructor. * * @param trackDataModel track data to be filled with imported values */ explicit TextImporter(TrackDataModel* trackDataModel); /** * Destructor. */ ~TextImporter(); /** * Update track data list with imported tags. * * @param text text to import * @param headerFormat header format * @param trackFormat track format * * @return true if tags were found. */ bool updateTrackData(const QString& text, const QString& headerFormat, const QString& trackFormat); /** * Import text from tags to other tags. * * @param sourceFormat format to create source text * @param extractionFormat regular expression to extract other tags * @param trackDataVector track data to process */ static void importFromTags( const QString& sourceFormat, const QString& extractionFormat, ImportTrackDataVector& trackDataVector); private: Q_DISABLE_COPY(TextImporter) /** * Look for album specific information (artist, album, year, genre) in * a header. * * @param frames frames to put resulting values in, * fields which are not found are not touched. * * @return true if one or more field were found. */ bool parseHeader(FrameCollection& frames); /** * Get next line as frames from imported file or clipboard. * * @param frames frames * @param start true to start with the first line, false for all * other lines * * @return true if ok (result in st), * false if end of file reached. */ bool getNextTags(FrameCollection& frames, bool start); /** * Get list with track durations. * * @return list with track durations, * empty if no track durations found. */ QList getTrackDurations(); /** contents of imported file/clipboard */ QString m_text; /** header format */ QString m_headerFormat; /** track format */ QString m_trackFormat; /** header parser object */ ImportParser* m_headerParser; /** track parser object */ ImportParser* m_trackParser; /** track data */ TrackDataModel* m_trackDataModel; }; #endif kid3-3.0.2/src/core/import/trackdatamatcher.cpp000066400000000000000000000273201224603511300214060ustar00rootroot00000000000000/** * \file trackdatamatcher.cpp * Shuffle imported tracks to optimize match with length, track or title. * * \b Project: Kid3 * \author Urs Fleisch * \date 19 Jun 2011 * * Copyright (C) 2011 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "trackdatamatcher.h" #include #include #include #include "trackdatamodel.h" /** * Match import data with length. * * @param trackDataModel tracks to match * @param diffCheckEnable true if time difference check is enabled * @param maxDiff maximum allowed time difference */ bool TrackDataMatcher::matchWithLength(TrackDataModel* trackDataModel, bool diffCheckEnable, int maxDiff) { struct MatchData { int fileLen; // length of file int importLen; // length of import int assignedTo; // number of file import is assigned to, -1 if not assigned int assignedFrom; // number of import assigned to file, -1 if not assigned }; bool failed = false; ImportTrackDataVector trackDataVector(trackDataModel->getTrackData()); unsigned numTracks = trackDataVector.size(); if (numTracks > 0) { MatchData* md = new MatchData[numTracks]; unsigned numFiles = 0, numImports = 0; unsigned i = 0; for (ImportTrackDataVector::const_iterator it = trackDataVector.begin(); it != trackDataVector.end(); ++it) { if (i >= numTracks) { break; } md[i].fileLen = (*it).getFileDuration(); if (md[i].fileLen > 0) { ++numFiles; } md[i].importLen = (*it).getImportDuration(); if (md[i].importLen > 0) { ++numImports; } md[i].assignedTo = -1; md[i].assignedFrom = -1; // If time difference checking is enabled and the time difference // is not larger then the allowed limit, do not reassign the track. if (diffCheckEnable) { if (md[i].fileLen != 0 && md[i].importLen != 0) { int diff = md[i].fileLen > md[i].importLen ? md[i].fileLen - md[i].importLen : md[i].importLen - md[i].fileLen; if (diff <= maxDiff) { md[i].assignedTo = i; md[i].assignedFrom = i; } } } ++i; } if (numFiles <= numImports) { // more imports than files => first look through all imports for (i = 0; i < numTracks; ++i) { if (md[i].assignedFrom == -1) { int bestTrack = -1; int bestDiff = INT_MAX; // Find the unassigned import with the best difference for (unsigned comparedTrack = 0; comparedTrack < numTracks; ++comparedTrack) { if (md[comparedTrack].assignedTo == -1) { int comparedDiff = md[i].fileLen > md[comparedTrack].importLen ? md[i].fileLen - md[comparedTrack].importLen : md[comparedTrack].importLen - md[i].fileLen; if (comparedDiff < bestDiff) { bestDiff = comparedDiff; bestTrack = comparedTrack; } } } if (bestTrack >= 0 && bestTrack < static_cast(numTracks)) { md[i].assignedFrom = bestTrack; md[bestTrack].assignedTo = i; } else { qDebug("No match for track %d", i); failed = true; break; } } } } else { // more files than imports => first look through all files for (i = 0; i < numTracks; ++i) { if (md[i].assignedTo == -1) { int bestTrack = -1; int bestDiff = INT_MAX; // Find the unassigned file with the best difference for (unsigned comparedTrack = 0; comparedTrack < numTracks; ++comparedTrack) { if (md[comparedTrack].assignedFrom == -1) { int comparedDiff = md[comparedTrack].fileLen > md[i].importLen ? md[comparedTrack].fileLen - md[i].importLen : md[i].importLen - md[comparedTrack].fileLen; if (comparedDiff < bestDiff) { bestDiff = comparedDiff; bestTrack = comparedTrack; } } } if (bestTrack >= 0 && bestTrack < static_cast(numTracks)) { md[i].assignedTo = bestTrack; md[bestTrack].assignedFrom = i; } else { qDebug("No match for track %d", i); failed = true; break; } } } } if (!failed) { ImportTrackDataVector oldTrackDataVector(trackDataVector); for (i = 0; i < numTracks; ++i) { trackDataVector[i].setFrameCollection( oldTrackDataVector[md[i].assignedFrom].getFrameCollection()); trackDataVector[i].setImportDuration( oldTrackDataVector[md[i].assignedFrom].getImportDuration()); } trackDataModel->setTrackData(trackDataVector); } delete [] md; } return !failed; } /** * Match import data with track number. * * @param trackDataModel tracks to match */ bool TrackDataMatcher::matchWithTrack(TrackDataModel* trackDataModel) { struct MatchData { int track; // track number starting with 0 int assignedTo; // number of file import is assigned to, -1 if not assigned int assignedFrom; // number of import assigned to file, -1 if not assigned }; bool failed = false; ImportTrackDataVector trackDataVector(trackDataModel->getTrackData()); unsigned numTracks = trackDataVector.size(); if (numTracks > 0) { MatchData* md = new MatchData[numTracks]; // 1st pass: Get track data and keep correct assignments. unsigned i = 0; for (ImportTrackDataVector::const_iterator it = trackDataVector.begin(); it != trackDataVector.end(); ++it) { if (i >= numTracks) { break; } if ((*it).getTrack() > 0 && (*it).getTrack() <= static_cast(numTracks)) { md[i].track = (*it).getTrack() - 1; } else { md[i].track = -1; } md[i].assignedTo = -1; md[i].assignedFrom = -1; if (md[i].track == static_cast(i)) { md[i].assignedTo = i; md[i].assignedFrom = i; } ++i; } // 2nd pass: Assign imported track numbers to unassigned tracks. for (i = 0; i < numTracks; ++i) { if (md[i].assignedTo == -1 && md[i].track >= 0 && md[i].track < static_cast(numTracks)) { if (md[md[i].track].assignedFrom == -1) { md[md[i].track].assignedFrom = i; md[i].assignedTo = md[i].track; } } } // 3rd pass: Assign remaining tracks. unsigned unassignedTrack = 0; for (i = 0; i < numTracks; ++i) { if (md[i].assignedFrom == -1) { while (unassignedTrack < numTracks) { if (md[unassignedTrack].assignedTo == -1) { md[i].assignedFrom = unassignedTrack; md[unassignedTrack++].assignedTo = i; break; } ++unassignedTrack; } if (md[i].assignedFrom == -1) { qDebug("No track assigned to %d", i); failed = true; } } } if (!failed) { ImportTrackDataVector oldTrackDataVector(trackDataVector); for (i = 0; i < numTracks; ++i) { trackDataVector[i].setFrameCollection( oldTrackDataVector[md[i].assignedFrom].getFrameCollection()); trackDataVector[i].setImportDuration( oldTrackDataVector[md[i].assignedFrom].getImportDuration()); } trackDataModel->setTrackData(trackDataVector); } delete [] md; } return !failed; } /** * Match import data with title. * * @param trackDataModel tracks to match */ bool TrackDataMatcher::matchWithTitle(TrackDataModel* trackDataModel) { struct MatchData { QSet fileWords; // words in file name QSet titleWords; // words in title int assignedTo; // number of file import is assigned to, -1 if not assigned int assignedFrom; // number of import assigned to file, -1 if not assigned }; bool failed = false; ImportTrackDataVector trackDataVector(trackDataModel->getTrackData()); unsigned numTracks = trackDataVector.size(); if (numTracks > 0) { MatchData* md = new MatchData[numTracks]; unsigned numFiles = 0, numImports = 0; unsigned i = 0; for (ImportTrackDataVector::const_iterator it = trackDataVector.begin(); it != trackDataVector.end(); ++it) { if (i >= numTracks) { break; } md[i].fileWords = it->getFilenameWords(); if (!md[i].fileWords.isEmpty()) { ++numFiles; } md[i].titleWords = it->getTitleWords(); if (!md[i].titleWords.isEmpty()) { ++numImports; } md[i].assignedTo = -1; md[i].assignedFrom = -1; ++i; } if (numFiles <= numImports) { // more imports than files => first look through all imports for (i = 0; i < numTracks; ++i) { if (md[i].assignedFrom == -1) { int bestTrack = -1; int bestMatch = -1; // Find the unassigned import with the best match for (unsigned comparedTrack = 0; comparedTrack < numTracks; ++comparedTrack) { if (md[comparedTrack].assignedTo == -1) { int comparedMatch = (md[i].fileWords & md[comparedTrack].titleWords).size(); if (comparedMatch > bestMatch) { bestMatch = comparedMatch; bestTrack = comparedTrack; } } } if (bestTrack >= 0 && bestTrack < static_cast(numTracks)) { md[i].assignedFrom = bestTrack; md[bestTrack].assignedTo = i; } else { qDebug("No match for track %d", i); failed = true; break; } } } } else { // more files than imports => first look through all files for (i = 0; i < numTracks; ++i) { if (md[i].assignedTo == -1) { int bestTrack = -1; int bestMatch = -1; // Find the unassigned file with the best match for (unsigned comparedTrack = 0; comparedTrack < numTracks; ++comparedTrack) { if (md[comparedTrack].assignedFrom == -1) { int comparedMatch = (md[comparedTrack].fileWords & md[i].titleWords).size(); if (comparedMatch > bestMatch) { bestMatch = comparedMatch; bestTrack = comparedTrack; } } } if (bestTrack >= 0 && bestTrack < static_cast(numTracks)) { md[i].assignedTo = bestTrack; md[bestTrack].assignedFrom = i; } else { qDebug("No match for track %d", i); failed = true; break; } } } } if (!failed) { ImportTrackDataVector oldTrackDataVector(trackDataVector); for (i = 0; i < numTracks; ++i) { trackDataVector[i].setFrameCollection( oldTrackDataVector[md[i].assignedFrom].getFrameCollection()); trackDataVector[i].setImportDuration( oldTrackDataVector[md[i].assignedFrom].getImportDuration()); } trackDataModel->setTrackData(trackDataVector); } delete [] md; } return !failed; } kid3-3.0.2/src/core/import/trackdatamatcher.h000066400000000000000000000033601224603511300210510ustar00rootroot00000000000000/** * \file trackdatamatcher.h * Shuffle imported tracks to optimize match with length, track or title. * * \b Project: Kid3 * \author Urs Fleisch * \date 19 Jun 2011 * * Copyright (C) 2011 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef TRACKDATAMATCHER_H #define TRACKDATAMATCHER_H #include "kid3api.h" class TrackDataModel; /** * Shuffle imported tracks to optimize match with length, track or title. */ namespace TrackDataMatcher { /** * Match import data with length. * * @param trackDataModel tracks to match * @param diffCheckEnable true if time difference check is enabled * @param maxDiff maximum allowed time difference */ bool KID3_CORE_EXPORT matchWithLength(TrackDataModel* trackDataModel, bool diffCheckEnable, int maxDiff); /** * Match import data with track number. * * @param trackDataModel tracks to match */ bool KID3_CORE_EXPORT matchWithTrack(TrackDataModel* trackDataModel); /** * Match import data with title. * * @param trackDataModel tracks to match */ bool KID3_CORE_EXPORT matchWithTitle(TrackDataModel* trackDataModel); } #endif // TRACKDATAMATCHER_H kid3-3.0.2/src/core/model/000077500000000000000000000000001224603511300151625ustar00rootroot00000000000000kid3-3.0.2/src/core/model/Sources.cmake000066400000000000000000000021301224603511300176030ustar00rootroot00000000000000set(model_SRCS model/iabortable.cpp model/audioplayer.cpp model/commandformatreplacer.cpp model/commandstablemodel.cpp model/configtablemodel.cpp model/dirlist.cpp model/dirproxymodel.cpp model/dirrenamer.cpp model/downloadclient.cpp model/expressionparser.cpp model/externalprocess.cpp model/filefilter.cpp model/fileproxymodel.cpp model/fileproxymodeliterator.cpp model/framelist.cpp model/frametablemodel.cpp model/iframeeditor.cpp model/kid3application.cpp model/modeliterator.cpp model/taggedfileiconprovider.cpp model/texttablemodel.cpp model/trackdatamodel.cpp model/checkablestringlistmodel.cpp ) set(model_MOC_HDRS model/audioplayer.h model/commandstablemodel.h model/dirrenamer.h model/downloadclient.h model/externalprocess.h model/filefilter.h model/fileproxymodel.h model/fileproxymodeliterator.h model/frametablemodel.h model/kid3application.h model/trackdatamodel.h ) if (HAVE_QTDBUS) set(model_SRCS ${model_SRCS} model/scriptinterface.cpp) set(model_MOC_HDRS ${model_MOC_HDRS} model/scriptinterface.h) endif (HAVE_QTDBUS) kid3-3.0.2/src/core/model/audioplayer.cpp000066400000000000000000000131371224603511300202110ustar00rootroot00000000000000/** * \file audioplayer.cpp * Audio player. * * \b Project: Kid3 * \author Urs Fleisch * \date 03-Aug-2011 * * Copyright (C) 2011-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "audioplayer.h" #if defined HAVE_PHONON || QT_VERSION >= 0x050000 #include #ifdef HAVE_PHONON #include #include #include #else #include #include #include #endif /** * Constructor. * * @param parent parent object */ AudioPlayer::AudioPlayer(QObject* parent) : QObject(parent) #ifdef HAVE_PHONON , m_fileNr(-1) #endif { setObjectName(QLatin1String("AudioPlayer")); #ifdef HAVE_PHONON m_mediaObject = new Phonon::MediaObject(this); m_mediaObject->setTickInterval(1000); m_audioOutput = new Phonon::AudioOutput(Phonon::MusicCategory, this); Phonon::createPath(m_mediaObject, m_audioOutput); connect(m_mediaObject, SIGNAL(aboutToFinish()), this, SLOT(aboutToFinish())); connect(m_mediaObject, SIGNAL(currentSourceChanged(Phonon::MediaSource)), this, SLOT(currentSourceChanged())); #else m_mediaPlayer = new QMediaPlayer(this); m_mediaPlaylist = new QMediaPlaylist(m_mediaPlayer); m_mediaPlayer->setPlaylist(m_mediaPlaylist); connect(m_mediaPlaylist, SIGNAL(currentIndexChanged(int)), this, SLOT(currentIndexChanged(int))); #endif } /** * Destructor. */ AudioPlayer::~AudioPlayer() { } /** * Set files to be played. * * @param files paths to files * @param fileNr index of file to play (default 0) */ void AudioPlayer::setFiles(const QStringList& files, int fileNr) { #ifdef HAVE_PHONON m_files = files; playTrack(fileNr); #else m_mediaPlaylist->clear(); foreach (const QString& file, files) { m_mediaPlaylist->addMedia(QUrl::fromLocalFile(file)); } m_mediaPlaylist->setCurrentIndex(fileNr); m_mediaPlayer->play(); #endif } #ifdef HAVE_PHONON /** * Select a track from the files and optionally start playing it. * * @param fileNr index in list of files set with setFiles() * @param play true to play track */ void AudioPlayer::selectTrack(int fileNr, bool play) { if (fileNr >= 0 && fileNr < m_files.size()) { m_fileNr = fileNr; const QString& fileName = m_files[m_fileNr]; if (QFile::exists(fileName)) { m_mediaObject->clearQueue(); m_mediaObject->setCurrentSource(fileName); if (play) { m_mediaObject->play(); } } } else { m_fileNr = -1; } } /** * Play a track from the files. * * @param fileNr index in list of files set with setFiles() */ void AudioPlayer::playTrack(int fileNr) { selectTrack(fileNr, true); } #endif // HAVE_PHONON /** * Toggle between play and pause. */ void AudioPlayer::playOrPause() { #ifdef HAVE_PHONON switch (m_mediaObject->state()) { case Phonon::PlayingState: m_mediaObject->pause(); break; case Phonon::PausedState: m_mediaObject->play(); break; default: playTrack(m_fileNr); break; } #else switch (m_mediaPlayer->state()) { case QMediaPlayer::PlayingState: m_mediaPlayer->pause(); break; case QMediaPlayer::PausedState: case QMediaPlayer::StoppedState: default: m_mediaPlayer->play(); break; } #endif } /** * Stop playback. */ void AudioPlayer::stop() { #ifdef HAVE_PHONON m_mediaObject->stop(); m_mediaObject->clearQueue(); #else m_mediaPlayer->stop(); #endif } #ifdef HAVE_PHONON /** * Update display and button state when the current source is changed. */ void AudioPlayer::currentSourceChanged() { if (m_fileNr >= 0 && m_fileNr < m_files.size()) { emit trackChanged(m_files[m_fileNr], m_fileNr > 0, m_fileNr + 1 < m_files.size()); } } /** * Queue next track when the current track is about to finish. */ void AudioPlayer::aboutToFinish() { int nextFileNr = m_fileNr + 1; if (nextFileNr >= 0 && nextFileNr < m_files.size()) { m_fileNr = nextFileNr; const QString& fileName = m_files[m_fileNr]; if (QFile::exists(fileName)) { Phonon::MediaSource source(fileName); m_mediaObject->enqueue(source); } } } #else /** * Update display and button state when the current source is changed. * @param position number of song in play list */ void AudioPlayer::currentIndexChanged(int position) { if (position >= 0 && position < m_mediaPlaylist->mediaCount()) { emit trackChanged(m_mediaPlaylist->currentMedia().canonicalUrl().toString(), position > 0, position + 1 < m_mediaPlaylist->mediaCount()); } } #endif /** * Select previous track. */ void AudioPlayer::previous() { #ifdef HAVE_PHONON if (m_fileNr > 0) selectTrack(m_fileNr - 1, m_mediaObject->state() == Phonon::PlayingState); #else m_mediaPlaylist->previous(); #endif } /** * Select next track. */ void AudioPlayer::next() { #ifdef HAVE_PHONON if (m_fileNr + 1 < m_files.size()) selectTrack(m_fileNr + 1, m_mediaObject->state() == Phonon::PlayingState); #else m_mediaPlaylist->next(); #endif } #endif // HAVE_PHONON kid3-3.0.2/src/core/model/audioplayer.h000066400000000000000000000073451224603511300176620ustar00rootroot00000000000000/** * \file audioplayer.h * Audio player. * * \b Project: Kid3 * \author Urs Fleisch * \date 03-Aug-2011 * * Copyright (C) 2011 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef AUDIOPLAYER_H #define AUDIOPLAYER_H #include #include "config.h" #if defined HAVE_PHONON || QT_VERSION >= 0x050000 #include #include "kid3api.h" #ifdef HAVE_PHONON namespace Phonon { class AudioOutput; class MediaObject; } #else class QMediaPlayer; class QMediaPlaylist; #endif /** * Audio player toolbar. */ class KID3_CORE_EXPORT AudioPlayer : public QObject { Q_OBJECT public: /** * Constructor. * * @param parent parent object */ explicit AudioPlayer(QObject* parent); /** * Destructor. */ virtual ~AudioPlayer(); /** * Set files to be played. * * @param files paths to files * @param fileNr index of file to play (default 0) */ void setFiles(const QStringList& files, int fileNr = 0); #ifdef HAVE_PHONON /** * Play a track from the files. * * @param fileNr index in list of files set with setFiles() */ void playTrack(int fileNr); /** * Access to media object. * @return media object. */ Phonon::MediaObject* mediaObject() { return m_mediaObject; } /** * Access to audio output. * @return audio output. */ Phonon::AudioOutput* audioOutput() { return m_audioOutput; } #else /** * Access to media player. * @return media player. */ QMediaPlayer* mediaPlayer() { return m_mediaPlayer; } #endif signals: /** * Emitted when the current track is changed. * @param filePath path of currently played audio file * @param hasPrevious true if a previous track is available * @param hasNext true if a next track is available */ void trackChanged(const QString& filePath, bool hasPrevious, bool hasNext); public slots: /** * Toggle between play and pause. */ void playOrPause(); /** * Stop playback. */ void stop(); /** * Select previous track. */ void previous(); /** * Select next track. */ void next(); private slots: #ifdef HAVE_PHONON /** * Update display and button state when the current source is changed. */ void currentSourceChanged(); /** * Queue next track when the current track is about to finish. */ void aboutToFinish(); #else /** * Update display and button state when the current source is changed. * @param position number of song in play list */ void currentIndexChanged(int position); #endif private: #ifdef HAVE_PHONON /** * Select a track from the files and optionally start playing it. * * @param fileNr index in list of files set with setFiles() * @param play true to play track */ void selectTrack(int fileNr, bool play); Phonon::MediaObject* m_mediaObject; Phonon::AudioOutput* m_audioOutput; QStringList m_files; int m_fileNr; #else QMediaPlayer* m_mediaPlayer; QMediaPlaylist* m_mediaPlaylist; #endif }; #else // HAVE_PHONON // Just to suppress moc "No relevant classes found" warning. class AudioPlayer : public QObject { Q_OBJECT }; #endif // HAVE_PHONON #endif // AUDIOPLAYER_H kid3-3.0.2/src/core/model/checkablestringlistmodel.cpp000066400000000000000000000070021224603511300227320ustar00rootroot00000000000000/** * \file checkablestringlistmodel.cpp * String list model with checkable items. * * \b Project: Kid3 * \author Urs Fleisch * \date 29 Dec 2012 * * Copyright (C) 2012 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "checkablestringlistmodel.h" /** * Constructor. * @param parent parent widget */ CheckableStringListModel::CheckableStringListModel(QObject* parent) : QStringListModel(parent) { } /** * Destructor. */ CheckableStringListModel::~CheckableStringListModel() { } /** * Get item flags for index. * @param index model index * @return item flags */ Qt::ItemFlags CheckableStringListModel::flags(const QModelIndex& index) const { Qt::ItemFlags theFlags = QStringListModel::flags(index); if (index.isValid()) { theFlags &= ~Qt::ItemIsEditable; theFlags |= Qt::ItemIsUserCheckable; } return theFlags; } /** * Get data for a given role. * @param index model index * @param role item data role * @return data for role */ QVariant CheckableStringListModel::data(const QModelIndex& index, int role) const { if (role == Qt::CheckStateRole && index.isValid() && index.column() == 0 && index.row() >= 0 && index.row() < 64) { return m_bitMask & (1ULL << index.row()) ? Qt::Checked : Qt::Unchecked; } return QStringListModel::data(index, role); } /** * Set data for a given role. * @param index model index * @param value data value * @param role item data role * @return true if successful */ bool CheckableStringListModel::setData(const QModelIndex& index, const QVariant& value, int role) { if (role == Qt::CheckStateRole && index.isValid() && index.column() == 0 && index.row() >= 0 && index.row() < 64) { quint64 mask = 1ULL << index.row(); if (value == Qt::Checked) { m_bitMask |= mask; } else if (value == Qt::Unchecked) { m_bitMask &= ~mask; } return true; } return QStringListModel::setData(index, value, role); } /** * Insert rows. * @param row first row * @param count number of rows to insert * @param parent parent model index * @return true if rows were successfully inserted. */ bool CheckableStringListModel::insertRows(int row, int count, const QModelIndex& parent) { quint64 mask = (1ULL << row) - 1; m_bitMask = (m_bitMask & mask) | ((m_bitMask & ~mask) << count); return QStringListModel::insertRows(row, count, parent); } /** * removeRows * @param row first row * @param count number of rows to remove * @param parent parent model index * @return true if rows were successfully removed. */ bool CheckableStringListModel::removeRows(int row, int count, const QModelIndex& parent) { m_bitMask = (m_bitMask & ((1ULL << row) - 1)) | ((m_bitMask & ~((1ULL << (row + count)) - 1)) >> count); return QStringListModel::removeRows(row, count, parent); } kid3-3.0.2/src/core/model/checkablestringlistmodel.h000066400000000000000000000062041224603511300224020ustar00rootroot00000000000000/** * \file checkablestringlistmodel.h * String list model with checkable items. * * \b Project: Kid3 * \author Urs Fleisch * \date 29 Dec 2012 * * Copyright (C) 2012 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef CHECKABLESTRINGLISTMODEL_H #define CHECKABLESTRINGLISTMODEL_H #include #include "kid3api.h" /** * String list model with checkable items. * Up to 64 items can be stored in such a model because the check states are * stored in a 64 bit member variable. */ class KID3_CORE_EXPORT CheckableStringListModel : public QStringListModel { public: /** * Constructor. * @param parent parent widget */ explicit CheckableStringListModel(QObject* parent = 0); /** * Destructor. */ virtual ~CheckableStringListModel(); /** * Get item flags for index. * @param index model index * @return item flags */ virtual Qt::ItemFlags flags(const QModelIndex& index) const; /** * Get data for a given role. * @param index model index * @param role item data role * @return data for role */ virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; /** * Set data for a given role. * @param index model index * @param value data value * @param role item data role * @return true if successful */ virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole); /** * Insert rows. * @param row first row * @param count number of rows to insert * @param parent parent model index * @return true if rows were successfully inserted. */ virtual bool insertRows(int row, int count, const QModelIndex& parent = QModelIndex()); /** * removeRows * @param row first row * @param count number of rows to remove * @param parent parent model index * @return true if rows were successfully removed. */ virtual bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex()); /** * Set mask with checked state for the items in the model. * @param mask bit mask containing check states, bit 0 is set if the first * item is checked, etc. */ void setBitMask(quint64 mask) { m_bitMask = mask; } /** * Get mask with checked state for the items in the model. * @return bit mask containing check states, bit 0 is set if the first * item is checked, etc. */ quint64 getBitMask() const { return m_bitMask; } private: quint64 m_bitMask; }; #endif // CHECKABLESTRINGLISTMODEL_H kid3-3.0.2/src/core/model/commandformatreplacer.cpp000066400000000000000000000122671224603511300222430ustar00rootroot00000000000000/** * \file commandformatreplacer.cpp * Replaces context command format codes in a string. * * \b Project: Kid3 * \author Urs Fleisch * \date 09 Aug 2011 * * Copyright (C) 2011-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "commandformatreplacer.h" #include #include #include #include #include "networkconfig.h" /** * Constructor. * * @param frames frame collection * @param str string with format codes * @param files file list * @param isDir true if directory */ CommandFormatReplacer::CommandFormatReplacer( const FrameCollection& frames, const QString& str, const QStringList& files, bool isDir) : FrameFormatReplacer(frames, str), m_files(files), m_isDir(isDir) {} /** * Destructor. */ CommandFormatReplacer::~CommandFormatReplacer() {} /** * Replace a format code (one character %c or multiple characters %{chars}). * Supported format fields: * Those supported by FrameFormatReplacer::getReplacement() * %f %{file} filename * %d %{directory} directory name * %b %{browser} the web browser set in the configuration * * @param code format code * * @return replacement string, * QString::null if code not found. */ QString CommandFormatReplacer::getReplacement(const QString& code) const { QString result = FrameFormatReplacer::getReplacement(code); if (result.isNull()) { QString name; if (code.length() == 1) { static const struct { char shortCode; const char* longCode; } shortToLong[] = { { 'f', "file" }, { 'd', "directory" }, { 'b', "browser" } }; const char c = code[0].toLatin1(); for (unsigned i = 0; i < sizeof(shortToLong) / sizeof(shortToLong[0]); ++i) { if (shortToLong[i].shortCode == c) { name = QString::fromLatin1(shortToLong[i].longCode); break; } } } else if (code.length() > 1) { name = code; } if (!name.isNull()) { if (name == QLatin1String("file")) { if (!m_files.isEmpty()) { result = m_files.front(); } } else if (name == QLatin1String("directory")) { if (!m_files.isEmpty()) { result = m_files.front(); if (!m_isDir) { int sepPos = result.lastIndexOf(QLatin1Char('/')); if (sepPos < 0) { sepPos = result.lastIndexOf(QDir::separator()); } if (sepPos >= 0) { result.truncate(sepPos); } } } } else if (name == QLatin1String("browser")) { result = NetworkConfig::instance().m_browser; } else if (name == QLatin1String("url")) { if (!m_files.empty()) { QUrl url; url.setScheme(QLatin1String("file")); url.setPath(m_files.front()); result = url.toString(); } } } } return result; } /** * Get help text for supported format codes. * * @param onlyRows if true only the tr elements are returned, * not the surrounding table * * @return help text. */ QString CommandFormatReplacer::getToolTip(bool onlyRows) { QString str; if (!onlyRows) str += QLatin1String("\n"); str += FrameFormatReplacer::getToolTip(true); str += QLatin1String("\n"); str += QLatin1String("\n"); str += QLatin1String("\n"); str += QLatin1String("\n"); str += QLatin1String("\n"); str += QLatin1String("\n"); str += QLatin1String("\n"); if (!onlyRows) str += QLatin1String("
%f%{file}"); str += QCoreApplication::translate("@default", "Filename"); str += QLatin1String("
%F%{files}"); str += QCoreApplication::translate("@default", QT_TRANSLATE_NOOP("@default", "Filenames")); str += QLatin1String("
%uf%{url}"); str += QCoreApplication::translate("@default", "URL"); str += QLatin1String("
%uF%{urls}"); str += QCoreApplication::translate("@default", QT_TRANSLATE_NOOP("@default", "URLs")); str += QLatin1String("
%d%{directory}"); str += QCoreApplication::translate("@default", QT_TRANSLATE_NOOP("@default", "Directory name")); str += QLatin1String("
%b%{browser}"); str += QCoreApplication::translate("@default", "Browser"); str += QLatin1String("
%ua...%u{artist}..."); str += QCoreApplication::translate("@default", QT_TRANSLATE_NOOP("@default", "Encode as URL")); str += QLatin1String("
\n"); return str; } kid3-3.0.2/src/core/model/commandformatreplacer.h000066400000000000000000000044311224603511300217020ustar00rootroot00000000000000/** * \file commandformatreplacer.h * Replaces context command format codes in a string. * * \b Project: Kid3 * \author Urs Fleisch * \date 09 Aug 2011 * * Copyright (C) 2011 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef COMMANDFORMATREPLACER_H #define COMMANDFORMATREPLACER_H #include "frame.h" /** * Replaces context command format codes in a string. */ class KID3_CORE_EXPORT CommandFormatReplacer : public FrameFormatReplacer { public: /** * Constructor. * * @param frames frame collection * @param str string with format codes * @param files file list * @param isDir true if directory */ explicit CommandFormatReplacer( const FrameCollection& frames, const QString& str, const QStringList& files, bool isDir); /** * Destructor. */ virtual ~CommandFormatReplacer(); /** * Get help text for supported format codes. * * @param onlyRows if true only the tr elements are returned, * not the surrounding table * * @return help text. */ static QString getToolTip(bool onlyRows = false); protected: /** * Replace a format code (one character %c or multiple characters %{chars}). * Supported format fields: * Those supported by FrameFormatReplacer::getReplacement() * %f %{file} filename * %d %{directory} directory name * %b %{browser} the web browser set in the configuration * * @param code format code * * @return replacement string, * QString::null if code not found. */ virtual QString getReplacement(const QString& code) const; private: const QStringList& m_files; const bool m_isDir; }; #endif // COMMANDFORMATREPLACER_H kid3-3.0.2/src/core/model/commandstablemodel.cpp000066400000000000000000000170231224603511300215230ustar00rootroot00000000000000/** * \file commandstablemodel.cpp * Context menu commands configuration table model. * * \b Project: Kid3 * \author Urs Fleisch * \date 13 Mar 2011 * * Copyright (C) 2005-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "commandstablemodel.h" #include "commandformatreplacer.h" /** Column indices. */ enum ColumnIndex { CI_Confirm, CI_Output, CI_Name, CI_Command, CI_NumColumns }; /** * Constructor. * @param parent parent widget */ CommandsTableModel::CommandsTableModel(QObject* parent) : QAbstractTableModel(parent) { setObjectName(QLatin1String("CommandsTableModel")); } /** * Destructor. */ CommandsTableModel::~CommandsTableModel() { } /** * Get item flags for index. * @param index model index * @return item flags */ Qt::ItemFlags CommandsTableModel::flags(const QModelIndex& index) const { Qt::ItemFlags theFlags = QAbstractTableModel::flags(index); if (index.isValid()) { theFlags |= Qt::ItemIsSelectable | Qt::ItemIsEnabled; if (index.column() == CI_Confirm || index.column() == CI_Output) { theFlags |= Qt::ItemIsUserCheckable; } else { theFlags |= Qt::ItemIsEditable; } } return theFlags; } /** * Get data for a given role. * @param index model index * @param role item data role * @return data for role */ QVariant CommandsTableModel::data(const QModelIndex& index, int role) const { if (!index.isValid() || index.row() < 0 || index.row() >= m_cmdList.size() || index.column() < 0 || index.column() >= CI_NumColumns) return QVariant(); const UserActionsConfig::MenuCommand& item = m_cmdList.at(index.row()); if (role == Qt::DisplayRole || role == Qt::EditRole) { switch (index.column()) { case CI_Name: return item.getName(); case CI_Command: return item.getCommand(); default: ; } } if (role == Qt::CheckStateRole) { switch (index.column()) { case CI_Confirm: return item.mustBeConfirmed() ? Qt::Checked : Qt::Unchecked; case CI_Output: return item.outputShown() ? Qt::Checked : Qt::Unchecked; default: ; } } return QVariant(); } /** * Set data for a given role. * @param index model index * @param value data value * @param role item data role * @return true if successful */ bool CommandsTableModel::setData(const QModelIndex& index, const QVariant& value, int role) { if (!index.isValid() || index.row() < 0 || index.row() >= m_cmdList.size() || index.column() < 0 || index.column() >= CI_NumColumns) return false; UserActionsConfig::MenuCommand& item = m_cmdList[index.row()]; if (role == Qt::EditRole) { switch (index.column()) { case CI_Name: item.setName(value.toString()); break; case CI_Command: item.setCommand(value.toString()); break; default: return false; } } else if (role == Qt::CheckStateRole) { switch (index.column()) { case CI_Confirm: item.setMustBeConfirmed(value.toInt() == Qt::Checked); break; case CI_Output: item.setOutputShown(value.toInt() == Qt::Checked); break; default: return false; } } else { return false; } emit dataChanged(index, index); return true; } /** * Get data for header section. * @param section column or row * @param orientation horizontal or vertical * @param role item data role * @return header data for role */ QVariant CommandsTableModel::headerData( int section, Qt::Orientation orientation, int role) const { if (role == Qt::ToolTipRole && orientation == Qt::Horizontal && section == CI_Command) return CommandFormatReplacer::getToolTip(); if (role != Qt::DisplayRole) return QVariant(); if (orientation == Qt::Horizontal) { switch (section) { case CI_Confirm: return tr("Confirm"); case CI_Output: return tr("Output"); case CI_Name: return tr("Name"); case CI_Command: return tr("Command"); default: return section + 1; } } return section + 1; } /** * Get number of rows. * @param parent parent model index, invalid for table models * @return number of rows, * if parent is valid number of children (0 for table models) */ int CommandsTableModel::rowCount(const QModelIndex& parent) const { return parent.isValid() ? 0 : m_cmdList.size(); } /** * Get number of columns. * @param parent parent model index, invalid for table models * @return number of columns, * if parent is valid number of children (0 for table models) */ int CommandsTableModel::columnCount(const QModelIndex& parent) const { return parent.isValid() ? 0 : CI_NumColumns; } /** * Insert rows. * @param row rows are inserted before this row, if 0 at the begin, * if rowCount() at the end * @param count number of rows to insert * @return true if successful */ bool CommandsTableModel::insertRows(int row, int count, const QModelIndex&) { beginInsertRows(QModelIndex(), row, row + count - 1); for (int i = 0; i < count; ++i) m_cmdList.insert(row, UserActionsConfig::MenuCommand()); endInsertRows(); return true; } /** * Remove rows. * @param row rows are removed starting with this row * @param count number of rows to remove * @return true if successful */ bool CommandsTableModel::removeRows(int row, int count, const QModelIndex&) { beginRemoveRows(QModelIndex(), row, row + count - 1); for (int i = 0; i < count; ++i) m_cmdList.removeAt(row); endRemoveRows(); return true; } /** * Get the resize modes to be used for the columns. * @return list of resize modes for the columns */ QList CommandsTableModel::getHorizontalResizeModes() const { QList resizeModes; for (int i = 0; i < CI_NumColumns; ++i) { QHeaderView::ResizeMode mode = QHeaderView::Interactive; if (i == CI_Confirm || i == CI_Output) mode = QHeaderView::ResizeToContents; else if (i == CI_Command) mode = QHeaderView::Stretch; resizeModes.append(mode); } return resizeModes; } /** * Set the model from the command list. * @param cmdList command list */ void CommandsTableModel::setCommandList( const QList& cmdList) { #if QT_VERSION >= 0x040600 beginResetModel(); m_cmdList = cmdList; endResetModel(); #else m_cmdList = cmdList; reset(); #endif } /** * Get the command list from the model. * @return command list */ QList CommandsTableModel::getCommandList() const { QList cmdList; for (QList::const_iterator it = m_cmdList.constBegin(); it != m_cmdList.constEnd(); ++it) { if (!it->getName().isEmpty()) { cmdList.append(*it); } } if (cmdList.isEmpty()) { // Make sure that their is at least one entry, so that new entries can // be added. cmdList.append(UserActionsConfig::MenuCommand()); } return cmdList; } kid3-3.0.2/src/core/model/commandstablemodel.h000066400000000000000000000105701224603511300211700ustar00rootroot00000000000000/** * \file commandstablemodel.h * Context menu commands configuration table model. * * \b Project: Kid3 * \author Urs Fleisch * \date 13 Mar 2011 * * Copyright (C) 2005-2011 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef COMMANDSTABLEMODEL_H #define COMMANDSTABLEMODEL_H #include #include #include #include "useractionsconfig.h" #include "kid3api.h" /** * Context menu commands configuration table model. */ class KID3_CORE_EXPORT CommandsTableModel : public QAbstractTableModel { Q_OBJECT public: /** * Constructor. * @param parent parent widget */ explicit CommandsTableModel(QObject* parent = 0); /** * Destructor. */ virtual ~CommandsTableModel(); /** * Get item flags for index. * @param index model index * @return item flags */ virtual Qt::ItemFlags flags(const QModelIndex& index) const; /** * Get data for a given role. * @param index model index * @param role item data role * @return data for role */ virtual QVariant data(const QModelIndex& index, int role=Qt::DisplayRole) const; /** * Set data for a given role. * @param index model index * @param value data value * @param role item data role * @return true if successful */ virtual bool setData(const QModelIndex& index, const QVariant& value, int role=Qt::EditRole); /** * Get data for header section. * @param section column or row * @param orientation horizontal or vertical * @param role item data role * @return header data for role */ virtual QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const; /** * Set data for header section. * Not supported. * @return false */ virtual bool setHeaderData(int, Qt::Orientation, const QVariant&, int=Qt::EditRole) { return false; } /** * Get number of rows. * @param parent parent model index, invalid for table models * @return number of rows, * if parent is valid number of children (0 for table models) */ virtual int rowCount(const QModelIndex& parent=QModelIndex()) const; /** * Get number of columns. * @param parent parent model index, invalid for table models * @return number of columns, * if parent is valid number of children (0 for table models) */ virtual int columnCount(const QModelIndex& parent=QModelIndex()) const; /** * Insert rows. * @param row rows are inserted before this row, if 0 at the begin, * if rowCount() at the end * @param count number of rows to insert * @param parent parent model index, invalid for table models * @return true if successful */ virtual bool insertRows(int row, int count, const QModelIndex& parent=QModelIndex()); /** * Remove rows. * @param row rows are removed starting with this row * @param count number of rows to remove * @param parent parent model index, invalid for table models * @return true if successful */ virtual bool removeRows(int row, int count, const QModelIndex& parent=QModelIndex()); /** * Get the resize modes to be used for the columns. * @return list of resize modes for the columns */ QList getHorizontalResizeModes() const; /** * Set the model from the command list. * @param cmdList command list */ void setCommandList(const QList& cmdList); /** * Get the command list from the model. * @return command list */ QList getCommandList() const; private: QList m_cmdList; }; #endif // COMMANDSTABLEMODEL_H kid3-3.0.2/src/core/model/configtablemodel.cpp000066400000000000000000000144631224603511300211740ustar00rootroot00000000000000/** * \file configtablemodel.cpp * Model for table with context menu to add and remove rows. * * \b Project: Kid3 * \author Urs Fleisch * \date 13 Mar 2011 * * Copyright (C) 2005-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "configtablemodel.h" /** * Constructor. * @param parent parent widget */ ConfigTableModel::ConfigTableModel(QObject* parent) : QAbstractTableModel(parent) { setObjectName(QLatin1String("ConfigTableModel")); } /** * Destructor. */ ConfigTableModel::~ConfigTableModel() { } /** * Get item flags for index. * @param index model index * @return item flags */ Qt::ItemFlags ConfigTableModel::flags(const QModelIndex& index) const { Qt::ItemFlags theFlags = QAbstractTableModel::flags(index); if (index.isValid()) theFlags |= Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable; return theFlags; } /** * Get data for a given role. * @param index model index * @param role item data role * @return data for role */ QVariant ConfigTableModel::data(const QModelIndex& index, int role) const { if (!index.isValid() || index.row() < 0 || index.row() >= m_keyValues.size() || index.column() < 0 || index.column() >= 2) return QVariant(); const QPair& keyValue = m_keyValues.at(index.row()); if (role == Qt::DisplayRole || role == Qt::EditRole) { if (index.column() == 0) return keyValue.first; else return keyValue.second; } return QVariant(); } /** * Set data for a given role. * @param index model index * @param value data value * @param role item data role * @return true if successful */ bool ConfigTableModel::setData(const QModelIndex& index, const QVariant& value, int role) { if (!index.isValid() || role != Qt::EditRole || index.row() < 0 || index.row() >= m_keyValues.size() || index.column() < 0 || index.column() >= 2) return false; QPair& keyValue = m_keyValues[index.row()]; if (index.column() == 0) { keyValue.first = value.toString(); } else { keyValue.second = value.toString(); } emit dataChanged(index, index); return true; } /** * Get data for header section. * @param section column or row * @param orientation horizontal or vertical * @param role item data role * @return header data for role */ QVariant ConfigTableModel::headerData( int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole) return QVariant(); if (orientation == Qt::Horizontal && section < m_labels.size()) { return m_labels[section]; } return section + 1; } /** * Get number of rows. * @param parent parent model index, invalid for table models * @return number of rows, * if parent is valid number of children (0 for table models) */ int ConfigTableModel::rowCount(const QModelIndex& parent) const { return parent.isValid() ? 0 : m_keyValues.size(); } /** * Get number of columns. * @param parent parent model index, invalid for table models * @return number of columns, * if parent is valid number of children (0 for table models) */ int ConfigTableModel::columnCount(const QModelIndex& parent) const { return parent.isValid() ? 0 : 2; } /** * Insert rows. * @param row rows are inserted before this row, if 0 at the begin, * if rowCount() at the end * @param count number of rows to insert * @param parent parent model index, invalid for table models * @return true if successful */ bool ConfigTableModel::insertRows(int row, int count, const QModelIndex&) { beginInsertRows(QModelIndex(), row, row + count - 1); for (int i = 0; i < count; ++i) m_keyValues.insert(row, QPair()); endInsertRows(); return true; } /** * Remove rows. * @param row rows are removed starting with this row * @param count number of rows to remove * @param parent parent model index, invalid for table models * @return true if successful */ bool ConfigTableModel::removeRows(int row, int count, const QModelIndex&) { beginRemoveRows(QModelIndex(), row, row + count - 1); for (int i = 0; i < count; ++i) m_keyValues.removeAt(row); endRemoveRows(); return true; } /** * Get the resize modes to be used for the columns. * @return list of resize modes for the columns */ QList ConfigTableModel::getHorizontalResizeModes() const { return QList() << QHeaderView::Stretch << QHeaderView::Stretch; } /** * Set the column labels. * @param labels column labels */ void ConfigTableModel::setLabels(const QStringList& labels) { #if QT_VERSION >= 0x040600 beginResetModel(); m_labels = labels; endResetModel(); #else m_labels = labels; reset(); #endif } /** * Set the model from a map. * * @param map map with keys and values */ void ConfigTableModel::setMap(const QMap& map) { #if QT_VERSION >= 0x040600 beginResetModel(); #endif m_keyValues.clear(); for (QMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it) { m_keyValues.append(qMakePair(it.key(), it.value())); } // make sure that at least one line is in the table if (m_keyValues.isEmpty()) m_keyValues.append(qMakePair(QString(), QString())); #if QT_VERSION >= 0x040600 endResetModel(); #else reset(); #endif } /** * Get map from the model. * @return map with keys and values */ QMap ConfigTableModel::getMap() const { QMap map; for (QList >::const_iterator it = m_keyValues.constBegin(); it != m_keyValues.constEnd(); ++it) { if (!it->first.isEmpty()) { map.insert(it->first, it->second); } } return map; } kid3-3.0.2/src/core/model/configtablemodel.h000066400000000000000000000107051224603511300206340ustar00rootroot00000000000000/** * \file configtablemodel.h * Model for table with context menu to add and remove rows. * * \b Project: Kid3 * \author Urs Fleisch * \date 13 Mar 2011 * * Copyright (C) 2005-2011 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef CONFIGTABLEMODEL_H #define CONFIGTABLEMODEL_H #include #include #include #include #include #include "kid3api.h" /** * Context menu commands configuration table model. */ class KID3_CORE_EXPORT ConfigTableModel : public QAbstractTableModel { public: /** * Constructor. * @param parent parent widget */ explicit ConfigTableModel(QObject* parent = 0); /** * Destructor. */ virtual ~ConfigTableModel(); /** * Get item flags for index. * @param index model index * @return item flags */ virtual Qt::ItemFlags flags(const QModelIndex& index) const; /** * Get data for a given role. * @param index model index * @param role item data role * @return data for role */ virtual QVariant data(const QModelIndex& index, int role=Qt::DisplayRole) const; /** * Set data for a given role. * @param index model index * @param value data value * @param role item data role * @return true if successful */ virtual bool setData(const QModelIndex& index, const QVariant& value, int role=Qt::EditRole); /** * Get data for header section. * @param section column or row * @param orientation horizontal or vertical * @param role item data role * @return header data for role */ virtual QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const; /** * Set data for header section. * Not supported. * @return false */ virtual bool setHeaderData(int, Qt::Orientation, const QVariant&, int=Qt::EditRole) { return false; } /** * Get number of rows. * @param parent parent model index, invalid for table models * @return number of rows, * if parent is valid number of children (0 for table models) */ virtual int rowCount(const QModelIndex& parent=QModelIndex()) const; /** * Get number of columns. * @param parent parent model index, invalid for table models * @return number of columns, * if parent is valid number of children (0 for table models) */ virtual int columnCount(const QModelIndex& parent=QModelIndex()) const; /** * Insert rows. * @param row rows are inserted before this row, if 0 at the begin, * if rowCount() at the end * @param count number of rows to insert * @param parent parent model index, invalid for table models * @return true if successful */ virtual bool insertRows(int row, int count, const QModelIndex& parent=QModelIndex()); /** * Remove rows. * @param row rows are removed starting with this row * @param count number of rows to remove * @param parent parent model index, invalid for table models * @return true if successful */ virtual bool removeRows(int row, int count, const QModelIndex& parent=QModelIndex()); /** * Get the resize modes to be used for the columns. * @return list of resize modes for the columns */ QList getHorizontalResizeModes() const; /** * Set the column labels. * @param labels column labels */ void setLabels(const QStringList& labels); /** * Set the model from a map. * @param map map with keys and values */ void setMap(const QMap& map); /** * Get map from the model. * @return map with keys and values */ QMap getMap() const; private: QStringList m_labels; QList > m_keyValues; }; #endif // CONFIGTABLEMODEL_H kid3-3.0.2/src/core/model/dirlist.cpp000066400000000000000000000032131224603511300173370ustar00rootroot00000000000000/** * \file dirlist.cpp * List of directories to operate on. * * \b Project: Kid3 * \author Urs Fleisch * \date 5 Jul 2005 * * Copyright (C) 2005-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "dirlist.h" #include /** * Constructor. * @param parent parent widget */ DirList::DirList(QWidget* parent) : QListView(parent) { setObjectName(QLatin1String("DirList")); } /** * Destructor. */ DirList::~DirList() {} /** * Fill the dirlist with the directories found in a directory. * * @param index index of path in filesystem model * @return false if name is not directory path, else true. */ bool DirList::readDir(const QModelIndex& index) { QAbstractProxyModel* proxyModel = qobject_cast(model()); QModelIndex rootIndex = proxyModel ? proxyModel->mapFromSource(index) : index; if (rootIndex.isValid()) { setRootIndex(rootIndex); if (m_entryToSelect.isValid()) { setCurrentIndex(m_entryToSelect); } return true; } return false; } kid3-3.0.2/src/core/model/dirlist.h000066400000000000000000000033351224603511300170110ustar00rootroot00000000000000/** * \file dirlist.h * List of directories to operate on. * * \b Project: Kid3 * \author Urs Fleisch * \date 5 Jul 2005 * * Copyright (C) 2005-2011 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef DIRLIST_H #define DIRLIST_H #include #include "kid3api.h" /** * List of directories to operate on. */ class KID3_CORE_EXPORT DirList : public QListView { public: /** * Constructor. * @param parent parent widget */ explicit DirList(QWidget* parent = 0); /** * Destructor. */ virtual ~DirList(); /** * Fill the dirlist with the directories found in a directory. * * @param index index of path in filesystem model * @return false if name is not directory path, else true. */ bool readDir(const QModelIndex& index); /** * Set index of entry to select in next call to readDir(). * * @param index model index of entry to select */ void setEntryToSelect(const QPersistentModelIndex& index) { m_entryToSelect = index; } private: /** entry to select in readDir() */ QPersistentModelIndex m_entryToSelect; }; #endif // DIRLIST_H kid3-3.0.2/src/core/model/dirproxymodel.cpp000066400000000000000000000030051224603511300205650ustar00rootroot00000000000000/** * \file dirproxymodel.cpp * Proxy for filesystem model which filters directories. * * \b Project: Kid3 * \author Urs Fleisch * \date 19-Mar-2011 * * Copyright (C) 2011-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "dirproxymodel.h" #include /** * Constructor. * * @param parent parent object */ DirProxyModel::DirProxyModel(QObject* parent) : QSortFilterProxyModel(parent) { setObjectName(QLatin1String("DirProxyModel")); } /** * Check if row should be included in model. * * @param srcRow source row * @param srcParent source parent * * @return true to include row. */ bool DirProxyModel::filterAcceptsRow(int srcRow, const QModelIndex& srcParent) const { QFileSystemModel* srcModel = qobject_cast(sourceModel()); if (srcModel) { return srcModel->isDir(srcModel->index(srcRow, 0, srcParent)); } return false; } kid3-3.0.2/src/core/model/dirproxymodel.h000066400000000000000000000027061224603511300202410ustar00rootroot00000000000000/** * \file dirproxymodel.h * Proxy for filesystem model which filters directories. * * \b Project: Kid3 * \author Urs Fleisch * \date 19-Mar-2011 * * Copyright (C) 2011 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef DIRPROXYMODEL_H #define DIRPROXYMODEL_H #include /** * Proxy for filesystem model which filters directories. */ class DirProxyModel : public QSortFilterProxyModel { public: /** * Constructor. * * @param parent parent object */ explicit DirProxyModel(QObject* parent = 0); protected: /** * Check if row should be included in model. * * @param srcRow source row * @param srcParent source parent * * @return true to include row. */ virtual bool filterAcceptsRow(int srcRow, const QModelIndex& srcParent) const; }; #endif // DIRPROXYMODEL_H kid3-3.0.2/src/core/model/dirrenamer.cpp000066400000000000000000000350371224603511300200260ustar00rootroot00000000000000/** * \file dirrenamer.cpp * Directory renamer. * * \b Project: Kid3 * \author Urs Fleisch * \date 23 Jul 2011 * * Copyright (C) 2011-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "dirrenamer.h" #include #include #include #include "saferename.h" #include "fileproxymodel.h" #include "modeliterator.h" /** * Constructor. * @param parent parent object */ DirRenamer::DirRenamer(QObject* parent) : QObject(parent), m_aborted(false), m_tagVersion(TrackData::TagV2V1), m_actionCreate(false) { setObjectName(QLatin1String("DirRenamer")); } /** * Destructor. */ DirRenamer::~DirRenamer() { } /** * Get parent directory. * * @param dir directory * * @return parent directory (terminated by separator), * empty string if no separator in dir. */ static QString parentDirectory(const QString& dir) { QString parent(dir); int slashPos = parent.lastIndexOf(QLatin1Char('/')); if (slashPos != -1) { parent.truncate(slashPos + 1); } else { parent = QLatin1String(""); } return parent; } /** Only defined for generation of translation files */ #define CREATE_DIR_FAILED_FOR_PO QT_TRANSLATE_NOOP("@default", "Create directory %1 failed\n") /** * Create a directory if it does not exist. * * @param dir directory path * @param errorMsg if not NULL and an error occurred, a message is appended here, * otherwise it is not touched * * @return true if directory exists or was created successfully. */ bool DirRenamer::createDirectory(const QString& dir, QString* errorMsg) const { if (QFileInfo(dir).isDir() || (QDir().mkdir(dir) && QFileInfo(dir).isDir())) { return true; } else { if (errorMsg) { errorMsg->append(tr("Create directory %1 failed\n").arg(dir)); } return false; } } /** Only defined for generation of translation files */ #define FILE_ALREADY_EXISTS_FOR_PO QT_TRANSLATE_NOOP("@default", "File %1 already exists\n") /** Only defined for generation of translation files */ #define IS_NOT_DIR_FOR_PO QT_TRANSLATE_NOOP("@default", "%1 is not a directory\n") /** Only defined for generation of translation files */ #define RENAME_FAILED_FOR_PO QT_TRANSLATE_NOOP("@default", "Rename %1 to %2 failed\n") /** * Rename a directory. * * @param olddir old directory name * @param newdir new directory name * @param index model index of item to rename * @param errorMsg if not NULL and an error occurred, a message is * appended here, otherwise it is not touched * * @return true if rename successful. */ bool DirRenamer::renameDirectory( const QString& olddir, const QString& newdir, const QPersistentModelIndex& index, QString* errorMsg) const { if (QFileInfo(newdir).exists()) { if (errorMsg) { errorMsg->append(tr("File %1 already exists\n").arg(newdir)); } return false; } if (!QFileInfo(olddir).isDir()) { if (errorMsg) { errorMsg->append(tr("%1 is not a directory\n").arg(olddir)); } return false; } if (index.isValid()) { // The directory must be closed before renaming on Windows. TaggedFileIterator::closeFileHandles(index); } if (Utils::safeRename(olddir, newdir) && QFileInfo(newdir).isDir()) { return true; } else { if (errorMsg) { errorMsg->append(tr("Rename %1 to %2 failed\n").arg(olddir).arg(newdir)); } return false; } } /** Only defined for generation of translation files */ #define ALREADY_EXISTS_FOR_PO QT_TRANSLATE_NOOP("@default", "%1 already exists\n") /** Only defined for generation of translation files */ #define IS_NOT_FILE_FOR_PO QT_TRANSLATE_NOOP("@default", "%1 is not a file\n") /** * Rename a file. * * @param oldfn old file name * @param newfn new file name * @param errorMsg if not NULL and an error occurred, a message is * appended here, otherwise it is not touched * @param index model index of item to rename * * @return true if rename successful or newfn already exists. */ bool DirRenamer::renameFile(const QString& oldfn, const QString& newfn, const QPersistentModelIndex& index, QString* errorMsg) const { if (QFileInfo(newfn).isFile()) { return true; } if (QFileInfo(newfn).exists()) { if (errorMsg) { errorMsg->append(tr("%1 already exists\n").arg(newfn)); } return false; } if (!QFileInfo(oldfn).isFile()) { if (errorMsg) { errorMsg->append(tr("%1 is not a file\n").arg(oldfn)); } return false; } if (TaggedFile* taggedFile = FileProxyModel::getTaggedFileOfIndex(index)) { // The file must be closed before renaming on Windows. taggedFile->closeFileHandle(); } if (Utils::safeRename(oldfn, newfn) && QFileInfo(newfn).isFile()) { return true; } else { if (errorMsg) { errorMsg->append(tr("Rename %1 to %2 failed\n").arg(oldfn).arg(newfn)); } return false; } } /** * Generate new directory name according to current settings. * * @param taggedFile file to get information from * @param olddir pointer to QString to place old directory name into, * NULL if not used * * @return new directory name. */ QString DirRenamer::generateNewDirname(TaggedFile* taggedFile, QString* olddir) { taggedFile->readTags(false); TrackData trackData(*taggedFile, m_tagVersion); QString newdir(taggedFile->getDirname()); #ifdef Q_OS_WIN32 newdir.replace(QLatin1Char('\\'), QLatin1Char('/')); #endif if (newdir.endsWith(QLatin1Char('/'))) { // remove trailing separator newdir.truncate(newdir.length() - 1); } if (olddir) { *olddir = newdir; } if (!trackData.isEmptyOrInactive()) { if (!m_actionCreate) { newdir = parentDirectory(newdir); } else if (!newdir.isEmpty()) { newdir.append(QLatin1Char('/')); } newdir.append(trackData.formatFilenameFromTags(m_format, true)); } return newdir; } /** * Clear the rename actions. * This method has to be called before scheduling new actions. */ void DirRenamer::clearActions() { m_actions.clear(); } /** * Add a rename action. * * @param type type of action * @param src source file or directory name * @param dest destination file or directory name * @param index model index of item to rename */ void DirRenamer::addAction(RenameAction::Type type, const QString& src, const QString& dest, const QPersistentModelIndex& index) { // do not add an action if the source or destination is already in an action for (RenameActionList::const_iterator it = m_actions.begin(); it != m_actions.end(); ++it) { if ((!src.isEmpty() && (*it).m_src == src) || (!dest.isEmpty() && (*it).m_dest == dest)){ return; } } RenameAction action(type, src, dest, index); m_actions.append(action); emit actionScheduled(describeAction(action)); } /** * Add a rename action. * * @param type type of action * @param dest destination file or directory name */ void DirRenamer::addAction(RenameAction::Type type, const QString& dest) { addAction(type, QString(), dest); } /** * Check if there is already an action scheduled for this source. * * @return true if a rename action for the source exists. */ bool DirRenamer::actionHasSource(const QString& src) const { if (src.isEmpty()) { return false; } for (RenameActionList::const_iterator it = m_actions.begin(); it != m_actions.end(); ++it) { if ((*it).m_src == src) { return true; } } return false; } /** * Check if there is already an action scheduled for this destination. * * @return true if a rename or create action for the destination exists. */ bool DirRenamer::actionHasDestination(const QString& dest) const { if (dest.isEmpty()) { return false; } for (RenameActionList::const_iterator it = m_actions.begin(); it != m_actions.end(); ++it) { if ((*it).m_dest == dest) { return true; } } return false; } /** * Replace directory name if there is already a rename action. * * @param src directory name, will be replaced if there is a rename action */ void DirRenamer::replaceIfAlreadyRenamed(QString& src) const { bool found = true; for (int i = 0; found && i < 5; ++i) { found = false; for (RenameActionList::const_iterator it = m_actions.begin(); it != m_actions.end(); ++it) { if ((*it).m_type == RenameAction::RenameDirectory && (*it).m_src == src) { src = (*it).m_dest; found = true; break; } } } } /** * Schedule the actions necessary to rename the directory containing a file. * * @param taggedFile file in directory */ void DirRenamer::scheduleAction(TaggedFile* taggedFile) { QString currentDirname; QString newDirname(generateNewDirname(taggedFile, ¤tDirname)); bool again = false; for (int round = 0; round < 2; ++round) { replaceIfAlreadyRenamed(currentDirname); if (newDirname != currentDirname) { if (newDirname.startsWith(currentDirname + QLatin1Char('/'))) { // A new directory is created in the current directory. bool createDir = true; QString dirWithFiles(currentDirname); for (int i = 0; createDir && newDirname.startsWith(currentDirname) && i < 5; i++) { QString newPart(newDirname.mid(currentDirname.length())); // currentDirname does not end with a separator, so newPart // starts with a separator and the search starts with the // second character. int slashPos = newPart.indexOf(QLatin1Char('/'), 1); if (slashPos != -1 && slashPos != (int)newPart.length() - 1) { newPart.truncate(slashPos); // the new part has multiple directories // => create one directory } else { createDir = false; } // Create a directory for each file and move it. addAction(RenameAction::CreateDirectory, currentDirname + newPart); if (!createDir) { addAction(RenameAction::RenameFile, dirWithFiles + QLatin1Char('/') + taggedFile->getFilename(), currentDirname + newPart + QLatin1Char('/') + taggedFile->getFilename(), taggedFile->getIndex()); } currentDirname = currentDirname + newPart; } } else { QString parent(parentDirectory(currentDirname)); if (newDirname.startsWith(parent)) { QString newPart(newDirname.mid(parent.length())); int slashPos = newPart.indexOf(QLatin1Char('/')); if (slashPos != -1 && slashPos != (int)newPart.length() - 1) { newPart.truncate(slashPos); // the new part has multiple directories // => rename current directory, then create additional // directories. again = true; } QString parentWithNewPart = parent + newPart; if ((QFileInfo(parentWithNewPart).isDir() && !actionHasSource(parentWithNewPart)) || actionHasDestination(parentWithNewPart)) { // directory already exists => move files addAction(RenameAction::RenameFile, currentDirname + QLatin1Char('/') + taggedFile->getFilename(), parentWithNewPart + QLatin1Char('/') + taggedFile->getFilename(), taggedFile->getIndex()); currentDirname = parentWithNewPart; } else { addAction(RenameAction::RenameDirectory, currentDirname, parentWithNewPart, taggedFile->getIndex().parent()); currentDirname = parentWithNewPart; } } else { // new directory name is too different addAction(RenameAction::ReportError, tr("New directory name is too different\n")); } } } if (!again) break; } } /** * Perform the scheduled rename actions. * * @param errorMsg if not 0 and an error occurred, a message is appended here, * otherwise it is not touched */ void DirRenamer::performActions(QString* errorMsg) { for (RenameActionList::const_iterator it = m_actions.begin(); it != m_actions.end(); ++it) { switch ((*it).m_type) { case RenameAction::CreateDirectory: createDirectory((*it).m_dest, errorMsg); break; case RenameAction::RenameDirectory: if (renameDirectory((*it).m_src, (*it).m_dest, (*it).m_index, errorMsg)) { if ((*it).m_src == m_dirName) { m_dirName = (*it).m_dest; } } break; case RenameAction::RenameFile: renameFile((*it).m_src, (*it).m_dest, (*it).m_index, errorMsg); break; case RenameAction::ReportError: default: if (errorMsg) { *errorMsg += (*it).m_dest; } } } } /** * Get description of an actions to be performed. * @return (action, [src,] dst) list describing the action to be * performed. */ QStringList DirRenamer::describeAction(const RenameAction& action) const { static const char* const typeStr[] = { QT_TRANSLATE_NOOP("@default", "Create directory"), QT_TRANSLATE_NOOP("@default", "Rename directory"), QT_TRANSLATE_NOOP("@default", "Rename file"), QT_TRANSLATE_NOOP("@default", "Error") }; static const unsigned numTypeStr = sizeof(typeStr) / sizeof(typeStr[0]); QStringList actionStrs; unsigned typeIdx = static_cast(action.m_type); if (typeIdx >= numTypeStr) { typeIdx = numTypeStr - 1; } actionStrs.append(QCoreApplication::translate("@default", typeStr[typeIdx])); if (!action.m_src.isEmpty()) { actionStrs.append(action.m_src); } actionStrs.append(action.m_dest); return actionStrs; } /** * Check if operation is aborted. * * @return true if aborted. */ bool DirRenamer::isAborted() const { return m_aborted; } /** * Clear state which is reported by isAborted(). */ void DirRenamer::clearAborted() { m_aborted = false; } /** * Abort operation. */ void DirRenamer::abort() { m_aborted = true; } kid3-3.0.2/src/core/model/dirrenamer.h000066400000000000000000000200321224603511300174600ustar00rootroot00000000000000/** * \file dirrenamer.h * Directory renamer. * * \b Project: Kid3 * \author Urs Fleisch * \date 23 Jul 2011 * * Copyright (C) 2011 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef DIRRENAMER_H #define DIRRENAMER_H #include #include #include "trackdata.h" #include "iabortable.h" #include "kid3api.h" class TaggedFile; /** * Directory renamer. */ class KID3_CORE_EXPORT DirRenamer : public QObject, public IAbortable { Q_OBJECT public: /** * Constructor. * @param parent parent object */ explicit DirRenamer(QObject* parent = 0); /** * Destructor. */ virtual ~DirRenamer(); /** * Check if operation is aborted. * * @return true if aborted. */ virtual bool isAborted() const; /** * Clear state which is reported by isAborted(). */ virtual void clearAborted(); /** * Abort operation. */ virtual void abort(); /** * Set version of tags used to get rename information. * @param tagVersion tag version */ void setTagVersion(TrackData::TagVersion tagVersion) { m_tagVersion = tagVersion; } /** * Set action to be performed. * @param create true for create action */ void setAction(bool create) { m_actionCreate = create; } /** * Set format to generate directory names. * @param format format */ void setFormat(const QString& format) { m_format = format; } /** * Generate new directory name according to current settings. * * @param taggedFile file to get information from * @param olddir pointer to QString to place old directory name into, * NULL if not used * * @return new directory name. */ QString generateNewDirname(TaggedFile* taggedFile, QString* olddir); /** * Clear the rename actions. * This method has to be called before scheduling new actions. */ void clearActions(); /** * Schedule the actions necessary to rename the directory containing a file. * * @param taggedFile file in directory */ void scheduleAction(TaggedFile* taggedFile); /** * Perform the scheduled rename actions. * * @param errorMsg if not 0 and an error occurred, a message is appended here, * otherwise it is not touched */ void performActions(QString* errorMsg); /** * Set directory name. * This should be done before calling performActions(), so that the directory * name is changed when the application directory is renamed. * * @param dirName directory name */ void setDirName(const QString& dirName) { m_dirName = dirName; } /** * Get directory name. * The directory name should be initialized with the value of * Kid3Application::getDirName() before performActions() is started and will * be updated if it is renamed while performing the actions. If it is * different from the application directory name after performActions(), the * new directory should be opened. * * @return directory. */ QString getDirName() const { return m_dirName; } signals: /** * Is emitted after an action has been scheduled. * * @param actionStrs description of action */ void actionScheduled(const QStringList& actionStrs); private: /** * An action performed while renaming a directory. */ class RenameAction { public: /** Action type. */ enum Type { CreateDirectory, RenameDirectory, RenameFile, ReportError, NumTypes }; /** * Constructor. * @param type type of action * @param src source file or directory name * @param dest destination file or directory name * @param index model index of item to rename */ RenameAction(Type type, const QString& src, const QString& dest, const QPersistentModelIndex& index) : m_type(type), m_src(src), m_dest(dest), m_index(index) {} /** * Constructor. */ RenameAction() : m_type(ReportError) {} /** * Destructor. */ ~RenameAction() {} /** * Test for equality. * @param rhs right hand side * @return true if equal. */ bool operator==(const RenameAction& rhs) const { return m_type == rhs.m_type && m_src == rhs.m_src && m_dest == rhs.m_dest; } Type m_type; /**< type of action */ QString m_src; /**< source file or directory name */ QString m_dest; /**< destination file or directory name */ QPersistentModelIndex m_index; /**< model index of item to rename */ }; /** List of rename actions. */ typedef QList RenameActionList; /** * Create a directory if it does not exist. * * @param dir directory path * @param errorMsg if not NULL and an error occurred, a message is appended here, * otherwise it is not touched * * @return true if directory exists or was created successfully. */ bool createDirectory(const QString& dir, QString* errorMsg) const; /** * Rename a directory. * * @param olddir old directory name * @param newdir new directory name * @param errorMsg if not NULL and an error occurred, a message is * appended here, otherwise it is not touched * @param index model index of item to rename * * @return true if rename successful. */ bool renameDirectory( const QString& olddir, const QString& newdir, const QPersistentModelIndex& index, QString *errorMsg) const; /** * Rename a file. * * @param oldfn old file name * @param newfn new file name * @param errorMsg if not NULL and an error occurred, a message is * appended here, otherwise it is not touched * @param index model index of item to rename * * @return true if rename successful or newfn already exists. */ bool renameFile(const QString& oldfn, const QString& newfn, const QPersistentModelIndex& index, QString* errorMsg) const; /** * Add a rename action. * * @param type type of action * @param src source file or directory name * @param dest destination file or directory name * @param index model index of item to rename */ void addAction(RenameAction::Type type, const QString& src, const QString& dest, const QPersistentModelIndex& index = QPersistentModelIndex()); /** * Add a rename action. * * @param type type of action * @param dest destination file or directory name */ void addAction(RenameAction::Type type, const QString& dest); /** * Check if there is already an action scheduled for this source. * * @return true if a rename action for the source exists. */ bool actionHasSource(const QString& src) const; /** * Check if there is already an action scheduled for this destination. * * @return true if a rename or create action for the destination exists. */ bool actionHasDestination(const QString& dest) const; /** * Replace directory name if there is already a rename action. * * @param src directory name, will be replaced if there is a rename action */ void replaceIfAlreadyRenamed(QString& src) const; /** * Get description of an actions to be performed. * @return (action, [src,] dst) list describing the action to be * performed. */ QStringList describeAction(const RenameAction& action) const; RenameActionList m_actions; bool m_aborted; TrackData::TagVersion m_tagVersion; bool m_actionCreate; QString m_format; QString m_dirName; }; #endif // DIRRENAMER_H kid3-3.0.2/src/core/model/downloadclient.cpp000066400000000000000000000073231224603511300207010ustar00rootroot00000000000000/** * \file downloadclient.cpp * Client to download via http. * * \b Project: Kid3 * \author Urs Fleisch * \date 12 Jun 2011 * * Copyright (C) 2011-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "downloadclient.h" #include "importconfig.h" /** * Constructor. * * @param netMgr network access manager */ DownloadClient::DownloadClient(QNetworkAccessManager* netMgr) : HttpClient(netMgr), m_canceled(false) { connect(this, SIGNAL(bytesReceived(QByteArray)), this, SLOT(requestFinished(QByteArray))); } /** * Destructor. */ DownloadClient::~DownloadClient() { } /** * Send a download request. * * @param hostName server * @param path path on server */ void DownloadClient::startDownload(const QString& hostName, const QString& path) { m_canceled = false; m_url = QLatin1String("http://"); m_url += hostName; m_url += path; emit downloadStarted(m_url); emit progress(tr("Ready."), 0, 0); sendRequest(hostName, path); } /** * Send a download request. * * @param url URL of resource to download */ void DownloadClient::startDownload(const QString& url) { int hostPos = url.indexOf(QLatin1String("://")); if (hostPos > 0) { int pathPos = url.indexOf(QLatin1Char('/'), hostPos + 3); if (pathPos > hostPos) { startDownload(url.mid(hostPos + 3, pathPos - hostPos - 3), url.mid(pathPos)); } } } /** * Cancel a download. */ void DownloadClient::cancelDownload() { m_canceled = true; abort(); emit aborted(); } /** * Handle response when request is finished. * downloadFinished() is emitted. * * @param data received data */ void DownloadClient::requestFinished(const QByteArray& data) { if (!m_canceled) { emit downloadFinished(data, getContentType(), m_url); } } /** * Get the URL of an image file. * The input URL is transformed using the match picture URL table to * get the URL of an image file. * * @param url URL from image drag * * @return URL of image file, empty if no image URL found. */ QString DownloadClient::getImageUrl(const QString& url) { QString imgurl; if (url.startsWith(QLatin1String("http://"))) { if (url.endsWith(QLatin1String(".jpg"), Qt::CaseInsensitive) || url.endsWith(QLatin1String(".jpeg"), Qt::CaseInsensitive) || url.endsWith(QLatin1String(".png"), Qt::CaseInsensitive)) { imgurl = url; } else { for (QMap::ConstIterator it = ImportConfig::instance().m_matchPictureUrlMap.begin(); it != ImportConfig::instance().m_matchPictureUrlMap.end(); ++it) { QRegExp re(it.key()); if (re.exactMatch(url)) { imgurl = url; imgurl.replace(re, *it); if (imgurl.indexOf(QLatin1String("%25")) != -1) { // double URL encoded: first decode imgurl = QUrl::fromPercentEncoding(imgurl.toUtf8()); } if (imgurl.indexOf(QLatin1String("%2F")) != -1) { // URL encoded: decode imgurl = QUrl::fromPercentEncoding(imgurl.toUtf8()); } break; } } } } return imgurl; } kid3-3.0.2/src/core/model/downloadclient.h000066400000000000000000000052201224603511300203400ustar00rootroot00000000000000/** * \file downloadclient.h * Client to download via http. * * \b Project: Kid3 * \author Urs Fleisch * \date 12 Jun 2011 * * Copyright (C) 2011 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef DOWNLOADCLIENT_H #define DOWNLOADCLIENT_H #include #include "httpclient.h" /** * Client to download via HTTP. * @see DownloadDialog */ class DownloadClient : public HttpClient { Q_OBJECT public: /** * Constructor. * * @param netMgr network access manager */ explicit DownloadClient(QNetworkAccessManager* netMgr); /** * Destructor. */ virtual ~DownloadClient(); /** * Send a download request. * * @param hostName server * @param path path on server */ void startDownload(const QString& hostName, const QString& path); /** * Send a download request. * * @param url URL of resource to download */ void startDownload(const QString& url); /** * Get the URL of an image file. * The input URL is transformed using the match picture URL table to * get the URL of an image file. * * @param url URL from image drag * * @return URL of image file, empty if no image URL found. */ static QString getImageUrl(const QString& url); public slots: /** * Cancel a download. */ void cancelDownload(); signals: /** * Emitted when download is started * @param url URL of download */ void downloadStarted(const QString& url); /** * Emitted when download finished. * @param data bytes containing download * @param contentType content type * @param url URL */ void downloadFinished(const QByteArray& data, const QString& contentType, const QString& url); /** * Emitted when a download is aborted. */ void aborted(); private slots: /** * Handle response when request is finished. * downloadFinished() is emitted. * * @param data received data */ void requestFinished(const QByteArray& data); private: QString m_url; bool m_canceled; }; #endif // DOWNLOADCLIENT_H kid3-3.0.2/src/core/model/expressionparser.cpp000066400000000000000000000171721224603511300213120ustar00rootroot00000000000000/** * \file expressionparser.h * Simple parser for expressions. * * \b Project: Kid3 * \author Urs Fleisch * \date 23 Jan 2008 * * Copyright (C) 2008-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * The RPN tokenizer is based on ExprEvaluator, * Copyright (C) 2004 the VideoLAN team, under the same license. */ #include "expressionparser.h" /** * Constructor. * * @param operators additional operators (besides not, and, or), * highest priority first */ ExpressionParser::ExpressionParser(QStringList operators) : m_operators(operators << QLatin1String("not") << QLatin1String("and") << QLatin1String("or")), m_error(false) { } /** * Destructor. */ ExpressionParser::~ExpressionParser() { } /** * Compare operator priority. * * @return true if op1 has less priority than op2. */ bool ExpressionParser::lessPriority(const QString& op1, const QString& op2) const { int index1 = m_operators.indexOf(op1); int index2 = m_operators.indexOf(op2); if (op1 == QLatin1String("(")) return true; if (index1 >= 0 && index2 >= 0) return index1 >= index2; return false; } /** * Tokenize an expression in reverse polish notation. * * @param expr with strings, operators, not, and, or, (, ). */ void ExpressionParser::tokenizeRpn(const QString& expr) { m_rpnStack.clear(); QStringList operatorStack; QString token; int begin = 0, end = 0, len = expr.length(); while (begin < len) { // skip spaces while (expr[begin] == QLatin1Char(' ')) { ++begin; } if (expr[begin] == QLatin1Char('(')) { // push '(' on operator stack and continue operatorStack.push_back(QLatin1String("(")); ++begin; } else if (expr[begin] == QLatin1Char(')')) { // after ')', pop operator stack until '(' is found while (!operatorStack.empty()) { QString lastOp = operatorStack.back(); operatorStack.pop_back(); if (lastOp == QLatin1String("(")) { break; } m_rpnStack.push_back(lastOp); } ++begin; } else { if (expr[begin] == QLatin1Char('"')) { // skip quoted string end = begin + 1; while (end < len && !(expr[end] == QLatin1Char('"') && (end <= 0 || expr[end - 1] != QLatin1Char('\\')))) { ++end; } token = expr.mid(begin + 1, end - begin - 1); token.replace(QLatin1String("\\\""), QLatin1String("\"")); begin = end + 1; } else { // skip spaces end = begin; while (end < len && expr[end] != QLatin1Char(' ') && expr[end] != QLatin1Char(')')) { ++end; } token = expr.mid(begin, end - begin); begin = end; } if (m_operators.contains(token)) { // pop the operator stack while the token has lower priority while (!operatorStack.empty() && lessPriority(token, operatorStack.back())) { QString lastOp = operatorStack.back(); operatorStack.pop_back(); m_rpnStack.push_back(lastOp); } operatorStack.push_back(token); } else { m_rpnStack.push_back(token); } } } // pop operator stack while (!operatorStack.empty()) { QString lastOp = operatorStack.back(); operatorStack.pop_back(); m_rpnStack.push_back(lastOp); } m_rpnIterator = m_rpnStack.begin(); } /** * Convert a string to a boolean. * * @param str string * @param b the boolean is returned here * * @return true if ok. */ static bool stringToBool(const QString& str, bool& b) { if (str == QLatin1String("1") || str == QLatin1String("true") || str == QLatin1String("on") || str == QLatin1String("yes")) { b = true; return true; } else if (str == QLatin1String("0") || str == QLatin1String("false") || str == QLatin1String("off") || str == QLatin1String("no")) { b = false; return true; } return false; } /** * Convert a boolean to a string. * * @param b boolean to convert * * @return "1" or "0". */ static QString boolToString(bool b) { return b ? QLatin1String("1") : QLatin1String("0"); } /** * Clear the variable stack before restarting an evaluation. */ void ExpressionParser::clearEvaluation() { m_rpnIterator = m_rpnStack.begin(); m_varStack.clear(); m_error = false; } /** * Pop a boolean from the variable stack. * Can be used to get the result after evaluate() returns false and * no error occurred. * * @param var the boolean is returned here * * @return true if ok. */ bool ExpressionParser::popBool(bool& var) { if (m_varStack.empty() || !stringToBool(m_varStack.back(), var)) { return false; } m_varStack.pop_back(); return true; } /** * Push a boolean to the variable stack. * Can be used to push the result of the operation returned by * evaluate() back onto the variable stack. * * @param var boolean to push */ void ExpressionParser::pushBool(bool var) { m_varStack.push_back(boolToString(var)); } /** * Pop two booleans from the variable stack. * * @param var1 first boolean * @param var2 second boolean * * @return true if ok. */ bool ExpressionParser::popTwoBools(bool& var1, bool& var2) { if (m_varStack.empty() || !stringToBool(m_varStack.back(), var1)) { return false; } m_varStack.pop_back(); if (m_varStack.empty() || !stringToBool(m_varStack.back(), var2)) { return false; } m_varStack.pop_back(); return true; } /** * Evaluate the RPN stack. * Boolean operations and, or, not are performed automatically. If another * operation has to be performed, the method stops and returns operator * and variables. The result can then be pushed onto the stack using * pushBool() and then the method can be called again. * * @param op the operator is returned here * @param var1 the first variable is returned here * @param var2 the second variable is returned here * * @return true if the RPN stack has more to evaluate, * if false, the evaluation is finished. */ bool ExpressionParser::evaluate(QString& op, QString& var1, QString& var2) { while (m_rpnIterator != m_rpnStack.end()) { QString token = *m_rpnIterator++; if (token == QLatin1String("and")) { bool var1, var2; if (!popTwoBools(var1, var2)) { m_error = true; break; } pushBool(var1 && var2); } else if (token == QLatin1String("or")) { bool var1, var2; if (!popTwoBools(var1, var2)) { m_error = true; break; } pushBool(var1 || var2); } else if (token == QLatin1String("not")) { bool var; if (!popBool(var)) { m_error = true; break; } pushBool(!var); } else if (m_operators.contains(token)) { if (m_varStack.empty()) { m_error = true; break; } var1 = m_varStack.back(); m_varStack.pop_back(); if (m_varStack.empty()) { m_error = true; break; } var2 = m_varStack.back(); m_varStack.pop_back(); op = token; return true; } else { m_varStack.push_back(token); } } return false; } kid3-3.0.2/src/core/model/expressionparser.h000066400000000000000000000065141224603511300207550ustar00rootroot00000000000000/** * \file expressionparser.h * Simple parser for expressions. * * \b Project: Kid3 * \author Urs Fleisch * \date 23 Jan 2008 * * Copyright (C) 2008 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef EXPRESSIONPARSER_H #define EXPRESSIONPARSER_H #include #include /** * Simple parser for expressions with boolean not, and, or and * other binary operations. */ class ExpressionParser { public: /** * Constructor. * * @param operators additional operators (besides not, and, or), * highest priority first */ ExpressionParser(QStringList operators); /** * Destructor. */ ~ExpressionParser(); /** * Tokenize an expression in reverse polish notation. * * @param expr with strings, operators, not, and, or, (, ). */ void tokenizeRpn(const QString& expr); /** * Clear the variable stack before restarting an evaluation. */ void clearEvaluation(); /** * Evaluate the RPN stack. * Boolean operations and, or, not are performed automatically. If another * operation has to be performed, the method stops and returns operator * and variables. The result can then be pushed onto the stack using * pushBool() and then the method can be called again. * * @param op the operator is returned here * @param var1 the first variable is returned here * @param var2 the second variable is returned here * * @return true if the RPN stack has more to evaluate, * if false, the evaluation is finished. */ bool evaluate(QString& op, QString& var1, QString& var2); /** * Push a boolean to the variable stack. * Can be used to push the result of the operation returned by * evaluate() back onto the variable stack. * * @param var boolean to push */ void pushBool(bool var); /** * Check if an error occurred. * @return true if an error occurred. */ bool hasError() const { return m_error; } /** * Pop a boolean from the variable stack. * Can be used to get the result after evaluate() returns false and * no error occurred. * * @param var the boolean is returned here * * @return true if ok. */ bool popBool(bool& var); private: /** * Compare operator priority. * * @return true if op1 has less priority than op2. */ bool lessPriority(const QString& op1, const QString& op2) const; /** * Pop two booleans from the variable stack. * * @param var1 first boolean * @param var2 second boolean * * @return true if ok. */ bool popTwoBools(bool& var1, bool& var2); QStringList m_rpnStack; QStringList m_varStack; const QStringList m_operators; QStringList::const_iterator m_rpnIterator; bool m_error; }; #endif kid3-3.0.2/src/core/model/externalprocess.cpp000066400000000000000000000131051224603511300211070ustar00rootroot00000000000000/** * \file externalprocess.cpp * Handler for external process. * * \b Project: Kid3 * \author Urs Fleisch * \date 22 Feb 2007 * * Copyright (C) 2007-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "externalprocess.h" #include #include #include #include #include #include #include #include "taggedfile.h" /** * Constructor. * * @param parent parent widget */ ExternalProcess::OutputViewer::OutputViewer(QWidget* parent) : QDialog(parent) { setObjectName(QLatin1String("OutputViewer")); setModal(false); QVBoxLayout* vlayout = new QVBoxLayout(this); m_textEdit = new QTextEdit(this); m_textEdit->setReadOnly(true); m_textEdit->setLineWrapMode(QTextEdit::NoWrap); m_textEdit->setStyleSheet(QLatin1String("font-family: \"Courier\";")); vlayout->addWidget(m_textEdit); QHBoxLayout* buttonLayout = new QHBoxLayout; QPushButton* clearButton = new QPushButton(tr("C&lear"), this); QSpacerItem* hspacer = new QSpacerItem(16, 0, QSizePolicy::Expanding, QSizePolicy::Minimum); QPushButton* closeButton = new QPushButton(tr("&Close"), this); buttonLayout->addWidget(clearButton); buttonLayout->addItem(hspacer); buttonLayout->addWidget(closeButton); connect(clearButton, SIGNAL(clicked()), m_textEdit, SLOT(clear())); connect(closeButton, SIGNAL(clicked()), this, SLOT(accept())); vlayout->addLayout(buttonLayout); resize(600, 424); } /** * Destructor. */ ExternalProcess::OutputViewer::~OutputViewer() {} /** * Append text. */ void ExternalProcess::OutputViewer::append(const QString& text) { if (text.isEmpty()) return; QString txt(text); txt.replace(QLatin1String("\r\n"), QLatin1String("\n")); int startPos = 0; int txtLen = txt.length(); while (startPos < txtLen) { QChar ch; int len; int crLfPos = txt.indexOf(QRegExp(QLatin1String("[\\r\\n]")), startPos); if (crLfPos >= startPos) { ch = txt.at(crLfPos); len = crLfPos - startPos; } else { ch = QChar(); len = -1; } QString line(txt.mid(startPos, len)); if (!m_textEdit->textCursor().atBlockEnd()) { QTextCursor cursor = m_textEdit->textCursor(); cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, line.length()); m_textEdit->setTextCursor(cursor); } m_textEdit->insertPlainText(line); if (ch == QLatin1Char('\r')) { m_textEdit->moveCursor(QTextCursor::StartOfLine); } else if (ch == QLatin1Char('\n')) { m_textEdit->moveCursor(QTextCursor::EndOfLine); m_textEdit->insertPlainText(ch); } if (len == -1) { break; } startPos = crLfPos + 1; } } /** * Scroll text to bottom. */ void ExternalProcess::OutputViewer::scrollToBottom() { m_textEdit->moveCursor(QTextCursor::End); } /** * Constructor. * * @param parent parent object */ ExternalProcess::ExternalProcess(QWidget* parent) : QObject(parent), m_parent(parent), m_process(0), m_outputViewer(0) { setObjectName(QLatin1String("ExternalProcess")); } /** * Destructor. */ ExternalProcess::~ExternalProcess() { delete m_outputViewer; } /** * Launch a command. * * @param name display name * @param args command and arguments * @param confirm true if confirmation required * @param showOutput true to show output of process */ void ExternalProcess::launchCommand(const QString& name, const QStringList& args, bool confirm, bool showOutput) { if (args.isEmpty()) return; if (confirm && QMessageBox::question( m_parent, name, tr("Execute ") + args.join(QLatin1String(" ")) + QLatin1Char('?'), QMessageBox::Ok, QMessageBox::Cancel) != QMessageBox::Ok) { return; } if (!m_process) { m_process = new QProcess(m_parent); } if (m_process->state() != QProcess::NotRunning) { m_process = new QProcess(m_parent); } if (showOutput) { if (!m_outputViewer) { m_outputViewer = new OutputViewer(0); } m_process->setProcessChannelMode(QProcess::MergedChannels); connect(m_process, SIGNAL(readyReadStandardOutput()), this, SLOT(readFromStdout())); m_outputViewer->setWindowTitle(name); m_outputViewer->show(); m_outputViewer->raise(); m_outputViewer->scrollToBottom(); } else { disconnect(m_process, SIGNAL(readyReadStandardOutput()), this, SLOT(readFromStdout())); } QStringList arguments = args; QString program = arguments.takeFirst(); m_process->start(program, arguments); if (!m_process->waitForStarted(10000)) { QMessageBox::warning( m_parent, name, tr("Could not execute ") + args.join(QLatin1String(" ")), QMessageBox::Ok, Qt::NoButton); } } /** * Read data from standard output and display it in the output viewer. */ void ExternalProcess::readFromStdout() { m_outputViewer->append(QString::fromLocal8Bit(m_process->readAllStandardOutput())); } kid3-3.0.2/src/core/model/externalprocess.h000066400000000000000000000045651224603511300205660ustar00rootroot00000000000000/** * \file externalprocess.h * Handler for external process. * * \b Project: Kid3 * \author Urs Fleisch * \date 22 Feb 2007 * * Copyright (C) 2007 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef EXTERNALPROCESS_H #define EXTERNALPROCESS_H #include #include #include "kid3api.h" class QProcess; class QString; class QStringList; class TaggedFile; /** * Handler for external process. */ class KID3_CORE_EXPORT ExternalProcess : public QObject { Q_OBJECT public: /** * Dialog to show output from external process. */ class OutputViewer : public QDialog { public: /** * Constructor. * * @param parent parent widget */ explicit OutputViewer(QWidget* parent); /** * Destructor. */ virtual ~OutputViewer(); /** * Append text. */ void append(const QString& text); /** * Scroll text to bottom. */ void scrollToBottom(); private: QTextEdit* m_textEdit; }; /** * Constructor. * * @param parent parent object */ explicit ExternalProcess(QWidget* parent = 0); /** * Destructor. */ virtual ~ExternalProcess(); /** * Launch a command. * * @param name display name * @param args command and arguments * @param confirm true if confirmation required * @param showOutput true to show output of process */ void launchCommand(const QString& name, const QStringList& args, bool confirm = false, bool showOutput = false); private slots: /** * Read data from standard output and display it in the output viewer. */ void readFromStdout(); private: QWidget* m_parent; QProcess* m_process; OutputViewer* m_outputViewer; }; #endif // EXTERNALPROCESS_H kid3-3.0.2/src/core/model/filefilter.cpp000066400000000000000000000140611224603511300200150ustar00rootroot00000000000000/** * \file filefilter.cpp * Filter for tagged files. * * \b Project: Kid3 * \author Urs Fleisch * \date 19 Jan 2008 * * Copyright (C) 2008-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "filefilter.h" #include "taggedfile.h" #include #include /** * Constructor. * @param parent parent object */ FileFilter::FileFilter(QObject* parent) : QObject(parent), m_parser(QStringList() << QLatin1String("equals") << QLatin1String("contains") << QLatin1String("matches")), m_aborted(false) { } /** * Destructor. */ FileFilter::~FileFilter() { } /** * Initialize the parser. * This method has to be called before the first call to parse() * and afterwards when the expression has been changed. */ void FileFilter::initParser() { m_parser.tokenizeRpn(m_filterExpression); } /** * Format a string from tag data. * * @param format format specification * * @return formatted string. */ QString FileFilter::formatString(const QString& format) { if (format.indexOf(QLatin1Char('%')) == -1) { return format; } QString str(format); str.replace(QLatin1String("%1"), QLatin1String("\v1")); str.replace(QLatin1String("%2"), QLatin1String("\v2")); str = m_trackData12.formatString(str); if (str.indexOf(QLatin1Char('\v')) != -1) { str.replace(QLatin1String("\v2"), QLatin1String("%")); str = m_trackData2.formatString(str); if (str.indexOf(QLatin1Char('\v')) != -1) { str.replace(QLatin1String("\v1"), QLatin1String("%")); str = m_trackData1.formatString(str); } } return str; } /** * Get help text for format codes supported by formatString(). * * @param onlyRows if true only the tr elements are returned, * not the surrounding table * * @return help text. */ QString FileFilter::getFormatToolTip(bool onlyRows) { QString str; if (!onlyRows) str += QLatin1String("\n"); str += TrackDataFormatReplacer::getToolTip(true); str += QLatin1String("\n"); str += QLatin1String("\n"); str += QLatin1String("\n"); str += QLatin1String("\n"); str += QLatin1String("\n"); str += QLatin1String("\n"); str += QLatin1String("\n"); str += QLatin1String("\n"); if (!onlyRows) str += QLatin1String("
%1a...%1{artist}..."); str += QCoreApplication::translate("@default", "Tag 1"); str += QLatin1Char(' '); str += QCoreApplication::translate("@default", "Artist"); str += QLatin1String("
%2a...%2{artist}..."); str += QCoreApplication::translate("@default", "Tag 2"); str += QLatin1Char(' '); str += QCoreApplication::translate("@default", "Artist"); str += QLatin1String("
equals"); str += QCoreApplication::translate("@default", QT_TRANSLATE_NOOP("@default", "True if strings are equal")); str += QLatin1String("
contains"); str += QCoreApplication::translate("@default", QT_TRANSLATE_NOOP("@default", "True if string contains substring")); str += QLatin1String("
matches"); str += QCoreApplication::translate("@default", QT_TRANSLATE_NOOP("@default", "True if string matches regexp")); str += QLatin1String("
and"); str += QCoreApplication::translate("@default", QT_TRANSLATE_NOOP("@default", "Logical AND")); str += QLatin1String("
or"); str += QCoreApplication::translate("@default", QT_TRANSLATE_NOOP("@default", "Logical OR")); str += QLatin1String("
not"); str += QCoreApplication::translate("@default", QT_TRANSLATE_NOOP("@default", "Logical negation")); str += QLatin1String("
\n"); return str; } /** * Evaluate the expression to a boolean result. * @see initParser() * @return result of expression. */ bool FileFilter::parse() { QString op, var1, var2; bool result = false; m_parser.clearEvaluation(); while (m_parser.evaluate(op, var1, var2)) { var1 = formatString(var1); var2 = formatString(var2); if (op == QLatin1String("equals")) { m_parser.pushBool(var1 == var2); } else if (op == QLatin1String("contains")) { m_parser.pushBool(var2.indexOf(var1) >= 0); } else if (op == QLatin1String("matches")) { m_parser.pushBool(QRegExp(var1).exactMatch(var2)); } } if (!m_parser.hasError()) { m_parser.popBool(result); } return result; } /** * Check if file passes through filter. * * @param taggedFile file to check * @param ok if not 0, false is returned here when parsing fails * * @return true if file passes through filter. */ bool FileFilter::filter(TaggedFile& taggedFile, bool* ok) { if (m_filterExpression.isEmpty()) { if (ok) *ok = true; return true; } m_trackData1 = ImportTrackData(taggedFile, ImportTrackData::TagV1); m_trackData2 = ImportTrackData(taggedFile, ImportTrackData::TagV2); m_trackData12 = ImportTrackData(taggedFile, ImportTrackData::TagV2V1); bool result = parse(); if (m_parser.hasError()) { if (ok) *ok = false; return false; } else { if (ok) *ok = true; return result; } } /** * Clear abort flag. */ void FileFilter::clearAborted() { m_aborted = false; } /** * Check if dialog was aborted. * @return true if aborted. */ bool FileFilter::isAborted() const { return m_aborted; } /** * Set abort flag. */ void FileFilter::abort() { m_aborted = true; } kid3-3.0.2/src/core/model/filefilter.h000066400000000000000000000064031224603511300174630ustar00rootroot00000000000000/** * \file filefilter.h * Filter for tagged files. * * \b Project: Kid3 * \author Urs Fleisch * \date 19 Jan 2008 * * Copyright (C) 2008 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef FILEFILTER_H #define FILEFILTER_H #include "expressionparser.h" #include "trackdata.h" #include "iabortable.h" #include #include class TaggedFile; /** * Filter for tagged files. */ class KID3_CORE_EXPORT FileFilter : public QObject, public IAbortable { Q_OBJECT public: /** Type of filter event. */ enum FilterEventType { Started, Directory, ParseError, FilePassed, FileFilteredOut, Finished, Aborted }; /** * Constructor. * @param parent parent object */ explicit FileFilter(QObject* parent = 0); /** * Destructor. */ virtual ~FileFilter(); /** * Set filter expression. * @param filterExpression filter expression */ void setFilterExpression(const QString& filterExpression) { m_filterExpression = filterExpression; } /** * Check if filter expression is empty. * @return true if filter expression is empty. */ bool isEmptyFilterExpression() const { return m_filterExpression.isEmpty(); } /** * Initialize the parser. * This method has to be called before the first call to parse() * and afterwards when the expression has been changed. */ void initParser(); /** * Check if file passes through filter. * * @param taggedFile file to check * @param ok if not 0, false is returned here when parsing fails * * @return true if file passes through filter. */ bool filter(TaggedFile& taggedFile, bool* ok = 0); /** * Clear abort flag. */ virtual void clearAborted(); /** * Check if dialog was aborted. * @return true if aborted. */ virtual bool isAborted() const; /** * Get help text for format codes supported by formatString(). * * @param onlyRows if true only the tr elements are returned, * not the surrounding table * * @return help text. */ static QString getFormatToolTip(bool onlyRows = false); public slots: /** * Set abort flag. */ virtual void abort(); private: /** * Format a string from tag data. * * @param format format specification * * @return formatted string. */ QString formatString(const QString& format); /** * Evaluate the expression to a boolean result. * @see initParser() * @return result of expression. */ bool parse(); QString m_filterExpression; ExpressionParser m_parser; ImportTrackData m_trackData1; ImportTrackData m_trackData2; ImportTrackData m_trackData12; bool m_aborted; }; #endif kid3-3.0.2/src/core/model/fileproxymodel.cpp000066400000000000000000000375311224603511300207410ustar00rootroot00000000000000/** * \file fileproxymodel.cpp * Proxy for filesystem model which filters files. * * \b Project: Kid3 * \author Urs Fleisch * \date 22-Mar-2011 * * Copyright (C) 2011-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "fileproxymodel.h" #include #include "taggedfileiconprovider.h" #include "itaggedfilefactory.h" #include "config.h" QList FileProxyModel::s_taggedFileFactories; /** * Constructor. * * @param parent parent object */ FileProxyModel::FileProxyModel(QObject* parent) : QSortFilterProxyModel(parent), m_iconProvider(new TaggedFileIconProvider), m_fsModel(0) { setObjectName(QLatin1String("FileProxyModel")); connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(updateInsertedRows(QModelIndex,int,int))); } /** * Get file information of model index. * @return file information */ QFileInfo FileProxyModel::fileInfo(const QModelIndex& index) const { if (m_fsModel) { QModelIndex sourceIndex(mapToSource(index)); return m_fsModel->fileInfo(sourceIndex); } return QFileInfo(); } /** * Get file path of model index. * @return path to file or directory */ QString FileProxyModel::filePath(const QModelIndex& index) const { if (m_fsModel) { QModelIndex sourceIndex(mapToSource(index)); return m_fsModel->filePath(sourceIndex); } return QString(); } /** * Check if model index represents directory. * @return true if directory */ bool FileProxyModel::isDir(const QModelIndex& index) const { if (m_fsModel) { QModelIndex sourceIndex(mapToSource(index)); return m_fsModel->isDir(sourceIndex); } return false; } /** * Delete file of index. * @return true if ok */ bool FileProxyModel::remove(const QModelIndex& index) const { if (m_fsModel) { QModelIndex sourceIndex(mapToSource(index)); return m_fsModel->remove(sourceIndex); } return false; } /** * Delete directory of index. * @return true if ok */ bool FileProxyModel::rmdir(const QModelIndex& index) const { if (m_fsModel) { QModelIndex sourceIndex(mapToSource(index)); return m_fsModel->rmdir(sourceIndex); } return false; } /** * Get index for given path and column. * @param path path to file or directory * @param column model column * @return model index, invalid if not found. */ QModelIndex FileProxyModel::index(const QString& path, int column) const { if (m_fsModel) { QModelIndex sourceIndex = m_fsModel->index(path, column); if (sourceIndex.isValid()) { return mapFromSource(sourceIndex); } } return QModelIndex(); } /** * Update the TaggedFile contents for rows inserted into the model. * @param parent parent model index * @param start starting row * @param end ending row */ void FileProxyModel::updateInsertedRows(const QModelIndex& parent, int start, int end) { const QAbstractItemModel* model = parent.model(); if (!model) return; for (int row = start; row <= end; ++row) { QModelIndex index(model->index(row, 0, parent)); initTaggedFileData(index); } } /** * Destructor. */ FileProxyModel::~FileProxyModel() { clearTaggedFileStore(); delete m_iconProvider; } /** * Get number of columns. * @param parent parent model index, invalid for table models * @return number of columns, * if parent is valid number of children (0 for table models) */ int FileProxyModel::columnCount(const QModelIndex&) const { return 1; } /** * Get data for header section. * @param section column or row * @param orientation horizontal or vertical * @param role item data role * @return header data for role */ QVariant FileProxyModel::headerData(int, Qt::Orientation, int) const { return QVariant(); } /** * Check if row should be included in model. * * @param srcRow source row * @param srcParent source parent * * @return true to include row. */ bool FileProxyModel::filterAcceptsRow( int srcRow, const QModelIndex& srcParent) const { QAbstractItemModel* srcModel = sourceModel(); if (srcModel) { QModelIndex srcIndex(srcModel->index(srcRow, 0, srcParent)); if (!m_filteredOut.isEmpty()) { if (m_filteredOut.contains(srcIndex)) return false; } QString item(srcIndex.data().toString()); if (item == QLatin1String(".") || item == QLatin1String("..")) return false; if (m_extensions.isEmpty() || !m_fsModel || m_fsModel->isDir(srcIndex)) return true; for (QStringList::const_iterator it = m_extensions.begin(); it != m_extensions.end(); ++it) { if (item.endsWith(*it, Qt::CaseInsensitive)) return true; } } return false; } /** * Get data for a given role. * @param index model index * @param role item data role * @return data for role */ QVariant FileProxyModel::data(const QModelIndex& index, int role) const { if (index.isValid()) { if (role == TaggedFileRole) { return retrieveTaggedFileVariant(index); } else if (role == Qt::DecorationRole && index.column() == 0) { TaggedFile* taggedFile = m_taggedFiles.value(index, 0); if (taggedFile) { return m_iconProvider->iconForTaggedFile(taggedFile); } } else if (role == Qt::BackgroundRole && index.column() == 0) { TaggedFile* taggedFile = m_taggedFiles.value(index, 0); if (taggedFile) { QColor color = m_iconProvider->backgroundForTaggedFile(taggedFile); if (color.isValid()) return color; } } } return QSortFilterProxyModel::data(index, role); } /** * Set data for a given role. * @param index model index * @param value data value * @param role item data role * @return true if successful */ bool FileProxyModel::setData(const QModelIndex& index, const QVariant& value, int role) { if (index.isValid() && role == TaggedFileRole) { return storeTaggedFileVariant(index, value); } return QSortFilterProxyModel::setData(index, value, role); } /** * Set source model. * @param sourceModel source model, must be QFileSystemModel */ void FileProxyModel::setSourceModel(QAbstractItemModel* sourceModel) { m_fsModel = qobject_cast(sourceModel); Q_ASSERT_X(m_fsModel != 0 , "setSourceModel", "sourceModel is not QFileSystemModel"); #if QT_VERSION >= 0x040700 disconnect(this, SIGNAL(directoryLoaded(QString))); connect(m_fsModel, SIGNAL(directoryLoaded(QString)), this, SIGNAL(directoryLoaded(QString))); #endif QSortFilterProxyModel::setSourceModel(sourceModel); } /** * Sets the name filters to apply against the existing files. * @param filters list of strings containing wildcards like "*.mp3" */ void FileProxyModel::setNameFilters(const QStringList& filters) { QRegExp wildcardRe(QLatin1String("\\.\\w+")); QSet exts; foreach (const QString& filter, filters) { int pos = 0; while ((pos = wildcardRe.indexIn(filter, pos)) != -1) { int len = wildcardRe.matchedLength(); exts.insert(filter.mid(pos, len).toLower()); pos += len; } } QStringList oldExtensions(m_extensions); m_extensions = exts.toList(); if (m_extensions != oldExtensions) { invalidateFilter(); } } /** * Filter out a model index. * @param index model index which has to be filtered out */ void FileProxyModel::filterOutIndex(const QPersistentModelIndex& index) { m_filteredOut.insert(mapToSource(index)); } /** * Stop filtering out indexes. */ void FileProxyModel::disableFilteringOutIndexes() { m_filteredOut.clear(); invalidateFilter(); } /** * Check if index filter is active. * @return true if indexes are filtered out */ bool FileProxyModel::isFilteringOutIndexes() const { return !m_filteredOut.isEmpty(); } /** * Make filter changes active after adding indexes to be filtered out. */ void FileProxyModel::applyFilteringOutIndexes() { invalidateFilter(); } /** * Emit dataChanged() to the model to force an update of the connected views, * e.g. when the modification state changes. * @param topLeft top left item changed * @param bottomRight bottom right item changed */ void FileProxyModel::emitDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight) { emit dataChanged(topLeft, bottomRight); } /** * Retrieve tagged file for an index. * @param index model index * @return QVariant with tagged file, invalid QVariant if not found. */ QVariant FileProxyModel::retrieveTaggedFileVariant( const QPersistentModelIndex& index) const { if (m_taggedFiles.contains(index)) return QVariant::fromValue(m_taggedFiles.value(index)); return QVariant(); } /** * Store tagged file from variant with index. * @param index model index * @param value QVariant containing tagged file * @return true if index and value valid */ bool FileProxyModel::storeTaggedFileVariant(const QPersistentModelIndex& index, QVariant value) { if (index.isValid()) { if (value.isValid()) { if (value.canConvert()) { TaggedFile* oldItem = m_taggedFiles.value(index, 0); delete oldItem; m_taggedFiles.insert(index, value.value()); return true; } } else { if (TaggedFile* oldFile = m_taggedFiles.value(index, 0)) { m_taggedFiles.remove(index); delete oldFile; } } } return false; } /** * Clear store with tagged files. */ void FileProxyModel::clearTaggedFileStore() { qDeleteAll(m_taggedFiles); m_taggedFiles.clear(); } /** * Initialize tagged file for model index. * @param index model index */ void FileProxyModel::initTaggedFileData(const QModelIndex& index) { QVariant dat = data(index, TaggedFileRole); if (dat.isValid() || isDir(index)) return; QFileInfo info = fileInfo(index); dat.setValue(createTaggedFile(info.path(), info.fileName(), index)); setData(index, dat, TaggedFileRole); } /** * Get tagged file data of model index. * * @param index model index * @param taggedFile a TaggedFile pointer is returned here * * @return true if index has a tagged file, *taggedFile is set to the pointer. */ bool FileProxyModel::getTaggedFileOfIndex(const QModelIndex& index, TaggedFile** taggedFile) { if (!(index.isValid() && index.model() != 0)) return false; QVariant data(index.model()->data(index, FileProxyModel::TaggedFileRole)); if (!data.canConvert()) return false; *taggedFile = data.value(); return true; } /** * Get tagged file of model index. * * @param index model index * * @return tagged file, 0 is returned if the index does not contain a * TaggedFile or if has a TaggedFile which is null. */ TaggedFile* FileProxyModel::getTaggedFileOfIndex(const QModelIndex& index) { if (!(index.isValid() && index.model() != 0)) return 0; QVariant data(index.model()->data(index, FileProxyModel::TaggedFileRole)); if (!data.canConvert()) return 0; return data.value(); } /** * Get directory path if model index is of directory. * * @param index model index * * @return directory path, null if not directory */ QString FileProxyModel::getPathIfIndexOfDir(const QModelIndex& index) { const FileProxyModel* model = qobject_cast(index.model()); if (!model || !model->isDir(index)) return QString(); return model->filePath(index); } /** * Read tagged file with ID3v2.4.0. * * @param taggedFile tagged file * * @return tagged file (can be newly created tagged file). */ TaggedFile* FileProxyModel::readWithId3V24(TaggedFile* taggedFile) { const QPersistentModelIndex& index = taggedFile->getIndex(); if (TaggedFile* tagLibFile = createTaggedFile(TaggedFile::TF_ID3v24, taggedFile->getDirname(), taggedFile->getFilename(), index)) { if (index.isValid()) { QVariant data; data.setValue(tagLibFile); // setData() will not invalidate the model, so this should be safe. QAbstractItemModel* setDataModel = const_cast( index.model()); if (setDataModel) { setDataModel->setData(index, data, FileProxyModel::TaggedFileRole); } } taggedFile = tagLibFile; taggedFile->readTags(false); } return taggedFile; } /** * Create a tagged file with a given feature. * * @param feature tagged file feature * @param dirName directory name * @param fileName filename * @param idx model index * * @return tagged file, 0 if feature not found or type not supported. */ TaggedFile* FileProxyModel::createTaggedFile( TaggedFile::Feature feature, const QString& dirName, const QString& fileName, const QPersistentModelIndex& idx) { TaggedFile* taggedFile = 0; foreach (ITaggedFileFactory* factory, s_taggedFileFactories) { foreach (const QString& key, factory->taggedFileKeys()) { if ((factory->taggedFileFeatures(key) & feature) != 0 && (taggedFile = factory->createTaggedFile(key, dirName, fileName, idx, feature)) != 0) { return taggedFile; } } } return 0; } /** * Create a tagged file. * * @param dirName directory name * @param fileName filename * @param idx model index * * @return tagged file, 0 if not found or type not supported. */ TaggedFile* FileProxyModel::createTaggedFile( const QString& dirName, const QString& fileName, const QPersistentModelIndex& idx) { TaggedFile* taggedFile = 0; foreach (ITaggedFileFactory* factory, s_taggedFileFactories) { foreach (const QString& key, factory->taggedFileKeys()) { taggedFile = factory->createTaggedFile(key, dirName, fileName, idx); if (taggedFile) { return taggedFile; } } } return 0; } /** * Read tagged file with ID3v2.3.0. * * @param taggedFile tagged file * * @return tagged file (can be newly created tagged file). */ TaggedFile* FileProxyModel::readWithId3V23(TaggedFile* taggedFile) { const QPersistentModelIndex& index = taggedFile->getIndex(); if (TaggedFile* id3libFile = createTaggedFile(TaggedFile::TF_ID3v23, taggedFile->getDirname(), taggedFile->getFilename(), index)) { if (index.isValid()) { QVariant data; data.setValue(id3libFile); // setData() will not invalidate the model, so this should be safe. QAbstractItemModel* setDataModel = const_cast( index.model()); if (setDataModel) { setDataModel->setData(index, data, FileProxyModel::TaggedFileRole); } } taggedFile = id3libFile; taggedFile->readTags(false); } return taggedFile; } /** * Read file with ID3v2.4 if it has an ID3v2.4 or ID3v2.2 tag. * ID3v2.2 files are also read with ID3v2.4 because id3lib corrupts * images in ID3v2.2 tags. * * @param taggedFile tagged file * * @return tagged file (can be new TagLibFile). */ TaggedFile* FileProxyModel::readWithId3V24IfId3V24(TaggedFile* taggedFile) { if (taggedFile && (taggedFile->taggedFileFeatures() & (TaggedFile::TF_ID3v23 | TaggedFile::TF_ID3v24)) == TaggedFile::TF_ID3v23 && !taggedFile->isChanged() && taggedFile->isTagInformationRead() && taggedFile->hasTagV2()) { QString id3v2Version = taggedFile->getTagFormatV2(); if (id3v2Version.isNull() || id3v2Version == QLatin1String("ID3v2.2.0")) { taggedFile = readWithId3V24(taggedFile); } } return taggedFile; } kid3-3.0.2/src/core/model/fileproxymodel.h000066400000000000000000000222101224603511300203720ustar00rootroot00000000000000/** * \file fileproxymodel.h * Proxy for filesystem model which filters files. * * \b Project: Kid3 * \author Urs Fleisch * \date 22-Mar-2011 * * Copyright (C) 2011 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef FILEPROXYMODEL_H #define FILEPROXYMODEL_H #include #include #include #include #include #include "taggedfile.h" #include "kid3api.h" class QFileSystemModel; class TaggedFileIconProvider; class ITaggedFileFactory; /** * Proxy for filesystem model which filters files. */ class KID3_CORE_EXPORT FileProxyModel : public QSortFilterProxyModel { Q_OBJECT public: /** Custom role, extending QFileSystemModel::Roles. */ enum Roles { TaggedFileRole = Qt::UserRole + 4 }; /** * Constructor. * * @param parent parent object */ explicit FileProxyModel(QObject* parent = 0); /** * Destructor. */ virtual ~FileProxyModel(); /** * Get number of columns. * @param parent parent model index, invalid for table models * @return number of columns, * if parent is valid number of children (0 for table models) */ virtual int columnCount(const QModelIndex& parent=QModelIndex()) const; /** * Get data for header section. * @param section column or row * @param orientation horizontal or vertical * @param role item data role * @return header data for role */ virtual QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const; /** * Get data for a given role. * @param index model index * @param role item data role * @return data for role */ virtual QVariant data(const QModelIndex& index, int role=Qt::DisplayRole) const; /** * Set data for a given role. * @param index model index * @param value data value * @param role item data role * @return true if successful */ virtual bool setData(const QModelIndex& index, const QVariant& value, int role=Qt::EditRole); /** * Set source model. * @param sourceModel source model, must be QFileSystemModel */ virtual void setSourceModel(QAbstractItemModel* sourceModel); /** * Sets the name filters to apply against the existing files. * @param filters list of strings containing wildcards like "*.mp3" */ void setNameFilters(const QStringList& filters); /** * Filter out a model index. * @param index model index which has to be filtered out */ void filterOutIndex(const QPersistentModelIndex& index); /** * Stop filtering out indexes. */ void disableFilteringOutIndexes(); /** * Check if index filter is active. * @return true if indexes are filtered out */ bool isFilteringOutIndexes() const; /** * Make filter changes active after adding indexes to be filtered out. */ void applyFilteringOutIndexes(); /** * Emit dataChanged() to the model to force an update of the connected views, * e.g. when the modification state changes. * @param topLeft top left item changed * @param bottomRight bottom right item changed */ void emitDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight); /** * Get file information of model index. * @return file information */ QFileInfo fileInfo(const QModelIndex& index) const; /** * Get file path of model index. * @return path to file or directory */ QString filePath(const QModelIndex& index) const; /** * Check if model index represents directory. * @return true if directory */ bool isDir(const QModelIndex& index) const; /** * Delete file of index. * @return true if ok */ bool remove(const QModelIndex& index) const; /** * Delete directory of index. * @return true if ok */ bool rmdir(const QModelIndex& index) const; /** * Get index for given path and column. * @param path path to file or directory * @param column model column * @return model index, invalid if not found. */ QModelIndex index(const QString& path, int column = 0) const; using QSortFilterProxyModel::index; /** * Access to tagged file factories. * @return reference to tagged file factories. */ static QList& taggedFileFactories() { return s_taggedFileFactories; } /** * Create a tagged file with a given feature. * * @param feature tagged file feature * @param dirName directory name * @param fileName filename * @param idx model index * * @return tagged file, 0 if feature not found or type not supported. */ static TaggedFile* createTaggedFile( TaggedFile::Feature feature, const QString& dirName, const QString& fileName, const QPersistentModelIndex& idx); /** * Create a tagged file. * * @param dirName directory name * @param fileName filename * @param idx model index * * @return tagged file, 0 if not found or type not supported. */ static TaggedFile* createTaggedFile( const QString& dirName, const QString& fileName, const QPersistentModelIndex& idx); /** * Get tagged file data of model index. * * @param index model index * @param taggedFile a TaggedFile pointer is returned here * * @return true if index has a tagged file, *taggedFile is set to the pointer. */ static bool getTaggedFileOfIndex(const QModelIndex& index, TaggedFile** taggedFile); /** * Get tagged file of model index. * * @param index model index * * @return tagged file, 0 is returned if the index does not contain a * TaggedFile or if has a TaggedFile which is null. */ static TaggedFile* getTaggedFileOfIndex(const QModelIndex& index); /** * Get directory path if model index is of directory. * * @param index model index * * @return directory path, null if not directory */ static QString getPathIfIndexOfDir(const QModelIndex& index); /** * Read tagged file with ID3v2.4.0. * * @param taggedFile tagged file * * @return tagged file (can be newly created tagged file). */ static TaggedFile* readWithId3V24(TaggedFile* taggedFile); /** * Read tagged file with ID3v2.3.0. * * @param taggedFile tagged file * * @return tagged file (can be newly created tagged file). */ static TaggedFile* readWithId3V23(TaggedFile* taggedFile); /** * Read file with ID3v2.4 if it has an ID3v2.4 or ID3v2.2 tag. * ID3v2.2 files are also read with ID3v2.4 because id3lib corrupts * images in ID3v2.2 tags. * * @param taggedFile tagged file * * @return tagged file (can be new TagLibFile). */ static TaggedFile* readWithId3V24IfId3V24(TaggedFile* taggedFile); signals: #if QT_VERSION >= 0x040700 /** * This signal is emitted when the gatherer thread has finished to load the * @a path. * * @param path directory fetched due to fetchMore() call. */ void directoryLoaded(const QString& path); #endif private slots: /** * Update the TaggedFile contents for rows inserted into the model. * @param parent parent model index * @param start starting row * @param end ending row */ void updateInsertedRows(const QModelIndex& parent, int start, int end); protected: /** * Check if row should be included in model. * * @param srcRow source row * @param srcParent source parent * * @return true to include row. */ virtual bool filterAcceptsRow(int srcRow, const QModelIndex& srcParent) const; private: /** * Retrieve tagged file for an index. * @param index model index * @return QVariant with tagged file, invalid QVariant if not found. */ QVariant retrieveTaggedFileVariant(const QPersistentModelIndex& index) const; /** * Store tagged file from variant with index. * @param index model index * @param value QVariant containing tagged file * @return true if index and value valid */ bool storeTaggedFileVariant(const QPersistentModelIndex& index, QVariant value); /** * Clear store with tagged files. */ void clearTaggedFileStore(); /** * Initialize tagged file for model index. * @param index model index */ void initTaggedFileData(const QModelIndex& index); QHash m_taggedFiles; QSet m_filteredOut; TaggedFileIconProvider* m_iconProvider; QFileSystemModel* m_fsModel; QStringList m_extensions; static QList s_taggedFileFactories; }; Q_DECLARE_METATYPE(TaggedFile*) #endif // FILEPROXYMODEL_H kid3-3.0.2/src/core/model/fileproxymodeliterator.cpp000066400000000000000000000121161224603511300225030ustar00rootroot00000000000000/** * \file fileproxymodeliterator.cpp * Iterator for FileProxyModel. * * \b Project: Kid3 * \author Urs Fleisch * \date 03-Feb-2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "fileproxymodeliterator.h" #include #include "fileproxymodel.h" /** * GreaterThan functor to sort modex indexes by string data. */ class PersistentModelIndexGreaterThan { public: /** * Greater than call operator. * @param lhs left hand side * @param rhs right hand side * @return true if string data of @a lhs is greater than string data of * @a rhs. */ inline bool operator()(const QPersistentModelIndex& lhs, const QPersistentModelIndex& rhs) const { return lhs.data().toString().compare(rhs.data().toString()) > 0; } }; /** * Constructor. * * @param model file proxy model */ FileProxyModelIterator::FileProxyModelIterator(FileProxyModel* model) : QObject(model), m_model(model), m_aborted(false) { #if QT_VERSION >= 0x040700 m_timeoutTimer = 0; #else m_timeoutTimer = new QTimer(this); m_timeoutTimer->setSingleShot(true); m_timeoutTimer->setInterval(1000); #endif } /** * Destructor. */ FileProxyModelIterator::~FileProxyModelIterator() { } /** * Abort operation. */ void FileProxyModelIterator::abort() { m_aborted = true; } /** * Check if operation is aborted. * * @return true if aborted. */ bool FileProxyModelIterator::isAborted() const { return m_aborted; } /** * Clear state which is reported by isAborted(). */ void FileProxyModelIterator::clearAborted() { m_aborted = false; } /** * Start iteration. * * @param rootIdx index of root element */ void FileProxyModelIterator::start(const QPersistentModelIndex& rootIdx) { m_nodes.clear(); m_rootIndexes.clear(); m_rootIndexes.append(rootIdx); m_aborted = false; fetchNext(); } /** * Start iteration. * * @param indexes indexes of root directories */ void FileProxyModelIterator::start(const QList& indexes) { m_nodes.clear(); m_rootIndexes = indexes; m_aborted = false; fetchNext(); } /** * Fetch next index. */ void FileProxyModelIterator::fetchNext() { int count = 0; while (!m_aborted) { if (m_nodes.isEmpty()) { if (m_rootIndexes.isEmpty()) { break; } else { m_nodes.push(m_rootIndexes.takeFirst()); } } m_nextIdx = m_nodes.top(); if (m_nextIdx.isValid()) { if (m_model->isDir(m_nextIdx) && m_model->canFetchMore(m_nextIdx)) { #if QT_VERSION >= 0x040700 connect(m_model, SIGNAL(directoryLoaded(QString)), this, SLOT(onDirectoryLoaded(QString))); #else // Qt < 4.7 does not have a directoryLoaded() signal, so // rowsInserted() and a slow timeout for empty directories // are used. connect(m_model, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(onRowsInserted())); connect(m_timeoutTimer, SIGNAL(timeout()), this, SLOT(onRowsInserted())); m_timeoutTimer->start(); #endif m_model->fetchMore(m_nextIdx); return; } if (++count >= 10) { // Avoid spinning too long to keep the GUI responsive. QTimer::singleShot(0, this, SLOT(fetchNext())); return; } m_nodes.pop(); QStack childNodes; for (int row = m_model->rowCount(m_nextIdx) - 1; row >= 0; --row) { childNodes.push(m_model->index(row, 0, m_nextIdx)); } qStableSort(childNodes.begin(), childNodes.end(), PersistentModelIndexGreaterThan()); m_nodes += childNodes; emit nextReady(m_nextIdx); } } m_nodes.clear(); m_rootIndexes.clear(); m_nextIdx = QPersistentModelIndex(); emit nextReady(m_nextIdx); } #if QT_VERSION >= 0x040700 /** * Called when the gatherer thread has finished to load the @a path. * * @param path directory fetched due to fetchMore() call. */ void FileProxyModelIterator::onDirectoryLoaded(const QString& path) { Q_UNUSED(path) disconnect(m_model, SIGNAL(directoryLoaded(QString)), this, SLOT(onDirectoryLoaded(QString))); fetchNext(); } #else /** * Check if the directory has been loaded. */ void FileProxyModelIterator::onRowsInserted() { disconnect(m_model, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(onRowsInserted())); disconnect(m_timeoutTimer, SIGNAL(timeout()), this, SLOT(onRowsInserted())); m_timeoutTimer->stop(); fetchNext(); } #endif kid3-3.0.2/src/core/model/fileproxymodeliterator.h000066400000000000000000000064661224603511300221630ustar00rootroot00000000000000/** * \file fileproxymodeliterator.h * Iterator for FileProxyModel. * * \b Project: Kid3 * \author Urs Fleisch * \date 03-Feb-2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef FILEPROXYMODELITERATOR_H #define FILEPROXYMODELITERATOR_H #include #include #include #include "iabortable.h" #include "kid3api.h" class QTimer; class FileProxyModel; /** * Iterator for FileProxyModel. * This iterator behaves differently than other iterators, e.g. ModelIterator. * The file system model is not completely loaded, subdirectories can be * fetched later using fetchMore(). This class fetches directories * continuously and waits for them to be fetched. Therefore the routine * doing the actual work has to be connected with a slot and will be called * when file nodes are available. The iteration will also be suspended after * some files so that other slots can be processed and the GUI remains * responsive. If the iteration shall stop before all files are processed, * abort() shall be called. */ class KID3_CORE_EXPORT FileProxyModelIterator : public QObject, public IAbortable { Q_OBJECT public: /** * Constructor. * * @param model file proxy model */ explicit FileProxyModelIterator(FileProxyModel* model); /** * Destructor. */ virtual ~FileProxyModelIterator(); /** * Abort operation. */ virtual void abort(); /** * Check if operation is aborted. * * @return true if aborted. */ virtual bool isAborted() const; /** * Clear state which is reported by isAborted(). */ virtual void clearAborted(); /** * Start iteration. * * @param rootIdx index of root element */ void start(const QPersistentModelIndex& rootIdx); /** * Start iteration. * * @param indexes indexes of root directories */ void start(const QList& indexes); signals: /** * Signaled when the next file node is ready to be processed. * * @param idx file model index */ void nextReady(const QPersistentModelIndex& idx); private slots: #if QT_VERSION >= 0x040700 /** * Called when the gatherer thread has finished to load the @a path. * * @param path directory fetched due to fetchMore() call. */ void onDirectoryLoaded(const QString& path); #else /** * Check if the directory has been loaded. */ void onRowsInserted(); #endif /** * Fetch next index. */ void fetchNext(); private: QList m_rootIndexes; QStack m_nodes; FileProxyModel* m_model; QPersistentModelIndex m_nextIdx; bool m_aborted; QTimer* m_timeoutTimer; }; #endif // FILEPROXYMODELITERATOR_H kid3-3.0.2/src/core/model/framelist.cpp000066400000000000000000000123641224603511300176620ustar00rootroot00000000000000/** * \file framelist.cpp * List of ID3v2.3 frames. * * \b Project: Kid3 * \author Urs Fleisch * \date 9 Jan 2003 * * Copyright (C) 2003-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "framelist.h" #include #include "taggedfile.h" #include "frametablemodel.h" #include "iframeeditor.h" /** * Constructor. * * @param ftm frame table model * @param selModel item selection model */ FrameList::FrameList(FrameTableModel* ftm, QItemSelectionModel* selModel) : QObject(ftm), m_taggedFile(0), m_frameTableModel(ftm), m_selectionModel(selModel), m_cursorRow(-1), m_cursorColumn(-1) { setObjectName(QLatin1String("FrameList")); } /** * Destructor. */ FrameList::~FrameList() {} /** * Get ID of selected frame list item. * * @return ID of selected item, * -1 if not item is selected. */ int FrameList::getSelectedId() const { const Frame* currentFrame = m_frameTableModel->getFrameOfIndex(m_selectionModel->currentIndex()); return currentFrame ? currentFrame->getIndex() : -1; } /** * Get frame of selected frame list item. * * @param frame the selected frame is returned here * * @return false if not item is selected. */ bool FrameList::getSelectedFrame(Frame& frame) const { const Frame* currentFrame = m_frameTableModel->getFrameOfIndex(m_selectionModel->currentIndex()); if (currentFrame) { frame = *currentFrame; return true; } return false; } /** * Select the frame by ID. * * @param id ID of frame to select */ void FrameList::setSelectedId(int id) { m_selectionModel->setCurrentIndex( m_frameTableModel->index( m_frameTableModel->getRowWithFrameIndex(id), 0), QItemSelectionModel::SelectCurrent); } /** * Get the name of the selected frame. * * @return name, QString::null if nothing selected. */ QString FrameList::getSelectedName() const { const Frame* currentFrame = m_frameTableModel->getFrameOfIndex(m_selectionModel->currentIndex()); return currentFrame ? currentFrame->getName() : QString(); } /** * Select a frame with a given name. * * @param name name of frame * * @return true if a frame with that name could be selected. */ bool FrameList::selectByName(const QString& name) { int row = m_frameTableModel->getRowWithFrameName(name); if (row < 0) return false; m_selectionModel->setCurrentIndex(m_frameTableModel->index(row, 0), QItemSelectionModel::SelectCurrent); return true; } /** * Set the frame table model from the tagged file. */ void FrameList::setModelFromTaggedFile() { if (m_taggedFile) { FrameCollection frames; m_taggedFile->getAllFramesV2(frames); m_frameTableModel->transferFrames(frames); } } /** * Delete selected frame. * * @return false if frame not found. */ bool FrameList::deleteFrame() { saveCursor(); Frame frame; if (getSelectedFrame(frame) && m_taggedFile) { m_taggedFile->deleteFrameV2(frame); setModelFromTaggedFile(); restoreCursor(); return true; } return false; } /** * Add and edit a new frame. * * @param frameEditor editor for frame fields * * @return true if frame added. */ bool FrameList::addAndEditFrame(IFrameEditor* frameEditor) { if (m_taggedFile) { quint64 oldChangedFrames = m_taggedFile->getChangedFramesV2(); if (!m_taggedFile->addFrameV2(m_frame)) { return false; } if (frameEditor && !frameEditor->editFrameOfTaggedFile(&m_frame, m_taggedFile)) { m_taggedFile->deleteFrameV2(m_frame); m_taggedFile->setChangedFramesV2(oldChangedFrames); return false; } int index = m_frame.getIndex(); setModelFromTaggedFile(); if (index != -1) { setSelectedId(index); } return true; } return false; } /** * Paste the selected frame from the copy buffer. * * @return true if frame pasted. */ bool FrameList::pasteFrame() { if (m_taggedFile && m_frame.getType() != Frame::FT_UnknownFrame) { m_taggedFile->addFrameV2(m_frame); m_taggedFile->setFrameV2(m_frame); return true; } return false; } /** * Save the current cursor position. */ void FrameList::saveCursor() { m_cursorRow = m_selectionModel->currentIndex().row(); m_cursorColumn = m_selectionModel->currentIndex().column(); } /** * Restore the cursor position saved with saveCursor(). */ void FrameList::restoreCursor() { int lastRow = m_frameTableModel->rowCount() - 1; if (m_cursorRow >= 0 && m_cursorColumn >= 0 && lastRow >= 0) { if (m_cursorRow > lastRow) { m_cursorRow = lastRow; } m_selectionModel->setCurrentIndex( m_frameTableModel->index(m_cursorRow, m_cursorColumn), QItemSelectionModel::SelectCurrent); } } kid3-3.0.2/src/core/model/framelist.h000066400000000000000000000072631224603511300173310ustar00rootroot00000000000000/** * \file framelist.h * List of frames. * * \b Project: Kid3 * \author Urs Fleisch * \date 9 Jan 2003 * * Copyright (C) 2003-2009 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef FRAMELIST_H #define FRAMELIST_H #include #include "frame.h" class QItemSelectionModel; class FrameTableModel; class TaggedFile; class IFrameEditor; /** * List of frames. */ class FrameList : public QObject { public: /** * Constructor. * * @param ftm frame table model * @param selModel item selection model */ FrameList(FrameTableModel* ftm, QItemSelectionModel* selModel); /** * Destructor. */ virtual ~FrameList(); /** * Set tagged file. * * @param taggedFile file */ void setTaggedFile(TaggedFile* taggedFile) { m_taggedFile = taggedFile; } /** * Get tagged file. * @return tagged file. */ TaggedFile* getTaggedFile() const { return m_taggedFile; } /** * Delete selected frame. * * @return false if frame not found. */ bool deleteFrame(); /** * Add and edit a new frame. * * @param frameEditor editor for frame fields * * @return true if frame added. */ bool addAndEditFrame(IFrameEditor* frameEditor); /** * Paste the selected frame from the copy buffer. * * @return true if frame pasted. */ bool pasteFrame(); /** * Set the frame in the copy buffer. * @param frame frame to set */ void setFrame(const Frame& frame) { m_frame = frame; } /** * Check if the frame in the copy buffer is a picture frame. * @return true if picture frame. */ bool isPictureFrame() const { return m_frame.getType() == Frame::FT_Picture; } /** * Get the name of the selected frame. * * @return name, QString::null if nothing selected. */ QString getSelectedName() const; /** * Select a frame with a given name. * * @param name name of frame * * @return true if a frame with that name could be selected. */ bool selectByName(const QString& name); /** * Get ID of selected frame list item. * * @return ID of selected item, * -1 if not item is selected. */ int getSelectedId() const; /** * Select the frame by ID. * * @param id ID of frame to select */ void setSelectedId(int id); private: FrameList(const FrameList&); FrameList& operator=(const FrameList&); /** * Save the current cursor position. */ void saveCursor(); /** * Restore the cursor position saved with saveCursor(). */ void restoreCursor(); /** * Get frame of selected frame list item. * * @param frame the selected frame is returned here * * @return false if not item is selected. */ bool getSelectedFrame(Frame& frame) const; /** * Set the frame table model from the tagged file. */ void setModelFromTaggedFile(); /** File containing tags */ TaggedFile* m_taggedFile; /** Frame used to add, edit and paste */ Frame m_frame; FrameTableModel* m_frameTableModel; QItemSelectionModel* m_selectionModel; int m_cursorRow; int m_cursorColumn; }; #endif // FRAMELIST_H kid3-3.0.2/src/core/model/frametablemodel.cpp000066400000000000000000000442551224603511300210230ustar00rootroot00000000000000/** * \file frametablemodel.cpp * Model for table with frames. * * \b Project: Kid3 * \author Urs Fleisch * \date 01 May 2011 * * Copyright (C) 2011-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "frametablemodel.h" #include #include #include "fileconfig.h" #include "tagconfig.h" #include "formatconfig.h" #include "genres.h" /** * Constructor. * @param id3v1 true if model for ID3v1 frames * @param parent parent widget */ FrameTableModel::FrameTableModel(bool id3v1, QObject* parent) : QAbstractTableModel(parent), m_markedRows(0), m_changedFrames(0), m_id3v1(id3v1) { setObjectName(QLatin1String("FrameTableModel")); } /** * Destructor. */ FrameTableModel::~FrameTableModel() { } /** * Get item flags for index. * @param index model index * @return item flags */ Qt::ItemFlags FrameTableModel::flags(const QModelIndex& index) const { Qt::ItemFlags theFlags = QAbstractTableModel::flags(index); if (index.isValid()) { if (index.column() == CI_Enable) { theFlags |= Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable; } else if (index.column() == CI_Value) { theFlags |= Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable; } } return theFlags; } /** * Get a display representation of the a frame name. * For ID3v2-IDs with description, only the ID is returned. * Other non-empty strings are translated. * * @param str frame name * * @return display representation of name. */ QString FrameTableModel::getDisplayName(const QString& str) { if (!str.isEmpty()) { int nlPos = str.indexOf(QLatin1Char('\n')); if (nlPos > 0) { // probably "TXXX - User defined text information\nDescription" or // "WXXX - User defined URL link\nDescription" return str.mid(nlPos + 1); } else if (str.midRef(4, 3) == QLatin1String(" - ")) { // probably "ID3-ID - Description" return str.left(4); } else { return QCoreApplication::translate("@default", str.toLatin1().data()); } } return str; } /** * Get data for a given role. * @param index model index * @param role item data role * @return data for role */ QVariant FrameTableModel::data(const QModelIndex& index, int role) const { if (!index.isValid() || index.row() < 0 || index.row() >= static_cast(m_frames.size()) || index.column() < 0 || index.column() >= CI_NumColumns) return QVariant(); FrameCollection::const_iterator it = frameAt(index.row()); if (role == Qt::DisplayRole || role == Qt::EditRole) { if (index.column() == CI_Enable) return getDisplayName(it->getName()); else if (index.column() == CI_Value) return it->getValue(); } else if (role == Qt::CheckStateRole && index.column() == CI_Enable) { return m_frameSelected.at(index.row()) ? Qt::Checked : Qt::Unchecked; } else if (role == Qt::BackgroundColorRole) { if (index.column() == CI_Enable) { return FileConfig::instance().m_markChanges && (it->isValueChanged() || (static_cast((*it).getType()) < sizeof(m_changedFrames) * 8 && (m_changedFrames & (1ULL << (*it).getType())) != 0)) ? QApplication::palette().mid() : Qt::NoBrush; } else if (index.column() == CI_Value && static_cast(index.row()) < sizeof(m_markedRows) * 8) { return (m_markedRows & (1ULL << index.row())) != 0 ? QBrush(Qt::red) : Qt::NoBrush; } } else if (role == FrameTypeRole) { return it->getType(); } return QVariant(); } /** * Set data for a given role. * @param index model index * @param value data value * @param role item data role * @return true if successful */ bool FrameTableModel::setData(const QModelIndex& index, const QVariant& value, int role) { if (!index.isValid() || index.row() < 0 || index.row() >= static_cast(m_frames.size()) || index.column() < 0 || index.column() >= CI_NumColumns) return false; if (role == Qt::EditRole && index.column() == CI_Value) { QString valueStr(value.toString()); FrameCollection::iterator it = frameAt(index.row()); if (valueStr != (*it).getValue()) { Frame& frame = const_cast(*it); if (valueStr.isNull()) valueStr = QLatin1String(""); frame.setValueIfChanged(valueStr); emit dataChanged(index, index); // Automatically set the checkbox when a value is changed if (!m_frameSelected.at(index.row())) { m_frameSelected[index.row()] = true; QModelIndex checkIndex(index.sibling(index.row(), CI_Enable)); emit dataChanged(checkIndex, checkIndex); } } return true; } else if (role == Qt::CheckStateRole && index.column() == CI_Enable) { bool isChecked(value.toInt() == Qt::Checked); if (isChecked != m_frameSelected.at(index.row())) { m_frameSelected[index.row()] = isChecked; emit dataChanged(index, index); } return true; } return false; } /** * Get data for header section. * @param section column or row * @param orientation horizontal or vertical * @param role item data role * @return header data for role */ QVariant FrameTableModel::headerData( int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole) return QVariant(); if (orientation == Qt::Horizontal) { return section == CI_Enable ? tr("Name") : tr("Data"); } return section + 1; } /** * Get number of rows. * @param parent parent model index, invalid for table models * @return number of rows, * if parent is valid number of children (0 for table models) */ int FrameTableModel::rowCount(const QModelIndex& parent) const { return parent.isValid() ? 0 : m_frames.size(); } /** * Get number of columns. * @param parent parent model index, invalid for table models * @return number of columns, * if parent is valid number of children (0 for table models) */ int FrameTableModel::columnCount(const QModelIndex& parent) const { return parent.isValid() ? 0 : CI_NumColumns; } /** * Insert rows. * @param row rows are inserted before this row, if 0 at the begin, * if rowCount() at the end * @param count number of rows to insert * @param parent parent model index, invalid for table models * @return true if successful */ bool FrameTableModel::insertRows(int, int count, const QModelIndex&) { for (int i = 0; i < count; ++i) insertFrame(Frame()); return true; } /** * Insert a frame. * @param frame frame to insert * @return true if successful */ void FrameTableModel::insertFrame(const Frame& frame) { FrameCollection::iterator it = m_frames.upper_bound(frame); int row = rowOf(it); beginInsertRows(QModelIndex(), row, row); it = m_frames.insert(it, frame); resizeFrameSelected(); endInsertRows(); } /** * Remove rows. * @param row rows are removed starting with this row * @param count number of rows to remove * @param parent parent model index, invalid for table models * @return true if successful */ bool FrameTableModel::removeRows(int row, int count, const QModelIndex&) { beginRemoveRows(QModelIndex(), row, row + count - 1); m_frames.erase(frameAt(row), frameAt(row + count)); resizeFrameSelected(); endRemoveRows(); return true; } /** * Get the frame at a specific position in the collection. * @param row position of frame * @return const iterator to frame */ FrameCollection::const_iterator FrameTableModel::frameAt(int row) const { FrameCollection::const_iterator it = m_frames.begin(); for (int i = 0; i < row; ++i) { if (++it == m_frames.end()) { break; } } return it; } /** * Get the frame at a specific position in the collection. * @param row position of frame * @return iterator to frame */ FrameCollection::iterator FrameTableModel::frameAt(int row) { FrameCollection::iterator it = m_frames.begin(); for (int i = 0; i < row; ++i) { if (++it == m_frames.end()) { break; } } return it; } /** * Get the row corresponding to a frame iterator. * @param frameIt frame iterator * @return row number, number of rows if not found. */ int FrameTableModel::rowOf(FrameCollection::iterator frameIt) const { int row = 0; for (FrameCollection::const_iterator it = m_frames.begin(); it != m_frames.end(); ++it) { if (frameIt == it) break; ++row; } return row; } /** * Get frame for index. * @param index model index * @return frame, 0 if no frame. */ const Frame* FrameTableModel::getFrameOfIndex(const QModelIndex& index) const { if (index.isValid() && index.row() < static_cast(m_frames.size())) { FrameCollection::const_iterator it = frameAt(index.row()); return &(*it); } return 0; } /** * Get row with frame with a specific frame index. * @param index frame index * @return row number, -1 if not found. */ int FrameTableModel::getRowWithFrameIndex(int index) const { int row = 0; for (FrameCollection::const_iterator it = m_frames.begin(); it != m_frames.end(); ++it) { if (it->getIndex() == index) { return row; } ++row; } return -1; } /** * Get row with frame with a specific frame name. * @param name name of frame * @return row number, -1 if not found. */ int FrameTableModel::getRowWithFrameName(const QString& name) const { int row = 0; for (FrameCollection::const_iterator it = m_frames.begin(); it != m_frames.end(); ++it) { if (it->getName() == name) { return row; } ++row; } return -1; } /** * Get filter with enabled frames. * * @param allDisabledToAllEnabled true to enable all if all are disabled * * @return filter with enabled frames. */ FrameFilter FrameTableModel::getEnabledFrameFilter( bool allDisabledToAllEnabled) const { FrameFilter filter; filter.enableAll(); bool allDisabled = true; int numberRows = rowCount(); int row = 0; for (FrameCollection::const_iterator it = m_frames.begin(); it != m_frames.end(); ++it) { if (row >= numberRows) break; if (!m_frameSelected.at(row)) { filter.enable(it->getType(), it->getName(), false); } else { allDisabled = false; } ++row; } if (allDisabledToAllEnabled && allDisabled) { filter.enableAll(); } return filter; } /** * Get enabled frames. * @return frame collection with enabled frames. */ FrameCollection FrameTableModel::getEnabledFrames() const { FrameCollection enabledFrames; const int numberRows = m_frameSelected.size(); int row = 0; for (FrameCollection::const_iterator it = m_frames.begin(); it != m_frames.end(); ++it) { if (row >= numberRows) break; if (m_frameSelected.at(row)) { enabledFrames.insert(*it); } ++row; } return enabledFrames; } /** * Clear frame collection. */ void FrameTableModel::clearFrames() { beginRemoveRows(QModelIndex(), 0, m_frames.size() - 1); m_frames.clear(); m_frameSelected.clear(); endRemoveRows(); } /** * Transfer frames to frame collection. * @param src frames to move into frame collection, will be cleared */ void FrameTableModel::transferFrames(FrameCollection& src) { int oldNumFrames = m_frames.size(); int newNumFrames = src.size(); int numRowsChanged = qMin(oldNumFrames, newNumFrames); if (newNumFrames < oldNumFrames) beginRemoveRows(QModelIndex(), newNumFrames, oldNumFrames - 1); else if (newNumFrames > oldNumFrames) beginInsertRows(QModelIndex(), oldNumFrames, newNumFrames - 1); m_frames.clear(); src.swap(m_frames); resizeFrameSelected(); if (newNumFrames < oldNumFrames) endRemoveRows(); else if (newNumFrames > oldNumFrames) endInsertRows(); if (numRowsChanged > 0) emit dataChanged(index(0, 0), index(numRowsChanged - 1, CI_NumColumns - 1)); } /** * Set values which are different inactive. * * @param others frames to compare, will be modified */ void FrameTableModel::filterDifferent(FrameCollection& others) { int oldNumFrames = m_frames.size(); m_frames.filterDifferent(others); resizeFrameSelected(); if (oldNumFrames > 0) emit dataChanged(index(0, 0), index(oldNumFrames - 1, CI_NumColumns - 1)); int newNumFrames = m_frames.size(); if (newNumFrames > oldNumFrames) { beginInsertRows(QModelIndex(), oldNumFrames, newNumFrames - 1); endInsertRows(); } } /** * Set the check state of all frames in the table. * * @param checked true to check the frames */ void FrameTableModel::setAllCheckStates(bool checked) { for (int row = 0; row < rowCount(); ++row) { m_frameSelected[row] = checked; } } /** * Select all frames in the table. */ void FrameTableModel::selectAllFrames() { setAllCheckStates(true); } /** * Deselect all frames in the table. */ void FrameTableModel::deselectAllFrames() { setAllCheckStates(false); } /** * Select changed frames in the table. */ void FrameTableModel::selectChangedFrames() { int row; FrameCollection::const_iterator it; for (row = 0, it = m_frames.begin(); row < m_frameSelected.size() && it != m_frames.end(); ++row, ++it) { if (it->isValueChanged()) { m_frameSelected[row] = true; } } } /** * Resize the bit array with the frame selection to match the frames size. */ void FrameTableModel::resizeFrameSelected() { // If all bits are set, set also the new bits. int oldSize = m_frameSelected.size(); int newSize = m_frames.size(); bool setNewBits = newSize > oldSize && oldSize > 0 && m_frameSelected.count(true) == oldSize; m_frameSelected.resize(newSize); if (setNewBits) { for (int i = oldSize; i < newSize; ++i) { m_frameSelected.setBit(i, true); } } } /** * Constructor. * @param parent parent widget */ FrameTableLineEdit::FrameTableLineEdit(QWidget* parent) : QLineEdit(parent) { setObjectName(QLatin1String("FrameTableLineEdit")); connect(this, SIGNAL(textChanged(QString)), this, SLOT(formatTextIfEnabled(QString))); } /** * Destructor. */ FrameTableLineEdit::~FrameTableLineEdit() {} /** * Format text if enabled. * @param txt text to format and set in line edit */ void FrameTableLineEdit::formatTextIfEnabled(const QString& txt) { if (TagFormatConfig::instance().m_formatWhileEditing) { QString str(txt); TagFormatConfig::instance().formatString(str); if (str != txt) { int curPos = cursorPosition(); setText(str); setCursorPosition(curPos); } } } /** * Create an editor to edit the cells contents. * @param parent parent widget * @param option style * @param index index of item * @return combo box editor widget. */ QWidget* FrameItemDelegate::createEditor( QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const { int row = index.row(); int col = index.column(); const FrameTableModel* ftModel = qobject_cast(index.model()); if (row >= 0 && (col == FrameTableModel::CI_Value || !ftModel)) { Frame::Type type = static_cast( index.data(FrameTableModel::FrameTypeRole).toInt()); bool id3v1 = ftModel && ftModel->isId3v1(); if (type == Frame::FT_Genre) { QComboBox* cb = new QComboBox(parent); if (!id3v1) { cb->setEditable(true); cb->setAutoCompletion(true); cb->setDuplicatesEnabled(false); } QStringList strList; for (const char** sl = Genres::s_strList; *sl != 0; ++sl) { strList += QString::fromLatin1(*sl); } if (TagConfig::instance().onlyCustomGenres()) { cb->addItem(QLatin1String("")); } else { cb->addItems(strList); } QStringList customGenres = TagConfig::instance().customGenres(); if (id3v1) { for (QStringList::const_iterator it = customGenres.begin(); it != customGenres.end(); ++it) { if (Genres::getNumber(*it) != 255) { cb->addItem(*it); } } if (cb->count() <= 1) { // No custom genres for ID3v1 => Show standard genres cb->clear(); cb->addItems(strList); } } else { cb->addItems(customGenres); } return cb; } else if (id3v1 && (type == Frame::FT_Comment || type == Frame::FT_Title || type == Frame::FT_Artist || type == Frame::FT_Album)) { FrameTableLineEdit* e = new FrameTableLineEdit(parent); e->setMaxLength(type == Frame::FT_Comment ? 28 : 30); e->setFrame(false); return e; } } return QItemDelegate::createEditor(parent, option, index); } /** * Set data to be edited by the editor. * @param editor editor widget * @param index index of item */ void FrameItemDelegate::setEditorData( QWidget* editor, const QModelIndex& index) const { QComboBox* cb = qobject_cast(editor); if (cb) { QString genreStr(index.model()->data(index).toString()); int genreIndex = genreStr.isNull() ? 0 : Genres::getIndex(Genres::getNumber(genreStr)); if (TagConfig::instance().onlyCustomGenres()) { genreIndex = cb->findText(genreStr); if (genreIndex < 0) genreIndex = 0; } else if (genreIndex <= 0) { genreIndex = cb->findText(genreStr); if (genreIndex < 0) genreIndex = Genres::count + 1; } cb->setItemText(genreIndex, genreStr); cb->setCurrentIndex(genreIndex); } else { QItemDelegate::setEditorData(editor, index); } } /** * Set model data supplied by editor. * @param editor editor widget * @param model model * @param index index of item */ void FrameItemDelegate::setModelData( QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const { QComboBox* cb = qobject_cast(editor); if (cb) { model->setData(index, cb->currentText()); } else { QItemDelegate::setModelData(editor, model, index); } } kid3-3.0.2/src/core/model/frametablemodel.h000066400000000000000000000221541224603511300204620ustar00rootroot00000000000000/** * \file frametablemodel.h * Model for table with frames. * * \b Project: Kid3 * \author Urs Fleisch * \date 01 May 2011 * * Copyright (C) 2011-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef FRAMETABLEMODEL_H #define FRAMETABLEMODEL_H #include #include #include #include #include "frame.h" #include "kid3api.h" /** * Model for table with frames. */ class KID3_CORE_EXPORT FrameTableModel : public QAbstractTableModel { Q_OBJECT public: /** Custom role. */ enum Roles { FrameTypeRole = Qt::UserRole + 1 }; /** Column indices. */ enum ColumnIndex { CI_Enable, CI_Value, CI_NumColumns }; /** * Constructor. * @param id3v1 true if model for ID3v1 frames * @param parent parent widget */ explicit FrameTableModel(bool id3v1, QObject* parent = 0); /** * Destructor. */ virtual ~FrameTableModel(); /** * Get item flags for index. * @param index model index * @return item flags */ virtual Qt::ItemFlags flags(const QModelIndex& index) const; /** * Get data for a given role. * @param index model index * @param role item data role * @return data for role */ virtual QVariant data(const QModelIndex& index, int role=Qt::DisplayRole) const; /** * Set data for a given role. * @param index model index * @param value data value * @param role item data role * @return true if successful */ virtual bool setData(const QModelIndex& index, const QVariant& value, int role=Qt::EditRole); /** * Get data for header section. * @param section column or row * @param orientation horizontal or vertical * @param role item data role * @return header data for role */ virtual QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const; /** * Set data for header section. * Not supported. * @return false */ virtual bool setHeaderData(int, Qt::Orientation, const QVariant&, int=Qt::EditRole) { return false; } /** * Get number of rows. * @param parent parent model index, invalid for table models * @return number of rows, * if parent is valid number of children (0 for table models) */ virtual int rowCount(const QModelIndex& parent=QModelIndex()) const; /** * Get number of columns. * @param parent parent model index, invalid for table models * @return number of columns, * if parent is valid number of children (0 for table models) */ virtual int columnCount(const QModelIndex& parent=QModelIndex()) const; /** * Remove rows. * @param row rows are removed starting with this row * @param count number of rows to remove * @param parent parent model index, invalid for table models * @return true if successful */ virtual bool removeRows(int row, int count, const QModelIndex& parent=QModelIndex()); /** * Insert rows. * @param row rows are inserted before this row, if 0 at the begin, * if rowCount() at the end * @param count number of rows to insert * @param parent parent model index, invalid for table models * @return true if successful */ virtual bool insertRows(int row, int count, const QModelIndex& parent=QModelIndex()); /** * Insert a frame. * @param frame frame to insert */ void insertFrame(const Frame& frame); /** * Check if model is for ID3v1 frames. * @return true if for ID3v1. */ bool isId3v1() const { return m_id3v1; } /** * Mark rows. * @param rowMask mask with bits of rows to mark */ void markRows(quint64 rowMask) { m_markedRows = rowMask; } /** * Mark changed frames. * @param frameMask mask with bits of frame types to mark */ void markChangedFrames(quint64 frameMask) { m_changedFrames = frameMask; } /** * Get frame for index. * @param index model index * @return frame, 0 if no frame. */ const Frame* getFrameOfIndex(const QModelIndex& index) const; /** * Get row with frame with a specific frame index. * @param index frame index * @return row number, -1 if not found. */ int getRowWithFrameIndex(int index) const; /** * Get row with frame with a specific frame name. * @param name name of frame * @return row number, -1 if not found. */ int getRowWithFrameName(const QString& name) const; /** * Get filter with enabled frames. * * @param allDisabledToAllEnabled true to enable all if all are disabled * * @return filter with enabled frames. */ FrameFilter getEnabledFrameFilter(bool allDisabledToAllEnabled = false) const; /** * Set the check state of all frames in the table. * * @param checked true to check the frames */ void setAllCheckStates(bool checked); /** * Get reference to frame collection. * @return frame collection. */ const FrameCollection& frames() const { return m_frames; } /** * Get enabled frames. * @return frame collection with enabled frames. */ FrameCollection getEnabledFrames() const; /** * Clear frame collection. */ void clearFrames(); /** * Transfer frames to frame collection. * @param src frames to move into frame collection, will be cleared */ void transferFrames(FrameCollection& src); /** * Set values which are different inactive. * * @param others frames to compare, will be modified */ void filterDifferent(FrameCollection& others); /** * Get a display representation of the a frame name. * For ID3v2-IDs with description, only the ID is returned. * Other non-empty strings are translated. * * @param str frame name * * @return display representation of name. */ static QString getDisplayName(const QString& str); public slots: /** * Select all frames in the table. */ void selectAllFrames(); /** * Deselect all frames in the table. */ void deselectAllFrames(); /** * Select changed frames in the table. */ void selectChangedFrames(); private: /** * Get the frame at a specific position in the collection. * @param row position of frame * @return const iterator to frame */ FrameCollection::const_iterator frameAt(int row) const; /** * Get the frame at a specific position in the collection. * @param row position of frame * @return iterator to frame */ FrameCollection::iterator frameAt(int row); /** * Get the row corresponding to a frame iterator. * @param frameIt frame iterator * @return row number, number of rows if not found. */ int rowOf(FrameCollection::iterator frameIt) const; /** * Resize the bit array with the frame selection to match the frames size. */ void resizeFrameSelected(); QBitArray m_frameSelected; quint64 m_markedRows; quint64 m_changedFrames; bool m_id3v1; FrameCollection m_frames; }; /** Line edit with automatic tag formatting. */ class FrameTableLineEdit : public QLineEdit { Q_OBJECT public: /** * Constructor. * @param parent parent widget */ explicit FrameTableLineEdit(QWidget* parent); /** * Destructor. */ virtual ~FrameTableLineEdit(); private slots: /** * Format text if enabled. * @param txt text to format and set in line edit */ void formatTextIfEnabled(const QString& txt); }; /** Delegate for table widget items. */ class KID3_CORE_EXPORT FrameItemDelegate : public QItemDelegate { public: /** * Constructor. * @param parent parent QTableView */ explicit FrameItemDelegate(QObject* parent) : QItemDelegate(parent) { setObjectName(QLatin1String("FrameItemDelegate")); } /** * Destructor. */ virtual ~FrameItemDelegate() {} /** * Create an editor to edit the cells contents. * @param parent parent widget * @param option style * @param index index of item * @return combo box editor widget. */ virtual QWidget* createEditor( QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const; /** * Set data to be edited by the editor. * @param editor editor widget * @param index index of item */ virtual void setEditorData(QWidget* editor, const QModelIndex& index) const; /** * Set model data supplied by editor. * @param editor editor widget * @param model model * @param index index of item */ virtual void setModelData( QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const; }; #endif // FRAMETABLEMODEL_H kid3-3.0.2/src/core/model/iabortable.cpp000066400000000000000000000016271224603511300200000ustar00rootroot00000000000000/** * \file iabortable.h * Interface for abortable operations. * * \b Project: Kid3 * \author Urs Fleisch * \date 27 Jan 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "iabortable.h" /** * Destructor. */ IAbortable::~IAbortable() { } kid3-3.0.2/src/core/model/iabortable.h000066400000000000000000000025261224603511300174440ustar00rootroot00000000000000/** * \file iabortable.h * Interface for abortable operations. * * \b Project: Kid3 * \author Urs Fleisch * \date 27 Jan 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef IABORTABLE_H #define IABORTABLE_H #include "kid3api.h" /** * Interface for abortable operations. */ class KID3_CORE_EXPORT IAbortable { public: /** * Destructor. */ virtual ~IAbortable() = 0; /** * Abort operation. */ virtual void abort() = 0; /** * Check if operation is aborted. * * @return true if aborted. */ virtual bool isAborted() const = 0; /** * Clear state which is reported by isAborted(). */ virtual void clearAborted() = 0; }; #endif // IABORTABLE_H kid3-3.0.2/src/core/model/iframeeditor.cpp000066400000000000000000000016431224603511300203440ustar00rootroot00000000000000/** * \file iframeeditor.cpp * Interface for editor of frame fields. * * \b Project: Kid3 * \author Urs Fleisch * \date 18 Jul 2011 * * Copyright (C) 2011 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "iframeeditor.h" /** * Destructor. */ IFrameEditor::~IFrameEditor() { } kid3-3.0.2/src/core/model/iframeeditor.h000066400000000000000000000033621224603511300200110ustar00rootroot00000000000000/** * \file iframeeditor.h * Interface for editor of frame fields. * * \b Project: Kid3 * \author Urs Fleisch * \date 18 Jul 2011 * * Copyright (C) 2011 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef IFRAMEEDITOR_H #define IFRAMEEDITOR_H #include "kid3api.h" class Frame; class TaggedFile; /** * Interface for editor of frame fields. */ class KID3_CORE_EXPORT IFrameEditor { public: /** * Destructor. */ virtual ~IFrameEditor() = 0; /** * Let user edit a frame and then update the fields * when the edits are accepted. * * @param frame frame to edit * @param taggedFile tagged file where frame has to be set * * @return true if frame edits are accepted. */ virtual bool editFrameOfTaggedFile(Frame* frame, TaggedFile* taggedFile) = 0; /** * Let user select a frame type. * * @param frame is filled with the selected frame if true is returned * @param taggedFile tagged file for which frame has to be selected * * @return false if no frame selected. */ virtual bool selectFrame(Frame* frame, const TaggedFile* taggedFile) = 0; }; #endif // IFRAMEEDITOR_H kid3-3.0.2/src/core/model/kid3application.cpp000066400000000000000000002355411224603511300207560ustar00rootroot00000000000000/** * \file kid3application.cpp * Kid3 application logic, independent of GUI. * * \b Project: Kid3 * \author Urs Fleisch * \date 10 Jul 2011 * * Copyright (C) 2011-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "kid3application.h" #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_QTDBUS #include #include #include "scriptinterface.h" #endif #include "icoreplatformtools.h" #include "fileproxymodel.h" #include "fileproxymodeliterator.h" #include "dirproxymodel.h" #include "modeliterator.h" #include "trackdatamodel.h" #include "frametablemodel.h" #include "framelist.h" #include "pictureframe.h" #include "textimporter.h" #include "textexporter.h" #include "dirrenamer.h" #include "configstore.h" #include "formatconfig.h" #include "tagconfig.h" #include "fileconfig.h" #include "importconfig.h" #include "guiconfig.h" #include "playlistconfig.h" #include "playlistcreator.h" #include "downloadclient.h" #include "iframeeditor.h" #include "batchimportprofile.h" #include "batchimporter.h" #include "iserverimporterfactory.h" #include "iservertrackimporterfactory.h" #include "itaggedfilefactory.h" #if defined HAVE_PHONON || QT_VERSION >= 0x050000 #include "audioplayer.h" #endif #include "importplugins.h" namespace { /** * Get the file name of the plugin from the plugin name. * @param pluginName name of the plugin * @return file name. */ QString pluginFileName(const QString& pluginName) { QString fileName = pluginName.toLower(); #ifdef Q_OS_WIN32 #ifdef Q_CC_MSVC fileName += QLatin1String(".dll"); #else fileName = QLatin1String("lib") + fileName + QLatin1String(".dll"); #endif #elif defined Q_OS_MAC fileName = QLatin1String("lib") + fileName + QLatin1String(".dylib"); #else fileName = QLatin1String("lib") + fileName + QLatin1String(".so"); #endif return fileName; } } /** Current directory */ QString Kid3Application::s_dirName; /** * Constructor. * @param platformTools platform tools * @param parent parent object */ Kid3Application::Kid3Application(ICorePlatformTools* platformTools, QObject* parent) : QObject(parent), m_platformTools(platformTools), m_fileSystemModel(new QFileSystemModel(this)), m_fileProxyModel(new FileProxyModel(this)), m_fileProxyModelIterator(new FileProxyModelIterator(m_fileProxyModel)), m_dirProxyModel(new DirProxyModel(this)), m_fileSelectionModel(new QItemSelectionModel(m_fileProxyModel, this)), m_trackDataModel(new TrackDataModel(this)), m_framesV1Model(new FrameTableModel(true, this)), m_framesV2Model(new FrameTableModel(false, this)), m_framesV1SelectionModel(new QItemSelectionModel(m_framesV1Model, this)), m_framesV2SelectionModel(new QItemSelectionModel(m_framesV2Model, this)), m_framelist(new FrameList(m_framesV2Model, m_framesV2SelectionModel)), m_configStore(new ConfigStore(m_platformTools->applicationSettings())), m_netMgr(new QNetworkAccessManager(this)), m_downloadClient(new DownloadClient(m_netMgr)), m_textExporter(new TextExporter(this)), m_dirRenamer(new DirRenamer(this)), m_batchImporter(new BatchImporter(m_netMgr)), m_openDirectoryTimeoutTimer(new QTimer(this)), #if defined HAVE_PHONON || QT_VERSION >= 0x050000 m_player(0), #endif m_expressionFileFilter(0), m_downloadImageDest(ImageForSelectedFiles), m_selectionSingleFile(0), m_selectionTagV1SupportedCount(0), m_selectionFileCount(0), m_selectionHasTagV1(false), m_selectionHasTagV2(false), m_fileFilter(0), m_batchImportProfile(0), m_batchImportTagVersion(TrackData::TagNone) { setObjectName(QLatin1String("Kid3Application")); m_openDirectoryTimeoutTimer->setSingleShot(true); m_openDirectoryTimeoutTimer->setInterval(1000); m_fileProxyModel->setSourceModel(m_fileSystemModel); m_dirProxyModel->setSourceModel(m_fileSystemModel); connect(m_fileSelectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(fileSelected())); setModified(false); setFiltered(false); initPlugins(); m_batchImporter->setImporters(m_importers, m_trackDataModel); #ifdef HAVE_QTDBUS if (QDBusConnection::sessionBus().isConnected()) { QString serviceName(QLatin1String("net.sourceforge.kid3")); QDBusConnection::sessionBus().registerService(serviceName); // For the case of multiple Kid3 instances running, register also a service // with the PID appended. On KDE such a service is already registered but // the call to registerService() seems to succeed nevertheless. serviceName += QLatin1Char('-'); serviceName += QString::number(::getpid()); QDBusConnection::sessionBus().registerService(serviceName); new ScriptInterface(this); if (!QDBusConnection::sessionBus().registerObject(QLatin1String("/Kid3"), this)) { qWarning("Registering D-Bus object failed"); } } else { qWarning("Cannot connect to the D-BUS session bus."); } #endif } /** * Destructor. */ Kid3Application::~Kid3Application() { delete m_configStore; #if defined Q_OS_MAC && QT_VERSION >= 0x050000 // If a song is played, then stopped and Kid3 is terminated, it will crash in // the QMediaPlayer destructor (Dispatch queue: com.apple.main-thread, // objc_msgSend() selector name: setRate). Avoid calling the destructor by // setting the QMediaPlayer's parent to null. See also: // https://qt-project.org/forums/viewthread/29651 if (m_player) { m_player->setParent(0); } #endif } /** * Load and initialize plugins depending on configuration. */ void Kid3Application::initPlugins() { // Load plugins, set information about plugins in configuration. ImportConfig& importCfg = ImportConfig::instance(); TagConfig& tagCfg = TagConfig::instance(); importCfg.availablePlugins().clear(); tagCfg.availablePlugins().clear(); foreach (QObject* plugin, loadPlugins()) { checkPlugin(plugin); } // Order the meta data plugins as configured. QStringList pluginOrder = tagCfg.pluginOrder(); if (!pluginOrder.isEmpty()) { QList orderedFactories; for (int i = 0; i < pluginOrder.size(); ++i) { orderedFactories.append(0); } foreach (ITaggedFileFactory* factory, FileProxyModel::taggedFileFactories()) { int idx = pluginOrder.indexOf(factory->name()); if (idx >= 0) { orderedFactories[idx] = factory; } else { orderedFactories.append(factory); } } orderedFactories.removeAll(0); #if QT_VERSION >= 0x040800 FileProxyModel::taggedFileFactories().swap(orderedFactories); #else FileProxyModel::taggedFileFactories() = orderedFactories; #endif } } /** * Load plugins. * @return list of plugin instances. */ QObjectList Kid3Application::loadPlugins() { QObjectList plugins = QPluginLoader::staticInstances(); // First check if we are running from the build directory to load the // plugins from there. QDir pluginsDir(qApp->applicationDirPath()); QString dirName = pluginsDir.dirName(); #ifdef Q_OS_WIN QString buildType; if (dirName.compare(QLatin1String("debug"), Qt::CaseInsensitive) == 0 || dirName.compare(QLatin1String("release"), Qt::CaseInsensitive) == 0) { buildType = dirName; pluginsDir.cdUp(); dirName = pluginsDir.dirName(); } #endif bool pluginsDirFound = pluginsDir.cd(QLatin1String( (dirName == QLatin1String("qt") || dirName == QLatin1String("kde") || dirName == QLatin1String("cli")) ? "../../plugins" : dirName == QLatin1String("test") ? "../plugins" : CFG_PLUGINSDIR)); #ifdef Q_OS_MAC if (!pluginsDirFound) { pluginsDirFound = pluginsDir.cd(QLatin1String("../../../../../plugins")); } #endif if (pluginsDirFound) { #ifdef Q_OS_WIN if (!buildType.isEmpty()) { pluginsDir.cd(buildType); } #endif ImportConfig& importCfg = ImportConfig::instance(); TagConfig& tagCfg = TagConfig::instance(); // Construct a set of disabled plugin file names QMap disabledImportPluginFileNames; foreach (const QString& pluginName, importCfg.m_disabledPlugins) { disabledImportPluginFileNames.insert(pluginFileName(pluginName), pluginName); } QMap disabledTagPluginFileNames; QStringList disabledTagPlugins = tagCfg.disabledPlugins(); foreach (const QString& pluginName, disabledTagPlugins) { disabledTagPluginFileNames.insert(pluginFileName(pluginName), pluginName); } foreach (const QString& fileName, pluginsDir.entryList(QDir::Files)) { if (disabledImportPluginFileNames.contains(fileName)) { importCfg.availablePlugins().append( disabledImportPluginFileNames.value(fileName)); continue; } if (disabledTagPluginFileNames.contains(fileName)) { tagCfg.availablePlugins().append( disabledTagPluginFileNames.value(fileName)); continue; } QPluginLoader loader(pluginsDir.absoluteFilePath(fileName)); QObject* plugin = loader.instance(); if (plugin) { QString name(plugin->objectName()); if (importCfg.m_disabledPlugins.contains(name)) { importCfg.availablePlugins().append(name); loader.unload(); } else if (disabledTagPlugins.contains(name)) { tagCfg.availablePlugins().append(name); loader.unload(); } else { plugins.append(plugin); } } } } return plugins; } /** * Check type of a loaded plugin and register it. * @param plugin instance returned by plugin loader */ void Kid3Application::checkPlugin(QObject* plugin) { if (IServerImporterFactory* importerFactory = qobject_cast(plugin)) { ImportConfig& importCfg = ImportConfig::instance(); importCfg.availablePlugins().append(plugin->objectName()); if (!importCfg.m_disabledPlugins.contains(plugin->objectName())) { foreach (const QString& key, importerFactory->serverImporterKeys()) { m_importers.append(importerFactory->createServerImporter( key, m_netMgr, m_trackDataModel)); } } } if (IServerTrackImporterFactory* importerFactory = qobject_cast(plugin)) { ImportConfig& importCfg = ImportConfig::instance(); importCfg.availablePlugins().append(plugin->objectName()); if (!importCfg.m_disabledPlugins.contains(plugin->objectName())) { foreach (const QString& key, importerFactory->serverTrackImporterKeys()) { m_trackImporters.append(importerFactory->createServerTrackImporter( key, m_netMgr, m_trackDataModel)); } } } if (ITaggedFileFactory* taggedFileFactory = qobject_cast(plugin)) { TagConfig& tagCfg = TagConfig::instance(); tagCfg.availablePlugins().append(plugin->objectName()); if (!tagCfg.disabledPlugins().contains(plugin->objectName())) { int features = tagCfg.taggedFileFeatures(); foreach (const QString& key, taggedFileFactory->taggedFileKeys()) { taggedFileFactory->initialize(key); features |= taggedFileFactory->taggedFileFeatures(key); } tagCfg.setTaggedFileFeatures(features); FileProxyModel::taggedFileFactories().append(taggedFileFactory); } } } #if defined HAVE_PHONON || QT_VERSION >= 0x050000 /** * Get audio player. * @return audio player. */ AudioPlayer* Kid3Application::getAudioPlayer() { if (!m_player) { m_player = new AudioPlayer(this); } return m_player; } #endif /** * Get settings. * @return settings. */ ISettings* Kid3Application::getSettings() const { return m_platformTools->applicationSettings(); } /** * Save settings to the configuration. */ void Kid3Application::saveConfig() { if (FileConfig::instance().m_loadLastOpenedFile) { FileConfig::instance().m_lastOpenedFile = m_fileProxyModel->filePath(currentOrRootIndex()); } m_configStore->writeToConfig(); getSettings()->sync(); } /** * Read settings from the configuration. */ void Kid3Application::readConfig() { if (FileConfig::instance().m_nameFilter.isEmpty()) { FileConfig::instance().m_nameFilter = createFilterString(); } notifyConfigurationChange(); FrameCollection::setQuickAccessFrames( TagConfig::instance().quickAccessFrames()); } /** * Open directory. * When finished directoryOpened() is emitted, also if false is returned. * * @param paths file or directory paths, if multiple paths are given, the * common directory is opened and the files are selected * @param fileCheck if true, only open directory if paths exist * * @return true if ok. */ bool Kid3Application::openDirectory(const QStringList& paths, bool fileCheck) { bool ok = true; QStringList filePaths; QStringList dirComponents; foreach (const QString& path, paths) { if (!path.isEmpty()) { QFileInfo fileInfo(path); if (fileCheck && !fileInfo.exists()) { ok = false; break; } QString dirPath; if (!fileInfo.isDir()) { dirPath = fileInfo.absolutePath(); if (fileInfo.isFile()) { filePaths.append(fileInfo.absoluteFilePath()); } } else { dirPath = QDir(path).absolutePath(); } QStringList dirPathComponents = dirPath.split(QDir::separator()); if (dirComponents.isEmpty()) { dirComponents = dirPathComponents; } else { // Reduce dirPath to common prefix. QStringList::iterator dirIt = dirComponents.begin(); QStringList::const_iterator dirPathIt = dirPathComponents.constBegin(); while (dirIt != dirComponents.end() && dirPathIt != dirPathComponents.constEnd() && *dirIt == *dirPathIt) { ++dirIt; ++dirPathIt; } dirComponents.erase(dirIt, dirComponents.end()); } } } QString dir; if (ok) { dir = dirComponents.join(QDir::separator()); if (dir.isEmpty() && !filePaths.isEmpty()) { dir = QDir::rootPath(); } ok = !dir.isEmpty(); } QModelIndex rootIndex; QModelIndexList fileIndexes; if (ok) { QStringList nameFilters(m_platformTools->getNameFilterPatterns( FileConfig::instance().m_nameFilter). split(QLatin1Char(' '))); m_fileProxyModel->setNameFilters(nameFilters); m_fileSystemModel->setFilter(QDir::AllEntries | QDir::AllDirs); rootIndex = m_fileSystemModel->setRootPath(dir); foreach (const QString& filePath, filePaths) { fileIndexes.append(m_fileSystemModel->index(filePath)); } ok = rootIndex.isValid(); } if (ok) { setModified(false); setFiltered(false); setDirName(dir); QModelIndex oldRootIndex = m_fileProxyModelRootIndex; m_fileProxyModelRootIndex = m_fileProxyModel->mapFromSource(rootIndex); m_fileProxyModelFileIndexes.clear(); foreach (const QModelIndex& fileIndex, fileIndexes) { m_fileProxyModelFileIndexes.append( m_fileProxyModel->mapFromSource(fileIndex)); } if (m_fileProxyModelRootIndex != oldRootIndex) { #if QT_VERSION >= 0x040700 connect(m_fileProxyModel, SIGNAL(directoryLoaded(QString)), this, SLOT(onDirectoryLoaded())); #else // Qt < 4.7 does not have a directoryLoaded() signal, so // rowsInserted() is used. connect(m_fileProxyModel, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(onDirectoryLoaded())); #endif // Last resort timeout for the case that directoryLoaded() would not be // fired and for empty directories with Qt < 4.7 connect(m_openDirectoryTimeoutTimer, SIGNAL(timeout()), this, SLOT(onDirectoryLoaded())); m_openDirectoryTimeoutTimer->start(); } else { QTimer::singleShot(0, this, SLOT(emitDirectoryOpened())); } } if (!ok) { QTimer::singleShot(0, this, SLOT(emitDirectoryOpened())); } return ok; } /** * Emit directoryOpened(). */ void Kid3Application::emitDirectoryOpened() { emit directoryOpened(m_fileProxyModelRootIndex, m_fileProxyModelFileIndexes); } /** * Called when the gatherer thread has finished to load the directory. */ void Kid3Application::onDirectoryLoaded() { #if QT_VERSION >= 0x040700 disconnect(m_fileProxyModel, SIGNAL(directoryLoaded(QString)), this, SLOT(onDirectoryLoaded())); #else disconnect(m_fileProxyModel, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(onDirectoryLoaded())); #endif disconnect(m_openDirectoryTimeoutTimer, SIGNAL(timeout()), this, SLOT(onDirectoryLoaded())); m_openDirectoryTimeoutTimer->stop(); // Do not directly emit so that directory can be sorted. QTimer::singleShot(100, this, SLOT(emitDirectoryOpened())); } /** * Get directory path of opened directory. * @return directory path. */ QString Kid3Application::getDirPath() const { return FileProxyModel::getPathIfIndexOfDir(m_fileProxyModelRootIndex); } /** * Get current index in file proxy model or root index if current index is * invalid. * @return current index, root index if not valid. */ QModelIndex Kid3Application::currentOrRootIndex() const { QModelIndex index(m_fileSelectionModel->currentIndex()); if (index.isValid()) return index; else return m_fileProxyModelRootIndex; } /** * Save all changed files. * saveStarted() and saveProgress() are emitted while saving files. * * @return list of files with error, empty if ok. */ QStringList Kid3Application::saveDirectory() { QStringList errorFiles; int numFiles = 0, totalFiles = 0; // Get number of files to be saved to display correct progressbar TaggedFileIterator countIt(m_fileProxyModelRootIndex); while (countIt.hasNext()) { if (countIt.next()->isChanged()) { ++totalFiles; } } emit saveStarted(totalFiles); TaggedFileIterator it(m_fileProxyModelRootIndex); while (it.hasNext()) { TaggedFile* taggedFile = it.next(); bool renamed = false; if (!taggedFile->writeTags(false, &renamed, FileConfig::instance().m_preserveTime)) { QString errorMsg = taggedFile->getFilename(); if (!QFileInfo(taggedFile->getAbsFilename()).isWritable()) { errorMsg = tr("%1 is not writable").arg(errorMsg); } errorFiles.push_back(errorMsg); } ++numFiles; emit saveProgress(numFiles); } return errorFiles; } /** * Update tags of selected files to contain contents of frame models. * * @param selItems list of selected file indexes */ void Kid3Application::frameModelsToTags( const QList& selItems) { if (!selItems.isEmpty()) { FrameCollection framesV1(m_framesV1Model->getEnabledFrames()); FrameCollection framesV2(m_framesV2Model->getEnabledFrames()); for (QList::const_iterator it = selItems.begin(); it != selItems.end(); ++it) { if (TaggedFile* taggedFile = FileProxyModel::getTaggedFileOfIndex(*it)) { taggedFile->setFramesV1(framesV1); taggedFile->setFramesV2(framesV2); } } } } /** * Update frame models to contain contents of selected files. * The properties starting with "selection" will be set by this method. * * @param selItems list of selected file indexes */ void Kid3Application::tagsToFrameModels( const QList& selItems) { m_selectionSingleFile = 0; m_selectionTagV1SupportedCount = 0; m_selectionFileCount = 0; m_selectionHasTagV1 = false; m_selectionHasTagV2 = false; for (QList::const_iterator it = selItems.begin(); it != selItems.end(); ++it) { TaggedFile* taggedFile = FileProxyModel::getTaggedFileOfIndex(*it); if (taggedFile) { taggedFile->readTags(false); taggedFile = FileProxyModel::readWithId3V24IfId3V24(taggedFile); if (taggedFile->isTagV1Supported()) { if (m_selectionTagV1SupportedCount == 0) { FrameCollection frames; taggedFile->getAllFramesV1(frames); m_framesV1Model->transferFrames(frames); } else { FrameCollection fileFrames; taggedFile->getAllFramesV1(fileFrames); m_framesV1Model->filterDifferent(fileFrames); } ++m_selectionTagV1SupportedCount; } if (m_selectionFileCount == 0) { FrameCollection frames; taggedFile->getAllFramesV2(frames); m_framesV2Model->transferFrames(frames); m_selectionSingleFile = taggedFile; } else { FrameCollection fileFrames; taggedFile->getAllFramesV2(fileFrames); m_framesV2Model->filterDifferent(fileFrames); m_selectionSingleFile = 0; } ++m_selectionFileCount; m_selectionHasTagV1 = m_selectionHasTagV1 || taggedFile->hasTagV1(); m_selectionHasTagV2 = m_selectionHasTagV2 || taggedFile->hasTagV2(); } } m_framesV1Model->setAllCheckStates(m_selectionTagV1SupportedCount == 1); m_framesV2Model->setAllCheckStates(m_selectionFileCount == 1); if (GuiConfig::instance().m_autoHideTags) { // If a tag is supposed to be absent, make sure that there is really no // unsaved data in the tag. if (!m_selectionHasTagV1 && (m_selectionTagV1SupportedCount > 0 || m_selectionFileCount == 0)) { const FrameCollection& frames = m_framesV1Model->frames(); for (FrameCollection::const_iterator it = frames.begin(); it != frames.end(); ++it) { if (!(*it).getValue().isEmpty()) { m_selectionHasTagV1 = true; break; } } } if (!m_selectionHasTagV2) { const FrameCollection& frames = m_framesV2Model->frames(); for (FrameCollection::const_iterator it = frames.begin(); it != frames.end(); ++it) { if (!(*it).getValue().isEmpty()) { m_selectionHasTagV2 = true; break; } } } } if (m_selectionSingleFile) { m_framelist->setTaggedFile(m_selectionSingleFile); if (TagConfig::instance().markTruncations()) { m_framesV1Model->markRows(m_selectionSingleFile->getTruncationFlags()); } if (FileConfig::instance().m_markChanges) { m_framesV1Model->markChangedFrames( m_selectionSingleFile->getChangedFramesV1()); m_framesV2Model->markChangedFrames( m_selectionSingleFile->getChangedFramesV2()); } } else { if (TagConfig::instance().markTruncations()) { m_framesV1Model->markRows(0); } if (FileConfig::instance().m_markChanges) { m_framesV1Model->markChangedFrames(0); m_framesV2Model->markChangedFrames(0); } } } /** * Revert file modifications. * Acts on selected files or all files if no file is selected. */ void Kid3Application::revertFileModifications() { SelectedTaggedFileIterator it(getRootIndex(), getFileSelectionModel(), true); while (it.hasNext()) { TaggedFile* taggedFile = it.next(); taggedFile->readTags(true); // update icon getFileProxyModel()->emitDataChanged(taggedFile->getIndex(), taggedFile->getIndex()); } if (!it.hasNoSelection()) { emit selectedFilesUpdated(); } else { emit fileModified(); } } /** * Update modification state from files. */ void Kid3Application::updateModified() { TaggedFileIterator it(m_fileProxyModelRootIndex); while (it.hasNext()) { TaggedFile* taggedFile = it.next(); if (taggedFile->isChanged()) { m_modified = true; return; } } m_modified = false; } /** * Import. * * @param tagMask tag mask * @param path path of file, "clipboard" for import from clipboard * @param fmtIdx index of format * * @return true if ok. */ bool Kid3Application::importTags(TrackData::TagVersion tagMask, const QString& path, int fmtIdx) { filesToTrackDataModel(ImportConfig::instance().m_importDest); QString text; if (path == QLatin1String("clipboard")) { QClipboard* cb = QApplication::clipboard(); text = cb->text(QClipboard::Clipboard); if (text.isNull()) text = cb->text(QClipboard::Selection); } else { QFile file(path); if (file.open(QIODevice::ReadOnly)) { text = QTextStream(&file).readAll(); file.close(); } } if (!text.isNull() && fmtIdx < ImportConfig::instance().m_importFormatHeaders.size()) { TextImporter(getTrackDataModel()).updateTrackData( text, ImportConfig::instance().m_importFormatHeaders.at(fmtIdx), ImportConfig::instance().m_importFormatTracks.at(fmtIdx)); trackDataModelToFiles(tagMask); return true; } return false; } /** * Export. * * @param tagVersion tag version * @param path path of file, "clipboard" for export to clipboard * @param fmtIdx index of format * * @return true if ok. */ bool Kid3Application::exportTags(TrackData::TagVersion tagVersion, const QString& path, int fmtIdx) { ImportTrackDataVector trackDataVector; filesToTrackData(tagVersion, trackDataVector); m_textExporter->setTrackData(trackDataVector); m_textExporter->updateTextUsingConfig(fmtIdx); if (path == QLatin1String("clipboard")) { m_textExporter->exportToClipboard(); return true; } else { return m_textExporter->exportToFile(path); } } /** * Write playlist according to playlist configuration. * * @param cfg playlist configuration to use * * @return true if ok. */ bool Kid3Application::writePlaylist(const PlaylistConfig& cfg) { PlaylistCreator plCtr(getDirPath(), cfg); QItemSelectionModel* selectModel = getFileSelectionModel(); bool noSelection = !cfg.m_onlySelectedFiles || !selectModel || !selectModel->hasSelection(); bool ok = true; QModelIndex rootIndex; if (cfg.m_location == PlaylistConfig::PL_CurrentDirectory) { // Get first child of parent of current index. rootIndex = currentOrRootIndex(); if (rootIndex.model() && rootIndex.model()->rowCount(rootIndex) <= 0) rootIndex = rootIndex.parent(); if (const QAbstractItemModel* model = rootIndex.model()) { for (int row = 0; row < model->rowCount(rootIndex); ++row) { QModelIndex index = model->index(row, 0, rootIndex); PlaylistCreator::Item plItem(index, plCtr); if (plItem.isFile() && (noSelection || selectModel->isSelected(index))) { ok = plItem.add() && ok; } } } } else { QString selectedDirPrefix; rootIndex = getRootIndex(); ModelIterator it(rootIndex); while (it.hasNext()) { QModelIndex index = it.next(); PlaylistCreator::Item plItem(index, plCtr); bool inSelectedDir = false; if (plItem.isDir()) { if (!selectedDirPrefix.isEmpty()) { if (plItem.getDirName().startsWith(selectedDirPrefix)) { inSelectedDir = true; } else { selectedDirPrefix = QLatin1String(""); } } if (inSelectedDir || noSelection || selectModel->isSelected(index)) { // if directory is selected, all its files are selected if (!inSelectedDir) { selectedDirPrefix = plItem.getDirName(); } } } else if (plItem.isFile()) { QString dirName = plItem.getDirName(); if (!selectedDirPrefix.isEmpty()) { if (dirName.startsWith(selectedDirPrefix)) { inSelectedDir = true; } else { selectedDirPrefix = QLatin1String(""); } } if (inSelectedDir || noSelection || selectModel->isSelected(index)) { ok = plItem.add() && ok; } } } } ok = plCtr.write() && ok; return ok; } /** * Write playlist using current playlist configuration. * * @return true if ok. */ bool Kid3Application::writePlaylist() { return writePlaylist(PlaylistConfig::instance()); } /** * Set track data with tagged files of directory. * * @param tagVersion tag version * @param trackDataList is filled with track data */ void Kid3Application::filesToTrackData(TrackData::TagVersion tagVersion, ImportTrackDataVector& trackDataList) { TaggedFileOfDirectoryIterator it(currentOrRootIndex()); while (it.hasNext()) { TaggedFile* taggedFile = it.next(); taggedFile->readTags(false); taggedFile = FileProxyModel::readWithId3V24IfId3V24(taggedFile); trackDataList.push_back(ImportTrackData(*taggedFile, tagVersion)); } } /** * Set track data model with tagged files of directory. * * @param tagVersion tag version */ void Kid3Application::filesToTrackDataModel(TrackData::TagVersion tagVersion) { ImportTrackDataVector trackDataList; filesToTrackData(tagVersion, trackDataList); getTrackDataModel()->setTrackData(trackDataList); } /** * Set tagged files of directory from track data model. * * @param tagVersion tags to set */ void Kid3Application::trackDataModelToFiles(TrackData::TagVersion tagVersion) { ImportTrackDataVector trackDataList(getTrackDataModel()->getTrackData()); ImportTrackDataVector::iterator it = trackDataList.begin(); FrameFilter flt((tagVersion & TrackData::TagV1) ? frameModelV1()->getEnabledFrameFilter(true) : frameModelV2()->getEnabledFrameFilter(true)); TaggedFileOfDirectoryIterator tfit(currentOrRootIndex()); while (tfit.hasNext()) { TaggedFile* taggedFile = tfit.next(); taggedFile->readTags(false); if (it != trackDataList.end()) { it->removeDisabledFrames(flt); formatFramesIfEnabled(*it); if (tagVersion & TrackData::TagV1) taggedFile->setFramesV1(*it, false); if (tagVersion & TrackData::TagV2) { FrameCollection oldFrames; taggedFile->getAllFramesV2(oldFrames); it->markChangedFrames(oldFrames); taggedFile->setFramesV2(*it, true); } ++it; } else { break; } } if ((tagVersion & TrackData::TagV2) && flt.isEnabled(Frame::FT_Picture) && !trackDataList.getCoverArtUrl().isEmpty()) { downloadImage(trackDataList.getCoverArtUrl(), ImageForImportTrackData); } if (getFileSelectionModel()->hasSelection()) { emit selectedFilesUpdated(); } else { emit fileModified(); } } /** * Download an image file. * * @param url URL of image * @param dest specifies affected files */ void Kid3Application::downloadImage(const QString& url, DownloadImageDestination dest) { QString imgurl(DownloadClient::getImageUrl(url)); if (!imgurl.isEmpty()) { m_downloadImageDest = dest; m_downloadClient->startDownload(imgurl); } } /** * Perform a batch import for the selected directories. * @param profile batch import profile * @param tagVersion import destination tag versions */ void Kid3Application::batchImport(const BatchImportProfile& profile, TrackData::TagVersion tagVersion) { m_batchImportProfile = &profile; m_batchImportTagVersion = tagVersion; m_batchImportAlbums.clear(); m_batchImportTrackDataList.clear(); m_lastProcessedDirName.clear(); m_batchImporter->clearAborted(); m_batchImporter->emitReportImportEvent(BatchImportProfile::ReadingDirectory, QString()); // If no directories are selected, process files of the current directory. QList indexes; foreach (const QModelIndex& index, m_fileSelectionModel->selectedIndexes()) { if (m_fileProxyModel->isDir(index)) { indexes.append(index); } } if (indexes.isEmpty()) { indexes.append(m_fileProxyModelRootIndex); } connect(m_fileProxyModelIterator, SIGNAL(nextReady(QPersistentModelIndex)), this, SLOT(batchImportNextFile(QPersistentModelIndex))); m_fileProxyModelIterator->start(indexes); } /** * Apply single file to batch import. * * @param index index of file in file proxy model */ void Kid3Application::batchImportNextFile(const QPersistentModelIndex& index) { bool terminated = !index.isValid(); if (!terminated) { if (TaggedFile* taggedFile = FileProxyModel::getTaggedFileOfIndex(index)) { taggedFile->readTags(false); taggedFile = FileProxyModel::readWithId3V24IfId3V24(taggedFile); if (taggedFile->getDirname() != m_lastProcessedDirName) { m_lastProcessedDirName = taggedFile->getDirname(); if (!m_batchImportTrackDataList.isEmpty()) { m_batchImportAlbums.append(m_batchImportTrackDataList); } m_batchImportTrackDataList.clear(); if (m_batchImporter->isAborted()) { terminated = true; } } m_batchImportTrackDataList.append(ImportTrackData(*taggedFile, m_batchImportTagVersion)); } } if (terminated) { m_fileProxyModelIterator->abort(); disconnect(m_fileProxyModelIterator, SIGNAL(nextReady(QPersistentModelIndex)), this, SLOT(batchImportNextFile(QPersistentModelIndex))); if (!m_batchImporter->isAborted()) { if (!m_batchImportTrackDataList.isEmpty()) { m_batchImportAlbums.append(m_batchImportTrackDataList); } m_batchImporter->setFrameFilter( (m_batchImportTagVersion & TrackData::TagV1) != 0 ? frameModelV1()->getEnabledFrameFilter(true) : frameModelV2()->getEnabledFrameFilter(true)); m_batchImporter->start(m_batchImportAlbums, *m_batchImportProfile, m_batchImportTagVersion); } } } /** * Format a filename if format while editing is switched on. * * @param taggedFile file to modify */ void Kid3Application::formatFileNameIfEnabled(TaggedFile* taggedFile) const { if (FilenameFormatConfig::instance().m_formatWhileEditing) { QString fn(taggedFile->getFilename()); FilenameFormatConfig::instance().formatString(fn); taggedFile->setFilename(fn); } } /** * Format frames if format while editing is switched on. * * @param frames frames */ void Kid3Application::formatFramesIfEnabled(FrameCollection& frames) const { TagFormatConfig::instance().formatFramesIfEnabled(frames); } /** * Get name of selected file. * * @return absolute file name, ends with "/" if it is a directory. */ QString Kid3Application::getFileNameOfSelectedFile() { QModelIndex index = getFileSelectionModel()->currentIndex(); QString dirname = FileProxyModel::getPathIfIndexOfDir(index); if (!dirname.isNull()) { if (!dirname.endsWith(QLatin1Char('/'))) dirname += QLatin1Char('/'); return dirname; } else if (TaggedFile* taggedFile = FileProxyModel::getTaggedFileOfIndex(index)) { return taggedFile->getAbsFilename(); } return QLatin1String(""); } /** * Set name of selected file. * Exactly one file has to be selected. * * @param name file name. */ void Kid3Application::setFileNameOfSelectedFile(const QString& name) { if (TaggedFile* taggedFile = getSelectedFile()) { QFileInfo fi(name); taggedFile->setFilename(fi.fileName()); emit selectedFilesUpdated(); } } /** * Apply filename format. */ void Kid3Application::applyFilenameFormat() { emit fileSelectionUpdateRequested(); SelectedTaggedFileIterator it(getRootIndex(), getFileSelectionModel(), true); while (it.hasNext()) { TaggedFile* taggedFile = it.next(); taggedFile->readTags(false); QString fn = taggedFile->getFilename(); FilenameFormatConfig::instance().formatString(fn); taggedFile->setFilename(fn); } emit selectedFilesUpdated(); } /** * Apply ID3 format. */ void Kid3Application::applyId3Format() { emit fileSelectionUpdateRequested(); FrameCollection frames; FrameFilter fltV1(frameModelV1()->getEnabledFrameFilter(true)); FrameFilter fltV2(frameModelV2()->getEnabledFrameFilter(true)); SelectedTaggedFileIterator it(getRootIndex(), getFileSelectionModel(), true); while (it.hasNext()) { TaggedFile* taggedFile = it.next(); taggedFile->readTags(false); taggedFile->getAllFramesV1(frames); frames.removeDisabledFrames(fltV1); TagFormatConfig::instance().formatFrames(frames); taggedFile->setFramesV1(frames); taggedFile->getAllFramesV2(frames); frames.removeDisabledFrames(fltV2); TagFormatConfig::instance().formatFrames(frames); taggedFile->setFramesV2(frames); } emit selectedFilesUpdated(); } /** * Apply text encoding. * Set the text encoding selected in the settings Tags/ID3v2/Text encoding * for all selected files which have an ID3v2 tag. */ void Kid3Application::applyTextEncoding() { emit fileSelectionUpdateRequested(); Frame::Field::TextEncoding encoding; switch (TagConfig::instance().textEncoding()) { case TagConfig::TE_UTF16: encoding = Frame::Field::TE_UTF16; break; case TagConfig::TE_UTF8: encoding = Frame::Field::TE_UTF8; break; case TagConfig::TE_ISO8859_1: default: encoding = Frame::Field::TE_ISO8859_1; } FrameCollection frames; SelectedTaggedFileIterator it(getRootIndex(), getFileSelectionModel(), true); while (it.hasNext()) { TaggedFile* taggedFile = it.next(); taggedFile->readTags(false); taggedFile->getAllFramesV2(frames); for (FrameCollection::iterator frameIt = frames.begin(); frameIt != frames.end(); ++frameIt) { Frame& frame = const_cast(*frameIt); Frame::Field::TextEncoding enc = encoding; if (taggedFile->getTagFormatV2() == QLatin1String("ID3v2.3.0")) { // TagLib sets the ID3v2.3.0 frame containing the date internally with // ISO-8859-1, so the encoding cannot be set for such frames. if (taggedFile->taggedFileKey() == QLatin1String("TaglibMetadata") && frame.getType() == Frame::FT_Date && enc != Frame::Field::TE_ISO8859_1) continue; // Only ISO-8859-1 and UTF16 are allowed for ID3v2.3.0. if (enc != Frame::Field::TE_ISO8859_1) enc = Frame::Field::TE_UTF16; } Frame::FieldList& fields = frame.fieldList(); for (Frame::FieldList::iterator fieldIt = fields.begin(); fieldIt != fields.end(); ++fieldIt) { if (fieldIt->m_id == Frame::Field::ID_TextEnc && fieldIt->m_value.toInt() != enc) { fieldIt->m_value = enc; frame.setValueChanged(); } } } taggedFile->setFramesV2(frames); } emit selectedFilesUpdated(); } /** * Copy tags 1 into copy buffer. */ void Kid3Application::copyTagsV1() { emit fileSelectionUpdateRequested(); m_copyTags = frameModelV1()->frames().copyEnabledFrames( frameModelV1()->getEnabledFrameFilter(true)); } /** * Copy tags 2 into copy buffer. */ void Kid3Application::copyTagsV2() { emit fileSelectionUpdateRequested(); m_copyTags = frameModelV2()->frames().copyEnabledFrames( frameModelV2()->getEnabledFrameFilter(true)); } /** * Copy tags into copy buffer. * * @param tagMask tag bit (1 for tag 1, 2 for tag 2) */ void Kid3Application::copyTags(TrackData::TagVersion tagMask) { if (tagMask & TrackData::TagV1) { copyTagsV1(); } else if (tagMask & TrackData::TagV2) { copyTagsV2(); } } /** * Paste from copy buffer to ID3v1 tags. */ void Kid3Application::pasteTagsV1() { emit fileSelectionUpdateRequested(); FrameCollection frames(m_copyTags.copyEnabledFrames( frameModelV1()->getEnabledFrameFilter(true))); formatFramesIfEnabled(frames); SelectedTaggedFileIterator it(getRootIndex(), getFileSelectionModel(), false); while (it.hasNext()) { it.next()->setFramesV1(frames, false); } emit selectedFilesUpdated(); } /** * Paste from copy buffer to ID3v2 tags. */ void Kid3Application::pasteTagsV2() { emit fileSelectionUpdateRequested(); FrameCollection frames(m_copyTags.copyEnabledFrames( frameModelV2()->getEnabledFrameFilter(true))); formatFramesIfEnabled(frames); SelectedTaggedFileIterator it(getRootIndex(), getFileSelectionModel(), false); while (it.hasNext()) { it.next()->setFramesV2(frames, false); } emit selectedFilesUpdated(); } /** * Paste from copy buffer to tags. * * @param tagMask tag bit (1 for tag 1, 2 for tag 2) */ void Kid3Application::pasteTags(TrackData::TagVersion tagMask) { if (tagMask & TrackData::TagV1) { pasteTagsV1(); } else if (tagMask & TrackData::TagV2) { pasteTagsV2(); } } /** * Copy ID3v1 tags to ID3v2 tags of selected files. */ void Kid3Application::copyV1ToV2() { emit fileSelectionUpdateRequested(); FrameCollection frames; FrameFilter flt(frameModelV2()->getEnabledFrameFilter(true)); SelectedTaggedFileIterator it(getRootIndex(), getFileSelectionModel(), false); while (it.hasNext()) { TaggedFile* taggedFile = it.next(); taggedFile->getAllFramesV1(frames); frames.removeDisabledFrames(flt); formatFramesIfEnabled(frames); taggedFile->setFramesV2(frames, false); } emit selectedFilesUpdated(); } /** * Copy ID3v2 tags to ID3v1 tags of selected files. */ void Kid3Application::copyV2ToV1() { emit fileSelectionUpdateRequested(); FrameCollection frames; FrameFilter flt(frameModelV1()->getEnabledFrameFilter(true)); SelectedTaggedFileIterator it(getRootIndex(), getFileSelectionModel(), false); while (it.hasNext()) { TaggedFile* taggedFile = it.next(); taggedFile->getAllFramesV2(frames); frames.removeDisabledFrames(flt); formatFramesIfEnabled(frames); taggedFile->setFramesV1(frames, false); } emit selectedFilesUpdated(); } /** * Set tag from other tag. * * @param tagMask tag bit (1 for tag 1, 2 for tag 2) */ void Kid3Application::copyToOtherTag(TrackData::TagVersion tagMask) { if (tagMask & TrackData::TagV1) { copyV2ToV1(); } else if (tagMask & TrackData::TagV2) { copyV1ToV2(); } } /** * Remove ID3v1 tags in selected files. */ void Kid3Application::removeTagsV1() { emit fileSelectionUpdateRequested(); FrameFilter flt(frameModelV1()->getEnabledFrameFilter(true)); SelectedTaggedFileIterator it(getRootIndex(), getFileSelectionModel(), false); while (it.hasNext()) { it.next()->deleteFramesV1(flt); } emit selectedFilesUpdated(); } /** * Remove ID3v2 tags in selected files. */ void Kid3Application::removeTagsV2() { emit fileSelectionUpdateRequested(); FrameFilter flt(frameModelV2()->getEnabledFrameFilter(true)); SelectedTaggedFileIterator it(getRootIndex(), getFileSelectionModel(), false); while (it.hasNext()) { it.next()->deleteFramesV2(flt); } emit selectedFilesUpdated(); } /** * Remove tags in selected files. * * @param tagMask tag bit (1 for tag 1, 2 for tag 2) */ void Kid3Application::removeTags(TrackData::TagVersion tagMask) { if (tagMask & TrackData::TagV1) { removeTagsV1(); } else if (tagMask & TrackData::TagV2) { removeTagsV2(); } } /** * Set ID3v1 tags according to filename. * If a single file is selected the tags in the GUI controls * are set, else the tags in the multiple selected files. */ void Kid3Application::getTagsFromFilenameV1() { emit fileSelectionUpdateRequested(); FrameCollection frames; QItemSelectionModel* selectModel = getFileSelectionModel(); SelectedTaggedFileIterator it(getRootIndex(), selectModel, false); FrameFilter flt(frameModelV1()->getEnabledFrameFilter(true)); while (it.hasNext()) { TaggedFile* taggedFile = it.next(); taggedFile->getAllFramesV1(frames); taggedFile->getTagsFromFilename(frames, m_filenameToTagsFormat); frames.removeDisabledFrames(flt); formatFramesIfEnabled(frames); taggedFile->setFramesV1(frames); } emit selectedFilesUpdated(); } /** * Set ID3v2 tags according to filename. * If a single file is selected the tags in the GUI controls * are set, else the tags in the multiple selected files. */ void Kid3Application::getTagsFromFilenameV2() { emit fileSelectionUpdateRequested(); FrameCollection frames; QItemSelectionModel* selectModel = getFileSelectionModel(); SelectedTaggedFileIterator it(getRootIndex(), selectModel, false); FrameFilter flt(frameModelV2()->getEnabledFrameFilter(true)); while (it.hasNext()) { TaggedFile* taggedFile = it.next(); taggedFile->getAllFramesV2(frames); taggedFile->getTagsFromFilename(frames, m_filenameToTagsFormat); frames.removeDisabledFrames(flt); formatFramesIfEnabled(frames); taggedFile->setFramesV2(frames); } emit selectedFilesUpdated(); } /** * Set tags according to filename. * * @param tagMask tag bit (1 for tag 1, 2 for tag 2) */ void Kid3Application::getTagsFromFilename(TrackData::TagVersion tagMask) { if (tagMask & TrackData::TagV1) { getTagsFromFilenameV1(); } else if (tagMask & TrackData::TagV2) { getTagsFromFilenameV2(); } } /** * Set filename according to tags. * If a single file is selected the tags in the GUI controls * are used, else the tags in the multiple selected files. * * @param tagVersion tag version */ void Kid3Application::getFilenameFromTags(TrackData::TagVersion tagVersion) { emit fileSelectionUpdateRequested(); QItemSelectionModel* selectModel = getFileSelectionModel(); SelectedTaggedFileIterator it(getRootIndex(), selectModel, false); while (it.hasNext()) { TaggedFile* taggedFile = it.next(); TrackData trackData(*taggedFile, tagVersion); if (!trackData.isEmptyOrInactive()) { taggedFile->setFilename( trackData.formatFilenameFromTags(m_tagsToFilenameFormat)); formatFileNameIfEnabled(taggedFile); } } emit selectedFilesUpdated(); } /** * Set format used to generate filename from tags. * When changed, filenameToTagsFormatChanged() is emitted. * @param format format */ void Kid3Application::setFilenameToTagsFormat(const QString& format) { if (m_filenameToTagsFormat != format) { m_filenameToTagsFormat = format; emit filenameToTagsFormatChanged(format); } } /** * Set format used to generate filename from tags without emitting * filenameToTagsFormatChanged() signal. * This has to be used when connected from the GUI to avoid that the GUI * is updated because of its own changes. * @param format format */ void Kid3Application::setFilenameToTagsFormatWithoutSignaling( const QString& format) { m_filenameToTagsFormat = format; } /** * Set format used to generate tags from filename. * When changed, tagsToFilenameFormatChanged() is emitted. * @param format format */ void Kid3Application::setTagsToFilenameFormat(const QString& format) { if (m_tagsToFilenameFormat != format) { m_tagsToFilenameFormat = format; emit tagsToFilenameFormatChanged(format); } } /** * Set format used to generate tags from filename without emitting * tagsToFilenameFormatChanged() signal. * This has to be used when connected from the GUI to avoid that the GUI * is updated because of its own changes. * @param format format */ void Kid3Application::setTagsToFilenameFormatWithoutSignaling( const QString& format) { m_tagsToFilenameFormat = format; } /** * Get the selected file. * * @return the selected file, * 0 if not exactly one file is selected */ TaggedFile* Kid3Application::getSelectedFile() { QModelIndexList selItems( m_fileSelectionModel->selectedIndexes()); if (selItems.size() != 1) return 0; return FileProxyModel::getTaggedFileOfIndex(selItems.first()); } /** * Edit selected frame. * * @param frameEditor editor for frame fields */ void Kid3Application::editFrame(IFrameEditor* frameEditor) { emit fileSelectionUpdateRequested(); TaggedFile* taggedFile = getSelectedFile(); if (const Frame* selectedFrame = frameModelV2()->getFrameOfIndex( getFramesV2SelectionModel()->currentIndex())) { Frame frame(*selectedFrame); if (taggedFile && frameEditor->editFrameOfTaggedFile(&frame, taggedFile)) { emit frameModified(taggedFile); } else if (!taggedFile) { // multiple files selected // Get the first selected file by using a temporary iterator. taggedFile = SelectedTaggedFileIterator( getRootIndex(), getFileSelectionModel(), false).peekNext(); if (taggedFile) { m_framelist->setTaggedFile(taggedFile); QString name = m_framelist->getSelectedName(); if (!name.isEmpty() && frameEditor->editFrameOfTaggedFile(&frame, taggedFile)) { m_framelist->setFrame(frame); // Start a new iteration because the file selection model can be // changed by editFrameOfTaggedFile(), e.g. when a file is exported // from a picture frame. SelectedTaggedFileIterator tfit(getRootIndex(), getFileSelectionModel(), false); while (tfit.hasNext()) { TaggedFile* currentFile = tfit.next(); FrameCollection frames; currentFile->getAllFramesV2(frames); for (FrameCollection::const_iterator it = frames.begin(); it != frames.end(); ++it) { if (it->getName() == name) { currentFile->deleteFrameV2(*it); m_framelist->setTaggedFile(currentFile); m_framelist->pasteFrame(); break; } } } } } emit selectedFilesUpdated(); } } } /** * Delete selected frame. * * @param frameName name of frame to delete, empty to delete selected frame */ void Kid3Application::deleteFrame(const QString& frameName) { emit fileSelectionUpdateRequested(); TaggedFile* taggedFile = getSelectedFile(); if (taggedFile && frameName.isEmpty()) { // delete selected frame from single file if (!m_framelist->deleteFrame()) { // frame not found return; } emit frameModified(taggedFile); } else { // multiple files selected or frame name specified bool firstFile = true; QString name; SelectedTaggedFileIterator tfit(getRootIndex(), getFileSelectionModel(), false); while (tfit.hasNext()) { TaggedFile* currentFile = tfit.next(); if (firstFile) { firstFile = false; taggedFile = currentFile; m_framelist->setTaggedFile(taggedFile); name = frameName.isEmpty() ? m_framelist->getSelectedName() : frameName; } FrameCollection frames; currentFile->getAllFramesV2(frames); for (FrameCollection::const_iterator it = frames.begin(); it != frames.end(); ++it) { if (it->getName() == name) { currentFile->deleteFrameV2(*it); break; } } } emit selectedFilesUpdated(); } } /** * Let the user select and edit a frame type and then edit the frame. * Add the frame if the edits are accepted. * * @param frameEditor frame editor * * @return true if edits accepted. */ bool Kid3Application::selectAddAndEditFrame(IFrameEditor* frameEditor) { if (TaggedFile* taggedFile = m_framelist->getTaggedFile()) { Frame frame; if (frameEditor->selectFrame(&frame, taggedFile)) { m_framelist->setFrame(frame); return m_framelist->addAndEditFrame(frameEditor); } } return false; } /** * Select a frame type and add such a frame to frame list. * * @param frame frame to add, if 0 the user has to select and edit the frame * @param frameEditor editor for frame fields, if not null and a frame * is set, the user can edit the frame before it is added */ void Kid3Application::addFrame(const Frame* frame, IFrameEditor* frameEditor) { emit fileSelectionUpdateRequested(); TaggedFile* taggedFile = getSelectedFile(); if (taggedFile) { bool frameAdded; if (!frame) { frameAdded = selectAddAndEditFrame(frameEditor); } else if (frameEditor) { m_framelist->setFrame(*frame); frameAdded = m_framelist->addAndEditFrame(frameEditor); } else { m_framelist->setFrame(*frame); frameAdded = m_framelist->pasteFrame(); } if (frameAdded) { emit frameModified(taggedFile); if (m_framelist->isPictureFrame()) { // update preview picture emit selectedFilesUpdated(); } } } else { // multiple files selected bool firstFile = true; int frameId = -1; SelectedTaggedFileIterator tfit(getRootIndex(), getFileSelectionModel(), false); while (tfit.hasNext()) { TaggedFile* currentFile = tfit.next(); if (firstFile) { firstFile = false; taggedFile = currentFile; m_framelist->setTaggedFile(currentFile); if (!frame) { if (selectAddAndEditFrame(frameEditor)) { frameId = m_framelist->getSelectedId(); } else { break; } } else if (frameEditor) { m_framelist->setFrame(*frame); if (m_framelist->addAndEditFrame(frameEditor)) { frameId = m_framelist->getSelectedId(); } else { break; } } else { m_framelist->setFrame(*frame); if (m_framelist->pasteFrame()) { frameId = m_framelist->getSelectedId(); } else { break; } } } else { m_framelist->setTaggedFile(currentFile); m_framelist->pasteFrame(); } } m_framelist->setTaggedFile(taggedFile); if (frameId != -1) { m_framelist->setSelectedId(frameId); } emit selectedFilesUpdated(); } } /** * Edit a picture frame if one exists or add a new one. * * @param frameEditor editor for frame fields */ void Kid3Application::editOrAddPicture(IFrameEditor* frameEditor) { if (m_framelist->selectByName(QLatin1String("Picture"))) { editFrame(frameEditor); } else { PictureFrame frame; addFrame(&frame, frameEditor); } } /** * Open directory or add pictures on drop. * * @param paths paths of directories or files in directory */ void Kid3Application::openDrop(const QStringList& paths) { QStringList filePaths; QStringList picturePaths; foreach (QString txt, paths) { int lfPos = txt.indexOf(QLatin1Char('\n')); if (lfPos > 0 && lfPos < static_cast(txt.length()) - 1) { txt.truncate(lfPos + 1); } QUrl url(txt); if (!url.path().isEmpty()) { #ifdef Q_OS_WIN32 QString dir = url.toString(); #else QString dir = url.path().trimmed(); #endif if (dir.endsWith(QLatin1String(".jpg"), Qt::CaseInsensitive) || dir.endsWith(QLatin1String(".jpeg"), Qt::CaseInsensitive) || dir.endsWith(QLatin1String(".png"), Qt::CaseInsensitive)) { picturePaths.append(dir); } else { filePaths.append(dir); } } } if (!filePaths.isEmpty()) { emit fileSelectionUpdateRequested(); emit confirmedOpenDirectoryRequested(filePaths); } else if (!picturePaths.isEmpty()) { foreach (const QString& picturePath, picturePaths) { PictureFrame frame; if (PictureFrame::setDataFromFile(frame, picturePath)) { QString fileName(picturePath); int slashPos = fileName.lastIndexOf(QLatin1Char('/')); if (slashPos != -1) { fileName = fileName.mid(slashPos + 1); } PictureFrame::setMimeTypeFromFileName(frame, fileName); PictureFrame::setDescription(frame, fileName); addFrame(&frame); emit selectedFilesUpdated(); } } } } /** * Add picture on drop. * * @param image dropped image. */ void Kid3Application::dropImage(const QImage& image) { if (!image.isNull()) { PictureFrame frame; if (PictureFrame::setDataFromImage(frame, image)) { addFrame(&frame); emit selectedFilesUpdated(); } } } /** * Handle URL on drop. * * @param txt dropped URL. */ void Kid3Application::dropUrl(const QString& txt) { downloadImage(txt, Kid3Application::ImageForSelectedFiles); } /** * Add a downloaded image. * * @param data HTTP response of download * @param mimeType MIME type of data * @param url URL of downloaded data */ void Kid3Application::imageDownloaded(const QByteArray& data, const QString& mimeType, const QString& url) { if (mimeType.startsWith(QLatin1String("image"))) { PictureFrame frame(data, url, PictureFrame::PT_CoverFront, mimeType); if (getDownloadImageDestination() == ImageForAllFilesInDirectory) { TaggedFileOfDirectoryIterator it(currentOrRootIndex()); while (it.hasNext()) { TaggedFile* taggedFile = it.next(); taggedFile->readTags(false); taggedFile->addFrameV2(frame); } } else if (getDownloadImageDestination() == ImageForImportTrackData) { const ImportTrackDataVector& trackDataVector( getTrackDataModel()->trackData()); for (ImportTrackDataVector::const_iterator it = trackDataVector.constBegin(); it != trackDataVector.constEnd(); ++it) { TaggedFile* taggedFile; if (it->isEnabled() && (taggedFile = it->getTaggedFile()) != 0) { taggedFile->readTags(false); taggedFile->addFrameV2(frame); } } } else { addFrame(&frame); } emit selectedFilesUpdated(); } } /** * Set the first file as the current file. * * @param select true to select the file * * @return true if a file exists. */ bool Kid3Application::firstFile(bool select) { m_fileSelectionModel->setCurrentIndex(getRootIndex(), QItemSelectionModel::NoUpdate); return nextFile(select); } /** * Set the next file as the current file. * * @param select true to select the file * * @return true if a next file exists. */ bool Kid3Application::nextFile(bool select) { QModelIndex current(m_fileSelectionModel->currentIndex()), next; if (m_fileProxyModel->rowCount(current) > 0) { // to first child next = m_fileProxyModel->index(0, 0, current); } else { QModelIndex parent = current; while (!next.isValid() && parent.isValid()) { // to next sibling or next sibling of parent int row = parent.row(); if (parent == getRootIndex()) { // do not move beyond root index return false; } parent = parent.parent(); if (row + 1 < m_fileProxyModel->rowCount(parent)) { // to next sibling next = m_fileProxyModel->index(row + 1, 0, parent); } } } if (!next.isValid()) return false; m_fileSelectionModel->setCurrentIndex(next, select ? QItemSelectionModel::ClearAndSelect : QItemSelectionModel::Current); return true; } /** * Set the previous file as the current file. * * @param select true to select the file * * @return true if a previous file exists. */ bool Kid3Application::previousFile(bool select) { QModelIndex current(m_fileSelectionModel->currentIndex()), previous; int row = current.row() - 1; if (row >= 0) { // to last leafnode of previous sibling previous = current.sibling(row, 0); row = m_fileProxyModel->rowCount(previous) - 1; while (row >= 0) { previous = m_fileProxyModel->index(row, 0, previous); row = m_fileProxyModel->rowCount(previous) - 1; } } else { // to parent previous = current.parent(); } if (!previous.isValid() || previous == getRootIndex()) return false; m_fileSelectionModel->setCurrentIndex(previous, select ? QItemSelectionModel::ClearAndSelect : QItemSelectionModel::Current); return true; } /** * Select or deselect the current file. * * @param select true to select the file, false to deselect it * * @return true if a current file exists. */ bool Kid3Application::selectCurrentFile(bool select) { QModelIndex currentIdx(m_fileSelectionModel->currentIndex()); if (!currentIdx.isValid() || currentIdx == getRootIndex()) return false; m_fileSelectionModel->setCurrentIndex(currentIdx, select ? QItemSelectionModel::Select : QItemSelectionModel::Deselect); return true; } /** * Select all files. */ void Kid3Application::selectAllFiles() { QItemSelection selection; ModelIterator it(m_fileProxyModelRootIndex); while (it.hasNext()) { selection.append(QItemSelectionRange(it.next())); } m_fileSelectionModel->select(selection, QItemSelectionModel::Select); } /** * Deselect all files. */ void Kid3Application::deselectAllFiles() { m_fileSelectionModel->clearSelection(); } /** * Fetch entries of directory if not already fetched. * This works like FileList::expand(), but without expanding tree view * items and independent of the GUI. The processing is done in the background * by QFileSystemModel, so the fetched items are not immediately available * after calling this method. * * @param index index of directory item */ void Kid3Application::fetchDirectory(const QModelIndex& index) { if (index.isValid() && m_fileProxyModel->canFetchMore(index)) { m_fileProxyModel->fetchMore(index); } } /** * Process change of selection. * The GUI is signaled to update the current selection and the controls. */ void Kid3Application::fileSelected() { emit fileSelectionUpdateRequested(); emit selectedFilesUpdated(); } /** * Schedule actions to rename a directory. * When finished renameActionsScheduled() is emitted. */ void Kid3Application::scheduleRenameActions() { m_dirRenamer->clearActions(); m_dirRenamer->clearAborted(); // If directories are selected, rename them, else process files of the // current directory. QList indexes; foreach (const QModelIndex& index, m_fileSelectionModel->selectedIndexes()) { if (m_fileProxyModel->isDir(index)) { indexes.append(index); } } if (indexes.isEmpty()) { indexes.append(m_fileProxyModelRootIndex); } connect(m_fileProxyModelIterator, SIGNAL(nextReady(QPersistentModelIndex)), this, SLOT(scheduleNextRenameAction(QPersistentModelIndex))); m_fileProxyModelIterator->start(indexes); } /** * Schedule rename action for a file. * * @param index index of file in file proxy model */ void Kid3Application::scheduleNextRenameAction(const QPersistentModelIndex& index) { bool terminated = !index.isValid(); if (!terminated) { if (TaggedFile* taggedFile = FileProxyModel::getTaggedFileOfIndex(index)) { taggedFile->readTags(false); taggedFile = FileProxyModel::readWithId3V24IfId3V24(taggedFile); m_dirRenamer->scheduleAction(taggedFile); if (m_dirRenamer->isAborted()) { terminated = true; } } } if (terminated) { m_fileProxyModelIterator->abort(); disconnect(m_fileProxyModelIterator, SIGNAL(nextReady(QPersistentModelIndex)), this, SLOT(scheduleNextRenameAction(QPersistentModelIndex))); emit renameActionsScheduled(); } } /** * Apply a file filter. * * @param fileFilter filter to apply. */ void Kid3Application::applyFilter(FileFilter& fileFilter) { m_fileProxyModel->disableFilteringOutIndexes(); setFiltered(false); fileFilter.clearAborted(); emit fileFiltered(FileFilter::Started, QString()); m_fileFilter = &fileFilter; m_lastProcessedDirName.clear(); connect(m_fileProxyModelIterator, SIGNAL(nextReady(QPersistentModelIndex)), this, SLOT(filterNextFile(QPersistentModelIndex))); m_fileProxyModelIterator->start(m_fileProxyModelRootIndex); } /** * Apply single file to file filter. * * @param index index of file in file proxy model */ void Kid3Application::filterNextFile(const QPersistentModelIndex& index) { if (!m_fileFilter) return; bool terminated = !index.isValid(); if (!terminated) { if (TaggedFile* taggedFile = FileProxyModel::getTaggedFileOfIndex(index)) { taggedFile->readTags(false); taggedFile = FileProxyModel::readWithId3V24IfId3V24(taggedFile); if (taggedFile->getDirname() != m_lastProcessedDirName) { m_lastProcessedDirName = taggedFile->getDirname(); emit fileFiltered(FileFilter::Directory, m_lastProcessedDirName); } bool ok; bool pass = m_fileFilter->filter(*taggedFile, &ok); if (ok) { emit fileFiltered( pass ? FileFilter::FilePassed : FileFilter::FileFilteredOut, taggedFile->getFilename()); if (!pass) m_fileProxyModel->filterOutIndex(taggedFile->getIndex()); } else { emit fileFiltered(FileFilter::ParseError, QString()); terminated = true; } if (m_fileFilter->isAborted()) { terminated = true; emit fileFiltered(FileFilter::Aborted, QString()); } } } if (terminated) { if (!m_fileFilter->isAborted()) { emit fileFiltered(FileFilter::Finished, QString()); } m_fileProxyModelIterator->abort(); m_fileProxyModel->applyFilteringOutIndexes(); setFiltered(!m_fileFilter->isEmptyFilterExpression()); emit fileModified(); disconnect(m_fileProxyModelIterator, SIGNAL(nextReady(QPersistentModelIndex)), this, SLOT(filterNextFile(QPersistentModelIndex))); } } /** * Apply a file filter. * * @param expression filter expression */ void Kid3Application::applyFilter(const QString& expression) { if (!m_expressionFileFilter) { m_expressionFileFilter = new FileFilter(this); } m_expressionFileFilter->clearAborted(); m_expressionFileFilter->setFilterExpression(expression); m_expressionFileFilter->initParser(); applyFilter(*m_expressionFileFilter); } /** * Perform rename actions and change application directory afterwards if it * was renamed. * * @return error messages, null string if no error occurred. */ QString Kid3Application::performRenameActions() { QString errorMsg; m_dirRenamer->setDirName(getDirName()); m_dirRenamer->performActions(&errorMsg); if (m_dirRenamer->getDirName() != getDirName()) { openDirectory(QStringList() << m_dirRenamer->getDirName()); } return errorMsg; } /** * Set the directory name from the tags. * The directory must not have modified files. * renameActionsScheduled() is emitted when the rename actions have been * scheduled. Then performRenameActions() has to be called to effectively * rename the directory. * * @param tagMask tag mask * @param format directory name format * @param create true to create, false to rename * * @return true if ok. */ bool Kid3Application::renameDirectory(TrackData::TagVersion tagMask, const QString& format, bool create) { TaggedFile* taggedFile = TaggedFileOfDirectoryIterator::first(currentOrRootIndex()); if (!isModified() && taggedFile) { m_dirRenamer->setTagVersion(tagMask); m_dirRenamer->setFormat(format); m_dirRenamer->setAction(create); scheduleRenameActions(); return true; } return false; } /** * Number tracks in selected files of directory. * * @param nr start number * @param total total number of tracks, used if >0 * @param tagVersion determines on which tags the numbers are set */ void Kid3Application::numberTracks(int nr, int total, TrackData::TagVersion tagVersion) { emit fileSelectionUpdateRequested(); int numDigits = TagConfig::instance().trackNumberDigits(); if (numDigits < 1 || numDigits > 5) numDigits = 1; // If directories are selected, number their files, else process the selected // files of the current directory. AbstractTaggedFileIterator* it = new TaggedFileOfSelectedDirectoriesIterator(getFileSelectionModel()); if (!it->hasNext()) { delete it; it = new SelectedTaggedFileOfDirectoryIterator( currentOrRootIndex(), getFileSelectionModel(), true); } while (it->hasNext()) { TaggedFile* taggedFile = it->next(); taggedFile->readTags(false); if (tagVersion & TrackData::TagV1) { int oldnr = taggedFile->getTrackNumV1(); if (nr != oldnr) { taggedFile->setTrackNumV1(nr); } } if (tagVersion & TrackData::TagV2) { // For tag 2 the frame is written, so that we have control over the // format and the total number of tracks, and it is possible to change // the format even if the numbers stay the same. QString value; if (total > 0) { value.sprintf("%0*d/%0*d", numDigits, nr, numDigits, total); } else { value.sprintf("%0*d", numDigits, nr); } FrameCollection frames; taggedFile->getAllFramesV2(frames); Frame frame(Frame::FT_Track, QLatin1String(""), QLatin1String(""), -1); FrameCollection::const_iterator it = frames.find(frame); if (it != frames.end()) { frame = *it; frame.setValueIfChanged(value); if (frame.isValueChanged()) { taggedFile->setFrameV2(frame); } } else { frame.setValue(value); frame.setExtendedType(Frame::ExtendedType(Frame::FT_Track)); taggedFile->setFrameV2(frame); } } ++nr; } emit selectedFilesUpdated(); delete it; } #if defined HAVE_PHONON || QT_VERSION >= 0x050000 /** * Play audio file. */ void Kid3Application::playAudio() { QStringList files; int fileNr = 0; if (m_fileSelectionModel->selectedIndexes().size() > 1) { // play only the selected files if more than one is selected SelectedTaggedFileIterator it(m_fileProxyModelRootIndex, m_fileSelectionModel, false); while (it.hasNext()) { files.append(it.next()->getAbsFilename()); } } else { // play all files if none or only one is selected int idx = 0; ModelIterator it(m_fileProxyModelRootIndex); while (it.hasNext()) { QModelIndex index = it.next(); if (TaggedFile* taggedFile = FileProxyModel::getTaggedFileOfIndex(index)) { files.append(taggedFile->getAbsFilename()); if (m_fileSelectionModel->isSelected(index)) { fileNr = idx; } ++idx; } } } emit aboutToPlayAudio(); getAudioPlayer()->setFiles(files, fileNr); } #endif /** * Get number of tracks in current directory. * * @return number of tracks, 0 if not found. */ int Kid3Application::getTotalNumberOfTracksInDir() { if (TaggedFile* taggedFile = TaggedFileOfDirectoryIterator::first( currentOrRootIndex())) { return taggedFile->getTotalNumberOfTracksInDir(); } return 0; } /** * Create a filter string for the file dialog. * The filter string contains entries for all supported types. * * @return filter string. */ QString Kid3Application::createFilterString() const { QStringList extensions; foreach (ITaggedFileFactory* factory, FileProxyModel::taggedFileFactories()) { foreach (const QString& key, factory->taggedFileKeys()) { extensions.append(factory->supportedFileExtensions(key)); } } // remove duplicates extensions.sort(); QString lastExt(QLatin1String("")); for (QStringList::iterator it = extensions.begin(); it != extensions.end();) { if (*it == lastExt) { it = extensions.erase(it); } else { lastExt = *it; ++it; } } QString allPatterns; QList > nameFilters; for (QStringList::const_iterator it = extensions.begin(); it != extensions.end(); ++it) { QString text = (*it).mid(1).toUpper(); QString pattern = QLatin1Char('*') + *it; if (!allPatterns.isEmpty()) { allPatterns += QLatin1Char(' '); } allPatterns += pattern; nameFilters.append(qMakePair(text, pattern)); } if (!allPatterns.isEmpty()) { nameFilters.prepend(qMakePair(tr("All Supported Files"), allPatterns)); } nameFilters.append(qMakePair(tr("All Files"), QString(QLatin1Char('*')))); return m_platformTools->fileDialogNameFilter(nameFilters); } /** * Notify the tagged file factories about the changed configuration. */ void Kid3Application::notifyConfigurationChange() { foreach (ITaggedFileFactory* factory, FileProxyModel::taggedFileFactories()) { foreach (const QString& key, factory->taggedFileKeys()) { factory->notifyConfigurationChange(key); } } } /** * Convert ID3v2.3 to ID3v2.4 tags. */ void Kid3Application::convertToId3v24() { emit fileSelectionUpdateRequested(); SelectedTaggedFileIterator it(getRootIndex(), getFileSelectionModel(), false); while (it.hasNext()) { TaggedFile* taggedFile = it.next(); taggedFile->readTags(false); if (taggedFile->hasTagV2() && !taggedFile->isChanged()) { QString tagFmt = taggedFile->getTagFormatV2(); if (tagFmt.length() >= 7 && tagFmt.startsWith(QLatin1String("ID3v2.")) && tagFmt[6] < QLatin1Char('4')) { if ((taggedFile->taggedFileFeatures() & (TaggedFile::TF_ID3v23 | TaggedFile::TF_ID3v24)) == TaggedFile::TF_ID3v23) { FrameCollection frames; taggedFile->getAllFramesV2(frames); FrameFilter flt; flt.enableAll(); taggedFile->deleteFramesV2(flt); // The file has to be reread to write ID3v2.4 tags taggedFile = FileProxyModel::readWithId3V24(taggedFile); // Restore the frames FrameFilter frameFlt; frameFlt.enableAll(); taggedFile->setFramesV2(frames.copyEnabledFrames(frameFlt), false); } // Write the file with ID3v2.4 tags bool renamed; int storedFeatures = taggedFile->activeTaggedFileFeatures(); taggedFile->setActiveTaggedFileFeatures(TaggedFile::TF_ID3v24); taggedFile->writeTags(true, &renamed, FileConfig::instance().m_preserveTime); taggedFile->setActiveTaggedFileFeatures(storedFeatures); taggedFile->readTags(true); } } } emit selectedFilesUpdated(); } /** * Convert ID3v2.4 to ID3v2.3 tags. */ void Kid3Application::convertToId3v23() { emit fileSelectionUpdateRequested(); SelectedTaggedFileIterator it(getRootIndex(), getFileSelectionModel(), false); while (it.hasNext()) { TaggedFile* taggedFile = it.next(); taggedFile->readTags(false); if (taggedFile->hasTagV2() && !taggedFile->isChanged()) { QString tagFmt = taggedFile->getTagFormatV2(); QString ext = taggedFile->getFileExtension(); if (tagFmt.length() >= 7 && tagFmt.startsWith(QLatin1String("ID3v2.")) && tagFmt[6] > QLatin1Char('3') && (ext == QLatin1String(".mp3") || ext == QLatin1String(".mp2") || ext == QLatin1String(".aac"))) { if (!(taggedFile->taggedFileFeatures() & TaggedFile::TF_ID3v23)) { FrameCollection frames; taggedFile->getAllFramesV2(frames); FrameFilter flt; flt.enableAll(); taggedFile->deleteFramesV2(flt); // The file has to be reread to write ID3v2.3 tags taggedFile = FileProxyModel::readWithId3V23(taggedFile); // Restore the frames FrameFilter frameFlt; frameFlt.enableAll(); taggedFile->setFramesV2(frames.copyEnabledFrames(frameFlt), false); } // Write the file with ID3v2.3 tags bool renamed; int storedFeatures = taggedFile->activeTaggedFileFeatures(); taggedFile->setActiveTaggedFileFeatures(TaggedFile::TF_ID3v23); taggedFile->writeTags(true, &renamed, FileConfig::instance().m_preserveTime); taggedFile->setActiveTaggedFileFeatures(storedFeatures); taggedFile->readTags(true); } } } emit selectedFilesUpdated(); } /** * Get value of frame. * To get binary data like a picture, the name of a file to write can be * added after the @a name, e.g. "Picture:/path/to/file". * * @param tagMask tag bit (1 for tag 1, 2 for tag 2) * @param name name of frame (e.g. "Artist") */ QString Kid3Application::getFrame(TrackData::TagVersion tagMask, const QString& name) { QString frameName(name); QString dataFileName; int colonIndex = frameName.indexOf(QLatin1Char(':')); if (colonIndex != -1) { dataFileName = frameName.mid(colonIndex + 1); frameName.truncate(colonIndex); } FrameTableModel* ft = (tagMask & TrackData::TagV2) ? m_framesV2Model : m_framesV1Model; FrameCollection::const_iterator it = ft->frames().findByName(frameName); if (it != ft->frames().end()) { if (!dataFileName.isEmpty()) { PictureFrame::writeDataToFile(*it, dataFileName); } return it->getValue(); } else { return QLatin1String(""); } } /** * Set value of frame. * For tag 2 (@a tagMask 2), if no frame with @a name exists, a new frame * is added, if @a value is empty, the frame is deleted. * To add binary data like a picture, a file can be added after the * @a name, e.g. "Picture:/path/to/file". * * @param tagMask tag bit (1 for tag 1, 2 for tag 2) * @param name name of frame (e.g. "Artist") * @param value value of frame */ bool Kid3Application::setFrame(TrackData::TagVersion tagMask, const QString& name, const QString& value) { QString frameName(name); QString dataFileName; int colonIndex = frameName.indexOf(QLatin1Char(':')); if (colonIndex != -1) { dataFileName = frameName.mid(colonIndex + 1); frameName.truncate(colonIndex); } FrameTableModel* ft = (tagMask & TrackData::TagV2) ? m_framesV2Model : m_framesV1Model; FrameCollection frames(ft->frames()); FrameCollection::const_iterator it = frames.findByName(frameName); if (it != frames.end()) { if (it->getType() == Frame::FT_Picture && !dataFileName.isEmpty() && (tagMask & 2) != 0) { deleteFrame(it->getName()); PictureFrame frame; PictureFrame::setDescription(frame, value); PictureFrame::setDataFromFile(frame, dataFileName); PictureFrame::setMimeTypeFromFileName(frame, dataFileName); addFrame(&frame); } else if (value.isEmpty() && (tagMask & 2) != 0) { deleteFrame(it->getName()); } else { Frame& frame = const_cast(*it); frame.setValueIfChanged(value); ft->transferFrames(frames); } return true; } else if (tagMask & 2) { Frame frame(Frame::ExtendedType(frameName), value, -1); if (frame.getType() == Frame::FT_Picture && !dataFileName.isEmpty()) { PictureFrame::setFields(frame); PictureFrame::setDescription(frame, value); PictureFrame::setDataFromFile(frame, dataFileName); PictureFrame::setMimeTypeFromFileName(frame, dataFileName); } addFrame(&frame); return true; } return false; } kid3-3.0.2/src/core/model/kid3application.h000066400000000000000000000675431224603511300204300ustar00rootroot00000000000000/** * \file kid3application.h * Kid3 application logic, independent of GUI. * * \b Project: Kid3 * \author Urs Fleisch * \date 10 Jul 2011 * * Copyright (C) 2011-2012 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KID3APPLICATION_H #define KID3APPLICATION_H #include #include #include "frame.h" #include "trackdata.h" #include "filefilter.h" #include "generalconfig.h" #include "config.h" class QFileSystemModel; class QItemSelectionModel; class QModelIndex; class QNetworkAccessManager; class QTimer; class FileProxyModel; class FileProxyModelIterator; class DirProxyModel; class TrackDataModel; class FrameTableModel; class ConfigStore; class PlaylistConfig; class DownloadClient; class TaggedFile; class FrameList; class IFrameEditor; class ServerImporter; class ServerTrackImporter; class ITaggedFileFactory; class TextExporter; class DirRenamer; class BatchImportProfile; class BatchImporter; class IAbortable; class ICorePlatformTools; #if defined HAVE_PHONON || QT_VERSION >= 0x050000 class AudioPlayer; #endif /** * Kid3 application logic, independent of GUI. */ class KID3_CORE_EXPORT Kid3Application : public QObject { Q_OBJECT public: /** Destination for downloadImage(). */ enum DownloadImageDestination { ImageForSelectedFiles, /**< only for current file */ ImageForAllFilesInDirectory, /**< for all files in directory */ ImageForImportTrackData /**< for enabled files in m_trackDataModel */ }; /** * Constructor. * @param platformTools platform tools * @param parent parent object */ explicit Kid3Application(ICorePlatformTools* platformTools, QObject* parent = 0); /** * Destructor. */ virtual ~Kid3Application(); /** * Get file system model. * @return file system model. */ QFileSystemModel* getFileSystemModel() { return m_fileSystemModel; } /** * Get file proxy model. * @return file proxy model. */ FileProxyModel* getFileProxyModel() { return m_fileProxyModel; } /** * Get file proxy model iterator. * @return file proxy model iterator. */ FileProxyModelIterator* getFileProxyModelIterator() { return m_fileProxyModelIterator; } /** * Get directory proxy model. * @return directory proxy model. */ DirProxyModel* getDirProxyModel() { return m_dirProxyModel; } /** * Get track data model. * @return track data model. */ TrackDataModel* getTrackDataModel() { return m_trackDataModel; } /** * Get selection model of files. */ QItemSelectionModel* getFileSelectionModel() { return m_fileSelectionModel; } /** * Get tag 1 frame table model. * @return frame table. */ FrameTableModel* frameModelV1() { return m_framesV1Model; } /** * Get tag 2 frame table model. * @return frame table. */ FrameTableModel* frameModelV2() { return m_framesV2Model; } /** * Get selection model of tag 1 frame table model. */ QItemSelectionModel* getFramesV1SelectionModel() { return m_framesV1SelectionModel; } /** * Get selection model of tag 2 frame table model. */ QItemSelectionModel* getFramesV2SelectionModel() { return m_framesV2SelectionModel; } /** * Get frame list. * @return frame list. */ FrameList* getFrameList() { return m_framelist; } /** * Get settings. * @return settings. */ ISettings* getSettings() const; /** * Get download client. * @return download client. */ DownloadClient* getDownloadClient() { return m_downloadClient; } /** * Get text exporter. * @return text exporter. */ TextExporter* getTextExporter() { return m_textExporter; } /** * Get available server importers. * @return list of server importers. */ QList getServerImporters() { return m_importers; } /** * Get available server track importers. * @return list of server track importers. */ QList getServerTrackImporters() { return m_trackImporters; } /** * Get directory renamer. * @return directory renamer. */ DirRenamer* getDirRenamer() { return m_dirRenamer; } /** * Get batch importer. * @return batch importer. */ BatchImporter* getBatchImporter() { return m_batchImporter; } #if defined HAVE_PHONON || QT_VERSION >= 0x050000 /** * Get audio player. * @return audio player. */ AudioPlayer* getAudioPlayer(); #endif /** * Get current index in file proxy model or root index if current index is * invalid. * @return current index, root index if not valid. */ QModelIndex currentOrRootIndex() const; /** * Save settings to the configuration. */ void saveConfig(); /** * Read settings from the configuration. */ void readConfig(); /** * Open directory. * When finished directoryOpened() is emitted, also if false is returned. * * @param paths file or directory paths, if multiple paths are given, the * common directory is opened and the files are selected * @param fileCheck if true, only open directory if paths exist * * @return true if ok. */ bool openDirectory(const QStringList& paths, bool fileCheck = false); /** * Get root index of opened directory in file proxy model. * @return index of directory root. */ QPersistentModelIndex getRootIndex() const { return m_fileProxyModelRootIndex; } /** * Get directory path of opened directory. * @return directory path. */ QString getDirPath() const; /** * Save all changed files. * saveStarted() and saveProgress() are emitted while saving files. * * @return list of files with error, empty if ok. */ QStringList saveDirectory(); /** * Update tags of selected files to contain contents of frame models. * * @param selItems list of selected file indexes */ void frameModelsToTags(const QList& selItems); /** * Update frame models to contain contents of selected files. * The properties starting with "selection" will be set by this method. * * @param selItems list of selected file indexes */ void tagsToFrameModels(const QList& selItems); /** * Check if a single file is selected. * @return if a single file is selected, this tagged file, else 0. */ const TaggedFile* selectionSingleFile() const { return m_selectionSingleFile; } /** * Number of selected files which support tag 1. * @return number of selected files which support tag 1. */ int selectionTagV1SupportedCount() const { return m_selectionTagV1SupportedCount; } /** * Number of selected files. * @return number of selected files which support tag 2. */ int selectionFileCount() const { return m_selectionFileCount; } /** * Check if any of the selected files has a tag 1. * @return true if any of the selected files has a tag 1. */ bool selectionHasTagV1() const { return m_selectionHasTagV1; } /** * Check if any of the selected files has a tag 2. * @return true if any of the selected files has a tag 2. */ bool selectionHasTagV2() const { return m_selectionHasTagV2; } /** * Import. * * @param tagMask tag mask * @param path path of file, "clipboard" for import from clipboard * @param fmtIdx index of format * * @return true if ok. */ bool importTags(TrackData::TagVersion tagMask, const QString& path, int fmtIdx); /** * Export. * * @param tagVersion tag version * @param path path of file, "clipboard" for export to clipboard * @param fmtIdx index of format * * @return true if ok. */ bool exportTags(TrackData::TagVersion tagVersion, const QString& path, int fmtIdx); /** * Write playlist according to playlist configuration. * * @param cfg playlist configuration to use * * @return true if ok. */ bool writePlaylist(const PlaylistConfig& cfg); /** * Write playlist using current playlist configuration. * * @return true if ok. */ bool writePlaylist(); /** * Perform rename actions and change application directory afterwards if it * was renamed. * * @return error messages, null string if no error occurred. */ QString performRenameActions(); /** * Set the directory name from the tags. * The directory must not have modified files. * renameActionsScheduled() is emitted when the rename actions have been * scheduled. Then performRenameActions() has to be called to effectively * rename the directory. * * @param tagMask tag mask * @param format directory name format * @param create true to create, false to rename * * @return true if ok. */ bool renameDirectory(TrackData::TagVersion tagMask, const QString& format, bool create); /** * Number tracks in selected files of directory. * * @param nr start number * @param total total number of tracks, used if >0 * @param tagVersion determines on which tags the numbers are set */ void numberTracks(int nr, int total, TrackData::TagVersion tagVersion); /** * Set track data with tagged files of directory. * * @param tagVersion tag version * @param trackDataList is filled with track data */ void filesToTrackData(TrackData::TagVersion tagVersion, ImportTrackDataVector& trackDataList); /** * Set track data model with tagged files of directory. * * @param tagVersion tag version */ void filesToTrackDataModel(TrackData::TagVersion tagVersion); /** * Set tagged files of directory from track data model. * * @param tagVersion tags to set */ void trackDataModelToFiles(TrackData::TagVersion tagVersion); /** * Download an image file. * * @param url URL of image * @param dest specifies affected files */ void downloadImage(const QString& url, DownloadImageDestination dest); /** * Get value of frame. * To get binary data like a picture, the name of a file to write can be * added after the @a name, e.g. "Picture:/path/to/file". * * @param tagMask tag bit (1 for tag 1, 2 for tag 2) * @param name name of frame (e.g. "artist") */ QString getFrame(TrackData::TagVersion tagMask, const QString& name); /** * Set value of frame. * For tag 2 (@a tagMask 2), if no frame with @a name exists, a new frame * is added, if @a value is empty, the frame is deleted. * To add binary data like a picture, a file can be added after the * @a name, e.g. "Picture:/path/to/file". * * @param tagMask tag bit (1 for tag 1, 2 for tag 2) * @param name name of frame (e.g. "artist") * @param value value of frame */ bool setFrame(TrackData::TagVersion tagMask, const QString& name, const QString& value); /** * Format a filename if format while editing is switched on. * * @param taggedFile file to modify */ void formatFileNameIfEnabled(TaggedFile* taggedFile) const; /** * Format frames if format while editing is switched on. * * @param frames frames */ void formatFramesIfEnabled(FrameCollection& frames) const; /** * Open directory or add pictures on drop. * * @param paths paths of directories or files in directory */ void openDrop(const QStringList& paths); /** * Add picture on drop. * * @param image dropped image. */ void dropImage(const QImage& image); /** * Handle URL on drop. * * @param txt dropped URL. */ void dropUrl(const QString& txt); /** * Get number of tracks in current directory. * * @return number of tracks, 0 if not found. */ int getTotalNumberOfTracksInDir(); /** * Get name of selected file. * * @return absolute file name, ends with "/" if it is a directory. */ QString getFileNameOfSelectedFile(); /** * Create a filter string for the file dialog. * The filter string contains entries for all supported types. * * @return filter string. */ QString createFilterString() const; /** * Get image destination set by downloadImage(). * @return image destination. */ DownloadImageDestination getDownloadImageDestination() const { return m_downloadImageDest; } /** * Set modification state. * * @param val true if a file is modified */ void setModified(bool val) { m_modified = val; } /** * Check modification state. * * @return true if a file is modified. */ bool isModified() { return m_modified; } /** * Update modification state from files. */ void updateModified(); /** * Set filter state. * * @param val true if list is filtered */ void setFiltered(bool val) { m_filtered = val; } /** * Check filter state. * * @return true if list is filtered. */ bool isFiltered() { return m_filtered; } /** * Get format used to generate filename from tags. * @return format */ QString getFilenameToTagsFormat() const { return m_filenameToTagsFormat; } /** * Get format used to generate tags from filename. * @return format */ QString getTagsToFilenameFormat() const { return m_tagsToFilenameFormat; } /** * Get the selected file. * * @return the selected file, * 0 if not exactly one file is selected */ TaggedFile* getSelectedFile(); /** * Get directory name. * @return directory. */ static QString getDirName() { return s_dirName; } /** * Set directory name. * @param dirName directory. */ static void setDirName(const QString& dirName) { s_dirName = dirName; } /** * Notify the tagged file factories about the changed configuration. */ static void notifyConfigurationChange(); /** * Load plugins. * @return list of plugin instances. */ static QObjectList loadPlugins(); public slots: /** * Revert file modifications. * Acts on selected files or all files if no file is selected. */ void revertFileModifications(); /** * Set name of selected file. * Exactly one file has to be selected. * * @param name file name. */ void setFileNameOfSelectedFile(const QString& name); /** * Apply filename format. */ void applyFilenameFormat(); /** * Apply ID3 format. */ void applyId3Format(); /** * Apply text encoding. * Set the text encoding selected in the settings Tags/ID3v2/Text encoding * for all selected files which have an ID3v2 tag. */ void applyTextEncoding(); /** * Convert ID3v2.3 to ID3v2.4 tags. */ void convertToId3v24(); /** * Convert ID3v2.4 to ID3v2.3 tags. */ void convertToId3v23(); /** * Copy tags 1 into copy buffer. */ void copyTagsV1(); /** * Copy tags 2 into copy buffer. */ void copyTagsV2(); /** * Copy tags into copy buffer. * * @param tagMask tag bit (1 for tag 1, 2 for tag 2) */ void copyTags(TrackData::TagVersion tagMask); /** * Paste from copy buffer to ID3v1 tags. */ void pasteTagsV1(); /** * Paste from copy buffer to ID3v2 tags. */ void pasteTagsV2(); /** * Paste from copy buffer to tags. * * @param tagMask tag bit (1 for tag 1, 2 for tag 2) */ void pasteTags(TrackData::TagVersion tagMask); /** * Copy ID3v1 tags to ID3v2 tags of selected files. */ void copyV1ToV2(); /** * Copy ID3v2 tags to ID3v1 tags of selected files. */ void copyV2ToV1(); /** * Set tag from other tag. * * @param tagMask tag bit (1 for tag 1, 2 for tag 2) */ void copyToOtherTag(TrackData::TagVersion tagMask); /** * Remove ID3v1 tags in selected files. */ void removeTagsV1(); /** * Remove ID3v2 tags in selected files. */ void removeTagsV2(); /** * Remove tags in selected files. * * @param tagMask tag bit (1 for tag 1, 2 for tag 2) */ void removeTags(TrackData::TagVersion tagMask); /** * Set ID3v1 tags according to filename. * If a single file is selected the tags in the GUI controls * are set, else the tags in the multiple selected files. */ void getTagsFromFilenameV1(); /** * Set ID3v2 tags according to filename. * If a single file is selected the tags in the GUI controls * are set, else the tags in the multiple selected files. */ void getTagsFromFilenameV2(); /** * Set tags according to filename. * * @param tagMask tag bit (1 for tag 1, 2 for tag 2) */ void getTagsFromFilename(TrackData::TagVersion tagMask); /** * Set format used to generate filename from tags. * @param format format */ void setFilenameToTagsFormat(const QString& format); /** * Set format used to generate filename from tags without emitting * filenameToTagsFormatChanged() signal. * This has to be used when connected from the GUI to avoid that the GUI * is updated because of its own changes. * @param format format */ void setFilenameToTagsFormatWithoutSignaling(const QString& format); /** * Set format used to generate tags from filename. * @param format format */ void setTagsToFilenameFormat(const QString& format); /** * Set format used to generate tags from filename without emitting * tagsToFilenameFormatChanged() signal. * This has to be used when connected from the GUI to avoid that the GUI * is updated because of its own changes. * @param format format */ void setTagsToFilenameFormatWithoutSignaling(const QString& format); /** * Set filename according to tags. * If a single file is selected the tags in the GUI controls * are used, else the tags in the multiple selected files. * * @param tagVersion tag version */ void getFilenameFromTags(TrackData::TagVersion tagVersion); /** * Edit selected frame. * * @param frameEditor editor for frame fields */ void editFrame(IFrameEditor* frameEditor); /** * Delete selected frame. * * @param frameName name of frame to delete, empty to delete selected frame */ void deleteFrame(const QString& frameName = QString()); /** * Select a frame type and add such a frame to frame list. * * @param frame frame to add, if 0 the user has to select and edit the frame * @param frameEditor editor for frame fields, if not null and a frame * is set, the user can edit the frame before it is added */ void addFrame(const Frame* frame, IFrameEditor* frameEditor = 0); /** * Edit a picture frame if one exists or add a new one. * * @param frameEditor editor for frame fields */ void editOrAddPicture(IFrameEditor* frameEditor); /** * Add a downloaded image. * * @param data HTTP response of download * @param mimeType MIME type of data * @param url URL of downloaded data */ void imageDownloaded(const QByteArray& data, const QString& mimeType, const QString& url); /** * Set the first file as the current file. * * @param select true to select the file * * @return true if a file exists. */ bool firstFile(bool select = true); /** * Set the next file as the current file. * * @param select true to select the file * * @return true if a next file exists. */ bool nextFile(bool select = true); /** * Set the previous file as the current file. * * @param select true to select the file * * @return true if a previous file exists. */ bool previousFile(bool select = true); /** * Select or deselect the current file. * * @param select true to select the file, false to deselect it * * @return true if a current file exists. */ bool selectCurrentFile(bool select = true); /** * Select all files. */ void selectAllFiles(); /** * Deselect all files. */ void deselectAllFiles(); /** * Fetch entries of directory if not already fetched. * This works like FileList::expand(), but without expanding tree view * items and independent of the GUI. The processing is done in the background * by QFileSystemModel, so the fetched items are not immediately available * after calling this method. * * @param index index of directory item */ void fetchDirectory(const QModelIndex& index); /** * Process change of selection. * The GUI is signaled to update the current selection and the controls. */ void fileSelected(); /** * Schedule actions to rename a directory. * When finished renameActionsScheduled() is emitted. */ void scheduleRenameActions(); /** * Apply a file filter. * * @param fileFilter filter to apply. */ void applyFilter(FileFilter& fileFilter); /** * Apply a file filter. * * @param expression filter expression */ void applyFilter(const QString& expression); /** * Perform a batch import for the selected directories. * @param profile batch import profile * @param tagVersion import destination tag versions */ void batchImport(const BatchImportProfile& profile, TrackData::TagVersion tagVersion); #if defined HAVE_PHONON || QT_VERSION >= 0x050000 /** * Play audio file. */ void playAudio(); #endif signals: /** * Emitted when a new directory is opened. * @param directoryIndex root path file proxy model index * @param fileIndexes file path indexes in the file proxy model */ void directoryOpened(const QPersistentModelIndex& directoryIndex, const QList& fileIndexes); /** * Emitted when a confirmed opening of a directory or file is requested. * @param paths directory or file paths */ void confirmedOpenDirectoryRequested(const QStringList& paths); /** * Emitted when saving files is started. * @param totalFiles total number of files to be saved */ void saveStarted(int totalFiles); /** * Emitted when a file has bee saved. * @param numFiles number of files saved */ void saveProgress(int numFiles); /** * Emitted before an operation on the selected files is performed. * The GUI should update the files of the current selection when * receiving this signal. */ void fileSelectionUpdateRequested(); /** * Emitted after an operation on the selected files has been performed. * The GUI should update its controls from the tags in the files when * receiving this signal. */ void selectedFilesUpdated(); /** * Emitted after a frame of a tagged file has been modified. * The GUI should update the corresponding controls when receiving this * signal. * * @param taggedFile tagged file with modified frame */ void frameModified(TaggedFile* taggedFile); /** * Emitted after a file has been modified. * The GUI should update its modification state when receiving this signal. */ void fileModified(); /** * Emitted when setFilenameToTagsFormat() changed. * @param format new format */ void filenameToTagsFormatChanged(const QString& format); /** * Emitted when setTagsToFilenameFormat() changed. * @param format new format */ void tagsToFilenameFormatChanged(const QString& format); /** * Emitted when a file is filtered. * @param type filter event type * @param fileName name of filtered file */ void fileFiltered(FileFilter::FilterEventType type, const QString& fileName); /** * Emitted before an audio file is played. * The GUI can display a player when receiving this signal. */ void aboutToPlayAudio(); /** * Emitted when all rename actions have been scheduled. * @see scheduleRenameActions() */ void renameActionsScheduled(); private slots: /** * Apply single file to file filter. * * @param index index of file in file proxy model */ void filterNextFile(const QPersistentModelIndex& index); /** * Apply single file to batch import. * * @param index index of file in file proxy model */ void batchImportNextFile(const QPersistentModelIndex& index); /** * Schedule rename action for a file. * * @param index index of file in file proxy model */ void scheduleNextRenameAction(const QPersistentModelIndex& index); /** * Emit directoryOpened(). */ void emitDirectoryOpened(); /** * Called when the gatherer thread has finished to load the directory. */ void onDirectoryLoaded(); private: /** * Load and initialize plugins depending on configuration. */ void initPlugins(); /** * Check type of a loaded plugin and register it. * @param plugin instance returned by plugin loader */ void checkPlugin(QObject* plugin); /** * Let the user select and edit a frame type and then edit the frame. * Add the frame if the edits are accepted. * * @param frameEditor frame editor * * @return true if edits accepted. */ bool selectAddAndEditFrame(IFrameEditor* frameEditor); ICorePlatformTools* m_platformTools; /** model of filesystem */ QFileSystemModel* m_fileSystemModel; FileProxyModel* m_fileProxyModel; FileProxyModelIterator* m_fileProxyModelIterator; DirProxyModel* m_dirProxyModel; QItemSelectionModel* m_fileSelectionModel; /** Track data model */ TrackDataModel* m_trackDataModel; FrameTableModel* m_framesV1Model; FrameTableModel* m_framesV2Model; QItemSelectionModel* m_framesV1SelectionModel; QItemSelectionModel* m_framesV2SelectionModel; /** Frame list */ FrameList* m_framelist; /** Configuration */ ConfigStore* m_configStore; /** Network access manager */ QNetworkAccessManager* m_netMgr; /** Download client */ DownloadClient* m_downloadClient; /** Text exporter */ TextExporter* m_textExporter; /** Directory renamer */ DirRenamer* m_dirRenamer; /** Batch importer */ BatchImporter* m_batchImporter; /** Timeout timer for openDirectory() */ QTimer* m_openDirectoryTimeoutTimer; #if defined HAVE_PHONON || QT_VERSION >= 0x050000 /** Audio player */ AudioPlayer* m_player; #endif FileFilter* m_expressionFileFilter; /** Affected files to add frame when downloading image */ DownloadImageDestination m_downloadImageDest; /** Copy buffer */ FrameCollection m_copyTags; /** true if any file was modified */ bool m_modified; /** true if list is filtered */ bool m_filtered; /** Root index in file proxy model */ QPersistentModelIndex m_fileProxyModelRootIndex; /** Indexes of opened file in file proxy model */ QList m_fileProxyModelFileIndexes; /** Format to generate tags from filename */ QString m_filenameToTagsFormat; /** Format to generate filename from tags */ QString m_tagsToFilenameFormat; /** Importers for different servers */ QList m_importers; /** Importer for MusicBrainz fingerprints */ QList m_trackImporters; /* Context for updateFrameModels() */ /** If a single file is selected, this tagged file, else 0 */ TaggedFile* m_selectionSingleFile; /** Number of selected files which support tag 1 */ int m_selectionTagV1SupportedCount; /** Number of selected files */ int m_selectionFileCount; /** true if any of the selected files has a tag 1 */ bool m_selectionHasTagV1; /** true if any of the selected files has a tag 2 */ bool m_selectionHasTagV2; /* Context for filterNextFile() */ FileFilter* m_fileFilter; QString m_lastProcessedDirName; /* Context for batchImportNextFile() */ const BatchImportProfile* m_batchImportProfile; TrackData::TagVersion m_batchImportTagVersion; QList m_batchImportAlbums; ImportTrackDataVector m_batchImportTrackDataList; /** Current directory */ static QString s_dirName; }; #endif // KID3APPLICATION_H kid3-3.0.2/src/core/model/modeliterator.cpp000066400000000000000000000263131224603511300205450ustar00rootroot00000000000000/** * \file modeliterator.cpp * Iterator for Qt models. * * \b Project: Kid3 * \author Urs Fleisch * \date 26-Mar-2011 * * Copyright (C) 2011 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "modeliterator.h" #include #include "fileproxymodel.h" /** * Constructor. * * @param rootIdx root of model to iterate */ ModelIterator::ModelIterator(const QModelIndex& rootIdx) : m_model(rootIdx.model()) { m_nodes.push(rootIdx); next(); } /** * Check if a next item exists. * @return true if there is a next index */ bool ModelIterator::hasNext() const { return m_model && m_nextIdx.isValid(); } /** * Advance iterator and return next item. * @return next index */ QModelIndex ModelIterator::next() { if (!m_model) return QModelIndex(); QModelIndex result = m_nextIdx; if (!m_nodes.isEmpty()) { m_nextIdx = m_nodes.pop(); if (m_nextIdx.isValid()) { for (int row = m_model->rowCount(m_nextIdx) - 1; row >= 0; --row) { m_nodes.push(m_model->index(row, 0, m_nextIdx)); } } } else { m_nextIdx = QModelIndex(); } return result; } /** * Get next item without moving iterator. * @return next index */ QModelIndex ModelIterator::peekNext() const { if (!m_model) return QModelIndex(); return m_nextIdx; } /** * Advance iterator and return data of next index. * @param role model item role to get * @return data of next index */ QVariant ModelIterator::nextData(int role) { if (!m_model) return QVariant(); return m_model->data(next(), role); } /** * Get data of next item without moving iterator. * @param role model item role to get * @return data of next index */ QVariant ModelIterator::peekNextData(int role) const { if (!m_model) return QVariant(); return m_model->data(m_nextIdx, role); } /** * Constructor. * * @param rootIdx root of model to iterate */ ModelBfsIterator::ModelBfsIterator(const QModelIndex& rootIdx) : m_model(rootIdx.model()), m_nextIdx(rootIdx), m_parentIdx(rootIdx), m_row(0) { } /** * Check if a next item exists. * @return true if there is a next index */ bool ModelBfsIterator::hasNext() const { return m_model && m_nextIdx.isValid(); } /** * Advance iterator and return next item. * @return next index */ QModelIndex ModelBfsIterator::next() { if (!m_model) return QModelIndex(); QModelIndex result = m_nextIdx; forever { if (m_parentIdx.isValid() && m_row < m_model->rowCount(m_parentIdx)) { m_nextIdx = m_model->index(m_row, 0, m_parentIdx); m_nodes.enqueue(m_nextIdx); ++m_row; break; } else if (!m_nodes.isEmpty()) { m_parentIdx = m_nodes.dequeue(); m_row = 0; } else { m_nextIdx = QModelIndex(); break; } } return result; } /** * Get next item without moving iterator. * @return next index */ QModelIndex ModelBfsIterator::peekNext() const { if (!m_model) return QModelIndex(); return m_nextIdx; } /** * Advance iterator and return data of next index. * @param role model item role to get * @return data of next index */ QVariant ModelBfsIterator::nextData(int role) { if (!m_model) return QVariant(); return m_model->data(next(), role); } /** * Get data of next item without moving iterator. * @param role model item role to get * @return data of next index */ QVariant ModelBfsIterator::peekNextData(int role) const { if (!m_model) return QVariant(); return m_model->data(m_nextIdx, role); } /** * Destructor. */ AbstractTaggedFileIterator::~AbstractTaggedFileIterator() { } /** * Constructor. * * @param rootIdx root of model to iterate */ TaggedFileIterator::TaggedFileIterator(const QModelIndex& rootIdx) : m_it(rootIdx), m_nextFile(0) { next(); } /** * Advance iterator and return next item. * @return next file */ TaggedFile* TaggedFileIterator::next() { TaggedFile* result = m_nextFile; m_nextFile = 0; while (m_it.hasNext()) { QModelIndex index = m_it.next(); if ((m_nextFile = FileProxyModel::getTaggedFileOfIndex(index)) != 0) break; } return result; } /** * Try to close the file handles. * * @param index root of model to iterate */ void TaggedFileIterator::closeFileHandles(const QModelIndex& index) { TaggedFileIterator it(index); while (it.hasNext()) { it.next()->closeFileHandle(); } } /** * Constructor. * * @param rootIdx root of model to iterate * @param selectModel selection model * @param allIfNoneSelected treat all files as selected when nothing is * selected */ SelectedTaggedFileIterator::SelectedTaggedFileIterator( const QModelIndex& rootIdx, const QItemSelectionModel* selectModel, bool allIfNoneSelected): m_it(rootIdx), m_nextFile(0), m_selectModel(selectModel), m_allSelected(!m_selectModel || (allIfNoneSelected && !m_selectModel->hasSelection())) { next(); } /** * Advance iterator and return next item. * @return next file */ TaggedFile* SelectedTaggedFileIterator::next() { TaggedFile* result = m_nextFile; m_nextFile = 0; while (m_it.hasNext()) { QModelIndex index = m_it.next(); if ((m_nextFile = FileProxyModel::getTaggedFileOfIndex(index)) != 0 && (m_allSelected || m_selectModel->isSelected(index))) break; else m_nextFile = 0; } return result; } /** * Check if nothing is selected. * @return true if nothing is selected. */ bool SelectedTaggedFileIterator::hasNoSelection() const { return m_selectModel && !m_selectModel->hasSelection(); } /** * Constructor. * * @param index of the directory or a file in it */ TaggedFileOfDirectoryIterator::TaggedFileOfDirectoryIterator( const QModelIndex& index) : m_row(0), m_model(index.model()), m_parentIdx(m_model && m_model->hasChildren(index) ? index: index.parent()) { next(); } /** * Check if a next item exists. * @return true if there is a next file */ bool TaggedFileOfDirectoryIterator::hasNext() const { return m_model && m_nextFile != 0; } /** * Advance iterator and return next item. * @return next file */ TaggedFile* TaggedFileOfDirectoryIterator::next() { if (!m_model) return 0; TaggedFile* result = m_nextFile; m_nextFile = 0; while (m_row < m_model->rowCount(m_parentIdx)) { QModelIndex index = m_model->index(m_row++, 0, m_parentIdx); if ((m_nextFile = FileProxyModel::getTaggedFileOfIndex(index)) != 0) break; } return result; } /** * Get next item without moving iterator. * @return next file */ TaggedFile* TaggedFileOfDirectoryIterator::peekNext() const { if (!m_model) return 0; return m_nextFile; } /** * Get first tagged file in directory. * @param index of the directory or a file in it * @return first tagged file in directory, 0 if none. */ TaggedFile* TaggedFileOfDirectoryIterator::first(const QModelIndex& index) { TaggedFileOfDirectoryIterator it(index); if (it.hasNext()) return it.peekNext(); return 0; } /** * Constructor. * * @param index of the directory or a file in it * @param selectModel selection model * @param allIfNoneSelected treat all files as selected when nothing is * selected */ SelectedTaggedFileOfDirectoryIterator::SelectedTaggedFileOfDirectoryIterator( const QModelIndex& index, const QItemSelectionModel* selectModel, bool allIfNoneSelected) : m_row(0), m_model(index.model()), m_parentIdx(m_model && m_model->hasChildren(index) ? index: index.parent()), m_selectModel(selectModel), m_allSelected(!m_selectModel || (allIfNoneSelected && !m_selectModel->hasSelection())) { next(); } /** * Check if a next item exists. * @return true if there is a next file */ bool SelectedTaggedFileOfDirectoryIterator::hasNext() const { return m_model && m_nextFile != 0; } /** * Advance iterator and return next item. * @return next file */ TaggedFile* SelectedTaggedFileOfDirectoryIterator::next() { if (!m_model) return 0; TaggedFile* result = m_nextFile; m_nextFile = 0; while (m_row < m_model->rowCount(m_parentIdx)) { QModelIndex index = m_model->index(m_row++, 0, m_parentIdx); if ((m_nextFile = FileProxyModel::getTaggedFileOfIndex(index)) != 0 && (m_allSelected || m_selectModel->isSelected(index))) break; else m_nextFile = 0; } return result; } /** * Get next item without moving iterator. * @return next file */ TaggedFile* SelectedTaggedFileOfDirectoryIterator::peekNext() const { if (!m_model) return 0; return m_nextFile; } /** * Constructor. * * @param selectModel selection model */ TaggedFileOfSelectedDirectoriesIterator::TaggedFileOfSelectedDirectoriesIterator( const QItemSelectionModel* selectModel) : m_model(0), m_dirIdx(0), m_row(0), m_nextFile(0) { if (selectModel && (m_model = qobject_cast(selectModel->model())) != 0) { foreach (const QModelIndex& index, selectModel->selectedIndexes()) { if (m_model->isDir(index)) { m_dirIndexes.append(getIndexesOfDirWithSubDirs(index)); } } } next(); } /** * Get indexes of directory and recursively all subdirectories. * @param dirIndex index of directory * @return list with dirIndex and its subdirectories. */ QModelIndexList TaggedFileOfSelectedDirectoriesIterator::getIndexesOfDirWithSubDirs( const QModelIndex& dirIndex) { QModelIndexList dirs; dirs.append(dirIndex); for (int dirsPos = 0; dirsPos < dirs.size(); ++dirsPos) { QModelIndex parentIndex(dirs.at(dirsPos)); for (int row = 0; row < m_model->rowCount(parentIndex); ++row) { QModelIndex index(m_model->index(row, 0, parentIndex)); if (m_model->isDir(index)) { dirs.append(index); } } } return dirs; } /** * Check if a next item exists. * @return true if there is a next file */ bool TaggedFileOfSelectedDirectoriesIterator::hasNext() const { return m_model && m_nextFile != 0; } /** * Advance iterator and return next item. * @return next file */ TaggedFile* TaggedFileOfSelectedDirectoriesIterator::next() { if (!m_model) return 0; TaggedFile* result = m_nextFile; m_nextFile = 0; while (!m_nextFile) { if (m_dirIdx >= m_dirIndexes.size()) break; QModelIndex parentIdx(m_dirIndexes.at(m_dirIdx)); while (m_row < m_model->rowCount(parentIdx)) { QModelIndex index = m_model->index(m_row++, 0, parentIdx); if ((m_nextFile = FileProxyModel::getTaggedFileOfIndex(index)) != 0) break; } if (m_row >= m_model->rowCount(parentIdx)) { ++m_dirIdx; m_row = 0; } } return result; } /** * Get next item without moving iterator. * @return next file */ TaggedFile* TaggedFileOfSelectedDirectoriesIterator::peekNext() const { if (!m_model) return 0; return m_nextFile; } kid3-3.0.2/src/core/model/modeliterator.h000066400000000000000000000252611224603511300202130ustar00rootroot00000000000000/** * \file modeliterator.h * Iterator for Qt models. * * \b Project: Kid3 * \author Urs Fleisch * \date 26-Mar-2011 * * Copyright (C) 2011 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MODELITERATOR_H #define MODELITERATOR_H #include #include #include #include "kid3api.h" class QItemSelectionModel; class TaggedFile; class FileProxyModel; /** * Generic Java-style iterator for Qt models. * Iterates using preorder traversal. Supports only one column. * * Typical usage: * @code * // get rootIndex... * ModelIterator it(rootIndex); * while (it.hasNext()) { * TaggedFile* taggedFile; * if (ModelIterator::getTaggedFileOfIndex(it.peekNext(), &taggedFile) * && taggedFile) { * // do something with taggedFile... * } * it.next(); * } * @endcode */ class ModelIterator { public: /** * Constructor. * * @param rootIdx root of model to iterate */ explicit ModelIterator(const QModelIndex& rootIdx); /** * Check if a next item exists. * @return true if there is a next index */ bool hasNext() const; /** * Advance iterator and return next item. * @return next index */ QModelIndex next(); /** * Get next item without moving iterator. * @return next index */ QModelIndex peekNext() const; /** * Advance iterator and return data of next index. * @param role model item role to get * @return data of next index */ QVariant nextData(int role); /** * Get data of next item without moving iterator. * @param role model item role to get * @return data of next index */ QVariant peekNextData(int role) const; private: QStack m_nodes; const QAbstractItemModel* m_model; QModelIndex m_nextIdx; }; /** * Generic Java-style iterator for Qt models. * Iterates using breadth-first-search. Supports only one column. */ class ModelBfsIterator { public: /** * Constructor. * * @param rootIdx root of model to iterate */ explicit ModelBfsIterator(const QModelIndex& rootIdx); /** * Check if a next item exists. * @return true if there is a next index */ bool hasNext() const; /** * Advance iterator and return next item. * @return next index */ QModelIndex next(); /** * Get next item without moving iterator. * @return next index */ QModelIndex peekNext() const; /** * Advance iterator and return data of next index. * @param role model item role to get * @return data of next index */ QVariant nextData(int role); /** * Get data of next item without moving iterator. * @param role model item role to get * @return data of next index */ QVariant peekNextData(int role) const; private: QQueue m_nodes; const QAbstractItemModel* m_model; QModelIndex m_nextIdx; QModelIndex m_parentIdx; int m_row; }; /** * Abstract base class for tagged file iterators. */ class KID3_CORE_EXPORT AbstractTaggedFileIterator { public: /** * Destructor. */ virtual ~AbstractTaggedFileIterator() = 0; /** * Check if a next item exists. * @return true if there is a next file */ virtual bool hasNext() const = 0; /** * Advance iterator and return next item. * @return next file */ virtual TaggedFile* next() = 0; /** * Get next item without moving iterator. * @return next file */ virtual TaggedFile* peekNext() const = 0; }; /** * Iterator to iterate over model indexes with tagged files. * All TaggedFiles returned while hasNext() is true are not null. * * Typical usage: * @code * // get rootIndex... * TaggedFileIterator it(rootIndex); * while (it.hasNext()) { * TaggedFile* taggedFile = it.next(); * // do something with taggedFile... * } * @endcode */ class KID3_CORE_EXPORT TaggedFileIterator : public AbstractTaggedFileIterator { public: /** * Constructor. * * @param rootIdx root of model to iterate */ explicit TaggedFileIterator(const QModelIndex& rootIdx); /** * Check if a next item exists. * @return true if there is a next file */ virtual bool hasNext() const { return m_nextFile != 0; } /** * Advance iterator and return next item. * @return next file */ virtual TaggedFile* next(); /** * Get next item without moving iterator. * @return next file */ virtual TaggedFile* peekNext() const { return m_nextFile; } /** * Try to close the file handles. * * @param index root of model to iterate */ static void closeFileHandles(const QModelIndex& index); private: ModelIterator m_it; TaggedFile* m_nextFile; }; /** * Iterator to iterate over model indexes with selected tagged files. * All TaggedFiles returned while hasNext() is true are not null. * * Typical usage: * @code * // get rootIndex, selectionModel... * SelectedTaggedFileIterator it(rootIndex, selectionModel, false); * while (it.hasNext()) { * TaggedFile* taggedFile = it.next(); * // do something with taggedFile... * } * @endcode */ class SelectedTaggedFileIterator : public AbstractTaggedFileIterator { public: /** * Constructor. * * @param rootIdx root of model to iterate * @param selectModel selection model * @param allIfNoneSelected treat all files as selected when nothing is * selected */ SelectedTaggedFileIterator(const QModelIndex& rootIdx, const QItemSelectionModel* selectModel, bool allIfNoneSelected); /** * Check if a next item exists. * @return true if there is a next file */ virtual bool hasNext() const { return m_nextFile != 0; } /** * Advance iterator and return next item. * @return next file */ virtual TaggedFile* next(); /** * Get next item without moving iterator. * @return next file */ virtual TaggedFile* peekNext() const { return m_nextFile; } /** * Check if nothing is selected. * @return true if nothing is selected. */ bool hasNoSelection() const; private: ModelIterator m_it; TaggedFile* m_nextFile; const QItemSelectionModel* m_selectModel; bool m_allSelected; }; /** * Iterator to iterate tagged files from a single directory. * All TaggedFiles returned while hasNext() is true are not null. * * Typical usage: * @code * // get currentIndex... * TaggedFileOfDirectoryIterator it(currentIndex); * while (it.hasNext()) { * TaggedFile* taggedFile = it.next(); * // do something with taggedFile... * } * @endcode */ class KID3_CORE_EXPORT TaggedFileOfDirectoryIterator : public AbstractTaggedFileIterator { public: /** * Constructor. * * @param index of the directory or a file in it */ explicit TaggedFileOfDirectoryIterator(const QModelIndex& index); /** * Check if a next item exists. * @return true if there is a next file */ virtual bool hasNext() const; /** * Advance iterator and return next item. * @return next file */ virtual TaggedFile* next(); /** * Get next item without moving iterator. * @return next file */ virtual TaggedFile* peekNext() const; /** * Get first tagged file in directory. * @param index of the directory or a file in it * @return first tagged file in directory, 0 if none. */ static TaggedFile* first(const QModelIndex& index); private: int m_row; const QAbstractItemModel* m_model; QModelIndex m_parentIdx; TaggedFile* m_nextFile; }; /** * Iterator to iterate selected tagged files from a single directory. * All TaggedFiles returned while hasNext() is true are not null. * * Typical usage: * @code * // get currentIndex, selectionModel... * SelectedTaggedFileOfDirectoryIterator it(currentIndex, selectionModel, false); * while (it.hasNext()) { * TaggedFile* taggedFile = it.next(); * // do something with taggedFile... * } * @endcode */ class SelectedTaggedFileOfDirectoryIterator : public AbstractTaggedFileIterator { public: /** * Constructor. * * @param index of the directory or a file in it * @param selectModel selection model * @param allIfNoneSelected treat all files as selected when nothing is * selected */ SelectedTaggedFileOfDirectoryIterator( const QModelIndex& index, const QItemSelectionModel* selectModel, bool allIfNoneSelected); /** * Check if a next item exists. * @return true if there is a next file */ virtual bool hasNext() const; /** * Advance iterator and return next item. * @return next file */ virtual TaggedFile* next(); /** * Get next item without moving iterator. * @return next file */ virtual TaggedFile* peekNext() const; private: int m_row; const QAbstractItemModel* m_model; QModelIndex m_parentIdx; TaggedFile* m_nextFile; const QItemSelectionModel* m_selectModel; bool m_allSelected; }; /** * Iterator to iterate all tagged files from selected directories. * All TaggedFiles returned while hasNext() is true are not null. * * Typical usage: * @code * // get selectionModel... * TaggedFileOfSelectedDirectoriesIterator it(selectionModel); * while (it.hasNext()) { * TaggedFile* taggedFile = it.next(); * // do something with taggedFile... * } * @endcode */ class TaggedFileOfSelectedDirectoriesIterator : public AbstractTaggedFileIterator { public: /** * Constructor. * * @param selectModel selection model */ explicit TaggedFileOfSelectedDirectoriesIterator( const QItemSelectionModel* selectModel); /** * Check if a next item exists. * @return true if there is a next file */ virtual bool hasNext() const; /** * Advance iterator and return next item. * @return next file */ virtual TaggedFile* next(); /** * Get next item without moving iterator. * @return next file */ virtual TaggedFile* peekNext() const; private: /** * Get indexes of directory and recursively all subdirectories. * @param dirIndex index of directory * @return list with dirIndex and its subdirectories. */ QModelIndexList getIndexesOfDirWithSubDirs(const QModelIndex& dirIndex); const FileProxyModel* m_model; QModelIndexList m_dirIndexes; int m_dirIdx; int m_row; TaggedFile* m_nextFile; }; #endif // MODELITERATOR_H kid3-3.0.2/src/core/model/net.sourceforge.Kid3.xml000066400000000000000000000112101224603511300216000ustar00rootroot00000000000000 kid3-3.0.2/src/core/model/scriptinterface.cpp000066400000000000000000000342641224603511300210640ustar00rootroot00000000000000/** * \file scriptinterface.cpp * D-Bus script adaptor. * * \b Project: Kid3 * \author Urs Fleisch * \date 20 Dec 2007 * * Copyright (C) 2007-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "scriptinterface.h" #ifdef HAVE_QTDBUS #include #include #include #include #include #include "kid3application.h" #include "taggedfile.h" #include "frametablemodel.h" #include "filefilter.h" #include "pictureframe.h" #include "fileproxymodel.h" #include "modeliterator.h" #include "batchimportconfig.h" #include "batchimportprofile.h" /** * Constructor. * * @param app parent application */ ScriptInterface::ScriptInterface(Kid3Application* app) : QDBusAbstractAdaptor(app), m_app(app) { setObjectName(QLatin1String("ScriptInterface")); setAutoRelaySignals(true); } /** * Destructor. */ ScriptInterface::~ScriptInterface() { } /** * Open file or directory. * * @param path path to file or directory * * @return true if ok. */ bool ScriptInterface::openDirectory(const QString& path) { return m_app->openDirectory(QStringList() << path, true); } /** * Save all modified files. * * @return true if ok, * else the error message is available using getErrorMessage(). */ bool ScriptInterface::save() { QStringList errorFiles = m_app->saveDirectory(); if (errorFiles.isEmpty()) { m_errorMsg.clear(); return true; } else { m_errorMsg = QLatin1String("Error while writing file:\n") + errorFiles.join(QLatin1String("\n")); return false; } } /** * Get a detailed error message provided by some methods. * * @return detailed error message. */ QString ScriptInterface::getErrorMessage() const { return m_errorMsg; } /** * Revert changes in the selected files. */ void ScriptInterface::revert() { m_app->revertFileModifications(); } /** * Import tags from a file. * * @param tagMask tag bit (1 for tag 1, 2 for tag 2) * @param path path of file, "clipboard" for import from clipboard * @param fmtIdx index of format * * @return true if ok. */ bool ScriptInterface::importFromFile(int tagMask, const QString& path, int fmtIdx) { return m_app->importTags(TrackData::tagVersionCast(tagMask), path, fmtIdx); } /** * Start an automatic batch import. * * @param tagMask tag mask (bit 0 for tag 1, bit 1 for tag 2) * @param profileName name of batch import profile to use * * @return true if profile found. */ bool ScriptInterface::batchImport(int tagMask, const QString& profileName) { BatchImportProfile profile; if (BatchImportConfig::instance().getProfileByName(profileName, profile)) { m_app->batchImport(profile, TrackData::tagVersionCast(tagMask)); return true; } return false; } /** * Download album cover art into the picture frame of the selected files. * * @param url URL of picture file or album art resource * @param allFilesInDir true to add the image to all files in the directory */ void ScriptInterface::downloadAlbumArt(const QString& url, bool allFilesInDir) { m_app->downloadImage(url, allFilesInDir ? Kid3Application::ImageForAllFilesInDirectory : Kid3Application::ImageForSelectedFiles); } /** * Export tags to a file. * * @param tagMask tag bit (1 for tag 1, 2 for tag 2) * @param path path of file, "clipboard" for export to clipboard * @param fmtIdx index of format * * @return true if ok. */ bool ScriptInterface::exportToFile(int tagMask, const QString& path, int fmtIdx) { return m_app->exportTags(TrackData::tagVersionCast(tagMask), path, fmtIdx); } /** * Create a playlist. * * @return true if ok. */ bool ScriptInterface::createPlaylist() { return m_app->writePlaylist(); } /** * Quit the application. */ void ScriptInterface::quit() { selectAll(); revert(); QCoreApplication::quit(); } /** * Select all files. */ void ScriptInterface::selectAll() { m_app->selectAllFiles(); } /** * Deselect all files. */ void ScriptInterface::deselectAll() { m_app->deselectAllFiles(); } /** * Set the first file as the current file. * * @return true if there is a first file. */ bool ScriptInterface::firstFile() { return m_app->firstFile(false); } /** * Set the previous file as the current file. * * @return true if there is a previous file. */ bool ScriptInterface::previousFile() { return m_app->previousFile(false); } /** * Set the next file as the current file. * * @return true if there is a next file. */ bool ScriptInterface::nextFile() { return m_app->nextFile(false); } /** * Select the first file. * * @return true if there is a first file. */ bool ScriptInterface::selectFirstFile() { return m_app->firstFile(true); } /** * Select the previous file. * * @return true if there is a previous file. */ bool ScriptInterface::selectPreviousFile() { return m_app->previousFile(true); } /** * Select the next file. * * @return true if there is a next file. */ bool ScriptInterface::selectNextFile() { return m_app->nextFile(true); } /** * Select the current file. * * @return true if there is a current file. */ bool ScriptInterface::selectCurrentFile() { return m_app->selectCurrentFile(true); } /** * Expand the current file item if it is a directory. * A file list item is a directory if getFileName() returns a name with * '/' as the last character. * The directory is fetched but not expanded in the GUI. To expand it in the * GUI, call nextFile() or selectNextFile() after expandDirectory(). * * @return true if current file item is a directory. */ bool ScriptInterface::expandDirectory() { QModelIndex index(m_app->getFileSelectionModel()->currentIndex()); if (!FileProxyModel::getPathIfIndexOfDir(index).isNull()) { m_app->fetchDirectory(index); return true; } return false; } /** * Apply the file name format. */ void ScriptInterface::applyFilenameFormat() { m_app->applyFilenameFormat(); } /** * Apply the tag format. */ void ScriptInterface::applyTagFormat() { m_app->applyId3Format(); } /** * Apply text encoding. */ void ScriptInterface::applyTextEncoding() { m_app->applyTextEncoding(); } /** * Set the directory name from the tags. * * @param tagMask tag mask (bit 0 for tag 1, bit 1 for tag 2) * @param format directory name format * @param create true to create, false to rename * * @return true if ok, * else the error message is available using getErrorMessage(). */ bool ScriptInterface::setDirNameFromTag(int tagMask, const QString& format, bool create) { connect(m_app, SIGNAL(renameActionsScheduled()), this, SLOT(onRenameActionsScheduled())); if (m_app->renameDirectory(TrackData::tagVersionCast(tagMask), format, create)) { return true; } else { disconnect(m_app, SIGNAL(renameActionsScheduled()), this, SLOT(onRenameActionsScheduled())); return false; } } void ScriptInterface::onRenameActionsScheduled() { disconnect(m_app, SIGNAL(renameActionsScheduled()), this, SLOT(onRenameActionsScheduled())); m_errorMsg = m_app->performRenameActions(); if (!m_errorMsg.isEmpty()) { m_errorMsg = QLatin1String("Error while renaming:\n") + m_errorMsg; } } /** * Set subsequent track numbers in the selected files. * * @param tagMask tag mask (bit 0 for tag 1, bit 1 for tag 2) * @param firstTrackNr number to use for first file */ void ScriptInterface::numberTracks(int tagMask, int firstTrackNr) { m_app->numberTracks(firstTrackNr, 0, TrackData::tagVersionCast(tagMask)); } /** * Filter the files. * * @param expression filter expression */ void ScriptInterface::filter(const QString& expression) { m_app->applyFilter(expression); } /** * Convert ID3v2.3 tags to ID3v2.4. */ void ScriptInterface::convertToId3v24() { m_app->convertToId3v24(); } /** * Convert ID3v2.4 tags to ID3v2.3. */ void ScriptInterface::convertToId3v23() { m_app->convertToId3v23(); } /** * Get path of directory. * * @return absolute path of directory. */ QString ScriptInterface::getDirectoryName() { return m_app->getDirPath(); } /** * Get name of current file. * * @return absolute file name, ends with "/" if it is a directory. */ QString ScriptInterface::getFileName() { return m_app->getFileNameOfSelectedFile(); } /** * Set name of selected file. * The file will be renamed when the directory is saved. * * @param name file name. */ void ScriptInterface::setFileName(const QString& name) { m_app->setFileNameOfSelectedFile(name); } /** * Set format to use when setting the filename from the tags. * * @param format file name format * @see setFileNameFromTag() */ void ScriptInterface::setFileNameFormat(const QString& format) { m_app->setTagsToFilenameFormat(format); } /** * Set the file names of the selected files from the tags. * * @param tagMask tag bit (1 for tag 1, 2 for tag 2) * @see setFileNameFormat() */ void ScriptInterface::setFileNameFromTag(int tagMask) { m_app->getFilenameFromTags(TrackData::tagVersionCast(tagMask)); } /** * Get value of frame. * To get binary data like a picture, the name of a file to write can be * added after the @a name, e.g. "Picture:/path/to/file". * * @param tagMask tag bit (1 for tag 1, 2 for tag 2) * @param name name of frame (e.g. "Artist") */ QString ScriptInterface::getFrame(int tagMask, const QString& name) { return m_app->getFrame(TrackData::tagVersionCast(tagMask), name); } /** * Set value of frame. * For tag 2 (@a tagMask 2), if no frame with @a name exists, a new frame * is added, if @a value is empty, the frame is deleted. * To add binary data like a picture, a file can be added after the * @a name, e.g. "Picture:/path/to/file". * * @param tagMask tag bit (1 for tag 1, 2 for tag 2) * @param name name of frame (e.g. "Artist") * @param value value of frame */ bool ScriptInterface::setFrame(int tagMask, const QString& name, const QString& value) { return m_app->setFrame(TrackData::tagVersionCast(tagMask), name, value); } /** * Get all frames of a tag. * * @param tagMask tag bit (1 for tag 1, 2 for tag 2) * * @return list with alternating frame names and values. */ QStringList ScriptInterface::getTag(int tagMask) { QStringList lst; FrameTableModel* ft = (tagMask & 2) ? m_app->frameModelV2() : m_app->frameModelV1(); for (FrameCollection::const_iterator it = ft->frames().begin(); it != ft->frames().end(); ++it) { lst << it->getName(); lst << it->getValue(); } return lst; } /** * Get technical information about file. * Properties are Format, Bitrate, Samplerate, Channels, Duration, * Channel Mode, VBR, Tag 1, Tag 2. * Properties which are not available are omitted. * * @return list with alternating property names and values. */ QStringList ScriptInterface::getInformation() { QStringList lst; QModelIndex index = m_app->getFileSelectionModel()->currentIndex(); if (TaggedFile* taggedFile = FileProxyModel::getTaggedFileOfIndex(index)) { TaggedFile::DetailInfo info; taggedFile->getDetailInfo(info); if (info.valid) { lst << QLatin1String("Format") << info.format; if (info.bitrate > 0 && info.bitrate < 999) { lst << QLatin1String("Bitrate") << QString::number(info.bitrate); } if (info.sampleRate > 0) { lst << QLatin1String("Samplerate") << QString::number(info.sampleRate); } if (info.channels > 0) { lst << QLatin1String("Channels") << QString::number(info.channels); } if (info.duration > 0) { lst << QLatin1String("Duration") << QString::number(info.duration); } if (info.channelMode == TaggedFile::DetailInfo::CM_Stereo || info.channelMode == TaggedFile::DetailInfo::CM_JointStereo) { lst << QLatin1String("Channel Mode") << (info.channelMode == TaggedFile::DetailInfo::CM_Stereo ? QLatin1String("Stereo") : QLatin1String("Joint Stereo")); } if (info.vbr) { lst << QLatin1String("VBR") << QLatin1String("1"); } } QString tag1 = taggedFile->getTagFormatV1(); if (!tag1.isEmpty()) { lst << QLatin1String("Tag 1") << tag1; } QString tag2 = taggedFile->getTagFormatV2(); if (!tag2.isEmpty()) { lst << QLatin1String("Tag 2") << tag2; } } return lst; } /** * Set tag from file name. * * @param tagMask tag bit (1 for tag 1, 2 for tag 2) */ void ScriptInterface::setTagFromFileName(int tagMask) { m_app->getTagsFromFilename(TrackData::tagVersionCast(tagMask)); } /** * Set tag from other tag. * * @param tagMask tag bit (1 for tag 1, 2 for tag 2) */ void ScriptInterface::setTagFromOtherTag(int tagMask) { m_app->copyToOtherTag(TrackData::tagVersionCast(tagMask)); } /** * Copy tag. * * @param tagMask tag bit (1 for tag 1, 2 for tag 2) */ void ScriptInterface::copyTag(int tagMask) { m_app->copyTags(TrackData::tagVersionCast(tagMask)); } /** * Paste tag. * * @param tagMask tag bit (1 for tag 1, 2 for tag 2) */ void ScriptInterface::pasteTag(int tagMask) { m_app->pasteTags(TrackData::tagVersionCast(tagMask)); } /** * Remove tag. * * @param tagMask tag bit (1 for tag 1, 2 for tag 2) */ void ScriptInterface::removeTag(int tagMask) { m_app->removeTags(TrackData::tagVersionCast(tagMask)); } /** * Reparse the configuration. * Automated configuration changes are possible by modifying * the configuration file and then reparsing the configuration. */ void ScriptInterface::reparseConfiguration() { m_app->readConfig(); } #if defined HAVE_PHONON || QT_VERSION >= 0x050000 /** * Play selected audio files. */ void ScriptInterface::playAudio() { m_app->playAudio(); } #endif #endif // HAVE_QTDBUS kid3-3.0.2/src/core/model/scriptinterface.h000066400000000000000000000223011224603511300205160ustar00rootroot00000000000000/** * \file scriptinterface.h * D-Bus script adaptor. * * \b Project: Kid3 * \author Urs Fleisch * \date 20 Dec 2007 * * Copyright (C) 2007-2012 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef SCRIPTINTERFACE_H #define SCRIPTINTERFACE_H #include "config.h" #ifdef HAVE_QTDBUS #include #include class Kid3Application; /** * Adaptor class for interface net.sourceforge.Kid3 * Create net.sourceforge.Kid3.xml with: * echo "#define HAVE_QTDBUS" >config.h * echo "#define HAVE_PHONON" >>config.h * qdbuscpp2xml scriptinterface.h >net.sourceforge.Kid3.xml * rm config.h */ class ScriptInterface : public QDBusAbstractAdaptor { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "net.sourceforge.Kid3") public: /** * Constructor. * * @param app parent application */ explicit ScriptInterface(Kid3Application* app); /** * Destructor. */ virtual ~ScriptInterface(); public slots: /** * Open file or directory. * * @param path path to file or directory * * @return true if ok. */ bool openDirectory(const QString& path); /** * Save all modified files. * * @return true if ok, * else the error message is available using getErrorMessage(). */ bool save(); /** * Get a detailed error message provided by some methods. * @return detailed error message. */ QString getErrorMessage() const; /** * Revert changes in the selected files. */ void revert(); /** * Import tags from a file. * * @param tagMask tag bit (1 for tag 1, 2 for tag 2) * @param path path of file, "clipboard" for import from clipboard * @param fmtIdx index of format * * @return true if ok. */ bool importFromFile(int tagMask, const QString& path, int fmtIdx); /** * Start an automatic batch import. * * @param tagMask tag mask (bit 0 for tag 1, bit 1 for tag 2) * @param profileName name of batch import profile to use * * @return true if profile found. */ bool batchImport(int tagMask, const QString& profileName); /** * Download album cover art into the picture frame of the selected files. * * @param url URL of picture file or album art resource * @param allFilesInDir true to add the image to all files in the directory */ void downloadAlbumArt(const QString& url, bool allFilesInDir); /** * Export tags to a file. * * @param tagMask tag bit (1 for tag 1, 2 for tag 2) * @param path path of file, "clipboard" for export to clipboard * @param fmtIdx index of format * * @return true if ok. */ bool exportToFile(int tagMask, const QString& path, int fmtIdx); /** * Create a playlist. * * @return true if ok. */ bool createPlaylist(); /** * Quit the application. * Omitted Q_NOREPLY because the Qt 3 moc chokes on it. */ void quit(); /** * Select all files. */ void selectAll(); /** * Deselect all files. */ void deselectAll(); /** * Set the first file as the current file. * * @return true if there is a first file. */ bool firstFile(); /** * Set the previous file as the current file. * * @return true if there is a previous file. */ bool previousFile(); /** * Set the next file as the current file. * * @return true if there is a next file. */ bool nextFile(); /** * Select the first file. * * @return true if there is a first file. */ bool selectFirstFile(); /** * Select the previous file. * * @return true if there is a previous file. */ bool selectPreviousFile(); /** * Select the next file. * * @return true if there is a next file. */ bool selectNextFile(); /** * Select the current file. * * @return true if there is a current file. */ bool selectCurrentFile(); /** * Expand or collapse the current file item if it is a directory. * A file list item is a directory if getFileName() returns a name with * '/' as the last character. * * @return true if current file item is a directory. */ bool expandDirectory(); /** * Apply the file name format. */ void applyFilenameFormat(); /** * Apply the tag format. */ void applyTagFormat(); /** * Apply text encoding. */ void applyTextEncoding(); /** * Set the directory name from the tags. * * @param tagMask tag mask (bit 0 for tag 1, bit 1 for tag 2) * @param format directory name format * @param create true to create, false to rename * * @return true if ok, * else the error message is available using getErrorMessage(). */ bool setDirNameFromTag(int tagMask, const QString& format, bool create); /** * Set subsequent track numbers in the selected files. * * @param tagMask tag mask (bit 0 for tag 1, bit 1 for tag 2) * @param firstTrackNr number to use for first file */ void numberTracks(int tagMask, int firstTrackNr); /** * Filter the files. * * @param expression filter expression */ void filter(const QString& expression); /** * Convert ID3v2.3 tags to ID3v2.4. */ void convertToId3v24(); /** * Convert ID3v2.4 tags to ID3v2.3. */ void convertToId3v23(); /** * Get path of directory. * * @return absolute path of directory. */ QString getDirectoryName(); /** * Get name of current file. * * @return absolute file name, ends with "/" if it is a directory. */ QString getFileName(); /** * Set name of selected file. * The file will be renamed when the directory is saved. * * @param name file name. */ void setFileName(const QString& name); /** * Set format to use when setting the filename from the tags. * * @param format file name format * @see setFileNameFromTag() */ void setFileNameFormat(const QString& format); /** * Set the file names of the selected files from the tags. * * @param tagMask tag bit (1 for tag 1, 2 for tag 2) * @see setFileNameFormat() */ void setFileNameFromTag(int tagMask); /** * Get value of frame. * To get binary data like a picture, the name of a file to write can be * added after the @a name, e.g. "Picture:/path/to/file". * * @param tagMask tag bit (1 for tag 1, 2 for tag 2) * @param name name of frame (e.g. "artist") */ QString getFrame(int tagMask, const QString& name); /** * Set value of frame. * For tag 2 (@a tagMask 2), if no frame with @a name exists, a new frame * is added, if @a value is empty, the frame is deleted. * To add binary data like a picture, a file can be added after the * @a name, e.g. "Picture:/path/to/file". * * @param tagMask tag bit (1 for tag 1, 2 for tag 2) * @param name name of frame (e.g. "artist") * @param value value of frame */ bool setFrame(int tagMask, const QString& name, const QString& value); /** * Get all frames of a tag. * * @param tagMask tag bit (1 for tag 1, 2 for tag 2) * * @return list with alternating frame names and values. */ QStringList getTag(int tagMask); /** * Get technical information about file. * Properties are Format, Bitrate, Samplerate, Channels, Duration, * Channel Mode, VBR, Tag 1, Tag 2. * Properties which are not available are omitted. * * @return list with alternating property names and values. */ QStringList getInformation(); /** * Set tag from file name. * * @param tagMask tag bit (1 for tag 1, 2 for tag 2) */ void setTagFromFileName(int tagMask); /** * Set tag from other tag. * * @param tagMask tag bit (1 for tag 1, 2 for tag 2) */ void setTagFromOtherTag(int tagMask); /** * Copy tag. * * @param tagMask tag bit (1 for tag 1, 2 for tag 2) */ void copyTag(int tagMask); /** * Paste tag. * * @param tagMask tag bit (1 for tag 1, 2 for tag 2) */ void pasteTag(int tagMask); /** * Remove tag. * * @param tagMask tag bit (1 for tag 1, 2 for tag 2) */ void removeTag(int tagMask); /** * Reparse the configuration. * Automated configuration changes are possible by modifying * the configuration file and then reparsing the configuration. */ void reparseConfiguration(); #if defined HAVE_PHONON || QT_VERSION >= 0x050000 /** * Play selected audio files. */ void playAudio(); #endif private slots: void onRenameActionsScheduled(); private: Kid3Application* m_app; QString m_errorMsg; }; #else // HAVE_QTDBUS #include // Just to suppress moc "No relevant classes found" warning. class ScriptInterface : public QObject { Q_OBJECT }; #endif // HAVE_QTDBUS #endif // SCRIPTINTERFACE_H kid3-3.0.2/src/core/model/taggedfileiconprovider.cpp000066400000000000000000000132761224603511300224160ustar00rootroot00000000000000/** * \file taggedfileiconprovider.cpp * Provides icons for tagged files. * * \b Project: Kid3 * \author Urs Fleisch * \date 29-Mar-2011 * * Copyright (C) 2011 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "taggedfileiconprovider.h" #include #include "taggedfile.h" #include "tagconfig.h" /* The bitmaps are stored here instead of using KDE bitmaps to make it work for the Qt only versions. */ /** picture for modified pixmap */ static const char* const modified_xpm[] = { "16 16 33 1", ". c None", "B c None", "A c None", "C c None", "D c None", "E c None", "# c #000000", "b c #006562", "j c #414041", "x c #525552", "f c #529594", "e c #52959c", "w c #5a555a", "v c #626162", "u c #626562", "r c #737173", "p c #737573", "q c #7b757b", "o c #838183", "m c #838583", "z c #8b8d8b", "l c #949194", "k c #9c959c", "i c #a4a1a4", "h c #a4a5a4", "y c #b4b6b4", "g c #bdb6bd", "a c #c5c2c5", "s c #c5c6c5", "c c #cdc6cd", "t c #dedade", "n c #eeeaee", "d c #ffffff", ".......##.......", "......#ab#......", ".....#cbde#.....", "....#abdddf#....", "...#gbddddde#...", "..#hijddddddf#..", ".#kjkljdddddd##.", "#mjnjmojddddjma#", "#jnpnjqrjddjqs#.", "#drtttjuvjjua#..", ".#dasajjwxws#...", "..#dyjzljxa#...A", "...#jrrjws#...AB", "....#cjxa#...ACB", ".....#cs#...ADE.", "......##...ABB.." }; /** picture for empty pixmap */ static const char* const null_xpm[] = { "16 16 2 1", "# c None", ". c None", ".#.#.#.#.#.#.#.#", "#.#.#.#.#.#.#.#.", ".#.#.#.#.#.#.#.#", "#.#.#.#.#.#.#.#.", ".#.#.#.#.#.#.#.#", "#.#.#.#.#.#.#.#.", ".#.#.#.#.#.#.#.#", "#.#.#.#.#.#.#.#.", ".#.#.#.#.#.#.#.#", "#.#.#.#.#.#.#.#.", ".#.#.#.#.#.#.#.#", "#.#.#.#.#.#.#.#.", ".#.#.#.#.#.#.#.#", "#.#.#.#.#.#.#.#.", ".#.#.#.#.#.#.#.#", "#.#.#.#.#.#.#.#." }; /** picture with V1 and V2 */ static const char* const v1v2_xpm[] = { "16 16 3 1", " c None", ". c #FFFFFF", "+ c #000000", " ", " .. .. . ", " .+ .+ .+ ", " .+ .+ .++ ", " .+.+ .+.+ ", " .+.+ .+ ", " .+ .+ ", " ", " .. .. .. ", " .+ .+ .++. ", " .+ .+.+ .+ ", " .+.+ .+ ", " .+.+ .+.. ", " .+ .++++ ", " ", " "}; /** picture with V1 */ static const char* const v1_xpm[] = { "16 16 3 1", " c None", ". c #FFFFFF", "+ c #000000", " ", " .. .. . ", " .+ .+ .+ ", " .+ .+ .++ ", " .+.+ .+.+ ", " .+.+ .+ ", " .+ .+ ", " ", " ", " ", " ", " ", " ", " ", " ", " "}; /** picture with V2 */ static const char* const v2_xpm[] = { "16 16 3 1", " c None", ". c #FFFFFF", "+ c #000000", " ", " ", " ", " ", " ", " ", " ", " ", " .. .. .. ", " .+ .+ .++. ", " .+ .+.+ .+ ", " .+.+ .+ ", " .+.+ .+.. ", " .+ .++++ ", " ", " "}; /** picture with NO TAG */ static const char* const notag_xpm[] = { "16 16 3 1", " c None", ". c #FFFFFF", "+ c #000000", " ", " .. .. .. ", " .+. .+ .++. ", " .++..+.+ .+ ", " .+.+.+.+ .+ ", " .+ .++.+..+ ", " .+ .+ .++ ", " ", " .... . .. ", " .+++ .+. .++ ", " .+ .+.+.+ .. ", " .+ .+++.+.++ ", " .+ .+.+.+..+ ", " .+ .+.+ .++ ", " ", " "}; /** * Constructor. */ TaggedFileIconProvider::TaggedFileIconProvider() : m_nullIcon(QPixmap((const char **)null_xpm)), m_modifiedIcon(QPixmap((const char **)modified_xpm)), m_v1v2Icon(QPixmap((const char **)v1v2_xpm)), m_v1Icon(QPixmap((const char **)v1_xpm)), m_v2Icon(QPixmap((const char **)v2_xpm)), m_notagIcon(QPixmap((const char **)notag_xpm)) { } /** * Get an icon for a tagged file. * * @param taggedFile tagged file * * @return icon for tagged file */ QIcon TaggedFileIconProvider::iconForTaggedFile(const TaggedFile* taggedFile) { if (taggedFile) { if (taggedFile->isChanged()) { return m_modifiedIcon; } else { if (!taggedFile->isTagInformationRead()) return m_nullIcon; if (taggedFile->hasTagV1()) return taggedFile->hasTagV2() ? m_v1v2Icon : m_v1Icon; else return taggedFile->hasTagV2() ? m_v2Icon : m_notagIcon; } } return QIcon(); } /** * Get background color for a tagged file. * * @param taggedFile tagged file * * @return background color for tagged file, invalid color if background * should not be set */ QColor TaggedFileIconProvider::backgroundForTaggedFile( const TaggedFile* taggedFile) { if (taggedFile && TagConfig::instance().markTruncations() && taggedFile->getTruncationFlags() != 0) return Qt::red; return QColor(); } kid3-3.0.2/src/core/model/taggedfileiconprovider.h000066400000000000000000000034411224603511300220540ustar00rootroot00000000000000/** * \file taggedfileiconprovider.h * Provides icons for tagged files. * * \b Project: Kid3 * \author Urs Fleisch * \date 29-Mar-2011 * * Copyright (C) 2011 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef TAGGEDFILEICONPROVIDER_H #define TAGGEDFILEICONPROVIDER_H #include #include class TaggedFile; /** * Provides icons for tagged files. */ class TaggedFileIconProvider { public: /** * Constructor. */ TaggedFileIconProvider(); /** * Get an icon for a tagged file. * * @param taggedFile tagged file * * @return icon for tagged file */ QIcon iconForTaggedFile(const TaggedFile* taggedFile); /** * Get background color for a tagged file. * * @param taggedFile tagged file * * @return background color for tagged file */ QColor backgroundForTaggedFile(const TaggedFile* taggedFile); private: /** Empty icon */ QIcon m_nullIcon; /** Icon for modified file */ QIcon m_modifiedIcon; /** Icon for V1V2 */ QIcon m_v1v2Icon; /** Icon for V1 */ QIcon m_v1Icon; /** Icon for V2 */ QIcon m_v2Icon; /** Icon for "no tag" */ QIcon m_notagIcon; }; #endif // TAGGEDFILEICONPROVIDER_H kid3-3.0.2/src/core/model/texttablemodel.cpp000066400000000000000000000102631224603511300207050ustar00rootroot00000000000000/** * \file texttablemodel.cpp * Model to display a text with tabulators in a table. * * \b Project: Kid3 * \author Urs Fleisch * \date 12 Aug 2011 * * Copyright (C) 2011-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "texttablemodel.h" #include /** * Constructor. * @param parent parent widget */ TextTableModel::TextTableModel(QObject* parent) : QAbstractTableModel(parent), m_hasHeaderLine(false) { setObjectName(QLatin1String("TextTableModel")); } /** * Destructor. */ TextTableModel::~TextTableModel() { } /** * Get item flags for index. * @param index model index * @return item flags */ Qt::ItemFlags TextTableModel::flags(const QModelIndex& index) const { if (index.isValid()) return Qt::ItemIsEnabled; return QAbstractTableModel::flags(index); } /** * Get data for a given role. * @param index model index * @param role item data role * @return data for role */ QVariant TextTableModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) return QVariant(); int rowNr = index.row() + (m_hasHeaderLine ? 1 : 0); if (!index.isValid() || rowNr < 0 || rowNr >= m_cells.size() || index.column() < 0) return QVariant(); const QStringList& row = m_cells.at(rowNr); if (index.column() < row.size() && (role == Qt::DisplayRole || role == Qt::EditRole)) { return row.at(index.column()); } return QVariant(); } /** * Get data for header section. * @param section column or row * @param orientation horizontal or vertical * @param role item data role * @return header data for role */ QVariant TextTableModel::headerData( int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole) return QVariant(); if (orientation == Qt::Horizontal && m_hasHeaderLine && !m_cells.isEmpty() && section < m_cells.first().size()) { return m_cells.first().at(section); } return section + 1; } /** * Get number of rows. * @param parent parent model index, invalid for table models * @return number of rows, * if parent is valid number of children (0 for table models) */ int TextTableModel::rowCount(const QModelIndex& parent) const { int numRows = m_cells.size(); if (m_hasHeaderLine && numRows > 0) --numRows; return parent.isValid() ? 0 : numRows; } /** * Get number of columns. * @param parent parent model index, invalid for table models * @return number of columns, * if parent is valid number of children (0 for table models) */ int TextTableModel::columnCount(const QModelIndex& parent) const { return parent.isValid() ? 0 : m_cells.isEmpty() ? 0 : m_cells.first().size(); } /** * Set the text to be displayed in the table. * @param text text with tab-separated columns and newline-separated rows * @param hasHeaderLine true if the first line is the header * @return true if the first line of the text contains a tab character. */ bool TextTableModel::setText(const QString& text, bool hasHeaderLine) { #if QT_VERSION >= 0x040600 beginResetModel(); #endif m_hasHeaderLine = hasHeaderLine; m_cells.clear(); QStringList lines = text.split(QRegExp(QLatin1String("[\\r\\n]+"))); if (lines.isEmpty() || lines.first().indexOf(QLatin1Char('\t')) == -1) { #if QT_VERSION >= 0x040600 endResetModel(); #else reset(); #endif return false; } for (int i = 0; i < lines.size(); ++i) { const QString& line = lines.at(i); if (i == lines.size() - 1 && line.isEmpty()) break; m_cells.append(line.split(QLatin1Char('\t'))); } #if QT_VERSION >= 0x040600 endResetModel(); #else reset(); #endif return true; } kid3-3.0.2/src/core/model/texttablemodel.h000066400000000000000000000057051224603511300203570ustar00rootroot00000000000000/** * \file texttablemodel.h * Model to display a text with tabulators in a table. * * \b Project: Kid3 * \author Urs Fleisch * \date 12 Aug 2011 * * Copyright (C) 2011 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef TEXTTABLEMODEL_H #define TEXTTABLEMODEL_H #include #include #include #include "kid3api.h" /** * Model to display a text with tabulators in a table. */ class KID3_CORE_EXPORT TextTableModel : public QAbstractTableModel { public: /** * Constructor. * @param parent parent widget */ explicit TextTableModel(QObject* parent = 0); /** * Destructor. */ virtual ~TextTableModel(); /** * Get item flags for index. * @param index model index * @return item flags */ virtual Qt::ItemFlags flags(const QModelIndex& index) const; /** * Get data for a given role. * @param index model index * @param role item data role * @return data for role */ virtual QVariant data(const QModelIndex& index, int role=Qt::DisplayRole) const; /** * Get data for header section. * @param section column or row * @param orientation horizontal or vertical * @param role item data role * @return header data for role */ virtual QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const; /** * Get number of rows. * @param parent parent model index, invalid for table models * @return number of rows, * if parent is valid number of children (0 for table models) */ virtual int rowCount(const QModelIndex& parent=QModelIndex()) const; /** * Get number of columns. * @param parent parent model index, invalid for table models * @return number of columns, * if parent is valid number of children (0 for table models) */ virtual int columnCount(const QModelIndex& parent=QModelIndex()) const; /** * Set the text to be displayed in the table. * @param text text with tab-separated columns and newline-separated rows * @param hasHeaderLine true if the first line is the header * @return true if the first line of the text contains a tab character. */ bool setText(const QString& text, bool hasHeaderLine); private: QList m_cells; bool m_hasHeaderLine; }; #endif // TEXTTABLEMODEL_H kid3-3.0.2/src/core/model/trackdatamodel.cpp000066400000000000000000000350251224603511300206520ustar00rootroot00000000000000/** * \file trackdatamodel.cpp * Model for table with track data. * * \b Project: Kid3 * \author Urs Fleisch * \date 15 May 2011 * * Copyright (C) 2011-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "trackdatamodel.h" #include "frametablemodel.h" /** * Constructor. * @param parent parent widget */ TrackDataModel::TrackDataModel(QObject* parent) : QAbstractTableModel(parent), m_diffCheckEnabled(false), m_maxDiff(0) { setObjectName(QLatin1String("TrackDataModel")); } /** * Destructor. */ TrackDataModel::~TrackDataModel() { } /** * Get item flags for index. * @param index model index * @return item flags */ Qt::ItemFlags TrackDataModel::flags(const QModelIndex& index) const { Qt::ItemFlags theFlags = QAbstractTableModel::flags(index); if (index.isValid()) { theFlags |= Qt::ItemIsSelectable | Qt::ItemIsEnabled; if (static_cast(m_frameTypes.at(index.column()).getType()) < FT_FirstTrackProperty) { theFlags |= Qt::ItemIsEditable; } if (index.column() == 0) { theFlags |= Qt::ItemIsUserCheckable; } } return theFlags; } /** * Get data for a given role. * @param index model index * @param role item data role * @return data for role */ QVariant TrackDataModel::data(const QModelIndex& index, int role) const { if (!index.isValid() || index.row() < 0 || index.row() >= static_cast(m_trackDataVector.size()) || index.column() < 0 || index.column() >= static_cast(m_frameTypes.size())) return QVariant(); if (role == Qt::DisplayRole || role == Qt::EditRole) { const ImportTrackData& trackData = m_trackDataVector.at(index.row()); Frame::ExtendedType type = m_frameTypes.at(index.column()); int typeOrProperty = static_cast(type.getType()); if (typeOrProperty < FT_FirstTrackProperty) { QString value(trackData.getValue(type)); if (!value.isNull()) return value; } else { switch (typeOrProperty) { case FT_FilePath: return trackData.getAbsFilename(); case FT_FileName: return trackData.getFilename(); case FT_Duration: if (int duration = trackData.getFileDuration()) { return TaggedFile::formatTime(duration); } break; case FT_ImportDuration: if (int duration = trackData.getImportDuration()) { return TaggedFile::formatTime(duration); } break; default: ; } } } else if (role == FrameTableModel::FrameTypeRole) { return m_frameTypes.at(index.column()).getType(); } else if (role == Qt::BackgroundColorRole) { if (index.column() == 0 && m_diffCheckEnabled) { const ImportTrackData& trackData = m_trackDataVector.at(index.row()); int diff = trackData.getTimeDifference(); if (diff >= 0) { return diff > m_maxDiff ? QBrush(Qt::red) : Qt::NoBrush; } } } else if (role == Qt::CheckStateRole && index.column() == 0) { return m_trackDataVector.at(index.row()).isEnabled() ? Qt::Checked : Qt::Unchecked; } return QVariant(); } /** * Set data for a given role. * @param index model index * @param value data value * @param role item data role * @return true if successful */ bool TrackDataModel::setData(const QModelIndex& index, const QVariant& value, int role) { if (!index.isValid() || index.row() < 0 || index.row() >= static_cast(m_trackDataVector.size()) || index.column() < 0 || index.column() >= static_cast(m_frameTypes.size())) return false; if (role == Qt::EditRole) { ImportTrackData& trackData = m_trackDataVector[index.row()]; Frame::ExtendedType type = m_frameTypes.at(index.column()); if (static_cast(type.getType()) >= FT_FirstTrackProperty) return false; trackData.setValue(type, value.toString()); return true; } else if (role == Qt::CheckStateRole && index.column() == 0) { bool isChecked(value.toInt() == Qt::Checked); if (isChecked != m_trackDataVector.at(index.row()).isEnabled()) { m_trackDataVector[index.row()].setEnabled(isChecked); emit dataChanged(index, index); } return true; } return false; } /** * Get data for header section. * @param section column or row * @param orientation horizontal or vertical * @param role item data role * @return header data for role */ QVariant TrackDataModel::headerData( int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole) return QVariant(); if (orientation == Qt::Horizontal && section < m_frameTypes.size()) { Frame::ExtendedType type = m_frameTypes.at(section); int typeOrProperty = static_cast(type.getType()); if (typeOrProperty < FT_FirstTrackProperty) { return typeOrProperty == Frame::FT_Track ? tr("Track") // shorter header for track number : FrameTableModel::getDisplayName(type.getName()); } else { switch (typeOrProperty) { case FT_FilePath: return tr("Absolute path to file"); case FT_FileName: return tr("Filename"); case FT_Duration: return tr("Duration"); case FT_ImportDuration: return tr("Length"); default: ; } } } else if (orientation == Qt::Vertical && section < m_trackDataVector.size()) { int fileDuration = m_trackDataVector.at(section).getFileDuration(); if (fileDuration > 0) { return TaggedFile::formatTime(fileDuration); } } return section + 1; } /** * Get number of rows. * @param parent parent model index, invalid for table models * @return number of rows, * if parent is valid number of children (0 for table models) */ int TrackDataModel::rowCount(const QModelIndex& parent) const { return parent.isValid() ? 0 : m_trackDataVector.size(); } /** * Get number of columns. * @param parent parent model index, invalid for table models * @return number of columns, * if parent is valid number of children (0 for table models) */ int TrackDataModel::columnCount(const QModelIndex& parent) const { return parent.isValid() ? 0 : m_frameTypes.size(); } /** * Insert rows. * @param row rows are inserted before this row, if 0 at the begin, * if rowCount() at the end * @param count number of rows to insert * @param parent parent model index, invalid for table models * @return true if successful */ bool TrackDataModel::insertRows(int row, int count, const QModelIndex&) { beginInsertRows(QModelIndex(), row, row + count - 1); m_trackDataVector.insert(row, count, ImportTrackData()); endInsertRows(); return true; } /** * Remove rows. * @param row rows are removed starting with this row * @param count number of rows to remove * @param parent parent model index, invalid for table models * @return true if successful */ bool TrackDataModel::removeRows(int row, int count, const QModelIndex&) { beginRemoveRows(QModelIndex(), row, row + count - 1); m_trackDataVector.remove(row, count); endRemoveRows(); return true; } /** * Insert columns. * @param column columns are inserted before this column, if 0 at the begin, * if columnCount() at the end * @param count number of columns to insert * @param parent parent model index, invalid for table models * @return true if successful */ bool TrackDataModel::insertColumns(int column, int count, const QModelIndex&) { beginInsertColumns(QModelIndex(), column, column + count - 1); for (int i = 0; i < count; ++i) m_frameTypes.insert(column, Frame::ExtendedType()); endInsertColumns(); return true; } /** * Remove columns. * @param column columns are removed starting with this column * @param count number of columns to remove * @param parent parent model index, invalid for table models * @return true if successful */ bool TrackDataModel::removeColumns(int column, int count, const QModelIndex&) { beginRemoveColumns(QModelIndex(), column, column + count - 1); for (int i = 0; i < count; ++i) m_frameTypes.removeAt(column); endRemoveColumns(); return true; } /** * Set the check state of all tracks in the table. * * @param checked true to check the tracks */ void TrackDataModel::setAllCheckStates(bool checked) { for (int row = 0; row < rowCount(); ++row) { m_trackDataVector[row].setEnabled(checked); } } /** * Set time difference check configuration. * * @param enable true to enable check * @param maxDiff maximum allowed time difference */ void TrackDataModel::setTimeDifferenceCheck(bool enable, int maxDiff) { bool changed = m_diffCheckEnabled != enable || m_maxDiff != maxDiff; m_diffCheckEnabled = enable; m_maxDiff = maxDiff; if (changed) emit dataChanged(index(0,0), index(rowCount() - 1, 0)); } /** * Calculate accuracy of imported track data. * @return accuracy in percent, -1 if unknown. */ int TrackDataModel::calculateAccuracy() const { int numImportTracks = 0, numTracks = 0, numMismatches = 0, numMatches = 0; for (ImportTrackDataVector::const_iterator it = m_trackDataVector.constBegin(); it != m_trackDataVector.constEnd(); ++it) { const ImportTrackData& trackData = *it; int diff = trackData.getTimeDifference(); if (diff >= 0) { if (diff > 3) { ++numMismatches; } else { ++numMatches; } } else { // no durations available => try to match using file name and title QSet titleWords = trackData.getTitleWords(); int numWords = titleWords.size(); if (numWords > 0) { QSet fileWords = trackData.getFilenameWords(); if (fileWords.size() < numWords) { numWords = fileWords.size(); } int wordMatch = numWords > 0 ? 100 * (fileWords & titleWords).size() / numWords : 0; if (wordMatch < 75) { ++numMismatches; } else { ++numMatches; } } } if (trackData.getImportDuration() != 0 || !trackData.getTitle().isEmpty()) { ++numImportTracks; } if (trackData.getFileDuration() != 0) { ++numTracks; } } if (numTracks > 0 && numImportTracks > 0 && (numMatches > 0 || numMismatches > 0)) { return numMatches * 100 / numTracks; } return -1; } /** * Get frame for index. * @param index model index * @return frame, 0 if no frame. */ const Frame* TrackDataModel::getFrameOfIndex(const QModelIndex& index) const { if (!index.isValid() || index.row() < 0 || index.row() >= static_cast(m_trackDataVector.size()) || index.column() < 0 || index.column() >= static_cast(m_frameTypes.size())) return 0; const ImportTrackData& trackData = m_trackDataVector.at(index.row()); Frame::ExtendedType type = m_frameTypes.at(index.column()); if (static_cast(type.getType()) >= FT_FirstTrackProperty) return 0; FrameCollection::const_iterator it = trackData.findByExtendedType(type); return it != trackData.end() ? &(*it) : 0; } /** * Set track data. * @param trackDataVector track data */ void TrackDataModel::setTrackData(const ImportTrackDataVector& trackDataVector) { static const int initFrameTypes[] = { FT_ImportDuration, FT_FileName, FT_FilePath, Frame::FT_Track, Frame::FT_Title, Frame::FT_Artist, Frame::FT_Album, Frame::FT_Date, Frame::FT_Genre, Frame::FT_Comment }; unsigned numStandardColumns = sizeof(initFrameTypes) / sizeof(initFrameTypes[0]); QList newFrameTypes; for (unsigned i = 0; i < numStandardColumns; ++i) { newFrameTypes.append( Frame::ExtendedType(static_cast(initFrameTypes[i]), QLatin1String(""))); } for (ImportTrackDataVector::const_iterator tit = trackDataVector.constBegin(); tit != trackDataVector.constEnd(); ++tit) { for (FrameCollection::const_iterator fit = tit->begin(); fit != tit->end(); ++fit) { Frame::ExtendedType type = fit->getExtendedType(); if (type.getType() > Frame::FT_LastV1Frame && !newFrameTypes.contains(type)) { newFrameTypes.append(type); } } } int oldNumTypes = m_frameTypes.size(); int newNumTypes = newFrameTypes.size(); int numColumnsChanged = qMin(oldNumTypes, newNumTypes); if (newNumTypes < oldNumTypes) beginRemoveColumns(QModelIndex(), newNumTypes, oldNumTypes - 1); else if (newNumTypes > oldNumTypes) beginInsertColumns(QModelIndex(), oldNumTypes, newNumTypes - 1); m_frameTypes = newFrameTypes; if (newNumTypes < oldNumTypes) endRemoveColumns(); else if (newNumTypes > oldNumTypes) endInsertColumns(); int oldNumTracks = m_trackDataVector.size(); int newNumTracks = trackDataVector.size(); int numRowsChanged = qMin(oldNumTracks, newNumTracks); if (newNumTracks < oldNumTracks) beginRemoveRows(QModelIndex(), newNumTracks, oldNumTracks - 1); else if (newNumTracks > oldNumTracks) beginInsertRows(QModelIndex(), oldNumTracks, newNumTracks - 1); m_trackDataVector = trackDataVector; if (newNumTracks < oldNumTracks) endRemoveRows(); else if (newNumTracks > oldNumTracks) endInsertRows(); if (numRowsChanged > 0) emit dataChanged( index(0, 0), index(numRowsChanged - 1, numColumnsChanged - 1)); } /** * Get track data. * @return track data */ ImportTrackDataVector TrackDataModel::getTrackData() const { return m_trackDataVector; } /** * Get the frame type for a column. * @param column model column * @return frame type of Frame::Type or TrackDataModel::TrackProperties, * -1 if column invalid. */ int TrackDataModel::frameTypeForColumn(int column) const { return column < m_frameTypes.size() ? m_frameTypes.at(column).getType() : -1; } /** * Get column for a frame type. * @param frameType frame type of Frame::Type or * TrackDataModel::TrackProperties. * @return model column, -1 if not found. */ int TrackDataModel::columnForFrameType(int frameType) const { return m_frameTypes.indexOf( Frame::ExtendedType(static_cast(frameType), QLatin1String(""))); } kid3-3.0.2/src/core/model/trackdatamodel.h000066400000000000000000000146141224603511300203200ustar00rootroot00000000000000/** * \file trackdatamodel.h * Model for table with track data. * * \b Project: Kid3 * \author Urs Fleisch * \date 15 May 2011 * * Copyright (C) 2011 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef TRACKDATAMODEL_H #define TRACKDATAMODEL_H #include #include #include "trackdata.h" #include "kid3api.h" /** * Model for table with track data. */ class KID3_CORE_EXPORT TrackDataModel : public QAbstractTableModel { Q_OBJECT public: /** Additional track properties extending Frame::Type. */ enum TrackProperties { FT_FirstTrackProperty = Frame::FT_UnknownFrame + 1, FT_FilePath = FT_FirstTrackProperty, FT_Duration, FT_ImportDuration, FT_FileName }; /** * Constructor. * @param parent parent widget */ explicit TrackDataModel(QObject* parent = 0); /** * Destructor. */ virtual ~TrackDataModel(); /** * Get item flags for index. * @param index model index * @return item flags */ virtual Qt::ItemFlags flags(const QModelIndex& index) const; /** * Get data for a given role. * @param index model index * @param role item data role * @return data for role */ virtual QVariant data(const QModelIndex& index, int role=Qt::DisplayRole) const; /** * Set data for a given role. * @param index model index * @param value data value * @param role item data role * @return true if successful */ virtual bool setData(const QModelIndex& index, const QVariant& value, int role=Qt::EditRole); /** * Get data for header section. * @param section column or row * @param orientation horizontal or vertical * @param role item data role * @return header data for role */ virtual QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const; /** * Set data for header section. * Not supported. * @return false */ virtual bool setHeaderData(int, Qt::Orientation, const QVariant&, int=Qt::EditRole) { return false; } /** * Get number of rows. * @param parent parent model index, invalid for table models * @return number of rows, * if parent is valid number of children (0 for table models) */ virtual int rowCount(const QModelIndex& parent=QModelIndex()) const; /** * Get number of columns. * @param parent parent model index, invalid for table models * @return number of columns, * if parent is valid number of children (0 for table models) */ virtual int columnCount(const QModelIndex& parent=QModelIndex()) const; /** * Insert rows. * @param row rows are inserted before this row, if 0 at the begin, * if rowCount() at the end * @param count number of rows to insert * @param parent parent model index, invalid for table models * @return true if successful */ virtual bool insertRows(int row, int count, const QModelIndex& parent=QModelIndex()); /** * Remove rows. * @param row rows are removed starting with this row * @param count number of rows to remove * @param parent parent model index, invalid for table models * @return true if successful */ virtual bool removeRows(int row, int count, const QModelIndex& parent=QModelIndex()); /** * Insert columns. * @param column columns are inserted before this column, if 0 at the begin, * if columnCount() at the end * @param count number of columns to insert * @param parent parent model index, invalid for table models * @return true if successful */ virtual bool insertColumns(int column, int count, const QModelIndex& parent=QModelIndex()); /** * Remove columns. * @param column columns are removed starting with this column * @param count number of columns to remove * @param parent parent model index, invalid for table models * @return true if successful */ virtual bool removeColumns(int column, int count, const QModelIndex& parent=QModelIndex()); /** * Set the check state of all tracks in the table. * * @param checked true to check the tracks */ void setAllCheckStates(bool checked); /** * Set time difference check configuration. * * @param enable true to enable check * @param maxDiff maximum allowed time difference */ void setTimeDifferenceCheck(bool enable, int maxDiff); /** * Calculate accuracy of imported track data. * @return accuracy in percent, -1 if unknown. */ int calculateAccuracy() const; /** * Get frame for index. * @param index model index * @return frame, 0 if no frame. */ const Frame* getFrameOfIndex(const QModelIndex& index) const; /** * Set track data. * @param trackDataVector track data */ void setTrackData(const ImportTrackDataVector& trackDataVector); /** * Get track data. * @return track data */ ImportTrackDataVector getTrackData() const; /** * Constant reference to track data. * @return track data */ const ImportTrackDataVector& trackData() const { return m_trackDataVector; } /** * Get the frame type for a column. * @param column model column * @return frame type of Frame::Type or TrackDataModel::TrackProperties, * -1 if column invalid. */ int frameTypeForColumn(int column) const; /** * Get column for a frame type. * @param frameType frame type of Frame::Type or * TrackDataModel::TrackProperties. * @return model column, -1 if not found. */ int columnForFrameType(int frameType) const; private: ImportTrackDataVector m_trackDataVector; QList m_frameTypes; bool m_diffCheckEnabled; int m_maxDiff; }; #endif // TRACKDATAMODEL_H kid3-3.0.2/src/core/tags/000077500000000000000000000000001224603511300150205ustar00rootroot00000000000000kid3-3.0.2/src/core/tags/Sources.cmake000066400000000000000000000003331224603511300174440ustar00rootroot00000000000000set(tags_SRCS tags/attributedata.cpp tags/genres.cpp tags/formatreplacer.cpp tags/frame.cpp tags/pictureframe.cpp tags/taggedfile.cpp tags/itaggedfilefactory.cpp tags/trackdata.cpp ) set(tags_MOC_HDRS) kid3-3.0.2/src/core/tags/attributedata.cpp000066400000000000000000000146631224603511300203730ustar00rootroot00000000000000/** * \file attributedata.cpp * String representation of attribute data. * * \b Project: Kid3 * \author Urs Fleisch * \date 28 Mar 2009 * * Copyright (C) 2009-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "attributedata.h" #include /** * Constructor. * * @param name owner of Windows media PRIV frame */ AttributeData::AttributeData(const QString& name) { /** PRIV-owner and type of Windows media PRIV frames */ static const struct TypeOfWmPriv { const char* str; Type type; } typeOfWmPriv[] = { { "AverageLevel", DWord }, { "PeakValue", DWord }, { "WM/AlbumArtist", Utf16 }, { "WM/AuthorURL", Utf16 }, { "WM/BeatsPerMinute", Utf16 }, { "WM/Composer", Utf16 }, { "WM/Conductor", Utf16 }, { "WM/ContentDistributor", Utf16 }, { "WM/ContentGroupDescription", Utf16 }, { "WM/EncodedBy", Utf16 }, { "WM/EncodingSettings", Utf16 }, { "WM/EncodingTime", Binary }, { "WM/Genre", Utf16 }, { "WM/InitialKey", Utf16 }, { "WM/Language", Utf16 }, { "WM/Lyrics", Utf16 }, { "WM/Lyrics_Synchronised", Binary }, { "WM/MCDI", Binary }, { "WM/MediaClassPrimaryID", Guid }, { "WM/MediaClassSecondaryID", Guid }, { "WM/Mood", Utf16 }, { "WM/ParentalRating", Utf16 }, { "WM/PartOfSet", Utf16 }, { "WM/Period", Utf16 }, { "WM/Picture", Binary }, { "WM/Producer", Utf16 }, { "WM/PromotionURL", Utf16 }, { "WM/Provider", Utf16 }, { "WM/Publisher", Utf16 }, { "WM/SubTitle", Utf16 }, { "WM/ToolName", Utf16 }, { "WM/ToolVersion", Utf16 }, { "WM/TrackNumber", Utf16 }, { "WM/UniqueFileIdentifier", Utf16 }, { "WM/UserWebURL", Binary }, { "WM/WMCollectionGroupID", Guid }, { "WM/WMCollectionID", Guid }, { "WM/WMContentID", Guid }, { "WM/Writer", Utf16 } }; static QMap strNumMap; if (strNumMap.empty()) { // first time initialization for (unsigned i = 0; i < sizeof(typeOfWmPriv) / sizeof(typeOfWmPriv[0]); ++i) { strNumMap.insert(QString::fromLatin1(typeOfWmPriv[i].str), typeOfWmPriv[i].type); } } QMap::const_iterator it = strNumMap.find(name); m_type = (it != strNumMap.end()) ? static_cast(*it) : Unknown; } /** * Convert attribute data to string. * * @param data byte array with data * @param str result string * * @return true if ok. */ bool AttributeData::toString(const QByteArray& data, QString& str) { switch (m_type) { case Utf16: { const ushort* unicode = reinterpret_cast(data.data()); int size = data.size() / 2; while (size > 0 && unicode[size - 1] == 0) { --size; } str = QString::fromUtf16(unicode, size); return true; } case Guid: if (data.size() == 16) { str.clear(); for (int i = 0; i < 16; ++i) { if (i == 4 || i == 6 || i == 8 || i == 10) { str += QLatin1Char('-'); } unsigned char c = (unsigned char)data[i]; unsigned char d = c >> 4; str += QLatin1Char(d >= 10 ? d - 10 + 'A' : d + '0'); d = c & 0x0f; str += QLatin1Char(d >= 10 ? d - 10 + 'A' : d + '0'); } return true; } break; case DWord: if (data.size() == 4) { ulong num = 0; for (int i = 3; i >= 0; --i) { num <<= 8; num |= static_cast(data[i]); } str.setNum(num); return true; } break; case Binary: case Unknown: default: ; } return false; } /** * Convert attribute data string to byte array. * * @param str string representation of data * @param data result data * * @return true if ok. */ bool AttributeData::toByteArray(const QString& str, QByteArray& data) { switch (m_type) { case Utf16: { const ushort* unicode = str.utf16(); data = QByteArray(reinterpret_cast(unicode), (str.length() + 1) * 2); return true; } case Guid: { QString hexStr(str.toUpper()); hexStr.remove(QLatin1Char('-')); if (hexStr.length() == 32) { unsigned char buf[16]; unsigned char* bufPtr = buf; for (int i = 0; i < 32;) { unsigned char h = (unsigned char)hexStr[i++].toLatin1(); unsigned char l = (unsigned char)hexStr[i++].toLatin1(); if (!((h >= '0' && h <= '9') || (h >= 'A' && h <= 'F')) || !((l >= '0' && l <= '9') || (l >= 'A' && l <= 'F'))) { return false; } *bufPtr++ = ((h >= 'A' ? h + 10 - 'A' : h - '0') << 4) | (l >= 'A' ? l + 10 - 'A' : l - '0'); } data = QByteArray(reinterpret_cast(buf), 16); return true; } break; } case DWord: { bool ok; ulong num = str.toULong(&ok); if (ok) { data.resize(4); for (int i = 0; i < 4; ++i) { data[i] = num & 0xff; num >>= 8; } return true; } break; } case Binary: case Unknown: default: ; } return false; } /** * Check if a string represents a hexadecimal number, i.e. * contains only characters 0..9, A..F. * * @param str string to check * @param lastAllowedLetter last allowed character (normally 'F') * @param additionalChars additional allowed characters * * @return true if string has hex format. */ bool AttributeData::isHexString(const QString& str, char lastAllowedLetter, const QString& additionalChars) { if (str.isEmpty()) { return false; } for (int i = 0; i < static_cast(str.length()); ++i) { char c = str[i].toLatin1(); if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= lastAllowedLetter) || additionalChars.indexOf(QLatin1Char(c)) != -1)) { return false; } } return true; } kid3-3.0.2/src/core/tags/attributedata.h000066400000000000000000000052151224603511300200310ustar00rootroot00000000000000/** * \file attributedata.h * String representation of attribute data. * * \b Project: Kid3 * \author Urs Fleisch * \date 28 Mar 2009 * * Copyright (C) 2009 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef ATTRIBUTEDATA_H #define ATTRIBUTEDATA_H #include #include #include "kid3api.h" /** Attribute data used e.g. by Windows Media Player. */ class KID3_CORE_EXPORT AttributeData { public: /** Attribute data types. */ enum Type { Unknown, /**< Unknown type */ Utf16, /**< UTF-16 encoded, zero-terminated Unicode string */ Guid, /**< 128-bit GUID */ DWord, /**< 32-bit value little-endian */ Binary /**< Binary data */ }; /** * Constructor. * * @param type type */ explicit AttributeData(Type type) { m_type = type; } /** * Constructor. * * @param name owner of Windows media PRIV frame */ explicit AttributeData(const QString& name); /** * Destructor. */ ~AttributeData() {} /** * Get type. * @return type. */ Type getType() const { return m_type; } /** * Convert attribute data to string. * * @param data byte array with data * @param str result string * * @return true if ok. */ bool toString(const QByteArray& data, QString& str); /** * Convert attribute data string to byte array. * * @param str string representation of data * @param data result data * * @return true if ok. */ bool toByteArray(const QString& str, QByteArray& data); /** * Check if a string represents a hexadecimal number, i.e. * contains only characters 0..9, A..F. * * @param str string to check * @param lastAllowedLetter last allowed character (normally 'F') * @param additionalChars additional allowed characters * * @return true if string has hex format. */ static bool isHexString(const QString& str, char lastAllowedLetter = 'F', const QString& additionalChars = QString()); private: Type m_type; }; #endif // ATTRIBUTEDATA_H kid3-3.0.2/src/core/tags/formatreplacer.cpp000066400000000000000000000121211224603511300205270ustar00rootroot00000000000000/** * \file formatreplacer.cpp * Replaces format codes in a string. * * \b Project: Kid3 * \author Urs Fleisch * \date 06 Jul 2008 * * Copyright (C) 2008-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "formatreplacer.h" #include /** * Constructor. * * @param str string with format codes */ FormatReplacer::FormatReplacer(const QString& str) : m_str(str) {} /** * Destructor. */ FormatReplacer::~FormatReplacer() {} /** * Replace escaped characters. * Replaces the escaped characters ("\n", "\t", "\r", "\\", "\a", "\b", * "\f", "\v") with the corresponding characters. */ void FormatReplacer::replaceEscapedChars() { if (!m_str.isEmpty()) { const int numEscCodes = 8; const QChar escCode[numEscCodes] = { QLatin1Char('n'), QLatin1Char('t'), QLatin1Char('r'), QLatin1Char('\\'), QLatin1Char('a'), QLatin1Char('b'), QLatin1Char('f'), QLatin1Char('v')}; const char escChar[numEscCodes] = { '\n', '\t', '\r', '\\', '\a', '\b', '\f', '\v'}; for (int pos = 0; pos < static_cast(m_str.length());) { pos = m_str.indexOf(QLatin1Char('\\'), pos); if (pos == -1) break; ++pos; for (int k = 0;; ++k) { if (k >= numEscCodes) { // invalid code at pos ++pos; break; } if (m_str[pos] == escCode[k]) { // code found, replace it m_str.replace(pos - 1, 2, QLatin1Char(escChar[k])); break; } } } } } /** * Replace percent codes. * * @param flags flags: FSF_SupportUrlEncode to support modifier u * (with code c "%uc") to URL encode, * FSF_ReplaceSeparators to replace directory separators * ('/', '\\', ':') in tags, * FSF_SupportHtmlEscape to support modifier h * (with code c "%hc") to replace HTML metacharacters * ('<', '>', '&', '"', ''', non-ascii) in tags. */ void FormatReplacer::replacePercentCodes(unsigned flags) { if (!m_str.isEmpty()) { for (int pos = 0; pos < static_cast(m_str.length());) { pos = m_str.indexOf(QLatin1Char('%'), pos); if (pos == -1) break; int codePos = pos + 1; int codeLen = 0; bool urlEncode = false; bool htmlEscape = false; QString repl; if ((flags & FSF_SupportUrlEncode) && m_str[codePos] == QLatin1Char('u')) { ++codePos; urlEncode = true; } if ((flags & FSF_SupportHtmlEscape) && m_str[codePos] == QLatin1Char('h')) { ++codePos; htmlEscape = true; } if (m_str[codePos] == QLatin1Char('{')) { int closingBracePos = m_str.indexOf(QLatin1Char('}'), codePos + 1); if (closingBracePos > codePos + 1) { QString longCode = m_str.mid(codePos + 1, closingBracePos - codePos - 1).toLower(); repl = getReplacement(longCode); codeLen = closingBracePos - pos + 1; } } else { repl = getReplacement(QString(m_str[codePos])); codeLen = codePos - pos + 1; } if (codeLen > 0) { if (flags & FSF_ReplaceSeparators) { repl.replace(QLatin1Char('/'), QLatin1Char('-')); repl.replace(QLatin1Char('\\'), QLatin1Char('-')); repl.replace(QLatin1Char(':'), QLatin1Char('-')); } if (urlEncode) { repl = QString::fromLatin1(QUrl::toPercentEncoding(repl)); } if (htmlEscape) { repl = escapeHtml(repl); } if (!repl.isNull() || codeLen > 2) { m_str.replace(pos, codeLen, repl); pos += repl.length(); } else { ++pos; } } else { ++pos; } } } } /** * Converts the plain text string @a plain to a HTML string with * HTML metacharacters replaced by HTML entities. * @param plain plain text * @return html text with HTML entities. */ QString FormatReplacer::escapeHtml(const QString& plain) { QString rich; rich.reserve(int(plain.length() * qreal(1.1))); for (int i = 0; i < plain.length(); ++i) { ushort ch = plain.at(i).unicode(); if (ch == '<') rich += QLatin1String("<"); else if (ch == '>') rich += QLatin1String(">"); else if (ch == '&') rich += QLatin1String("&"); else if (ch == '"') rich += QLatin1String("""); else if (ch == '\'') rich += QLatin1String("'"); else if (ch >= 128) rich += QString(QLatin1String("&#%1;")).arg(ch); else rich += plain.at(i); } return rich; } kid3-3.0.2/src/core/tags/formatreplacer.h000066400000000000000000000060311224603511300201770ustar00rootroot00000000000000/** * \file formatreplacer.h * Replaces format codes in a string. * * \b Project: Kid3 * \author Urs Fleisch * \date 06 Jul 2008 * * Copyright (C) 2008-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef FORMATREPLACER_H #define FORMATREPLACER_H #include #include "kid3api.h" /** * Replaces format codes in a string. */ class KID3_CORE_EXPORT FormatReplacer { public: /** Flags for replacePercentCodes(). */ enum FormatStringFlags { FSF_SupportUrlEncode = (1 << 0), FSF_ReplaceSeparators = (1 << 1), FSF_SupportHtmlEscape = (1 << 2) }; /** * Constructor. * * @param str string with format codes */ explicit FormatReplacer(const QString& str = QString()); /** * Destructor. */ virtual ~FormatReplacer(); /** * Set string with format codes. * @param str string with format codes */ void setString(const QString& str) { m_str = str; } /** * Get string. * The string set with setString() can be modified using * replaceEscapedChars() and replacePercentCodes(). * @return string. */ QString getString() const { return m_str; } /** * Replace escaped characters. * Replaces the escaped characters ("\n", "\t", "\r", "\\", "\a", "\b", * "\f", "\v") with the corresponding characters. */ void replaceEscapedChars(); /** * Replace percent codes. * * @param flags flags: FSF_SupportUrlEncode to support modifier u * (with code c "%uc") to URL encode, * FSF_ReplaceSeparators to replace directory separators * ('/', '\\', ':') in tags, * FSF_SupportHtmlEscape to support modifier h * (with code c "%hc") to replace HTML metacharacters * ('<', '>', '&', '"', ''', non-ascii) in tags. */ void replacePercentCodes(unsigned flags = 0); /** * Converts the plain text string @a plain to a HTML string with * HTML metacharacters replaced by HTML entities. * @param plain plain text * @return html text with HTML entities. */ static QString escapeHtml(const QString& plain); protected: /** * Replace a format code (one character %c or multiple characters %{chars}). * * @param code format code * * @return replacement string, * QString::null if code not found. */ virtual QString getReplacement(const QString& code) const = 0; private: QString m_str; }; #endif // FORMATREPLACER_H kid3-3.0.2/src/core/tags/frame.cpp000066400000000000000000000717551224603511300166350ustar00rootroot00000000000000/** * \file frame.cpp * Generalized frame. * * \b Project: Kid3 * \author Urs Fleisch * \date 25 Aug 2007 * * Copyright (C) 2007-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "frame.h" #include #include #include #include "pictureframe.h" namespace { /** * Get name of frame from type. * * @param type type * * @return name. */ const char* getNameFromType(Frame::Type type) { static const char* const names[] = { QT_TRANSLATE_NOOP("@default", "Title"), // FT_Title, QT_TRANSLATE_NOOP("@default", "Artist"), // FT_Artist, QT_TRANSLATE_NOOP("@default", "Album"), // FT_Album, QT_TRANSLATE_NOOP("@default", "Comment"), // FT_Comment, QT_TRANSLATE_NOOP("@default", "Date"), // FT_Date, QT_TRANSLATE_NOOP("@default", "Track Number"), // FT_Track, QT_TRANSLATE_NOOP("@default", "Genre"), // FT_Genre, // FT_LastV1Frame = FT_Track, QT_TRANSLATE_NOOP("@default", "Album Artist"), // FT_AlbumArtist QT_TRANSLATE_NOOP("@default", "Arranger"), // FT_Arranger, QT_TRANSLATE_NOOP("@default", "Author"), // FT_Author, QT_TRANSLATE_NOOP("@default", "BPM"), // FT_Bpm, QT_TRANSLATE_NOOP("@default", "Catalog Number"), // FT_CatalogNumber, QT_TRANSLATE_NOOP("@default", "Compilation"), // FT_Compilation, QT_TRANSLATE_NOOP("@default", "Composer"), // FT_Composer, QT_TRANSLATE_NOOP("@default", "Conductor"), // FT_Conductor, QT_TRANSLATE_NOOP("@default", "Copyright"), // FT_Copyright, QT_TRANSLATE_NOOP("@default", "Disc Number"), // FT_Disc, QT_TRANSLATE_NOOP("@default", "Encoded-by"), // FT_EncodedBy, QT_TRANSLATE_NOOP("@default", "Encoder Settings"), // FT_EncoderSettings, QT_TRANSLATE_NOOP("@default", "Encoding Time"), // FT_EncodingTime, QT_TRANSLATE_NOOP("@default", "Grouping"), // FT_Grouping, QT_TRANSLATE_NOOP("@default", "Initial Key"), // FT_InitialKey, QT_TRANSLATE_NOOP("@default", "ISRC"), // FT_Isrc, QT_TRANSLATE_NOOP("@default", "Language"), // FT_Language, QT_TRANSLATE_NOOP("@default", "Lyricist"), // FT_Lyricist, QT_TRANSLATE_NOOP("@default", "Lyrics"), // FT_Lyrics, QT_TRANSLATE_NOOP("@default", "Media"), // FT_Media, QT_TRANSLATE_NOOP("@default", "Mood"), // FT_Mood, QT_TRANSLATE_NOOP("@default", "Original Album"), // FT_OriginalAlbum, QT_TRANSLATE_NOOP("@default", "Original Artist"), // FT_OriginalArtist, QT_TRANSLATE_NOOP("@default", "Original Date"), // FT_OriginalDate, QT_TRANSLATE_NOOP("@default", "Part"), // FT_Part, QT_TRANSLATE_NOOP("@default", "Performer"), // FT_Performer, QT_TRANSLATE_NOOP("@default", "Picture"), // FT_Picture, QT_TRANSLATE_NOOP("@default", "Publisher"), // FT_Publisher, QT_TRANSLATE_NOOP("@default", "Release Country"), // FT_ReleaseCountry, QT_TRANSLATE_NOOP("@default", "Remixer"), // FT_Remixer, QT_TRANSLATE_NOOP("@default", "Sort Album"), // FT_SortAlbum, QT_TRANSLATE_NOOP("@default", "Sort Album Artist"), // FT_SortAlbumArtist, QT_TRANSLATE_NOOP("@default", "Sort Artist"), // FT_SortArtist, QT_TRANSLATE_NOOP("@default", "Sort Composer"), // FT_SortComposer, QT_TRANSLATE_NOOP("@default", "Sort Name"), // FT_SortName, QT_TRANSLATE_NOOP("@default", "Subtitle"), // FT_Subtitle, QT_TRANSLATE_NOOP("@default", "Website"), // FT_Website, QT_TRANSLATE_NOOP("@default", "WWW Audio File"), // FT_WWWAudioFile, QT_TRANSLATE_NOOP("@default", "WWW Audio Source") // FT_WWWAudioSource, // FT_LastFrame = FT_WWWAudioSource }; struct not_used { int array_size_check[ sizeof(names) / sizeof(names[0]) == Frame::FT_LastFrame + 1 ? 1 : -1 ]; }; return type <= Frame::FT_LastFrame ? names[type] : "Unknown"; } /** * Get type of frame from English name. * * @param name name, spaces and case are ignored * * @return type. */ Frame::Type getTypeFromName(QString name) { static QMap strNumMap; if (strNumMap.empty()) { // first time initialization for (int i = 0; i <= Frame::FT_LastFrame; ++i) { Frame::Type type = static_cast(i); strNumMap.insert(QString::fromLatin1(getNameFromType(type)). remove(QLatin1Char(' ')).toUpper(), type); } } QMap::const_iterator it = strNumMap.find(name.remove(QLatin1Char(' ')).toUpper()); if (it != strNumMap.end()) { return static_cast(*it); } return Frame::FT_Other; } } Frame::ExtendedType::ExtendedType(const QString& name) : m_type(getTypeFromName(name)), m_name(name) { } Frame::ExtendedType::ExtendedType(Type type) : m_type(type), m_name(QString::fromLatin1(getNameFromType(type))) { } /** * Get name of type. * @return name. */ QString Frame::ExtendedType::getName() const { return m_type != FT_Other ? QString::fromLatin1(getNameFromType(m_type)) : m_name; } /** * Get translated name of type. * @return name. */ QString Frame::ExtendedType::getTranslatedName() const { return m_type != FT_Other ? QCoreApplication::translate("@default", getNameFromType(m_type)) : m_name; } /** * Constructor. */ Frame::Frame() : m_index(-1), m_valueChanged(false) { } /** * Constructor. */ Frame::Frame(Type type, const QString& value, const QString& name, int index) : m_extendedType(type, name), m_index(index), m_valueChanged(false), m_value(value) { } /** * Constructor. * @param type type and internal name * @param value value * @param index index inside tag, -1 if unknown */ Frame::Frame(const ExtendedType& type, const QString& value, int index) : m_extendedType(type), m_index(index), m_valueChanged(false), m_value(value) { } /** * Destructor. */ Frame::~Frame() { } /** * Set the value from a field in the field list. */ void Frame::setValueFromFieldList() { if (!getFieldList().empty()) { QString text; for (Frame::FieldList::const_iterator fldIt = getFieldList().begin(); fldIt != getFieldList().end(); ++fldIt) { int id = (*fldIt).m_id; if (id == Frame::Field::ID_Text || id == Frame::Field::ID_Description || id == Frame::Field::ID_Url) { m_value = (*fldIt).m_value.toString(); if (id == Frame::Field::ID_Text) { // highest priority, will not be overwritten break; } } } } } /** * Set a field in the field list from the value. */ void Frame::setFieldListFromValue() { if (!fieldList().empty()) { Frame::FieldList::iterator it = fieldList().end(); for (Frame::FieldList::iterator fldIt = fieldList().begin(); fldIt != fieldList().end(); ++fldIt) { int id = (*fldIt).m_id; if (id == Frame::Field::ID_Text || id == Frame::Field::ID_Description || id == Frame::Field::ID_Url) { it = fldIt; if (id == Frame::Field::ID_Text) { // highest priority, will not be overwritten break; } } } if (it != fieldList().end()) { (*it).m_value = m_value; } } } /** * Convert string (e.g. "track/total number of tracks") to number. * * @param str string to convert * @param ok if not 0, true is returned here if conversion is ok * * @return number in string ignoring total after slash. */ int Frame::numberWithoutTotal(const QString& str, bool* ok) { int slashPos = str.indexOf(QLatin1Char('/')); return slashPos == -1 ? str.toInt(ok) : str.left(slashPos).toInt(ok); } /** * Get the value of a field. * * @param id field ID * * @return field value, invalid if field not found. */ QVariant Frame::getFieldValue(Field::Id id) const { for (FieldList::const_iterator it = m_fieldList.begin(); it != m_fieldList.end(); ++it) { if ((*it).m_id == id) { return (*it).m_value; } } return QVariant(); } /** * Set value as string and mark it as changed if it is changed. * This method will avoid setting "different" representations. * @param value value as string */ void Frame::setValueIfChanged(const QString& value) { if (value != differentRepresentation()) { QString oldValue(getValue()); if (value != oldValue && !(value.isEmpty() && oldValue.isEmpty())) { setValue(value); setValueChanged(); } } } /** * Check if the fields in another frame are equal. * * @param other other frame * * @return true if equal. */ bool Frame::isEqual(const Frame& other) const { if (getType() != other.getType() || getValue() != other.getValue()) return false; const FieldList& otherFieldList = other.getFieldList(); if (m_fieldList.size() != otherFieldList.size()) return false; FieldList::const_iterator thisIt, otherIt; for (thisIt = m_fieldList.constBegin(), otherIt = otherFieldList.constBegin(); thisIt != m_fieldList.constEnd() && otherIt != otherFieldList.constEnd(); ++thisIt, ++otherIt) { if (thisIt->m_id != otherIt->m_id || thisIt->m_value != otherIt->m_value) { return false; } } return true; } #ifndef QT_NO_DEBUG /** * Convert frame type to string. * @param type frame type * @return string representation of type. */ static const char* frameTypeToString(Frame::Type type) { static const char* const typeStr[] = { "FT_Title", "FT_Artist", "FT_Album", "FT_Comment", "FT_Date", "FT_Track", "FT_Genre", "FT_AlbumArtist", "FT_Arranger", "FT_Author", "FT_Bpm", "FT_CatalogNumber", "FT_Compilation", "FT_Composer", "FT_Conductor", "FT_Copyright", "FT_Disc", "FT_EncodedBy", "FT_EncoderSettings", "FT_EncodingTime", "FT_Grouping", "FT_InitialKey", "FT_Isrc", "FT_Language", "FT_Lyricist", "FT_Lyrics", "FT_Media", "FT_Mood", "FT_OriginalAlbum", "FT_OriginalArtist", "FT_OriginalDate", "FT_Part", "FT_Performer", "FT_Picture", "FT_Publisher", "FT_ReleaseCountry", "FT_Remixer", "FT_SortAlbum", "FT_SortAlbumArtist", "FT_SortArtist", "FT_SortComposer", "FT_SortName", "FT_Subtitle", "FT_Website", "FT_WWWAudioFile", "FT_WWWAudioSource", "FT_Other", "FT_UnknownFrame" }; return type >= 0 && (unsigned)type <= sizeof(typeStr) / sizeof(typeStr[0]) ? typeStr[type] : "ILLEGAL"; } /** * Convert field id to string. * @param id field id * @return string representation of id. */ static const char* fieldIdToString(int id) { static const char* const idStr[] = { "ID_NoField", "ID_TextEnc", "ID_Text", "ID_Url", "ID_Data", "ID_Description", "ID_Owner", "ID_Email", "ID_Rating", "ID_Filename", "ID_Language", "ID_PictureType", "ID_ImageFormat", "ID_MimeType", "ID_Counter", "ID_Id", "ID_VolumeAdj", "ID_NumBits", "ID_VolChgRight", "ID_VolChgLeft", "ID_PeakVolRight", "ID_PeakVolLeft", "ID_TimestampFormat", "ID_ContentType" }; return id >= 0 && (unsigned)id <= sizeof(idStr) / sizeof(idStr[0]) ? idStr[id] : "ILLEGAL"; } /** * Get string representation of variant. * @param val variant value * @return string representation. */ static QString variantToString(const QVariant& val) { if (val.type() == QVariant::ByteArray) { return QString(QLatin1String("ByteArray of %1 bytes")).arg(val.toByteArray().size()); } else { return val.toString(); } } /** * Dump contents of frame to debug console. */ void Frame::dump() const { qDebug("Frame: name=%s, value=%s, type=%s, index=%d, valueChanged=%u", getInternalName().toLatin1().data(), m_value.toLatin1().data(), frameTypeToString(getType()), m_index, m_valueChanged); qDebug(" fields="); for (FieldList::const_iterator it = m_fieldList.begin(); it != m_fieldList.end(); ++it) { qDebug(" Field: id=%s, value=%s", fieldIdToString((*it).m_id), variantToString((*it).m_value).toLatin1().data()); } } #endif /** * Bit mask containing the bits of all frame types which shall be used as * quick access frames. * This mask has to be handled like FrameFilter::m_enabledFrames. */ quint64 FrameCollection::s_quickAccessFrames = FrameCollection::DEFAULT_QUICK_ACCESS_FRAMES; /** * Set values which are different inactive. * * @param others frames to compare, will be modified */ void FrameCollection::filterDifferent(FrameCollection& others) { QByteArray frameData, othersData; for (iterator it = begin(); it != end(); ++it) { Frame& frame = const_cast(*it); // This frame list is not tied to a specific file, so the // index is not valid. frame.setIndex(-1); iterator othersIt = others.find(frame); if (othersIt == others.end() || (frame.getType() != Frame::FT_Picture && frame.getValue() != othersIt->getValue()) || (frame.getType() == Frame::FT_Picture && !(PictureFrame::getData(frame, frameData) && PictureFrame::getData(*othersIt, othersData) && frameData == othersData))) { frame.setDifferent(); } while (othersIt != others.end() && !(frame < *othersIt)) { // Mark equal frames as already handled using index -2, used below. // This is probably faster than removing the frames. const_cast(*othersIt).setIndex(-2); ++othersIt; } } // Insert frames which are in others but not in this (not marked as already // handled by index -2) as different frames. for (iterator othersIt = others.begin(); othersIt != others.end(); ++othersIt) { if (othersIt->getIndex() != -2) { Frame& frame = const_cast(*othersIt); frame.setIndex(-1); frame.setDifferent(); insert(frame); } } } /** * Add standard frames which are missing. */ void FrameCollection::addMissingStandardFrames() { quint64 mask; int i; for (i = Frame::FT_FirstFrame, mask = 1ULL; i <= Frame::FT_LastFrame; ++i, mask <<= 1) { if (s_quickAccessFrames & mask) { Frame frame(static_cast(i), QString(), QString(), -1); FrameCollection::const_iterator it = find(frame); if (it == end()) { insert(frame); } } } } /** * Copy enabled frames. * * @param flt filter with enabled frames * * @return copy with enabled frames. */ FrameCollection FrameCollection::copyEnabledFrames(const FrameFilter& flt) const { FrameCollection frames; for (const_iterator it = begin(); it != end(); ++it) { if (flt.isEnabled(it->getType(), it->getName())) { Frame frame = *it; frame.setIndex(-1); frames.insert(frame); } } return frames; } /** * Remove all frames which are not enabled from the collection. * * @param flt filter with enabled frames */ void FrameCollection::removeDisabledFrames(const FrameFilter& flt) { for (iterator it = begin(); it != end();) { if (!flt.isEnabled(it->getType(), it->getName())) { erase(it++); } else { ++it; } } } /** * Copy frames which are empty or inactive from other frames. * This can be used to merge two frame collections. * * @param frames other frames */ void FrameCollection::merge(const FrameCollection& frames) { for (const_iterator otherIt = frames.begin(); otherIt != frames.end(); ++otherIt) { iterator it = find(*otherIt); if (it != end()) { QString value(otherIt->getValue()); Frame& frameFound = const_cast(*it); if (frameFound.getValue().isEmpty() && !value.isEmpty()) { frameFound.setValueIfChanged(value); } } else { Frame frame(*otherIt); frame.setIndex(-1); frame.setValueChanged(true); insert(frame); } } } /** * Check if the standard tags are empty or inactive. * * @return true if empty or inactive. */ bool FrameCollection::isEmptyOrInactive() const { return getTitle().isEmpty() && getArtist().isEmpty() && getAlbum().isEmpty() && getComment().isEmpty() && getYear() <= 0 && getTrack() <= 0 && getGenre().isEmpty(); } /** * Search for a frame only by name. * * @param name the name of the frame to find, a case-insensitive search for * the first name starting with this string is performed * * @return iterator or end() if not found. */ FrameCollection::const_iterator FrameCollection::searchByName( const QString& name) const { if (name.isEmpty()) return end(); const_iterator it; QString ucName = name.toUpper().remove(QLatin1Char('/')); int len = ucName.length(); for (it = begin(); it != end(); ++it) { QString ucFrameName(it->getName().toUpper().remove(QLatin1Char('/'))); if (ucName == ucFrameName.leftRef(len)) { break; } int nlPos = ucFrameName.indexOf(QLatin1Char('\n')); if (nlPos > 0 && ucName == ucFrameName.midRef(nlPos + 1, len)) { // Description in TXXX, WXXX, COMM, PRIV matches break; } } return it; } /** * Find a frame by name. * * @param name the name of the frame to find, if the exact name is not * found, a case-insensitive search for the first name * starting with this string is performed * * @return iterator or end() if not found. */ FrameCollection::const_iterator FrameCollection::findByName(const QString& name) const { Frame frame(Frame::ExtendedType(name), QLatin1String(""), -1); const_iterator it = find(frame); if (it != end()) { return it; } return searchByName(name); } /** * Find a frame by type or name. * * @param type type and name of the frame to find, if the exact name is not * found, a case-insensitive search for the first name * starting with this string is performed * * @return iterator or end() if not found. */ FrameCollection::const_iterator FrameCollection::findByExtendedType( const Frame::ExtendedType& type) const { Frame frame(type, QLatin1String(""), -1); const_iterator it = find(frame); if (it == end()) { it = searchByName(frame.getInternalName()); } return it; } /** * Find a frame by index. * * @param index the index in the frame, see \ref Frame::getIndex() * * @return iterator or end() if not found. */ FrameCollection::const_iterator FrameCollection::findByIndex(int index) const { const_iterator it; for (it = begin(); it != end(); ++it) { if (it->getIndex() == index) { break; } } return it; } /** * Get value by type. * * @param type type * * @return value, QString::null if not found. */ QString FrameCollection::getValue(Frame::Type type) const { Frame frame(type, QLatin1String(""), QLatin1String(""), -1); const_iterator it = find(frame); return it != end() ? it->getValue() : QString(); } /** * Get value by type and name. * * @param type type and name of the frame to find, if the exact name is not * found, a case-insensitive search for the first name * starting with this string is performed * * @return value, QString::null if not found. */ QString FrameCollection::getValue(const Frame::ExtendedType& type) const { const_iterator it = findByExtendedType(type); return it != end() ? it->getValue() : QString(); } /** * Set value by type. * * @param type type * @param value value, nothing is done if QString::null */ void FrameCollection::setValue(Frame::Type type, const QString& value) { if (!value.isNull()) { Frame frame(type, QLatin1String(""), QLatin1String(""), -1); iterator it = find(frame); if (it != end()) { Frame& frameFound = const_cast(*it); frameFound.setValueIfChanged(value); } else { frame.setValueIfChanged(value); insert(frame); } } } /** * Set value by type and name. * * @param type type and name of the frame to find, if the exact name is not * found, a case-insensitive search for the first name * starting with this string is performed * @param value value, nothing is done if QString::null */ void FrameCollection::setValue(const Frame::ExtendedType& type, const QString& value) { if (!value.isNull()) { Frame frame(type, QLatin1String(""), -1); const_iterator it = find(frame); if (it == end()) { it = searchByName(type.getInternalName()); } if (it != end()) { Frame& frameFound = const_cast(*it); frameFound.setValueIfChanged(value); } else { frame.setValueIfChanged(value); insert(frame); } } } /** * Get integer value by type. * * @param type type * * @return value, 0 if empty, -1 if not found. */ int FrameCollection::getIntValue(Frame::Type type) const { QString str = getValue(type); return str.isNull() ? -1 : str.toInt(); } /** * Set integer value by type. * * @param type type * @param value value, 0 to set empty, nothing is done if -1 */ void FrameCollection::setIntValue(Frame::Type type, int value) { if (value != -1) { QString str = value != 0 ? QString::number(value) : QLatin1String(""); setValue(type, str); } } /** * Compare the frames with another frame collection and mark the value as * changed on frames which are different. * * @param other other frame collection */ void FrameCollection::markChangedFrames(const FrameCollection& other) { for (FrameCollection::iterator it = begin(); it != end(); ++it) { const_iterator otherIt = it->getIndex() != -1 ? other.findByIndex(it->getIndex()) : other.find(*it); Frame& frame = const_cast(*it); frame.setValueChanged(!(otherIt != other.end() && otherIt->isEqual(*it))); } } #ifndef QT_NO_DEBUG /** * Dump contents of frame collection to debug console. */ void FrameCollection::dump() const { qDebug("FrameCollection:"); for (const_iterator it = begin(); it != end(); ++it) { (*it).dump(); } } #endif /** * Constructor. * All frames are disabled */ FrameFilter::FrameFilter() : m_enabledFrames(0) {} /** * Destructor. */ FrameFilter::~FrameFilter() {} /** * Enable all frames. */ void FrameFilter::enableAll() { m_enabledFrames = FTM_AllFrames; m_disabledOtherFrames.clear(); } /** * Check if all fields are true. * * @return true if all fields are true. */ bool FrameFilter::areAllEnabled() const { return (m_enabledFrames & FTM_AllFrames) == FTM_AllFrames && m_disabledOtherFrames.empty(); } /** * Check if frame is enabled. * * @param type frame type * @param name frame name * * @return true if frame is enabled. */ bool FrameFilter::isEnabled(Frame::Type type, const QString& name) const { if (type <= Frame::FT_LastFrame) { return (m_enabledFrames & (1ULL << type)) != 0; } else if (!name.isEmpty()) { std::set::const_iterator it = m_disabledOtherFrames.find(name); return it == m_disabledOtherFrames.end(); } else { return true; } } /** * Enable or disable frame. * * @param type frame type * @param name frame name * @param en true to enable */ void FrameFilter::enable(Frame::Type type, const QString& name, bool en) { if (type <= Frame::FT_LastFrame) { if (en) { m_enabledFrames |= (1ULL << type); } else { m_enabledFrames &= ~(1ULL << type); } } else if (!name.isEmpty()) { if (en) { std::set::iterator it = m_disabledOtherFrames.find(name); if (it != m_disabledOtherFrames.end()) { m_disabledOtherFrames.erase(it); } } else { m_disabledOtherFrames.insert(name); } } } /** * Constructor. * * @param frames frame collection * @param str string with format codes */ FrameFormatReplacer::FrameFormatReplacer( const FrameCollection& frames, const QString& str) : FormatReplacer(str), m_frames(frames) {} /** * Destructor. */ FrameFormatReplacer::~FrameFormatReplacer() {} /** * Replace a format code (one character %c or multiple characters %{chars}). * Supported format fields: * %s title (song) * %l album * %a artist * %c comment * %y year * %t track, two digits, i.e. leading zero if < 10 * %T track, without leading zeroes * %g genre * * @param code format code * * @return replacement string, * QString::null if code not found. */ QString FrameFormatReplacer::getReplacement(const QString& code) const { QString result; QString name; if (code.length() == 1) { static const struct { char shortCode; const char* longCode; } shortToLong[] = { { 's', "title" }, { 'l', "album" }, { 'a', "artist" }, { 'c', "comment" }, { 'y', "year" }, { 't', "track" }, { 'T', "tracknumber" }, { 'g', "genre" } }; const char c = code[0].toLatin1(); for (unsigned i = 0; i < sizeof(shortToLong) / sizeof(shortToLong[0]); ++i) { if (shortToLong[i].shortCode == c) { name = QString::fromLatin1(shortToLong[i].longCode); break; } } } else if (code.length() > 1) { name = code; } if (!name.isNull()) { QString lcName(name.toLower()); int fieldWidth = lcName == QLatin1String("track") ? 2 : -1; if (lcName == QLatin1String("year")) { name = QLatin1String("date"); } else if (lcName == QLatin1String("tracknumber")) { name = QLatin1String("track number"); } int len = lcName.length(); if (len > 2 && lcName.at(len - 2) == QLatin1Char('.') && lcName.at(len - 1) >= QLatin1Char('0') && lcName.at(len - 1) <= QLatin1Char('9')) { fieldWidth = lcName.at(len - 1).toLatin1() - '0'; lcName.truncate(len - 2); name.truncate(len - 2); } FrameCollection::const_iterator it = m_frames.findByName(name); if (it != m_frames.end()) { result = it->getValue(); if (result.isNull()) { // code was found, but value is empty result = QLatin1String(""); } if (it->getType() == Frame::FT_Picture && result.isEmpty()) { QVariant fieldValue = it->getFieldValue(Frame::Field::ID_Data); if (fieldValue.isValid() && fieldValue.toByteArray().size() > 0) { // If there is a picture without description, return "1", so that // an empty value indicates "no picture" result = QLatin1String("1"); } } } if (lcName == QLatin1String("year")) { QRegExp yearRe(QLatin1String("^\\d{4}-\\d{2}")); if (yearRe.indexIn(result) == 0) { result.truncate(4); } } if (fieldWidth > 0) { bool ok; int nr = Frame::numberWithoutTotal(result, &ok); if (ok) { result.sprintf("%0*d", fieldWidth, nr); } } } return result; } /** * Get help text for supported format codes. * * @param onlyRows if true only the tr elements are returned, * not the surrounding table * * @return help text. */ QString FrameFormatReplacer::getToolTip(bool onlyRows) { QString str; if (!onlyRows) str += QLatin1String("\n"); str += QLatin1String("\n"); str += QLatin1String("\n"); str += QLatin1String("\n"); str += QLatin1String("\n"); str += QLatin1String("\n"); str += QLatin1String("\n"); str += QLatin1String("\n"); str += QLatin1String("\n"); str += QLatin1String("\n"); if (!onlyRows) str += QLatin1String("
%s%{title}"); str += QCoreApplication::translate("@default", "Title"); str += QLatin1String("
%l%{album}"); str += QCoreApplication::translate("@default", "Album"); str += QLatin1String("
%a%{artist}"); str += QCoreApplication::translate("@default", "Artist"); str += QLatin1String("
%c%{comment}"); str += QCoreApplication::translate("@default", "Comment"); str += QLatin1String("
%y%{year}"); str += QCoreApplication::translate("@default", QT_TRANSLATE_NOOP("@default", "Year")); str += QLatin1String("
%t%{track}"); str += QCoreApplication::translate("@default", QT_TRANSLATE_NOOP("@default", "Track")); str += QLatin1String(" "01"
%t%{track.3}"); str += QCoreApplication::translate("@default", QT_TRANSLATE_NOOP("@default", "Track")); str += QLatin1String(" "001"
%T%{tracknumber}"); str += QCoreApplication::translate("@default", QT_TRANSLATE_NOOP("@default", "Track")); str += QLatin1String(" "1"
%g%{genre}"); str += QCoreApplication::translate("@default", "Genre"); str += QLatin1String("
\n"); return str; } kid3-3.0.2/src/core/tags/frame.h000066400000000000000000000477201224603511300162750ustar00rootroot00000000000000/** * \file frame.h * Generalized frame. * * \b Project: Kid3 * \author Urs Fleisch * \date 25 Aug 2007 * * Copyright (C) 2007-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef FRAME_H #define FRAME_H #include "formatreplacer.h" #include #include #include #include #include "kid3api.h" /** Generalized frame. */ class KID3_CORE_EXPORT Frame { public: /** Generalized frame types. */ enum Type { FT_Title, FT_FirstFrame = FT_Title, FT_Artist, FT_Album, FT_Comment, FT_Date, FT_Track, FT_Genre, FT_LastV1Frame = FT_Genre, FT_AlbumArtist, FT_Arranger, FT_Author, FT_Bpm, FT_CatalogNumber, FT_Compilation, FT_Composer, FT_Conductor, FT_Copyright, FT_Disc, FT_EncodedBy, FT_EncoderSettings, FT_EncodingTime, FT_Grouping, FT_InitialKey, FT_Isrc, FT_Language, FT_Lyricist, FT_Lyrics, FT_Media, FT_Mood, FT_OriginalAlbum, FT_OriginalArtist, FT_OriginalDate, FT_Part, FT_Performer, FT_Picture, FT_Publisher, FT_ReleaseCountry, FT_Remixer, FT_SortAlbum, FT_SortAlbumArtist, FT_SortArtist, FT_SortComposer, FT_SortName, FT_Subtitle, FT_Website, FT_WWWAudioFile, FT_WWWAudioSource, FT_LastFrame = FT_WWWAudioSource, FT_Other, FT_UnknownFrame }; /** Field in frame. */ struct Field { /** * Types of fields in a frame, must be the same as id3lib's ID3_FieldID. **/ enum Id { ID_NoField, ID_TextEnc, ID_Text, ID_Url, ID_Data, ID_Description, ID_Owner, ID_Email, ID_Rating, ID_Filename, ID_Language, ID_PictureType, ID_ImageFormat, ID_MimeType, ID_Counter, ID_Id, ID_VolumeAdj, ID_NumBits, ID_VolChgRight, ID_VolChgLeft, ID_PeakVolRight, ID_PeakVolLeft, ID_TimestampFormat, ID_ContentType, // These are additional fields used by TagLib ID_Price, ID_Date, ID_Seller }; /** Text encoding for fields of type ID_TextEnc. */ enum TextEncoding { TE_ISO8859_1 = 0, TE_UTF16 = 1, TE_UTF16BE = 2, TE_UTF8 = 3 }; #ifdef Q_CC_MSVC /** * Equality operator. * Needed when building with MSVC and BUILD_SHARED_LIBS. * @param rhs right hand side to compare * @return true if this == rhs. */ bool operator==(const Field& rhs) const { return m_id == rhs.m_id && m_value == rhs.m_value; } #endif int m_id; /**< type of field. */ QVariant m_value; /**< value of field. */ }; /** list of fields. */ typedef QList FieldList; /** * Type and name of frame. */ class KID3_CORE_EXPORT ExtendedType { public: /** * Constructor. */ ExtendedType() : m_type(FT_UnknownFrame) {} /** * Constructor. * @param type type * @param name internal name */ ExtendedType(Type type, const QString& name) : m_type(type), m_name(name) {} /** * Constructor. * @param name internal name */ explicit ExtendedType(const QString& name); /** * Constructor. * @param type type */ explicit ExtendedType(Type type); /** * Get name of type. * @return name. */ QString getName() const; /** * Get translated name of type. * @return name. */ QString getTranslatedName() const; /** * Get internal name of type. * @return name. */ QString getInternalName() const { return m_name; } /** * Less than operator. * @param rhs right hand side to compare * @return true if this < rhs. */ bool operator<(const ExtendedType& rhs) const { return m_type < rhs.m_type || (m_type == FT_Other && m_type == rhs.m_type && m_name < rhs.m_name); } /** * Equality operator. * @param rhs right hand side to compare * @return true if this == rhs. */ bool operator==(const ExtendedType& rhs) const { return m_type == rhs.m_type && (m_type != FT_Other || m_name == rhs.m_name); } /** * Get type * @return type. */ Type getType() const { return m_type; } private: friend class Frame; Type m_type; QString m_name; }; /** * Constructor. */ Frame(); /** * Constructor. * @param type type * @param value value * @param name internal name * @param index index inside tag, -1 if unknown */ Frame(Type type, const QString& value, const QString& name, int index); /** * Constructor. * @param type type and internal name * @param value value * @param index index inside tag, -1 if unknown */ Frame(const ExtendedType& type, const QString& value, int index); /** * Destructor. */ ~Frame(); /** * Less than operator. * Needed for sorting in multiset. * @param rhs right hand side to compare * @return true if this < rhs. */ bool operator<(const Frame& rhs) const { return m_extendedType < rhs.m_extendedType; } #ifdef Q_CC_MSVC /** * Equality operator. * Needed when building with MSVC and BUILD_SHARED_LIBS. * @param rhs right hand side to compare * @return true if this == rhs. */ bool operator==(const Frame& rhs) const { return m_extendedType == rhs.m_extendedType && m_value == rhs.m_value && m_fieldList == rhs.m_fieldList; } #endif /** * Get type of frame. * @return type. */ Type getType() const { return m_extendedType.m_type; } /** * Set type of frame. * @param type type of frame */ void setType(Type type) { m_extendedType.m_type = type; } /** * Get type and name of frame. * @return extended type. */ ExtendedType getExtendedType() const { return m_extendedType; } /** * Set type and name of frame. * @param type extended type of frame */ void setExtendedType(const ExtendedType& type) { m_extendedType = type; } /** * Get index of frame. * @return index. */ int getIndex() const { return m_index; } /** * Set index of frame. * @param index index of frame */ void setIndex(int index) { m_index = index; } /** * Get name of frame. * @return name. */ QString getName() const { return m_extendedType.getName(); } /** * Get internal name of frame. * @return name. */ QString getInternalName() const { return m_extendedType.getInternalName(); } /** * Get value as string. * @return value. */ QString getValue() const { return m_value; } /** * Set value as string. * @param value value as string */ void setValue(const QString& value) { m_value = value; } /** * Check if value is empty. * @return true if empty. */ bool isEmpty() const { return m_value.isEmpty(); } /** * Check if frame is inactive. * @return true if inactive. */ bool isInactive() const { return m_value.isNull(); } /** * Set frame inactive. */ void setInactive() { m_value = QString(); } /** * Check if frame represents different frames in multiple files. * @return true if different. */ bool isDifferent() const { return m_value == differentRepresentation(); } /** * Represents different frames in multiple files. */ void setDifferent() { m_value = differentRepresentation(); } /** * Check if value is changed. * @return true if changed. */ bool isValueChanged() const { return m_valueChanged; } /** * Mark the value as changed. * @param changed true to mark as changed */ void setValueChanged(bool changed = true) { m_valueChanged = changed; } /** * Set value as string and mark it as changed if it is changed. * This method will avoid setting "different" representations. * @param value value as string */ void setValueIfChanged(const QString& value); /** * Set the value from a field in the field list. */ void setValueFromFieldList(); /** * Set a field in the field list from the value. */ void setFieldListFromValue(); /** * Get fields in the frame. * @return field list. */ const FieldList& getFieldList() const { return m_fieldList; } /** * Set fields in the frame. * @param fields field list */ void setFieldList(const FieldList& fields) { m_fieldList = fields; } /** * Get fields in the frame. * @return reference to field list. */ FieldList& fieldList() { return m_fieldList; } /** * Get the value of a field. * * @param id field ID * * @return field value, invalid if field not found. */ QVariant getFieldValue(Field::Id id) const; /** * Check if the fields in another frame are equal. * * @param other other frame * * @return true if equal. */ bool isEqual(const Frame& other) const; #ifndef QT_NO_DEBUG /** * Dump contents of frame to debug console. */ void dump() const; #endif /** * If a frame contains a string list as a value, it is stored in a single * string, separated by this special separator character. * * @return separator character. */ static QLatin1Char stringListSeparator() { return QLatin1Char('|'); } /** * Convert string (e.g. "track/total number of tracks") to number. * * @param str string to convert * @param ok if not 0, true is returned here if conversion is ok * * @return number in string ignoring total after slash. */ static int numberWithoutTotal(const QString& str, bool* ok = 0); /** * Get representation of different frames in multiple files. * @return "different" representation. */ static QChar differentRepresentation() { return QChar(0x2260); } private: friend class TaggedFile; ExtendedType m_extendedType; int m_index; bool m_valueChanged; QString m_value; FieldList m_fieldList; }; /** Filter to enable a subset of frame types. */ class KID3_CORE_EXPORT FrameFilter { public: /** * Constructor. * All frames are disabled */ FrameFilter(); /** * Destructor. */ ~FrameFilter(); /** * Enable all frames. */ void enableAll(); /** * Check if all fields are true. * * @return true if all fields are true. */ bool areAllEnabled() const; /** * Check if frame is enabled. * * @param type frame type * @param name frame name * * @return true if frame is enabled. */ bool isEnabled(Frame::Type type, const QString& name = QString()) const; /** * Enable or disable frame. * * @param type frame type * @param name frame name * @param en true to enable */ void enable(Frame::Type type, const QString& name = QString(), bool en = true); private: static const quint64 FTM_AllFrames = (1ULL << (Frame::FT_LastFrame + 1)) - 1; quint64 m_enabledFrames; std::set m_disabledOtherFrames; }; /** Collection of frames. */ class KID3_CORE_EXPORT FrameCollection : public std::multiset { public: /** * Default value for quick access frames. */ static const quint64 DEFAULT_QUICK_ACCESS_FRAMES = (1ULL << Frame::FT_Title) | (1ULL << Frame::FT_Artist) | (1ULL << Frame::FT_Album) | (1ULL << Frame::FT_Comment) | (1ULL << Frame::FT_Date) | (1ULL << Frame::FT_Track) | (1ULL << Frame::FT_Genre); /** * Constructor. */ FrameCollection() {} /** * Destructor. */ ~FrameCollection() {} /** * Set values which are different inactive. * * @param others frames to compare, will be modified! */ void filterDifferent(FrameCollection& others); /** * Add standard frames which are missing. */ void addMissingStandardFrames(); /** * Copy enabled frames. * * @param flt filter with enabled frames * * @return copy with enabled frames. */ FrameCollection copyEnabledFrames(const FrameFilter& flt) const; /** * Remove all frames which are not enabled from the collection. * * @param flt filter with enabled frames */ void removeDisabledFrames(const FrameFilter& flt); /** * Copy frames which are empty or inactive from other frames. * This can be used to merge two frame collections. * * @param frames other frames */ void merge(const FrameCollection& frames); /** * Check if the standard tags are empty or inactive. * * @return true if empty or inactive. */ bool isEmptyOrInactive() const; /** * Find a frame by name. * * @param name the name of the frame to find, if the exact name is not * found, a case-insensitive search for the first name * starting with this string is performed * * @return iterator or end() if not found. */ const_iterator findByName(const QString& name) const; /** * Find a frame by type or name. * * @param type type and name of the frame to find, if the exact name is not * found, a case-insensitive search for the first name * starting with this string is performed * * @return iterator or end() if not found. */ const_iterator findByExtendedType(const Frame::ExtendedType& type) const; /** * Find a frame by index. * * @param index the index in the frame, see \ref Frame::getIndex() * * @return iterator or end() if not found. */ const_iterator findByIndex(int index) const; /** * Get value by type. * * @param type type * * @return value, QString::null if not found. */ QString getValue(Frame::Type type) const; /** * Get value by type and name. * * @param type type and name of the frame to find, if the exact name is not * found, a case-insensitive search for the first name * starting with this string is performed * * @return value, QString::null if not found. */ QString getValue(const Frame::ExtendedType& type) const; /** * Set value by type. * * @param type type * @param value value, nothing is done if QString::null */ void setValue(Frame::Type type, const QString& value); /** * Set value by type and name. * * @param type type and name of the frame to find, if the exact name is not * found, a case-insensitive search for the first name * starting with this string is performed * @param value value, nothing is done if QString::null */ void setValue(const Frame::ExtendedType& type, const QString& value); /** * Get integer value by type. * * @param type type * * @return value, 0 if empty, -1 if not found. */ int getIntValue(Frame::Type type) const; /** * Set integer value by type. * * @param type type * @param value value, 0 to set empty, nothing is done if -1 */ void setIntValue(Frame::Type type, int value); /** * Get artist. * * @return artist, QString::null if not found. */ QString getArtist() const { return getValue(Frame::FT_Artist); } /** * Set artist. * * @param artist artist, nothing is done if QString::null */ void setArtist(const QString& artist) { setValue(Frame::FT_Artist, artist); } /** * Get album. * * @return album, QString::null if not found. */ QString getAlbum() const { return getValue(Frame::FT_Album); } /** * Set album. * * @param album album, nothing is done if QString::null */ void setAlbum(const QString& album) { setValue(Frame::FT_Album, album); } /** * Get title. * * @return title, QString::null if not found. */ QString getTitle() const { return getValue(Frame::FT_Title); } /** * Set title. * * @param title title, nothing is done if QString::null */ void setTitle(const QString& title) { setValue(Frame::FT_Title, title); } /** * Get comment. * * @return comment, QString::null if not found. */ QString getComment() const { return getValue(Frame::FT_Comment); } /** * Set comment. * * @param comment comment, nothing is done if QString::null */ void setComment(const QString& comment) { setValue(Frame::FT_Comment, comment); } /** * Get genre. * * @return genre, QString::null if not found. */ QString getGenre() const { return getValue(Frame::FT_Genre); } /** * Set genre. * * @param genre genre, nothing is done if QString::null */ void setGenre(const QString& genre) { setValue(Frame::FT_Genre, genre); } /** * Get track. * * @return track, -1 if not found. */ int getTrack() const { return getIntValue(Frame::FT_Track); } /** * Set track. * * @param track track, nothing is done if -1 */ void setTrack(int track) { setIntValue(Frame::FT_Track, track); } /** * Get year. * * @return year, -1 if not found. */ int getYear() const { return getIntValue(Frame::FT_Date); } /** * Set year. * * @param year year, nothing is done if -1 */ void setYear(int year) { setIntValue(Frame::FT_Date, year); } /** * Compare the frames with another frame collection and mark the value as * changed on frames which are different. * * @param other other frame collection */ void markChangedFrames(const FrameCollection& other); #ifndef QT_NO_DEBUG /** * Dump contents of frame collection to debug console. */ void dump() const; #endif /** * Set mask containing the bits of all frame types which shall be used as * quick access frames. * @param mask bit mask with bits for quick access frames set, default is * DEFAULT_QUICK_ACCESS_FRAMES. */ static void setQuickAccessFrames(quint64 mask) { s_quickAccessFrames = mask; } /** * Get mask containing the bits of all frame types which shall be used as * quick access frames. * @return mask bit mask with bits for quick access frames set. */ static quint64 getQuickAccessFrames() { return s_quickAccessFrames; } private: /** * Search for a frame only by name. * * @param name the name of the frame to find, a case-insensitive search for * the first name starting with this string is performed * * @return iterator or end() if not found. */ const_iterator searchByName(const QString& name) const; /** * Bit mask containing the bits of all frame types which shall be used as * quick access frames. * This mask has to be handled like FrameFilter::m_enabledFrames. */ static quint64 s_quickAccessFrames; }; /** * Replaces frame format codes in a string. */ class KID3_CORE_EXPORT FrameFormatReplacer : public FormatReplacer { public: /** * Constructor. * * @param frames frame collection * @param str string with format codes */ explicit FrameFormatReplacer( const FrameCollection& frames, const QString& str = QString()); /** * Destructor. */ virtual ~FrameFormatReplacer(); /** * Get help text for supported format codes. * * @param onlyRows if true only the tr elements are returned, * not the surrounding table * * @return help text. */ static QString getToolTip(bool onlyRows = false); protected: /** * Replace a format code (one character %c or multiple characters %{chars}). * Supported format fields: * %s title (song) * %l album * %a artist * %c comment * %y year * %t track, two digits, i.e. leading zero if < 10 * %T track, without leading zeroes * %g genre * * @param code format code * * @return replacement string, * QString::null if code not found. */ virtual QString getReplacement(const QString& code) const; private: const FrameCollection& m_frames; }; #endif // FRAME_H kid3-3.0.2/src/core/tags/genres.cpp000066400000000000000000000270171224603511300170160ustar00rootroot00000000000000/** * \file genres.cpp * Alphabetical list of genres. * * \b Project: Kid3 * \author Urs Fleisch * \date 9 Jan 2003 * * Copyright (C) 2003-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "genres.h" #include #include /** * Alphabetic list of genres, starts with unknown (empty) entry. * * 125: Last ID3v1, 142: WinAmp 1.91, 145: WinAmp 1.92, 148: WinAmp 5.6, 255: unknown */ const char* Genres::s_genre[Genres::count + 3] = { "", // 255, "A Cappella", // 123, "Abstract", // 148, "Acid", // 34, "Acid Jazz", // 74, "Acid Punk", // 73, "Acoustic", // 99, "Alternative", // 20, "Alternative Rock", // 40, "Ambient", // 26, "Anime", // 145, "Art Rock", // 149, "Audio Theatre", // 184, "Audiobook", // 183, "Avantgarde", // 90, "Ballad", // 116, "Baroque", // 150, "Bass", // 41, "Beat", // 135, "Bebob", // 85, "Bhangra", // 151, "Big Band", // 96, "Big Beat", // 152, "Black Metal", // 138, "Bluegrass", // 89, "Blues", // 0, "Booty Bass", // 107, "Breakbeat", // 153, "BritPop", // 132, "Cabaret", // 65, "Celtic", // 88, "Chamber Music", // 104, "Chanson", // 102, "Chillout", // 154, "Chorus", // 97, "Christian Gangsta Rap", // 136, "Christian Rap", // 61, "Christian Rock", // 141, "Classic Rock", // 1, "Classical", // 32, "Club", // 112, "Club-House", // 128, "Comedy", // 57, "Contemporary Christian", // 140, "Country", // 2, "Crossover", // 139, "Cult", // 58, "Dance", // 3, "Dance Hall", // 125, "Darkwave", // 50, "Death Metal", // 22, "Disco", // 4, "Downtempo", // 155, "Dream", // 55, "Drum & Bass", // 127, "Drum Solo", // 122, "Dub", // 156, "Dubstep", // 189, "Duet", // 120, "EBM", // 157, "Easy Listening", // 98, "Eclectic", // 158, "Electro", // 159, "Electroclash", // 160, "Electronic", // 52, "Emo", // 161, "Ethnic", // 48, "Euro-House", // 124, "Euro-Techno", // 25, "Eurodance", // 54, "Experimental", // 162, "Folk", // 80, "Folk/Rock", // 81, "Folklore", // 115, "Freestyle", // 119, "Funk", // 5, "Fusion", // 30, "Fusion", // 84, "G-Funk", // 188, "Game", // 36, "Gangsta", // 59, "Garage", // 163, "Garage Rock", // 190, "Global", // 164, "Goa", // 126, "Gospel", // 38, "Gothic", // 49, "Gothic Rock", // 91, "Grunge", // 6, "Hard Rock", // 79, "Hardcore", // 129, "Heavy Metal", // 137, "Hip-Hop", // 7, "House", // 35, "Humour", // 100, "IDM", // 165, "Illbient", // 166, "Indie", // 131, "Indie Rock", // 187, "Industrial", // 19, "Industro-Goth", // 167, "Instrumental", // 33, "Instrumental Pop", // 46, "Instrumental Rock", // 47, "Jam Band", // 168, "Jazz", // 8, "Jazz+Funk", // 29, "Jpop", // 146, "Jungle", // 63, "Krautrock", // 169, "Latin", // 86, "Leftfield", // 170, "Lo-Fi", // 71, "Lounge", // 171, "Math Rock", // 172, "Meditative", // 45, "Merengue", // 142, "Metal", // 9, "Musical", // 77, "National Folk", // 82, "Native American", // 64, "Neoclassical", // 182, "Neue Deutsche Welle", // 185, "New Age", // 10, "New Romantic", // 173, "New Wave", // 66, "Noise", // 39, "Nu-Breakz", // 174, "Oldies", // 11, "Opera", // 103, "Other", // 12, "Podcast", // 186, "Polka", // 75, "Polsk Punk", // 134, "Pop", // 13, "Pop-Folk", // 53, "Pop/Funk", // 62, "Porn Groove", // 109, "Post-Punk", // 175, "Post-Rock", // 176, "Power Ballad", // 117, "Pranks", // 23, "Primus", // 108, "Progressive Rock", // 92, "Psybient", // 191, "Psychedelic", // 67, "Psychedelic Rock", // 93, "Psytrance", // 177, "Punk", // 43, "Punk Rock", // 121, "R&B", // 14, "Rap", // 15, "Rave", // 68, "Reggae", // 16, "Retro", // 76, "Revival", // 87, "Rhythmic Soul", // 118, "Rock", // 17, "Rock & Roll", // 78, "Salsa", // 143, "Samba", // 114, "Satire", // 110, "Shoegaze", // 178, "Showtunes", // 69, "Ska", // 21, "Slow Jam", // 111, "Slow Rock", // 95, "Sonata", // 105, "Soul", // 42, "Sound Clip", // 37, "Soundtrack", // 24, "Southern Rock", // 56, "Space", // 44, "Space Rock", // 179, "Speech", // 101, "Swing", // 83, "Symphonic Rock", // 94, "Symphony", // 106, "Synthpop", // 147, "Tango", // 113, "Techno", // 18, "Techno-Industrial", // 51, "Terror", // 130, "Thrash Metal", // 144, "Top 40", // 60, "Trailer", // 70, "Trance", // 31, "Tribal", // 72, "Trip-Hop", // 27, "Trop Rock", // 180, "Vocal", // 28, "World Music", // 181, "Worldbeat", // 133, "Custom", // place for temporary custom genres 0 // end of StrList }; /** * s_genreNum[n] gives the number of the n-th genre * in the alphabetically sorted list. */ const unsigned char Genres::s_genreNum[Genres::count + 1] = { 255, 123, 148, 34, 74, 73, 99, 20, 40, 26, 145, 149, 184, 183, 90, 116, 150, 41, 135, 85, 151, 96, 152, 138, 89, 0, 107, 153, 132, 65, 88, 104, 102, 154, 97, 136, 61, 141, 1, 32, 112, 128, 57, 140, 2, 139, 58, 3, 125, 50, 22, 4, 155, 55, 127, 122, 156, 189, 120, 157, 98, 158, 159, 160, 52, 161, 48, 124, 25, 54, 162, 80, 81, 115, 119, 5, 30, 84, 188, 36, 59, 163, 190, 164, 126, 38, 49, 91, 6, 79, 129, 137, 7, 35, 100, 165, 166, 131, 187, 19, 167, 33, 46, 47, 168, 8, 29, 146, 63, 169, 86, 170, 71, 171, 172, 45, 142, 9, 77, 82, 64, 182, 185, 10, 173, 66, 39, 174, 11, 103, 12, 186, 75, 134, 13, 53, 62, 109, 175, 176, 117, 23, 108, 92, 191, 67, 93, 177, 43, 121, 14, 15, 68, 16, 76, 87, 118, 17, 78, 143, 114, 110, 178, 69, 21, 111, 95, 105, 42, 37, 24, 56, 44, 179, 101, 83, 94, 106, 147, 113, 18, 51, 130, 144, 60, 70, 31, 72, 27, 180, 28, 181, 133 }; const char** Genres::s_strList = &s_genre[0]; /** * Get name assigned to genre number. * * @param num genre number * * @return name, empty string for unknown number. */ const char* Genres::getName(int num) { return s_genre[getIndex(num)]; } /** * Get the index in the alphabethically sorted list from the genre number. * * @param num genre number * * @return index, 0 for unknown number. */ int Genres::getIndex(int num) { int i; for (i = 0; i < Genres::count + 1; i++) { if (s_genreNum[i] == num) { return i; } } return 0; // 0 for unknown entry } /** * Get the genre number from a string containing a genre text. * * @param str string with genre * * @return genre number, 255 for unknown index. */ int Genres::getNumber(const QString& str) { static QMap strNumMap; if (strNumMap.empty()) { // first time initialization for (int i = 0; i < Genres::count + 1; i++) { strNumMap.insert(QString::fromLatin1(s_genre[i]), s_genreNum[i]); } } QMap::const_iterator it = strNumMap.find(str); if (it != strNumMap.end()) { return *it; } return 255; // 255 for unknown } /** * Get a name string from a string with a number or a name. * ID3v2 genres can be stored as "9", "(9)", "(9)Metal" or "Metal". * * @param str genre string. */ QString Genres::getNameString(const QString& str) { if (!str.isEmpty()) { int cpPos, n; bool ok; if ((str[0] == QLatin1Char('(')) && ((cpPos = str.indexOf(QLatin1Char(')'), 2)) > 1)) { n = str.mid(1, cpPos - 1).toInt(&ok); if (ok && n <= 0xff) { return QString::fromLatin1(getName(n)); } } else if ((n = str.toInt(&ok)) >= 0 && n <= 0xff && ok) { return QString::fromLatin1(getName(n)); } } return str; } /** * Get a number representation of a genre name if possible. * * @param str string with genre name * @param parentheses true to put the number in parentheses * * @return genre string. */ QString Genres::getNumberString(const QString& str, bool parentheses) { int n = getNumber(str); if (n < 0xff) { if (parentheses) { QString s(QLatin1String("(")); s += QString::number(n); s += QLatin1Char(')'); return s; } else { return QString::number(n); } } return str; } kid3-3.0.2/src/core/tags/genres.h000066400000000000000000000054131224603511300164570ustar00rootroot00000000000000/** * \file genres.h * Alphabetical list of genres. * * \b Project: Kid3 * \author Urs Fleisch * \date 9 Jan 2003 * * Copyright (C) 2003-2007 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef GENRES_H #define GENRES_H #include #include "kid3api.h" class QString; /** Alphabetically sorted list of genres, conversion to/from genre numbers */ class KID3_CORE_EXPORT Genres { public: /** * Get name assigned to genre number. * * @param num genre number * * @return name, empty string for unknown number. */ static const char* getName(int num); /** * Get the index in the alphabethically sorted list from the genre number. * * @param num genre number * * @return index, 0 for unknown number. */ static int getIndex(int num); /** * Get the genre number from a string containing a genre text. * * @param str string with genre * * @return genre number, 255 for unknown index. */ static int getNumber(const QString& str); /** * Get a name string from a string with a number or a name. * ID3v2 genres can be stored as "9", "(9)", "(9)Metal" or "Metal". * * @param str genre string. */ static QString getNameString(const QString& str); /** * Get a number representation of a genre name if possible. * * @param str string with genre name * @param parentheses true to put the number in parentheses * * @return genre string. */ static QString getNumberString(const QString& str, bool parentheses); /** Number of genres */ #ifdef Q_OS_WIN32 enum { count = 192 }; #else static const int count = 192; #endif /** * Pointer to alphabetic list of genres. * NULL terminated, to be used in combo box. */ static const char** s_strList; private: /** * Alphabetic list of genres, starts with unknown (empty) entry. * * 125: Last ID3v1, 142: WinAmp 1.91, 145: WinAmp 1.92, 255: unknown */ static const char* s_genre[Genres::count + 3]; /** * s_genreNum[n] gives the number of the n-th genre * in the alphabetically sorted list. */ static const unsigned char s_genreNum[Genres::count + 1]; }; #endif /* GENRES_H */ kid3-3.0.2/src/core/tags/itaggedfilefactory.cpp000066400000000000000000000022251224603511300213610ustar00rootroot00000000000000/** * \file itaggedfilefactory.cpp * Interface for tagged file factory. * * \b Project: Kid3 * \author Urs Fleisch * \date 22 Jul 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "itaggedfilefactory.h" /** * Destructor. */ ITaggedFileFactory::~ITaggedFileFactory() { // Just defining "virtual ~ITaggedFileFactory() {}" in the header file // will lead to unresolved symbols when building with shared libraries on // Windows and a class from another library inherits from this class. } kid3-3.0.2/src/core/tags/itaggedfilefactory.h000066400000000000000000000061451224603511300210330ustar00rootroot00000000000000/** * \file itaggedfilefactory.h * Interface for tagged file factory. * * \b Project: Kid3 * \author Urs Fleisch * \date 22 Jul 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef ITAGGEDFILEFACTORY_H #define ITAGGEDFILEFACTORY_H #include #include "kid3api.h" class QString; class QStringList; class QPersistentModelIndex; class TaggedFile; /** * Interface for tagged file factory. */ class KID3_CORE_EXPORT ITaggedFileFactory { public: /** * Destructor. */ virtual ~ITaggedFileFactory() = 0; /** * Get name of factory, the same as the QObject::objectName() of the plugin. * @return factory name. */ virtual QString name() const = 0; /** * Get keys of available tagged file formats. * @return list of keys. */ virtual QStringList taggedFileKeys() const = 0; /** * Get features supported. * @param key tagged file key * @return bit mask with TaggedFile::Feature flags set. */ virtual int taggedFileFeatures(const QString& key) const = 0; /** * Initialize tagged file factory. * This method has to be called before creating a tagged file. * It can be called after the application is initialized and therefore can * access application data which is not possible in the constructor. * * @param key tagged file key */ virtual void initialize(const QString& key) = 0; /** * Create a tagged file. * * @param key tagged file key * @param dirName directory name * @param fileName filename * @param idx model index * @param features optional tagged file features (TaggedFile::Feature flags) * to activate at creation * * @return tagged file, 0 if type not supported. */ virtual TaggedFile* createTaggedFile( const QString& key, const QString& dirName, const QString& fileName, const QPersistentModelIndex& idx, int features = 0) = 0; /** * Get a list with all extensions (e.g. ".mp3") supported by TaggedFile subclass. * * @param key tagged file key * * @return list of file extensions. */ virtual QStringList supportedFileExtensions(const QString& key) const = 0; /** * Notify about configuration change. * This method shall be called when the configuration changes. * * @param key tagged file key */ virtual void notifyConfigurationChange(const QString& key) = 0; }; Q_DECLARE_INTERFACE(ITaggedFileFactory, "net.sourceforge.kid3.ITaggedFileFactory") #endif // ITAGGEDFILEFACTORY_H kid3-3.0.2/src/core/tags/pictureframe.cpp000066400000000000000000000425711224603511300202230ustar00rootroot00000000000000/** * \file pictureframe.cpp * Frame containing picture. * * \b Project: Kid3 * \author Urs Fleisch * \date 03 Mar 2008 * * Copyright (C) 2008-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "pictureframe.h" #include #include #include /** * Constructor. * * @param data binary picture data * @param description description * @param pictureType picture type * @param mimeType MIME type * @param enc text encoding * @param imgFormat image format */ PictureFrame::PictureFrame( const QByteArray& data, const QString& description, PictureType pictureType, const QString& mimeType, Field::TextEncoding enc, const QString& imgFormat) { setType(FT_Picture); setFields(*this, enc, imgFormat, mimeType, pictureType, description, data); } /** * Constructor. * * @param frame general frame */ PictureFrame::PictureFrame(const Frame& frame) { *(static_cast(this)) = frame; setType(FT_Picture); // Make sure all fields are available in the correct order Field::TextEncoding enc = Field::TE_ISO8859_1; PictureType pictureType = PT_CoverFront; QString imgFormat(QLatin1String("JPG")), mimeType(QLatin1String("image/jpeg")), description; QByteArray data; getFields(*this, enc, imgFormat, mimeType, pictureType, description, data); setFields(*this, enc, imgFormat, mimeType, pictureType, description, data); } /** * Destructor. */ PictureFrame::~PictureFrame() { } /** * Set all properties. * * @param frame frame to set * @param enc text encoding * @param imgFormat image format * @param mimeType MIME type * @param pictureType picture type * @param description description * @param data binary picture data */ void PictureFrame::setFields(Frame& frame, Field::TextEncoding enc, const QString& imgFormat, const QString& mimeType, PictureType pictureType, const QString& description, const QByteArray& data) { Field field; FieldList& fields = frame.fieldList(); fields.clear(); field.m_id = Field::ID_TextEnc; field.m_value = enc; fields.push_back(field); field.m_id = Field::ID_ImageFormat; field.m_value = imgFormat; fields.push_back(field); field.m_id = Field::ID_MimeType; field.m_value = mimeType; fields.push_back(field); field.m_id = Field::ID_PictureType; field.m_value = pictureType; fields.push_back(field); field.m_id = Field::ID_Description; field.m_value = description; fields.push_back(field); field.m_id = Field::ID_Data; field.m_value = data; fields.push_back(field); frame.setValue(description); } /** * Get all properties. * Unavailable fields are not set. * * @param frame frame to get * @param enc text encoding * @param imgFormat image format * @param mimeType MIME type * @param pictureType picture type * @param description description * @param data binary picture data */ void PictureFrame::getFields(const Frame& frame, Field::TextEncoding& enc, QString& imgFormat, QString& mimeType, PictureType& pictureType, QString& description, QByteArray& data) { for (Frame::FieldList::const_iterator it = frame.getFieldList().begin(); it != frame.getFieldList().end(); ++it) { switch ((*it).m_id) { case Field::ID_TextEnc: enc = static_cast((*it).m_value.toInt()); break; case Field::ID_ImageFormat: imgFormat = (*it).m_value.toString(); break; case Field::ID_MimeType: mimeType = (*it).m_value.toString(); break; case Field::ID_PictureType: pictureType = static_cast((*it).m_value.toInt()); break; case Field::ID_Description: description = (*it).m_value.toString(); break; case Field::ID_Data: data = (*it).m_value.toByteArray(); break; default: qDebug("Unknown picture field ID"); } } } /** * Check if all the fields of two picture frames are equal. * @param f1 first picture frame * @param f2 second picture frame * @return true if equal. */ bool PictureFrame::areFieldsEqual(const Frame& f1, const Frame& f2) { Field::TextEncoding enc1, enc2; QString imgFormat1, imgFormat2; QString mimeType1, mimeType2; PictureType pictureType1, pictureType2; QString description1, description2; QByteArray data1, data2; getFields(f1, enc1, imgFormat1, mimeType1, pictureType1, description1, data1); getFields(f2, enc2, imgFormat2, mimeType2, pictureType2, description2, data2); return (data1 == data2 && description1 == description2 && mimeType1 == mimeType2 && pictureType1 == pictureType2 && imgFormat1 == imgFormat2 && enc1 == enc2); } /** * Set value of a field. * * @param frame frame to set * @param id field ID * @param value field value * * @return true if field found and set. */ bool PictureFrame::setField(Frame& frame, Field::Id id, const QVariant& value) { for (Frame::FieldList::iterator it = frame.fieldList().begin(); it != frame.fieldList().end(); ++it) { if ((*it).m_id == id) { (*it).m_value = value; if (id == Field::ID_Description) frame.setValue(value.toString()); return true; } } return false; } /** * Get value of a field. * * @param frame frame to get * @param id field ID * * @return field value, invalid if not found. */ QVariant PictureFrame::getField(const Frame& frame, Field::Id id) { QVariant result; if (!frame.getFieldList().empty()) { for (Frame::FieldList::const_iterator it = frame.getFieldList().begin(); it != frame.getFieldList().end(); ++it) { if ((*it).m_id == id) { result = (*it).m_value; break; } } } return result; } /** * Set text encoding. * * @param frame frame to set * @param enc text encoding * * @return true if field found and set. */ bool PictureFrame::setTextEncoding(Frame& frame, Field::TextEncoding enc) { return setField(frame, Field::ID_TextEnc, enc); } /** * Get text encoding. * * @param frame frame to get * @param enc the text encoding is returned here * * @return true if field found. */ bool PictureFrame::getTextEncoding(const Frame& frame, Field::TextEncoding& enc) { QVariant var(getField(frame, Field::ID_TextEnc)); if (var.isValid()) { enc = static_cast(var.toInt()); return true; } return false; } /** * Set image format. * * @param frame frame to set * @param imgFormat image format * * @return true if field found and set. */ bool PictureFrame::setImageFormat(Frame& frame, const QString& imgFormat) { return setField(frame, Field::ID_ImageFormat, imgFormat); } /** * Get image format. * * @param frame frame to get * @param imgFormat the image format is returned here * * @return true if field found. */ bool PictureFrame::getImageFormat(const Frame& frame, QString& imgFormat) { QVariant var(getField(frame, Field::ID_ImageFormat)); if (var.isValid()) { imgFormat = var.toString(); return true; } return false; } /** * Set MIME type. * * @param frame frame to set * @param mimeType MIME type * * @return true if field found and set. */ bool PictureFrame::setMimeType(Frame& frame, const QString& mimeType) { return setField(frame, Field::ID_MimeType, mimeType); } /** * Get MIME type. * * @param frame frame to get * @param mimeType the MIME type is returned here * * @return true if field found. */ bool PictureFrame::getMimeType(const Frame& frame, QString& mimeType) { QVariant var(getField(frame, Field::ID_MimeType)); if (var.isValid()) { mimeType = var.toString(); return true; } return false; } /** * Set picture type. * * @param frame frame to set * @param pictureType picture type * * @return true if field found and set. */ bool PictureFrame::setPictureType(Frame& frame, PictureType pictureType) { return setField(frame, Field::ID_PictureType, pictureType); } /** * Get picture type. * * @param frame frame to get * @param pictureType the picture type is returned here * * @return true if field found. */ bool PictureFrame::getPictureType(const Frame& frame, PictureType& pictureType) { QVariant var(getField(frame, Field::ID_PictureType)); if (var.isValid()) { pictureType = static_cast(var.toInt()); return true; } return false; } /** * Set description. * * @param frame frame to set * @param description description * * @return true if field found and set. */ bool PictureFrame::setDescription(Frame& frame, const QString& description) { return setField(frame, Field::ID_Description, description); } /** * Get description. * * @param frame frame to get * @param description the description is returned here * * @return true if field found. */ bool PictureFrame::getDescription(const Frame& frame, QString& description) { QVariant var(getField(frame, Field::ID_Description)); if (var.isValid()) { description = var.toString(); return true; } return false; } /** * Set binary data. * * @param frame frame to set * @param data binary data * * @return true if field found and set. */ bool PictureFrame::setData(Frame& frame, const QByteArray& data) { return setField(frame, Field::ID_Data, data); } /** * Get binary data. * * @param frame frame to get * @param data the binary data is returned here * * @return true if field found. */ bool PictureFrame::getData(const Frame& frame, QByteArray& data) { QVariant var(getField(frame, Field::ID_Data)); if (var.isValid()) { data = var.toByteArray(); return true; } return false; } /** * Read binary data from file. * * @param frame frame to set * @param fileName name of data file * * @return true if file read, field found and set. */ bool PictureFrame::setDataFromFile(Frame& frame, const QString& fileName) { bool result = false; if (!fileName.isEmpty()) { QFile file(fileName); if (file.open(QIODevice::ReadOnly)) { size_t size = file.size(); char* data = new char[size]; QDataStream stream(&file); stream.readRawData(data, size); QByteArray ba; ba = QByteArray(data, size); result = setData(frame, ba); delete [] data; file.close(); } } return result; } /** * Get binary data from image. * * @param frame frame to set * @param image image * * @return true if field found and set. */ bool PictureFrame::setDataFromImage(Frame& frame, const QImage& image) { QByteArray ba; QBuffer buffer(&ba); buffer.open(QIODevice::WriteOnly); image.save(&buffer, "JPG"); return setData(frame, ba); } /** * Save binary data to a file. * * @param frame frame * @param fileName name of data file to save * * @return true if field found and saved. */ bool PictureFrame::writeDataToFile(const Frame& frame, const QString& fileName) { QByteArray ba; if (getData(frame, ba)) { QFile file(fileName); if (file.open(QIODevice::WriteOnly)) { QDataStream stream(&file); stream.writeRawData(ba.data(), ba.size()); file.close(); return true; } } return false; } /** * Set the MIME type and image format from the file name extension. * * @param frame frame to set * @param fileName name of data file * * @return true if field found and set. */ bool PictureFrame::setMimeTypeFromFileName(Frame& frame, const QString& fileName) { if (fileName.endsWith(QLatin1String(".jpg"), Qt::CaseInsensitive) || fileName.endsWith(QLatin1String(".jpeg"), Qt::CaseInsensitive)) { return setMimeType(frame, QLatin1String("image/jpeg")) && setImageFormat(frame, QLatin1String("JPG")); } else if (fileName.endsWith(QLatin1String(".png"), Qt::CaseInsensitive)) { return setMimeType(frame, QLatin1String("image/png")) && setImageFormat(frame, QLatin1String("PNG")); } return false; } /** * Get a 32-bit number from a byte array stored in big-endian order. * * @param data byte array * @param index index of first byte in data * * @return big endian 32-bit value. */ static unsigned long getBigEndianULongFromByteArray(const QByteArray& data, int index) { return ((unsigned char)data[index + 3] & 0xff) | (((unsigned char)data[index + 2] & 0xff) << 8) | (((unsigned char)data[index + 1] & 0xff) << 16) | (((unsigned char)data[index + 0] & 0xff) << 24); } /** * Render a 32-bit number to a byte array in big-endian order. * * @param value 32-bit value * @param data byte array * @param index index of first byte in data */ static void renderBigEndianULongToByteArray(unsigned long value, QByteArray& data, int index) { data[index + 3] = value & 0xff; value >>= 8; data[index + 2] = value & 0xff; value >>= 8; data[index + 1] = value & 0xff; value >>= 8; data[index + 0] = value & 0xff; } /** * Copy characters into a byte array. * * @param str source string * @param data destination byte array * @param index index of first byte in data * @param len number of bytes to copy */ static void renderCharsToByteArray(const char* str, QByteArray& data, int index, int len) { for (int i = 0; i < len; ++i) { data[index++] = *str++; } } /** * Set picture from a base64 string. * * @param frame frame to set * @param base64Value base64 string */ void PictureFrame::setFieldsFromBase64(Frame& frame, const QString& base64Value) { QByteArray ba = QByteArray::fromBase64(base64Value.toLatin1()); PictureFrame::PictureType pictureType = PictureFrame::PT_CoverFront; QString mimeType(QLatin1String("image/jpeg")); QString description(QLatin1String("")); if (frame.getInternalName() == QLatin1String("METADATA_BLOCK_PICTURE")) { unsigned long baSize = static_cast(ba.size()); if (baSize < 32) return; int index = 0; pictureType = static_cast( getBigEndianULongFromByteArray(ba, index)); index += 4; unsigned long mimeLen = getBigEndianULongFromByteArray(ba, index); index += 4; if (baSize < index + mimeLen + 24) return; mimeType = QString::fromLatin1(ba.data() + index, mimeLen); index += mimeLen; unsigned long descLen = getBigEndianULongFromByteArray(ba, index); index += 4; if (baSize < index + descLen + 20) return; description = QString::fromUtf8(ba.data() + index, descLen); index += descLen; index += 16; // width, height, depth, number of colors unsigned long picLen = getBigEndianULongFromByteArray(ba, index); index += 4; if (baSize < index + picLen) return; ba = ba.mid(index); } PictureFrame::setFields( frame, Frame::Field::TE_UTF8, QLatin1String(""), mimeType, pictureType, description, ba); } /** * Get picture to a base64 string. * * @param frame frame to get * @param base64Value base64 string to set */ void PictureFrame::getFieldsToBase64(const Frame& frame, QString& base64Value) { Frame::Field::TextEncoding enc; PictureFrame::PictureType pictureType = PictureFrame::PT_CoverFront; QString imgFormat, mimeType, description; QByteArray pic; PictureFrame::getFields(frame, enc, imgFormat, mimeType, pictureType, description, pic); if (frame.getInternalName() == QLatin1String("METADATA_BLOCK_PICTURE")) { QByteArray mimeStr = mimeType.toLatin1(); QByteArray descStr = description.toUtf8(); int mimeLen = mimeStr.length(); int descLen = descStr.length(); int picLen = pic.size(); QByteArray ba(32 + mimeLen + descLen + picLen, '\0'); int index = 0; renderBigEndianULongToByteArray(pictureType, ba, index); index += 4; renderBigEndianULongToByteArray(mimeLen, ba, index); index += 4; renderCharsToByteArray(mimeStr, ba, index, mimeLen); index += mimeLen; renderBigEndianULongToByteArray(descLen, ba, index); index += 4; renderCharsToByteArray(descStr, ba, index, descLen); index += descLen; int width = 0, height = 0, depth = 0, numColors = 0; QImage image; if (image.loadFromData(pic)) { width = image.width(); height = image.height(); depth = image.depth(); #if QT_VERSION >= 0x040600 numColors = image.colorCount(); #else numColors = image.numColors(); #endif } renderBigEndianULongToByteArray(width, ba, index); index += 4; renderBigEndianULongToByteArray(height, ba, index); index += 4; renderBigEndianULongToByteArray(depth, ba, index); index += 4; renderBigEndianULongToByteArray(numColors, ba, index); index += 4; renderBigEndianULongToByteArray(picLen, ba, index); index += 4; renderCharsToByteArray(pic.data(), ba, index, picLen); pic = ba; } base64Value = QString::fromLatin1(pic.toBase64()); } kid3-3.0.2/src/core/tags/pictureframe.h000066400000000000000000000204631224603511300176640ustar00rootroot00000000000000/** * \file pictureframe.h * Frame containing picture. * * \b Project: Kid3 * \author Urs Fleisch * \date 03 Mar 2008 * * Copyright (C) 2008-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef PICTUREFRAME_H #define PICTUREFRAME_H #include "frame.h" class QImage; /** Frame containing picture. */ class KID3_CORE_EXPORT PictureFrame : public Frame { public: /** Picture type, compatible with ID3v2 and FLAC. */ enum PictureType { PT_Other = 0, PT_Png32Icon = 1, PT_OtherIcon = 2, PT_CoverFront = 3, PT_CoverBack = 4, PT_LeafletPage = 5, PT_Media = 6, PT_LeadArtist = 7, PT_Artist = 8, PT_Conductor = 9, PT_Band = 10, PT_Composer = 11, PT_Lyricist = 12, PT_RecordingLocation = 13, PT_DuringRecording = 14, PT_DuringPerformance = 15, PT_Video = 16, PT_Fish = 17, PT_Illustration = 18, PT_ArtistLogo = 19, PT_PublisherLogo = 20 }; /** * Constructor. * * @param data binary picture data * @param description description * @param pictureType picture type * @param mimeType MIME type * @param enc text encoding * @param imgFormat image format */ explicit PictureFrame( const QByteArray& data = QByteArray(), const QString& description = QLatin1String(""), PictureType pictureType = PT_CoverFront, const QString& mimeType = QLatin1String("image/jpeg"), Field::TextEncoding enc = Field::TE_ISO8859_1, const QString& imgFormat = QLatin1String("JPG")); /** * Constructor. * * @param frame general frame */ explicit PictureFrame(const Frame& frame); /** * Destructor. */ ~PictureFrame(); /** * Set all properties. * * @param frame frame to set * @param enc text encoding * @param imgFormat image format * @param mimeType MIME type * @param pictureType picture type * @param description description * @param data binary picture data */ static void setFields( Frame& frame, Field::TextEncoding enc = Field::TE_ISO8859_1, const QString& imgFormat = QLatin1String("JPG"), const QString& mimeType = QLatin1String("image/jpeg"), PictureType pictureType = PT_CoverFront, const QString& description = QLatin1String(""), const QByteArray& data = QByteArray()); /** * Get all properties. * Unavailable fields are not set. * * @param frame frame to get * @param enc text encoding * @param imgFormat image format * @param mimeType MIME type * @param pictureType picture type * @param description description * @param data binary picture data */ static void getFields(const Frame& frame, Field::TextEncoding& enc, QString& imgFormat, QString& mimeType, PictureType& pictureType, QString& description, QByteArray& data); /** * Check if all the fields of two picture frames are equal. * @param f1 first picture frame * @param f2 second picture frame * @return true if equal. */ static bool areFieldsEqual(const Frame& f1, const Frame& f2); /** * Set text encoding. * * @param frame frame to set * @param enc text encoding * * @return true if field found and set. */ static bool setTextEncoding(Frame& frame, Field::TextEncoding enc); /** * Get text encoding. * * @param frame frame to get * @param enc the text encoding is returned here * * @return true if field found. */ static bool getTextEncoding(const Frame& frame, Field::TextEncoding& enc); /** * Set image format. * * @param frame frame to set * @param imgFormat image format * * @return true if field found and set. */ static bool setImageFormat(Frame& frame, const QString& imgFormat); /** * Get image format. * * @param frame frame to get * @param imgFormat the image format is returned here * * @return true if field found. */ static bool getImageFormat(const Frame& frame, QString& imgFormat); /** * Set MIME type. * * @param frame frame to set * @param mimeType MIME type * * @return true if field found and set. */ static bool setMimeType(Frame& frame, const QString& mimeType); /** * Get MIME type. * * @param frame frame to get * @param mimeType the MIME type is returned here * * @return true if field found. */ static bool getMimeType(const Frame& frame, QString& mimeType); /** * Set picture type. * * @param frame frame to set * @param pictureType picture type * * @return true if field found and set. */ static bool setPictureType(Frame& frame, PictureType pictureType); /** * Get picture type. * * @param frame frame to get * @param pictureType the picture type is returned here * * @return true if field found. */ static bool getPictureType(const Frame& frame, PictureType& pictureType); /** * Set description. * * @param frame frame to set * @param description description * * @return true if field found and set. */ static bool setDescription(Frame& frame, const QString& description); /** * Get description. * * @param frame frame to get * @param description the description is returned here * * @return true if field found. */ static bool getDescription(const Frame& frame, QString& description); /** * Set binary data. * * @param frame frame to set * @param data binary data * * @return true if field found and set. */ static bool setData(Frame& frame, const QByteArray& data); /** * Get binary data. * * @param frame frame to get * @param data the binary data is returned here * * @return true if field found. */ static bool getData(const Frame& frame, QByteArray& data); /** * Read binary data from file. * * @param frame frame to set * @param fileName name of data file * * @return true if file read, field found and set. */ static bool setDataFromFile(Frame& frame, const QString& fileName); /** * Get binary data from image. * * @param frame frame to set * @param image image * * @return true if field found and set. */ static bool setDataFromImage(Frame& frame, const QImage& image); /** * Save binary data to a file. * * @param frame frame * @param fileName name of data file to save * * @return true if field found and saved. */ static bool writeDataToFile(const Frame& frame, const QString& fileName); /** * Set the MIME type and image format from the file name extension. * * @param frame frame to set * @param fileName name of data file * * @return true if field found and set. */ static bool setMimeTypeFromFileName(Frame& frame, const QString& fileName); /** * Set picture from a base64 string. * * @param frame frame to set * @param base64Value base64 string */ static void setFieldsFromBase64(Frame& frame, const QString& base64Value); /** * Get picture to a base64 string. * * @param frame frame to get * @param base64Value base64 string to set */ static void getFieldsToBase64(const Frame& frame, QString& base64Value); private: /** * Set value of a field. * * @param frame frame to set * @param id field ID * @param value field value * * @return true if field found and set. */ static bool setField(Frame& frame, Field::Id id, const QVariant& value); /** * Get value of a field. * * @param frame frame to get * @param id field ID * * @return field value, invalid if not found. */ static QVariant getField(const Frame& frame, Field::Id id); }; #endif // PICTUREFRAME_H kid3-3.0.2/src/core/tags/taggedfile.cpp000066400000000000000000000725071224603511300176320ustar00rootroot00000000000000/** * \file taggedfile.cpp * Handling of tagged files. * * \b Project: Kid3 * \author Urs Fleisch * \date 25 Sep 2005 * * Copyright (C) 2005-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "taggedfile.h" #include #include #include #include #include "tagconfig.h" #include "genres.h" #include "modeliterator.h" #include "saferename.h" /** * Constructor. * * @param dn directory name * @param fn filename * @param idx model index */ TaggedFile::TaggedFile(const QString& dn, const QString& fn, const QPersistentModelIndex& idx) : m_dirname(dn), m_filename(fn), m_newFilename(fn), m_index(idx), m_changedV1(false), m_changedFramesV1(0), m_changedV2(false), m_changedFramesV2(0), m_truncation(0) { } /** * Destructor. */ TaggedFile::~TaggedFile() { } /** * Get features supported. * @return bit mask with Feature flags set. */ int TaggedFile::taggedFileFeatures() const { return 0; } /** * Get currently active tagged file features. * @return active tagged file features. * @see setActiveTaggedFileFeatures() */ int TaggedFile::activeTaggedFileFeatures() const { return 0; } /** * Activate some features provided by the tagged file. * For example, if the TF_ID3v24 feature is provided, it can be set, so that * writeTags() will write ID3v2.4.0 tags. If the feature is deactivated by * passing 0, tags in the default format will be written again. * * @param features bit mask with some of the Feature flags which are * provided by this file, as returned by taggedFileFeatures(), 0 to disable * special features. */ void TaggedFile::setActiveTaggedFileFeatures(int features) { Q_UNUSED(features) } /** * Get ID3v1 title. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ QString TaggedFile::getTitleV1() { return QString(); } /** * Get ID3v1 artist. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ QString TaggedFile::getArtistV1() { return QString(); } /** * Get ID3v1 album. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ QString TaggedFile::getAlbumV1() { return QString(); } /** * Get ID3v1 comment. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ QString TaggedFile::getCommentV1() { return QString(); } /** * Get ID3v1 year. * * @return number, * 0 if the field does not exist, * -1 if the tags do not exist. */ int TaggedFile::getYearV1() { return -1; } /** * Get ID3v1 track. * * @return number, * 0 if the field does not exist, * -1 if the tags do not exist. */ int TaggedFile::getTrackNumV1() { return -1; } /** * Get ID3v1 genre. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ QString TaggedFile::getGenreV1() { return QString(); } /** * Remove ID3v1 frames. * * @param flt filter specifying which frames to remove */ void TaggedFile::deleteFramesV1(const FrameFilter& flt) { if (flt.isEnabled(Frame::FT_Title)) setTitleV1(QLatin1String("")); if (flt.isEnabled(Frame::FT_Artist)) setArtistV1(QLatin1String("")); if (flt.isEnabled(Frame::FT_Album)) setAlbumV1(QLatin1String("")); if (flt.isEnabled(Frame::FT_Comment)) setCommentV1(QLatin1String("")); if (flt.isEnabled(Frame::FT_Date)) setYearV1(0); if (flt.isEnabled(Frame::FT_Track)) setTrackNumV1(0); if (flt.isEnabled(Frame::FT_Genre)) setGenreV1(QLatin1String("")); } /** * Set ID3v1 title. * * @param str string to set, "" to remove field. */ void TaggedFile::setTitleV1(const QString&) { } /** * Set ID3v1 artist. * * @param str string to set, "" to remove field. */ void TaggedFile::setArtistV1(const QString&) { } /** * Set ID3v1 album. * * @param str string to set, "" to remove field. */ void TaggedFile::setAlbumV1(const QString&) { } /** * Set ID3v1 comment. * * @param str string to set, "" to remove field. */ void TaggedFile::setCommentV1(const QString&) { } /** * Set ID3v1 year. * * @param num number to set, 0 to remove field. */ void TaggedFile::setYearV1(int) { } /** * Set ID3v1 track. * * @param num number to set, 0 to remove field. */ void TaggedFile::setTrackNumV1(int) { } /** * Set ID3v1 genre as text. * * @param str string to set, "" to remove field, QString::null to ignore. */ void TaggedFile::setGenreV1(const QString&) { } /** * Check if file has an ID3v1 tag. * * @return true if a V1 tag is available. * @see isTagInformationRead() */ bool TaggedFile::hasTagV1() const { return false; } /** * Check if ID3v1 tags are supported by the format of this file. * * @return true if V1 tags are supported. */ bool TaggedFile::isTagV1Supported() const { return false; } /** * Get absolute filename. * * @return absolute file path. */ QString TaggedFile::getAbsFilename() const { QDir dir(m_dirname); return QDir::cleanPath(dir.absoluteFilePath(m_newFilename)); } /** * Mark tag 1 as changed. * * @param type type of changed frame */ void TaggedFile::markTag1Changed(Frame::Type type) { m_changedV1 = true; if (static_cast(type) < sizeof(m_changedFramesV1) * 8) { m_changedFramesV1 |= (1ULL << type); } } /** * Mark tag 2 as changed. * * @param type type of changed frame */ void TaggedFile::markTag2Changed(Frame::Type type) { m_changedV2 = true; if (static_cast(type) < sizeof(m_changedFramesV2) * 8) { m_changedFramesV2 |= (1ULL << type); } } /** * Remove artist part from album string. * This is used when only the album is needed, but the regexp in * getTagsFromFilename() matched a "artist - album" string. * * @param album album string * * @return album with artist removed. */ static QString removeArtist(const QString& album) { QString str(album); int pos = str.indexOf(QLatin1String(" - ")); if (pos != -1) { str.remove(0, pos + 3); } return str; } /** * Get tags from filename. * Supported formats: * album/track - artist - song * artist - album/track song * /artist - album - track - song * album/artist - track - song * artist/album/track song * album/artist - song * * @param frames frames to put result * @param fmt format string containing the following codes: * %s title (song) * %l album * %a artist * %c comment * %y year * %t track */ void TaggedFile::getTagsFromFilename(FrameCollection& frames, const QString& fmt) { QRegExp re; QString fn(getAbsFilename()); // construct regular expression from format string // if the format does not contain a '_', they are replaced by spaces // in the filename. QString fileName(fn); if (!fmt.contains(QLatin1Char('_'))) { fileName.replace(QLatin1Char('_'), QLatin1Char(' ')); } // escape regexp characters QString pattern; uint fmtLen = fmt.length(); static const QString escChars(QLatin1String("+?.*^$()[]{}|\\")); for (uint i = 0; i < fmtLen; ++i) { const QChar ch = fmt.at(i); if (escChars.contains(ch)) { pattern += QLatin1Char('\\'); } pattern += ch; } // and finally a dot followed by 3 or 4 characters for the extension pattern += QLatin1String("\\..{3,4}$"); static const struct { const char* from; const char* to; } codeToName[] = { { "%s", "%\\{title\\}" }, { "%l", "%\\{album\\}" }, { "%a", "%\\{artist\\}" }, { "%c", "%\\{comment\\}" }, { "%y", "%\\{date\\}" }, { "%t", "%\\{track number\\}" }, { "%g", "%\\{genre\\}" }, { "%\\{year\\}", "%\\{date\\}" }, { "%\\{track\\}", "%\\{track number\\}" }, { "%\\{tracknumber\\}", "%\\{track number\\}" }, { "%\\{discnumber\\}", "%\\{disc number\\}" } }; int percentIdx = 0, nr = 1; for (unsigned i = 0; i < sizeof(codeToName) / sizeof(codeToName[0]); ++i) { pattern.replace(QString::fromLatin1(codeToName[i].from), QString::fromLatin1(codeToName[i].to)); } QMap codePos; while (((percentIdx = pattern.indexOf(QLatin1String("%\\{"), percentIdx)) >= 0) && (percentIdx < static_cast(pattern.length()) - 1)) { int closingBracePos = pattern.indexOf(QLatin1String("\\}"), percentIdx + 3); if (closingBracePos > percentIdx + 3) { QString code = pattern.mid(percentIdx + 3, closingBracePos - percentIdx - 3); codePos[code] = nr++; if (code == QLatin1String("track number") || code == QLatin1String("date") || code == QLatin1String("disc number") || code == QLatin1String("bpm")) { pattern.replace(percentIdx, closingBracePos - percentIdx + 2, QLatin1String("(\\d{1,4})")); percentIdx += 9; } else { pattern.replace(percentIdx, closingBracePos - percentIdx + 2, QLatin1String("([^-_\\./ ][^/]*[^-_/ ])")); percentIdx += 23; } } else { percentIdx += 3; } } re.setPattern(pattern); if (re.indexIn(fileName) != -1) { for (QMap::iterator it = codePos.begin(); it != codePos.end(); ++it) { QString name = it.key(); QString str = re.cap(*it); if (!str.isEmpty()) { if (name == QLatin1String("track number") && str.length() == 2 && str[0] == QLatin1Char('0')) { // remove leading zero str = str.mid(1); } if (name != QLatin1String("ignore")) frames.setValue(Frame::ExtendedType(name), str); } } return; } // album/track - artist - song re.setPattern(QLatin1String("([^/]+)/(\\d{1,3})[-_\\. ]+([^-_\\./ ][^/]+)[_ ]-[_ ]([^-_\\./ ][^/]+)\\..{3,4}$")); if (re.indexIn(fn) != -1) { frames.setAlbum(removeArtist(re.cap(1))); frames.setTrack(re.cap(2).toInt()); frames.setArtist(re.cap(3)); frames.setTitle(re.cap(4)); return; } // artist - album (year)/track song re.setPattern(QLatin1String("([^/]+)[_ ]-[_ ]([^/]+)[_ ]\\((\\d{4})\\)/(\\d{1,3})[-_\\. ]+([^-_\\./ ][^/]+)\\..{3,4}$")); if (re.indexIn(fn) != -1) { frames.setArtist(re.cap(1)); frames.setAlbum(re.cap(2)); frames.setYear(re.cap(3).toInt()); frames.setTrack(re.cap(4).toInt()); frames.setTitle(re.cap(5)); return; } // artist - album/track song re.setPattern(QLatin1String("([^/]+)[_ ]-[_ ]([^/]+)/(\\d{1,3})[-_\\. ]+([^-_\\./ ][^/]+)\\..{3,4}$")); if (re.indexIn(fn) != -1) { frames.setArtist(re.cap(1)); frames.setAlbum(re.cap(2)); frames.setTrack(re.cap(3).toInt()); frames.setTitle(re.cap(4)); return; } // /artist - album - track - song re.setPattern(QLatin1String("/([^/]+[^-_/ ])[_ ]-[_ ]([^-_/ ][^/]+[^-_/ ])[-_\\. ]+(\\d{1,3})[-_\\. ]+([^-_\\./ ][^/]+)\\..{3,4}$")); if (re.indexIn(fn) != -1) { frames.setArtist(re.cap(1)); frames.setAlbum(re.cap(2)); frames.setTrack(re.cap(3).toInt()); frames.setTitle(re.cap(4)); return; } // album/artist - track - song re.setPattern(QLatin1String("([^/]+)/([^/]+[^-_\\./ ])[-_\\. ]+(\\d{1,3})[-_\\. ]+([^-_\\./ ][^/]+)\\..{3,4}$")); if (re.indexIn(fn) != -1) { frames.setAlbum(removeArtist(re.cap(1))); frames.setArtist(re.cap(2)); frames.setTrack(re.cap(3).toInt()); frames.setTitle(re.cap(4)); return; } // artist/album/track song re.setPattern(QLatin1String("([^/]+)/([^/]+)/(\\d{1,3})[-_\\. ]+([^-_\\./ ][^/]+)\\..{3,4}$")); if (re.indexIn(fn) != -1) { frames.setArtist(re.cap(1)); frames.setAlbum(re.cap(2)); frames.setTrack(re.cap(3).toInt()); frames.setTitle(re.cap(4)); return; } // album/artist - song re.setPattern(QLatin1String("([^/]+)/([^/]+[^-_/ ])[_ ]-[_ ]([^-_/ ][^/]+)\\..{3,4}$")); if (re.indexIn(fn) != -1) { frames.setAlbum(removeArtist(re.cap(1))); frames.setArtist(re.cap(2)); frames.setTitle(re.cap(3)); return; } } /** * Format a time string "h:mm:ss". * If the time is less than an hour, the hour is not put into the * string and the minute is not padded with zeroes. * * @param seconds time in seconds * * @return string with the time in hours, minutes and seconds. */ QString TaggedFile::formatTime(unsigned seconds) { unsigned hours = seconds / 3600; seconds %= 3600; unsigned minutes = seconds / 60; seconds %= 60; QString timeStr; if (hours > 0) { timeStr.sprintf("%u:%02u:%02u", hours, minutes, seconds); } else { timeStr.sprintf("%u:%02u", minutes, seconds); } return timeStr; } /** * Rename a file. * This methods takes care of case insensitive filesystems. * * @param fnOld old filename * @param fnNew new filename * * @return true if ok. */ bool TaggedFile::renameFile(const QString& fnOld, const QString& fnNew) const { if (fnNew.toLower() == fnOld.toLower()) { // If the filenames only differ in case, the new file is reported to // already exist on case insensitive filesystems (e.g. Windows), // so it is checked if the new file is really the old file by // comparing inodes and devices. If the files are not the same, // another file would be overwritten and an error is reported. if (QFile::exists(m_dirname + QDir::separator() + fnNew)) { struct stat statOld, statNew; if (::stat((m_dirname + QDir::separator() + fnOld).toLatin1().data(), &statOld) == 0 && ::stat((m_dirname + QDir::separator() + fnNew).toLatin1().data(), &statNew) == 0 && !(statOld.st_ino == statNew.st_ino && statOld.st_dev == statNew.st_dev)) { qDebug("rename(%s, %s): %s already exists", fnOld.toLatin1().data(), fnNew.toLatin1().data(), fnNew.toLatin1().data()); return false; } } // if the filenames only differ in case, first rename to a // temporary filename, so that it works also with case // insensitive filesystems (e.g. Windows). QString temp_filename(fnNew); temp_filename.append(QLatin1String("_CASE")); if (!Utils::safeRename(m_dirname, fnOld, temp_filename)) { qDebug("rename(%s, %s) failed", fnOld.toLatin1().data(), temp_filename.toLatin1().data()); return false; } if (!Utils::safeRename(m_dirname, temp_filename, fnNew)) { qDebug("rename(%s, %s) failed", temp_filename.toLatin1().data(), fnNew.toLatin1().data()); return false; } } else if (QFile::exists(m_dirname + QDir::separator() + fnNew)) { qDebug("rename(%s, %s): %s already exists", fnOld.toLatin1().data(), fnNew.toLatin1().data(), fnNew.toLatin1().data()); return false; } else if (!Utils::safeRename(m_dirname, fnOld, fnNew)) { qDebug("rename(%s, %s) failed", fnOld.toLatin1().data(), fnNew.toLatin1().data()); return false; } return true; } /** * Get field name for comment from configuration. * * @return field name. */ QString TaggedFile::getCommentFieldName() const { return TagConfig::instance().commentName(); } /** * Split a track string into number and total. * * @param str track * @param total the total is returned here if found, else 0 * * @return number, 0 if parsing failed, -1 if str is null */ int TaggedFile::splitNumberAndTotal(const QString& str, int* total) { if (total) *total = 0; if (str.isNull()) return -1; int slashPos = str.indexOf(QLatin1Char('/')); if (slashPos == -1) return str.toInt(); if (total) *total = str.mid(slashPos + 1).toInt(); return str.left(slashPos).toInt(); } /** * Get the total number of tracks in the directory. * * @return total number of tracks, -1 if unavailable. */ int TaggedFile::getTotalNumberOfTracksInDir() const { int numTracks = -1; QModelIndex parentIdx = m_index.parent(); if (parentIdx.isValid()) { numTracks = 0; TaggedFileOfDirectoryIterator it(parentIdx); while (it.hasNext()) { it.next(); ++numTracks; } } return numTracks; } /** * Get the total number of tracks if it is enabled. * * @return total number of tracks, * -1 if disabled or unavailable. */ int TaggedFile::getTotalNumberOfTracksIfEnabled() const { return TagConfig::instance().enableTotalNumberOfTracks() ? getTotalNumberOfTracksInDir() : -1; } /** * Format track number/total number of tracks with configured digits. * * @param num track number, <= 0 if empty * @param numTracks total number of tracks, <= 0 to disable * * @return formatted "track/total" string. */ QString TaggedFile::trackNumberString(int num, int numTracks) const { int numDigits = getTrackNumberDigits(); QString str; if (num != 0) { if (numDigits > 0) { str.sprintf("%0*d", numDigits, num); } else { str.setNum(num); } if (numTracks > 0) { str += QLatin1Char('/'); if (numDigits > 0) { str += QString().sprintf("%0*d", numDigits, numTracks); } else { str += QString::number(numTracks); } } } else { str = QLatin1String(""); } return str; } /** * Format the track number (digits, total number of tracks) if enabled. * * @param value string containing track number, will be modified * @param addTotal true to add total number of tracks if enabled * "/t" with t = total number of tracks will be appended * if enabled and value contains a number */ void TaggedFile::formatTrackNumberIfEnabled(QString& value, bool addTotal) const { int numDigits = getTrackNumberDigits(); int numTracks = addTotal ? getTotalNumberOfTracksIfEnabled() : -1; if (numTracks > 0 || numDigits > 1) { bool ok; int trackNr = value.toInt(&ok); if (ok && trackNr > 0) { if (numTracks > 0) { value.sprintf("%0*d/%0*d", numDigits, trackNr, numDigits, numTracks); } else { value.sprintf("%0*d", numDigits, trackNr); } } } } /** * Get the number of track number digits configured. * * @return track number digits, * 1 if invalid or unavailable. */ int TaggedFile::getTrackNumberDigits() const { int numDigits = TagConfig::instance().trackNumberDigits(); if (numDigits < 1 || numDigits > 5) numDigits = 1; return numDigits; } /** * Remove ID3v2 frames. * * @param flt filter specifying which frames to remove */ void TaggedFile::deleteFramesV2(const FrameFilter& flt) { if (flt.isEnabled(Frame::FT_Title)) setTitleV2(QLatin1String("")); if (flt.isEnabled(Frame::FT_Artist)) setArtistV2(QLatin1String("")); if (flt.isEnabled(Frame::FT_Album)) setAlbumV2(QLatin1String("")); if (flt.isEnabled(Frame::FT_Comment)) setCommentV2(QLatin1String("")); if (flt.isEnabled(Frame::FT_Date)) setYearV2(0); if (flt.isEnabled(Frame::FT_Track)) setTrackV2(QLatin1String("")); if (flt.isEnabled(Frame::FT_Genre)) setGenreV2(QLatin1String("")); } /** * Get the format of tag 1. * * @return string describing format of tag 1, * e.g. "ID3v1.1", "ID3v2.3", "Vorbis", "APE", * QString::null if unknown. */ QString TaggedFile::getTagFormatV1() const { return QString(); } /** * Get the format of tag 2. * * @return string describing format of tag 2, * e.g. "ID3v1.1", "ID3v2.3", "Vorbis", "APE", * QString::null if unknown. */ QString TaggedFile::getTagFormatV2() const { return QString(); } /** * Check if a string has to be truncated. * * @param str string to be checked * @param flag flag to be set if string has to be truncated * @param len maximum length of string * * @return str truncated to len characters if necessary, else QString::null. */ QString TaggedFile::checkTruncation( const QString& str, quint64 flag, int len) { if (static_cast(str.length()) > len) { QString s = str; s.truncate(len); m_truncation |= flag; return s; } else { m_truncation &= ~flag; return QString(); } } /** * Check if a number has to be truncated. * * @param val value to be checked * @param flag flag to be set if number has to be truncated * @param max maximum value * * @return val truncated to max if necessary, else -1. */ int TaggedFile::checkTruncation(int val, quint64 flag, int max) { if (val > max) { m_truncation |= flag; return max; } else { m_truncation &= ~flag; return -1; } } /** * Get a specific frame from the tags 1. * * @param type frame type * @param frame the frame is returned here * * @return true if ok. */ bool TaggedFile::getFrameV1(Frame::Type type, Frame& frame) { int n = -1; bool number = false; switch (type) { case Frame::FT_Album: frame.m_value = getAlbumV1(); break; case Frame::FT_Artist: frame.m_value = getArtistV1(); break; case Frame::FT_Comment: frame.m_value = getCommentV1(); break; case Frame::FT_Date: n = getYearV1(); number = true; break; case Frame::FT_Genre: frame.m_value = getGenreV1(); break; case Frame::FT_Title: frame.m_value = getTitleV1(); break; case Frame::FT_Track: n = getTrackNumV1(); number = true; break; default: // maybe handled in a subclass return false; } if (number) { if (n == -1) { frame.m_value = QString(); } else if (n == 0) { frame.m_value = QLatin1String(""); } else { frame.m_value.setNum(n); } } frame.setType(type); return true; } /** * Set a frame in the tags 1. * * @param frame frame to set. * * @return true if ok. */ bool TaggedFile::setFrameV1(const Frame& frame) { int n = -1; if (frame.getType() == Frame::FT_Date || frame.getType() == Frame::FT_Track) { if (frame.isInactive()) { n = -1; } else if (frame.isEmpty()) { n = 0; } else { n = Frame::numberWithoutTotal(frame.m_value); } } switch (frame.getType()) { case Frame::FT_Album: setAlbumV1(frame.m_value); break; case Frame::FT_Artist: setArtistV1(frame.m_value); break; case Frame::FT_Comment: setCommentV1(frame.m_value); break; case Frame::FT_Date: setYearV1(n); break; case Frame::FT_Genre: setGenreV1(frame.m_value); break; case Frame::FT_Title: setTitleV1(frame.m_value); break; case Frame::FT_Track: setTrackNumV1(n); break; default: // maybe handled in a subclass return false; } return true; } /** * Get a specific frame from the tags 2. * * @param type frame type * @param frame the frame is returned here * * @return true if ok. */ bool TaggedFile::getFrameV2(Frame::Type type, Frame& frame) { int n = -1; bool number = false; switch (type) { case Frame::FT_Album: frame.m_value = getAlbumV2(); break; case Frame::FT_Artist: frame.m_value = getArtistV2(); break; case Frame::FT_Comment: frame.m_value = getCommentV2(); break; case Frame::FT_Date: n = getYearV2(); number = true; break; case Frame::FT_Genre: frame.m_value = getGenreV2(); break; case Frame::FT_Title: frame.m_value = getTitleV2(); break; case Frame::FT_Track: frame.m_value = getTrackV2(); break; default: // maybe handled in a subclass return false; } if (number) { if (n == -1) { frame.m_value = QString(); } else if (n == 0) { frame.m_value = QLatin1String(""); } else { frame.m_value.setNum(n); } } frame.setType(type); return true; } /** * Set a frame in the tags 2. * * @param frame frame to set * * @return true if ok. */ bool TaggedFile::setFrameV2(const Frame& frame) { int n = -1; if (frame.getType() == Frame::FT_Date) { if (frame.isInactive()) { n = -1; } else if (frame.isEmpty()) { n = 0; } else { n = Frame::numberWithoutTotal(frame.m_value); } } switch (frame.getType()) { case Frame::FT_Album: setAlbumV2(frame.m_value); break; case Frame::FT_Artist: setArtistV2(frame.m_value); break; case Frame::FT_Comment: setCommentV2(frame.m_value); break; case Frame::FT_Date: setYearV2(n); break; case Frame::FT_Genre: setGenreV2(frame.m_value); break; case Frame::FT_Title: setTitleV2(frame.m_value); break; case Frame::FT_Track: setTrackV2(frame.m_value); break; default: // maybe handled in a subclass return false; } return true; } /** * Add a frame in the tags 2. * * @param frame frame to add, a field list may be added by this method * * @return true if ok. */ bool TaggedFile::addFrameV2(Frame& frame) { return TaggedFile::setFrameV2(frame); } /** * Delete a frame in the tags 2. * * @param frame frame to delete. * * @return true if ok. */ bool TaggedFile::deleteFrameV2(const Frame& frame) { Frame emptyFrame(frame); emptyFrame.setValue(QLatin1String("")); return setFrameV2(emptyFrame); } /** * Get all frames in tag 1. * * @param frames frame collection to set. */ void TaggedFile::getAllFramesV1(FrameCollection& frames) { frames.clear(); Frame frame; for (int i = Frame::FT_FirstFrame; i <= Frame::FT_LastV1Frame; ++i) { if (getFrameV1(static_cast(i), frame)) { frames.insert(frame); } } } /** * Set frames in tag 1. * * @param frames frame collection * @param onlyChanged only frames with value marked as changed are set */ void TaggedFile::setFramesV1(const FrameCollection& frames, bool onlyChanged) { for (FrameCollection::const_iterator it = frames.begin(); it != frames.end(); ++it) { if (!onlyChanged || it->isValueChanged()) { setFrameV1(*it); } } } /** * Get all frames in tag 2. * This generic implementation only supports the standard tags and should * be reimplemented in derived classes. * * @param frames frame collection to set. */ void TaggedFile::getAllFramesV2(FrameCollection& frames) { frames.clear(); Frame frame; for (int i = Frame::FT_FirstFrame; i <= Frame::FT_LastV1Frame; ++i) { if (getFrameV2(static_cast(i), frame)) { frames.insert(frame); } } } /** * Close any file handles which are held open by the tagged file object. * The default implementation does nothing. If a concrete subclass holds * any file handles open, it has to close them in this method. This method * can be used before operations which require that a file is not open, * e.g. file renaming on Windows. */ void TaggedFile::closeFileHandle() { } /** * Set frames in tag 2. * * @param frames frame collection * @param onlyChanged only frames with value marked as changed are set */ void TaggedFile::setFramesV2(const FrameCollection& frames, bool onlyChanged) { bool myFramesValid = false; FrameCollection myFrames; for (FrameCollection::const_iterator it = frames.begin(); it != frames.end(); ++it) { if (!onlyChanged || it->isValueChanged()) { if (it->getIndex() != -1) { // The frame has an index, so the original tag can be modified setFrameV2(*it); } else { // The frame does not have an index if (it->getType() <= Frame::FT_LastV1Frame) { // Standard tags can be handled with the basic method TaggedFile::setFrameV2(*it); } else { // The frame has to be looked up and modified if (!myFramesValid) { getAllFramesV2(myFrames); myFramesValid = true; } FrameCollection::iterator myIt = myFrames.find(*it); if (myIt != myFrames.end() && myIt->getIndex() != -1) { Frame myFrame(*it); myFrame.setIndex(myIt->getIndex()); setFrameV2(myFrame); } else { // Such a frame does not exist, add a new one. Frame myFrame(*it); addFrameV2(myFrame); setFrameV2(myFrame); } } } } } } /** * Constructor. */ TaggedFile::DetailInfo::DetailInfo() : valid(false), vbr(false), channelMode(CM_None), channels(0), sampleRate(0), bitrate(0), duration(0) { } /** * Destructor. */ TaggedFile::DetailInfo::~DetailInfo() { } /** * Get string representation of detail information. * @return information summary as string. */ QString TaggedFile::DetailInfo::toString() const { QString str; if (valid) { str = format; str += QLatin1Char(' '); if (bitrate > 0 && bitrate < 999) { if (vbr) str += QLatin1String("VBR "); str += QString::number(bitrate); str += QLatin1String(" kbps "); } if (sampleRate > 0) { str += QString::number(sampleRate); str += QLatin1String(" Hz "); } switch (channelMode) { case TaggedFile::DetailInfo::CM_Stereo: str += QLatin1String("Stereo "); break; case TaggedFile::DetailInfo::CM_JointStereo: str += QLatin1String("Joint Stereo "); break; default: if (channels > 0) { str += QString::number(channels); str += QLatin1String(" Channels "); } } if (duration > 0) { str += TaggedFile::formatTime(duration); } } return str; } kid3-3.0.2/src/core/tags/taggedfile.h000066400000000000000000000504651224603511300172760ustar00rootroot00000000000000/** * \file taggedfile.h * Base class for tagged files. * * \b Project: Kid3 * \author Urs Fleisch * \date 25 Sep 2005 * * Copyright (C) 2005-2011 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef TAGGEDFILE_H #define TAGGEDFILE_H #include #include #include #include #include "frame.h" /** Base class for tagged files. */ class KID3_CORE_EXPORT TaggedFile { public: /** * Special features and formats supported. * Additional information which cannot be deduced from the file format * supported. */ enum Feature { TF_ID3v11 = 1 << 0, /**< Supports ID3v1.1 tags */ TF_ID3v22 = 1 << 1, /**< Supports ID3v2.2 tags */ TF_ID3v23 = 1 << 2, /**< Supports ID3v2.3 tags */ TF_ID3v24 = 1 << 3, /**< Supports ID3v2.4 tags */ TF_OggPictures = 1 << 4 /**< Supports pictures in Ogg files */ }; /** Information about file. */ struct KID3_CORE_EXPORT DetailInfo { /** Channel mode. */ enum ChannelMode { CM_None, CM_Stereo, CM_JointStereo }; /** Constructor. */ DetailInfo(); /** Destructor. */ ~DetailInfo(); bool valid; /**< true if information valid */ bool vbr; /**< true if variable bitrate */ QString format; /**< format description */ ChannelMode channelMode; /**< channel mode */ unsigned channels; /**< number of channels > 0 */ unsigned sampleRate; /**< sample rate in Hz > 0 */ unsigned bitrate; /**< 0 < bitrate in kbps < 999 */ unsigned long duration; /**< duration in seconds > 0 */ /** * Get string representation of detail information. * @return information summary as string. */ QString toString() const; }; /** * Constructor. * * @param dn directory name * @param fn filename * @param idx model index */ TaggedFile(const QString& dn, const QString& fn, const QPersistentModelIndex& idx); /** * Destructor. */ virtual ~TaggedFile(); /** * Set file name. * * @param fn file name */ void setFilename(const QString& fn) { m_newFilename = fn; } /** * Get file name. * * @return file name */ const QString& getFilename() const { return m_newFilename; } /** * Get directory name. * * @return directory name */ QString getDirname() const { return m_dirname; } /** * Get key of tagged file format. * @return key. */ virtual QString taggedFileKey() const = 0; /** * Get features supported. * @return bit mask with Feature flags set. */ virtual int taggedFileFeatures() const; /** * Get currently active tagged file features. * @return active tagged file features. * @see setActiveTaggedFileFeatures() */ virtual int activeTaggedFileFeatures() const; /** * Activate some features provided by the tagged file. * For example, if the TF_ID3v24 feature is provided, it can be set, so that * writeTags() will write ID3v2.4.0 tags. If the feature is deactivated by * passing 0, tags in the default format will be written again. * * @param features bit mask with some of the Feature flags which are * provided by this file, as returned by taggedFileFeatures(), 0 to disable * special features. */ virtual void setActiveTaggedFileFeatures(int features); /** * Read tags from file. * * @param force true to force reading even if tags were already read. */ virtual void readTags(bool force) = 0; /** * Write tags to file and rename it if necessary. * * @param force true to force writing even if file was not changed. * @param renamed will be set to true if the file was renamed, * i.e. the file name is no longer valid, else *renamed * is left unchanged * @param preserve true to preserve file time stamps * * @return true if ok, false if the file could not be written or renamed. */ virtual bool writeTags(bool force, bool* renamed, bool preserve) = 0; /** * Get ID3v1 title. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ virtual QString getTitleV1(); /** * Get ID3v1 artist. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ virtual QString getArtistV1(); /** * Get ID3v1 album. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ virtual QString getAlbumV1(); /** * Get ID3v1 comment. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ virtual QString getCommentV1(); /** * Get ID3v1 year. * * @return number, * 0 if the field does not exist, * -1 if the tags do not exist. */ virtual int getYearV1(); /** * Get ID3v1 track. * * @return number, * 0 if the field does not exist, * -1 if the tags do not exist. */ virtual int getTrackNumV1(); /** * Get ID3v1 genre. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ virtual QString getGenreV1(); /** * Remove ID3v1 frames. * * @param flt filter specifying which frames to remove */ virtual void deleteFramesV1(const FrameFilter& flt); /** * Set ID3v1 title. * * @param str string to set, "" to remove field. */ virtual void setTitleV1(const QString& str); /** * Set ID3v1 artist. * * @param str string to set, "" to remove field. */ virtual void setArtistV1(const QString& str); /** * Set ID3v1 album. * * @param str string to set, "" to remove field. */ virtual void setAlbumV1(const QString& str); /** * Set ID3v1 comment. * * @param str string to set, "" to remove field. */ virtual void setCommentV1(const QString& str); /** * Set ID3v1 year. * * @param num number to set, 0 to remove field. */ virtual void setYearV1(int num); /** * Set ID3v1 track. * * @param num number to set, 0 to remove field. */ virtual void setTrackNumV1(int num); /** * Set ID3v1 genre as text. * * @param str string to set, "" to remove field, QString::null to ignore. */ virtual void setGenreV1(const QString& str); /** * Check if file has an ID3v1 tag. * * @return true if a V1 tag is available. * @see isTagInformationRead() */ virtual bool hasTagV1() const; /** * Check if ID3v1 tags are supported by the format of this file. * * @return true if V1 tags are supported. */ virtual bool isTagV1Supported() const; /** * Get ID3v2 title. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ virtual QString getTitleV2() = 0; /** * Get ID3v2 artist. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ virtual QString getArtistV2() = 0; /** * Get ID3v2 album. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ virtual QString getAlbumV2() = 0; /** * Get ID3v2 comment. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ virtual QString getCommentV2() = 0; /** * Get ID3v2 year. * * @return number, * 0 if the field does not exist, * -1 if the tags do not exist. */ virtual int getYearV2() = 0; /** * Get ID3v2 track. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ virtual QString getTrackV2() = 0; /** * Remove ID3v2 frames. * * @param flt filter specifying which frames to remove */ virtual void deleteFramesV2(const FrameFilter& flt); /** * Get ID3v2 genre as text. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ virtual QString getGenreV2() = 0; /** * Set ID3v2 title. * * @param str string to set, "" to remove field. */ virtual void setTitleV2(const QString& str) = 0; /** * Set ID3v2 artist. * * @param str string to set, "" to remove field. */ virtual void setArtistV2(const QString& str) = 0; /** * Set ID3v2 album. * * @param str string to set, "" to remove field. */ virtual void setAlbumV2(const QString& str) = 0; /** * Set ID3v2 comment. * * @param str string to set, "" to remove field. */ virtual void setCommentV2(const QString& str) = 0; /** * Set ID3v2 year. * * @param num number to set, 0 to remove field. */ virtual void setYearV2(int num) = 0; /** * Set ID3v2 track. * * @param track string to set, "" to remove field, QString::null to ignore. */ virtual void setTrackV2(const QString& track) = 0; /** * Set ID3v2 genre as text. * * @param str string to set, "" to remove field, QString::null to ignore. */ virtual void setGenreV2(const QString& str) = 0; /** * Check if file has an ID3v2 tag. * * @return true if a V2 tag is available. * @see isTagInformationRead() */ virtual bool hasTagV2() const = 0; /** * Check if tag information has already been read. * * @return true if information is available, * false if the tags have not been read yet, in which case * hasTagV1() and hasTagV2() do not return meaningful information. */ virtual bool isTagInformationRead() const = 0; /** * Get technical detail information. * * @param info the detail information is returned here */ virtual void getDetailInfo(DetailInfo& info) const = 0; /** * Get duration of file. * * @return duration in seconds, * 0 if unknown. */ virtual unsigned getDuration() const = 0; /** * Get file extension including the dot. * * @return file extension, e.g. ".mp3". */ virtual QString getFileExtension() const = 0; /** * Get the format of tag 1. * * @return string describing format of tag 1, * e.g. "ID3v1.1", "ID3v2.3", "Vorbis", "APE", * QString::null if unknown. */ virtual QString getTagFormatV1() const; /** * Get the format of tag 2. * * @return string describing format of tag 2, * e.g. "ID3v1.1", "ID3v2.3", "Vorbis", "APE", * QString::null if unknown. */ virtual QString getTagFormatV2() const; /** * Get a specific frame from the tags 1. * * @param type frame type * @param frame the frame is returned here * * @return true if ok. */ virtual bool getFrameV1(Frame::Type type, Frame& frame); /** * Set a frame in the tags 1. * * @param frame frame to set. * * @return true if ok. */ virtual bool setFrameV1(const Frame& frame); /** * Get a specific frame from the tags 2. * * @param type frame type * @param frame the frame is returned here * * @return true if ok. */ virtual bool getFrameV2(Frame::Type type, Frame& frame); /** * Set a frame in the tags 2. * * @param frame frame to set * * @return true if ok. */ virtual bool setFrameV2(const Frame& frame); /** * Add a frame in the tags 2. * * @param frame frame to add, a field list may be added by this method * * @return true if ok. */ virtual bool addFrameV2(Frame& frame); /** * Delete a frame in the tags 2. * * @param frame frame to delete. * * @return true if ok. */ virtual bool deleteFrameV2(const Frame& frame); /** * Get a list of frame IDs which can be added. * * @return list with frame IDs. */ virtual QStringList getFrameIds() const = 0; /** * Get all frames in tag 1. * * @param frames frame collection to set. */ virtual void getAllFramesV1(FrameCollection& frames); /** * Get all frames in tag 2. * This generic implementation only supports the standard tags and should * be reimplemented in derived classes. * * @param frames frame collection to set. */ virtual void getAllFramesV2(FrameCollection& frames); /** * Close any file handles which are held open by the tagged file object. * The default implementation does nothing. If a concrete subclass holds * any file handles open, it has to close them in this method. This method * can be used before operations which require that a file is not open, * e.g. file renaming on Windows. */ virtual void closeFileHandle(); /** * Set frames in tag 1. * * @param frames frame collection * @param onlyChanged only frames with value marked as changed are set */ void setFramesV1(const FrameCollection& frames, bool onlyChanged = true); /** * Set frames in tag 2. * * @param frames frame collection * @param onlyChanged only frames with value marked as changed are set */ void setFramesV2(const FrameCollection& frames, bool onlyChanged = true); /** * Get tags from filename. * Supported formats: * album/track - artist - song * artist - album/track song * /artist - album - track - song * album/artist - track - song * artist/album/track song * album/artist - song * * @param frames frames to put result * @param fmt format string containing the following codes: * %s title (song) * %l album * %a artist * %c comment * %y year * %t track */ void getTagsFromFilename(FrameCollection& frames, const QString& fmt); /** * Check if file is changed. * * @return true if file was changed. */ bool isChanged() const { return m_changedV1 || m_changedV2 || m_newFilename != m_filename; } /** * Check if filename is changed. * * @return true if filename was changed. */ bool isFilenameChanged() const { return m_newFilename != m_filename; } /** * Get absolute filename. * * @return absolute file path. */ QString getAbsFilename() const; /** * Check if tag 2 was changed. * @return true if tag 2 was changed. */ bool isTag2Changed() const { return m_changedV2; } /** * Mark tag 2 as changed. * * @param type type of changed frame */ void markTag2Changed(Frame::Type type); /** * Mark tag 2 as unchanged. */ void markTag2Unchanged() { m_changedV2 = false; m_changedFramesV2 = 0; } /** * Get the mask of the frame types changed in tag 1. * @return mask of frame types. */ quint64 getChangedFramesV1() const { return m_changedFramesV1; } /** * Get the mask of the frame types changed in tag 2. * @return mask of frame types. */ quint64 getChangedFramesV2() const { return m_changedFramesV2; } /** * Set the mask of the frame types changed in tag 2. * @param mask mask of frame types */ void setChangedFramesV2(quint64 mask) { m_changedFramesV2 = mask; m_changedV2 = mask != 0; } /** * Get the truncation flags. * @return truncation flags. */ quint64 getTruncationFlags() const { return m_truncation; } /** * Format track number/total number of tracks with configured digits. * * @param num track number, <= 0 if empty * @param numTracks total number of tracks, <= 0 to disable * * @return formatted "track/total" string. */ QString trackNumberString(int num, int numTracks) const; /** * Format the track number (digits, total number of tracks) if enabled. * * @param value string containing track number, will be modified * @param addTotal true to add total number of tracks if enabled * "/t" with t = total number of tracks will be appended * if enabled and value contains a number */ void formatTrackNumberIfEnabled(QString& value, bool addTotal) const; /** * Get the total number of tracks in the directory. * * @return total number of tracks, -1 if unavailable. */ int getTotalNumberOfTracksInDir() const; /** * Get index of tagged file in model. * @return index */ const QPersistentModelIndex& getIndex() const { return m_index; } /** * Format a time string "h:mm:ss". * If the time is less than an hour, the hour is not put into the * string and the minute is not padded with zeroes. * * @param seconds time in seconds * * @return string with the time in hours, minutes and seconds. */ static QString formatTime(unsigned seconds); /** * Free static resources. */ static void staticCleanup(); protected: /** * Rename a file. * This methods takes care of case insensitive filesystems. * * @param fnOld old filename * @param fnNew new filename * * @return true if ok. */ bool renameFile(const QString& fnOld, const QString& fnNew) const; /** * Get field name for comment from configuration. * * @return field name. */ QString getCommentFieldName() const; /** * Split a track string into number and total. * * @param str track * @param total the total is returned here if found, else 0 * * @return number, 0 if parsing failed, -1 if str is null */ static int splitNumberAndTotal(const QString& str, int* total=0); /** * Get the total number of tracks if it is enabled. * * @return total number of tracks, * -1 if disabled or unavailable. */ int getTotalNumberOfTracksIfEnabled() const; /** * Get the number of track number digits configured. * * @return track number digits, * 1 if invalid or unavailable. */ int getTrackNumberDigits() const; /** * Get current filename. * @return existing name. */ const QString& currentFilename() const { return m_filename; } /** * Set current filename to new filename. */ void updateCurrentFilename() { m_filename = m_newFilename; } /** * Check if tag 1 was changed. * @return true if tag 1 was changed. */ bool isTag1Changed() const { return m_changedV1; } /** * Mark tag 1 as changed. * * @param type type of changed frame */ void markTag1Changed(Frame::Type type); /** * Mark tag 1 as unchanged. */ void markTag1Unchanged() { m_changedV1 = false; m_changedFramesV1 = 0; clearTrunctionFlags(); } /** * Check if a string has to be truncated. * * @param str string to be checked * @param flag flag to be set if string has to be truncated * @param len maximum length of string * * @return str truncated to len characters if necessary, else QString::null. */ QString checkTruncation(const QString& str, quint64 flag, int len = 30); /** * Check if a number has to be truncated. * * @param val value to be checked * @param flag flag to be set if number has to be truncated * @param max maximum value * * @return val truncated to max if necessary, else -1. */ int checkTruncation(int val, quint64 flag, int max = 255); /** * Clear all truncation flags. */ void clearTrunctionFlags() { m_truncation = 0; } private: TaggedFile(const TaggedFile&); TaggedFile& operator=(const TaggedFile&); /** Directory name */ QString m_dirname; /** File name */ QString m_filename; /** New file name */ QString m_newFilename; /** Index of file in model */ QPersistentModelIndex m_index; /** true if ID3v1 tags were changed */ bool m_changedV1; /** changed tag 1 frame types */ quint64 m_changedFramesV1; /** true if ID3v2 tags were changed */ bool m_changedV2; /** changed tag 2 frame types */ quint64 m_changedFramesV2; /** Truncation flags. */ quint64 m_truncation; }; #endif // TAGGEDFILE_H kid3-3.0.2/src/core/tags/trackdata.cpp000066400000000000000000000440731224603511300174720ustar00rootroot00000000000000/** * \file trackdata.cpp * Track data, frames with association to tagged file. * * \b Project: Kid3 * \author Urs Fleisch * \date 23 Feb 2007 * * Copyright (C) 2007-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "trackdata.h" #include #include #include #include #include "fileproxymodel.h" /** * Constructor. * * @param trackData track data * @param str string with format codes */ TrackDataFormatReplacer::TrackDataFormatReplacer( const TrackData& trackData, const QString& str) : FrameFormatReplacer(trackData, str), m_trackData(trackData) {} /** * Destructor. */ TrackDataFormatReplacer::~TrackDataFormatReplacer() {} /** * Replace a format code (one character %c or multiple characters %{chars}). * Supported format fields: * Those supported by FrameFormatReplacer::getReplacement() * %f filename * %p path to file * %u URL of file * %d duration in minutes:seconds * %D duration in seconds * %n number of tracks * * @param code format code * * @return replacement string, * QString::null if code not found. */ QString TrackDataFormatReplacer::getReplacement(const QString& code) const { QString result = FrameFormatReplacer::getReplacement(code); if (result.isNull()) { QString name; if (code.length() == 1) { static const struct { char shortCode; const char* longCode; } shortToLong[] = { { 'f', "file" }, { 'p', "filepath" }, { 'u', "url" }, { 'd', "duration" }, { 'D', "seconds" }, { 'n', "tracks" }, { 'e', "extension" }, { 'O', "tag1" }, { 'o', "tag2" }, { 'b', "bitrate" }, { 'v', "vbr" }, { 'r', "samplerate" }, { 'm', "mode" }, { 'C', "channels" }, { 'k', "codec" } }; const char c = code[0].toLatin1(); for (unsigned i = 0; i < sizeof(shortToLong) / sizeof(shortToLong[0]); ++i) { if (shortToLong[i].shortCode == c) { name = QString::fromLatin1(shortToLong[i].longCode); break; } } } else if (code.length() > 1) { name = code; } if (!name.isNull()) { TaggedFile::DetailInfo info; m_trackData.getDetailInfo(info); if (name == QLatin1String("file")) { QString filename(m_trackData.getAbsFilename()); int sepPos = filename.lastIndexOf(QLatin1Char('/')); if (sepPos < 0) { sepPos = filename.lastIndexOf(QDir::separator()); } if (sepPos >= 0) { filename.remove(0, sepPos + 1); } result = filename; } else if (name == QLatin1String("filepath")) { result = m_trackData.getAbsFilename(); } else if (name == QLatin1String("url")) { QUrl url; url.setPath(m_trackData.getAbsFilename()); url.setScheme(QLatin1String("file")); result = url.toString(); } else if (name == QLatin1String("duration")) { result = TaggedFile::formatTime(m_trackData.getFileDuration()); } else if (name == QLatin1String("seconds")) { result = QString::number(m_trackData.getFileDuration()); } else if (name == QLatin1String("tracks")) { result = QString::number(m_trackData.getTotalNumberOfTracksInDir()); } else if (name == QLatin1String("extension")) { result = m_trackData.getFileExtension(); } else if (name == QLatin1String("tag1")) { result = m_trackData.getTagFormatV1(); } else if (name == QLatin1String("tag2")) { result = m_trackData.getTagFormatV2(); } else if (name == QLatin1String("bitrate")) { result.setNum(info.bitrate); } else if (name == QLatin1String("vbr")) { result = info.vbr ? QLatin1String("VBR") : QLatin1String(""); } else if (name == QLatin1String("samplerate")) { result.setNum(info.sampleRate); } else if (name == QLatin1String("mode")) { switch (info.channelMode) { case TaggedFile::DetailInfo::CM_Stereo: result = QLatin1String("Stereo"); break; case TaggedFile::DetailInfo::CM_JointStereo: result = QLatin1String("Joint Stereo"); break; case TaggedFile::DetailInfo::CM_None: default: result = QLatin1String(""); } } else if (name == QLatin1String("channels")) { result.setNum(info.channels); } else if (name == QLatin1String("codec")) { result = info.format; } } } return result; } /** * Get help text for supported format codes. * * @param onlyRows if true only the tr elements are returned, * not the surrounding table * * @return help text. */ QString TrackDataFormatReplacer::getToolTip(bool onlyRows) { QString str; if (!onlyRows) str += QLatin1String("\n"); str += FrameFormatReplacer::getToolTip(true); str += QLatin1String("\n"); str += QLatin1String("\n"); str += QLatin1String("\n"); str += QLatin1String("\n"); str += QLatin1String("\n"); str += QLatin1String("\n"); str += QLatin1String("\n"); str += QLatin1String("\n"); str += QLatin1String("\n"); str += QLatin1String("\n"); str += QLatin1String("\n"); str += QLatin1String("\n"); str += QLatin1String("\n"); str += QLatin1String("\n"); str += QLatin1String("\n"); str += QLatin1String("\n"); if (!onlyRows) str += QLatin1String("
%f%{file}"); str += QCoreApplication::translate("@default", "Filename"); str += QLatin1String("
%p%{filepath}"); str += QCoreApplication::translate("@default", QT_TRANSLATE_NOOP("@default", "Absolute path to file")); str += QLatin1String("
%u%{url}"); str += QCoreApplication::translate("@default", "URL"); str += QLatin1String("
%d%{duration}"); str += QCoreApplication::translate("@default", QT_TRANSLATE_NOOP("@default", "Length")); str += QLatin1String(" "M:S"
%D%{seconds}"); str += QCoreApplication::translate("@default", QT_TRANSLATE_NOOP("@default", "Length")); str += QLatin1String(" "S"
%n%{tracks}"); str += QCoreApplication::translate("@default", QT_TRANSLATE_NOOP("@default", "Number of tracks")); str += QLatin1String("
%e%{extension}"); str += QCoreApplication::translate("@default", QT_TRANSLATE_NOOP("@default", "Extension")); str += QLatin1String("
%O%{tag1}"); str += QCoreApplication::translate("@default", "Tag 1"); str += QLatin1String("
%o%{tag2}"); str += QCoreApplication::translate("@default", "Tag 2"); str += QLatin1String("
%b%{bitrate}"); str += QCoreApplication::translate("@default", QT_TRANSLATE_NOOP("@default", "Bitrate")); str += QLatin1String("
%v%{vbr}"); str += QCoreApplication::translate("@default", QT_TRANSLATE_NOOP("@default", "VBR")); str += QLatin1String("
%r%{samplerate}"); str += QCoreApplication::translate("@default", QT_TRANSLATE_NOOP("@default", "Samplerate")); str += QLatin1String("
%m%{mode}Stereo, Joint Stereo
%C%{channels}"); str += QCoreApplication::translate("@default", QT_TRANSLATE_NOOP("@default", "Channels")); str += QLatin1String("
%k%{codec}"); str += QCoreApplication::translate("@default", QT_TRANSLATE_NOOP("@default", "Codec")); str += QLatin1String("
%ha...%h{artist}..."); str += QCoreApplication::translate("@default", QT_TRANSLATE_NOOP("@default", "Escape for HTML")); str += QLatin1String("
\n"); return str; } /** * Constructor. */ TrackData::TrackData() {} /** * Constructor. * All fields except the import duration are set from the tagged file, * which should be read using readTags() before. * * @param taggedFile tagged file providing track data * @param tagVersion source of frames */ TrackData::TrackData(TaggedFile& taggedFile, TagVersion tagVersion) : m_taggedFileIndex(taggedFile.getIndex()) { switch (tagVersion) { case TagV1: taggedFile.getAllFramesV1(*this); break; case TagV2: taggedFile.getAllFramesV2(*this); break; case TagV2V1: { FrameCollection framesV1; taggedFile.getAllFramesV1(framesV1); taggedFile.getAllFramesV2(*this); merge(framesV1); break; } case TagNone: ; } } /** * Get tagged file associated with this track data. * @return tagged file, 0 if none assigned. */ TaggedFile* TrackData::getTaggedFile() const { return FileProxyModel::getTaggedFileOfIndex(m_taggedFileIndex); } /** * Get duration of file. * @return duration of file. */ int TrackData::getFileDuration() const { TaggedFile* taggedFile = getTaggedFile(); return taggedFile ? taggedFile->getDuration() : 0; } /** * Get absolute filename. * * @return absolute file path. */ QString TrackData::getAbsFilename() const { TaggedFile* taggedFile = getTaggedFile(); return taggedFile ? taggedFile->getAbsFilename() : QString(); } /** * Get filename. * * @return filename. */ QString TrackData::getFilename() const { TaggedFile* taggedFile = getTaggedFile(); return taggedFile ? taggedFile->getFilename() : QString(); } /** * Get the format of tag 1. * * @return string describing format of tag 1, * e.g. "ID3v1.1", "ID3v2.3", "Vorbis", "APE", * QString::null if unknown. */ QString TrackData::getTagFormatV1() const { TaggedFile* taggedFile = getTaggedFile(); return taggedFile ? taggedFile->getTagFormatV1() : QString(); } /** * Get the format of tag 2. * * @return string describing format of tag 2, * e.g. "ID3v2.3", "Vorbis", "APE", * QString::null if unknown. */ QString TrackData::getTagFormatV2() const { TaggedFile* taggedFile = getTaggedFile(); return taggedFile ? taggedFile->getTagFormatV2() : QString(); } /** * Get detail info. * @param info the detail information is returned here */ void TrackData::getDetailInfo(TaggedFile::DetailInfo& info) const { if (TaggedFile* taggedFile = getTaggedFile()) { taggedFile->getDetailInfo(info); } } /** * Format a string from track data. * Supported format fields: * Those supported by TrackDataFormatReplacer::getReplacement() * * @param format format specification * * @return formatted string. */ QString TrackData::formatString(const QString& format) const { TrackDataFormatReplacer fmt(*this, format); fmt.replaceEscapedChars(); fmt.replacePercentCodes(FormatReplacer::FSF_SupportHtmlEscape); return fmt.getString(); } /** * Create filename from tags according to format string. * * @param str format string containing codes supported by * TrackDataFormatReplacer::getReplacement() * @param isDirname true to generate a directory name * * @return format string with format codes replaced by tags. */ QString TrackData::formatFilenameFromTags(QString str, bool isDirname) const { if (!isDirname) { // first remove directory part from str const int sepPos = str.lastIndexOf(QLatin1Char('/')); if (sepPos >= 0) { str.remove(0, sepPos + 1); } // add extension to str str += getFileExtension(); } TrackDataFormatReplacer fmt(*this, str); fmt.replacePercentCodes(isDirname ? FormatReplacer::FSF_ReplaceSeparators : 0); return fmt.getString(); } /** * Get help text for format codes supported by formatString(). * * @param onlyRows if true only the tr elements are returned, * not the surrounding table * * @return help text. */ QString TrackData::getFormatToolTip(bool onlyRows) { return TrackDataFormatReplacer::getToolTip(onlyRows); } /** * Get file extension including the dot. * * @return file extension, e.g. ".mp3". */ QString TrackData::getFileExtension() const { QString fileExtension; QString absFilename; if (TaggedFile* taggedFile = getTaggedFile()) { fileExtension = taggedFile->getFileExtension(); absFilename = taggedFile->getAbsFilename(); } if (!fileExtension.isEmpty()) { return fileExtension; } else { int dotPos = absFilename.lastIndexOf(QLatin1Char('.')); return dotPos != -1 ? absFilename.mid(dotPos) : QString(); } } /** * Get the total number of tracks in the directory. * * @return total number of tracks, -1 if unavailable. */ int TrackData::getTotalNumberOfTracksInDir() const { TaggedFile* taggedFile = getTaggedFile(); return taggedFile ? taggedFile->getTotalNumberOfTracksInDir() : -1; } /** * Get the difference between the imported duration and the track's duration. * @return absolute value of time difference in seconds, -1 if not available. */ int ImportTrackData::getTimeDifference() const { int fileDuration = getFileDuration(); int importDuration = getImportDuration(); return fileDuration != 0 && importDuration != 0 ? fileDuration > importDuration ? fileDuration - importDuration : importDuration - fileDuration : -1; } namespace { /** * Get lower case words found in string. * @return lower case words. */ QSet getLowerCaseWords(const QString& str) { if (!str.isEmpty()) { QString normalized = str.normalized(QString::NormalizationForm_D).toLower(); QString simplified; for (QString::const_iterator it = normalized.constBegin(); it != normalized.constEnd(); ++it) { if (it->isLetter()) { simplified += *it; } else if (it->isPunct() || it->isSpace() || it->isSymbol()) { simplified += QLatin1Char(' '); } } return simplified.split(QLatin1Char(' '), QString::SkipEmptyParts).toSet(); } return QSet(); } } /** * Get words of file name. * @return lower case words found in file name. */ QSet ImportTrackData::getFilenameWords() const { QString fileName = getFilename(); int endIndex = fileName.lastIndexOf(QLatin1Char('.')); if (endIndex > 0) { fileName.truncate(endIndex); } return getLowerCaseWords(fileName); } /** * Get words of title. * @return lower case words found in title. */ QSet ImportTrackData::getTitleWords() const { return getLowerCaseWords(getTitle()); } /** * Clear vector and associated data. */ void ImportTrackDataVector::clearData() { clear(); m_coverArtUrl = QString(); } /** * Get album artist. * @return album artist. */ QString ImportTrackDataVector::getArtist() const { return getFrame(Frame::FT_Artist); } /** * Get album title. * @return album title. */ QString ImportTrackDataVector::getAlbum() const { return getFrame(Frame::FT_Album); } /** * Check if tag 1 is supported in the first track. * @return true if tag 1 is supported. */ bool ImportTrackDataVector::isTagV1Supported() const { if (!isEmpty()) { TaggedFile* taggedFile = at(0).getTaggedFile(); if (taggedFile) { return taggedFile->isTagV1Supported(); } } return true; } /** * Get frame from first track. * @param type frame type * @return value of frame. */ QString ImportTrackDataVector::getFrame(Frame::Type type) const { QString result; if (!isEmpty()) { const ImportTrackData& trackData = at(0); result = trackData.getValue(type); if (!result.isEmpty()) return result; TaggedFile* taggedFile = trackData.getTaggedFile(); FrameCollection frames; taggedFile->getAllFramesV2(frames); result = frames.getValue(type); if (!result.isEmpty()) return result; taggedFile->getAllFramesV1(frames); result = frames.getValue(type); } return result; } /** * Read the tags from the files. * This can be used to fill the track data with another tag version. * * @param tagVersion tag version to read */ void ImportTrackDataVector::readTags(ImportTrackData::TagVersion tagVersion) { for (iterator it = begin(); it != end(); ++it) { if (TaggedFile* taggedFile = it->getTaggedFile()) { switch (tagVersion) { case ImportTrackData::TagV1: taggedFile->getAllFramesV1(*it); break; case ImportTrackData::TagV2: taggedFile->getAllFramesV2(*it); break; case ImportTrackData::TagV2V1: { FrameCollection framesV1; taggedFile->getAllFramesV1(framesV1); taggedFile->getAllFramesV2(*it); it->merge(framesV1); break; } case ImportTrackData::TagNone: ; } } it->setImportDuration(0); it->setEnabled(true); } setCoverArtUrl(QString()); } #ifndef QT_NO_DEBUG /** * Dump contents of tracks to debug console. */ void ImportTrackDataVector::dump() const { qDebug("ImportTrackDataVector (%s - %s, %s):", qPrintable(getArtist()), qPrintable(getAlbum()), qPrintable(getCoverArtUrl())); for (const_iterator it = constBegin(); it != constEnd(); ++it) { const ImportTrackData& trackData = *it; int fileDuration = trackData.getFileDuration(); int importDuration = trackData.getImportDuration(); qDebug("%d:%02d, %d:%02d, %s, %d, %s, %s, %s, %d, %s", fileDuration / 60, fileDuration % 60, importDuration / 60, importDuration % 60, qPrintable(trackData.getFilename()), trackData.getTrack(), qPrintable(trackData.getTitle()), qPrintable(trackData.getArtist()), qPrintable(trackData.getAlbum()), trackData.getYear(), qPrintable(trackData.getGenre())); } } #endif kid3-3.0.2/src/core/tags/trackdata.h000066400000000000000000000215551224603511300171370ustar00rootroot00000000000000/** * \file trackdata.h * Track data, frames with association to tagged file. * * \b Project: Kid3 * \author Urs Fleisch * \date 7 Jul 2005 * * Copyright (C) 2005-2011 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef TRACKDATA_H #define TRACKDATA_H #include #include #include #include "frame.h" #include "taggedfile.h" #include "kid3api.h" /** * Track data, frames with association to tagged file. */ class KID3_CORE_EXPORT TrackData : public FrameCollection { public: /** Tag version contained in track data. */ enum TagVersion { TagNone = 0, /**< Empty or imported and not from a tag */ TagV1 = 1, /**< Tag 1 */ TagV2 = 2, /**< Tag 2 */ /** Tag 1 and 2 or merged from tag 2 and tag 1 (where tag 2 is not set) */ TagV2V1 = TagV1 | TagV2 }; /** * Constructor. */ TrackData(); /** * Constructor. * All fields are set from the tagged file, * which should be read using readTags() before. * * @param taggedFile tagged file providing track data * @param tagVersion source of frames */ TrackData(TaggedFile& taggedFile, TagVersion tagVersion); /** * Get duration of file. * @return duration of file. */ int getFileDuration() const; /** * Get absolute filename. * * @return absolute file path. */ QString getAbsFilename() const; /** * Get filename. * * @return filename. */ QString getFilename() const; /** * Get file extension including the dot. * * @return file extension, e.g. ".mp3". */ QString getFileExtension() const; /** * Get the total number of tracks in the directory. * * @return total number of tracks, -1 if unavailable. */ int getTotalNumberOfTracksInDir() const; /** * Get the format of tag 1. * * @return string describing format of tag 1, * e.g. "ID3v1.1", "ID3v2.3", "Vorbis", "APE", * QString::null if unknown. */ QString getTagFormatV1() const; /** * Get the format of tag 2. * * @return string describing format of tag 2, * e.g. "ID3v2.3", "Vorbis", "APE", * QString::null if unknown. */ QString getTagFormatV2() const; /** * Get detail info. * @param info the detail information is returned here */ void getDetailInfo(TaggedFile::DetailInfo& info) const; /** * Format a string from track data. * Supported format fields: * Those supported by TrackDataFormatReplacer::getReplacement() * * @param format format specification * * @return formatted string. */ QString formatString(const QString& format) const; /** * Create filename from tags according to format string. * * @param str format string containing codes supported by * TrackDataFormatReplacer::getReplacement() * @param isDirname true to generate a directory name * * @return format string with format codes replaced by tags. */ QString formatFilenameFromTags(QString str, bool isDirname = false) const; /** * Get frames. * @return frames. */ FrameCollection& getFrameCollection() { return *(static_cast(this)); } /** * Set frames. * @param frames frames */ void setFrameCollection(const FrameCollection& frames) { *(static_cast(this)) = frames; } /** * Get tagged file associated with this track data. * @return tagged file, 0 if none assigned. */ TaggedFile* getTaggedFile() const; /** * Get help text for format codes supported by formatString(). * * @param onlyRows if true only the tr elements are returned, * not the surrounding table * * @return help text. */ static QString getFormatToolTip(bool onlyRows = false); /** * Cast a mask of tag version bits to a TagVersion enum. * @param tagMask tag mask (bit 0 for tag 1, bit 1 for tag 2) * @return tag version enum value. */ static TagVersion tagVersionCast(int tagMask) { return static_cast(tagMask & 3); } private: QPersistentModelIndex m_taggedFileIndex; }; /** * Track data used for import. */ class ImportTrackData : public TrackData { public: /** * Constructor. */ ImportTrackData() : m_importDuration(0), m_enabled(true) {} /** * Constructor. * All fields except the import duration are set from the tagged file, * which should be read using readTags() before. * * @param taggedFile tagged file providing track data * @param tagVersion source of frames */ ImportTrackData(TaggedFile& taggedFile, TagVersion tagVersion) : TrackData(taggedFile, tagVersion), m_importDuration(0), m_enabled(true) {} /** * Get duration of import. * @return duration of import. */ int getImportDuration() const { return m_importDuration; } /** * Set duration of import. * @param duration duration of import */ void setImportDuration(int duration) { m_importDuration = duration; } /** * Check if track is enabled. * @return true if enabled (true is default). */ bool isEnabled() const { return m_enabled; } /** * Enable or disable track. * @param enabled true to enable */ void setEnabled(bool enabled) { m_enabled = enabled; } /** * Get the difference between the imported duration and the track's duration. * @return absolute value of time difference in seconds, -1 if not available. */ int getTimeDifference() const; /** * Get words of file name. * @return lower case words found in file name. */ QSet getFilenameWords() const; /** * Get words of title. * @return lower case words found in title. */ QSet getTitleWords() const; private: int m_importDuration; bool m_enabled; }; /** * Vector containing tracks to import and artist, album names. */ class KID3_CORE_EXPORT ImportTrackDataVector : public QVector { public: /** * Clear vector and associated data. */ void clearData(); /** * Get album artist. * @return album artist. */ QString getArtist() const; /** * Get album title. * @return album title. */ QString getAlbum() const; /** * Check if tag 1 is supported in the first track. * @return true if tag 1 is supported. */ bool isTagV1Supported() const; /** * Get cover art URL. * @return cover art URL. */ QString getCoverArtUrl() const { return m_coverArtUrl; } /** * Set cover art URL. * @param coverArtUrl cover art URL */ void setCoverArtUrl(const QString& coverArtUrl) { m_coverArtUrl = coverArtUrl; } /** * Read the tags from the files. * This can be used to fill the track data with another tag version. * * @param tagVersion tag version to read */ void readTags(ImportTrackData::TagVersion tagVersion); #ifndef QT_NO_DEBUG /** * Dump contents of tracks to debug console. */ void dump() const; #endif private: /** * Get frame from first track. * @param type frame type * @return value of frame. */ QString getFrame(Frame::Type type) const; QString m_coverArtUrl; }; /** * Replaces track data format codes in a string. */ class KID3_CORE_EXPORT TrackDataFormatReplacer : public FrameFormatReplacer { public: /** * Constructor. * * @param trackData track data * @param str string with format codes */ explicit TrackDataFormatReplacer( const TrackData& trackData, const QString& str = QString()); /** * Destructor. */ virtual ~TrackDataFormatReplacer(); /** * Get help text for supported format codes. * * @param onlyRows if true only the tr elements are returned, * not the surrounding table * * @return help text. */ static QString getToolTip(bool onlyRows = false); protected: /** * Replace a format code (one character %c or multiple characters %{chars}). * Supported format fields: * Those supported by FrameFormatReplacer::getReplacement() * %f filename * %p path to file * %u URL of file * %d duration in minutes:seconds * %D duration in seconds * %n number of tracks * * @param code format code * * @return replacement string, * QString::null if code not found. */ virtual QString getReplacement(const QString& code) const; private: const TrackData& m_trackData; }; #endif // TRACKDATA_H kid3-3.0.2/src/core/utils/000077500000000000000000000000001224603511300152225ustar00rootroot00000000000000kid3-3.0.2/src/core/utils/Sources.cmake000066400000000000000000000002551224603511300176510ustar00rootroot00000000000000set(utils_SRCS utils/debugutils.cpp utils/saferename.cpp utils/loadtranslation.cpp utils/icoreplatformtools.cpp utils/coreplatformtools.cpp ) set(utils_MOC_HDRS) kid3-3.0.2/src/core/utils/coreplatformtools.cpp000066400000000000000000000174071224603511300215150ustar00rootroot00000000000000/** * \file coreplatformtools.cpp * Core platform specific tools for Qt. * * \b Project: Kid3 * \author Urs Fleisch * \date 10 Aug 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "coreplatformtools.h" #include #include #include #include "config.h" #include "kid3settings.h" /** * Constructor. */ CorePlatformTools::CorePlatformTools() : m_settings(0), m_config(0) { } /** * Destructor. */ CorePlatformTools::~CorePlatformTools() { delete m_config; } /** * Get application settings. * @return settings instance. */ ISettings* CorePlatformTools::applicationSettings() { if (!m_config) { m_settings = new QSettings( QSettings::UserScope, QLatin1String("Kid3"), QLatin1String("Kid3"), qApp); m_config = new Kid3Settings(m_settings); } return m_config; } #ifdef Q_OS_WIN32 #include #ifdef Q_CC_MSVC #include #else #include #include #include #endif bool CorePlatformTools::moveToTrash(const QString& path) const { typedef int (WINAPI *SHFileOperationW_t)(LPSHFILEOPSTRUCTW); HMODULE hshell32 = GetModuleHandleA("shell32.dll"); SHFileOperationW_t pSHFileOperationW = reinterpret_cast( GetProcAddress(hshell32, "SHFileOperationW")); if (!pSHFileOperationW) { // SHFileOperationW is only available since Windows XP. return false; } QFileInfo fi(path); const QString absPath(fi.absoluteFilePath()); QVector from(absPath.length() + 2); int i; for (i = 0; i < absPath.length(); i++) { from[i] = absPath.at(i).unicode(); } from[i++] = 0; from[i++] = 0; SHFILEOPSTRUCTW fileOp; fileOp.hwnd = 0; fileOp.wFunc = FO_DELETE; fileOp.pFrom = from.data(); fileOp.pTo = 0; fileOp.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT; fileOp.fAnyOperationsAborted = 0; fileOp.hNameMappings = 0; fileOp.lpszProgressTitle = 0; return pSHFileOperationW(&fileOp) == 0; } #elif defined Q_OS_MAC #include bool CorePlatformTools::moveToTrash(const QString& path) const { QFileInfo fi(path); const QString absPath(fi.absoluteFilePath()); FSRef fsRef; OSErr err = FSPathMakeRefWithOptions( reinterpret_cast( QFile::encodeName(absPath).constData()), kFSPathMakeRefDoNotFollowLeafSymlink, &fsRef, 0); if (err != noErr) return false; return FSMoveObjectToTrashSync(&fsRef, 0, kFSFileOperationDefaultOptions) == noErr; } #else /* * Implemented according to Desktop Trash Can Specification at * http://www.freedesktop.org/wiki/Specifications/trash-spec */ #include #include #include #include #include #include #include #include #ifdef HAVE_MNTENT_H #include #endif namespace { bool moveToTrashDir(const QFileInfo& fi, const QString& trashDir) { QString absPath(fi.absoluteFilePath()); QString fileName(fi.fileName()); QString filesPath(trashDir + QLatin1String("/files")); QString infoPath(trashDir + QLatin1String("/info")); QString baseName(fi.baseName()); QString suffix(fi.completeSuffix()); QString destName(fileName); int counter = 1; while (QFile::exists(filesPath + QLatin1Char('/') + destName) || QFile::exists(infoPath + QLatin1Char('/') + destName + QLatin1String(".trashinfo"))) { ++counter; destName = QString(QLatin1String("%1.%2.%3")).arg(baseName).arg(counter).arg(suffix); } if (!(QDir(filesPath).exists() || QDir().mkpath(filesPath)) || !(QDir(infoPath).exists() || QDir().mkpath(infoPath))) return false; QFile file(infoPath + QLatin1Char('/') + destName + QLatin1String(".trashinfo")); if (!file.open(QIODevice::WriteOnly)) return false; QTextStream stream(&file); stream << QString(QLatin1String("[Trash Info]\nPath=%1\nDeletionDate=%2\n")). arg(absPath). arg(QDateTime::currentDateTime().toString(Qt::ISODate)); file.close(); return QDir().rename(absPath, filesPath + QLatin1Char('/') + destName); } bool findMountPoint(dev_t dev, QString& mountPoint) { #ifdef HAVE_MNTENT_H if (FILE* fp = ::setmntent("/proc/mounts", "r")) { struct stat st; struct mntent* mnt; while ((mnt = ::getmntent(fp)) != 0) { if (::stat(mnt->mnt_dir, &st) != 0) { continue; } if (st.st_dev == dev) { ::endmntent(fp); mountPoint = QString::fromLatin1(mnt->mnt_dir); return true; } } ::endmntent(fp); } #endif return false; } bool findExtVolumeTrash(const QString& volumeRoot, QString& trashDir) { struct stat st; trashDir = volumeRoot + QLatin1String("/.Trash"); uid_t uid = ::getuid(); if (QDir(trashDir).exists() && ::lstat(trashDir.toLocal8Bit().data(), &st) == 0 && (S_ISDIR(st.st_mode) && !S_ISLNK(st.st_mode) && (st.st_mode & S_ISVTX))) { trashDir += QString(QLatin1String("/%1")).arg(uid); } else { trashDir += QString(QLatin1String("-%1")).arg(uid); } if (QDir(trashDir).exists() || QDir().mkpath(trashDir)) { return true; } return false; } } // anonymous namespace bool CorePlatformTools::moveToTrash(const QString& path) const { QFileInfo fi(path); const QString absPath(fi.absoluteFilePath()); if (!fi.exists() || !fi.isWritable()) return false; struct stat pathStat; struct stat trashStat; if (::lstat(QFile::encodeName(absPath).constData(), &pathStat) != 0 || ::lstat(QFile::encodeName(QDir::homePath()).constData(), &trashStat) != 0) return false; QString topDir; QString trashDir; if (pathStat.st_dev == trashStat.st_dev) { QByteArray xdhEnv = qgetenv("XDG_DATA_HOME"); topDir = !xdhEnv.isEmpty() ? QString::fromLatin1(xdhEnv) : QDir::homePath() + QLatin1String("/.local/share"); trashDir = topDir + QLatin1String("/Trash"); } else if (!(findMountPoint(pathStat.st_dev, topDir) && findExtVolumeTrash(topDir, trashDir))) { return false; } return moveToTrashDir(fi, trashDir); } #endif /** * Construct a name filter string suitable for file dialogs. * @param nameFilters list of description, filter pairs, e.g. * [("Images", "*.jpg *.jpeg *.png"), ("All Files", "*")]. * @return name filter string. */ QString CorePlatformTools::fileDialogNameFilter( const QList >& nameFilters) const { QString filter; for (QList >::const_iterator it = nameFilters.constBegin(); it != nameFilters.constEnd(); ++it) { if (!filter.isEmpty()) { filter += QLatin1String(";;"); } filter += it->first; filter += QLatin1String(" ("); filter += it->second; filter += QLatin1Char(')'); } return filter; } /** * Get file pattern part of m_nameFilter. * @param nameFilter name filter string * @return file patterns, e.g. "*.mp3". */ QString CorePlatformTools::getNameFilterPatterns(const QString& nameFilter) const { int start = nameFilter.indexOf(QLatin1Char('(')), end = nameFilter.indexOf(QLatin1Char(')')); return start != -1 && end != -1 && end > start ? nameFilter.mid(start + 1, end - start - 1) : QString(); } kid3-3.0.2/src/core/utils/coreplatformtools.h000066400000000000000000000041451224603511300211550ustar00rootroot00000000000000/** * \file coreplatformtools.h * Core platform specific tools for Qt. * * \b Project: Kid3 * \author Urs Fleisch * \date 10 Aug 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef COREPLATFORMTOOLS_H #define COREPLATFORMTOOLS_H #include "icoreplatformtools.h" class QSettings; /** * Core platform specific tools for Qt. */ class KID3_CORE_EXPORT CorePlatformTools : public ICorePlatformTools { public: /** * Constructor. */ CorePlatformTools(); /** * Destructor. */ virtual ~CorePlatformTools(); /** * Get application settings. * @return settings instance. */ virtual ISettings* applicationSettings(); /** * Move file or directory to trash. * * @param path path to file or directory * * @return true if ok. */ virtual bool moveToTrash(const QString& path) const; /** * Construct a name filter string suitable for file dialogs. * @param nameFilters list of description, filter pairs, e.g. * [("Images", "*.jpg *.jpeg *.png"), ("All Files", "*")]. * @return name filter string. */ virtual QString fileDialogNameFilter( const QList >& nameFilters) const; /** * Get file pattern part of m_nameFilter. * @param nameFilter name filter string * @return file patterns, e.g. "*.mp3". */ virtual QString getNameFilterPatterns(const QString& nameFilter) const; private: QSettings* m_settings; ISettings* m_config; }; #endif // COREPLATFORMTOOLS_H kid3-3.0.2/src/core/utils/debugutils.cpp000066400000000000000000000054101224603511300200750ustar00rootroot00000000000000/** * \file debugutils.cpp * Utility functions for debugging. * * \b Project: Kid3 * \author Urs Fleisch * \date 13 Jan 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef QT_NO_DEBUG #include "debugutils.h" /** * Dump an item model. * @param model item model to dump * @param parent parent model index * @param indent number of spaces to indent */ void DebugUtils::dumpModel(const QAbstractItemModel& model, const QModelIndex& parent, int indent) { if (indent == 0) { QString name(model.objectName()); if (name.isEmpty()) { if (const QMetaObject* metaObject = model.metaObject()) { name = QString::fromLatin1(metaObject->className()); } } qDebug("Dump for %s", qPrintable(name)); QString columnStr; for (int i = 0; i < model.columnCount(parent); ++i) { if (i != 0) columnStr += QLatin1String(", "); columnStr += QString::number(i); columnStr += QLatin1String(": "); columnStr += model.headerData(i, Qt::Horizontal).toString(); } qDebug("%s", qPrintable(columnStr)); } if (!model.hasChildren(parent)) return; for (int row = 0; row < model.rowCount(parent); ++row) { QString rowStr(indent, QLatin1Char(' ')); QString rowHeader(model.headerData(row, Qt::Vertical).toString()); rowStr += QString::number(row); if (!rowHeader.isEmpty()) { rowStr += QLatin1Char(' '); rowStr += rowHeader; } rowStr += QLatin1Char(':'); QModelIndexList indexesWithChildren; for (int column = 0; column < model.columnCount(parent); ++column) { QModelIndex idx(model.index(row, column, parent)); if (column > 0) rowStr += QLatin1String(","); rowStr += QString(QLatin1String("%1%2:")). arg(model.hasChildren(idx) ? QLatin1String("p") : QLatin1String("")). arg(column); rowStr += model.data(idx).toString(); if (model.hasChildren(idx)) indexesWithChildren.append(idx); } qDebug("%s", qPrintable(rowStr)); foreach (const QModelIndex& idx, indexesWithChildren) { dumpModel(model, idx, indent + 2); } } } #endif kid3-3.0.2/src/core/utils/debugutils.h000066400000000000000000000024211224603511300175410ustar00rootroot00000000000000/** * \file debugutils.h * Utility functions for debugging. * * \b Project: Kid3 * \author Urs Fleisch * \date 13 Jan 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef QT_NO_DEBUG #ifndef DEBUGUTILS_H #define DEBUGUTILS_H #include class QAbstractItemModel; namespace DebugUtils { /** * Dump an item model. * @param model item model to dump * @param parent parent model index * @param indent number of spaces to indent */ void dumpModel(const QAbstractItemModel& model, const QModelIndex& parent = QModelIndex(), int indent = 0); } #endif // DEBUGUTILS_H #endif // QT_NO_DEBUG kid3-3.0.2/src/core/utils/icoreplatformtools.cpp000066400000000000000000000017141224603511300216600ustar00rootroot00000000000000/** * \file icoreplatformtools.cpp * Interface for GUI independent platform specific tools. * * \b Project: Kid3 * \author Urs Fleisch * \date 06 Apr 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "icoreplatformtools.h" /** * Destructor. */ ICorePlatformTools::~ICorePlatformTools() { } kid3-3.0.2/src/core/utils/icoreplatformtools.h000066400000000000000000000040751224603511300213300ustar00rootroot00000000000000/** * \file icoreplatformtools.h * Interface for GUI independent platform specific tools. * * \b Project: Kid3 * \author Urs Fleisch * \date 06 Apr 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef ICOREPLATFORMTOOLS_H #define ICOREPLATFORMTOOLS_H #include #include #include "kid3api.h" class QString; class ISettings; /** * Interface for GUI independent platform specific tools. */ class KID3_CORE_EXPORT ICorePlatformTools { public: /** * Destructor. */ virtual ~ICorePlatformTools() = 0; /** * Get application settings. * @return settings instance. */ virtual ISettings* applicationSettings() = 0; /** * Move file or directory to trash. * * @param path path to file or directory * * @return true if ok. */ virtual bool moveToTrash(const QString& path) const = 0; /** * Construct a name filter string suitable for file dialogs. * @param nameFilters list of description, filter pairs, e.g. * [("Images", "*.jpg *.jpeg *.png"), ("All Files", "*")]. * @return name filter string. */ virtual QString fileDialogNameFilter( const QList >& nameFilters) const = 0; /** * Get file pattern part of m_nameFilter. * @param nameFilter name filter string * @return file patterns, e.g. "*.mp3". */ virtual QString getNameFilterPatterns(const QString& nameFilter) const = 0; }; #endif // ICOREPLATFORMTOOLS_H kid3-3.0.2/src/core/utils/kid3api.h000066400000000000000000000026001224603511300167150ustar00rootroot00000000000000/** * \file kid3api.h * Macros for import and export of shared library symbols. * * \b Project: Kid3 * \author Urs Fleisch * \date 26 Feb 2012 * * Copyright (C) 2012 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KID3API_H #define KID3API_H #ifdef KID3_SHARED #include #ifdef KID3_BUILD_CORE_LIB #define KID3_CORE_EXPORT Q_DECL_EXPORT #else #define KID3_CORE_EXPORT Q_DECL_IMPORT #endif #ifdef KID3_BUILD_GUI_LIB #define KID3_GUI_EXPORT Q_DECL_EXPORT #else #define KID3_GUI_EXPORT Q_DECL_IMPORT #endif #ifdef KID3_BUILD_PLUGIN_LIB #define KID3_PLUGIN_EXPORT Q_DECL_EXPORT #else #define KID3_PLUGIN_EXPORT Q_DECL_IMPORT #endif #else #define KID3_CORE_EXPORT #define KID3_GUI_EXPORT #define KID3_PLUGIN_EXPORT #endif #endif // KID3API_H kid3-3.0.2/src/core/utils/loadtranslation.cpp000066400000000000000000000067551224603511300211410ustar00rootroot00000000000000/** * \file loadtranslation.cpp * Load application translation. * * \b Project: Kid3 * \author Urs Fleisch * \date 26 Mar 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "loadtranslation.h" #include #include #include #include #include #include #include "config.h" /** * @brief Load application translation. * * @param lang preferred language, if not set, the language is determined by * the system configuration */ void Utils::loadTranslation(const QString& lang) { QLocale locale; QStringList languages( #if QT_VERSION >= 0x040800 && !defined Q_OS_WIN32 locale.uiLanguages() #else locale.name() #endif ); if (!lang.isEmpty()) { languages.prepend(lang); } QString translationsDir; #ifdef CFG_TRANSLATIONSDIR translationsDir = QLatin1String(CFG_TRANSLATIONSDIR); prependApplicationDirPathIfRelative(translationsDir); #endif // '-' is added to default delimiters because it is used on Mac OS X instead // of '_'. const QString searchDelimiters(QLatin1String("_.-")); // translation file for Qt QTranslator* qtTr = new QTranslator(qApp); foreach (const QString& localeName, languages) { if ( localeName.startsWith(QLatin1String("en")) || #if defined Q_OS_WIN32 || defined Q_OS_MAC (!translationsDir.isNull() && qtTr->load(QLatin1String("qt_") + localeName, translationsDir, searchDelimiters)) || qtTr->load(QLatin1String("qt_") + localeName, QLatin1String("."), searchDelimiters) #else qtTr->load(QLatin1String("qt_") + localeName, QLibraryInfo::location(QLibraryInfo::TranslationsPath), searchDelimiters) #endif ) { break; } } qApp->installTranslator(qtTr); // translation file for application strings QTranslator* kid3Tr = new QTranslator(qApp); foreach (const QString& localeName, languages) { if ( localeName.startsWith(QLatin1String("en")) || (!translationsDir.isNull() && kid3Tr->load(QLatin1String("kid3_") + localeName, translationsDir, searchDelimiters)) || kid3Tr->load(QLatin1String("kid3_") + localeName, QLatin1String("."), searchDelimiters) ) { break; } } qApp->installTranslator(kid3Tr); } /** * Prepend the application directory path to a path if it is relative. * * @param path file or directory path, will be modified if relative */ void Utils::prependApplicationDirPathIfRelative(QString& path) { if (QFileInfo(path).isRelative()) { QString appDir = QCoreApplication::applicationDirPath(); if (!appDir.isEmpty()) { if (!appDir.endsWith(QLatin1Char('/'))) { appDir.append(QLatin1Char('/')); } path.prepend(appDir); } } } kid3-3.0.2/src/core/utils/loadtranslation.h000066400000000000000000000026361224603511300206000ustar00rootroot00000000000000/** * \file loadtranslation.h * Load application translation. * * \b Project: Kid3 * \author Urs Fleisch * \date 26 Mar 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef LOADTRANSLATION_H #define LOADTRANSLATION_H #include #include "kid3api.h" namespace Utils { /** * @brief Load application translation. * * @param lang preferred language, if not set, the language is determined by * the system configuration */ void KID3_CORE_EXPORT loadTranslation(const QString& lang = QString()); /** * Prepend the application directory path to a path if it is relative. * * @param path file or directory path, will be modified if relative */ void KID3_CORE_EXPORT prependApplicationDirPathIfRelative(QString& path); } #endif // LOADTRANSLATION_H kid3-3.0.2/src/core/utils/qtcompatmac.h000066400000000000000000000022731224603511300177100ustar00rootroot00000000000000/** * \file qtcompatmac.h * Qt compatibility macros. * * \b Project: Kid3 * \author Urs Fleisch * \date 31 Oct 2006 * * Copyright (C) 2006-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef QTCOMPATMAC_H #define QTCOMPATMAC_H #include #if QT_VERSION >= 0x040600 /** Get icon from theme. */ #define QCM_QIcon_fromTheme(n) QIcon::fromTheme(QLatin1String(n), QIcon(QLatin1String(":/images/" n ".png"))) #else /** Get icon from theme. */ #define QCM_QIcon_fromTheme(n) QIcon(QLatin1String(":/images/" n ".png")) #endif #endif // QTCOMPATMAC_H kid3-3.0.2/src/core/utils/saferename.cpp000066400000000000000000000034641224603511300200430ustar00rootroot00000000000000/** * \file saferename.cpp * Safely rename a file. * * \b Project: Kid3 * \author Urs Fleisch * \date 16 Feb 2012 * * Copyright (C) 2012 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "saferename.h" #include #ifdef Q_OS_WIN32 bool Utils::hasIllegalFileNameCharacters(const QString& fileName) { static const char illegalChars[] = "<>:\"|?*"; QString fileNameWithoutDrive( (QDir::isAbsolutePath(fileName) && fileName.midRef(1, 2) == QLatin1String(":/")) ? fileName.mid(3) : fileName); for (const char* chPtr = illegalChars; *chPtr; ++chPtr) { if (fileNameWithoutDrive.contains(QLatin1Char(*chPtr))) { return true; } } return false; } #else bool Utils::hasIllegalFileNameCharacters(const QString&) { return false; } #endif bool Utils::safeRename(const QString& oldName, const QString& newName) { if (hasIllegalFileNameCharacters(newName)) return false; return QDir().rename(oldName, newName); } bool Utils::safeRename(const QString& dirPath, const QString& oldName, const QString& newName) { if (hasIllegalFileNameCharacters(newName)) return false; return QDir(dirPath).rename(oldName, newName); } kid3-3.0.2/src/core/utils/saferename.h000066400000000000000000000034651224603511300175110ustar00rootroot00000000000000/** * \file saferename.h * Safely rename a file. * * \b Project: Kid3 * \author Urs Fleisch * \date 16 Feb 2012 * * Copyright (C) 2012 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef SAFERENAME_H #define SAFERENAME_H #include "kid3api.h" class QString; namespace Utils { /** * Check if file name has illegal characters. * * @param fileName file name * * @return true if file name contains illegal characters. */ bool KID3_CORE_EXPORT hasIllegalFileNameCharacters(const QString& fileName); /** * Rename a file. * Renames the file using QDir::rename() if @a newName does not contain * illegal characters. * * @param oldName old file name * @param newName new file name * * @return true if ok. */ bool KID3_CORE_EXPORT safeRename(const QString& oldName, const QString& newName); /** * Rename a file. * Renames the file using QDir::rename() if @a newName does not contain * illegal characters. * * @param dirPath directory path * @param oldName old file name * @param newName new file name * * @return true if ok. */ bool KID3_CORE_EXPORT safeRename(const QString& dirPath, const QString& oldName, const QString& newName); } #endif // SAFERENAME_H kid3-3.0.2/src/gui/000077500000000000000000000000001224603511300137165ustar00rootroot00000000000000kid3-3.0.2/src/gui/CMakeLists.txt000066400000000000000000000017001224603511300164540ustar00rootroot00000000000000include_directories( ../core/utils ../core/config ../core/tags ../core/export ../core/import ../core/model widgets dialogs forms ) set(gui_SRCS) set(gui_MOC_HDRS) foreach (_subdir widgets dialogs forms) include(${_subdir}/Sources.cmake) set(gui_SRCS ${gui_SRCS} ${${_subdir}_SRCS}) set(gui_MOC_HDRS ${gui_MOC_HDRS} ${${_subdir}_MOC_HDRS}) endforeach() add_definitions(-DKID3_BUILD_GUI_LIB) if (NOT BUILD_SHARED_LIBS) add_definitions(${QT_EXECUTABLE_COMPILE_FLAGS}) endif (NOT BUILD_SHARED_LIBS) if (WITH_GCC_PCH) add_definitions(${GCC_PCH_COMPILE_FLAGS}) endif (WITH_GCC_PCH) qt4_wrap_cpp(gui_GEN_MOC_SRCS ${gui_MOC_HDRS}) add_library(kid3-gui ${gui_SRCS} ${gui_GEN_MOC_SRCS}) set_target_properties(kid3-gui PROPERTIES VERSION ${KID3_VERSION}) target_link_libraries(kid3-gui kid3-core) if (BUILD_SHARED_LIBS) install(TARGETS kid3-gui LIBRARY DESTINATION ${WITH_LIBDIR} RUNTIME DESTINATION ${WITH_BINDIR}) endif (BUILD_SHARED_LIBS) kid3-3.0.2/src/gui/dialogs/000077500000000000000000000000001224603511300153405ustar00rootroot00000000000000kid3-3.0.2/src/gui/dialogs/Sources.cmake000066400000000000000000000021171224603511300177660ustar00rootroot00000000000000set(dialogs_SRCS dialogs/batchimportdialog.cpp dialogs/batchimportsourcedialog.cpp dialogs/browsecoverartdialog.cpp dialogs/configdialogpages.cpp dialogs/contexthelp.cpp dialogs/downloaddialog.cpp dialogs/editframedialog.cpp dialogs/editframefieldsdialog.cpp dialogs/exportdialog.cpp dialogs/filterdialog.cpp dialogs/importdialog.cpp dialogs/servertrackimportdialog.cpp dialogs/numbertracksdialog.cpp dialogs/playlistdialog.cpp dialogs/rendirdialog.cpp dialogs/serverimportdialog.cpp dialogs/tagimportdialog.cpp dialogs/textimportdialog.cpp ) set(dialogs_MOC_HDRS dialogs/batchimportdialog.h dialogs/batchimportsourcedialog.h dialogs/browsecoverartdialog.h dialogs/configdialogpages.h dialogs/downloaddialog.h dialogs/editframedialog.h dialogs/editframefieldsdialog.h dialogs/exportdialog.h dialogs/filterdialog.h dialogs/importdialog.h dialogs/servertrackimportdialog.h dialogs/numbertracksdialog.h dialogs/playlistdialog.h dialogs/rendirdialog.h dialogs/serverimportdialog.h dialogs/tagimportdialog.h dialogs/textimportdialog.h ) kid3-3.0.2/src/gui/dialogs/batchimportdialog.cpp000066400000000000000000000351161224603511300215460ustar00rootroot00000000000000/** * \file batchimportdialog.cpp * Batch import dialog. * * \b Project: Kid3 * \author Urs Fleisch * \date 2 Jan 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "batchimportdialog.h" #include #include #include #include #include #include #include #include #include #include "batchimportsourcedialog.h" #include "batchimportconfig.h" #include "batchimportsourcesmodel.h" #include "serverimporter.h" #include "contexthelp.h" #include "abstractlistedit.h" /** * Widget to edit a list of import sources. */ class BatchImportSourceListEdit : public AbstractListEdit { public: /** * Constructor. * * @param model item model * @param parent parent widget */ explicit BatchImportSourceListEdit(BatchImportSourcesModel* model, QWidget* parent = 0); /** * Destructor. */ virtual ~BatchImportSourceListEdit(); /** * Set names of import servers. * @param servers server names */ void setServerNames(const QStringList& servers) { m_serverNames = servers; } public slots: /** * Add a new item. */ virtual void addItem(); /** * Edit the selected item. */ virtual void editItem(); private: QTableView* m_tableView; QStringList m_serverNames; }; /** * Constructor. * * @param model item model, e.g. a QStringListModel * @param parent parent widget */ BatchImportSourceListEdit::BatchImportSourceListEdit( BatchImportSourcesModel* model, QWidget* parent) : AbstractListEdit(m_tableView = new QTableView, model, parent) { setObjectName(QLatin1String("BatchImportSourceListEdit")); m_tableView->setSelectionBehavior(QAbstractItemView::SelectRows); #if QT_VERSION >= 0x050000 m_tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); #else m_tableView->horizontalHeader()->setResizeMode(QHeaderView::ResizeToContents); #endif } /** * Destructor. */ BatchImportSourceListEdit::~BatchImportSourceListEdit() { } /** * Add a new item. */ void BatchImportSourceListEdit::addItem() { BatchImportSourceDialog* dialog = new BatchImportSourceDialog(this); dialog->setServerNames(m_serverNames); if (dialog->exec() == QDialog::Accepted) { BatchImportProfile::Source source; dialog->getSource(source); if (BatchImportSourcesModel* model = qobject_cast(getItemView()->model())) { int row = model->rowCount(); model->insertRow(row); model->setBatchImportSource(row, source); } } } /** * Edit the selected item. */ void BatchImportSourceListEdit::editItem() { QModelIndex index = getItemView()->currentIndex(); if (index.isValid()) { if (BatchImportSourcesModel* model = qobject_cast(getItemView()->model())) { BatchImportProfile::Source source; model->getBatchImportSource(index.row(), source); BatchImportSourceDialog* dialog = new BatchImportSourceDialog(this); dialog->setServerNames(m_serverNames); dialog->setSource(source); if (dialog->exec() == QDialog::Accepted) { dialog->getSource(source); model->setBatchImportSource(index.row(), source); } } } } /** * Constructor. * * @param parent parent widget */ BatchImportDialog::BatchImportDialog(const QList& importers, QWidget* parent) : QDialog(parent), m_importers(importers), m_profileIdx(-1), m_isAbortButton(false) { setObjectName(QLatin1String("BatchImportDialog")); setWindowTitle(tr("Automatic Import")); setSizeGripEnabled(true); QVBoxLayout* vlayout = new QVBoxLayout(this); QSplitter* splitter = new QSplitter(Qt::Vertical); m_edit = new QTextEdit(this); m_edit->setReadOnly(true); m_edit->setAcceptRichText(false); splitter->addWidget(m_edit); QWidget* profileWidget = new QWidget; QVBoxLayout* profileLayout = new QVBoxLayout(profileWidget); profileLayout->setContentsMargins(0, 0, 0, 0); QHBoxLayout* destLayout = new QHBoxLayout; QLabel* destLabel = new QLabel(tr("D&estination:")); destLayout->addWidget(destLabel); m_destComboBox = new QComboBox; m_destComboBox->setEditable(false); m_destComboBox->addItem(tr("Tag 1"), TrackData::TagV1); m_destComboBox->addItem(tr("Tag 2"), TrackData::TagV2); m_destComboBox->addItem(tr("Tag 1 and Tag 2"), TrackData::TagV2V1); destLabel->setBuddy(m_destComboBox); destLayout->addWidget(m_destComboBox); destLayout->addStretch(); profileLayout->addLayout(destLayout); QHBoxLayout* nameLayout = new QHBoxLayout; QLabel* profileLabel = new QLabel(tr("&Profile:")); nameLayout->addWidget(profileLabel); m_profileComboBox = new QComboBox; m_profileComboBox->setEditable(true); connect(m_profileComboBox, SIGNAL(activated(int)), this, SLOT(changeProfile(int))); connect(m_profileComboBox, SIGNAL(editTextChanged(QString)), this, SLOT(changeProfileName(QString))); profileLabel->setBuddy(m_profileComboBox); nameLayout->addWidget(m_profileComboBox, 1); QPushButton* profileAddButton = new QPushButton(tr("Add")); connect(profileAddButton, SIGNAL(clicked()), this, SLOT(addProfile())); nameLayout->addWidget(profileAddButton); QPushButton* profileRemoveButton = new QPushButton(tr("Remove")); connect(profileRemoveButton, SIGNAL(clicked()), this, SLOT(removeProfile())); nameLayout->addWidget(profileRemoveButton); profileLayout->addLayout(nameLayout); QStringList servers; foreach (const ServerImporter* si, m_importers) { servers.append(QString::fromLatin1(si->name())); } m_profileModel = new BatchImportSourcesModel(this); BatchImportSourceListEdit* profileListEdit = new BatchImportSourceListEdit(m_profileModel, this); profileListEdit->setServerNames(servers); profileLayout->addWidget(profileListEdit); splitter->addWidget(profileWidget); vlayout->addWidget(splitter); QHBoxLayout* hlayout = new QHBoxLayout; QPushButton* helpButton = new QPushButton(tr("&Help"), this); helpButton->setAutoDefault(false); hlayout->addWidget(helpButton); connect(helpButton, SIGNAL(clicked()), this, SLOT(showHelp())); QPushButton* saveButton = new QPushButton(tr("&Save Settings"), this); saveButton->setAutoDefault(false); hlayout->addWidget(saveButton); connect(saveButton, SIGNAL(clicked()), this, SLOT(saveConfig())); hlayout->addStretch(); m_startAbortButton = new QPushButton(this); setAbortButton(false); QPushButton* closeButton = new QPushButton(tr("&Close"), this); m_startAbortButton->setAutoDefault(false); closeButton->setAutoDefault(false); hlayout->addWidget(m_startAbortButton); hlayout->addWidget(closeButton); connect(m_startAbortButton, SIGNAL(clicked()), this, SLOT(startOrAbortImport())); connect(closeButton, SIGNAL(clicked()), this, SLOT(reject())); connect(this, SIGNAL(rejected()), this, SIGNAL(abort())); vlayout->addLayout(hlayout); } /** * Destructor. */ BatchImportDialog::~BatchImportDialog() {} /** * Start or abort batch import. */ void BatchImportDialog::startOrAbortImport() { if (m_isAbortButton) { emit abort(); } else { setProfileFromGuiControls(); if (m_profileIdx >= 0 && m_profileIdx < m_profiles.size()) { m_edit->clear(); m_currentProfile = m_profiles.at(m_profileIdx); emit start( m_currentProfile, TrackData::tagVersionCast( m_destComboBox->itemData(m_destComboBox->currentIndex()).toInt())); } } } /** * Add a new profile to the list of profiles. */ void BatchImportDialog::addNewProfile() { BatchImportProfile profile; profile.setName(tr("New")); m_profiles.append(profile); m_profileIdx = m_profiles.size() - 1; } /** * Add a new profile. */ void BatchImportDialog::addProfile() { setProfileFromGuiControls(); // First search for an existing empty profile. for (int index = 0; index < m_profiles.size(); ++index) { if (m_profiles.at(index).getSources().isEmpty()) { m_profileIdx = index; setGuiControlsFromProfile(); return; } } addNewProfile(); setGuiControlsFromProfile(); } /** * Remove the selected profile. */ void BatchImportDialog::removeProfile() { int index = m_profileComboBox->currentIndex(); if (index >= 0 && index < m_profiles.size()) { m_profiles.removeAt(index); if (m_profileIdx >= m_profiles.size()) m_profileIdx = m_profiles.size() - 1; setGuiControlsFromProfile(); } } /** * Switch to different profile. * @param index combo box index to set */ void BatchImportDialog::changeProfile(int index) { setProfileFromGuiControls(); m_profileIdx = index; setGuiControlsFromProfile(); } /** * Change name of current profile. * @param name profile name */ void BatchImportDialog::changeProfileName(const QString& name) { int index = m_profileComboBox->currentIndex(); if (index >= 0 && index < m_profiles.size()) { m_profiles[index].setName(name); m_profileComboBox->setItemText(index, name); } } /** * Update profile from GUI controls. */ void BatchImportDialog::setProfileFromGuiControls() { QList sources = m_profileModel->getBatchImportSources(); if (m_profiles.isEmpty() && !sources.isEmpty()) { addNewProfile(); m_profileComboBox->setEditText(m_profiles.at(0).getName()); } if (m_profileIdx >= 0 && m_profileIdx < m_profiles.size()) { BatchImportProfile& profile = m_profiles[m_profileIdx]; profile.setSources(sources); } } /** * Update GUI controls from profiles. */ void BatchImportDialog::setGuiControlsFromProfile() { if (m_profiles.isEmpty()) { m_profileIdx = -1; m_profileComboBox->clear(); m_profileModel->setBatchImportSources(QList()); return; } if (m_profileIdx < 0 || m_profileIdx >= m_profiles.size()) m_profileIdx = 0; m_profileModel->setBatchImportSources(m_profiles.at(m_profileIdx).getSources()); if (m_profileComboBox->count() == m_profiles.size()) { m_profileComboBox->setItemText(m_profileIdx, m_profiles.at(m_profileIdx).getName()); } else { m_profileComboBox->clear(); foreach (const BatchImportProfile& profile, m_profiles) { m_profileComboBox->addItem(profile.getName()); } } m_profileComboBox->setCurrentIndex(m_profileIdx); } /** * Set the filter combo box and line edit from the configuration. */ void BatchImportDialog::setProfileFromConfig() { const QStringList& names = BatchImportConfig::instance().m_profileNames; const QStringList& sources = BatchImportConfig::instance().m_profileSources; m_profiles.clear(); QStringList::const_iterator namesIt, sourcesIt; for (namesIt = names.constBegin(), sourcesIt = sources.constBegin(); namesIt != names.constEnd() && sourcesIt != sources.constEnd(); ++namesIt, ++sourcesIt) { BatchImportProfile profile; profile.setName(*namesIt); profile.setSourcesFromString(*sourcesIt); m_profiles.append(profile); } m_profileIdx = BatchImportConfig::instance().m_profileIdx; setGuiControlsFromProfile(); } /** * Read the local settings from the configuration. */ void BatchImportDialog::readConfig() { m_edit->clear(); setAbortButton(false); TrackData::TagVersion importDest = BatchImportConfig::instance().m_importDest; int index = m_destComboBox->findData(importDest); m_destComboBox->setCurrentIndex(index); setProfileFromConfig(); if (!BatchImportConfig::instance().m_windowGeometry.isEmpty()) { restoreGeometry(BatchImportConfig::instance().m_windowGeometry); } } /** * Save the local settings to the configuration. */ void BatchImportDialog::saveConfig() { BatchImportConfig::instance().m_importDest = TrackData::tagVersionCast( m_destComboBox->itemData(m_destComboBox->currentIndex()).toInt()); QStringList& names = BatchImportConfig::instance().m_profileNames; QStringList& sources = BatchImportConfig::instance().m_profileSources; names.clear(); sources.clear(); setProfileFromGuiControls(); foreach (const BatchImportProfile& profile, m_profiles) { names.append(profile.getName()); sources.append(profile.getSourcesAsString()); } BatchImportConfig::instance().m_profileIdx = m_profileComboBox->currentIndex(); BatchImportConfig::instance().m_windowGeometry = saveGeometry(); } /** * Show help. */ void BatchImportDialog::showHelp() { ContextHelp::displayHelp(QLatin1String("batch-import")); } /** * Show information about import event. * @param type import event type * @param text text to display */ void BatchImportDialog::showImportEvent(BatchImportProfile::ImportEventType type, const QString& text) { QString eventText; switch (type) { case BatchImportProfile::ReadingDirectory: setAbortButton(true); eventText = tr("Reading Directory"); break; case BatchImportProfile::Started: setAbortButton(true); eventText = tr("Started"); break; case BatchImportProfile::SourceSelected: eventText = tr("Source"); break; case BatchImportProfile::QueryingAlbumList: eventText = tr("Querying"); break; case BatchImportProfile::FetchingTrackList: case BatchImportProfile::FetchingCoverArt: eventText = tr("Fetching"); break; case BatchImportProfile::TrackListReceived: eventText = tr("Data received"); break; case BatchImportProfile::CoverArtReceived: eventText = tr("Cover"); break; case BatchImportProfile::Finished: setAbortButton(false); eventText = tr("Finished"); break; case BatchImportProfile::Aborted: setAbortButton(false); eventText = tr("Aborted"); break; case BatchImportProfile::Error: eventText = tr("Error"); } if (!text.isEmpty()) { eventText += QLatin1String(": "); eventText += text; } m_edit->append(eventText); } /** * Set button to Start or Abort. * @param enableAbort true to set Abort button */ void BatchImportDialog::setAbortButton(bool enableAbort) { m_isAbortButton = enableAbort; m_startAbortButton->setText(m_isAbortButton ? tr("A&bort") : tr("S&tart")); } kid3-3.0.2/src/gui/dialogs/batchimportdialog.h000066400000000000000000000075531224603511300212170ustar00rootroot00000000000000/** * \file batchimportdialog.h * Batch import dialog. * * \b Project: Kid3 * \author Urs Fleisch * \date 2 Jan 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef BATCHIMPORTDIALOG_H #define BATCHIMPORTDIALOG_H #include #include "batchimportprofile.h" #include "trackdata.h" class QPushButton; class QTextEdit; class QComboBox; class ServerImporter; class BatchImportSourcesModel; /** * Batch import dialog. */ class BatchImportDialog : public QDialog { Q_OBJECT public: /** * Constructor. * * @param importers server importers * @param parent parent widget */ explicit BatchImportDialog(const QList& importers, QWidget* parent = 0); /** * Destructor. */ virtual ~BatchImportDialog(); /** * Read the local settings from the configuration. */ void readConfig(); signals: /** * Start batch import with given @a profile. * @param profile batch import profile * @param tagVersion import destination tag versions */ void start(const BatchImportProfile& profile, TrackData::TagVersion tagVersion); /** * Abort batch import. */ void abort(); public slots: /** * Show information about import event. * @param type import event type * @param text text to display */ void showImportEvent(BatchImportProfile::ImportEventType type, const QString& text); private slots: /** * Save the local settings to the configuration. */ void saveConfig(); /** * Show help. */ void showHelp(); /** * Start or abort batch import. */ void startOrAbortImport(); /** * Add a new profile. */ void addProfile(); /** * Remove the selected profile. */ void removeProfile(); /** * Switch to different profile. * @param index combo box index to set */ void changeProfile(int index); /** * Change name of current profile. * @param name profile name */ void changeProfileName(const QString& name); private: /** * Set the profile from the configuration. */ void setProfileFromConfig(); /** * Update profile from GUI controls. */ void setProfileFromGuiControls(); /** * Update GUI controls from profiles. */ void setGuiControlsFromProfile(); /** * Add a new profile to the list of profiles. */ void addNewProfile(); /** * Set button to Start or Abort. * @param enableAbort true to set Abort button */ void setAbortButton(bool enableAbort); /** Text editor */ QTextEdit* m_edit; /** Combo box with import destinations */ QComboBox* m_destComboBox; /** Combo box with profile name */ QComboBox* m_profileComboBox; /** Start/Abort button */ QPushButton* m_startAbortButton; /** Model containing currently selected import sources */ BatchImportSourcesModel* m_profileModel; /** Importers for different servers */ QList m_importers; /** Batch import profiles */ QList m_profiles; /** Index of currently selected profile */ int m_profileIdx; /** true if m_startAbortButton is an Abort button */ bool m_isAbortButton; /** Currently used batch import profile */ BatchImportProfile m_currentProfile; }; #endif kid3-3.0.2/src/gui/dialogs/batchimportsourcedialog.cpp000066400000000000000000000105231224603511300227620ustar00rootroot00000000000000/** * \file batchimportdialog.cpp * Dialog to add or edit a batch import source. * * \b Project: Kid3 * \author Urs Fleisch * \date 2 Jan 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "batchimportsourcedialog.h" #include #include #include #include #include #include #include /** * Constructor. * @param parent parent widget */ BatchImportSourceDialog::BatchImportSourceDialog(QWidget* parent) : QDialog(parent) { setObjectName(QLatin1String("BatchImportSourceDialog")); setWindowTitle(tr("Import Source")); setSizeGripEnabled(true); QVBoxLayout* vlayout = new QVBoxLayout(this); QHBoxLayout* serverLayout = new QHBoxLayout; QLabel* serverLabel = new QLabel(tr("&Server:")); serverLayout->addWidget(serverLabel); m_serverComboBox = new QComboBox; serverLabel->setBuddy(m_serverComboBox); serverLayout->addWidget(m_serverComboBox); vlayout->addLayout(serverLayout); QHBoxLayout* accuracyLayout = new QHBoxLayout; QLabel* accuracyLabel = new QLabel(tr("&Accuracy:")); accuracyLayout->addWidget(accuracyLabel); m_accuracySpinBox = new QSpinBox; m_accuracySpinBox->setRange(0, 100); m_accuracySpinBox->setValue(75); accuracyLabel->setBuddy(m_accuracySpinBox); accuracyLayout->addWidget(m_accuracySpinBox); vlayout->addLayout(accuracyLayout); QHBoxLayout* tagsCoverLayout = new QHBoxLayout; m_standardTagsCheckBox = new QCheckBox(tr("&Standard Tags")); m_standardTagsCheckBox->setChecked(true); m_additionalTagsCheckBox = new QCheckBox(tr("&Additional Tags")); m_additionalTagsCheckBox->setChecked(true); m_coverArtCheckBox = new QCheckBox(tr("C&over Art")); m_coverArtCheckBox->setChecked(true); tagsCoverLayout->addWidget(m_standardTagsCheckBox); tagsCoverLayout->addWidget(m_additionalTagsCheckBox); tagsCoverLayout->addWidget(m_coverArtCheckBox); vlayout->addLayout(tagsCoverLayout); QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); vlayout->addWidget(buttonBox); } /** * Destructor. */ BatchImportSourceDialog::~BatchImportSourceDialog() { } /** * Set names of import servers. * @param servers server names */ void BatchImportSourceDialog::setServerNames(const QStringList& servers) { if (m_serverComboBox) { m_serverComboBox->clear(); m_serverComboBox->addItems(servers); m_serverComboBox->setCurrentIndex(servers.size() - 1); } } /** * Fill batch import source from dialog controls. * @param source batch import source to be filled */ void BatchImportSourceDialog::getSource(BatchImportProfile::Source& source) { source.setName(m_serverComboBox->currentText()); source.setRequiredAccuracy(m_accuracySpinBox->value()); source.enableStandardTags(m_standardTagsCheckBox->isChecked()); source.enableAdditionalTags(m_additionalTagsCheckBox->isChecked()); source.enableCoverArt(m_coverArtCheckBox->isChecked()); } /** * Set dialog controls from batch import source. * @param source batch import source containing properties to be set */ void BatchImportSourceDialog::setSource(const BatchImportProfile::Source& source) { int index = m_serverComboBox->findText(source.getName()); if (index != -1) { m_serverComboBox->setCurrentIndex(index); } m_accuracySpinBox->setValue(source.getRequiredAccuracy()); m_standardTagsCheckBox->setChecked(source.standardTagsEnabled()); m_additionalTagsCheckBox->setChecked(source.additionalTagsEnabled()); m_coverArtCheckBox->setChecked(source.coverArtEnabled()); } kid3-3.0.2/src/gui/dialogs/batchimportsourcedialog.h000066400000000000000000000040121224603511300224230ustar00rootroot00000000000000/** * \file batchimportdialog.h * Dialog to add or edit a batch import source. * * \b Project: Kid3 * \author Urs Fleisch * \date 2 Jan 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef BATCHIMPORTSOURCEDIALOG_H #define BATCHIMPORTSOURCEDIALOG_H #include #include "batchimportprofile.h" class QComboBox; class QSpinBox; class QCheckBox; /** * Dialog to add or edit a batch import source. */ class BatchImportSourceDialog : public QDialog { Q_OBJECT public: /** * Constructor. * @param parent parent widget */ explicit BatchImportSourceDialog(QWidget* parent = 0); /** * Destructor. */ virtual ~BatchImportSourceDialog(); /** * Fill batch import source from dialog controls. * @param source batch import source to be filled */ void getSource(BatchImportProfile::Source& source); /** * Set dialog controls from batch import source. * @param source batch import source containing properties to be set */ void setSource(const BatchImportProfile::Source& source); /** * Set names of import servers. * @param servers server names */ void setServerNames(const QStringList& servers); private: QComboBox* m_serverComboBox; QSpinBox* m_accuracySpinBox; QCheckBox* m_standardTagsCheckBox; QCheckBox* m_additionalTagsCheckBox; QCheckBox* m_coverArtCheckBox; }; #endif // BATCHIMPORTSOURCEDIALOG_H kid3-3.0.2/src/gui/dialogs/browsecoverartdialog.cpp000066400000000000000000000171011224603511300222730ustar00rootroot00000000000000/** * \file browsecoverartdialog.cpp * Browse cover art dialog. * * \b Project: Kid3 * \author Urs Fleisch * \date 11-Jan-2009 * * Copyright (C) 2009-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "browsecoverartdialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "networkconfig.h" #include "importconfig.h" #include "contexthelp.h" #include "externalprocess.h" #include "configtable.h" #include "configtablemodel.h" #include "formatlistedit.h" /** * Get help text for supported format codes. * * @return help text. */ static QString getToolTip() { QString str = QLatin1String("\n"); str += FrameFormatReplacer::getToolTip(true); str += QLatin1String("\n"); str += QLatin1String("
%ua...%u{artist}..."); str += QCoreApplication::translate("@default", QT_TRANSLATE_NOOP("@default", "Encode as URL")); str += QLatin1String("
\n"); return str; } /** * Constructor. * * @param parent parent widget */ BrowseCoverArtDialog::BrowseCoverArtDialog(QWidget* parent) : QDialog(parent), m_process(0) { setObjectName(QLatin1String("BrowseCoverArtDialog")); setModal(true); setWindowTitle(tr("Browse Cover Art")); setSizeGripEnabled(true); QVBoxLayout* vlayout = new QVBoxLayout(this); m_edit = new QTextEdit(this); m_edit->setReadOnly(true); vlayout->addWidget(m_edit); QGroupBox* artistAlbumBox = new QGroupBox(tr("&Artist/Album"), this); m_artistLineEdit = new QLineEdit(artistAlbumBox); m_albumLineEdit = new QLineEdit(artistAlbumBox); QHBoxLayout* hbox = new QHBoxLayout; hbox->addWidget(m_artistLineEdit); hbox->addWidget(m_albumLineEdit); artistAlbumBox->setLayout(hbox); vlayout->addWidget(artistAlbumBox); connect(m_artistLineEdit, SIGNAL(returnPressed()), this, SLOT(showPreview())); connect(m_albumLineEdit, SIGNAL(returnPressed()), this, SLOT(showPreview())); QGroupBox* srcbox = new QGroupBox(tr("&Source"), this); m_formatListEdit = new FormatListEdit( QStringList() << tr("Source:") << tr("URL:"), QStringList() << QString() << getToolTip(), srcbox); QVBoxLayout* vbox = new QVBoxLayout; vbox->addWidget(m_formatListEdit); srcbox->setLayout(vbox); vlayout->addWidget(srcbox); connect(m_formatListEdit, SIGNAL(formatChanged()), this, SLOT(showPreview())); QGroupBox* tabbox = new QGroupBox(tr("&URL extraction"), this); m_matchUrlTableModel = new ConfigTableModel(tabbox); m_matchUrlTableModel->setLabels( QStringList() << tr("Match") << tr("Picture URL")); m_matchUrlTable = new ConfigTable(m_matchUrlTableModel, tabbox); m_matchUrlTable->setHorizontalResizeModes( m_matchUrlTableModel->getHorizontalResizeModes()); QVBoxLayout* tablayout = new QVBoxLayout; tablayout->addWidget(m_matchUrlTable); tabbox->setLayout(tablayout); vlayout->addWidget(tabbox); QHBoxLayout* hlayout = new QHBoxLayout; QPushButton* helpButton = new QPushButton(tr("&Help"), this); helpButton->setAutoDefault(false); hlayout->addWidget(helpButton); connect(helpButton, SIGNAL(clicked()), this, SLOT(showHelp())); QPushButton* saveButton = new QPushButton(tr("&Save Settings"), this); saveButton->setAutoDefault(false); hlayout->addWidget(saveButton); connect(saveButton, SIGNAL(clicked()), this, SLOT(saveConfig())); QSpacerItem* hspacer = new QSpacerItem(16, 0, QSizePolicy::Expanding, QSizePolicy::Minimum); hlayout->addItem(hspacer); QPushButton* browseButton = new QPushButton(tr("&Browse"), this); QPushButton* cancelButton = new QPushButton(tr("&Cancel"), this); browseButton->setAutoDefault(false); cancelButton->setAutoDefault(false); hlayout->addWidget(browseButton); hlayout->addWidget(cancelButton); connect(browseButton, SIGNAL(clicked()), this, SLOT(accept())); connect(cancelButton, SIGNAL(clicked()), this, SLOT(reject())); vlayout->addLayout(hlayout); } /** * Destructor. */ BrowseCoverArtDialog::~BrowseCoverArtDialog() { delete m_process; } /** * Show browse command as preview. */ void BrowseCoverArtDialog::showPreview() { m_frames.setArtist(m_artistLineEdit->text()); m_frames.setAlbum(m_albumLineEdit->text()); FrameFormatReplacer fmt(m_frames, m_formatListEdit->getCurrentFormat(1)); fmt.replaceEscapedChars(); fmt.replacePercentCodes(FormatReplacer::FSF_SupportUrlEncode); m_url = fmt.getString(); QString txt(QLatin1String("

")); txt += tr("Click Browse to start"); txt += QLatin1String("

"); txt += NetworkConfig::instance().m_browser; txt += QLatin1Char(' '); txt += m_url; txt += QLatin1String("

"); txt += tr("Then drag the picture from the browser to Kid3."); txt += QLatin1String("

"); m_edit->clear(); m_edit->append(txt); } /** * Set frames for which picture has to be found. * * @param frames track data */ void BrowseCoverArtDialog::setFrames(const FrameCollection& frames) { m_frames = frames; m_artistLineEdit->setText(m_frames.getArtist()); m_albumLineEdit->setText(m_frames.getAlbum()); showPreview(); } /** * Set the source combo box and line edits from the configuration. */ void BrowseCoverArtDialog::setSourceFromConfig() { m_formatListEdit->setFormats( QList() << ImportConfig::instance().m_pictureSourceNames << ImportConfig::instance().m_pictureSourceUrls, ImportConfig::instance().m_pictureSourceIdx); } /** * Read the local settings from the configuration. */ void BrowseCoverArtDialog::readConfig() { setSourceFromConfig(); m_matchUrlTableModel->setMap(ImportConfig::instance().m_matchPictureUrlMap); if (!ImportConfig::instance().m_browseCoverArtWindowGeometry.isEmpty()) { restoreGeometry(ImportConfig::instance().m_browseCoverArtWindowGeometry); } } /** * Save the local settings to the configuration. */ void BrowseCoverArtDialog::saveConfig() { QList formats = m_formatListEdit->getFormats( &ImportConfig::instance().m_pictureSourceIdx); ImportConfig::instance().m_pictureSourceNames = formats.at(0); ImportConfig::instance().m_pictureSourceUrls = formats.at(1); ImportConfig::instance().m_matchPictureUrlMap = m_matchUrlTableModel->getMap(); ImportConfig::instance().m_browseCoverArtWindowGeometry = saveGeometry(); setSourceFromConfig(); } /** * Show help. */ void BrowseCoverArtDialog::showHelp() { ContextHelp::displayHelp(QLatin1String("browse_pictures")); } /** * Hide modal dialog, start browse command. */ void BrowseCoverArtDialog::accept() { if (!m_process) { m_process = new ExternalProcess(this); } m_process->launchCommand( tr("Browse Cover Art"), QStringList() << NetworkConfig::instance().m_browser << m_url); QDialog::accept(); } kid3-3.0.2/src/gui/dialogs/browsecoverartdialog.h000066400000000000000000000050341224603511300217420ustar00rootroot00000000000000/** * \file browsecoverartdialog.h * Browse cover art dialog. * * \b Project: Kid3 * \author Urs Fleisch * \date 11-Jan-2009 * * Copyright (C) 2009 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef BROWSECOVERARTDIALOG_H #define BROWSECOVERARTDIALOG_H #include #include "frame.h" class QTextEdit; class QLineEdit; class ExternalProcess; class ConfigTable; class ConfigTableModel; class FormatListEdit; /** * Browse cover art dialog. */ class BrowseCoverArtDialog : public QDialog { Q_OBJECT public: /** * Constructor. * * @param parent parent widget */ explicit BrowseCoverArtDialog(QWidget* parent); /** * Destructor. */ virtual ~BrowseCoverArtDialog(); /** * Read the local settings from the configuration. */ void readConfig(); /** * Set frames for which picture has to be found. * * @param frames track data */ void setFrames(const FrameCollection& frames); public slots: /** * Hide modal dialog, start browse command. */ virtual void accept(); private slots: /** * Show browse command as preview. */ void showPreview(); /** * Save the local settings to the configuration. */ void saveConfig(); /** * Show help. */ void showHelp(); private: /** * Set the source combo box and line edits from the configuration. */ void setSourceFromConfig(); /** Text editor with command preview */ QTextEdit* m_edit; /** Combobox with artist */ QLineEdit* m_artistLineEdit; /** Combobox with album */ QLineEdit* m_albumLineEdit; /** format editor */ FormatListEdit* m_formatListEdit; /** Table to extract picture URL */ ConfigTable* m_matchUrlTable; /** Table model to extract picture URL */ ConfigTableModel* m_matchUrlTableModel; /** Formatted URL */ QString m_url; /** Track data */ FrameCollection m_frames; /** Process for browser command */ ExternalProcess* m_process; }; #endif kid3-3.0.2/src/gui/dialogs/configdialogpages.cpp000066400000000000000000000610561224603511300215210ustar00rootroot00000000000000/** * \file configdialogpages.cpp * Pages for configuration dialog. * * \b Project: Kid3 * \author Urs Fleisch * \date 17 Sep 2003 * * Copyright (C) 2003-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "configdialogpages.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "formatconfig.h" #include "formatbox.h" #include "tagconfig.h" #include "fileconfig.h" #include "useractionsconfig.h" #include "guiconfig.h" #include "networkconfig.h" #include "importconfig.h" #include "stringlistedit.h" #include "configtable.h" #include "commandstablemodel.h" #include "checkablestringlistmodel.h" #include "contexthelp.h" #include "frame.h" enum { TextEncodingV1Latin1Index = 13 }; /** * Remove aliases in braces from text encoding combo box entry. * * @param comboEntry text encoding combo box entry * * @return codec name. */ static QString getTextEncodingV1CodecName(const QString& comboEntry) { int braceIdx = comboEntry.indexOf(QLatin1String(" (")); return braceIdx == -1 ? comboEntry : comboEntry.left(braceIdx); } /** * Constructor. */ ConfigDialogPages::ConfigDialogPages(QObject* parent) : QObject(parent), m_loadLastOpenedFileCheckBox(0), m_preserveTimeCheckBox(0), m_markChangesCheckBox(0), m_coverFileNameLineEdit(0), m_markTruncationsCheckBox(0), m_textEncodingV1ComboBox(0), m_totalNumTracksCheckBox(0), m_commentNameComboBox(0), m_pictureNameComboBox(0), m_genreNotNumericCheckBox(0), m_textEncodingComboBox(0), m_id3v2VersionComboBox(0), m_trackNumberDigitsSpinBox(0), m_fnFormatBox(0), m_id3FormatBox(0), m_onlyCustomGenresCheckBox(0), m_genresEditModel(0), m_quickAccessTagsModel(0), m_playOnDoubleClickCheckBox(0), m_commandsTable(0), m_commandsTableModel(0), m_browserLineEdit(0), m_proxyCheckBox(0), m_proxyLineEdit(0), m_proxyAuthenticationCheckBox(0), m_proxyUserNameLineEdit(0), m_proxyPasswordLineEdit(0), m_enabledMetadataPluginsModel(0), m_enabledPluginsModel(0) { } /** * Destructor. */ ConfigDialogPages::~ConfigDialogPages() { } /** * Create page with tags settings. * @return tags page. */ QWidget* ConfigDialogPages::createTagsPage() { QWidget* tagsPage = new QWidget; QVBoxLayout* vlayout = new QVBoxLayout(tagsPage); QWidget* tag1Page = new QWidget; QVBoxLayout* tag1Layout = new QVBoxLayout(tag1Page); QGroupBox* v1GroupBox = new QGroupBox(tr("ID3v1"), tag1Page); QGridLayout* v1GroupBoxLayout = new QGridLayout(v1GroupBox); m_markTruncationsCheckBox = new QCheckBox(tr("&Mark truncated fields"), v1GroupBox); v1GroupBoxLayout->addWidget(m_markTruncationsCheckBox, 0, 0, 1, 2); QLabel* textEncodingV1Label = new QLabel(tr("Text &encoding:"), v1GroupBox); m_textEncodingV1ComboBox = new QComboBox(v1GroupBox); static const char* const codecs[] = { "Apple Roman (macintosh)", "Big5", "big5-0", "Big5-HKSCS", "big5hkscs-0", "EUC-JP", "EUC-KR", "GB18030", "GBK (windows-936)", "hp-roman8", "IBM850", "IBM866", "ISO-2022-JP (JIS7)", "ISO-8859-1 (latin1)", "ISO-8859-2 (latin2)", "ISO-8859-3 (latin3)", "ISO-8859-4 (latin4)", "ISO-8859-5 (cyrillic)", "ISO-8859-6 (arabic)", "ISO-8859-7 (greek)", "ISO-8859-8 (hebrew)", "ISO-8859-9 (latin5)", "ISO-8859-10 (latin6)", "ISO-8859-13 (baltic)", "ISO-8859-14 (latin8, iso-celtic)", "ISO-8859-15 (latin9)", "ISO-8859-16 (latin10)", "ISO-10646-UCS-2 (UTF-16)", "Iscii-Bng", "Iscii-Dev", "Iscii-Gjr", "Iscii-Knd", "Iscii-Mlm", "Iscii-Ori", "Iscii-Pnj", "Iscii-Tlg", "Iscii-Tml", "jisx0201*-0", "KOI8-R", "KOI8-U", "ksc5601.1987-0", "mulelao-1", "Shift_JIS (SJIS, MS_Kanji)", "TIS-620 (ISO 8859-11)", "TSCII", "UTF-8", "windows-1250", "windows-1251", "windows-1252", "windows-1253", "windows-1254", "windows-1255", "windows-1256", "windows-1257", "windows-1258", "WINSAMI2 (WS2)", 0 }; Q_ASSERT(std::strcmp(codecs[TextEncodingV1Latin1Index], "ISO-8859-1 (latin1)") == 0); const char* const* str = codecs; m_textEncodingV1List.clear(); while (*str) { m_textEncodingV1List += QString::fromLatin1(*str++); } m_textEncodingV1ComboBox->addItems(m_textEncodingV1List); m_textEncodingV1ComboBox->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum)); textEncodingV1Label->setBuddy(m_textEncodingV1ComboBox); v1GroupBoxLayout->addWidget(textEncodingV1Label, 1, 0); v1GroupBoxLayout->addWidget(m_textEncodingV1ComboBox, 1, 1); tag1Layout->addWidget(v1GroupBox); tag1Layout->addStretch(); QWidget* tag2Page = new QWidget; QVBoxLayout* tag2Layout = new QVBoxLayout(tag2Page); QGroupBox* v2GroupBox = new QGroupBox(tr("ID3v2"), tag2Page); QGridLayout* v2GroupBoxLayout = new QGridLayout(v2GroupBox); m_totalNumTracksCheckBox = new QCheckBox(tr("Use &track/total number of tracks format"), v2GroupBox); v2GroupBoxLayout->addWidget(m_totalNumTracksCheckBox, 0, 0, 1, 2); QLabel* trackNumberDigitsLabel = new QLabel(tr("Track number &digits:"), v2GroupBox); m_trackNumberDigitsSpinBox = new QSpinBox(v2GroupBox); m_trackNumberDigitsSpinBox->setMaximum(5); m_genreNotNumericCheckBox = new QCheckBox(tr("&Genre as text instead of numeric string"), v2GroupBox); QLabel* textEncodingLabel = new QLabel(tr("Text &encoding:"), v2GroupBox); m_textEncodingComboBox = new QComboBox(v2GroupBox); m_textEncodingComboBox->insertItem(TagConfig::TE_ISO8859_1, tr("ISO-8859-1")); m_textEncodingComboBox->insertItem(TagConfig::TE_UTF16, tr("UTF16")); m_textEncodingComboBox->insertItem(TagConfig::TE_UTF8, tr("UTF8")); m_textEncodingComboBox->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum)); textEncodingLabel->setBuddy(m_textEncodingComboBox); v2GroupBoxLayout->addWidget(m_genreNotNumericCheckBox, 1, 0, 1, 2); v2GroupBoxLayout->addWidget(textEncodingLabel, 2, 0); v2GroupBoxLayout->addWidget(m_textEncodingComboBox, 2, 1); const TagConfig& tagCfg = TagConfig::instance(); if (!(tagCfg.taggedFileFeatures() & (TaggedFile::TF_ID3v22 | TaggedFile::TF_ID3v23 | TaggedFile::TF_ID3v24))) { m_genreNotNumericCheckBox->hide(); textEncodingLabel->hide(); m_textEncodingComboBox->hide(); } QLabel* id3v2VersionLabel = new QLabel(tr("&Version used for new tags:"), v2GroupBox); m_id3v2VersionComboBox = new QComboBox(v2GroupBox); if (tagCfg.taggedFileFeatures() & TaggedFile::TF_ID3v23) m_id3v2VersionComboBox->addItem(tr("ID3v2.3.0"), TagConfig::ID3v2_3_0); if (tagCfg.taggedFileFeatures() & TaggedFile::TF_ID3v24) m_id3v2VersionComboBox->addItem(tr("ID3v2.4.0"), TagConfig::ID3v2_4_0); m_id3v2VersionComboBox->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum)); id3v2VersionLabel->setBuddy(m_id3v2VersionComboBox); v2GroupBoxLayout->addWidget(id3v2VersionLabel, 3, 0); v2GroupBoxLayout->addWidget(m_id3v2VersionComboBox, 3, 1); if (m_id3v2VersionComboBox->count() < 2) { id3v2VersionLabel->hide(); m_id3v2VersionComboBox->hide(); } trackNumberDigitsLabel->setBuddy(m_trackNumberDigitsSpinBox); v2GroupBoxLayout->addWidget(trackNumberDigitsLabel, 4, 0); v2GroupBoxLayout->addWidget(m_trackNumberDigitsSpinBox, 4, 1); tag2Layout->addWidget(v2GroupBox); QGroupBox* vorbisGroupBox = new QGroupBox(tr("Ogg/Vorbis"), tag2Page); QLabel* commentNameLabel = new QLabel(tr("Co&mment field name:"), vorbisGroupBox); m_commentNameComboBox = new QComboBox(vorbisGroupBox); QLabel* pictureNameLabel = new QLabel(tr("&Picture field name:"), vorbisGroupBox); m_pictureNameComboBox = new QComboBox(vorbisGroupBox); m_commentNameComboBox->setEditable(true); QStringList items; items += QLatin1String("COMMENT"); items += QLatin1String("DESCRIPTION"); m_commentNameComboBox->addItems(items); m_commentNameComboBox->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum)); commentNameLabel->setBuddy(m_commentNameComboBox); m_pictureNameComboBox->addItems(QStringList() << QLatin1String("METADATA_BLOCK_PICTURE") << QLatin1String("COVERART")); m_pictureNameComboBox->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum)); pictureNameLabel->setBuddy(m_pictureNameComboBox); QGridLayout* vorbisGroupBoxLayout = new QGridLayout(vorbisGroupBox); vorbisGroupBoxLayout->addWidget(commentNameLabel, 0, 0); vorbisGroupBoxLayout->addWidget(m_commentNameComboBox, 0, 1); vorbisGroupBoxLayout->addWidget(pictureNameLabel, 1, 0); vorbisGroupBoxLayout->addWidget(m_pictureNameComboBox, 1, 1); vorbisGroupBox->setLayout(vorbisGroupBoxLayout); tag2Layout->addWidget(vorbisGroupBox); if (!(tagCfg.taggedFileFeatures() & TaggedFile::TF_OggPictures)) { vorbisGroupBox->hide(); } QHBoxLayout* genresQuickAccessLayout = new QHBoxLayout; QGroupBox* genresGroupBox = new QGroupBox(tr("Custom &Genres"), tag2Page); m_onlyCustomGenresCheckBox = new QCheckBox(tr("&Show only custom genres"), genresGroupBox); m_genresEditModel = new QStringListModel(genresGroupBox); StringListEdit* genresEdit = new StringListEdit(m_genresEditModel, genresGroupBox); QVBoxLayout* vbox = new QVBoxLayout; vbox->addWidget(m_onlyCustomGenresCheckBox); vbox->addWidget(genresEdit); genresGroupBox->setLayout(vbox); genresQuickAccessLayout->addWidget(genresGroupBox); QGroupBox* quickAccessTagsGroupBox = new QGroupBox(tr("&Quick Access Tags")); QVBoxLayout* quickAccessTagsLayout = new QVBoxLayout(quickAccessTagsGroupBox); QListView* quickAccessTagsListView = new QListView; m_quickAccessTagsModel = new CheckableStringListModel(quickAccessTagsGroupBox); QStringList unifiedFrameNames; for (int i = Frame::FT_FirstFrame; i< Frame::FT_LastFrame; ++i) { unifiedFrameNames.append( Frame::ExtendedType(static_cast(i)).getTranslatedName()); } m_quickAccessTagsModel->setStringList(unifiedFrameNames); quickAccessTagsListView->setModel(m_quickAccessTagsModel); quickAccessTagsLayout->addWidget(quickAccessTagsListView); genresQuickAccessLayout->addWidget(quickAccessTagsGroupBox); tag2Layout->addLayout(genresQuickAccessLayout); QWidget* tag1AndTag2Page = new QWidget; QVBoxLayout* tag1AndTag2Layout = new QVBoxLayout(tag1AndTag2Page); QString id3FormatTitle(tr("&Tag Format")); m_id3FormatBox = new FormatBox(id3FormatTitle, tag1AndTag2Page); tag1AndTag2Layout->addWidget(m_id3FormatBox); QTabWidget* tagsTabWidget = new QTabWidget; if (tagCfg.taggedFileFeatures() & TaggedFile::TF_ID3v11) { tagsTabWidget->addTab(tag1Page, tr("Tag &1")); } tagsTabWidget->addTab(tag2Page, tr("Tag &2")); tagsTabWidget->addTab(tag1AndTag2Page, tr("Tag 1 a&nd Tag 2")); tagsTabWidget->setCurrentIndex(1); vlayout->addWidget(tagsTabWidget); return tagsPage; } /** * Create page with files settings. * @return files page. */ QWidget* ConfigDialogPages::createFilesPage() { QWidget* filesPage = new QWidget; QVBoxLayout* vlayout = new QVBoxLayout(filesPage); QGroupBox* startupGroupBox = new QGroupBox(tr("Startup"), filesPage); m_loadLastOpenedFileCheckBox = new QCheckBox(tr("&Load last-opened files"), startupGroupBox); QVBoxLayout* startupLayout = new QVBoxLayout; startupLayout->addWidget(m_loadLastOpenedFileCheckBox); startupGroupBox->setLayout(startupLayout); vlayout->addWidget(startupGroupBox); QGroupBox* saveGroupBox = new QGroupBox(tr("Save"), filesPage); m_preserveTimeCheckBox = new QCheckBox(tr("&Preserve file timestamp"), saveGroupBox); m_markChangesCheckBox = new QCheckBox(tr("&Mark changes"), saveGroupBox); QLabel* coverFileNameLabel = new QLabel(tr("F&ilename for cover:"), saveGroupBox); m_coverFileNameLineEdit = new QLineEdit(saveGroupBox); coverFileNameLabel->setBuddy(m_coverFileNameLineEdit); QHBoxLayout* hbox = new QHBoxLayout; hbox->setContentsMargins(2, 0, 2, 0); hbox->addWidget(coverFileNameLabel); hbox->addWidget(m_coverFileNameLineEdit); QVBoxLayout* vbox = new QVBoxLayout; vbox->addWidget(m_preserveTimeCheckBox); vbox->addWidget(m_markChangesCheckBox); vbox->addLayout(hbox); saveGroupBox->setLayout(vbox); vlayout->addWidget(saveGroupBox); QString fnFormatTitle(tr("&Filename Format")); m_fnFormatBox = new FormatBox(fnFormatTitle, filesPage); vlayout->addWidget(m_fnFormatBox); return filesPage; } /** * Create page with actions settings. * @return actions page. */ QWidget* ConfigDialogPages::createActionsPage() { QWidget* actionsPage = new QWidget; QVBoxLayout* vlayout = new QVBoxLayout(actionsPage); QGroupBox* browserGroupBox = new QGroupBox(tr("Browser"), actionsPage); QLabel* browserLabel = new QLabel(tr("Web &browser:"), browserGroupBox); m_browserLineEdit = new QLineEdit(browserGroupBox); browserLabel->setBuddy(m_browserLineEdit); QHBoxLayout* hbox = new QHBoxLayout; hbox->addWidget(browserLabel); hbox->addWidget(m_browserLineEdit); browserGroupBox->setLayout(hbox); vlayout->addWidget(browserGroupBox); QGroupBox* commandsGroupBox = new QGroupBox(tr("Context &Menu Commands"), actionsPage); m_playOnDoubleClickCheckBox = new QCheckBox(tr("&Play on double click"), commandsGroupBox); m_commandsTableModel = new CommandsTableModel(commandsGroupBox); m_commandsTable = new ConfigTable(m_commandsTableModel, commandsGroupBox); m_commandsTable->setHorizontalResizeModes( m_commandsTableModel->getHorizontalResizeModes()); QVBoxLayout* commandsLayout = new QVBoxLayout; commandsLayout->addWidget(m_playOnDoubleClickCheckBox); commandsLayout->addWidget(m_commandsTable); commandsGroupBox->setLayout(commandsLayout); vlayout->addWidget(commandsGroupBox); return actionsPage; } /** * Create page with network settings. * @return network page. */ QWidget* ConfigDialogPages::createNetworkPage() { QWidget* networkPage = new QWidget; QVBoxLayout* vlayout = new QVBoxLayout(networkPage); QGroupBox* proxyGroupBox = new QGroupBox(tr("Proxy"), networkPage); m_proxyCheckBox = new QCheckBox(tr("&Proxy:"), proxyGroupBox); m_proxyLineEdit = new QLineEdit(proxyGroupBox); m_proxyAuthenticationCheckBox = new QCheckBox(tr("&Use authentication with proxy"), proxyGroupBox); QLabel* proxyUserNameLabel = new QLabel(tr("Proxy user &name:"), proxyGroupBox); m_proxyUserNameLineEdit = new QLineEdit(proxyGroupBox); proxyUserNameLabel->setBuddy(m_proxyUserNameLineEdit); QLabel* proxyPasswordLabel = new QLabel(tr("Proxy pass&word:"), proxyGroupBox); m_proxyPasswordLineEdit = new QLineEdit(proxyGroupBox); proxyPasswordLabel->setBuddy(m_proxyPasswordLineEdit); m_proxyPasswordLineEdit->setEchoMode(QLineEdit::Password); QVBoxLayout* vbox = new QVBoxLayout; QHBoxLayout* proxyHbox = new QHBoxLayout; proxyHbox->addWidget(m_proxyCheckBox); proxyHbox->addWidget(m_proxyLineEdit); vbox->addLayout(proxyHbox); vbox->addWidget(m_proxyAuthenticationCheckBox); QGridLayout* authLayout = new QGridLayout; authLayout->addWidget(proxyUserNameLabel, 0, 0); authLayout->addWidget(m_proxyUserNameLineEdit, 0, 1); authLayout->addWidget(proxyPasswordLabel, 1, 0); authLayout->addWidget(m_proxyPasswordLineEdit, 1, 1); vbox->addLayout(authLayout); proxyGroupBox->setLayout(vbox); vlayout->addWidget(proxyGroupBox); QSpacerItem* vspacer = new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding); vlayout->addItem(vspacer); return networkPage; } /** * Create page with plugins settings. * @return plugins page. */ QWidget* ConfigDialogPages::createPluginsPage() { QWidget* pluginsPage = new QWidget; QVBoxLayout* vlayout = new QVBoxLayout(pluginsPage); QGroupBox* metadataGroupBox = new QGroupBox( tr("&Metadata Plugins && Priority"), pluginsPage); QVBoxLayout* metadataPluginsLayout = new QVBoxLayout(metadataGroupBox); m_enabledMetadataPluginsModel = new CheckableStringListModel(metadataGroupBox); StringListEdit* metadataEdit = new StringListEdit(m_enabledMetadataPluginsModel, metadataGroupBox); metadataEdit->setEditingDisabled(true); metadataPluginsLayout->addWidget(metadataEdit); vlayout->addWidget(metadataGroupBox); QGroupBox* pluginsGroupBox = new QGroupBox(tr("A&vailable Plugins")); QVBoxLayout* pluginsLayout = new QVBoxLayout(pluginsGroupBox); QListView* pluginsListView = new QListView; pluginsListView->setSelectionMode(QAbstractItemView::NoSelection); m_enabledPluginsModel = new CheckableStringListModel(pluginsGroupBox); pluginsListView->setModel(m_enabledPluginsModel); pluginsLayout->addWidget(pluginsListView); vlayout->addWidget(pluginsGroupBox); vlayout->addStretch(); vlayout->addWidget( new QLabel(tr("Changes take only effect after a restart!"))); return pluginsPage; } /** * Set values in dialog from current configuration. */ void ConfigDialogPages::setConfig() { const FormatConfig& fnCfg = FilenameFormatConfig::instance(); const FormatConfig& id3Cfg = TagFormatConfig::instance(); const TagConfig& tagCfg = TagConfig::instance(); const FileConfig& fileCfg = FileConfig::instance(); const UserActionsConfig& userActionsCfg = UserActionsConfig::instance(); const GuiConfig& guiCfg = GuiConfig::instance(); const NetworkConfig& networkCfg = NetworkConfig::instance(); const ImportConfig& importCfg = ImportConfig::instance(); m_fnFormatBox->fromFormatConfig(fnCfg); m_id3FormatBox->fromFormatConfig(id3Cfg); m_markTruncationsCheckBox->setChecked(tagCfg.markTruncations()); m_totalNumTracksCheckBox->setChecked(tagCfg.enableTotalNumberOfTracks()); m_loadLastOpenedFileCheckBox->setChecked(fileCfg.m_loadLastOpenedFile); m_preserveTimeCheckBox->setChecked(fileCfg.m_preserveTime); m_markChangesCheckBox->setChecked(fileCfg.m_markChanges); m_coverFileNameLineEdit->setText(fileCfg.m_defaultCoverFileName); m_onlyCustomGenresCheckBox->setChecked(tagCfg.onlyCustomGenres()); m_genresEditModel->setStringList(tagCfg.customGenres()); m_quickAccessTagsModel->setBitMask(tagCfg.quickAccessFrames()); m_commandsTableModel->setCommandList(userActionsCfg.m_contextMenuCommands); int idx = m_commentNameComboBox->findText(tagCfg.commentName()); if (idx >= 0) { m_commentNameComboBox->setCurrentIndex(idx); } else { m_commentNameComboBox->addItem(tagCfg.commentName()); m_commentNameComboBox->setCurrentIndex(m_commentNameComboBox->count() - 1); } m_pictureNameComboBox->setCurrentIndex(tagCfg.pictureNameItem()); m_genreNotNumericCheckBox->setChecked(tagCfg.genreNotNumeric()); int textEncodingV1Index = TextEncodingV1Latin1Index; int index = 0; for (QStringList::const_iterator it = m_textEncodingV1List.begin(); it != m_textEncodingV1List.end(); ++it) { if (getTextEncodingV1CodecName(*it) == tagCfg.textEncodingV1()) { textEncodingV1Index = index; break; } ++index; } m_textEncodingV1ComboBox->setCurrentIndex(textEncodingV1Index); m_textEncodingComboBox->setCurrentIndex(tagCfg.textEncoding()); m_id3v2VersionComboBox->setCurrentIndex( m_id3v2VersionComboBox->findData(tagCfg.id3v2Version())); m_trackNumberDigitsSpinBox->setValue(tagCfg.trackNumberDigits()); m_browserLineEdit->setText(networkCfg.m_browser); m_playOnDoubleClickCheckBox->setChecked(guiCfg.m_playOnDoubleClick); m_proxyCheckBox->setChecked(networkCfg.m_useProxy); m_proxyLineEdit->setText(networkCfg.m_proxy); m_proxyAuthenticationCheckBox->setChecked(networkCfg.m_useProxyAuthentication); m_proxyUserNameLineEdit->setText(networkCfg.m_proxyUserName); m_proxyPasswordLineEdit->setText(networkCfg.m_proxyPassword); QStringList metadataPlugins; QStringList pluginOrder = tagCfg.pluginOrder(); if (!pluginOrder.isEmpty()) { for (int i = 0; i < pluginOrder.size(); ++i) { metadataPlugins.append(QString()); } foreach (const QString& pluginName, tagCfg.getAvailablePlugins()) { int idx = pluginOrder.indexOf(pluginName); if (idx >= 0) { metadataPlugins[idx] = pluginName; } else { metadataPlugins.append(pluginName); } } metadataPlugins.removeAll(QString()); } else { metadataPlugins = tagCfg.getAvailablePlugins(); } quint64 metadataPluginsMask = 0; quint64 mask = 1; QStringList disabledTagPlugins = tagCfg.disabledPlugins(); for (int i = 0; i < metadataPlugins.size(); ++i, mask <<= 1) { if (!disabledTagPlugins.contains(metadataPlugins.at(i))) { metadataPluginsMask |= mask; } } m_enabledMetadataPluginsModel->setStringList(metadataPlugins); m_enabledMetadataPluginsModel->setBitMask(metadataPluginsMask); QStringList importPlugins = importCfg.getAvailablePlugins(); quint64 importPluginsMask = 0; mask = 1; for (int i = 0; i < importPlugins.size(); ++i, mask <<= 1) { if (!importCfg.m_disabledPlugins.contains(importPlugins.at(i))) { importPluginsMask |= mask; } } m_enabledPluginsModel->setStringList(importPlugins); m_enabledPluginsModel->setBitMask(importPluginsMask); } /** * Get values from dialog and store them in the current configuration. */ void ConfigDialogPages::getConfig() const { FormatConfig& fnCfg = FilenameFormatConfig::instance(); FormatConfig& id3Cfg = TagFormatConfig::instance(); TagConfig& tagCfg = TagConfig::instance(); FileConfig& fileCfg = FileConfig::instance(); UserActionsConfig& userActionsCfg = UserActionsConfig::instance(); GuiConfig& guiCfg = GuiConfig::instance(); NetworkConfig& networkCfg = NetworkConfig::instance(); ImportConfig& importCfg = ImportConfig::instance(); m_fnFormatBox->toFormatConfig(fnCfg); m_id3FormatBox->toFormatConfig(id3Cfg); tagCfg.setMarkTruncations(m_markTruncationsCheckBox->isChecked()); tagCfg.setEnableTotalNumberOfTracks(m_totalNumTracksCheckBox->isChecked()); fileCfg.m_loadLastOpenedFile = m_loadLastOpenedFileCheckBox->isChecked(); fileCfg.m_preserveTime = m_preserveTimeCheckBox->isChecked(); fileCfg.m_markChanges = m_markChangesCheckBox->isChecked(); fileCfg.m_defaultCoverFileName = m_coverFileNameLineEdit->text(); tagCfg.setOnlyCustomGenres(m_onlyCustomGenresCheckBox->isChecked()); tagCfg.setCustomGenres(m_genresEditModel->stringList()); tagCfg.setQuickAccessFrames(m_quickAccessTagsModel->getBitMask()); userActionsCfg.m_contextMenuCommands = m_commandsTableModel->getCommandList(); tagCfg.setCommentName(m_commentNameComboBox->currentText()); tagCfg.setPictureNameItem(m_pictureNameComboBox->currentIndex()); tagCfg.setGenreNotNumeric(m_genreNotNumericCheckBox->isChecked()); tagCfg.setTextEncodingV1( getTextEncodingV1CodecName(m_textEncodingV1ComboBox->currentText())); tagCfg.setTextEncoding(m_textEncodingComboBox->currentIndex()); tagCfg.setId3v2Version(m_id3v2VersionComboBox->itemData( m_id3v2VersionComboBox->currentIndex()).toInt()); tagCfg.setTrackNumberDigits(m_trackNumberDigitsSpinBox->value()); networkCfg.m_browser = m_browserLineEdit->text(); guiCfg.m_playOnDoubleClick = m_playOnDoubleClickCheckBox->isChecked(); networkCfg.m_useProxy = m_proxyCheckBox->isChecked(); networkCfg.m_proxy = m_proxyLineEdit->text(); networkCfg.m_useProxyAuthentication = m_proxyAuthenticationCheckBox->isChecked(); networkCfg.m_proxyUserName = m_proxyUserNameLineEdit->text(); networkCfg.m_proxyPassword = m_proxyPasswordLineEdit->text(); QStringList pluginOrder, disabledPlugins; for (int row = 0; row < m_enabledMetadataPluginsModel->rowCount(); ++row) { QString pluginName = m_enabledMetadataPluginsModel->index(row).data().toString(); pluginOrder.append(pluginName); if (m_enabledMetadataPluginsModel->index(row).data(Qt::CheckStateRole). toInt() != Qt::Checked) { disabledPlugins.append(pluginName); } } tagCfg.setPluginOrder(pluginOrder); tagCfg.setDisabledPlugins(disabledPlugins); disabledPlugins.clear(); for (int row = 0; row < m_enabledPluginsModel->rowCount(); ++row) { if (m_enabledPluginsModel->index(row).data(Qt::CheckStateRole). toInt() != Qt::Checked) { disabledPlugins.append( m_enabledPluginsModel->index(row).data().toString()); } } importCfg.m_disabledPlugins = disabledPlugins; } kid3-3.0.2/src/gui/dialogs/configdialogpages.h000066400000000000000000000106361224603511300211640ustar00rootroot00000000000000/** * \file configdialogpages.h * Pages for configuration dialog. * * \b Project: Kid3 * \author Urs Fleisch * \date 17 Sep 2003 * * Copyright (C) 2003-2012 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef CONFIGDIALOGPAGES_H #define CONFIGDIALOGPAGES_H #include #include #include "config.h" #include "kid3api.h" class QCheckBox; class QString; class QWidget; class QComboBox; class QLineEdit; class QSpinBox; class QStringListModel; class FormatBox; class ConfigTable; class CommandsTableModel; class CheckableStringListModel; /** * Pages for configuration dialog. */ class KID3_GUI_EXPORT ConfigDialogPages : public QObject { Q_OBJECT public: /** * Constructor. */ explicit ConfigDialogPages(QObject* parent = 0); /** * Destructor. */ virtual ~ConfigDialogPages(); /** * Create page with tags settings. * @return tags page. */ QWidget* createTagsPage(); /** * Create page with files settings. * @return files page. */ QWidget* createFilesPage(); /** * Create page with actions settings. * @return actions page. */ QWidget* createActionsPage(); /** * Create page with network settings. * @return network page. */ QWidget* createNetworkPage(); /** * Create page with plugins settings. * @return plugins page. */ QWidget* createPluginsPage(); /** * Set values in pages from current configuration. */ void setConfig(); /** * Get values from pages and store them in the current configuration. */ void getConfig() const; private: /** Load last-opened files checkbox */ QCheckBox* m_loadLastOpenedFileCheckBox; /** Preserve timestamp checkbox */ QCheckBox* m_preserveTimeCheckBox; /** Mark changes checkbox */ QCheckBox* m_markChangesCheckBox; /** Filename for cover lineedit */ QLineEdit* m_coverFileNameLineEdit; /** Mark truncated fields checkbox */ QCheckBox* m_markTruncationsCheckBox; /** ID3v1 text encodings */ QStringList m_textEncodingV1List; /** ID3v1 text encoding combo box */ QComboBox* m_textEncodingV1ComboBox; /** Use track/total number of tracks format checkbox */ QCheckBox* m_totalNumTracksCheckBox; /** Comment field name combo box */ QComboBox* m_commentNameComboBox; /** Picture field name combo box */ QComboBox* m_pictureNameComboBox; /** Genre as text instead of numeric string checkbox */ QCheckBox* m_genreNotNumericCheckBox; /** ID3v2 text encoding combo box */ QComboBox* m_textEncodingComboBox; /** ID3v2 version combo box */ QComboBox* m_id3v2VersionComboBox; /** Number of digits in track number spin box */ QSpinBox* m_trackNumberDigitsSpinBox; /** Filename Format box */ FormatBox* m_fnFormatBox; /** ID3 Format box */ FormatBox* m_id3FormatBox; /** Only custom genres checkbox */ QCheckBox* m_onlyCustomGenresCheckBox; /** Model with list of custom genres */ QStringListModel* m_genresEditModel; /** Model with standard tags selection */ CheckableStringListModel* m_quickAccessTagsModel; /** Play on double click checkbox */ QCheckBox* m_playOnDoubleClickCheckBox; /** Commands table */ ConfigTable* m_commandsTable; /** Commands table model */ CommandsTableModel* m_commandsTableModel; /** Browser line edit */ QLineEdit* m_browserLineEdit; /** Use proxy check box */ QCheckBox* m_proxyCheckBox; /** Proxy line edit */ QLineEdit* m_proxyLineEdit; /** Use proxy authentication check box */ QCheckBox* m_proxyAuthenticationCheckBox; /** Proxy user name line edit */ QLineEdit* m_proxyUserNameLineEdit; /** Proxy password line edit */ QLineEdit* m_proxyPasswordLineEdit; /** Model with enabled metadata plugins */ CheckableStringListModel* m_enabledMetadataPluginsModel; /** Model with enabled plugins */ CheckableStringListModel* m_enabledPluginsModel; }; #endif kid3-3.0.2/src/gui/dialogs/contexthelp.cpp000066400000000000000000000024621224603511300204050ustar00rootroot00000000000000/** * \file contexthelp.cpp * Context sensitive help. * * \b Project: Kid3 * \author Urs Fleisch * \date 09 Jul 2011 * * Copyright (C) 2011-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "contexthelp.h" #include "iplatformtools.h" IPlatformTools* ContextHelp::s_platformTools = 0; /** * Initialize context help. * * @param platformTools platform tools to use */ void ContextHelp::init(IPlatformTools* platformTools) { s_platformTools = platformTools; } /** * Display help for a topic. * * @param anchor anchor in help document */ void ContextHelp::displayHelp(const QString& anchor) { if (s_platformTools) { s_platformTools->displayHelp(anchor); } } kid3-3.0.2/src/gui/dialogs/contexthelp.h000066400000000000000000000025731224603511300200550ustar00rootroot00000000000000/** * \file contexthelp.h * Context sensitive help. * * \b Project: Kid3 * \author Urs Fleisch * \date 09 Jul 2011 * * Copyright (C) 2011-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef CONTEXTHELP_H #define CONTEXTHELP_H #include #include "kid3api.h" class IPlatformTools; /** * Context sensitive help. */ class KID3_GUI_EXPORT ContextHelp { public: /** * Initialize context help. * * @param platformTools platform tools to use */ static void init(IPlatformTools* platformTools); /** * Display help for a topic. * * @param anchor anchor in help document */ static void displayHelp(const QString& anchor = QString()); private: static IPlatformTools* s_platformTools; }; #endif // CONTEXTHELP_H kid3-3.0.2/src/gui/dialogs/downloaddialog.cpp000066400000000000000000000035261224603511300210410ustar00rootroot00000000000000/** * \file downloaddialog.cpp * Dialog displayed during a download. * * \b Project: Kid3 * \author Urs Fleisch * \date 31 Dec 2008 * * Copyright (C) 2008-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "downloaddialog.h" #include /** * Constructor. * * @param parent parent widget * @param caption dialog title */ DownloadDialog::DownloadDialog(QWidget* parent, const QString& caption) : QProgressDialog(parent) { setObjectName(QLatin1String("DownloadDialog")); setWindowTitle(caption); } /** * Destructor. */ DownloadDialog::~DownloadDialog() { } /** * Show dialog to report start of download. * @param url URL of download */ void DownloadDialog::showStartOfDownload(const QString& url) { m_url = url; setLabelText(url); } /** * Display message in status bar. * * @param msg status message * @param receivedBytes bytes received * @param totalBytes total bytes */ void DownloadDialog::updateProgressStatus(const QString& msg, int receivedBytes, int totalBytes) { setLabelText(m_url + QLatin1Char('\n') + msg); if (receivedBytes >= 0 && totalBytes >= 0) { setRange(0, totalBytes); setValue(receivedBytes); } } kid3-3.0.2/src/gui/dialogs/downloaddialog.h000066400000000000000000000042731224603511300205060ustar00rootroot00000000000000/** * \file downloaddialog.h * Dialog displayed during a download. * * \b Project: Kid3 * \author Urs Fleisch * \date 31 Dec 2008 * * Copyright (C) 2008-2009 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef DOWNLOADDIALOG_H #define DOWNLOADDIALOG_H #include #include /** * Dialog displayed during a download. * * The download dialog can be used together with the DownloadClient to get * progress feedback. These two objects have to be connected in the following * way (DownloadClient to DownloadDialog): * - progress() to updateProgressStatus(), * - downloadStarted() to showStartOfDownload(), * - cancelDownload() from canceled(), * - aborted() to reset(). * * A download is started with DownloadClient::startDownload() and termination * signaled by DownloadClient::downloadFinished(). */ class DownloadDialog : public QProgressDialog { Q_OBJECT public: /** * Constructor. * * @param parent parent widget * @param caption dialog title */ DownloadDialog(QWidget* parent, const QString& caption); /** * Destructor. */ virtual ~DownloadDialog(); public slots: /** * Show dialog to report start of download. * @param url URL of download */ void showStartOfDownload(const QString& url); /** * Display progress status. * * @param msg status message * @param receivedBytes bytes received * @param totalBytes total bytes */ void updateProgressStatus(const QString& msg, int receivedBytes, int totalBytes); private: QString m_url; }; #endif // DOWNLOADDIALOG_H kid3-3.0.2/src/gui/dialogs/editframedialog.cpp000066400000000000000000000040711224603511300211660ustar00rootroot00000000000000/** * \file editframedialog.cpp * Field edit dialog. * * \b Project: Kid3 * \author Urs Fleisch * \date 10 Jun 2009 * * Copyright (C) 2003-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "editframedialog.h" #include #include /** * Constructor. * * @param parent parent widget * @param caption window title * @param text text to edit */ EditFrameDialog::EditFrameDialog(QWidget* parent, const QString& caption, const QString& text) : QDialog(parent) { setObjectName(QLatin1String("EditFrameDialog")); setModal(true); setWindowTitle(caption); QVBoxLayout* vlayout = new QVBoxLayout(this); m_edit = new QTextEdit(this); m_edit->setPlainText(text); m_edit->moveCursor(QTextCursor::End); vlayout->addWidget(m_edit); QHBoxLayout* hlayout = new QHBoxLayout; QSpacerItem* hspacer = new QSpacerItem(16, 0, QSizePolicy::Expanding, QSizePolicy::Minimum); m_okButton = new QPushButton(tr("&OK"), this); m_cancelButton = new QPushButton(tr("&Cancel"), this); hlayout->addItem(hspacer); hlayout->addWidget(m_okButton); hlayout->addWidget(m_cancelButton); m_okButton->setDefault(true); connect(m_okButton, SIGNAL(clicked()), this, SLOT(accept())); connect(m_cancelButton, SIGNAL(clicked()), this, SLOT(reject())); vlayout->addLayout(hlayout); setMinimumWidth(400); } /** * Destructor. */ EditFrameDialog::~EditFrameDialog() { } kid3-3.0.2/src/gui/dialogs/editframedialog.h000066400000000000000000000032251224603511300206330ustar00rootroot00000000000000/** * \file editframedialog.h * Field edit dialog. * * \b Project: Kid3 * \author Urs Fleisch * \date 10 Jun 2009 * * Copyright (C) 2003-2007 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef EDITFRAMEDIALOG_H #define EDITFRAMEDIALOG_H #include #include class QPushButton; /** Field edit dialog */ class EditFrameDialog : public QDialog { Q_OBJECT public: /** * Constructor. * * @param parent parent widget * @param caption window title * @param text text to edit */ EditFrameDialog(QWidget* parent, const QString& caption, const QString& text); /** * Destructor. */ virtual ~EditFrameDialog(); /** * Set text to edit. * @param text text */ void setText(const QString& text) { m_edit->setPlainText(text); } /** * Get edited text. * @return text. */ QString getText() const { return m_edit->toPlainText(); } private: QTextEdit* m_edit; QPushButton* m_okButton; QPushButton* m_cancelButton; }; #endif // EDITFRAMEDIALOG_H kid3-3.0.2/src/gui/dialogs/editframefieldsdialog.cpp000066400000000000000000000617071224603511300223660ustar00rootroot00000000000000/** * \file editframefieldsdialog.cpp * Field edit dialog. * * \b Project: Kid3 * \author Urs Fleisch * \date 10 Jun 2009 * * Copyright (C) 2003-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "editframefieldsdialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kid3application.h" #include "imageviewer.h" #include "taggedfile.h" #include "config.h" #include "fileconfig.h" #include "iplatformtools.h" /** QTextEdit with label above */ class LabeledTextEdit : public QWidget { public: /** * Constructor. * * @param parent parent widget */ LabeledTextEdit(QWidget* parent); /** * Get text. * * @return text. */ QString text() const { return m_edit->toPlainText(); } /** * Set text. * * @param txt text */ void setText(const QString& txt) { m_edit->setPlainText(txt); } /** * Set focus to text field. */ void setFocus() { m_edit->setFocus(); } /** * Set label. * * @param txt label */ void setLabel(const QString& txt) { m_label->setText(txt); } private: /** Label above edit */ QLabel* m_label; /** Text editor */ QTextEdit* m_edit; }; /** LineEdit with label above */ class LabeledLineEdit : public QWidget { public: /** * Constructor. * * @param parent parent widget */ LabeledLineEdit(QWidget* parent); /** * Get text. * * @return text. */ QString text() const { return m_edit->text(); } /** * Set text. * * @param txt text */ void setText(const QString& txt) { m_edit->setText(txt); } /** * Set label. * * @param txt label */ void setLabel(const QString& txt) { m_label->setText(txt); } private: /** Label above edit */ QLabel* m_label; /** Line editor */ QLineEdit* m_edit; }; /** Combo box with label above */ class LabeledComboBox : public QWidget { public: /** * Constructor. * * @param parent parent widget * @param strlst list with ComboBox items, terminated by NULL */ LabeledComboBox(QWidget* parent, const char** strlst); /** * Get index of selected item. * * @return index. */ int currentItem() const { return m_combo->currentIndex(); } /** * Set index of selected item. * * @param idx index */ void setCurrentItem(int idx) { m_combo->setCurrentIndex(idx); } /** * Set label. * * @param txt label */ void setLabel(const QString& txt) { m_label->setText(txt); } private: /** Label above combo box */ QLabel* m_label; /** Combo box */ QComboBox* m_combo; }; /** QSpinBox with label above */ class LabeledSpinBox : public QWidget { public: /** * Constructor. * * @param parent parent widget */ LabeledSpinBox(QWidget* parent); /** * Get value. * * @return text. */ int value() const { return m_spinbox->value(); } /** * Set value. * * @param value value */ void setValue(int value) { m_spinbox->setValue(value); } /** * Set label. * * @param txt label */ void setLabel(const QString& txt) { m_label->setText(txt); } private: /** Label above edit */ QLabel* m_label; /** Text editor */ QSpinBox* m_spinbox; }; /** * Constructor. * * @param parent parent widget */ LabeledTextEdit::LabeledTextEdit(QWidget* parent) : QWidget(parent) { setObjectName(QLatin1String("LabeledTextEdit")); QVBoxLayout* layout = new QVBoxLayout(this); m_label = new QLabel(this); m_edit = new QTextEdit(this); layout->setContentsMargins(0, 0, 0, 0); m_edit->setAcceptRichText(false); layout->addWidget(m_label); layout->addWidget(m_edit); } /** * Constructor. * * @param parent parent widget */ LabeledLineEdit::LabeledLineEdit(QWidget* parent) : QWidget(parent) { setObjectName(QLatin1String("LabeledLineEdit")); QVBoxLayout* layout = new QVBoxLayout(this); m_label = new QLabel(this); m_edit = new QLineEdit(this); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(m_label); layout->addWidget(m_edit); } /** * Constructor. * * @param parent parent widget * @param strlst list with ComboBox items, terminated by NULL */ LabeledComboBox::LabeledComboBox(QWidget* parent, const char **strlst) : QWidget(parent) { setObjectName(QLatin1String("LabeledComboBox")); QVBoxLayout* layout = new QVBoxLayout(this); m_label = new QLabel(this); m_combo = new QComboBox(this); layout->setContentsMargins(0, 0, 0, 0); QStringList strList; while (*strlst) { strList += QCoreApplication::translate("@default", *strlst++); } m_combo->addItems(strList); layout->addWidget(m_label); layout->addWidget(m_combo); } /** * Constructor. * * @param parent parent widget */ LabeledSpinBox::LabeledSpinBox(QWidget* parent) : QWidget(parent) { setObjectName(QLatin1String("LabeledSpinBox")); QVBoxLayout* layout = new QVBoxLayout(this); m_label = new QLabel(this); m_spinbox = new QSpinBox(this); if (layout && m_label && m_spinbox) { m_spinbox->setRange(0, INT_MAX); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(m_label); layout->addWidget(m_spinbox); } } /** Base class for MP3 field controls */ class Mp3FieldControl : public FieldControl { public: /** * Constructor. * @param field field to edit */ Mp3FieldControl(Frame::Field& field) : m_field(field) {} /** * Destructor. */ virtual ~Mp3FieldControl() {} protected: /** * Get description for ID3_Field. * * @param id ID of field * @return description or NULL if id unknown. */ const char* getFieldIDString(Frame::Field::Id id) const; /** field */ Frame::Field& m_field; }; /** Control to edit standard UTF text fields */ class TextFieldControl : public Mp3FieldControl { public: /** * Constructor. * @param field field to edit */ TextFieldControl(Frame::Field& field) : Mp3FieldControl(field), m_edit(0) {} /** * Destructor. */ virtual ~TextFieldControl() {} /** * Update field from data in field control. */ virtual void updateTag(); /** * Create widget to edit field data. * * @param parent parent widget * * @return widget to edit field data. */ virtual QWidget* createWidget(QWidget* parent); protected: /** Text editor widget */ LabeledTextEdit* m_edit; }; /** Control to edit single line text fields */ class LineFieldControl : public Mp3FieldControl { public: /** * Constructor. * @param field field to edit */ LineFieldControl(Frame::Field& field) : Mp3FieldControl(field), m_edit(0) {} /** * Destructor. */ virtual ~LineFieldControl() {} /** * Update field from data in field control. */ virtual void updateTag(); /** * Create widget to edit field data. * * @param parent parent widget * * @return widget to edit field data. */ virtual QWidget* createWidget(QWidget* parent); protected: /** Line editor widget */ LabeledLineEdit* m_edit; }; /** Control to edit integer fields */ class IntFieldControl : public Mp3FieldControl { public: /** * Constructor. * @param field field to edit */ IntFieldControl(Frame::Field& field) : Mp3FieldControl(field), m_numInp(0) {} /** * Destructor. */ virtual ~IntFieldControl() {} /** * Update field from data in field control. */ virtual void updateTag(); /** * Create widget to edit field data. * * @param parent parent widget * * @return widget to edit field data. */ virtual QWidget* createWidget(QWidget* parent); protected: /** Spin box widget */ LabeledSpinBox* m_numInp; }; /** Control to edit integer fields using a combo box with given values */ class IntComboBoxControl : public Mp3FieldControl { public: /** * Constructor. * @param field field to edit * @param lst list of strings with possible selections, NULL terminated */ IntComboBoxControl(Frame::Field& field, const char **lst) : Mp3FieldControl(field), m_ptInp(0), m_strLst(lst) {} /** * Destructor. */ virtual ~IntComboBoxControl() {} /** * Update field from data in field control. */ virtual void updateTag(); /** * Create widget to edit field data. * * @param parent parent widget * * @return widget to edit field data. */ virtual QWidget* createWidget(QWidget* parent); protected: /** Combo box widget */ LabeledComboBox* m_ptInp; /** List of strings with possible selections */ const char** m_strLst; }; /** Control to import, export and view data from binary fields */ class BinFieldControl : public Mp3FieldControl { public: /** * Constructor. * @param platformTools platform tools * @param field field to edit * @param frame frame with fields to edit * @param taggedFile file */ BinFieldControl(IPlatformTools* platformTools, Frame::Field& field, const Frame& frame, const TaggedFile* taggedFile) : Mp3FieldControl(field), m_platformTools(platformTools), m_bos(0), m_frame(frame), m_taggedFile(taggedFile) {} /** * Destructor. */ virtual ~BinFieldControl() {} /** * Update field from data in field control. */ virtual void updateTag(); /** * Create widget to edit field data. * * @param parent parent widget * * @return widget to edit field data. */ virtual QWidget* createWidget(QWidget* parent); protected: /** Platform dependent tools */ IPlatformTools* m_platformTools; /** Import, Export, View buttons */ BinaryOpenSave* m_bos; /** frame with fields to edit */ const Frame& m_frame; /** tagged file */ const TaggedFile* m_taggedFile; }; /** * Constructor. * * @param platformTools platform tools * @param parent parent widget * @param field field containing binary data */ BinaryOpenSave::BinaryOpenSave(IPlatformTools* platformTools, QWidget* parent, const Frame::Field& field) : QWidget(parent), m_platformTools(platformTools), m_byteArray(field.m_value.toByteArray()), m_isChanged(false) { setObjectName(QLatin1String("BinaryOpenSave")); QHBoxLayout* layout = new QHBoxLayout(this); m_label = new QLabel(this); m_clipButton = new QPushButton(tr("From Clip&board"), this); QPushButton* openButton = new QPushButton(tr("&Import"), this); QPushButton* saveButton = new QPushButton(tr("&Export"), this); QPushButton* viewButton = new QPushButton(tr("&View"), this); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(m_label); layout->addWidget(m_clipButton); layout->addWidget(openButton); layout->addWidget(saveButton); layout->addWidget(viewButton); connect(m_clipButton, SIGNAL(clicked()), this, SLOT(clipData())); connect(openButton, SIGNAL(clicked()), this, SLOT(loadData())); connect(saveButton, SIGNAL(clicked()), this, SLOT(saveData())); connect(viewButton, SIGNAL(clicked()), this, SLOT(viewData())); connect(QApplication::clipboard(), SIGNAL(dataChanged()), this, SLOT(setClipButtonState())); setClipButtonState(); } /** * Enable the "From Clipboard" button if the clipboard contains an image. */ void BinaryOpenSave::setClipButtonState() { QClipboard* cb = QApplication::clipboard(); m_clipButton->setEnabled( cb && (cb->mimeData()->hasFormat(QLatin1String("image/jpeg")) || cb->mimeData()->hasImage())); } /** * Load image from clipboard. */ void BinaryOpenSave::clipData() { QClipboard* cb = QApplication::clipboard(); if (cb) { if (cb->mimeData()->hasFormat(QLatin1String("image/jpeg"))) { m_byteArray = cb->mimeData()->data(QLatin1String("image/jpeg")); m_isChanged = true; } else if (cb->mimeData()->hasImage()) { QBuffer buffer(&m_byteArray); buffer.open(QIODevice::WriteOnly); cb->image().save(&buffer, "JPG"); m_isChanged = true; } } } /** * Request name of file to import binary data from. * The data is imported later when Ok is pressed in the parent dialog. */ void BinaryOpenSave::loadData() { QString loadfilename = m_platformTools->getOpenFileName(this, QString(), m_defaultDir.isEmpty() ? Kid3Application::getDirName() : m_defaultDir, m_filter, 0); if (!loadfilename.isEmpty()) { QFile file(loadfilename); if (file.open(QIODevice::ReadOnly)) { size_t size = file.size(); char* data = new char[size]; QDataStream stream(&file); stream.readRawData(data, size); m_byteArray = QByteArray(data, size); m_isChanged = true; delete [] data; file.close(); } } } /** * Request name of file and export binary data. */ void BinaryOpenSave::saveData() { QString dir = m_defaultDir.isEmpty() ? Kid3Application::getDirName() : m_defaultDir; if (!m_defaultFile.isEmpty()) { QChar separator = QDir::separator(); if (!dir.endsWith(separator)) { dir += separator; } dir += m_defaultFile; } QString fn = m_platformTools->getSaveFileName( this, QString(), dir, m_filter, 0); if (!fn.isEmpty()) { QFile file(fn); if (file.open(QIODevice::WriteOnly)) { QDataStream stream(&file); stream.writeRawData(m_byteArray.data(), m_byteArray.size()); file.close(); } } } /** * Create image from binary data and display it in window. */ void BinaryOpenSave::viewData() { QImage image; if (image.loadFromData(m_byteArray)) { ImageViewer iv(this, image); iv.exec(); } } /** * Get description for ID3_Field. * * @param id ID of field * @return description or NULL if id unknown. */ const char* Mp3FieldControl::getFieldIDString(Frame::Field::Id id) const { static const char* const idStr[] = { "Unknown", QT_TRANSLATE_NOOP("@default", "Text Encoding"), QT_TRANSLATE_NOOP("@default", "Text"), QT_TRANSLATE_NOOP("@default", "URL"), QT_TRANSLATE_NOOP("@default", "Data"), QT_TRANSLATE_NOOP("@default", "Description"), QT_TRANSLATE_NOOP("@default", "Owner"), QT_TRANSLATE_NOOP("@default", "Email"), QT_TRANSLATE_NOOP("@default", "Rating"), QT_TRANSLATE_NOOP("@default", "Filename"), QT_TRANSLATE_NOOP("@default", "Language"), QT_TRANSLATE_NOOP("@default", "Picture Type"), QT_TRANSLATE_NOOP("@default", "Image format"), QT_TRANSLATE_NOOP("@default", "Mimetype"), QT_TRANSLATE_NOOP("@default", "Counter"), QT_TRANSLATE_NOOP("@default", "Identifier"), QT_TRANSLATE_NOOP("@default", "Volume Adjustment"), QT_TRANSLATE_NOOP("@default", "Number of Bits"), QT_TRANSLATE_NOOP("@default", "Volume Change Right"), QT_TRANSLATE_NOOP("@default", "Volume Change Left"), QT_TRANSLATE_NOOP("@default", "Peak Volume Right"), QT_TRANSLATE_NOOP("@default", "Peak Volume Left"), QT_TRANSLATE_NOOP("@default", "Timestamp Format"), QT_TRANSLATE_NOOP("@default", "Content Type"), QT_TRANSLATE_NOOP("@default", "Price"), QT_TRANSLATE_NOOP("@default", "Date"), QT_TRANSLATE_NOOP("@default", "Seller") }; struct not_used { int array_size_check[ sizeof(idStr) / sizeof(idStr[0]) == Frame::Field::ID_Seller + 1 ? 1 : -1 ]; }; return idStr[id <= Frame::Field::ID_Seller ? id : 0]; } /** * Update field with data from dialog. */ void TextFieldControl::updateTag() { m_field.m_value = m_edit->text(); } /** * Create widget for dialog. * * @param parent parent widget * @return widget to edit field. */ QWidget* TextFieldControl::createWidget(QWidget* parent) { m_edit = new LabeledTextEdit(parent); if (m_edit == NULL) return NULL; m_edit->setLabel(QCoreApplication::translate("@default", getFieldIDString(static_cast(m_field.m_id)))); m_edit->setText(m_field.m_value.toString()); m_edit->setFocus(); return m_edit; } /** * Update field with data from dialog. */ void LineFieldControl::updateTag() { m_field.m_value = m_edit->text(); } /** * Create widget for dialog. * * @param parent parent widget * @return widget to edit field. */ QWidget* LineFieldControl::createWidget(QWidget* parent) { m_edit = new LabeledLineEdit(parent); m_edit->setLabel(QCoreApplication::translate("@default", getFieldIDString(static_cast(m_field.m_id)))); m_edit->setText(m_field.m_value.toString()); return m_edit; } /** * Update field with data from dialog. */ void IntFieldControl::updateTag() { m_field.m_value = m_numInp->value(); } /** * Create widget for dialog. * * @param parent parent widget * @return widget to edit field. */ QWidget* IntFieldControl::createWidget(QWidget* parent) { m_numInp = new LabeledSpinBox(parent); m_numInp->setLabel(QCoreApplication::translate("@default", getFieldIDString(static_cast(m_field.m_id)))); m_numInp->setValue(m_field.m_value.toInt()); return m_numInp; } /** * Update field with data from dialog. */ void IntComboBoxControl::updateTag() { m_field.m_value = m_ptInp->currentItem(); } /** * Create widget for dialog. * * @param parent parent widget * @return widget to edit field. */ QWidget* IntComboBoxControl::createWidget(QWidget* parent) { m_ptInp = new LabeledComboBox(parent, m_strLst); m_ptInp->setLabel(QCoreApplication::translate("@default", getFieldIDString(static_cast(m_field.m_id)))); m_ptInp->setCurrentItem(m_field.m_value.toInt()); return m_ptInp; } /** * Update field with data from dialog. */ void BinFieldControl::updateTag() { if (m_bos && m_bos->isChanged()) { m_field.m_value = m_bos->getData(); } } /** * Create widget for dialog. * * @param parent parent widget * @return widget to edit field. */ QWidget* BinFieldControl::createWidget(QWidget* parent) { m_bos = new BinaryOpenSave(m_platformTools, parent, m_field); m_bos->setLabel(QCoreApplication::translate("@default", getFieldIDString(static_cast(m_field.m_id)))); if (m_taggedFile) { m_bos->setDefaultDir(m_taggedFile->getDirname()); } if (m_frame.getType() == Frame::FT_Picture) { m_bos->setDefaultFile(FileConfig::instance().m_defaultCoverFileName); m_bos->setFilter(m_platformTools->fileDialogNameFilter( QList >() << qMakePair(QCoreApplication::translate("@default", QT_TRANSLATE_NOOP("@default", "Images")), QString(QLatin1String("*.jpg *.jpeg *.png"))) << qMakePair(QCoreApplication::translate("@default", QT_TRANSLATE_NOOP("@default", "All Files")), QString(QLatin1Char('*'))))); } return m_bos; } /** * Update fields and get edited fields. * * @return field list. */ const Frame::FieldList& EditFrameFieldsDialog::getUpdatedFieldList() { QListIterator it(m_fieldcontrols); while (it.hasNext()) { it.next()->updateTag(); } return m_fields; } /** * Constructor. * * @param platformTools platform tools * @param parent parent widget * @param caption caption * @param frame frame with fields to edit * @param taggedFile file */ EditFrameFieldsDialog::EditFrameFieldsDialog( IPlatformTools* platformTools, QWidget* parent, const QString& caption, const Frame& frame, const TaggedFile* taggedFile) : QDialog(parent), m_platformTools(platformTools), m_fields(frame.getFieldList()) { setObjectName(QLatin1String("EditFrameFieldsDialog")); setModal(true); setWindowTitle(caption); qDeleteAll(m_fieldcontrols); m_fieldcontrols.clear(); QVBoxLayout* vlayout = new QVBoxLayout(this); for (Frame::FieldList::iterator fldIt = m_fields.begin(); fldIt != m_fields.end(); ++fldIt) { Frame::Field& fld = *fldIt; switch (fld.m_value.type()) { case QVariant::Int: case QVariant::UInt: if (fld.m_id == Frame::Field::ID_TextEnc) { static const char* strlst[] = { QT_TRANSLATE_NOOP("@default", "ISO-8859-1"), QT_TRANSLATE_NOOP("@default", "UTF16"), QT_TRANSLATE_NOOP("@default", "UTF16BE"), QT_TRANSLATE_NOOP("@default", "UTF8"), NULL }; IntComboBoxControl* cbox = new IntComboBoxControl(fld, strlst); m_fieldcontrols.append(cbox); } else if (fld.m_id == Frame::Field::ID_PictureType) { static const char* strlst[] = { QT_TRANSLATE_NOOP("@default", "Other"), QT_TRANSLATE_NOOP("@default", "32x32 pixels PNG file icon"), QT_TRANSLATE_NOOP("@default", "Other file icon"), QT_TRANSLATE_NOOP("@default", "Cover (front)"), QT_TRANSLATE_NOOP("@default", "Cover (back)"), QT_TRANSLATE_NOOP("@default", "Leaflet page"), QT_TRANSLATE_NOOP("@default", "Media"), QT_TRANSLATE_NOOP("@default", "Lead artist/lead performer/soloist"), QT_TRANSLATE_NOOP("@default", "Artist/performer"), QT_TRANSLATE_NOOP("@default", "Conductor"), QT_TRANSLATE_NOOP("@default", "Band/Orchestra"), QT_TRANSLATE_NOOP("@default", "Composer"), QT_TRANSLATE_NOOP("@default", "Lyricist/text writer"), QT_TRANSLATE_NOOP("@default", "Recording Location"), QT_TRANSLATE_NOOP("@default", "During recording"), QT_TRANSLATE_NOOP("@default", "During performance"), QT_TRANSLATE_NOOP("@default", "Movie/video screen capture"), QT_TRANSLATE_NOOP("@default", "A bright coloured fish"), QT_TRANSLATE_NOOP("@default", "Illustration"), QT_TRANSLATE_NOOP("@default", "Band/artist logotype"), QT_TRANSLATE_NOOP("@default", "Publisher/Studio logotype"), NULL }; IntComboBoxControl* cbox = new IntComboBoxControl(fld, strlst); m_fieldcontrols.append(cbox); } else if (fld.m_id == Frame::Field::ID_TimestampFormat) { static const char* strlst[] = { QT_TRANSLATE_NOOP("@default", "Other"), QT_TRANSLATE_NOOP("@default", "MPEG frames as unit"), QT_TRANSLATE_NOOP("@default", "Milliseconds as unit"), NULL }; IntComboBoxControl* cbox = new IntComboBoxControl(fld, strlst); m_fieldcontrols.append(cbox); } else if (fld.m_id == Frame::Field::ID_ContentType) { static const char* strlst[] = { QT_TRANSLATE_NOOP("@default", "Other"), QT_TRANSLATE_NOOP("@default", "Lyrics"), QT_TRANSLATE_NOOP("@default", "Text transcription"), QT_TRANSLATE_NOOP("@default", "Movement/part name"), QT_TRANSLATE_NOOP("@default", "Events"), QT_TRANSLATE_NOOP("@default", "Chord"), QT_TRANSLATE_NOOP("@default", "Trivia/pop up"), NULL }; IntComboBoxControl* cbox = new IntComboBoxControl(fld, strlst); m_fieldcontrols.append(cbox); } else { IntFieldControl* intctl = new IntFieldControl(fld); m_fieldcontrols.append(intctl); } break; case QVariant::String: if (fld.m_id == Frame::Field::ID_Text) { // Large textedit for text fields TextFieldControl* textctl = new TextFieldControl(fld); m_fieldcontrols.append(textctl); } else { LineFieldControl* textctl = new LineFieldControl(fld); m_fieldcontrols.append(textctl); } break; case QVariant::ByteArray: { BinFieldControl* binctl = new BinFieldControl( m_platformTools, fld, frame, taggedFile); m_fieldcontrols.append(binctl); break; } default: qDebug("Unknown type %d in field %d", fld.m_value.type(), fld.m_id); } } QListIterator it(m_fieldcontrols); while (it.hasNext()) { vlayout->addWidget(it.next()->createWidget(this)); } QHBoxLayout* hlayout = new QHBoxLayout; QSpacerItem* hspacer = new QSpacerItem(16, 0, QSizePolicy::Expanding, QSizePolicy::Minimum); QPushButton* okButton = new QPushButton(tr("&OK"), this); QPushButton* cancelButton = new QPushButton(tr("&Cancel"), this); hlayout->addItem(hspacer); hlayout->addWidget(okButton); hlayout->addWidget(cancelButton); okButton->setDefault(true); connect(okButton, SIGNAL(clicked()), this, SLOT(accept())); connect(cancelButton, SIGNAL(clicked()), this, SLOT(reject())); vlayout->addLayout(hlayout); setMinimumWidth(525); } /** * Destructor. */ EditFrameFieldsDialog::~EditFrameFieldsDialog() { qDeleteAll(m_fieldcontrols); m_fieldcontrols.clear(); } kid3-3.0.2/src/gui/dialogs/editframefieldsdialog.h000066400000000000000000000106631224603511300220260ustar00rootroot00000000000000/** * \file editframefieldsdialog.h * Field edit dialog. * * \b Project: Kid3 * \author Urs Fleisch * \date 10 Jun 2009 * * Copyright (C) 2003-2007 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef EDITFRAMEFIELDSDIALOG_H #define EDITFRAMEFIELDSDIALOG_H #include #include #include "frame.h" #include class TaggedFile; class IPlatformTools; /** Row of buttons to load, save and view binary data */ class BinaryOpenSave : public QWidget { Q_OBJECT public: /** * Constructor. * * @param platformTools platform tools * @param parent parent widget * @param field field containing binary data */ BinaryOpenSave(IPlatformTools* platformTools, QWidget* parent, const Frame::Field& field); /** * Set label. * * @param txt label */ void setLabel(const QString& txt) { m_label->setText(txt); } /** * Check if data changed. * @return true if data changed. */ bool isChanged() const { return m_isChanged; } /** * Get binary data. * @return byte array. */ const QByteArray& getData() const { return m_byteArray; } /** * Set default directory name. * @param defaultDir default directory name */ void setDefaultDir(const QString& defaultDir) { m_defaultDir = defaultDir; } /** * Set default file name. * @param defaultFile default file name */ void setDefaultFile(const QString& defaultFile) { m_defaultFile = defaultFile; } /** * Set filter. * @param filter filter for file dialog */ void setFilter(const QString& filter) { m_filter = filter; } public slots: /** * Enable the "From Clipboard" button if the clipboard contains an image. */ void setClipButtonState(); /** * Load image from clipboard. */ void clipData(); /** * Request name of file to import binary data from. * The data is imported later when Ok is pressed in the parent dialog. */ void loadData(); /** * Request name of file and export binary data. */ void saveData(); /** * Create image from binary data and display it in window. */ void viewData(); private: IPlatformTools* m_platformTools; /** Array with binary data */ QByteArray m_byteArray; /** true if m_byteArray changed */ bool m_isChanged; /** Label left of buttons */ QLabel* m_label; /** From Clipboard button */ QPushButton* m_clipButton; /** Default directory name */ QString m_defaultDir; /** Default file name */ QString m_defaultFile; /** Filter names */ QString m_filter; }; /** Base class for field controls */ class FieldControl : public QObject { public: /** * Constructor. */ FieldControl() {} /** * Destructor. */ virtual ~FieldControl() {} /** * Update field from data in field control. */ virtual void updateTag() = 0; /** * Create widget to edit field data. * * @param parent parent widget * * @return widget to edit field data. */ virtual QWidget* createWidget(QWidget* parent) = 0; }; /** Field edit dialog */ class EditFrameFieldsDialog : public QDialog { Q_OBJECT public: /** * Constructor. * * @param platformTools platform tools * @param parent parent widget * @param caption caption * @param frame frame with fields to edit * @param taggedFile file */ EditFrameFieldsDialog(IPlatformTools* platformTools, QWidget* parent, const QString& caption, const Frame& frame, const TaggedFile* taggedFile); /** * Destructor. */ virtual ~EditFrameFieldsDialog(); /** * Update fields and get edited fields. * * @return field list. */ const Frame::FieldList& getUpdatedFieldList(); private: IPlatformTools* m_platformTools; Frame::FieldList m_fields; QList m_fieldcontrols; }; #endif // EDITFRAMEFIELDSDIALOG_H kid3-3.0.2/src/gui/dialogs/exportdialog.cpp000066400000000000000000000173771224603511300205640ustar00rootroot00000000000000/** * \file exportdialog.cpp * Export dialog. * * \b Project: Kid3 * \author Urs Fleisch * \date 10 May 2006 * * Copyright (C) 2006-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "exportdialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "taggedfile.h" #include "genres.h" #include "exportconfig.h" #include "importconfig.h" #include "contexthelp.h" #include "textexporter.h" #include "texttablemodel.h" #include "formatlistedit.h" #include "iplatformtools.h" /** * Constructor. * * @param platformTools platform tools * @param parent parent widget * @param textExporter text exporter to use */ ExportDialog::ExportDialog(IPlatformTools* platformTools, QWidget* parent, TextExporter* textExporter) : QDialog(parent), m_platformTools(platformTools), m_textExporter(textExporter), m_textTableModel(new TextTableModel(this)) { setObjectName(QLatin1String("ExportDialog")); setModal(true); setWindowTitle(tr("Export")); setSizeGripEnabled(true); QVBoxLayout* vlayout = new QVBoxLayout(this); m_edit = new QTextEdit(this); m_edit->setAcceptRichText(false); vlayout->addWidget(m_edit); m_table = new QTableView(this); m_table->setModel(m_textTableModel); m_table->hide(); vlayout->addWidget(m_table); QString formatToolTip = ImportTrackData::getFormatToolTip(); m_formatListEdit = new FormatListEdit( QStringList() << tr("Format:") << tr("Header:") << tr("Tracks:") << tr("Footer:"), QStringList() << QString() << formatToolTip << formatToolTip << formatToolTip, this); connect(m_formatListEdit, SIGNAL(formatChanged()), this, SLOT(showPreview())); vlayout->addWidget(m_formatListEdit); QHBoxLayout* butlayout = new QHBoxLayout; m_fileButton = new QPushButton(tr("To F&ile"), this); m_fileButton->setAutoDefault(false); butlayout->addWidget(m_fileButton); connect(m_fileButton, SIGNAL(clicked()), this, SLOT(slotToFile())); m_clipButton = new QPushButton(tr("To Clip&board"), this); m_clipButton->setAutoDefault(false); butlayout->addWidget(m_clipButton); connect(m_clipButton, SIGNAL(clicked()), this, SLOT(slotToClipboard())); QSpacerItem* butspacer = new QSpacerItem(16, 0, QSizePolicy::Expanding, QSizePolicy::Minimum); butlayout->addItem(butspacer); QLabel* srcLabel = new QLabel(tr("&Source:"), this); butlayout->addWidget(srcLabel); m_srcComboBox = new QComboBox(this); m_srcComboBox->setEditable(false); m_srcComboBox->addItem(tr("Tag 1"), TrackData::TagV1); m_srcComboBox->addItem(tr("Tag 2"), TrackData::TagV2); srcLabel->setBuddy(m_srcComboBox); butlayout->addWidget(m_srcComboBox); connect(m_srcComboBox, SIGNAL(activated(int)), this, SLOT(onSrcComboBoxActivated(int))); vlayout->addLayout(butlayout); QHBoxLayout* hlayout = new QHBoxLayout; QPushButton* helpButton = new QPushButton(tr("&Help"), this); helpButton->setAutoDefault(false); hlayout->addWidget(helpButton); connect(helpButton, SIGNAL(clicked()), this, SLOT(showHelp())); QPushButton* saveButton = new QPushButton(tr("&Save Settings"), this); saveButton->setAutoDefault(false); hlayout->addWidget(saveButton); connect(saveButton, SIGNAL(clicked()), this, SLOT(saveConfig())); QSpacerItem* hspacer = new QSpacerItem(16, 0, QSizePolicy::Expanding, QSizePolicy::Minimum); hlayout->addItem(hspacer); QPushButton* closeButton = new QPushButton(tr("&Close"), this); closeButton->setAutoDefault(false); hlayout->addWidget(closeButton); connect(closeButton, SIGNAL(clicked()), this, SLOT(accept())); vlayout->addLayout(hlayout); } /** * Destructor. */ ExportDialog::~ExportDialog() { } /** * Export to a file. */ void ExportDialog::slotToFile() { QString fileName = m_platformTools->getSaveFileName(this, QString(), ImportConfig::instance().m_importDir, QString(), 0); if (!fileName.isEmpty()) { if (!m_textExporter->exportToFile(fileName)) { QMessageBox::warning( 0, tr("File Error"), tr("Error while writing file:\n") + fileName, QMessageBox::Ok, Qt::NoButton); } } } /** * Export to clipboard. */ void ExportDialog::slotToClipboard() { m_textExporter->exportToClipboard(); } /** * Show exported text as preview in editor. */ void ExportDialog::showPreview() { m_textExporter->updateText(m_formatListEdit->getCurrentFormat(1), m_formatListEdit->getCurrentFormat(2), m_formatListEdit->getCurrentFormat(3)); QString text(m_textExporter->getText()); if (m_textTableModel->setText( text, !m_formatListEdit->getCurrentFormat(1).isEmpty())) { m_table->resizeColumnsToContents(); m_table->show(); m_edit->hide(); } else { m_edit->setPlainText(text); m_table->hide(); m_edit->show(); } } /** * Set the format combo box and line edits from the configuration. */ void ExportDialog::setFormatFromConfig() { m_formatListEdit->setFormats( QList() << ExportConfig::instance().m_exportFormatNames << ExportConfig::instance().m_exportFormatHeaders << ExportConfig::instance().m_exportFormatTracks << ExportConfig::instance().m_exportFormatTrailers, ExportConfig::instance().m_exportFormatIdx); } /** * Read the local settings from the configuration. */ void ExportDialog::readConfig() { m_srcComboBox->setCurrentIndex( m_srcComboBox->findData(ExportConfig::instance().m_exportSrcV1)); setFormatFromConfig(); if (!ExportConfig::instance().m_exportWindowGeometry.isEmpty()) { restoreGeometry(ExportConfig::instance().m_exportWindowGeometry); } } /** * Save the local settings to the configuration. */ void ExportDialog::saveConfig() { ExportConfig::instance().m_exportSrcV1 = TrackData::tagVersionCast( m_srcComboBox->itemData(m_srcComboBox->currentIndex()).toInt()); QList formats = m_formatListEdit->getFormats( &ExportConfig::instance().m_exportFormatIdx); ExportConfig::instance().m_exportFormatNames = formats.at(0); ExportConfig::instance().m_exportFormatHeaders = formats.at(1); ExportConfig::instance().m_exportFormatTracks = formats.at(2); ExportConfig::instance().m_exportFormatTrailers = formats.at(3); ExportConfig::instance().m_exportWindowGeometry = saveGeometry(); setFormatFromConfig(); } /** * Show help. */ void ExportDialog::showHelp() { ContextHelp::displayHelp(QLatin1String("export")); } /** * Called when the source combo box selection is changed. * @param index combo box index */ void ExportDialog::onSrcComboBoxActivated(int index) { m_textExporter->readTagsInTrackData( TrackData::tagVersionCast(m_srcComboBox->itemData(index).toInt())); showPreview(); } kid3-3.0.2/src/gui/dialogs/exportdialog.h000066400000000000000000000052211224603511300202120ustar00rootroot00000000000000/** * \file exportdialog.h * Export dialog. * * \b Project: Kid3 * \author Urs Fleisch * \date 10 May 2006 * * Copyright (C) 2006-2008 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef EXPORTDIALOG_H #define EXPORTDIALOG_H #include #include #include "trackdata.h" class IPlatformTools; class QTextEdit; class QTableView; class QPushButton; class QComboBox; class TextExporter; class TextTableModel; class FormatListEdit; /** * Export dialog. */ class ExportDialog : public QDialog { Q_OBJECT public: /** * Constructor. * * @param platformTools platform tools * @param parent parent widget * @param textExporter text exporter to use */ ExportDialog(IPlatformTools* platformTools, QWidget* parent, TextExporter* textExporter); /** * Destructor. */ ~ExportDialog(); /** * Read the local settings from the configuration. */ void readConfig(); public slots: /** * Show exported text as preview in editor. */ void showPreview(); private slots: /** * Export to a file. */ void slotToFile(); /** * Export to clipboard. */ void slotToClipboard(); /** * Save the local settings to the configuration. */ void saveConfig(); /** * Show help. */ void showHelp(); /** * Called when the source combo box selection is changed. * @param index combo box index */ void onSrcComboBoxActivated(int index); private: /** * Set the format combo box and line edits from the configuration. */ void setFormatFromConfig(); IPlatformTools* m_platformTools; /** Text editor */ QTextEdit* m_edit; /** Table view */ QTableView* m_table; /** Format editor */ FormatListEdit* m_formatListEdit; /** To File button */ QPushButton* m_fileButton; /** To Clipboard button */ QPushButton* m_clipButton; /** combobox with export sources */ QComboBox* m_srcComboBox; /** text exporter */ TextExporter* m_textExporter; /** text table model */ TextTableModel* m_textTableModel; }; #endif kid3-3.0.2/src/gui/dialogs/filterdialog.cpp000066400000000000000000000130441224603511300205130ustar00rootroot00000000000000/** * \file filterdialog.cpp * Filter dialog. * * \b Project: Kid3 * \author Urs Fleisch * \date 16 Jan 2008 * * Copyright (C) 2008-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "filterdialog.h" #include #include #include #include #include #include #include #include "filterconfig.h" #include "contexthelp.h" #include "formatlistedit.h" /** * Constructor. * * @param parent parent widget */ FilterDialog::FilterDialog(QWidget* parent) : QDialog(parent), m_isAbortButton(false) { setObjectName(QLatin1String("FilterDialog")); setWindowTitle(tr("Filter")); setSizeGripEnabled(true); QVBoxLayout* vlayout = new QVBoxLayout(this); m_edit = new QTextEdit(this); m_edit->setReadOnly(true); m_edit->setTabStopWidth(20); m_edit->setAcceptRichText(false); vlayout->addWidget(m_edit); m_formatListEdit = new FormatListEdit( QStringList() << tr("&Filter:") << tr("&Expression:"), QStringList() << QString() << FileFilter::getFormatToolTip(), this); vlayout->addWidget(m_formatListEdit); QHBoxLayout* hlayout = new QHBoxLayout; QPushButton* helpButton = new QPushButton(tr("&Help"), this); helpButton->setAutoDefault(false); hlayout->addWidget(helpButton); connect(helpButton, SIGNAL(clicked()), this, SLOT(showHelp())); QPushButton* saveButton = new QPushButton(tr("&Save Settings"), this); saveButton->setAutoDefault(false); hlayout->addWidget(saveButton); connect(saveButton, SIGNAL(clicked()), this, SLOT(saveConfig())); QSpacerItem* hspacer = new QSpacerItem(16, 0, QSizePolicy::Expanding, QSizePolicy::Minimum); hlayout->addItem(hspacer); m_applyButton = new QPushButton(this); setAbortButton(false); QPushButton* closeButton = new QPushButton(tr("&Close"), this); m_applyButton->setAutoDefault(false); closeButton->setAutoDefault(false); hlayout->addWidget(m_applyButton); hlayout->addWidget(closeButton); connect(m_applyButton, SIGNAL(clicked()), this, SLOT(applyOrAbortFilter())); connect(closeButton, SIGNAL(clicked()), this, SLOT(reject())); connect(this, SIGNAL(rejected()), &m_fileFilter, SLOT(abort())); vlayout->addLayout(hlayout); } /** * Destructor. */ FilterDialog::~FilterDialog() {} /** * Apply or abort filter. */ void FilterDialog::applyOrAbortFilter() { if (m_isAbortButton) { m_fileFilter.abort(); } else { m_edit->clear(); m_fileFilter.setFilterExpression(m_formatListEdit->getCurrentFormat(1)); m_fileFilter.initParser(); emit apply(m_fileFilter); } } /** * Set the filter combo box and line edit from the configuration. */ void FilterDialog::setFiltersFromConfig() { m_formatListEdit->setFormats( QList() << FilterConfig::instance().m_filterNames << FilterConfig::instance().m_filterExpressions, FilterConfig::instance().m_filterIdx); } /** * Read the local settings from the configuration. */ void FilterDialog::readConfig() { m_fileFilter.clearAborted(); m_edit->clear(); setAbortButton(false); setFiltersFromConfig(); if (!FilterConfig::instance().m_windowGeometry.isEmpty()) { restoreGeometry(FilterConfig::instance().m_windowGeometry); } } /** * Save the local settings to the configuration. */ void FilterDialog::saveConfig() { QList formats = m_formatListEdit->getFormats( &FilterConfig::instance().m_filterIdx); FilterConfig::instance().m_filterNames = formats.at(0); FilterConfig::instance().m_filterExpressions = formats.at(1); FilterConfig::instance().m_windowGeometry = saveGeometry(); setFiltersFromConfig(); } /** * Show help. */ void FilterDialog::showHelp() { ContextHelp::displayHelp(QLatin1String("filter")); } /** * Show information about filter event. */ void FilterDialog::showFilterEvent(FileFilter::FilterEventType type, const QString& fileName) { switch (type) { case FileFilter::Started: m_edit->append(tr("Started")); setAbortButton(true); break; case FileFilter::Directory: m_edit->append(QLatin1Char('\t') + fileName); break; case FileFilter::ParseError: m_edit->append(QLatin1String("parse error")); break; case FileFilter::FilePassed: m_edit->append(QLatin1String("+\t") + fileName); break; case FileFilter::FileFilteredOut: m_edit->append(QLatin1String("-\t") + fileName); break; case FileFilter::Finished: m_edit->append(tr("Finished")); setAbortButton(false); break; case FileFilter::Aborted: m_edit->append(tr("Aborted")); setAbortButton(false); break; } } /** * Set button to Start or Abort. * @param enableAbort true to set Abort button */ void FilterDialog::setAbortButton(bool enableAbort) { m_isAbortButton = enableAbort; m_applyButton->setText(m_isAbortButton ? tr("A&bort") : tr("&Apply")); } kid3-3.0.2/src/gui/dialogs/filterdialog.h000066400000000000000000000050551224603511300201630ustar00rootroot00000000000000/** * \file filterdialog.h * Filter dialog. * * \b Project: Kid3 * \author Urs Fleisch * \date 16 Jan 2008 * * Copyright (C) 2008 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef FILTERDIALOG_H #define FILTERDIALOG_H #include #include #include "filefilter.h" class QPushButton; class FormatListEdit; /** * Filter dialog. */ class FilterDialog : public QDialog { Q_OBJECT public: /** * Constructor. * * @param parent parent widget */ explicit FilterDialog(QWidget* parent); /** * Destructor. */ ~FilterDialog(); /** * Read the local settings from the configuration. */ void readConfig(); /** * Display information in text view. * * @param text text to display */ void showInformation(const QString& text) { m_edit->append(text); } signals: /** * Is triggered when the selected @a filter has to be applied. */ void apply(FileFilter&); public slots: /** * Show information about filter event. * * @param type filter event type * @param fileName name of filtered file */ void showFilterEvent(FileFilter::FilterEventType type, const QString& fileName); private slots: /** * Save the local settings to the configuration. */ void saveConfig(); /** * Show help. */ void showHelp(); /** * Apply or abort filter. */ void applyOrAbortFilter(); private: /** * Set the filter combo box and line edit from the configuration. */ void setFiltersFromConfig(); /** * Set button to Apply or Abort. * @param enableAbort true to set Abort button */ void setAbortButton(bool enableAbort); /** Text editor */ QTextEdit* m_edit; /** format editor */ FormatListEdit* m_formatListEdit; /** Apply button */ QPushButton* m_applyButton; /** file filter used */ FileFilter m_fileFilter; /** true if m_applyButton is an Abort button */ bool m_isAbortButton; }; #endif kid3-3.0.2/src/gui/dialogs/importdialog.cpp000066400000000000000000000507721224603511300205510ustar00rootroot00000000000000/** * \file importdialog.cpp * Import dialog. * * \b Project: Kid3 * \author Urs Fleisch * \date 17 Sep 2003 * * Copyright (C) 2003-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "importdialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "config.h" #include "genres.h" #include "serverimporter.h" #include "servertrackimporter.h" #include "serverimportdialog.h" #include "servertrackimportdialog.h" #include "textimportdialog.h" #include "tagimportdialog.h" #include "contexthelp.h" #include "taggedfile.h" #include "trackdata.h" #include "trackdatamodel.h" #include "frametablemodel.h" #include "trackdatamatcher.h" #include "qtcompatmac.h" #include "iplatformtools.h" namespace { /** * Get list of frame types whose visibility can be changed using a context menu. * @return list of frame types of Frame::Type or * TrackDataModel::TrackProperties. */ QList checkableFrameTypes() { return QList() << TrackDataModel::FT_FileName << TrackDataModel::FT_FilePath; } } /** * Constructor. * * @param platformTools platform tools * @param parent parent widget * @param caption dialog title * @param trackDataModel track data to be filled with imported values, * is passed with durations of files set * @param importers server importers * @param trackImporters server track importers */ ImportDialog::ImportDialog(IPlatformTools* platformTools, QWidget* parent, QString& caption, TrackDataModel* trackDataModel, const QList& importers, const QList& trackImporters) : QDialog(parent), m_platformTools(platformTools), m_autoStartSubDialog(-1), m_columnVisibility(0ULL), m_trackDataModel(trackDataModel), m_importers(importers), m_trackImporters(trackImporters) { setObjectName(QLatin1String("ImportDialog")); setModal(false); setWindowTitle(caption); setSizeGripEnabled(true); m_serverImportDialog = 0; m_textImportDialog = 0; m_tagImportDialog = 0; m_serverTrackImportDialog = 0; QVBoxLayout* vlayout = new QVBoxLayout(this); m_trackDataTable = new QTableView(this); m_trackDataTable->setModel(m_trackDataModel); m_trackDataTable->resizeColumnsToContents(); m_trackDataTable->setItemDelegateForColumn( m_trackDataModel->columnForFrameType(Frame::FT_Genre), new FrameItemDelegate(this)); #if QT_VERSION >= 0x050000 m_trackDataTable->verticalHeader()->setSectionsMovable(true); m_trackDataTable->horizontalHeader()->setSectionsMovable(true); #else m_trackDataTable->verticalHeader()->setMovable(true); m_trackDataTable->horizontalHeader()->setMovable(true); #endif m_trackDataTable->horizontalHeader()->setContextMenuPolicy( Qt::CustomContextMenu); connect(m_trackDataTable->verticalHeader(), SIGNAL(sectionMoved(int,int,int)), this, SLOT(moveTableRow(int,int,int))); connect(m_trackDataTable->horizontalHeader(), SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showTableHeaderContextMenu(QPoint))); vlayout->addWidget(m_trackDataTable); QHBoxLayout* accuracyLayout = new QHBoxLayout; QLabel* accuracyLabel = new QLabel(tr("Accuracy:")); accuracyLayout->addWidget(accuracyLabel); m_accuracyPercentLabel = new QLabel(QLatin1String("-")); m_accuracyPercentLabel->setMinimumWidth( m_accuracyPercentLabel->fontMetrics().width(QLatin1String("100%"))); accuracyLayout->addWidget(m_accuracyPercentLabel); QLabel* coverArtLabel = new QLabel(tr("Cover Art:")); accuracyLayout->addWidget(coverArtLabel); m_coverArtUrlLabel = new QLabel(QLatin1String(" -")); accuracyLayout->addWidget(m_coverArtUrlLabel); accuracyLayout->addStretch(); vlayout->addLayout(accuracyLayout); QHBoxLayout* butlayout = new QHBoxLayout; QPushButton* fileButton = new QPushButton(tr("From F&ile/Clipboard...")); fileButton->setAutoDefault(false); butlayout->addWidget(fileButton); QPushButton* tagsButton = new QPushButton(tr("From T&ags...")); tagsButton->setAutoDefault(false); butlayout->addWidget(tagsButton); QPushButton* serverButton = new QPushButton(tr("&From Server:")); serverButton->setAutoDefault(false); butlayout->addWidget(serverButton); m_serverComboBox = new QComboBox; m_serverComboBox->setEditable(false); foreach (const ServerImporter* si, m_importers) { m_serverComboBox->addItem(QCoreApplication::translate("@default", si->name())); } foreach (const ServerTrackImporter* si, m_trackImporters) { m_serverComboBox->addItem(QCoreApplication::translate("@default", si->name())); } butlayout->addWidget(m_serverComboBox); if (m_serverComboBox->count() == 0) { serverButton->hide(); m_serverComboBox->hide(); } QSpacerItem* butspacer = new QSpacerItem(16, 0, QSizePolicy::Expanding, QSizePolicy::Minimum); butlayout->addItem(butspacer); QLabel* destLabel = new QLabel; destLabel->setText(tr("D&estination:")); butlayout->addWidget(destLabel); m_destComboBox = new QComboBox; m_destComboBox->setEditable(false); m_destComboBox->addItem(tr("Tag 1"), TrackData::TagV1); m_destComboBox->addItem(tr("Tag 2"), TrackData::TagV2); m_destComboBox->addItem(tr("Tag 1 and Tag 2"), TrackData::TagV2V1); destLabel->setBuddy(m_destComboBox); butlayout->addWidget(m_destComboBox); QToolButton* revertButton = new QToolButton; revertButton->setIcon( m_platformTools->iconFromTheme(QLatin1String("document-revert"))); revertButton->setToolTip(tr("Revert")); connect(revertButton, SIGNAL(clicked()), this, SLOT(changeTagDestination())); butlayout->addWidget(revertButton); vlayout->addLayout(butlayout); QHBoxLayout* matchLayout = new QHBoxLayout; m_mismatchCheckBox = new QCheckBox( tr("Check maximum allowable time &difference (sec):")); matchLayout->addWidget(m_mismatchCheckBox); m_maxDiffSpinBox = new QSpinBox; m_maxDiffSpinBox->setMaximum(9999); matchLayout->addWidget(m_maxDiffSpinBox); QSpacerItem* matchSpacer = new QSpacerItem(16, 0, QSizePolicy::Expanding, QSizePolicy::Minimum); matchLayout->addItem(matchSpacer); QLabel* matchLabel = new QLabel(tr("Match with:")); matchLayout->addWidget(matchLabel); QPushButton* lengthButton = new QPushButton(tr("&Length")); lengthButton->setAutoDefault(false); matchLayout->addWidget(lengthButton); QPushButton* trackButton = new QPushButton(tr("T&rack")); trackButton->setAutoDefault(false); matchLayout->addWidget(trackButton); QPushButton* titleButton = new QPushButton(tr("&Title")); titleButton->setAutoDefault(false); matchLayout->addWidget(titleButton); vlayout->addLayout(matchLayout); connect(fileButton, SIGNAL(clicked()), this, SLOT(fromText())); connect(tagsButton, SIGNAL(clicked()), this, SLOT(fromTags())); connect(serverButton, SIGNAL(clicked()), this, SLOT(fromServer())); connect(m_serverComboBox, SIGNAL(activated(int)), this, SLOT(fromServer())); connect(lengthButton, SIGNAL(clicked()), this, SLOT(matchWithLength())); connect(trackButton, SIGNAL(clicked()), this, SLOT(matchWithTrack())); connect(titleButton, SIGNAL(clicked()), this, SLOT(matchWithTitle())); connect(m_mismatchCheckBox, SIGNAL(toggled(bool)), this, SLOT(showPreview())); connect(m_maxDiffSpinBox, SIGNAL(valueChanged(int)), this, SLOT(maxDiffChanged())); connect(this, SIGNAL(finished(int)), this, SLOT(hideSubdialogs())); QHBoxLayout* hlayout = new QHBoxLayout; QSpacerItem* hspacer = new QSpacerItem(16, 0, QSizePolicy::Expanding, QSizePolicy::Minimum); QPushButton* helpButton = new QPushButton(tr("&Help"), this); helpButton->setAutoDefault(false); QPushButton* saveButton = new QPushButton(tr("&Save Settings"), this); saveButton->setAutoDefault(false); QPushButton* okButton = new QPushButton(tr("&OK"), this); okButton->setAutoDefault(false); QPushButton* cancelButton = new QPushButton(tr("&Cancel"), this); cancelButton->setAutoDefault(false); hlayout->addWidget(helpButton); hlayout->addWidget(saveButton); hlayout->addItem(hspacer); hlayout->addWidget(okButton); hlayout->addWidget(cancelButton); connect(helpButton, SIGNAL(clicked()), this, SLOT(showHelp())); connect(saveButton, SIGNAL(clicked()), this, SLOT(saveConfig())); connect(okButton, SIGNAL(clicked()), this, SLOT(accept())); connect(cancelButton, SIGNAL(clicked()), this, SLOT(reject())); vlayout->addLayout(hlayout); } /** * Destructor. */ ImportDialog::~ImportDialog() { delete m_textImportDialog; delete m_tagImportDialog; delete m_serverImportDialog; delete m_serverTrackImportDialog; } /** * Import from server and preview in table. */ void ImportDialog::fromServer() { if (m_serverComboBox) displayServerImportDialog(m_serverComboBox->currentIndex()); } /** * Import from text. */ void ImportDialog::fromText() { if (!m_textImportDialog) { m_textImportDialog = new TextImportDialog( m_platformTools, this, m_trackDataModel); connect(m_textImportDialog, SIGNAL(trackDataUpdated()), this, SLOT(showPreview())); } m_textImportDialog->clear(); m_textImportDialog->show(); } /** * Import from tags. */ void ImportDialog::fromTags() { if (!m_tagImportDialog) { m_tagImportDialog = new TagImportDialog(this, m_trackDataModel); connect(m_tagImportDialog, SIGNAL(trackDataUpdated()), this, SLOT(showPreview())); } m_tagImportDialog->clear(); m_tagImportDialog->show(); } /** * Display server import dialog. * * @param importerIdx importer index, if invalid but not negative the * MusicBrainz Fingerprint dialog is displayed */ void ImportDialog::displayServerImportDialog(int importerIdx) { if (importerIdx >= 0) { if (importerIdx < m_importers.size()) { displayServerImportDialog(m_importers.at(importerIdx)); } else if (importerIdx - m_importers.size() < m_trackImporters.size()) { displayServerTrackImportDialog( m_trackImporters.at(importerIdx - m_importers.size())); } } } /** * Display server import dialog. * * @param source import source */ void ImportDialog::displayServerImportDialog(ServerImporter* source) { if (!m_serverImportDialog) { m_serverImportDialog = new ServerImportDialog(this); connect(m_serverImportDialog, SIGNAL(trackDataUpdated()), this, SLOT(showPreview())); connect(m_serverImportDialog, SIGNAL(accepted()), this, SLOT(onServerImportDialogClosed())); } m_serverImportDialog->setImportSource(source); m_serverImportDialog->setArtistAlbum( m_trackDataModel->trackData().getArtist(), m_trackDataModel->trackData().getAlbum()); m_serverImportDialog->show(); } /** * Import from track server and preview in table. * * @param source import source */ void ImportDialog::displayServerTrackImportDialog(ServerTrackImporter* source) { if (!m_serverTrackImportDialog) { m_serverTrackImportDialog = new ServerTrackImportDialog(this, m_trackDataModel); connect(m_serverTrackImportDialog, SIGNAL(trackDataUpdated()), this, SLOT(showPreview())); } m_serverTrackImportDialog->setImportSource(source); m_serverTrackImportDialog->initTable(); m_serverTrackImportDialog->exec(); } /** * Hide subdialogs. */ void ImportDialog::hideSubdialogs() { if (m_serverImportDialog) m_serverImportDialog->hide(); if (m_textImportDialog) m_textImportDialog->hide(); if (m_tagImportDialog) m_tagImportDialog->hide(); } /** * Shows the dialog as a modeless dialog. * * @param importerIndex index of importer to use, -1 for none */ void ImportDialog::showWithSubDialog(int importerIndex) { m_autoStartSubDialog = importerIndex; if (importerIndex >= 0 && importerIndex < m_serverComboBox->count()) { m_serverComboBox->setCurrentIndex(importerIndex); } show(); if (m_autoStartSubDialog >= 0) { displayServerImportDialog(m_autoStartSubDialog); } } /** * Clear dialog data. */ void ImportDialog::clear() { m_serverComboBox->setCurrentIndex(ImportConfig::instance().m_importServer); TrackData::TagVersion importDest = ImportConfig::instance().m_importDest; int index = m_destComboBox->findData(importDest); m_destComboBox->setCurrentIndex(index); if (importDest == TrackData::TagV1 && !m_trackDataModel->trackData().isTagV1Supported()) { index = m_destComboBox->findData(TrackData::TagV2); m_destComboBox->setCurrentIndex(index); changeTagDestination(); } m_mismatchCheckBox->setChecked(ImportConfig::instance().m_enableTimeDifferenceCheck); m_maxDiffSpinBox->setValue(ImportConfig::instance().m_maxTimeDifference); m_columnVisibility = ImportConfig::instance().m_importVisibleColumns; foreach (int frameType, checkableFrameTypes()) { if (frameType < 64) { int column = m_trackDataModel->columnForFrameType(frameType); if (column != -1) { m_trackDataTable->setColumnHidden( column, (m_columnVisibility & (1ULL << frameType)) == 0ULL); } } } if (!ImportConfig::instance().m_importWindowGeometry.isEmpty()) { restoreGeometry(ImportConfig::instance().m_importWindowGeometry); } showPreview(); } /** * Show fields to import in text as preview in table. */ void ImportDialog::showPreview() { // make time difference check bool diffCheckEnable; int maxDiff; getTimeDifferenceCheck(diffCheckEnable, maxDiff); m_trackDataModel->setTimeDifferenceCheck(diffCheckEnable, maxDiff); m_trackDataTable->scrollToTop(); m_trackDataTable->resizeColumnsToContents(); m_trackDataTable->resizeRowsToContents(); int accuracy = m_trackDataModel->calculateAccuracy(); m_accuracyPercentLabel->setText(accuracy >= 0 && accuracy <= 100 ? QString::number(accuracy) + QLatin1Char('%') : QLatin1String("-")); QString coverArtUrl = m_trackDataModel->getTrackData().getCoverArtUrl(); m_coverArtUrlLabel->setText(coverArtUrl.isEmpty() ? QLatin1String("-") : coverArtUrl); } /** * Called when server import dialog is closed. */ void ImportDialog::onServerImportDialogClosed() { // This is used to prevent that the import dialog is brought behind the // main window when the server import dialog is closed, which happened // with Qt 5 on Mac OS X. show(); raise(); activateWindow(); } /** * Get import destination. * * @return TagV1, TagV2 or TagV2V1 for ID3v1, ID3v2 or both. */ TrackData::TagVersion ImportDialog::getDestination() const { return TrackData::tagVersionCast( m_destComboBox->itemData(m_destComboBox->currentIndex()).toInt()); } /** * Show help. */ void ImportDialog::showHelp() { ContextHelp::displayHelp(QLatin1String("import")); } /** * Save the local settings to the configuration. */ void ImportDialog::saveConfig() { ImportConfig::instance().m_importDest = TrackData::tagVersionCast( m_destComboBox->itemData(m_destComboBox->currentIndex()).toInt()); ImportConfig::instance().m_importServer = m_serverComboBox->currentIndex(); getTimeDifferenceCheck(ImportConfig::instance().m_enableTimeDifferenceCheck, ImportConfig::instance().m_maxTimeDifference); ImportConfig::instance().m_importVisibleColumns = m_columnVisibility; ImportConfig::instance().m_importWindowGeometry = saveGeometry(); } /** * Get time difference check configuration. * * @param enable true if check is enabled * @param maxDiff maximum allowed time difference */ void ImportDialog::getTimeDifferenceCheck(bool& enable, int& maxDiff) const { enable = m_mismatchCheckBox->isChecked(); maxDiff = m_maxDiffSpinBox->value(); } /** * Called when the maximum time difference value is changed. */ void ImportDialog::maxDiffChanged() { if (m_mismatchCheckBox->isChecked()) { showPreview(); } } /** * Move a table row. * * The first parameter @a section is not used. * @param fromIndex index of position moved from * @param fromIndex index of position moved to */ void ImportDialog::moveTableRow(int, int fromIndex, int toIndex) { QHeaderView* vHeader = qobject_cast(sender()); if (vHeader) { // revert movement, but avoid recursion disconnect(vHeader, SIGNAL(sectionMoved(int,int,int)), 0, 0); vHeader->moveSection(toIndex, fromIndex); connect(vHeader, SIGNAL(sectionMoved(int,int,int)), this, SLOT(moveTableRow(int,int,int))); } ImportTrackDataVector trackDataVector(m_trackDataModel->getTrackData()); int numTracks = static_cast(trackDataVector.size()); if (fromIndex < numTracks && toIndex < numTracks) { // swap elements but keep file durations and names ImportTrackData fromData(trackDataVector[fromIndex]); ImportTrackData toData(trackDataVector[toIndex]); trackDataVector[fromIndex].setFrameCollection(toData.getFrameCollection()); trackDataVector[toIndex].setFrameCollection(fromData.getFrameCollection()); trackDataVector[fromIndex].setImportDuration(toData.getImportDuration()); trackDataVector[toIndex].setImportDuration(fromData.getImportDuration()); m_trackDataModel->setTrackData(trackDataVector); // redisplay the table showPreview(); } } /** * Called when the destination combo box value is changed. */ void ImportDialog::changeTagDestination() { ImportTrackDataVector trackDataVector(m_trackDataModel->getTrackData()); trackDataVector.readTags(getDestination()); m_trackDataModel->setTrackData(trackDataVector); showPreview(); } /** * Match import data with length. */ void ImportDialog::matchWithLength() { bool diffCheckEnable; int maxDiff; getTimeDifferenceCheck(diffCheckEnable, maxDiff); if (TrackDataMatcher::matchWithLength(m_trackDataModel, diffCheckEnable, maxDiff)) showPreview(); } /** * Match import data with track number. */ void ImportDialog::matchWithTrack() { if (TrackDataMatcher::matchWithTrack(m_trackDataModel)) showPreview(); } /** * Match import data with title. */ void ImportDialog::matchWithTitle() { if (TrackDataMatcher::matchWithTitle(m_trackDataModel)) showPreview(); } /** * Display custom context menu for horizontal table header. * * @param pos position where context menu is drawn on screen */ void ImportDialog::showTableHeaderContextMenu(const QPoint& pos) { if (QWidget* widget = qobject_cast(sender())) { QMenu menu(widget); foreach (int frameType, checkableFrameTypes()) { int column = m_trackDataModel->columnForFrameType(frameType); if (column != -1) { QAction* action = new QAction(&menu); action->setText( m_trackDataModel->headerData(column, Qt::Horizontal).toString()); action->setData(frameType); action->setCheckable(true); action->setChecked((m_columnVisibility & (1ULL << frameType)) != 0ULL); connect(action, SIGNAL(triggered(bool)), this, SLOT(toggleTableColumnVisibility(bool))); menu.addAction(action); } } menu.setMouseTracking(true); menu.exec(widget->mapToGlobal(pos)); } } /** * Toggle visibility of table column. * * @param visible true to make column visible */ void ImportDialog::toggleTableColumnVisibility(bool visible) { if (QAction* action = qobject_cast(sender())) { bool ok; int frameType = action->data().toInt(&ok); if (ok && frameType < 64) { if (visible) { m_columnVisibility |= 1ULL << frameType; } else { m_columnVisibility &= ~(1ULL << frameType); } int column = m_trackDataModel->columnForFrameType(frameType); if (column != -1) { m_trackDataTable->setColumnHidden(column, !visible); } } if (visible) { m_trackDataTable->resizeColumnsToContents(); } } } kid3-3.0.2/src/gui/dialogs/importdialog.h000066400000000000000000000136661224603511300202170ustar00rootroot00000000000000/** * \file importdialog.h * Import dialog. * * \b Project: Kid3 * \author Urs Fleisch * \date 17 Sep 2003 * * Copyright (C) 2003-2012 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef IMPORTDIALOG_H #define IMPORTDIALOG_H #include #include "config.h" #include "importconfig.h" class IPlatformTools; class QString; class QPushButton; class QComboBox; class QLineEdit; class QCheckBox; class QSpinBox; class QTableView; class QLabel; class TrackDataModel; class ServerTrackImportDialog; class ServerImporter; class ServerImportDialog; class TextImportDialog; class TagImportDialog; class ImportTrackDataVector; class FrameCollection; class FreedbConfig; class ServerTrackImporter; /** * Import dialog. */ class ImportDialog : public QDialog { Q_OBJECT public: /** * Constructor. * * @param platformTools platform tools * @param parent parent widget * @param caption dialog title * @param trackDataModel track data to be filled with imported values, * is passed with durations of files set * @param importers server importers * @param trackImporters server track importers */ ImportDialog(IPlatformTools* platformTools, QWidget* parent, QString& caption, TrackDataModel* trackDataModel, const QList& importers, const QList& trackImporters); /** * Destructor. */ ~ImportDialog(); /** * Shows the dialog as a modeless dialog. * * @param importerIndex index of importer to use, -1 for none */ void showWithSubDialog(int importerIndex); /** * Clear dialog data. */ void clear(); /** * Get import destination. * * @return TagV1, TagV2 or TagV2V1 for ID3v1, ID3v2 or both. */ TrackData::TagVersion getDestination() const; private slots: /** * Show help. */ void showHelp(); /** * Save the local settings to the configuration. */ void saveConfig(); /** * Called when the maximum time difference value is changed. */ void maxDiffChanged(); /** * Move a table row. * * The first parameter @a section is not used. * @param fromIndex index of position moved from * @param toIndex index of position moved to */ void moveTableRow(int, int fromIndex, int toIndex); /** * Import from server and preview in table. */ void fromServer(); /** * Import from text. */ void fromText(); /** * Import from tags. */ void fromTags(); /** * Show fields to import in text as preview in table. */ void showPreview(); /** * Called when server import dialog is closed. */ void onServerImportDialogClosed(); /** * Match import data with length. */ void matchWithLength(); /** * Match import data with track number. */ void matchWithTrack(); /** * Match import data with title. */ void matchWithTitle(); /** * Hide subdialogs. */ void hideSubdialogs(); /** * Called when the destination combo box value is changed. */ void changeTagDestination(); /** * Display custom context menu for horizontal table header. * * @param pos position where context menu is drawn on screen */ void showTableHeaderContextMenu(const QPoint& pos); /** * Toggle visibility of table column. * * @param visible true to make column visible */ void toggleTableColumnVisibility(bool visible); private: /** * Get time difference check configuration. * * @param enable true if check is enabled * @param maxDiff maximum allowed time difference */ void getTimeDifferenceCheck(bool& enable, int& maxDiff) const; /** * Display server import dialog. * * @param importerIdx importer index, if invalid but not negative the * MusicBrainz Fingerprint dialog is displayed */ void displayServerImportDialog(int importerIdx); /** * Display server import dialog. * * @param source import source */ void displayServerImportDialog(ServerImporter* source); /** * Import from track server and preview in table. * * @param source import source */ void displayServerTrackImportDialog(ServerTrackImporter* source); IPlatformTools* m_platformTools; /** Index of importer for subdialog to open when starting, -1 for none */ int m_autoStartSubDialog; /** Mask for visibility of optional columns */ quint64 m_columnVisibility; /** Preview table */ QTableView* m_trackDataTable; /** Track data model */ TrackDataModel* m_trackDataModel; /** Accuracy value */ QLabel* m_accuracyPercentLabel; /** URL of cover art to be imported */ QLabel* m_coverArtUrlLabel; /** combobox with import servers */ QComboBox* m_serverComboBox; /** combobox with import destinations */ QComboBox* m_destComboBox; QCheckBox* m_mismatchCheckBox; QSpinBox* m_maxDiffSpinBox; /** importers for different servers */ QList m_importers; /** track importers for differen servers */ QList m_trackImporters; /** Server track import dialog */ ServerTrackImportDialog* m_serverTrackImportDialog; /** Server import dialog */ ServerImportDialog* m_serverImportDialog; /** Text import dialog */ TextImportDialog* m_textImportDialog; /** Tag import dialog */ TagImportDialog* m_tagImportDialog; }; #endif kid3-3.0.2/src/gui/dialogs/numbertracksdialog.cpp000066400000000000000000000130331224603511300217240ustar00rootroot00000000000000/** * \file numbertracksdialog.cpp * Number tracks dialog. * * \b Project: Kid3 * \author Urs Fleisch * \date 25 May 2006 * * Copyright (C) 2006-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "numbertracksdialog.h" #include #include #include #include #include #include #include #include #include #include "numbertracksconfig.h" #include "contexthelp.h" /** * Constructor. * * @param parent parent widget */ NumberTracksDialog::NumberTracksDialog(QWidget* parent) : QDialog(parent) { setObjectName(QLatin1String("NumberTracksDialog")); setModal(true); setWindowTitle(tr("Number Tracks")); QVBoxLayout* vlayout = new QVBoxLayout(this); QHBoxLayout* trackLayout = new QHBoxLayout; QLabel* trackLabel = new QLabel(tr("&Start number:"), this); m_trackSpinBox = new QSpinBox(this); m_trackSpinBox->setMaximum(9999); m_trackSpinBox->setValue(NumberTracksConfig::instance().m_numberTracksStart); trackLayout->addWidget(trackLabel); trackLayout->addWidget(m_trackSpinBox); trackLabel->setBuddy(m_trackSpinBox); QSpacerItem* trackSpacer = new QSpacerItem(16, 0, QSizePolicy::Expanding, QSizePolicy::Minimum); trackLayout->addItem(trackSpacer); QLabel* destLabel = new QLabel(tr("&Destination:"), this); m_destComboBox = new QComboBox(this); m_destComboBox->setEditable(false); m_destComboBox->addItem(tr("Tag 1"), TrackData::TagV1); m_destComboBox->addItem(tr("Tag 2"), TrackData::TagV2); m_destComboBox->addItem(tr("Tag 1 and Tag 2"), TrackData::TagV2V1); m_destComboBox->setCurrentIndex( m_destComboBox->findData(NumberTracksConfig::instance().m_numberTracksDst)); trackLayout->addWidget(destLabel); trackLayout->addWidget(m_destComboBox); destLabel->setBuddy(m_destComboBox); vlayout->addLayout(trackLayout); QHBoxLayout* totalLayout = new QHBoxLayout; m_totalNumTracksCheckBox = new QCheckBox(tr("&Total number of tracks:"), this); m_totalNumTrackSpinBox = new QSpinBox(this); if (m_totalNumTracksCheckBox && m_totalNumTrackSpinBox) { m_totalNumTrackSpinBox->setMaximum(999); totalLayout->addWidget(m_totalNumTracksCheckBox); totalLayout->addWidget(m_totalNumTrackSpinBox); } QSpacerItem* totalSpacer = new QSpacerItem(16, 0, QSizePolicy::Expanding, QSizePolicy::Minimum); totalLayout->addItem(totalSpacer); vlayout->addLayout(totalLayout); QHBoxLayout* hlayout = new QHBoxLayout; QPushButton* helpButton = new QPushButton(tr("&Help"), this); hlayout->addWidget(helpButton); connect(helpButton, SIGNAL(clicked()), this, SLOT(showHelp())); QPushButton* saveButton = new QPushButton(tr("&Save Settings"), this); saveButton->setAutoDefault(false); hlayout->addWidget(saveButton); connect(saveButton, SIGNAL(clicked()), this, SLOT(saveConfig())); QSpacerItem* hspacer = new QSpacerItem(16, 0, QSizePolicy::Expanding, QSizePolicy::Minimum); hlayout->addItem(hspacer); QPushButton* okButton = new QPushButton(tr("&OK"), this); hlayout->addWidget(okButton); connect(okButton, SIGNAL(clicked()), this, SLOT(accept())); QPushButton* cancelButton = new QPushButton(tr("&Cancel"), this); hlayout->addWidget(cancelButton); connect(cancelButton, SIGNAL(clicked()), this, SLOT(reject())); vlayout->addLayout(hlayout); } /** * Destructor. */ NumberTracksDialog::~NumberTracksDialog() {} /** * Get start number. */ int NumberTracksDialog::getStartNumber() const { return m_trackSpinBox->value(); } /** * Get destination. * * @return TagV1, TagV2 or TagV2V1 if ID3v1, ID2v2 or both are destination */ TrackData::TagVersion NumberTracksDialog::getDestination() const { return TrackData::tagVersionCast( m_destComboBox->itemData(m_destComboBox->currentIndex()).toInt()); } /** * Save the local settings to the configuration. */ void NumberTracksDialog::saveConfig() { NumberTracksConfig::instance().m_numberTracksDst = getDestination(); NumberTracksConfig::instance().m_numberTracksStart = m_trackSpinBox->value(); } /** * Show help. */ void NumberTracksDialog::showHelp() { ContextHelp::displayHelp(QLatin1String("number-tracks")); } /** * Set the total number of tracks. * * @param numTracks number of tracks * @param enable true to enable setting of total */ void NumberTracksDialog::setTotalNumberOfTracks(int numTracks, bool enable) { m_totalNumTrackSpinBox->setValue(numTracks); m_totalNumTracksCheckBox->setChecked(enable); } /** * Get the total number of tracks. * * @param enable true is returned here if total number of tracks is checked * * @return number of tracks entered */ int NumberTracksDialog::getTotalNumberOfTracks(bool* enable) const { *enable = m_totalNumTracksCheckBox->isChecked(); return m_totalNumTrackSpinBox->value(); } kid3-3.0.2/src/gui/dialogs/numbertracksdialog.h000066400000000000000000000045241224603511300213760ustar00rootroot00000000000000/** * \file numbertracksdialog.h * Number tracks dialog. * * \b Project: Kid3 * \author Urs Fleisch * \date 25 May 2006 * * Copyright (C) 2006-2007 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef NUMBERTRACKSDIALOG_H #define NUMBERTRACKSDIALOG_H #include #include "trackdata.h" class QSpinBox; class QComboBox; class QCheckBox; /** * Number tracks dialog. */ class NumberTracksDialog : public QDialog { Q_OBJECT public: /** * Constructor. * * @param parent parent widget */ explicit NumberTracksDialog(QWidget* parent); /** * Destructor. */ ~NumberTracksDialog(); /** * Get start number. */ int getStartNumber() const; /** * Get destination. * * @return TagV1, TagV2 or TagV2V1 if ID3v1, ID2v2 or both are destination */ TrackData::TagVersion getDestination() const; /** * Set the total number of tracks. * * @param numTracks number of tracks * @param enable true to enable setting of total */ void setTotalNumberOfTracks(int numTracks, bool enable); /** * Get the total number of tracks. * * @param enable true is returned here if total number of tracks is checked * * @return number of tracks entered */ int getTotalNumberOfTracks(bool* enable) const; private slots: /** * Save the local settings to the configuration. */ void saveConfig(); /** * Show help. */ void showHelp(); private: /** spinbox with starting track number */ QSpinBox* m_trackSpinBox; /** combobox with destination */ QComboBox* m_destComboBox; /** total number of tracks checkbox */ QCheckBox* m_totalNumTracksCheckBox; /** spinbox with total number of tracks */ QSpinBox* m_totalNumTrackSpinBox; }; #endif kid3-3.0.2/src/gui/dialogs/playlistdialog.cpp000066400000000000000000000261561224603511300210770ustar00rootroot00000000000000/** * \file playlistdialog.cpp * Create playlist dialog. * * \b Project: Kid3 * \author Urs Fleisch * \date 13 Sep 2009 * * Copyright (C) 2009-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "playlistdialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "playlistconfig.h" #include "contexthelp.h" #include "trackdata.h" /** * Constructor. * * @param parent parent widget */ PlaylistDialog::PlaylistDialog(QWidget* parent): QDialog(parent) { setObjectName(QLatin1String("PlaylistDialog")); setModal(true); setWindowTitle(tr("Create Playlist")); setSizeGripEnabled(true); QVBoxLayout* vlayout = new QVBoxLayout(this); QGroupBox* fnGroupBox = new QGroupBox(this); QVBoxLayout* fnGroupBoxLayout = new QVBoxLayout(fnGroupBox); m_sameAsDirNameButton = new QRadioButton(this); fnGroupBoxLayout->addWidget(m_sameAsDirNameButton); QHBoxLayout* fileNameFormatLayout = new QHBoxLayout; m_fileNameFormatButton = new QRadioButton(this); m_fileNameFormatComboBox = new QComboBox(this); m_fileNameFormatComboBox->setToolTip(TrackDataFormatReplacer::getToolTip()); fileNameFormatLayout->addWidget(m_fileNameFormatButton); fileNameFormatLayout->addWidget(m_fileNameFormatComboBox); fnGroupBoxLayout->addLayout(fileNameFormatLayout); QHBoxLayout* locationLayout = new QHBoxLayout; QLabel* locationLabel = new QLabel(this); m_locationComboBox = new QComboBox(this); locationLayout->addWidget(locationLabel); locationLayout->addWidget(m_locationComboBox); fnGroupBoxLayout->addLayout(locationLayout); vlayout->addWidget(fnGroupBox); QGroupBox* pcGroupBox = new QGroupBox(this); QVBoxLayout* pcGroupBoxLayout = new QVBoxLayout(pcGroupBox); QHBoxLayout* formatLayout = new QHBoxLayout; QLabel* formatLabel = new QLabel(this); m_formatComboBox = new QComboBox(this); formatLayout->addWidget(formatLabel); formatLayout->addWidget(m_formatComboBox); pcGroupBoxLayout->addLayout(formatLayout); m_onlySelectedFilesCheckBox = new QCheckBox(this); pcGroupBoxLayout->addWidget(m_onlySelectedFilesCheckBox); QFrame* sortLine = new QFrame(pcGroupBox); sortLine->setFrameShape(QFrame::HLine); sortLine->setFrameShadow(QFrame::Sunken); pcGroupBoxLayout->addWidget(sortLine); QButtonGroup* sortButtonGroup = new QButtonGroup(pcGroupBox); m_sortFileNameButton = new QRadioButton(this); pcGroupBoxLayout->addWidget(m_sortFileNameButton); QHBoxLayout* sortTagFieldLayout = new QHBoxLayout; m_sortTagFieldButton = new QRadioButton(this); m_sortTagFieldComboBox = new QComboBox(this); m_sortTagFieldComboBox->setToolTip(TrackDataFormatReplacer::getToolTip()); sortTagFieldLayout->addWidget(m_sortTagFieldButton); sortTagFieldLayout->addWidget(m_sortTagFieldComboBox); pcGroupBoxLayout->addLayout(sortTagFieldLayout); sortButtonGroup->addButton(m_sortFileNameButton); sortButtonGroup->addButton(m_sortTagFieldButton); QFrame* pathLine = new QFrame(pcGroupBox); pathLine->setFrameShape(QFrame::HLine); pathLine->setFrameShadow(QFrame::Sunken); pcGroupBoxLayout->addWidget(pathLine); QButtonGroup* pathButtonGroup = new QButtonGroup(pcGroupBox); m_relPathButton = new QRadioButton(this); pcGroupBoxLayout->addWidget(m_relPathButton); m_fullPathButton = new QRadioButton(this); pcGroupBoxLayout->addWidget(m_fullPathButton); pathButtonGroup->addButton(m_relPathButton); pathButtonGroup->addButton(m_fullPathButton); QFrame* writeLine = new QFrame(pcGroupBox); writeLine->setFrameShape(QFrame::HLine); writeLine->setFrameShadow(QFrame::Sunken); pcGroupBoxLayout->addWidget(writeLine); QButtonGroup* writeButtonGroup = new QButtonGroup(pcGroupBox); m_writeListButton = new QRadioButton(this); pcGroupBoxLayout->addWidget(m_writeListButton); QHBoxLayout* writeInfoLayout = new QHBoxLayout; m_writeInfoButton = new QRadioButton(this); m_writeInfoComboBox = new QComboBox(this); m_writeInfoComboBox->setToolTip(TrackDataFormatReplacer::getToolTip()); writeInfoLayout->addWidget(m_writeInfoButton); writeInfoLayout->addWidget(m_writeInfoComboBox); pcGroupBoxLayout->addLayout(writeInfoLayout); writeButtonGroup->addButton(m_writeListButton); writeButtonGroup->addButton(m_writeInfoButton); vlayout->addWidget(pcGroupBox); fnGroupBox->setTitle(tr("Playlist File Name")); m_sameAsDirNameButton->setText(tr("Same as &directory name")); m_sameAsDirNameButton->setChecked(true); m_fileNameFormatButton->setText(tr("&Format:")); m_fileNameFormatComboBox->setEditable(true); m_fileNameFormatComboBox->setEnabled(false); m_fileNameFormatComboBox->addItems( QStringList() << QLatin1String("%{artist} - %{album}") << QLatin1String("%{artist} - [%{year}] %{album}") << QLatin1String("%{album}") << QLatin1String("playlist_%{artist}_-_%{album}") << QLatin1String("playlist")); connect(m_fileNameFormatButton, SIGNAL(toggled(bool)), m_fileNameFormatComboBox, SLOT(setEnabled(bool))); locationLabel->setText(tr("Cr&eate in:")); locationLabel->setBuddy(m_locationComboBox); m_locationComboBox->addItems( QStringList() << tr("Current directory") << tr("Every directory") << tr("Top-level directory")); pcGroupBox->setTitle(tr("Playlist Content")); formatLabel->setText(tr("For&mat:")); formatLabel->setBuddy(m_formatComboBox); m_formatComboBox->addItems(QStringList() << QLatin1String("M3U") << QLatin1String("PLS") << QLatin1String("XSPF")); m_onlySelectedFilesCheckBox->setText(tr("Incl&ude only the selected files")); m_sortFileNameButton->setText(tr("Sort by file &name")); m_sortFileNameButton->setChecked(true); m_sortTagFieldButton->setText(tr("Sort by &tag field")); m_sortTagFieldComboBox->setEditable(true); m_sortTagFieldComboBox->setEnabled(false); QStringList lst; for (int type = Frame::FT_FirstFrame; type <= Frame::FT_LastFrame; ++type) { QString frameName = Frame::ExtendedType(static_cast(type), QLatin1String("")).getName(). toLower(); if (frameName == QLatin1String("track number")) frameName = QLatin1String("track.3"); lst.append(QLatin1String("%{") + frameName + QLatin1String("}")); } m_sortTagFieldComboBox->addItems(lst); connect(m_sortTagFieldButton, SIGNAL(toggled(bool)), m_sortTagFieldComboBox, SLOT(setEnabled(bool))); m_relPathButton->setText(tr("Use &relative path for files in playlist")); m_relPathButton->setChecked(true); m_fullPathButton->setText(tr("Use full p&ath for files in playlist")); m_writeListButton->setText(tr("Write only &list of files")); m_writeListButton->setChecked(true); m_writeInfoButton->setText(tr("Write &info using")); m_writeInfoComboBox->setEditable(true); m_writeInfoComboBox->setEnabled(false); m_writeInfoComboBox->addItems( QStringList() << QLatin1String("%{artist} - %{title}") << QLatin1String("%{title}") << QLatin1String("%{track.1}/%{tracks} - %{artist} - %{album} - %{title}")); connect(m_writeInfoButton, SIGNAL(toggled(bool)), m_writeInfoComboBox, SLOT(setEnabled(bool))); QHBoxLayout* hlayout = new QHBoxLayout; QPushButton* helpButton = new QPushButton(tr("&Help"), this); helpButton->setAutoDefault(false); hlayout->addWidget(helpButton); connect(helpButton, SIGNAL(clicked()), this, SLOT(showHelp())); QPushButton* saveButton = new QPushButton(tr("&Save Settings"), this); saveButton->setAutoDefault(false); hlayout->addWidget(saveButton); connect(saveButton, SIGNAL(clicked()), this, SLOT(saveConfig())); QSpacerItem* hspacer = new QSpacerItem(16, 0, QSizePolicy::Expanding, QSizePolicy::Minimum); hlayout->addItem(hspacer); QPushButton* okButton = new QPushButton(tr("&OK"), this); hlayout->addWidget(okButton); connect(okButton, SIGNAL(clicked()), this, SLOT(accept())); QPushButton* cancelButton = new QPushButton(tr("&Cancel"), this); hlayout->addWidget(cancelButton); connect(cancelButton, SIGNAL(clicked()), this, SLOT(reject())); vlayout->addLayout(hlayout); } /** * Destructor. */ PlaylistDialog::~PlaylistDialog() {} /** * Read the local settings from the configuration. */ void PlaylistDialog::readConfig() { m_fileNameFormatButton->setChecked( PlaylistConfig::instance().m_useFileNameFormat); m_sameAsDirNameButton->setChecked( !PlaylistConfig::instance().m_useFileNameFormat); m_onlySelectedFilesCheckBox->setChecked( PlaylistConfig::instance().m_onlySelectedFiles); m_sortTagFieldButton->setChecked(PlaylistConfig::instance().m_useSortTagField); m_sortFileNameButton->setChecked(!PlaylistConfig::instance().m_useSortTagField); m_fullPathButton->setChecked(PlaylistConfig::instance().m_useFullPath); m_relPathButton->setChecked(!PlaylistConfig::instance().m_useFullPath); m_writeInfoButton->setChecked(PlaylistConfig::instance().m_writeInfo); m_writeListButton->setChecked(!PlaylistConfig::instance().m_writeInfo); m_locationComboBox->setCurrentIndex(PlaylistConfig::instance().m_location); m_formatComboBox->setCurrentIndex(PlaylistConfig::instance().m_format); m_fileNameFormatComboBox->setEditText( PlaylistConfig::instance().m_fileNameFormat); m_sortTagFieldComboBox->setEditText(PlaylistConfig::instance().m_sortTagField); m_writeInfoComboBox->setEditText(PlaylistConfig::instance().m_infoFormat); } /** * Get the current dialog configuration. * * @param cfg the current configuration is returned here */ void PlaylistDialog::getCurrentConfig(PlaylistConfig& cfg) const { cfg.m_useFileNameFormat = m_fileNameFormatButton->isChecked(); cfg.m_onlySelectedFiles = m_onlySelectedFilesCheckBox->isChecked(); cfg.m_useSortTagField = m_sortTagFieldButton->isChecked(); cfg.m_useFullPath = m_fullPathButton->isChecked(); cfg.m_writeInfo = m_writeInfoButton->isChecked(); cfg.m_location = static_cast( m_locationComboBox->currentIndex()); cfg.m_format = static_cast( m_formatComboBox->currentIndex()); cfg.m_fileNameFormat = m_fileNameFormatComboBox->currentText(); cfg.m_sortTagField = m_sortTagFieldComboBox->currentText(); cfg.m_infoFormat = m_writeInfoComboBox->currentText(); } /** * Save the local settings to the configuration. */ void PlaylistDialog::saveConfig() const { getCurrentConfig(PlaylistConfig::instance()); } /** * Show help. */ void PlaylistDialog::showHelp() { ContextHelp::displayHelp(QLatin1String("create-playlist")); } kid3-3.0.2/src/gui/dialogs/playlistdialog.h000066400000000000000000000042211224603511300205310ustar00rootroot00000000000000/** * \file playlistdialog.h * Create playlist dialog. * * \b Project: Kid3 * \author Urs Fleisch * \date 13 Sep 2009 * * Copyright (C) 2009 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef PLAYLISTDIALOG_H #define PLAYLISTDIALOG_H #include class QRadioButton; class QCheckBox; class QComboBox; class PlaylistConfig; /** * Playlist dialog. */ class PlaylistDialog : public QDialog { Q_OBJECT public: /** * Constructor. * * @param parent parent widget */ explicit PlaylistDialog(QWidget* parent); /** * Destructor. */ ~PlaylistDialog(); /** * Read the local settings from the configuration. */ void readConfig(); /** * Get the current dialog configuration. * * @param cfg the current configuration is returned here */ void getCurrentConfig(PlaylistConfig& cfg) const; private slots: /** * Save the local settings to the configuration. */ void saveConfig() const; /** * Show help. */ void showHelp(); private: QRadioButton* m_sameAsDirNameButton; QRadioButton* m_fileNameFormatButton; QComboBox* m_locationComboBox; QComboBox* m_formatComboBox; QCheckBox* m_onlySelectedFilesCheckBox; QRadioButton* m_sortFileNameButton; QRadioButton* m_sortTagFieldButton; QRadioButton* m_relPathButton; QRadioButton* m_fullPathButton; QRadioButton* m_writeListButton; QRadioButton* m_writeInfoButton; QComboBox* m_fileNameFormatComboBox; QComboBox* m_sortTagFieldComboBox; QComboBox* m_writeInfoComboBox; }; #endif // PLAYLISTDIALOG_H kid3-3.0.2/src/gui/dialogs/rendirdialog.cpp000066400000000000000000000204311224603511300205070ustar00rootroot00000000000000/** * \file rendirdialog.cpp * Rename directory dialog. * * \b Project: Kid3 * \author Urs Fleisch * \date 21 Mar 2004 * * Copyright (C) 2004-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "rendirdialog.h" #include #include #include #include #include #include #include #include #include #include #include #include "taggedfile.h" #include "frame.h" #include "trackdata.h" #include "contexthelp.h" #include "rendirconfig.h" #include "dirrenamer.h" /** * Constructor. * * @param parent parent widget * @param dirRenamer directory renamer */ RenDirDialog::RenDirDialog(QWidget* parent, DirRenamer* dirRenamer) : QWizard(parent), m_taggedFile(0), m_dirRenamer(dirRenamer) { setObjectName(QLatin1String("RenDirDialog")); setModal(true); setWindowTitle(tr("Rename Directory")); setSizeGripEnabled(true); QWizardPage* mainPage = new QWizardPage; QVBoxLayout* mainLayout = new QVBoxLayout(mainPage); setupMainPage(mainPage, mainLayout); mainPage->setTitle(tr("Format")); addPage(mainPage); QWizardPage* previewPage = new QWizardPage; setupPreviewPage(previewPage); previewPage->setTitle(tr("Preview")); addPage(previewPage); setOptions(HaveHelpButton | HaveCustomButton1); setButtonText(CustomButton1, tr("&Save Settings")); connect(this, SIGNAL(helpRequested()), this, SLOT(showHelp())); connect(this, SIGNAL(customButtonClicked(int)), this, SLOT(saveConfig())); connect(this, SIGNAL(currentIdChanged(int)), this, SLOT(pageChanged())); } /** * Destructor. */ RenDirDialog::~RenDirDialog() {} /** * Set up the main wizard page. * * @param page widget * @param vlayout layout */ void RenDirDialog::setupMainPage(QWidget* page, QVBoxLayout* vlayout) { if (!page || !vlayout) { return; } QHBoxLayout* actionLayout = new QHBoxLayout; m_actionComboBox = new QComboBox(page); m_tagversionComboBox = new QComboBox(page); m_actionComboBox->insertItem(ActionRename, tr("Rename Directory")); m_actionComboBox->insertItem(ActionCreate, tr("Create Directory")); actionLayout->addWidget(m_actionComboBox); connect(m_actionComboBox, SIGNAL(activated(int)), this, SLOT(slotUpdateNewDirname())); m_tagversionComboBox->addItem(tr("From Tag 2 and Tag 1"), TrackData::TagV2V1); m_tagversionComboBox->addItem(tr("From Tag 1"), TrackData::TagV1); m_tagversionComboBox->addItem(tr("From Tag 2"), TrackData::TagV2); actionLayout->addWidget(m_tagversionComboBox); connect(m_tagversionComboBox, SIGNAL(activated(int)), this, SLOT(slotUpdateNewDirname())); vlayout->addLayout(actionLayout); QHBoxLayout* formatLayout = new QHBoxLayout; QLabel* formatLabel = new QLabel(tr("&Format:"), page); m_formatComboBox = new QComboBox(page); QStringList strList; for (const char** sl = RenDirConfig::s_defaultDirFmtList; *sl != 0; ++sl) { strList += QString::fromLatin1(*sl); } m_formatComboBox->addItems(strList); m_formatComboBox->setEditable(true); m_formatComboBox->setItemText(RenDirConfig::instance().m_dirFormatItem, RenDirConfig::instance().m_dirFormatText); m_formatComboBox->setCurrentIndex(RenDirConfig::instance().m_dirFormatItem); m_tagversionComboBox->setCurrentIndex( m_tagversionComboBox->findData(RenDirConfig::instance().m_renDirSrc)); formatLabel->setBuddy(m_formatComboBox); formatLayout->addWidget(formatLabel); formatLayout->addWidget(m_formatComboBox); connect(m_formatComboBox, SIGNAL(activated(int)), this, SLOT(slotUpdateNewDirname())); connect(m_formatComboBox, SIGNAL(editTextChanged(QString)), this, SLOT(slotUpdateNewDirname())); vlayout->addLayout(formatLayout); QGridLayout* fromToLayout = new QGridLayout; vlayout->addLayout(fromToLayout); QLabel* fromLabel = new QLabel(tr("From:"), page); m_currentDirLabel = new QLabel(page); QLabel* toLabel = new QLabel(tr("To:"), page); m_newDirLabel = new QLabel(page); fromToLayout->addWidget(fromLabel, 0, 0); fromToLayout->addWidget(m_currentDirLabel, 0, 1); fromToLayout->addWidget(toLabel, 1, 0); fromToLayout->addWidget(m_newDirLabel, 1, 1); } /** * Set up the preview wizard page. * * @param page widget */ void RenDirDialog::setupPreviewPage(QWidget* page) { QVBoxLayout* vlayout = new QVBoxLayout(page); m_edit = new QTextEdit(page); m_edit->setReadOnly(true); m_edit->setAcceptRichText(false); vlayout->addWidget(m_edit); } /** * Start dialog. * * @param taggedFile file to use for rename preview * @param dirName if taggedFile is 0, the directory can be set here */ void RenDirDialog::startDialog(TaggedFile* taggedFile, const QString& dirName) { m_taggedFile = taggedFile; if (m_taggedFile) { slotUpdateNewDirname(); } else { m_currentDirLabel->setText(dirName); m_newDirLabel->clear(); } restart(); } /** * Set new directory name. * * @param dir new directory name */ void RenDirDialog::setNewDirname(const QString& dir) { m_newDirLabel->setText(dir); } /** * Get new directory name. * * @return new directory name. */ QString RenDirDialog::getNewDirname() const { return m_newDirLabel->text(); } /** * Set configuration from dialog in directory renamer. */ void RenDirDialog::setDirRenamerConfiguration() { m_dirRenamer->setTagVersion(TrackData::tagVersionCast(m_tagversionComboBox->itemData(m_tagversionComboBox->currentIndex()).toInt())); m_dirRenamer->setAction(m_actionComboBox->currentIndex() == ActionCreate); m_dirRenamer->setFormat(m_formatComboBox->currentText()); } /** * Set new directory name according to current settings. */ void RenDirDialog::slotUpdateNewDirname() { if (m_taggedFile) { setDirRenamerConfiguration(); QString currentDirname; QString newDirname(m_dirRenamer->generateNewDirname(m_taggedFile, ¤tDirname)); m_currentDirLabel->setText(currentDirname); setNewDirname(newDirname); } } /** * Save the local settings to the configuration. */ void RenDirDialog::saveConfig() { RenDirConfig::instance().m_dirFormatItem = m_formatComboBox->currentIndex(); RenDirConfig::instance().m_dirFormatText = m_formatComboBox->currentText(); RenDirConfig::instance().m_renDirSrc = TrackData::tagVersionCast( m_tagversionComboBox->itemData(m_tagversionComboBox->currentIndex()).toInt()); } /** * Show help. */ void RenDirDialog::showHelp() { ContextHelp::displayHelp(QLatin1String("rename-directory")); } /** * Request action scheduling and then accept dialog. */ void RenDirDialog::requestActionSchedulingAndAccept() { QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); setDirRenamerConfiguration(); emit actionSchedulingRequested(); QApplication::restoreOverrideCursor(); accept(); } /** * Clear action preview. */ void RenDirDialog::clearPreview() { if (m_edit) { m_edit->clear(); m_edit->setLineWrapMode(QTextEdit::NoWrap); } } /** * Display action preview. * * @param actionStrs description of action */ void RenDirDialog::displayActionPreview(const QStringList& actionStrs) { QString str = actionStrs.at(0); int width = fontMetrics().width(str) + 8; if (m_edit->tabStopWidth() < width) { m_edit->setTabStopWidth(width); } if (actionStrs.size() > 1) { str += QLatin1Char('\t'); str += actionStrs.at(1); } if (actionStrs.size() > 2) { str += QLatin1String("\n\t"); str += actionStrs.at(2); } m_edit->append(str); } /** * Wizard page changed. */ void RenDirDialog::pageChanged() { if (currentId() == 1) { clearPreview(); setDirRenamerConfiguration(); emit actionSchedulingRequested(); } } /** * Called when the wizard is canceled. */ void RenDirDialog::reject() { m_dirRenamer->abort(); QWizard::reject(); } kid3-3.0.2/src/gui/dialogs/rendirdialog.h000066400000000000000000000066761224603511300201730ustar00rootroot00000000000000/** * \file rendirdialog.h * Rename directory dialog. * * \b Project: Kid3 * \author Urs Fleisch * \date 21 Mar 2004 * * Copyright (C) 2004-2007 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef RENDIRDIALOG_H #define RENDIRDIALOG_H #include #include #include #include "trackdata.h" class QComboBox; class QLabel; class TaggedFile; class QVBoxLayout; class QTextEdit; class DirRenamer; /** * Rename directory dialog. */ class RenDirDialog : public QWizard { Q_OBJECT public: /** * Constructor. * * @param parent parent widget * @param dirRenamer directory renamer */ RenDirDialog(QWidget* parent, DirRenamer* dirRenamer); /** * Destructor. */ ~RenDirDialog(); /** * Start dialog. * * @param taggedFile file to use for rename preview * @param dirName if taggedFile is 0, the directory can be set here */ void startDialog(TaggedFile* taggedFile, const QString& dirName = QString()); /** * Set new directory name. * * @param dir new directory name */ void setNewDirname(const QString& dir); /** * Get new directory name. * * @return new directory name. */ QString getNewDirname() const; public slots: /** * Display action preview. * * @param actionStrs description of action */ void displayActionPreview(const QStringList& actionStrs); protected: /** * Called when the wizard is canceled. */ virtual void reject(); signals: /** * Emitted when scheduling of actions using clearActions() followed by * one or multiple scheduleAction() calls is requested. */ void actionSchedulingRequested(); private slots: /** * Set new directory name according to current settings. */ void slotUpdateNewDirname(); /** * Save the local settings to the configuration. */ void saveConfig(); /** * Show help. */ void showHelp(); /** * Request action scheduling and then accept dialog. */ void requestActionSchedulingAndAccept(); /** * Wizard page changed. */ void pageChanged(); private: /** Action to be performed. */ enum Action { ActionRename = 0, ActionCreate = 1 }; /** * Set up the main wizard page. * * @param page widget * @param vlayout layout */ void setupMainPage(QWidget* page, QVBoxLayout* vlayout); /** * Set up the preview wizard page. * * @param page widget */ void setupPreviewPage(QWidget* page); /** * Clear action preview. */ void clearPreview(); /** * Set configuration from dialog in directory renamer. */ void setDirRenamerConfiguration(); QComboBox* m_formatComboBox; QComboBox* m_actionComboBox; QComboBox* m_tagversionComboBox; QLabel* m_currentDirLabel; QLabel* m_newDirLabel; QTextEdit* m_edit; TaggedFile* m_taggedFile; DirRenamer* m_dirRenamer; }; #endif kid3-3.0.2/src/gui/dialogs/serverimportdialog.cpp000066400000000000000000000324631224603511300217750ustar00rootroot00000000000000/** * \file serverimportdialog.cpp * Generic dialog to import from a server. * * \b Project: Kid3 * \author Urs Fleisch * \date 09 Oct 2006 * * Copyright (C) 2006-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "serverimportdialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "serverimporter.h" #include "serverimporterconfig.h" #include "contexthelp.h" #include "trackdata.h" /** * Constructor. * * @param parent parent widget */ ServerImportDialog::ServerImportDialog(QWidget* parent) : QDialog(parent), m_serverComboBox(0), m_cgiLineEdit(0), m_standardTagsCheckBox(0), m_additionalTagsCheckBox(0), m_coverArtCheckBox(0), m_source(0) { setObjectName(QLatin1String("ServerImportDialog")); QVBoxLayout* vlayout = new QVBoxLayout(this); QHBoxLayout* findLayout = new QHBoxLayout; m_artistLineEdit = new QComboBox(this); m_albumLineEdit = new QComboBox(this); m_findButton = new QPushButton(tr("&Find"), this); m_artistLineEdit->setEditable(true); m_artistLineEdit->setAutoCompletion(true); m_artistLineEdit->setDuplicatesEnabled(false); m_albumLineEdit->setEditable(true); m_albumLineEdit->setAutoCompletion(true); m_albumLineEdit->setDuplicatesEnabled(false); m_artistLineEdit->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum)); m_albumLineEdit->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum)); m_findButton->setDefault(true); findLayout->addWidget(m_artistLineEdit); findLayout->addWidget(m_albumLineEdit); findLayout->addWidget(m_findButton); connect(m_findButton, SIGNAL(clicked()), this, SLOT(slotFind())); vlayout->addLayout(findLayout); QHBoxLayout* serverLayout = new QHBoxLayout; m_serverLabel = new QLabel(tr("&Server:"), this); m_serverComboBox = new QComboBox(this); m_serverComboBox->setEditable(true); m_cgiLabel = new QLabel(tr("C&GI Path:"), this); m_cgiLineEdit = new QLineEdit(this); serverLayout->addWidget(m_serverLabel); serverLayout->addWidget(m_serverComboBox); m_serverLabel->setBuddy(m_serverComboBox); serverLayout->addWidget(m_cgiLabel); serverLayout->addWidget(m_cgiLineEdit); m_cgiLabel->setBuddy(m_cgiLineEdit); vlayout->addLayout(serverLayout); QHBoxLayout* hlayout = new QHBoxLayout; m_standardTagsCheckBox = new QCheckBox(tr("&Standard Tags"), this); m_additionalTagsCheckBox = new QCheckBox(tr("&Additional Tags"), this); m_coverArtCheckBox = new QCheckBox(tr("C&over Art"), this); hlayout->addWidget(m_standardTagsCheckBox); hlayout->addWidget(m_additionalTagsCheckBox); hlayout->addWidget(m_coverArtCheckBox); vlayout->addLayout(hlayout); m_albumListBox = new QListView(this); m_albumListBox->setEditTriggers(QAbstractItemView::NoEditTriggers); vlayout->addWidget(m_albumListBox); connect(m_albumListBox, SIGNAL(activated(QModelIndex)), this, SLOT(requestTrackList(QModelIndex))); QHBoxLayout* buttonLayout = new QHBoxLayout; m_helpButton = new QPushButton(tr("&Help"), this); m_saveButton = new QPushButton(tr("&Save Settings"), this); QPushButton* closeButton = new QPushButton(tr("&Close"), this); buttonLayout->addWidget(m_helpButton); connect(m_helpButton, SIGNAL(clicked()), this, SLOT(showHelp())); buttonLayout->addWidget(m_saveButton); connect(m_saveButton, SIGNAL(clicked()), this, SLOT(saveConfig())); QSpacerItem* hspacer = new QSpacerItem(16, 0, QSizePolicy::Expanding, QSizePolicy::Minimum); buttonLayout->addItem(hspacer); buttonLayout->addWidget(closeButton); connect(closeButton, SIGNAL(clicked()), this, SLOT(accept())); vlayout->addLayout(buttonLayout); m_statusBar = new QStatusBar(this); vlayout->addWidget(m_statusBar); showStatusMessage(tr("Ready.")); } /** * Destructor. */ ServerImportDialog::~ServerImportDialog() { } /** * Set importer to be used. * * @param source import source to use */ void ServerImportDialog::setImportSource(ServerImporter* source) { if (m_source) { disconnect(m_source, SIGNAL(progress(QString,int,int)), this, SLOT(showStatusMessage(QString))); disconnect(m_source, SIGNAL(findFinished(QByteArray)), this, SLOT(slotFindFinished(QByteArray))); disconnect(m_source, SIGNAL(albumFinished(QByteArray)), this, SLOT(slotAlbumFinished(QByteArray))); } m_source = source; if (m_source) { connect(m_source, SIGNAL(progress(QString,int,int)), this, SLOT(showStatusMessage(QString))); connect(m_source, SIGNAL(findFinished(QByteArray)), this, SLOT(slotFindFinished(QByteArray))); connect(m_source, SIGNAL(albumFinished(QByteArray)), this, SLOT(slotAlbumFinished(QByteArray))); setWindowTitle(QCoreApplication::translate("@default", m_source->name())); if (m_source->defaultServer()) { m_serverLabel->show(); m_serverComboBox->show(); if (m_source->defaultCgiPath()) { m_cgiLabel->show(); m_cgiLineEdit->show(); } else { m_cgiLabel->hide(); m_cgiLineEdit->hide(); } if (m_source->serverList()) { QStringList strList; for (const char** sl = m_source->serverList(); *sl != 0; ++sl) { strList += QString::fromLatin1(*sl); } m_serverComboBox->clear(); m_serverComboBox->addItems(strList); } } else { m_serverLabel->hide(); m_serverComboBox->hide(); m_cgiLabel->hide(); m_cgiLineEdit->hide(); } if (m_source->additionalTags()) { m_standardTagsCheckBox->show(); m_additionalTagsCheckBox->show(); m_coverArtCheckBox->show(); } else { m_standardTagsCheckBox->hide(); m_additionalTagsCheckBox->hide(); m_coverArtCheckBox->hide(); } m_albumListBox->setModel(m_source->getAlbumListModel()); if (m_source->helpAnchor()) { m_helpButton->show(); } else { m_helpButton->hide(); } if (m_source->config()) { m_saveButton->show(); } else { m_saveButton->hide(); } } } /** * Display message in status bar. * * @param msg status message */ void ServerImportDialog::showStatusMessage(const QString& msg) { m_statusBar->showMessage(msg); } /** * Get string with server and port. * * @return "servername:port". */ QString ServerImportDialog::getServer() const { if (m_serverComboBox) { QString server(m_serverComboBox->currentText()); if (server.isEmpty() && m_source) { server = QString::fromLatin1(m_source->defaultServer()); } return server; } else { return QString(); } } /** * Set string with server and port. * * @param srv "servername:port" */ void ServerImportDialog::setServer(const QString& srv) { if (m_serverComboBox) { int idx = m_serverComboBox->findText(srv); if (idx >= 0) { m_serverComboBox->setCurrentIndex(idx); } else { m_serverComboBox->addItem(srv); m_serverComboBox->setCurrentIndex(m_serverComboBox->count() - 1); } } } /** * Get string with CGI path. * * @return CGI path, e.g. "/~cddb/cddb.cgi". */ QString ServerImportDialog::getCgiPath() const { if (m_cgiLineEdit) { QString cgi(m_cgiLineEdit->text()); if (cgi.isEmpty() && m_source) { cgi = QString::fromLatin1(m_source->defaultCgiPath()); } return cgi; } else { return QString(); } } /** * Set string with CGI path. * * @param cgi CGI path, e.g. "/~cddb/cddb.cgi". */ void ServerImportDialog::setCgiPath(const QString& cgi) { if (m_cgiLineEdit) { m_cgiLineEdit->setText(cgi); } } /** * Get standard tags option. * * @return true if standard tags are enabled. */ bool ServerImportDialog::getStandardTags() const { return m_standardTagsCheckBox ? m_standardTagsCheckBox->checkState() == Qt::Checked : false; } /** * Set standard tags option. * * @param enable true if standard tags are enabled */ void ServerImportDialog::setStandardTags(bool enable) { if (m_standardTagsCheckBox) { m_standardTagsCheckBox->setCheckState( enable ? Qt::Checked : Qt::Unchecked); } } /** * Get additional tags option. * * @return true if additional tags are enabled. */ bool ServerImportDialog::getAdditionalTags() const { return m_additionalTagsCheckBox ? m_additionalTagsCheckBox->checkState() == Qt::Checked : false; } /** * Set additional tags option. * * @param enable true if additional tags are enabled */ void ServerImportDialog::setAdditionalTags(bool enable) { if (m_additionalTagsCheckBox) { m_additionalTagsCheckBox->setCheckState( enable ? Qt::Checked : Qt::Unchecked); } } /** * Get cover art option. * * @return true if cover art are enabled. */ bool ServerImportDialog::getCoverArt() const { return m_coverArtCheckBox ? m_coverArtCheckBox->checkState() == Qt::Checked : false; } /** * Set cover art option. * * @param enable true if cover art are enabled */ void ServerImportDialog::setCoverArt(bool enable) { if (m_coverArtCheckBox) { m_coverArtCheckBox->setCheckState( enable ? Qt::Checked : Qt::Unchecked); } } /** * Get the local configuration. * * @param cfg configuration */ void ServerImportDialog::getImportSourceConfig(ServerImporterConfig* cfg) const { cfg->m_server = getServer(); cfg->m_cgiPath = getCgiPath(); cfg->m_standardTags = getStandardTags(); cfg->m_additionalTags = getAdditionalTags(); cfg->m_coverArt = getCoverArt(); cfg->m_windowGeometry = saveGeometry(); } /** * Save the local settings to the configuration. */ void ServerImportDialog::saveConfig() { if (m_source && m_source->config()) { getImportSourceConfig(m_source->config()); } } /** * Set a find string from artist and album information. * * @param artist artist * @param album album */ void ServerImportDialog::setArtistAlbum(const QString& artist, const QString& album) { if (m_source && m_source->config()) { ServerImporterConfig* cf = m_source->config(); setServer(cf->m_server); setCgiPath(cf->m_cgiPath); setStandardTags(cf->m_standardTags); setAdditionalTags(cf->m_additionalTags); setCoverArt(cf->m_coverArt); if (!cf->m_windowGeometry.isEmpty()) { restoreGeometry(cf->m_windowGeometry); } } if (!(artist.isEmpty() && album.isEmpty())) { int idx = m_artistLineEdit->findText(artist); if (idx >= 0) { m_artistLineEdit->setCurrentIndex(idx); } else { m_artistLineEdit->addItem(artist); m_artistLineEdit->setCurrentIndex(m_artistLineEdit->count() - 1); } idx = m_albumLineEdit->findText(album); if (idx >= 0) { m_albumLineEdit->setCurrentIndex(idx); } else { m_albumLineEdit->addItem(album); m_albumLineEdit->setCurrentIndex(m_albumLineEdit->count() - 1); } QLineEdit* lineEdit = m_artistLineEdit->lineEdit(); if (lineEdit) { lineEdit->selectAll(); } m_artistLineEdit->setFocus(); } } /** * Query a search for a keyword from the server. */ void ServerImportDialog::slotFind() { ServerImporterConfig cfg; getImportSourceConfig(&cfg); if (m_source) m_source->find(&cfg, m_artistLineEdit->currentText(), m_albumLineEdit->currentText()); } /** * Process finished find request. * * @param searchStr search data received */ void ServerImportDialog::slotFindFinished(const QByteArray& searchStr) { if (m_source) m_source->parseFindResults(searchStr); m_albumListBox->setFocus(); } /** * Process finished album data. * * @param albumStr album track data received */ void ServerImportDialog::slotAlbumFinished(const QByteArray& albumStr) { if (m_source) { m_source->setStandardTags(getStandardTags()); m_source->setAdditionalTags(getAdditionalTags()); m_source->setCoverArt(getCoverArt()); m_source->parseAlbumResults(albumStr); } emit trackDataUpdated(); } /** * Request track list from server. * * @param li standard item containing an AlbumListItem */ void ServerImportDialog::requestTrackList(QStandardItem* li) { AlbumListItem* ali = static_cast(li); if (ali && ali->type() == AlbumListItem::Type) { ServerImporterConfig cfg; getImportSourceConfig(&cfg); if (m_source) m_source->getTrackList(&cfg, ali->getCategory(), ali->getId()); } } /** * Request track list from server. * * @param index model index of list containing an AlbumListItem */ void ServerImportDialog::requestTrackList(const QModelIndex& index) { if (m_source) requestTrackList(m_source->getAlbumListModel()->itemFromIndex(index)); } /** * Show help. */ void ServerImportDialog::showHelp() { if (m_source && m_source->helpAnchor()) { ContextHelp::displayHelp(QString::fromLatin1(m_source->helpAnchor())); } } kid3-3.0.2/src/gui/dialogs/serverimportdialog.h000066400000000000000000000115211224603511300214320ustar00rootroot00000000000000/** * \file serverimportdialog.h * Generic dialog to import from a server. * * \b Project: Kid3 * \author Urs Fleisch * \date 09 Oct 2006 * * Copyright (C) 2006-2011 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef SERVERIMPORTDIALOG_H #define SERVERIMPORTDIALOG_H #include "config.h" #include #include #include class QLineEdit; class QLabel; class QComboBox; class QPushButton; class QCheckBox; class QStatusBar; class QListView; class QStandardItemModel; class ServerImporter; class ServerImporterConfig; class ImportTrackDataVector; /** * Generic dialog to import from an external source. */ class ServerImportDialog : public QDialog { Q_OBJECT public: /** * Constructor. * * @param parent parent widget */ explicit ServerImportDialog(QWidget* parent); /** * Destructor. */ virtual ~ServerImportDialog(); /** * Set importer to be used. * * @param source import source to use */ void setImportSource(ServerImporter* source); /** * Get string with server and port. * * @return "servername:port". */ QString getServer() const; /** * Set string with server and port. * * @param srv "servername:port" */ void setServer(const QString& srv); /** * Get string with CGI path. * * @return CGI path, e.g. "/~cddb/cddb.cgi". */ QString getCgiPath() const; /** * Set string with CGI path. * * @param cgi CGI path, e.g. "/~cddb/cddb.cgi". */ void setCgiPath(const QString& cgi); /** * Get standard tags option. * * @return true if standard tags are enabled. */ bool getStandardTags() const; /** * Set standard tags option. * * @param enable true if standard tags are enabled */ void setStandardTags(bool enable); /** * Get additional tags option. * * @return true if additional tags are enabled. */ bool getAdditionalTags() const; /** * Set additional tags option. * * @param enable true if additional tags are enabled */ void setAdditionalTags(bool enable); /** * Get cover art option. * * @return true if cover art are enabled. */ bool getCoverArt() const; /** * Set cover art option. * * @param enable true if cover art are enabled */ void setCoverArt(bool enable); /** * Set a find string from artist and album information. * * @param artist artist * @param album album */ void setArtistAlbum(const QString& artist, const QString& album); private slots: /** * Query a search for a keyword from the server. */ void slotFind(); /** * Process finished find request. * * @param searchStr search data received */ void slotFindFinished(const QByteArray& searchStr); /** * Process finished album data. * * @param albumStr album track data received */ void slotAlbumFinished(const QByteArray& albumStr); /** * Request track list from server. * * @param li standard item containing an AlbumListItem */ void requestTrackList(QStandardItem* li); /** * Request track list from server. * * @param index model index of list containing an AlbumListItem */ void requestTrackList(const QModelIndex& index); /** * Save the local settings to the configuration. */ void saveConfig(); /** * Show help. */ void showHelp(); /** * Display message in status bar. * * @param msg status message */ void showStatusMessage(const QString& msg); signals: /** * Emitted when the m_trackDataVector was updated with new imported data. */ void trackDataUpdated(); protected: QListView* m_albumListBox; /**< list box with albums to select */ private: /** * Get the local configuration. * * @param cfg configuration */ void getImportSourceConfig(ServerImporterConfig* cfg) const; QComboBox* m_artistLineEdit; QComboBox* m_albumLineEdit; QPushButton* m_findButton; QLabel* m_serverLabel; QComboBox* m_serverComboBox; QLabel* m_cgiLabel; QLineEdit* m_cgiLineEdit; QCheckBox* m_standardTagsCheckBox; QCheckBox* m_additionalTagsCheckBox; QCheckBox* m_coverArtCheckBox; QPushButton* m_helpButton; QPushButton* m_saveButton; QStatusBar* m_statusBar; ServerImporter* m_source; }; #endif kid3-3.0.2/src/gui/dialogs/servertrackimportdialog.cpp000066400000000000000000000321701224603511300230150ustar00rootroot00000000000000/** * \file servertrackimportdialog.cpp * Generic dialog for track based import from a server. * * \b Project: Kid3 * \author Urs Fleisch * \date 13 Sep 2005 * * Copyright (C) 2005-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "servertrackimportdialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "serverimporterconfig.h" #include "contexthelp.h" #include "servertrackimporter.h" #include "comboboxdelegate.h" #include "trackdatamodel.h" /** * Constructor. * * @param parent parent widget * @param trackDataModel track data to be filled with imported values, * is passed with filenames set */ ServerTrackImportDialog::ServerTrackImportDialog(QWidget* parent, TrackDataModel* trackDataModel) : QDialog(parent), m_statusBar(0), m_client(0), m_trackDataModel(trackDataModel) { setObjectName(QLatin1String("ServerTrackImportDialog")); setModal(true); QVBoxLayout* vlayout = new QVBoxLayout(this); QHBoxLayout* serverLayout = new QHBoxLayout; m_serverLabel = new QLabel(tr("&Server:"), this); m_serverComboBox = new QComboBox(this); m_serverComboBox->setEditable(true); m_serverComboBox->setSizePolicy( QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum)); m_serverLabel->setBuddy(m_serverComboBox); serverLayout->addWidget(m_serverLabel); serverLayout->addWidget(m_serverComboBox); vlayout->addLayout(serverLayout); m_albumTableModel = new QStandardItemModel(this); m_albumTableModel->setColumnCount(2); m_albumTableModel->setHorizontalHeaderLabels( QStringList() << QLatin1String("08 A Not So Short Title/Medium Sized Artist - And The Album Title [2005]") << QLatin1String("A Not So Short State")); m_albumTable = new QTableView(this); m_albumTable->setModel(m_albumTableModel); #if QT_VERSION >= 0x050000 m_albumTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); #else m_albumTable->horizontalHeader()->setResizeMode(0, QHeaderView::Stretch); #endif m_albumTable->setSelectionMode(QAbstractItemView::NoSelection); m_albumTable->resizeColumnsToContents(); m_albumTable->setItemDelegateForColumn(0, new ComboBoxDelegate(this)); m_albumTableModel->setHorizontalHeaderLabels( QStringList() << tr("Track Title/Artist - Album") << tr("State")); initTable(); vlayout->addWidget(m_albumTable); QHBoxLayout* hlayout = new QHBoxLayout; QSpacerItem* hspacer = new QSpacerItem(16, 0, QSizePolicy::Expanding, QSizePolicy::Minimum); m_helpButton = new QPushButton(tr("&Help"), this); m_saveButton = new QPushButton(tr("&Save Settings"), this); QPushButton* okButton = new QPushButton(tr("&OK"), this); QPushButton* applyButton = new QPushButton(tr("&Apply"), this); QPushButton* cancelButton = new QPushButton(tr("&Cancel"), this); hlayout->addWidget(m_helpButton); hlayout->addWidget(m_saveButton); hlayout->addItem(hspacer); hlayout->addWidget(okButton); hlayout->addWidget(applyButton); hlayout->addWidget(cancelButton); // auto default is switched off to use the return key to set the server // configuration okButton->setAutoDefault(false); cancelButton->setAutoDefault(false); applyButton->setAutoDefault(false); connect(m_helpButton, SIGNAL(clicked()), this, SLOT(showHelp())); connect(m_saveButton, SIGNAL(clicked()), this, SLOT(saveConfig())); connect(okButton, SIGNAL(clicked()), this, SLOT(accept())); connect(cancelButton, SIGNAL(clicked()), this, SLOT(reject())); connect(applyButton, SIGNAL(clicked()), this, SLOT(apply())); vlayout->addLayout(hlayout); m_statusBar = new QStatusBar(this); vlayout->addWidget(m_statusBar); connect(m_albumTable->selectionModel(), SIGNAL(currentRowChanged(QModelIndex,QModelIndex)), this, SLOT(showFilenameInStatusBar(QModelIndex))); } /** * Destructor. */ ServerTrackImportDialog::~ServerTrackImportDialog() { stopClient(); } /** * Set importer to be used. * * @param source import source to use */ void ServerTrackImportDialog::setImportSource(ServerTrackImporter* source) { if (m_client) { disconnect(m_client, SIGNAL(statusChanged(int,QString)), this, SLOT(setFileStatus(int,QString))); disconnect(m_client, SIGNAL(resultsReceived(int,ImportTrackDataVector&)), this, SLOT(setResults(int,ImportTrackDataVector&))); } m_client = source; if (m_client) { connect(m_client, SIGNAL(statusChanged(int,QString)), this, SLOT(setFileStatus(int,QString))); connect(m_client, SIGNAL(resultsReceived(int,ImportTrackDataVector&)), this, SLOT(setResults(int,ImportTrackDataVector&))); setWindowTitle(QCoreApplication::translate("@default", m_client->name())); if (m_client->defaultServer()) { m_serverLabel->show(); m_serverComboBox->show(); if (m_client->serverList()) { QStringList strList; for (const char** sl = m_client->serverList(); *sl != 0; ++sl) { strList += QString::fromLatin1(*sl); } m_serverComboBox->clear(); m_serverComboBox->addItems(strList); } } else { m_serverLabel->hide(); m_serverComboBox->hide(); } if (m_client->helpAnchor()) { m_helpButton->show(); } else { m_helpButton->hide(); } if (m_client->config()) { m_saveButton->show(); } else { m_saveButton->hide(); } } } /** * Initialize the table model. * Has to be called before reusing the dialog with new track data. */ void ServerTrackImportDialog::initTable() { if (m_client && m_client->config()) { setServer(m_client->config()->m_server); } unsigned numRows = 0; const ImportTrackDataVector& trackDataVector(m_trackDataModel->trackData()); for (ImportTrackDataVector::const_iterator it = trackDataVector.constBegin(); it != trackDataVector.constEnd(); ++it) { if (it->isEnabled()) { ++numRows; } } m_trackResults.resize(numRows); m_albumTableModel->setRowCount(numRows); for (unsigned i = 0; i < numRows; ++i) { QStandardItem* item = new QStandardItem; QStringList cbItems; cbItems << tr("No result") << tr("Unknown"); item->setData(cbItems.first(), Qt::EditRole); item->setData(cbItems, Qt::UserRole); m_albumTableModel->setItem(i, 0, item); item = new QStandardItem(tr("Unknown")); item->setFlags(item->flags() & ~Qt::ItemIsEditable); m_albumTableModel->setItem(i, 1, item); } showFilenameInStatusBar(m_albumTable->currentIndex()); } /** * Clear all results. */ void ServerTrackImportDialog::clearResults() { unsigned numRows = m_trackResults.size(); for (unsigned i = 0; i < numRows; ++i) { m_trackResults[i].clear(); setFileStatus(i, tr("Unknown")); updateFileTrackData(i); } } /** * Create and start the track import client. */ void ServerTrackImportDialog::startClient() { if (m_client) { clearResults(); ServerImporterConfig cfg; cfg.m_server = getServer(); m_client->setConfig(&cfg); m_client->start(); } } /** * Stop and destroy the track import client. */ void ServerTrackImportDialog::stopClient() { if (m_client) { m_client->stop(); } } /** * Hides the dialog and sets the result to QDialog::Accepted. */ void ServerTrackImportDialog::accept() { apply(); stopClient(); QDialog::accept(); } /** * Hides the dialog and sets the result to QDialog::Rejected. */ void ServerTrackImportDialog::reject() { stopClient(); QDialog::reject(); } /** * Apply imported data. */ void ServerTrackImportDialog::apply() { ImportTrackDataVector trackDataVector(m_trackDataModel->getTrackData()); trackDataVector.setCoverArtUrl(QString()); ImportTrackDataVector::iterator it = trackDataVector.begin(); bool newTrackData = false; unsigned numRows = m_albumTableModel->rowCount(); for (unsigned index = 0; index < numRows; ++index) { while (it != trackDataVector.end() && !it->isEnabled()) { ++it; } if (it == trackDataVector.end()) { break; } QModelIndex idx(m_albumTableModel->index(index, 0)); if (idx.isValid()) { int selectedItem = idx.data(Qt::UserRole).toStringList().indexOf( idx.data(Qt::EditRole).toString()); if (selectedItem > 0) { const ImportTrackData& selectedData = m_trackResults[index][selectedItem - 1]; it->setTitle(selectedData.getTitle()); it->setArtist(selectedData.getArtist()); it->setAlbum(selectedData.getAlbum()); it->setTrack(selectedData.getTrack()); it->setYear(selectedData.getYear()); it->setImportDuration(selectedData.getImportDuration()); newTrackData = true; } } ++it; } if (newTrackData) { m_trackDataModel->setTrackData(trackDataVector); emit trackDataUpdated(); } } /** * Shows the dialog as a modal dialog. */ int ServerTrackImportDialog::exec() { startClient(); return QDialog::exec(); } /** * Set the status of a file. * * @param index index of file * @param status status string */ void ServerTrackImportDialog::setFileStatus(int index, const QString& status) { m_albumTableModel->setData(m_albumTableModel->index(index, 1), status); } /** * Update the track data combo box of a file. * * @param index index of file */ void ServerTrackImportDialog::updateFileTrackData(int index) { QStringList stringList; unsigned numResults = m_trackResults[index].size(); QString str(numResults == 0 ? tr("No result") : tr("No result selected")); stringList.push_back(str); for (ImportTrackDataVector::const_iterator it = m_trackResults[index].begin(); it != m_trackResults[index].end(); ++it) { str.sprintf("%02d ", (*it).getTrack()); str += (*it).getTitle(); str += QLatin1Char('/'); str += (*it).getArtist(); str += QLatin1String(" - "); str += (*it).getAlbum(); if ((*it).getYear() > 0) { str += QString(QLatin1String(" [%1]")).arg((*it).getYear()); } stringList.push_back(str); } m_albumTableModel->setData(m_albumTableModel->index(index, 0), stringList, Qt::UserRole); m_albumTableModel->setData(m_albumTableModel->index(index, 0), stringList.at(numResults == 1 ? 1 : 0), Qt::EditRole); } /** * Set result list for a file. * * @param index index of file * @param trackDataVector result list */ void ServerTrackImportDialog::setResults( int index, ImportTrackDataVector& trackDataVector) { m_trackResults[index] = trackDataVector; updateFileTrackData(index); } /** * Get string with server and port. * * @return "servername:port". */ QString ServerTrackImportDialog::getServer() const { QString server(m_serverComboBox->currentText()); if (server.isEmpty() && m_client && m_client->defaultServer()) { server = QString::fromLatin1(m_client->defaultServer()); } return server; } /** * Set string with server and port. * * @param srv "servername:port" */ void ServerTrackImportDialog::setServer(const QString& srv) { int idx = m_serverComboBox->findText(srv); if (idx >= 0) { m_serverComboBox->setCurrentIndex(idx); } else { m_serverComboBox->addItem(srv); m_serverComboBox->setCurrentIndex(m_serverComboBox->count() - 1); } } /** * Save the local settings to the configuration. */ void ServerTrackImportDialog::saveConfig() { if (m_client && m_client->config()) { m_client->config()->m_server = getServer(); } } /** * Show help. */ void ServerTrackImportDialog::showHelp() { if (m_client && m_client->helpAnchor()) { ContextHelp::displayHelp(QString::fromLatin1(m_client->helpAnchor())); } } /** * Show the name of the current track in the status bar. * * @param row table row */ void ServerTrackImportDialog::showFilenameInStatusBar(const QModelIndex& index) { if (m_statusBar) { int row = index.row(); int rowNr = 0; const ImportTrackDataVector& trackDataVector(m_trackDataModel->trackData()); for (ImportTrackDataVector::const_iterator it = trackDataVector.constBegin(); it != trackDataVector.constEnd(); ++it) { if (it->isEnabled()) { if (rowNr == row) { m_statusBar->showMessage(it->getFilename()); return; } ++rowNr; } } m_statusBar->clearMessage(); } } kid3-3.0.2/src/gui/dialogs/servertrackimportdialog.h000066400000000000000000000101531224603511300224570ustar00rootroot00000000000000/** * \file servertrackimportdialog.h * Generic dialog for track based import from a server. * * \b Project: Kid3 * \author Urs Fleisch * \date 13 Sep 2005 * * Copyright (C) 2005-2012 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef SERVERTRACKIMPORTDIALOG_H #define SERVERTRACKIMPORTDIALOG_H #include #include #include class QTableView; class QStandardItemModel; class QLineEdit; class QLabel; class QComboBox; class QPushButton; class QCheckBox; class QStatusBar; class QModelIndex; class TrackDataModel; class ImportTrackData; class ImportTrackDataVector; class ServerTrackImporter; /** * Generic dialog for track based import from a server. */ class ServerTrackImportDialog : public QDialog { Q_OBJECT public: /** * Constructor. * * @param parent parent widget * @param trackDataModel track data to be filled with imported values, * is passed with filenames set */ ServerTrackImportDialog(QWidget* parent, TrackDataModel* trackDataModel); /** * Destructor. */ virtual ~ServerTrackImportDialog(); /** * Set importer to be used. * * @param source import source to use */ void setImportSource(ServerTrackImporter* source); /** * Initialize the table model. * Has to be called before reusing the dialog with new track data. */ void initTable(); /** * Get string with server and port. * * @return "servername:port". */ QString getServer() const; /** * Set string with server and port. * * @param srv "servername:port" */ void setServer(const QString& srv); signals: /** * Emitted when the m_trackDataModel was updated with new imported data. */ void trackDataUpdated(); public slots: /** * Shows the dialog as a modal dialog. */ int exec(); protected slots: /** * Hides the dialog and sets the result to QDialog::Accepted. */ virtual void accept(); /** * Hides the dialog and sets the result to QDialog::Rejected. */ virtual void reject(); private slots: /** * Apply imported data. */ void apply(); /** * Set the status of a file. * * @param index index of file * @param status status string */ void setFileStatus(int index, const QString& status); /** * Update the track data combo box of a file. * * @param index index of file */ void updateFileTrackData(int index); /** * Set result list for a file. * * @param index index of file * @param trackDataVector result list */ void setResults(int index, ImportTrackDataVector& trackDataVector); /** * Save the local settings to the configuration. */ void saveConfig(); /** * Show help. */ void showHelp(); /** * Show the name of the current track in the status bar. * * @param row table row */ void showFilenameInStatusBar(const QModelIndex& index); private: /** * Clear all results. */ void clearResults(); /** * Create and start the track import client. */ void startClient(); /** * Stop and destroy the track import client. */ void stopClient(); QLabel* m_serverLabel; QComboBox* m_serverComboBox; QTableView* m_albumTable; QPushButton* m_helpButton; QPushButton* m_saveButton; QStandardItemModel* m_albumTableModel; QStatusBar* m_statusBar; ServerTrackImporter* m_client; TrackDataModel* m_trackDataModel; QVector m_trackResults; }; #endif // SERVERTRACKIMPORTDIALOG_H kid3-3.0.2/src/gui/dialogs/tagimportdialog.cpp000066400000000000000000000105501224603511300212330ustar00rootroot00000000000000/** * \file tagimportdialog.cpp * Dialog to import from other tags. * * \b Project: Kid3 * \author Urs Fleisch * \date 20 Jun 2011 * * Copyright (C) 2011-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "tagimportdialog.h" #include #include #include "textimporter.h" #include "importparser.h" #include "trackdatamodel.h" #include "importconfig.h" #include "contexthelp.h" #include "formatlistedit.h" /** * Constructor. * * @param parent parent widget * @param trackDataModel track data to be filled with imported values */ TagImportDialog::TagImportDialog(QWidget* parent, TrackDataModel* trackDataModel) : QDialog(parent), m_trackDataModel(trackDataModel) { setObjectName(QLatin1String("TagImportDialog")); setWindowTitle(tr("Import from Tags")); setSizeGripEnabled(true); QVBoxLayout* vboxLayout = new QVBoxLayout(this); m_formatListEdit = new FormatListEdit( QStringList() << tr("Format:") << tr("Source:") << tr("Extraction:"), QStringList() << QString() << TrackDataFormatReplacer::getToolTip() << ImportParser::getFormatToolTip(), this); vboxLayout->addWidget(m_formatListEdit); QHBoxLayout* buttonLayout = new QHBoxLayout; QPushButton* helpButton = new QPushButton(tr("&Help"), this); helpButton->setAutoDefault(false); buttonLayout->addWidget(helpButton); connect(helpButton, SIGNAL(clicked()), this, SLOT(showHelp())); QPushButton* saveButton = new QPushButton(tr("&Save Settings"), this); saveButton->setAutoDefault(false); buttonLayout->addWidget(saveButton); connect(saveButton, SIGNAL(clicked()), this, SLOT(saveConfig())); buttonLayout->addStretch(); QPushButton* applyButton = new QPushButton(tr("&Apply"), this); applyButton->setAutoDefault(false); buttonLayout->addWidget(applyButton); connect(applyButton, SIGNAL(clicked()), this, SLOT(apply())); QPushButton* closeButton = new QPushButton(tr("&Close"), this); closeButton->setAutoDefault(false); buttonLayout->addWidget(closeButton); connect(closeButton, SIGNAL(clicked()), this, SLOT(accept())); vboxLayout->addLayout(buttonLayout); } /** * Destructor. */ TagImportDialog::~TagImportDialog() { } /** * Clear dialog data. */ void TagImportDialog::clear() { setFormatFromConfig(); } /** * Apply import to track data. */ void TagImportDialog::apply() { ImportTrackDataVector trackDataVector(m_trackDataModel->getTrackData()); TextImporter::importFromTags(m_formatListEdit->getCurrentFormat(1), m_formatListEdit->getCurrentFormat(2), trackDataVector); m_trackDataModel->setTrackData(trackDataVector); emit trackDataUpdated(); } /** * Set the format combo box and line edits from the configuration. */ void TagImportDialog::setFormatFromConfig() { m_formatListEdit->setFormats( QList() << ImportConfig::instance().m_importTagsNames << ImportConfig::instance().m_importTagsSources << ImportConfig::instance().m_importTagsExtractions, ImportConfig::instance().m_importTagsIdx); } /** * Save the local settings to the configuration. */ void TagImportDialog::saveConfig() { QList formats = m_formatListEdit->getFormats( &ImportConfig::instance().m_importTagsIdx); ImportConfig::instance().m_importTagsNames = formats.at(0); ImportConfig::instance().m_importTagsSources = formats.at(1); ImportConfig::instance().m_importTagsExtractions = formats.at(2); setFormatFromConfig(); } /** * Show help. */ void TagImportDialog::showHelp() { ContextHelp::displayHelp(QLatin1String("import-tags")); } kid3-3.0.2/src/gui/dialogs/tagimportdialog.h000066400000000000000000000036761224603511300207130ustar00rootroot00000000000000/** * \file tagimportdialog.h * Dialog to import from other tags. * * \b Project: Kid3 * \author Urs Fleisch * \date 20 Jun 2011 * * Copyright (C) 2011 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef TAGIMPORTDIALOG_H #define TAGIMPORTDIALOG_H #include class FormatListEdit; class TrackDataModel; /** * Dialog to import from a text (file or clipboard). */ class TagImportDialog : public QDialog { Q_OBJECT public: /** * Constructor. * * @param parent parent widget * @param trackDataModel track data to be filled with imported values */ explicit TagImportDialog(QWidget* parent, TrackDataModel* trackDataModel); /** * Destructor. */ virtual ~TagImportDialog(); /** * Clear dialog data. */ void clear(); private slots: /** * Apply import to track data. */ void apply(); /** * Save the local settings to the configuration. */ void saveConfig(); /** * Show help. */ void showHelp(); signals: /** * Emitted when the m_trackDataVector was updated with new imported data. */ void trackDataUpdated(); private: /** * Set the format combo box and line edits from the configuration. */ void setFormatFromConfig(); FormatListEdit* m_formatListEdit; TrackDataModel* m_trackDataModel; }; #endif kid3-3.0.2/src/gui/dialogs/textimportdialog.cpp000066400000000000000000000136451224603511300214540ustar00rootroot00000000000000/** * \file textimportdialog.cpp * Dialog to import from a text (file or clipboard). * * \b Project: Kid3 * \author Urs Fleisch * \date 19 Jun 2011 * * Copyright (C) 2011-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "textimportdialog.h" #include #include #include #include #include #include #include #include #include "config.h" #include "iplatformtools.h" #include "textimporter.h" #include "importparser.h" #include "importconfig.h" #include "contexthelp.h" #include "formatlistedit.h" /** * Constructor. * * @param platformTools platform tools * @param parent parent widget * @param trackDataModel track data to be filled with imported values */ TextImportDialog::TextImportDialog(IPlatformTools* platformTools, QWidget* parent, TrackDataModel* trackDataModel) : QDialog(parent), m_platformTools(platformTools), m_textImporter(new TextImporter(trackDataModel)) { setObjectName(QLatin1String("TextImportDialog")); setWindowTitle(tr("Import from File/Clipboard")); setSizeGripEnabled(true); QVBoxLayout* vboxLayout = new QVBoxLayout(this); QString formatToolTip = ImportParser::getFormatToolTip(); m_formatListEdit = new FormatListEdit( QStringList() << tr("Format:") << tr("Header:") << tr("Tracks:"), QStringList() << QString() << formatToolTip << formatToolTip, this); vboxLayout->addWidget(m_formatListEdit); QHBoxLayout* buttonLayout = new QHBoxLayout; QPushButton* helpButton = new QPushButton(tr("&Help"), this); helpButton->setAutoDefault(false); buttonLayout->addWidget(helpButton); connect(helpButton, SIGNAL(clicked()), this, SLOT(showHelp())); QPushButton* saveButton = new QPushButton(tr("&Save Settings"), this); saveButton->setAutoDefault(false); buttonLayout->addWidget(saveButton); connect(saveButton, SIGNAL(clicked()), this, SLOT(saveConfig())); buttonLayout->addStretch(); QPushButton* fileButton = new QPushButton(tr("From F&ile"), this); fileButton->setAutoDefault(false); buttonLayout->addWidget(fileButton); connect(fileButton, SIGNAL(clicked()), this, SLOT(fromFile())); QPushButton* clipButton = new QPushButton(tr("From Clip&board"), this); clipButton->setAutoDefault(false); buttonLayout->addWidget(clipButton); connect(clipButton, SIGNAL(clicked()), this, SLOT(fromClipboard())); QPushButton* closeButton = new QPushButton(tr("&Close"), this); closeButton->setAutoDefault(false); buttonLayout->addWidget(closeButton); connect(closeButton, SIGNAL(clicked()), this, SLOT(accept())); vboxLayout->addLayout(buttonLayout); } /** * Destructor. */ TextImportDialog::~TextImportDialog() { delete m_textImporter; } /** * Clear dialog data. */ void TextImportDialog::clear() { setFormatFromConfig(); } /** * Set the format combo box and line edits from the configuration. */ void TextImportDialog::setFormatFromConfig() { m_formatListEdit->setFormats( QList() << ImportConfig::instance().m_importFormatNames << ImportConfig::instance().m_importFormatHeaders << ImportConfig::instance().m_importFormatTracks, ImportConfig::instance().m_importFormatIdx); } /** * Import from a file. * * @param fn file name * * @return true if ok. */ bool TextImportDialog::importFromFile(const QString& fn) { if (!fn.isEmpty()) { QFile file(fn); if (file.open(QIODevice::ReadOnly)) { ImportConfig::instance().m_importDir = QFileInfo(file).dir().path(); QTextStream stream(&file); QString text = stream.readAll(); if (!text.isNull() && m_textImporter->updateTrackData( text, m_formatListEdit->getCurrentFormat(1), m_formatListEdit->getCurrentFormat(2))) { emit trackDataUpdated(); } file.close(); return true; } } return false; } /** * Let user select file, assign file contents to text and preview in * table. */ void TextImportDialog::fromFile() { importFromFile(m_platformTools->getOpenFileName(this, QString(), ImportConfig::instance().m_importDir, QString(), 0) ); } /** * Assign clipboard contents to text and preview in table. */ void TextImportDialog::fromClipboard() { QClipboard* cb = QApplication::clipboard(); QString text = cb->text(QClipboard::Clipboard); if (text.isNull()) text = cb->text(QClipboard::Selection); if (!text.isNull() && m_textImporter->updateTrackData( text, m_formatListEdit->getCurrentFormat(1), m_formatListEdit->getCurrentFormat(2))) emit trackDataUpdated(); } /** * Save the local settings to the configuration. */ void TextImportDialog::saveConfig() { QList formats = m_formatListEdit->getFormats( &ImportConfig::instance().m_importFormatIdx); ImportConfig::instance().m_importFormatNames = formats.at(0); ImportConfig::instance().m_importFormatHeaders = formats.at(1); ImportConfig::instance().m_importFormatTracks = formats.at(2); setFormatFromConfig(); } /** * Show help. */ void TextImportDialog::showHelp() { ContextHelp::displayHelp(QLatin1String("import-text")); } kid3-3.0.2/src/gui/dialogs/textimportdialog.h000066400000000000000000000046451224603511300211210ustar00rootroot00000000000000/** * \file textimportdialog.h * Dialog to import from a text (file or clipboard). * * \b Project: Kid3 * \author Urs Fleisch * \date 19 Jun 2011 * * Copyright (C) 2011 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef TEXTIMPORTDIALOG_H #define TEXTIMPORTDIALOG_H #include class TextImporter; class TrackDataModel; class FormatListEdit; class IPlatformTools; /** * Dialog to import from a text (file or clipboard). */ class TextImportDialog : public QDialog { Q_OBJECT public: /** * Constructor. * * @param platformTools platform tools * @param parent parent widget * @param trackDataModel track data to be filled with imported values */ TextImportDialog(IPlatformTools* platformTools, QWidget* parent, TrackDataModel* trackDataModel); /** * Destructor. */ virtual ~TextImportDialog(); /** * Clear dialog data. */ void clear(); private slots: /** * Let user select file, assign file contents to text and preview in * table. */ void fromFile(); /** * Assign clipboard contents to text and preview in table. */ void fromClipboard(); /** * Save the local settings to the configuration. */ void saveConfig(); /** * Show help. */ void showHelp(); signals: /** * Emitted when the m_trackDataVector was updated with new imported data. */ void trackDataUpdated(); private: /** * Import from a file. * * @param fn file name * * @return true if ok. */ bool importFromFile(const QString& fn); /** * Set the format combo box and line edits from the configuration. */ void setFormatFromConfig(); IPlatformTools* m_platformTools; /** format editor */ FormatListEdit* m_formatListEdit; /** text importer */ TextImporter* m_textImporter; }; #endif kid3-3.0.2/src/gui/forms/000077500000000000000000000000001224603511300150445ustar00rootroot00000000000000kid3-3.0.2/src/gui/forms/Sources.cmake000066400000000000000000000003061224603511300174700ustar00rootroot00000000000000set(forms_SRCS forms/filelist.cpp forms/kid3form.cpp forms/basemainwindow.cpp forms/iplatformtools.cpp ) set(forms_MOC_HDRS forms/filelist.h forms/kid3form.h forms/basemainwindow.h ) kid3-3.0.2/src/gui/forms/basemainwindow.cpp000066400000000000000000001050151224603511300205610ustar00rootroot00000000000000/** * \file basemainwindow.cpp * Base class for main window. * * \b Project: Kid3 * \author Urs Fleisch * \date 9 Jan 2003 * * Copyright (C) 2003-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "basemainwindow.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kid3form.h" #include "kid3application.h" #include "framelist.h" #include "frametablemodel.h" #include "frametable.h" #include "importdialog.h" #include "batchimportdialog.h" #include "browsecoverartdialog.h" #include "exportdialog.h" #include "numbertracksdialog.h" #include "filterdialog.h" #include "rendirdialog.h" #include "downloadclient.h" #include "downloaddialog.h" #include "playlistdialog.h" #include "editframedialog.h" #include "editframefieldsdialog.h" #include "fileproxymodel.h" #include "fileproxymodeliterator.h" #include "modeliterator.h" #include "filelist.h" #include "dirlist.h" #include "pictureframe.h" #include "fileconfig.h" #include "playlistconfig.h" #include "exportconfig.h" #include "guiconfig.h" #include "tagconfig.h" #include "filterconfig.h" #include "contexthelp.h" #include "frame.h" #include "textexporter.h" #include "serverimporter.h" #include "batchimporter.h" #include "dirrenamer.h" #include "iplatformtools.h" #include "qtcompatmac.h" #include "saferename.h" #if defined HAVE_PHONON || QT_VERSION >= 0x050000 #include "audioplayer.h" #endif #if defined HAVE_PHONON || QT_VERSION >= 0x050000 #include "playtoolbar.h" #endif /** * Constructor. * * @param mainWin main window widget * @param platformTools platform specific tools */ BaseMainWindowImpl::BaseMainWindowImpl(QMainWindow* mainWin, IPlatformTools* platformTools) : m_platformTools(platformTools), m_w(mainWin), m_self(0), m_app(new Kid3Application(m_platformTools, this)), m_importDialog(0), m_batchImportDialog(0), m_browseCoverArtDialog(0), m_exportDialog(0), m_renDirDialog(0), m_numberTracksDialog(0), m_filterDialog(0), m_downloadDialog(new DownloadDialog(m_w, tr("Download"))), m_playlistDialog(0), m_progressDialog(0) #if defined HAVE_PHONON || QT_VERSION >= 0x050000 , m_playToolBar(0) #endif { ContextHelp::init(m_platformTools); DownloadClient* downloadClient = m_app->getDownloadClient(); connect(downloadClient, SIGNAL(progress(QString,int,int)), m_downloadDialog, SLOT(updateProgressStatus(QString,int,int))); connect(downloadClient, SIGNAL(downloadStarted(QString)), m_downloadDialog, SLOT(showStartOfDownload(QString))); connect(downloadClient, SIGNAL(aborted()), m_downloadDialog, SLOT(reset())); connect(m_downloadDialog, SIGNAL(canceled()), downloadClient, SLOT(cancelDownload())); connect(downloadClient, SIGNAL(downloadFinished(QByteArray,QString,QString)), m_app, SLOT(imageDownloaded(QByteArray,QString,QString))); connect(m_app, SIGNAL(fileSelectionUpdateRequested()), this, SLOT(updateCurrentSelection())); connect(m_app, SIGNAL(selectedFilesUpdated()), this, SLOT(updateGuiControls())); connect(m_app, SIGNAL(frameModified(TaggedFile*)), this, SLOT(updateAfterFrameModification(TaggedFile*))); connect(m_app, SIGNAL(fileModified()), this, SLOT(updateModificationState())); connect(m_app, SIGNAL(confirmedOpenDirectoryRequested(QStringList)), this, SLOT(confirmedOpenDirectory(QStringList))); connect(m_app, SIGNAL(directoryOpened(QPersistentModelIndex,QList)), this, SLOT(onDirectoryOpened())); #if defined HAVE_PHONON || QT_VERSION >= 0x050000 connect(m_app, SIGNAL(aboutToPlayAudio()), this, SLOT(showPlayToolBar())); #endif } /** * Destructor. */ BaseMainWindowImpl::~BaseMainWindowImpl() { delete m_importDialog; delete m_batchImportDialog; delete m_renDirDialog; delete m_numberTracksDialog; delete m_filterDialog; delete m_browseCoverArtDialog; delete m_playlistDialog; #if defined HAVE_PHONON || QT_VERSION >= 0x050000 delete m_playToolBar; #endif } /** * Initialize main window. * Shall be called at end of constructor body. */ void BaseMainWindowImpl::init() { m_w->statusBar()->showMessage(tr("Ready.")); m_form = new Kid3Form(m_app, this, m_w); m_w->setCentralWidget(m_form); m_self->initActions(); m_w->resize(m_w->sizeHint()); readOptions(); } /** * Open directory, user has to confirm if current directory modified. * * @param paths directory or file paths */ void BaseMainWindowImpl::confirmedOpenDirectory(const QStringList& paths) { if (!saveModified()) { return; } QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); slotStatusMsg(tr("Opening directory...")); m_app->openDirectory(paths, false); slotStatusMsg(tr("Ready.")); QApplication::restoreOverrideCursor(); } /** * Update the recent file list and the caption when a new directory * is opened. */ void BaseMainWindowImpl::onDirectoryOpened() { m_self->addDirectoryToRecentFiles(m_app->getDirName()); updateWindowCaption(); } /** * Save application options. */ void BaseMainWindowImpl::saveOptions() { m_self->saveConfig(); m_form->saveConfig(); m_app->saveConfig(); } /** * Load application options. */ void BaseMainWindowImpl::readOptions() { m_app->readConfig(); m_self->readConfig(); m_form->readConfig(); } /** * Save all changed files. * * @param updateGui true to update GUI (controls, status, cursor) */ void BaseMainWindowImpl::saveDirectory(bool updateGui) { if (updateGui) { updateCurrentSelection(); QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); slotStatusMsg(tr("Saving directory...")); } QProgressBar* progress = new QProgressBar; m_w->statusBar()->addPermanentWidget(progress); progress->setMinimum(0); connect(m_app, SIGNAL(saveStarted(int)), progress, SLOT(setMaximum(int))); connect(m_app, SIGNAL(saveProgress(int)), progress, SLOT(setValue(int))); qApp->processEvents(); QStringList errorFiles = m_app->saveDirectory(); m_w->statusBar()->removeWidget(progress); delete progress; updateModificationState(); if (!errorFiles.empty()) { m_platformTools->errorList( m_w, tr("Error while writing file:\n"), errorFiles, tr("File Error")); } if (updateGui) { slotStatusMsg(tr("Ready.")); QApplication::restoreOverrideCursor(); updateGuiControls(); } } /** * If anything was modified, save after asking user. * * @param doNotRevert if true, modifications are not reverted, this can be * used to skip the possibly long process if the application is not be closed * * @return false if user canceled. */ bool BaseMainWindowImpl::saveModified(bool doNotRevert) { bool completed=true; if(m_app->isModified() && !m_app->getDirName().isEmpty()) { int want_save = m_platformTools->warningYesNoCancel( m_w, tr("The current directory has been modified.\n" "Do you want to save it?"), tr("Warning")); switch(want_save) { case QMessageBox::Yes: saveDirectory(); completed=true; break; case QMessageBox::No: if (!doNotRevert) { if (m_form->getFileList()->selectionModel()) m_form->getFileList()->selectionModel()->clearSelection(); m_app->revertFileModifications(); m_app->setModified(false); } completed=true; break; case QMessageBox::Cancel: completed=false; break; default: completed=false; break; } } return completed; } /** * Free allocated resources. * Our destructor may not be called, so cleanup is done here. */ void BaseMainWindowImpl::cleanup() { m_app->getSettings()->sync(); } /** * Update modification state before closing. * If anything was modified, save after asking user. * Save options before closing. * This method shall be called by closeEvent() (Qt) or * queryClose() (KDE). * * @return false if user canceled, * true will quit the application. */ bool BaseMainWindowImpl::queryBeforeClosing() { updateCurrentSelection(); if (saveModified(true)) { saveOptions(); cleanup(); return true; } return false; } /** * Request new directory and open it. */ void BaseMainWindowImpl::slotFileOpen() { updateCurrentSelection(); if(saveModified()) { static QString flt = m_app->createFilterString(); QString filter(FileConfig::instance().m_nameFilter); QStringList dirs = m_platformTools->getOpenFileNames( m_w, QString(), m_app->getDirName(), flt, &filter); if (!dirs.isEmpty()) { if (!filter.isEmpty()) { FileConfig::instance().m_nameFilter = filter; } m_app->openDirectory(dirs); } } } /** * Request new directory and open it. */ void BaseMainWindowImpl::slotFileOpenDirectory() { updateCurrentSelection(); if(saveModified()) { QString dir = m_platformTools->getExistingDirectory(m_w, QString(), m_app->getDirName()); if (!dir.isEmpty()) { m_app->openDirectory(QStringList() << dir); } } } /** * Open recent directory. * * @param dir directory to open */ void BaseMainWindowImpl::openRecentDirectory(const QString& dir) { updateCurrentSelection(); confirmedOpenDirectory(QStringList() << dir); } /** * Save modified files. */ void BaseMainWindowImpl::slotFileSave() { saveDirectory(true); } /** * Quit application. */ void BaseMainWindowImpl::slotFileQuit() { slotStatusMsg(tr("Exiting...")); m_w->close(); /* this will lead to call of closeEvent(), queryClose() */ } /** * Change status message. * * @param text message */ void BaseMainWindowImpl::slotStatusMsg(const QString& text) { m_w->statusBar()->showMessage(text); // processEvents() is necessary to make the change of the status bar // visible when it is changed back again in the same function, // i.e. in the same call from the Qt main event loop. qApp->processEvents(); } /** * Show playlist dialog. */ void BaseMainWindowImpl::slotPlaylistDialog() { if (!m_playlistDialog) { m_playlistDialog = new PlaylistDialog(m_w); } m_playlistDialog->readConfig(); if (m_playlistDialog->exec() == QDialog::Accepted) { PlaylistConfig cfg; m_playlistDialog->getCurrentConfig(cfg); writePlaylist(cfg); } } /** * Write playlist according to playlist configuration. * * @param cfg playlist configuration to use * * @return true if ok. */ bool BaseMainWindowImpl::writePlaylist(const PlaylistConfig& cfg) { QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); slotStatusMsg(tr("Creating playlist...")); bool ok = m_app->writePlaylist(cfg); slotStatusMsg(tr("Ready.")); QApplication::restoreOverrideCursor(); return ok; } /** * Create playlist. * * @return true if ok. */ bool BaseMainWindowImpl::slotCreatePlaylist() { return writePlaylist(PlaylistConfig::instance()); } /** * Update track data and create import dialog. */ void BaseMainWindowImpl::setupImportDialog() { m_app->filesToTrackDataModel(ImportConfig::instance().m_importDest); if (!m_importDialog) { QString caption(tr("Import")); m_importDialog = new ImportDialog(m_platformTools, m_w, caption, m_app->getTrackDataModel(), m_app->getServerImporters(), m_app->getServerTrackImporters()); connect(m_importDialog, SIGNAL(accepted()), this, SLOT(applyImportedTrackData())); } m_importDialog->clear(); } /** * Set tagged files of directory from imported track data model. */ void BaseMainWindowImpl::applyImportedTrackData() { m_app->trackDataModelToFiles(m_importDialog->getDestination()); } /** * Import. */ void BaseMainWindowImpl::slotImport() { if (QAction* action = qobject_cast(sender())) { setupImportDialog(); if (m_importDialog) { m_importDialog->showWithSubDialog(action->data().toInt()); } } } /** * Batch import. */ void BaseMainWindowImpl::slotBatchImport() { if (!m_batchImportDialog) { m_batchImportDialog = new BatchImportDialog(m_app->getServerImporters(), m_w); connect(m_batchImportDialog, SIGNAL(start(BatchImportProfile,TrackData::TagVersion)), m_app, SLOT(batchImport(BatchImportProfile,TrackData::TagVersion))); connect(m_app->getBatchImporter(), SIGNAL(reportImportEvent(BatchImportProfile::ImportEventType, QString)), m_batchImportDialog, SLOT(showImportEvent(BatchImportProfile::ImportEventType, QString))); connect(m_batchImportDialog, SIGNAL(abort()), m_app->getBatchImporter(), SLOT(abort())); connect(m_app->getBatchImporter(), SIGNAL(finished()), this, SLOT(updateGuiControls())); } m_app->getBatchImporter()->clearAborted(); m_batchImportDialog->readConfig(); m_batchImportDialog->show(); } /** * Browse album cover artwork. */ void BaseMainWindowImpl::slotBrowseCoverArt() { if (!m_browseCoverArtDialog) { m_browseCoverArtDialog = new BrowseCoverArtDialog(m_w); } FrameCollection frames2; QModelIndex index = m_form->getFileList()->currentIndex(); if (TaggedFile* taggedFile = FileProxyModel::getTaggedFileOfIndex(index)) { taggedFile->readTags(false); FrameCollection frames1; taggedFile->getAllFramesV1(frames1); taggedFile->getAllFramesV2(frames2); frames2.merge(frames1); } m_browseCoverArtDialog->readConfig(); m_browseCoverArtDialog->setFrames(frames2); m_browseCoverArtDialog->exec(); } /** * Export. */ void BaseMainWindowImpl::slotExport() { m_exportDialog = new ExportDialog( m_platformTools, m_w, m_app->getTextExporter()); m_exportDialog->readConfig(); ImportTrackDataVector trackDataVector; m_app->filesToTrackData(ExportConfig::instance().m_exportSrcV1, trackDataVector); m_app->getTextExporter()->setTrackData(trackDataVector); m_exportDialog->showPreview(); m_exportDialog->exec(); delete m_exportDialog; m_exportDialog = 0; } /** * Toggle auto hiding of tags. */ void BaseMainWindowImpl::slotSettingsAutoHideTags() { GuiConfig::instance().m_autoHideTags = m_self->autoHideTagsAction()->isChecked(); updateCurrentSelection(); updateGuiControls(); } /** * Show or hide picture. */ void BaseMainWindowImpl::slotSettingsShowHidePicture() { GuiConfig::instance().m_hidePicture = !m_self->showHidePictureAction()->isChecked(); m_form->hidePicture(GuiConfig::instance().m_hidePicture); // In Qt3 the picture is displayed too small if Kid3 is started with picture // hidden, and then "Show Picture" is triggered while a file with a picture // is selected. Thus updating the controls is only done for Qt4, in Qt3 the // file has to be selected again for the picture to be shown. if (!GuiConfig::instance().m_hidePicture) { updateGuiControls(); } } /** * Apply configuration changes. */ void BaseMainWindowImpl::applyChangedConfiguration() { m_app->saveConfig(); if (!TagConfig::instance().markTruncations()) { m_app->frameModelV1()->markRows(0); } if (!FileConfig::instance().m_markChanges) { m_app->frameModelV1()->markChangedFrames(0); m_app->frameModelV2()->markChangedFrames(0); m_form->markChangedFilename(false); } m_app->notifyConfigurationChange(); quint64 oldQuickAccessFrames = FrameCollection::getQuickAccessFrames(); if (TagConfig::instance().quickAccessFrames() != oldQuickAccessFrames) { FrameCollection::setQuickAccessFrames( TagConfig::instance().quickAccessFrames()); updateGuiControls(); } } /** * Rename directory. */ void BaseMainWindowImpl::slotRenameDirectory() { if (saveModified()) { if (!m_renDirDialog) { m_renDirDialog = new RenDirDialog(m_w, m_app->getDirRenamer()); connect(m_renDirDialog, SIGNAL(actionSchedulingRequested()), m_app, SLOT(scheduleRenameActions())); connect(m_app->getDirRenamer(), SIGNAL(actionScheduled(QStringList)), m_renDirDialog, SLOT(displayActionPreview(QStringList))); } if (TaggedFile* taggedFile = TaggedFileOfDirectoryIterator::first(m_app->currentOrRootIndex())) { m_renDirDialog->startDialog(taggedFile); } else { m_renDirDialog->startDialog(0, m_app->getDirName()); } if (m_renDirDialog->exec() == QDialog::Accepted) { QString errorMsg(m_app->performRenameActions()); if (!errorMsg.isEmpty()) { m_platformTools->warningDialog(m_w, tr("Error while renaming:\n"), errorMsg, tr("File Error")); } } } } /** * Number tracks. */ void BaseMainWindowImpl::slotNumberTracks() { if (!m_numberTracksDialog) { m_numberTracksDialog = new NumberTracksDialog(m_w); } m_numberTracksDialog->setTotalNumberOfTracks( m_app->getTotalNumberOfTracksInDir(), TagConfig::instance().enableTotalNumberOfTracks()); if (m_numberTracksDialog->exec() == QDialog::Accepted) { int nr = m_numberTracksDialog->getStartNumber(); bool totalEnabled; int total = m_numberTracksDialog->getTotalNumberOfTracks(&totalEnabled); if (!totalEnabled) total = 0; TagConfig::instance().setEnableTotalNumberOfTracks(totalEnabled); m_app->numberTracks(nr, total, m_numberTracksDialog->getDestination()); } } /** * Filter. */ void BaseMainWindowImpl::slotFilter() { if (saveModified()) { if (!m_filterDialog) { m_filterDialog = new FilterDialog(m_w); connect(m_filterDialog, SIGNAL(apply(FileFilter&)), m_app, SLOT(applyFilter(FileFilter&))); connect(m_app, SIGNAL(fileFiltered(FileFilter::FilterEventType,QString)), m_filterDialog, SLOT(showFilterEvent(FileFilter::FilterEventType,QString))); } FilterConfig::instance().setFilenameFormat( m_app->getTagsToFilenameFormat()); m_filterDialog->readConfig(); m_filterDialog->show(); } } #if defined HAVE_PHONON || QT_VERSION >= 0x050000 /** * Play audio file. */ void BaseMainWindowImpl::slotPlayAudio() { m_app->playAudio(); } /** * Show play tool bar. */ void BaseMainWindowImpl::showPlayToolBar() { if (!m_playToolBar) { m_playToolBar = new PlayToolBar(m_app->getAudioPlayer(), m_w); m_playToolBar->setAllowedAreas(Qt::TopToolBarArea | Qt::BottomToolBarArea); m_w->addToolBar(Qt::BottomToolBarArea, m_playToolBar); connect(m_playToolBar, SIGNAL(errorMessage(QString)), this, SLOT(slotStatusMsg(QString))); } m_playToolBar->show(); } #endif /** * Update modification state, caption and listbox entries. */ void BaseMainWindowImpl::updateModificationState() { bool modified = false; TaggedFileIterator it(m_form->getFileList()->rootIndex()); while (it.hasNext()) { TaggedFile* taggedFile = it.next(); if (taggedFile->isChanged()) { modified = true; m_form->getFileList()->dataChanged(taggedFile->getIndex(), taggedFile->getIndex()); } } m_app->setModified(modified); updateWindowCaption(); } /** * Set window title with information from directory, filter and modification * state. */ void BaseMainWindowImpl::updateWindowCaption() { QString cap; if (!m_app->getDirName().isEmpty()) { cap += QDir(m_app->getDirName()).dirName(); } if (m_app->isFiltered()) { cap += tr(" [filtered]"); } m_self->setWindowCaption(cap, m_app->isModified()); } /** * Update files of current selection. */ void BaseMainWindowImpl::updateCurrentSelection() { const QList& selItems = m_form->getFileList()->getCurrentSelection(); if (!selItems.isEmpty()) { m_form->frameTableV1()->acceptEdit(); m_form->frameTableV2()->acceptEdit(); m_app->frameModelsToTags(selItems); if (m_form->isFilenameEditEnabled()) { if (TaggedFile* taggedFile = FileProxyModel::getTaggedFileOfIndex(selItems.first())) { taggedFile->setFilename(m_form->getFilename()); } } } updateModificationState(); } /** * Update GUI controls from the tags in the files. * The new selection is stored and the GUI controls and frame list * updated accordingly (filtered for multiple selection). */ void BaseMainWindowImpl::updateGuiControls() { m_form->getFileList()->updateCurrentSelection(); const QList& selItems = m_form->getFileList()->getCurrentSelection(); m_app->tagsToFrameModels(selItems); TaggedFile::DetailInfo info; if (const TaggedFile* selectedFile = m_app->selectionSingleFile()) { m_form->setFilenameEditEnabled(true); m_form->setFilename(selectedFile->getFilename()); selectedFile->getDetailInfo(info); m_form->setDetailInfo(info); m_form->setTagFormatV1(selectedFile->getTagFormatV1()); m_form->setTagFormatV2(selectedFile->getTagFormatV2()); if (FileConfig::instance().m_markChanges) { m_form->markChangedFilename(selectedFile->isFilenameChanged()); } } else { if (m_app->selectionFileCount() > 1) { m_form->setFilenameEditEnabled(false); m_form->setFilename(Frame::differentRepresentation()); } m_form->setDetailInfo(info); m_form->setTagFormatV1(QString()); m_form->setTagFormatV2(QString()); if (FileConfig::instance().m_markChanges) { m_form->markChangedFilename(false); } } if (!GuiConfig::instance().m_hidePicture) { FrameCollection::const_iterator it = m_app->frameModelV2()->frames().find( Frame(Frame::FT_Picture, QLatin1String(""), QLatin1String(""), -1)); if (it == m_app->frameModelV2()->frames().end() || it->isInactive()) { m_form->setPictureData(0); } else { QByteArray data; m_form->setPictureData(PictureFrame::getData(*it, data) ? &data : 0); } } updateModificationState(); m_form->enableControlsV1(m_app->selectionTagV1SupportedCount() > 0 || m_app->selectionFileCount() == 0); if (GuiConfig::instance().m_autoHideTags) { m_form->hideV1(!m_app->selectionHasTagV1()); m_form->hideV2(!m_app->selectionHasTagV2()); } } /** * Update ID3v2 tags in GUI controls from file displayed in frame list. * * @param taggedFile the selected file */ void BaseMainWindowImpl::updateAfterFrameModification(TaggedFile* taggedFile) { if (taggedFile) { FrameCollection frames; taggedFile->getAllFramesV2(frames); m_app->frameModelV2()->transferFrames(frames); updateModificationState(); } } /** * Get type of frame from translated name. * * @param name name, spaces and case are ignored * * @return type. */ static Frame::Type getTypeFromTranslatedName(QString name) { static QMap strNumMap; if (strNumMap.empty()) { // first time initialization for (int i = 0; i <= Frame::FT_LastFrame; ++i) { Frame::Type type = static_cast(i); strNumMap.insert(Frame::ExtendedType(type, QLatin1String("")).getTranslatedName(). remove(QLatin1Char(' ')).toUpper(), type); } } QMap::const_iterator it = strNumMap.find(name.remove(QLatin1Char(' ')).toUpper()); if (it != strNumMap.end()) { return static_cast(*it); } return Frame::FT_Other; } /** * Let user select a frame type. * * @param frame is filled with the selected frame if true is returned * @param taggedFile tagged file for which frame has to be selected * * @return false if no frame selected. */ bool BaseMainWindowImpl::selectFrame(Frame* frame, const TaggedFile* taggedFile) { bool ok = false; if (taggedFile && frame) { QString name = QInputDialog::getItem( m_w, tr("Add Frame"), tr("Select the frame ID"), taggedFile->getFrameIds(), 0, true, &ok); if (ok) { Frame::Type type = getTypeFromTranslatedName(name); *frame = Frame(type, QLatin1String(""), name, -1); } } return ok; } /** * Create dialog to edit a frame and update the fields * if Ok is returned. * * @param frame frame to edit * @param taggedFile tagged file where frame has to be set * * @return true if Ok selected in dialog. */ bool BaseMainWindowImpl::editFrameOfTaggedFile(Frame* frame, TaggedFile* taggedFile) { if (!frame || !taggedFile) return false; bool result = true; QString name(frame->getInternalName()); if (!name.isEmpty()) { int nlPos = name.indexOf(QLatin1Char('\n')); if (nlPos > 0) { // probably "TXXX - User defined text information\nDescription" or // "WXXX - User defined URL link\nDescription" name.truncate(nlPos); } name = QCoreApplication::translate("@default", name.toLatin1().data()); } if (frame->getFieldList().empty()) { EditFrameDialog* dialog = new EditFrameDialog(m_w, name, frame->getValue()); result = dialog && dialog->exec() == QDialog::Accepted; if (result) { frame->setValue(dialog->getText()); } } else { EditFrameFieldsDialog* dialog = new EditFrameFieldsDialog(m_platformTools, m_w, name, *frame, taggedFile); result = dialog && dialog->exec() == QDialog::Accepted; if (result) { frame->setFieldList(dialog->getUpdatedFieldList()); frame->setValueFromFieldList(); } } if (result) { if (taggedFile->setFrameV2(*frame)) { taggedFile->markTag2Changed(frame->getType()); } } return result; } /** * Rename the selected file(s). */ void BaseMainWindowImpl::renameFile() { QItemSelectionModel* selectModel = m_form->getFileList()->selectionModel(); FileProxyModel* model = qobject_cast(m_form->getFileList()->model()); if (!selectModel || !model) return; QList selItems; foreach (const QModelIndex& index, selectModel->selectedIndexes()) selItems.append(index); foreach (const QPersistentModelIndex& index, selItems) { TaggedFile* taggedFile = FileProxyModel::getTaggedFileOfIndex(index); QString absFilename, dirName, fileName; if (taggedFile) { absFilename = taggedFile->getAbsFilename(); dirName = taggedFile->getDirname(); fileName = taggedFile->getFilename(); } else { QFileInfo fi(model->fileInfo(index)); absFilename = fi.filePath(); dirName = fi.dir().path(); fileName = fi.fileName(); } bool ok; QString newFileName = QInputDialog::getText( m_w, tr("Rename File"), tr("Enter new file name:"), QLineEdit::Normal, fileName, &ok); if (ok && !newFileName.isEmpty() && newFileName != fileName) { if (taggedFile) { if (taggedFile->isChanged()) { taggedFile->setFilename(newFileName); if (selItems.size() == 1) m_form->setFilename(newFileName); continue; } // This will close the file. // The file must be closed before renaming on Windows. taggedFile->closeFileHandle(); } else if (model->isDir(index)) { // The directory must be closed before renaming on Windows. TaggedFileIterator::closeFileHandles(index); } QString newPath = dirName + QLatin1Char('/') + newFileName; if (!Utils::safeRename(absFilename, newPath)) { QMessageBox::warning( 0, tr("File Error"), tr("Error while renaming:\n") + tr("Rename %1 to %2 failed\n").arg(fileName).arg(newFileName), QMessageBox::Ok, Qt::NoButton); } } } } /** Only defined for generation of translation files */ #define WANT_TO_DELETE_FOR_PO QT_TRANSLATE_NOOP("@default", "Do you really want to move these %1 items to the trash?") /** * Delete the selected file(s). */ void BaseMainWindowImpl::deleteFile() { QItemSelectionModel* selectModel = m_form->getFileList()->selectionModel(); FileProxyModel* model = qobject_cast(m_form->getFileList()->model()); if (!selectModel || !model) return; QStringList files; QList selItems; foreach (const QModelIndex& index, selectModel->selectedIndexes()) selItems.append(index); foreach (const QPersistentModelIndex& index, selItems) { files.append(model->filePath(index)); } unsigned numFiles = files.size(); if (numFiles > 0) { if (m_platformTools->warningContinueCancelList( m_w, numFiles > 1 ? tr("Do you really want to move these %1 items to the trash?"). arg(numFiles) : tr("Do you really want to move this item to the trash?"), files, tr("Move to Trash"))) { bool rmdirError = false; files.clear(); foreach (const QPersistentModelIndex& index, selItems) { QString absFilename(model->filePath(index)); if (model->isDir(index)) { if (!m_platformTools->moveToTrash(absFilename)) { rmdirError = true; files.append(absFilename); } } else { if (TaggedFile* taggedFile = FileProxyModel::getTaggedFileOfIndex(index)) { // This will close the file. // The file must be closed before deleting on Windows. taggedFile->closeFileHandle(); } if (!m_platformTools->moveToTrash(absFilename)) { files.append(absFilename); } } } if (!files.isEmpty()) { QString txt; if (rmdirError) txt += tr("Directory must be empty.\n"); txt += tr("Could not move these files to the Trash"); m_platformTools->errorList(m_w, txt, files, tr("File Error")); } } } } /** * Expand the file list. */ void BaseMainWindowImpl::expandFileList() { m_expandFileListStartTime = QDateTime::currentDateTime(); connect(m_app->getFileProxyModelIterator(), SIGNAL(nextReady(QPersistentModelIndex)), this, SLOT(expandNextDirectory(QPersistentModelIndex))); m_app->getFileProxyModelIterator()->start(m_form->getFileList()->rootIndex()); } /** * Expand item if it is a directory. * * @param index index of file in file proxy model */ void BaseMainWindowImpl::expandNextDirectory(const QPersistentModelIndex& index) { bool terminated = !index.isValid(); if (!terminated) { if (m_app->getFileProxyModel()->isDir(index)) { m_form->getFileList()->expand(index); } if (m_expandFileListStartTime.isValid() && m_expandFileListStartTime.secsTo(QDateTime::currentDateTime()) >= 3) { // Operation is taking some time, show dialog to abort it. m_expandFileListStartTime = QDateTime(); if (!m_progressDialog) { m_progressDialog = new QProgressDialog(m_w); } m_progressDialog->setWindowTitle(tr("Expand All")); m_progressDialog->setLabelText(QString()); m_progressDialog->setCancelButtonText(tr("A&bort")); m_progressDialog->setMinimum(0); m_progressDialog->setMaximum(0); m_progressDialog->setAutoClose(true); m_progressDialog->show(); } if (m_progressDialog && m_progressDialog->wasCanceled()) { terminated = true; } } if (terminated) { m_app->getFileProxyModelIterator()->abort(); disconnect(m_app->getFileProxyModelIterator(), SIGNAL(nextReady(QPersistentModelIndex)), this, SLOT(expandNextDirectory(QPersistentModelIndex))); if (m_progressDialog) { m_progressDialog->reset(); } } } /** * Constructor. * * @param platformTools platform specific tools */ BaseMainWindow::BaseMainWindow(QMainWindow* mainWin, IPlatformTools* platformTools) : m_impl(new BaseMainWindowImpl(mainWin, platformTools)) { m_impl->setBackPointer(this); } /** * Destructor. */ BaseMainWindow::~BaseMainWindow() { delete m_impl; } /** * Initialize main window. * Shall be called at end of constructor body in derived classes. */ void BaseMainWindow::init() { m_impl->init(); } /** * Let user select a frame type. * * @param frame is filled with the selected frame if true is returned * @param taggedFile tagged file for which frame has to be selected * * @return false if no frame selected. */ bool BaseMainWindow::selectFrame(Frame* frame, const TaggedFile* taggedFile) { return m_impl->selectFrame(frame, taggedFile); } /** * Create dialog to edit a frame and update the fields * if Ok is returned. * * @param frame frame to edit * @param taggedFile tagged file where frame has to be set * * @return true if Ok selected in dialog. */ bool BaseMainWindow::editFrameOfTaggedFile(Frame* frame, TaggedFile* taggedFile) { return m_impl->editFrameOfTaggedFile(frame, taggedFile); } #if defined HAVE_PHONON || QT_VERSION >= 0x050000 /** * Play audio file. */ void BaseMainWindow::slotPlayAudio() { m_impl->slotPlayAudio(); } #endif /** * Change status message. * * @param text message */ void BaseMainWindow::slotStatusMsg(const QString& text) { m_impl->slotStatusMsg(text); } /** * Update files of current selection. */ void BaseMainWindow::updateCurrentSelection() { m_impl->updateCurrentSelection(); } /** * Open directory, user has to confirm if current directory modified. * * @param paths directory or file paths */ void BaseMainWindow::confirmedOpenDirectory(const QStringList& paths) { m_impl->confirmedOpenDirectory(paths); } /** * Update modification state before closing. * If anything was modified, save after asking user. * Save options before closing. * This method shall be called by closeEvent() (Qt) or * queryClose() (KDE). * * @return false if user canceled, * true will quit the application. */ bool BaseMainWindow::queryBeforeClosing() { return m_impl->queryBeforeClosing(); } /** * Open recent directory. * * @param dir directory to open */ void BaseMainWindow::openRecentDirectory(const QString& dir) { m_impl->openRecentDirectory(dir); } /** * Set window title with information from directory, filter and modification * state. */ void BaseMainWindow::updateWindowCaption() { m_impl->updateWindowCaption(); } /** * Access to application. * @return application. */ Kid3Application* BaseMainWindow::app() { return m_impl->app(); } /** * Access to main form. * @return main form. */ Kid3Form* BaseMainWindow::form() { return m_impl->form(); } kid3-3.0.2/src/gui/forms/basemainwindow.h000066400000000000000000000313361224603511300202320ustar00rootroot00000000000000/** * \file basemainwindow.h * Base class for main window. * * \b Project: Kid3 * \author Urs Fleisch * \date 9 Jan 2003 * * Copyright (C) 2003-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef BASEMAINWINDOW_H #define BASEMAINWINDOW_H #include #include #include "config.h" #include "iframeeditor.h" #include "trackdata.h" #include "kid3api.h" class QProgressDialog; class Kid3Form; class Kid3Application; class TaggedFile; class ImportDialog; class BatchImportDialog; class ExportDialog; class BrowseCoverArtDialog; class RenDirDialog; class NumberTracksDialog; class RenDirDialog; class FilterDialog; class FileFilter; class DownloadDialog; class PlaylistDialog; class PlaylistConfig; #if defined HAVE_PHONON || QT_VERSION >= 0x050000 class PlayToolBar; #endif class DirContents; class FileProxyModel; class DirProxyModel; class TrackDataModel; class IPlatformTools; class BaseMainWindow; /** * Implementation class for BaseMainWindow. * The reason for this implementation class is that a QObject is needed to * have slots. However, BaseMainWindow cannot inherit from QObject because it * is used with multiple inheritance together with another class which is a * QObject (actually a QMainWindow). Therefore the functionality of the main * put into this class which is then used as an implementation class by * BaseMainWindow. */ class KID3_GUI_EXPORT BaseMainWindowImpl : public QObject, public IFrameEditor { Q_OBJECT public: /** * Constructor. * * @param mainWin main window widget * @param platformTools platform specific tools */ BaseMainWindowImpl(QMainWindow* mainWin, IPlatformTools* platformTools); /** * Destructor. */ virtual ~BaseMainWindowImpl(); /** * Create dialog to edit a frame and update the fields * if Ok is returned. * * @param frame frame to edit * @param taggedFile tagged file where frame has to be set * * @return true if Ok selected in dialog. */ virtual bool editFrameOfTaggedFile(Frame* frame, TaggedFile* taggedFile); /** * Let user select a frame type. * * @param frame is filled with the selected frame if true is returned * @param taggedFile tagged file for which frame has to be selected * * @return false if no frame selected. */ virtual bool selectFrame(Frame* frame, const TaggedFile* taggedFile); /** * Set back pointer for implementation class. * * @param self back pointer */ void setBackPointer(BaseMainWindow* self) { m_self = self; } /** * Initialize main window. * Shall be called at end of constructor body. */ void init(); /** * Update modification state before closing. * If anything was modified, save after asking user. * Save options before closing. * This method shall be called by closeEvent() (Qt) or * queryClose() (KDE). * * @return false if user canceled, * true will quit the application. */ bool queryBeforeClosing(); /** * Open recent directory. * * @param dir directory to open */ void openRecentDirectory(const QString& dir); /** * Set window title with information from directory, filter and modification * state. */ void updateWindowCaption(); /** * Apply configuration changes. */ void applyChangedConfiguration(); /** * Access to application. * @return application. */ Kid3Application* app() { return m_app; } /** * Access to main form. * @return main form. */ Kid3Form* form() { return m_form; } public slots: /** * Open directory, user has to confirm if current directory modified. * * @param paths directory or file paths */ void confirmedOpenDirectory(const QStringList& paths); /** * Update the recent file list and the caption when a new directory * is opened. */ void onDirectoryOpened(); /** * Request new directory and open it. */ void slotFileOpen(); /** * Request new directory and open it. */ void slotFileOpenDirectory(); /** * Save modified files. */ void slotFileSave(); /** * Quit application. */ void slotFileQuit(); /** * Change status message. * * @param text message */ void slotStatusMsg(const QString& text); /** * Show playlist dialog. */ void slotPlaylistDialog(); /** * Create playlist. * * @return true if ok. */ bool slotCreatePlaylist(); /** * Import. */ void slotImport(); /** * Batch import. */ void slotBatchImport(); /** * Browse album cover artwork. */ void slotBrowseCoverArt(); /** * Export. */ void slotExport(); /** * Toggle auto hiding of tags. */ void slotSettingsAutoHideTags(); /** * Show or hide picture. */ void slotSettingsShowHidePicture(); /** * Rename directory. */ void slotRenameDirectory(); /** * Number tracks. */ void slotNumberTracks(); /** * Filter. */ void slotFilter(); #if defined HAVE_PHONON || QT_VERSION >= 0x050000 /** * Play audio file. */ void slotPlayAudio(); #endif /** * Update files of current selection. */ void updateCurrentSelection(); /** * Update GUI controls from the tags in the files. * The new selection is stored and the GUI controls and frame list * updated accordingly (filtered for multiple selection). */ void updateGuiControls(); /** * Rename the selected file(s). */ void renameFile(); /** * Delete the selected file(s). */ void deleteFile(); /** * Expand the file list. */ void expandFileList(); private slots: /** * Update ID3v2 tags in GUI controls from file displayed in frame list. * * @param taggedFile the selected file */ void updateAfterFrameModification(TaggedFile* taggedFile); /** * Update modification state, caption and listbox entries. */ void updateModificationState(); #if defined HAVE_PHONON || QT_VERSION >= 0x050000 /** * Show play tool bar. */ void showPlayToolBar(); #endif /** * Expand item if it is a directory. * * @param index index of file in file proxy model */ void expandNextDirectory(const QPersistentModelIndex& index); /** * Set tagged files of directory from imported track data model. */ void applyImportedTrackData(); private: /** * Free allocated resources. * Our destructor may not be called, so cleanup is done here. */ void cleanup(); /** * Save application options. */ void saveOptions(); /** * Load application options. */ void readOptions(); /** * Save all changed files. * * @param updateGui true to update GUI (controls, status, cursor) */ void saveDirectory(bool updateGui = false); /** * If anything was modified, save after asking user. * * @param doNotRevert if true, modifications are not reverted, this can be * used to skip the possibly long process if the application is not be closed * * @return false if user canceled. */ bool saveModified(bool doNotRevert = false); /** * Update track data and create import dialog. */ void setupImportDialog(); /** * Write playlist according to playlist configuration. * * @param cfg playlist configuration to use * * @return true if ok. */ bool writePlaylist(const PlaylistConfig& cfg); IPlatformTools* m_platformTools; QMainWindow* m_w; BaseMainWindow* m_self; /** GUI with controls */ Kid3Form* m_form; /** Application logic */ Kid3Application* m_app; /** Import dialog */ ImportDialog* m_importDialog; /** Batch import dialog */ BatchImportDialog* m_batchImportDialog; /** Browse cover art dialog */ BrowseCoverArtDialog* m_browseCoverArtDialog; /** Export dialog */ ExportDialog* m_exportDialog; /** Rename directory dialog */ RenDirDialog* m_renDirDialog; /** Number tracks dialog */ NumberTracksDialog* m_numberTracksDialog; /** Filter dialog */ FilterDialog* m_filterDialog; /** Download dialog */ DownloadDialog* m_downloadDialog; /** Playlist dialog */ PlaylistDialog* m_playlistDialog; /** Progress dialog */ QProgressDialog* m_progressDialog; #if defined HAVE_PHONON || QT_VERSION >= 0x050000 /** Play toolbar */ PlayToolBar* m_playToolBar; #endif QDateTime m_expandFileListStartTime; }; /** * Base class for the main window. * The main window classes for Qt (QMainWindow) and KDE (KXmlGuiWindow) * have common functionality. The actual Kid3 main window can inherit from both * the platform dependent main window class and this base class. Differences * between the platforms can be handled by implementing the pure virtual methods * of this class. Because this class cannot be a QObject (QMainWindow is * already a QObject), most of its functionality is delegated to a QObject * implementation class. */ class KID3_GUI_EXPORT BaseMainWindow : public IFrameEditor { public: /** * Constructor. * * @param mainWin main window instance * @param platformTools platform specific tools */ BaseMainWindow(QMainWindow* mainWin, IPlatformTools* platformTools); /** * Destructor. */ virtual ~BaseMainWindow(); /** * Init menu and toolbar actions. */ virtual void initActions() = 0; /** * Add directory to recent files list. * * @param dirName path to directory */ virtual void addDirectoryToRecentFiles(const QString& dirName) = 0; /** * Read settings from the configuration. */ virtual void readConfig() = 0; /** * Store geometry and recent files in settings. */ virtual void saveConfig() = 0; /** * Get action for Settings/Auto Hide Tags. * @return action. */ virtual QAction* autoHideTagsAction() = 0; /** * Get action for Settings/Hide Picture. * @return action. */ virtual QAction* showHidePictureAction() = 0; /** * Set main window caption. * * @param caption caption without application name * @param modified true if any file is modified */ virtual void setWindowCaption(const QString& caption, bool modified) = 0; /** * Create dialog to edit a frame and update the fields * if Ok is returned. * * @param frame frame to edit * @param taggedFile tagged file where frame has to be set * * @return true if Ok selected in dialog. */ virtual bool editFrameOfTaggedFile(Frame* frame, TaggedFile* taggedFile); /** * Let user select a frame type. * * @param frame is filled with the selected frame if true is returned * @param taggedFile tagged file for which frame has to be selected * * @return false if no frame selected. */ virtual bool selectFrame(Frame* frame, const TaggedFile* taggedFile); #if defined HAVE_PHONON || QT_VERSION >= 0x050000 /** * Play audio file. */ void slotPlayAudio(); #endif /** * Update files of current selection. */ void updateCurrentSelection(); /** * Open directory, user has to confirm if current directory modified. * * @param paths directory or file paths */ void confirmedOpenDirectory(const QStringList& paths); /** * Access to implementation object. * @return implementation object. */ BaseMainWindowImpl* impl() { return m_impl; } protected: /** * Initialize main window. * Shall be called at end of constructor body in derived classes. */ void init(); /** * Change status message. * * @param text message */ void slotStatusMsg(const QString& text); /** * Update modification state before closing. * If anything was modified, save after asking user. * Save options before closing. * This method shall be called by closeEvent() (Qt) or * queryClose() (KDE). * * @return false if user canceled, * true will quit the application. */ bool queryBeforeClosing(); /** * Open recent directory. * * @param dir directory to open */ void openRecentDirectory(const QString& dir); /** * Set window title with information from directory, filter and modification * state. */ void updateWindowCaption(); /** * Access to application. * @return application. */ Kid3Application* app(); /** * Access to main form. * @return main form. */ Kid3Form* form(); private: BaseMainWindowImpl* m_impl; }; #endif /* BASEMAINWINDOW_H */ kid3-3.0.2/src/gui/forms/filelist.cpp000066400000000000000000000237761224603511300174020ustar00rootroot00000000000000/** * \file filelist.cpp * List of files to operate on. * * \b Project: Kid3 * \author Urs Fleisch * \date 9 Jan 2003 * * Copyright (C) 2003-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "filelist.h" #include #include #include #include #include #include #include "fileproxymodel.h" #include "modeliterator.h" #include "taggedfile.h" #include "basemainwindow.h" #include "useractionsconfig.h" #include "guiconfig.h" #include "externalprocess.h" #include "commandformatreplacer.h" /** * Constructor. * @param parent parent widget * @param mainWin main window */ FileList::FileList(QWidget* parent, BaseMainWindowImpl* mainWin) : QTreeView(parent), m_process(0), m_mainWin(mainWin) { setObjectName(QLatin1String("FileList")); setSelectionMode(ExtendedSelection); setSortingEnabled(false); setContextMenuPolicy(Qt::CustomContextMenu); connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(customContextMenu(QPoint))); #if defined HAVE_PHONON || QT_VERSION >= 0x050000 connect(this, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(playIfTaggedFile(QModelIndex))); #endif header()->hide(); } /** * Destructor. */ FileList::~FileList() { delete m_process; } /** * Returns the recommended size for the widget. * @return recommended size. */ QSize FileList::sizeHint() const { return QSize(fontMetrics().maxWidth() * 25, QTreeView::sizeHint().height()); } /** * Fill the filelist with the files found in a directory. * * @param dirIndex index of directory in file proxy model * @param fileIndexes indexes of files to select in file proxy model * (optional, else empty) * * @return false if name is not directory path, else true. */ bool FileList::readDir(const QPersistentModelIndex& dirIndex, const QList& fileIndexes) { if (dirIndex.isValid()) { setRootIndex(dirIndex); if (QItemSelectionModel* selModel = selectionModel()) { selModel->clearSelection(); if (!fileIndexes.isEmpty()) { foreach (const QPersistentModelIndex& fileIndex, fileIndexes) { selModel->select(fileIndex, QItemSelectionModel::Select); scrollTo(fileIndex); } selModel->setCurrentIndex(fileIndexes.first(), QItemSelectionModel::NoUpdate); } else { setCurrentIndex(dirIndex); // Make sure that this invisible root index item is not selected selModel->clearSelection(); } } return true; } return false; } /** * Update the stored current selection with the list of all selected items. */ void FileList::updateCurrentSelection() { if (!selectionModel()) return; m_currentSelection.clear(); foreach (const QModelIndex& index, selectionModel()->selectedIndexes()) { m_currentSelection.append(QPersistentModelIndex(index)); } } /** * Display a context menu with operations for selected files. * * @param index index of item * @param pos position where context menu is drawn on screen */ void FileList::contextMenu(const QModelIndex& index, const QPoint& pos) { if (index.isValid()) { QMenu menu(this); menu.addAction(tr("&Expand all"), m_mainWin, SLOT(expandFileList())); menu.addAction(tr("&Collapse all"), this, SLOT(collapseAll())); menu.addAction(tr("&Rename"), m_mainWin, SLOT(renameFile())); menu.addAction(tr("&Move to Trash"), m_mainWin, SLOT(deleteFile())); #if defined HAVE_PHONON || QT_VERSION >= 0x050000 menu.addAction(tr("&Play"), m_mainWin, SLOT(slotPlayAudio())); #endif int id = 0; for (QList::const_iterator it = UserActionsConfig::instance().m_contextMenuCommands.begin(); it != UserActionsConfig::instance().m_contextMenuCommands.end(); ++it) { QString name((*it).getName()); if (!name.isEmpty()) { menu.addAction(name); } ++id; } connect(&menu, SIGNAL(triggered(QAction*)), this, SLOT(executeAction(QAction*))); menu.setMouseTracking(true); menu.exec(pos); } } /** * Format a string list from the selected files. * Supported format fields: * Those supported by FrameFormatReplacer::getReplacement(), * when prefixed with u, encoded as URL * %f filename * %F list of files * %uf URL of single file * %uF list of URLs * %d directory name * %b the web browser set in the configuration * * @todo %f and %F are full paths, which is inconsistent with the * export format strings but compatible with .desktop files. * %d is duration in export format. * The export codes should be changed. * * @param format format specification * * @return formatted string list. */ QStringList FileList::formatStringList(const QStringList& format) { QStringList files; TaggedFile* firstSelectedFile = 0; QModelIndexList selItems(selectionModel() ? selectionModel()->selectedIndexes() : QModelIndexList()); foreach (const QModelIndex& index, selItems) { if (TaggedFile* taggedFile = FileProxyModel::getTaggedFileOfIndex(index)) { if (!firstSelectedFile) { firstSelectedFile = taggedFile; } files.append(taggedFile->getAbsFilename()); } } QString dirPath; if (files.isEmpty() && !selItems.isEmpty()) { dirPath = FileProxyModel::getPathIfIndexOfDir(selItems.first()); if (!dirPath.isNull()) { files.append(dirPath); firstSelectedFile = TaggedFileOfDirectoryIterator::first(selItems.first()); } } FrameCollection frames; QStringList fmt; for (QStringList::const_iterator it = format.begin(); it != format.end(); ++it) { if ((*it).indexOf(QLatin1Char('%')) == -1) { fmt.push_back(*it); } else { if (*it == QLatin1String("%F") || *it == QLatin1String("%{files}")) { // list of files fmt += files; } else if (*it == QLatin1String("%uF") || *it == QLatin1String("%{urls}")) { // list of URLs or URL QUrl url; url.setScheme(QLatin1String("file")); for (QStringList::const_iterator fit = files.begin(); fit != files.end(); ++fit) { url.setPath(*fit); fmt.push_back(url.toString()); } } else { if (firstSelectedFile) { // use merged tags 1 and 2 to format string FrameCollection frames1; firstSelectedFile->getAllFramesV1(frames1); firstSelectedFile->getAllFramesV2(frames); frames.merge(frames1); } QString str(*it); str.replace(QLatin1String("%uf"), QLatin1String("%{url}")); CommandFormatReplacer cfr(frames, str, files, !dirPath.isNull()); cfr.replacePercentCodes(FrameFormatReplacer::FSF_SupportUrlEncode); fmt.push_back(cfr.getString()); } } } return fmt; } /** * Execute a context menu command. * * @param id command ID */ void FileList::executeContextCommand(int id) { if (id < static_cast(UserActionsConfig::instance().m_contextMenuCommands.size())) { QStringList args; const UserActionsConfig::MenuCommand& menuCmd = UserActionsConfig::instance().m_contextMenuCommands[id]; QString cmd = menuCmd.getCommand(); int len = cmd.length(); int end = 0; while (end < len) { int begin = end; while (begin < len && cmd[begin] == QLatin1Char(' ')) ++begin; if (begin >= len) break; if (cmd[begin] == QLatin1Char('"')) { ++begin; QString str; while (begin < len) { if (cmd[begin] == QLatin1Char('\\') && begin + 1 < len && (cmd[begin + 1] == QLatin1Char('\\') || cmd[begin + 1] == QLatin1Char('"'))) { ++begin; } else if (cmd[begin] == QLatin1Char('"')) { break; } str += cmd[begin]; ++begin; } args.push_back(str); end = begin; } else { end = cmd.indexOf(QLatin1Char(' '), begin + 1); if (end == -1) end = len; args.push_back(cmd.mid(begin, end - begin)); } ++end; } args = formatStringList(args); if (!m_process) { m_process = new ExternalProcess(this); } m_process->launchCommand(menuCmd.getName(), args, menuCmd.mustBeConfirmed(), menuCmd.outputShown()); } } /** * Execute a context menu action. * * @param action action of selected menu */ void FileList::executeAction(QAction* action) { if (action) { QString name = action->text().remove(QLatin1Char('&')); int id = 0; for (QList::const_iterator it = UserActionsConfig::instance().m_contextMenuCommands.begin(); it != UserActionsConfig::instance().m_contextMenuCommands.end(); ++it) { if (name == (*it).getName()) { executeContextCommand(id); break; } ++id; } } } /** * Display a custom context menu with operations for selected files. * * @param pos position where context menu is drawn on screen */ void FileList::customContextMenu(const QPoint& pos) { contextMenu(currentIndex(), mapToGlobal(pos)); } #if defined HAVE_PHONON || QT_VERSION >= 0x050000 /** * Play item if it is a tagged file. * * @param index model index of item */ void FileList::playIfTaggedFile(const QModelIndex& index) { if (GuiConfig::instance().m_playOnDoubleClick && FileProxyModel::getTaggedFileOfIndex(index)) { m_mainWin->slotPlayAudio(); } } #endif kid3-3.0.2/src/gui/forms/filelist.h000066400000000000000000000104751224603511300170370ustar00rootroot00000000000000/** * \file filelist.h * List of files to operate on. * * \b Project: Kid3 * \author Urs Fleisch * \date 9 Jan 2003 * * Copyright (C) 2003-2011 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef FILELIST_H #define FILELIST_H #include #include #include "config.h" class BaseMainWindowImpl; class ExternalProcess; /** * List of files to operate on. */ class FileList : public QTreeView { Q_OBJECT public: /** * Constructor. * @param parent parent widget * @param mainWin main window */ FileList(QWidget* parent, BaseMainWindowImpl* mainWin); /** * Destructor. */ virtual ~FileList(); /** * Returns the recommended size for the widget. * @return recommended size. */ virtual QSize sizeHint() const; /** * Fill the filelist with the files found in a directory. * * @param dirIndex index of directory in file proxy model * @param fileIndexes indexes of files to select in file proxy model * (optional, else empty) * * @return false if name is not directory path, else true. */ bool readDir(const QPersistentModelIndex& dirIndex, const QList& fileIndexes = QList()); /** * Get the stored current selection. * @return stored selection. */ const QList& getCurrentSelection() const { return m_currentSelection; } /** * Clear the stored current selection. */ void clearCurrentSelection() { m_currentSelection.clear(); } /** * Update the stored current selection with the list of all selected items. */ void updateCurrentSelection(); /** * Get current index or root index if current index is invalid. * @return current index, root index if not valid. */ QModelIndex currentOrRootIndex() const { return currentIndex().isValid() ? currentIndex() : rootIndex(); } private slots: /** * Display a context menu with operations for selected files. * * @param index index of item * @param pos position where context menu is drawn on screen */ void contextMenu(const QModelIndex& index, const QPoint& pos); /** * Execute a context menu command. * * @param id command ID */ void executeContextCommand(int id); /** * Execute a context menu action. * * @param action action of selected menu */ void executeAction(QAction* action); /** * Display a custom context menu with operations for selected files. * * @param pos position where context menu is drawn on screen */ void customContextMenu(const QPoint& pos); #if defined HAVE_PHONON || QT_VERSION >= 0x050000 /** * Play item if it is a tagged file. * * @param index model index of item */ void playIfTaggedFile(const QModelIndex& index); #endif private: Q_DISABLE_COPY(FileList) /** * Format a string list from the selected files. * Supported format fields: * Those supported by FrameFormatReplacer::getReplacement(), * when prefixed with u, encoded as URL * %f filename * %F list of files * %uf URL of single file * %uF list of URLs * %d directory name * %b the web browser set in the configuration * * @todo %f and %F are full paths, which is inconsistent with the * export format strings but compatible with .desktop files. * %d is duration in export format. * The export codes should be changed. * * @param format format specification * * @return formatted string list. */ QStringList formatStringList(const QStringList& format); /** Process for context menu commands */ ExternalProcess* m_process; QList m_currentSelection; BaseMainWindowImpl* m_mainWin; }; #endif // FILELIST_H kid3-3.0.2/src/gui/forms/iplatformtools.cpp000066400000000000000000000016541224603511300206340ustar00rootroot00000000000000/** * \file iplatformtools.cpp * Interface for platform specific tools. * * \b Project: Kid3 * \author Urs Fleisch * \date 30 Mar 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "iplatformtools.h" /** * Destructor. */ IPlatformTools::~IPlatformTools() { } kid3-3.0.2/src/gui/forms/iplatformtools.h000066400000000000000000000105601224603511300202750ustar00rootroot00000000000000/** * \file iplatformtools.h * Interface for platform specific tools. * * \b Project: Kid3 * \author Urs Fleisch * \date 30 Mar 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef IPLATFORMTOOLS_H #define IPLATFORMTOOLS_H #include "icoreplatformtools.h" class QStringList; class QWidget; class QIcon; /** * Interface for platform specific tools. */ class KID3_GUI_EXPORT IPlatformTools : public ICorePlatformTools { public: /** * Destructor. */ virtual ~IPlatformTools() = 0; /** * Display help for a topic. * * @param anchor anchor in help document */ virtual void displayHelp(const QString& anchor) = 0; /** * Get a themed icon by name. * @param name name of icon * @return icon. */ virtual QIcon iconFromTheme(const QString& name) const = 0; /** * Display error dialog with item list. * @param parent parent widget * @param text text * @param strlist list of items * @param caption caption */ virtual void errorList(QWidget* parent, const QString& text, const QStringList& strlist, const QString& caption) = 0; /** * Display warning dialog with yes, no, cancel buttons. * @param parent parent widget * @param text text * @param caption caption * @return QMessageBox::Yes, QMessageBox::No or QMessageBox::Cancel. */ virtual int warningYesNoCancel(QWidget* parent, const QString& text, const QString& caption) = 0; /** * Display dialog to select an existing file. * @param parent parent widget * @param caption caption * @param dir directory * @param filter filter * @param selectedFilter the selected filter is returned here * @return selected file, empty if canceled. */ virtual QString getOpenFileName(QWidget* parent, const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter) = 0; /** * Display dialog to select existing files. * @param parent parent widget * @param caption caption * @param dir directory * @param filter filter * @param selectedFilter the selected filter is returned here * @return selected files, empty if canceled. */ virtual QStringList getOpenFileNames(QWidget* parent, const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter) = 0; /** * Display dialog to select a file to save. * @param parent parent widget * @param caption caption * @param dir directory * @param filter filter * @param selectedFilter the selected filter is returned here * @return selected file, empty if canceled. */ virtual QString getSaveFileName(QWidget* parent, const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter) = 0; /** * Display dialog to select an existing directory. * @param parent parent widget * @param caption caption * @param startDir start directory * @return selected directory, empty if canceled. */ virtual QString getExistingDirectory(QWidget* parent, const QString& caption, const QString& startDir) = 0; /** * Display warning dialog. * @param parent parent widget * @param text text * @param details detailed message * @param caption caption */ virtual void warningDialog(QWidget* parent, const QString& text, const QString& details, const QString& caption) = 0; /** * Display warning dialog with options to continue or cancel. * @param parent parent widget * @param text text * @param strlist list of items * @param caption caption * @return true if continue was selected. */ virtual bool warningContinueCancelList(QWidget* parent, const QString& text, const QStringList& strlist, const QString& caption) = 0; }; #endif // IPLATFORMTOOLS_H kid3-3.0.2/src/gui/forms/kid3form.cpp000066400000000000000000000655741224603511300173070ustar00rootroot00000000000000/** * \file kid3form.cpp * GUI for kid3. * * \b Project: Kid3 * \author Urs Fleisch * \date 8 Apr 2003 * * Copyright (C) 2003-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "kid3form.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "frametable.h" #include "frametablemodel.h" #include "trackdata.h" #include "genres.h" #include "basemainwindow.h" #include "filelist.h" #include "dirlist.h" #include "picturelabel.h" #include "fileconfig.h" #include "guiconfig.h" #include "formatconfig.h" #include "dirproxymodel.h" #include "fileproxymodel.h" #include "kid3application.h" /** Collapse pixmap, will be allocated in constructor */ QPixmap* Kid3Form::s_collapsePixmap = 0; /** Expand pixmap, will be allocated in constructor */ QPixmap* Kid3Form::s_expandPixmap = 0; /** picture for collapse pixmap */ static const char* const collapse_xpm[] = { "7 7 3 1", " \tc None", ".\tc #FFFFFF", "+\tc #000000", ".......", ".......", ".......", ".+++++.", ".......", ".......", "......." }; /** picture for expand pixmap */ static const char* const expand_xpm[] = { "7 7 3 1", " \tc None", ".\tc #FFFFFF", "+\tc #000000", ".......", "...+...", "...+...", ".+++++.", "...+...", "...+...", "......." }; /** * Event filter for double click on picture label. */ class PictureDblClickHandler : public QObject { public: /** * Constructor. */ PictureDblClickHandler(Kid3Application* app, IFrameEditor* frameEditor) : QObject(app), m_app(app), m_frameEditor(frameEditor) {} virtual ~PictureDblClickHandler() {} protected: /** * Event filter function, calls Kid3Application::editOrAddPicture(). * * @param obj watched object * @param event event for object * * @return true if event is filtered. */ virtual bool eventFilter(QObject* obj, QEvent* event); private: Kid3Application* m_app; IFrameEditor* m_frameEditor; }; /** * Event filter function, calls Kid3Application::editOrAddPicture() on double click. * * @param obj watched object * @param event event for object * * @return true if event is filtered. */ bool PictureDblClickHandler::eventFilter(QObject* obj, QEvent* event) { if (event->type() == QEvent::MouseButtonDblClick) { m_app->editOrAddPicture(m_frameEditor); return true; } else { // standard event processing return QObject::eventFilter(obj, event); } } /** * Constructs an Id3Form as a child of 'parent', with the * name 'name' and widget flags set to 'f'. * @param app application * @param parent parent widget */ Kid3Form::Kid3Form(Kid3Application* app, BaseMainWindowImpl* mainWin, QWidget* parent) : QSplitter(parent), m_app(app), m_mainWin(mainWin) { setObjectName(QLatin1String("Kid3Form")); if (!s_collapsePixmap) { s_collapsePixmap = new QPixmap((const char**)collapse_xpm); } if (!s_expandPixmap) { s_expandPixmap = new QPixmap((const char**)expand_xpm); } setAcceptDrops(true); setWindowTitle(tr("Kid3")); m_vSplitter = new QSplitter(Qt::Vertical, this); m_fileListBox = new FileList(m_vSplitter, m_mainWin); m_fileListBox->setModel(m_app->getFileProxyModel()); m_fileListBox->setSelectionModel(m_app->getFileSelectionModel()); m_dirListBox = new DirList(m_vSplitter); m_dirListBox->setModel(m_app->getDirProxyModel()); connect(m_app, SIGNAL(directoryOpened(QPersistentModelIndex,QList)), this, SLOT(setDirectoryIndex(QPersistentModelIndex,QList))); m_rightHalfVBox = new QWidget; QScrollArea* scrollView = new QScrollArea(this); scrollView->setWidget(m_rightHalfVBox); scrollView->setWidgetResizable(true); QVBoxLayout* rightHalfLayout = new QVBoxLayout(m_rightHalfVBox); rightHalfLayout->setSpacing(0); m_fileButton = new QToolButton(m_rightHalfVBox); m_fileButton->setIcon(*s_collapsePixmap); m_fileButton->setAutoRaise(true); #ifdef Q_OS_MAC m_fileButton->setStyleSheet(QLatin1String("border: 0;")); #endif m_fileLabel = new QLabel(tr("F&ile"), m_rightHalfVBox); QHBoxLayout* fileButtonLayout = new QHBoxLayout; fileButtonLayout->addWidget(m_fileButton); fileButtonLayout->addWidget(m_fileLabel); rightHalfLayout->addLayout(fileButtonLayout); m_fileWidget = new QWidget(m_rightHalfVBox); m_fileWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); rightHalfLayout->addWidget(m_fileWidget); QGridLayout* fileLayout = new QGridLayout(m_fileWidget); m_nameLabel = new QLabel(tr("Name:"), m_fileWidget); fileLayout->addWidget(m_nameLabel, 0, 0); m_nameLineEdit = new QLineEdit(m_fileWidget); fileLayout->addWidget(m_nameLineEdit, 0, 1, 1, 4); m_fileLabel->setBuddy(m_nameLineEdit); QLabel* formatLabel = new QLabel(tr("Format:") + QChar(0x2191), m_fileWidget); fileLayout->addWidget(formatLabel, 1, 0); m_formatComboBox = new QComboBox(m_fileWidget); m_formatComboBox->setEditable(true); m_formatComboBox->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon); m_formatComboBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); m_formatComboBox->setToolTip(TrackDataFormatReplacer::getToolTip()); connect(m_formatComboBox, SIGNAL(editTextChanged(QString)), m_app, SLOT(setTagsToFilenameFormatWithoutSignaling(QString))); connect(m_app, SIGNAL(tagsToFilenameFormatChanged(QString)), m_formatComboBox, SLOT(setEditText(QString))); fileLayout->addWidget(m_formatComboBox, 1, 1); QLabel* fromTagLabel = new QLabel(tr("From:"), m_fileWidget); fileLayout->addWidget(fromTagLabel, 1, 2); m_fnV1Button = new QPushButton(tr("Tag 1"), m_fileWidget); m_fnV1Button->setToolTip(tr("Filename from Tag 1")); fileLayout->addWidget(m_fnV1Button, 1, 3); QPushButton* fnV2Button = new QPushButton(tr("Tag 2"), m_fileWidget); fnV2Button->setToolTip(tr("Filename from Tag 2")); fileLayout->addWidget(fnV2Button, 1, 4); QLabel* formatFromFilenameLabel = new QLabel(tr("Format:") + QChar(0x2193), m_fileWidget); fileLayout->addWidget(formatFromFilenameLabel, 2, 0); m_formatFromFilenameComboBox = new QComboBox(m_fileWidget); m_formatFromFilenameComboBox->setEditable(true); m_formatFromFilenameComboBox->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon); m_formatFromFilenameComboBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); m_formatFromFilenameComboBox->setToolTip(FrameFormatReplacer::getToolTip()); connect(m_formatFromFilenameComboBox, SIGNAL(editTextChanged(QString)), m_app, SLOT(setFilenameToTagsFormatWithoutSignaling(QString))); connect(m_app, SIGNAL(filenameToTagsFormatChanged(QString)), m_formatFromFilenameComboBox, SLOT(setEditText(QString))); fileLayout->addWidget(m_formatFromFilenameComboBox, 2, 1); QLabel* toTagLabel = new QLabel(tr("To:"), m_fileWidget); fileLayout->addWidget(toTagLabel, 2, 2); m_toTagV1Button = new QPushButton(tr("Tag 1"), m_fileWidget); m_toTagV1Button->setToolTip(tr("Tag 1 from Filename")); fileLayout->addWidget(m_toTagV1Button, 2, 3); QPushButton* toTagV2Button = new QPushButton(tr("Tag 2"), m_fileWidget); toTagV2Button->setToolTip(tr("Tag 2 from Filename")); fileLayout->addWidget(toTagV2Button, 2, 4); m_tag1Button = new QToolButton(m_rightHalfVBox); m_tag1Button->setIcon(*s_collapsePixmap); m_tag1Button->setAutoRaise(true); #ifdef Q_OS_MAC m_tag1Button->setStyleSheet(QLatin1String("border: 0;")); #endif m_tag1Label = new QLabel(tr("Tag &1"), m_rightHalfVBox); QHBoxLayout* tag1ButtonLayout = new QHBoxLayout; tag1ButtonLayout->addWidget(m_tag1Button); tag1ButtonLayout->addWidget(m_tag1Label); rightHalfLayout->addLayout(tag1ButtonLayout); m_tag1Widget = new QWidget(m_rightHalfVBox); m_tag1Widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); rightHalfLayout->addWidget(m_tag1Widget, 100); QHBoxLayout* idV1HBoxLayout = new QHBoxLayout(m_tag1Widget); m_framesV1Table = new FrameTable(m_app->frameModelV1(), m_tag1Widget); m_framesV1Table->setSelectionModel(m_app->getFramesV1SelectionModel()); idV1HBoxLayout->addWidget(m_framesV1Table, 100); m_tag1Label->setBuddy(m_framesV1Table); QVBoxLayout* buttonsV1VBoxLayout = new QVBoxLayout; idV1HBoxLayout->addLayout(buttonsV1VBoxLayout); QPushButton* id3V1PushButton = new QPushButton(tr("From Tag 2"), m_tag1Widget); buttonsV1VBoxLayout->addWidget(id3V1PushButton); QPushButton* copyV1PushButton = new QPushButton(tr("Copy"), m_tag1Widget); buttonsV1VBoxLayout->addWidget(copyV1PushButton); QPushButton* pasteV1PushButton = new QPushButton(tr("Paste"), m_tag1Widget); buttonsV1VBoxLayout->addWidget(pasteV1PushButton); QPushButton* removeV1PushButton = new QPushButton(tr("Remove"), m_tag1Widget); buttonsV1VBoxLayout->addWidget(removeV1PushButton); buttonsV1VBoxLayout->addItem( new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding)); m_tag2Button = new QToolButton(m_rightHalfVBox); m_tag2Button->setIcon(*s_collapsePixmap); m_tag2Button->setAutoRaise(true); #ifdef Q_OS_MAC m_tag2Button->setStyleSheet(QLatin1String("border: 0;")); #endif m_tag2Label = new QLabel(tr("Tag &2"), m_rightHalfVBox); QHBoxLayout* tag2ButtonLayout = new QHBoxLayout; tag2ButtonLayout->addWidget(m_tag2Button); tag2ButtonLayout->addWidget(m_tag2Label); rightHalfLayout->addLayout(tag2ButtonLayout); m_tag2Widget = new QWidget(m_rightHalfVBox); rightHalfLayout->addWidget(m_tag2Widget, 100); QHBoxLayout* idV2HBoxLayout = new QHBoxLayout(m_tag2Widget); m_framesV2Table = new FrameTable(m_app->frameModelV2(), m_tag2Widget); m_framesV2Table->setSelectionModel(m_app->getFramesV2SelectionModel()); idV2HBoxLayout->addWidget(m_framesV2Table); m_tag2Label->setBuddy(m_framesV2Table); QVBoxLayout* buttonsV2VBoxLayout = new QVBoxLayout; idV2HBoxLayout->addLayout(buttonsV2VBoxLayout); m_id3V2PushButton = new QPushButton(tr("From Tag 1"), m_tag2Widget); buttonsV2VBoxLayout->addWidget(m_id3V2PushButton); QPushButton* copyV2PushButton = new QPushButton(tr("Copy"), m_tag2Widget); buttonsV2VBoxLayout->addWidget(copyV2PushButton); QPushButton* pasteV2PushButton = new QPushButton(tr("Paste"), m_tag2Widget); buttonsV2VBoxLayout->addWidget(pasteV2PushButton); QPushButton* removeV2PushButton = new QPushButton(tr("Remove"), m_tag2Widget); buttonsV2VBoxLayout->addWidget(removeV2PushButton); QFrame* frameLine = new QFrame; frameLine->setFrameShape(QFrame::HLine); frameLine->setFrameShadow(QFrame::Sunken); buttonsV2VBoxLayout->addWidget(frameLine); QPushButton* editFramesPushButton = new QPushButton(tr("Edit"), m_tag2Widget); buttonsV2VBoxLayout->addWidget(editFramesPushButton); QPushButton* framesAddPushButton = new QPushButton(tr("Add"), m_tag2Widget); buttonsV2VBoxLayout->addWidget(framesAddPushButton); QPushButton* deleteFramesPushButton = new QPushButton(tr("Delete"), m_tag2Widget); buttonsV2VBoxLayout->addWidget(deleteFramesPushButton); m_pictureLabel = new PictureLabel(this); m_pictureLabel->installEventFilter(new PictureDblClickHandler(m_app, m_mainWin)); buttonsV2VBoxLayout->addWidget(m_pictureLabel); buttonsV2VBoxLayout->addItem( new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding)); rightHalfLayout->insertStretch(-1); // signals and slots connections connect(id3V1PushButton, SIGNAL(clicked()), m_app, SLOT(copyV2ToV1())); connect(copyV1PushButton, SIGNAL(clicked()), m_app, SLOT(copyTagsV1())); connect(pasteV1PushButton, SIGNAL(clicked()), m_app, SLOT(pasteTagsV1())); connect(removeV1PushButton, SIGNAL(clicked()), m_app, SLOT(removeTagsV1())); connect(m_id3V2PushButton, SIGNAL(clicked()), m_app, SLOT(copyV1ToV2())); connect(copyV2PushButton, SIGNAL(clicked()), m_app, SLOT(copyTagsV2())); connect(pasteV2PushButton, SIGNAL(clicked()), m_app, SLOT(pasteTagsV2())); connect(removeV2PushButton, SIGNAL(clicked()), m_app, SLOT(removeTagsV2())); connect(framesAddPushButton, SIGNAL(clicked()), this, SLOT(addFrame())); connect(deleteFramesPushButton, SIGNAL(clicked()), this, SLOT(deleteFrame())); connect(editFramesPushButton, SIGNAL(clicked()), this, SLOT(editFrame())); connect(m_fnV1Button, SIGNAL(clicked()), this, SLOT(fnFromID3V1())); connect(fnV2Button, SIGNAL(clicked()), this, SLOT(fnFromID3V2())); connect(m_toTagV1Button, SIGNAL(clicked()), m_app, SLOT(getTagsFromFilenameV1())); connect(toTagV2Button, SIGNAL(clicked()), m_app, SLOT(getTagsFromFilenameV2())); connect(m_nameLineEdit, SIGNAL(textChanged(QString)), this, SLOT(nameLineEditChanged(QString))); connect(m_dirListBox, SIGNAL(activated(QModelIndex)), this, SLOT(dirSelected(QModelIndex))); connect(m_fileButton, SIGNAL(clicked()), this, SLOT(showHideFile())); connect(m_tag1Button, SIGNAL(clicked()), this, SLOT(showHideTag1())); connect(m_tag2Button, SIGNAL(clicked()), this, SLOT(showHideTag2())); // tab order setTabOrder(m_fileListBox, m_dirListBox); setTabOrder(m_dirListBox, m_nameLineEdit); setTabOrder(m_nameLineEdit, m_formatComboBox); setTabOrder(m_formatComboBox, m_formatFromFilenameComboBox); setTabOrder(m_formatFromFilenameComboBox, m_fnV1Button); setTabOrder(m_fnV1Button, fnV2Button); setTabOrder(fnV2Button, m_toTagV1Button); setTabOrder(m_toTagV1Button, toTagV2Button); setTabOrder(toTagV2Button, id3V1PushButton); setTabOrder(id3V1PushButton, copyV1PushButton); setTabOrder(copyV1PushButton, pasteV1PushButton); setTabOrder(pasteV1PushButton, removeV1PushButton); setTabOrder(removeV1PushButton, m_id3V2PushButton); setTabOrder(m_id3V2PushButton, copyV2PushButton); setTabOrder(copyV2PushButton, pasteV2PushButton); setTabOrder(pasteV2PushButton, removeV2PushButton); setTabOrder(removeV2PushButton, editFramesPushButton); setTabOrder(editFramesPushButton, framesAddPushButton); setTabOrder(framesAddPushButton, deleteFramesPushButton); setTabOrder(deleteFramesPushButton, m_framesV1Table); setTabOrder(m_framesV1Table, m_framesV2Table); } /** * Destructor. */ Kid3Form::~Kid3Form() { } /** * Accept drag. * * @param ev drag event. */ void Kid3Form::dragEnterEvent(QDragEnterEvent* ev) { if (ev->mimeData()->hasFormat(QLatin1String("text/uri-list")) || ev->mimeData()->hasImage()) ev->acceptProposedAction(); } /** * Handle drop event. * * @param ev drop event. */ void Kid3Form::dropEvent(QDropEvent* ev) { if (ev->mimeData()->hasImage()) { QImage image = qvariant_cast(ev->mimeData()->imageData()); m_app->dropImage(image); return; } QList urls = ev->mimeData()->urls(); if (urls.isEmpty()) return; if ( #if QT_VERSION >= 0x040800 urls.first().isLocalFile() #else !urls.first().toLocalFile().isEmpty() #endif ) { QStringList localFiles; foreach (const QUrl& url, urls) { localFiles.append(url.toLocalFile()); } m_app->openDrop(localFiles); } else { QString text = urls.first().toString(); if (text.startsWith(QLatin1String("http://"))) { m_app->dropUrl(text); } } } /** * Frame list button Edit. */ void Kid3Form::editFrame() { m_app->editFrame(m_mainWin); } /** * Frame list button Add. */ void Kid3Form::addFrame() { m_app->addFrame(0, m_mainWin); } /** * Frame list button Delete. */ void Kid3Form::deleteFrame() { m_app->deleteFrame(); } /** * Set filename according to ID3v1 tags. */ void Kid3Form::fnFromID3V1() { m_app->getFilenameFromTags(TrackData::TagV1); } /** * Set filename according to ID3v1 tags. */ void Kid3Form::fnFromID3V2() { m_app->getFilenameFromTags(TrackData::TagV2); } /** * Filename line edit is changed. * @param txt contents of line edit */ void Kid3Form::nameLineEditChanged(const QString& txt) { formatLineEdit(m_nameLineEdit, txt, &FilenameFormatConfig::instance()); } /** * Mark the filename as changed. * @param en true to mark as changed */ void Kid3Form::markChangedFilename(bool en) { if (en) { QPalette changedPalette(m_nameLabel->palette()); changedPalette.setBrush(QPalette::Active, QPalette::Window, changedPalette.mid()); m_nameLabel->setPalette(changedPalette); } else { m_nameLabel->setPalette(QPalette()); } m_nameLabel->setAutoFillBackground(en); } /** * Format string within line edit. * * @param le line edit * @param txt text in line edit * @param fcfg format configuration */ void Kid3Form::formatLineEdit(QLineEdit* le, const QString& txt, const FormatConfig* fcfg) { if (fcfg->m_formatWhileEditing) { QString str(txt); fcfg->formatString(str); if (str != txt) { int curPos = le->cursorPosition(); le->setText(str); le->setCursorPosition(curPos); } } } /** * Directory list box directory selected. * * @param index selected item */ void Kid3Form::dirSelected(const QModelIndex& index) { QString dirPath = index.data(QFileSystemModel::FilePathRole).toString(); if (!dirPath.isEmpty()) { m_dirListBox->setEntryToSelect( dirPath.endsWith(QLatin1String("..")) ? index.parent() : QModelIndex()); m_mainWin->updateCurrentSelection(); m_mainWin->confirmedOpenDirectory(QStringList() << dirPath); } } /** * Enable or disable controls requiring ID3v1 tags. * * @param enable true to enable */ void Kid3Form::enableControlsV1(bool enable) { m_fnV1Button->setEnabled(enable); m_toTagV1Button->setEnabled(enable); m_id3V2PushButton->setEnabled(enable); m_tag1Widget->setEnabled(enable); } /** * Display the format of tag 1. * * @param str string describing format, e.g. "ID3v1.1" */ void Kid3Form::setTagFormatV1(const QString& str) { QString txt = tr("Tag &1"); if (!str.isEmpty()) { txt += QLatin1String(": "); txt += str; } m_tag1Label->setText(txt); } /** * Display the format of tag 2. * * @param str string describing format, e.g. "ID3v2.4" */ void Kid3Form::setTagFormatV2(const QString& str) { QString txt = tr("Tag &2"); if (!str.isEmpty()) { txt += QLatin1String(": "); txt += str; } m_tag2Label->setText(txt); } /** * Adjust the size of the right half box. */ void Kid3Form::adjustRightHalfBoxSize() { m_rightHalfVBox->adjustSize(); } /** * Hide or show file controls. * * @param hide true to hide, false to show */ void Kid3Form::hideFile(bool hide) { if (hide) { m_fileWidget->hide(); m_fileButton->setIcon(*s_expandPixmap); } else { m_fileWidget->show(); m_fileButton->setIcon(*s_collapsePixmap); } } /** * Hide or show tag 1 controls. * * @param hide true to hide, false to show */ void Kid3Form::hideV1(bool hide) { if (hide) { m_tag1Widget->hide(); m_tag1Button->setIcon(*s_expandPixmap); } else { m_tag1Widget->show(); m_tag1Button->setIcon(*s_collapsePixmap); } } /** * Hide or show tag 2 controls. * * @param hide true to hide, false to show */ void Kid3Form::hideV2(bool hide) { if (hide) { m_tag2Widget->hide(); m_tag2Button->setIcon(*s_expandPixmap); } else { m_tag2Widget->show(); m_tag2Button->setIcon(*s_collapsePixmap); } } /** * Toggle visibility of file controls. */ void Kid3Form::showHideFile() { hideFile(!m_fileWidget->isHidden()); } /** * Toggle visibility of tag 1 controls. */ void Kid3Form::showHideTag1() { hideV1(!m_tag1Widget->isHidden()); } /** * Toggle visibility of tag 2 controls. */ void Kid3Form::showHideTag2() { hideV2(!m_tag2Widget->isHidden()); } /** * Hide or show picture. * * @param hide true to hide, false to show */ void Kid3Form::hidePicture(bool hide) { if (hide) { m_pictureLabel->hide(); } else { m_pictureLabel->show(); } } /** * Set focus on filename controls. */ void Kid3Form::setFocusFilename() { m_nameLineEdit->setFocus(); } /** * Set focus on tag 1 controls. */ void Kid3Form::setFocusV1() { m_framesV1Table->setFocus(); } /** * Set focus on tag 2 controls. */ void Kid3Form::setFocusV2() { m_framesV2Table->setFocus(); } /** * Set focus on file list. */ void Kid3Form::setFocusFileList() { m_fileListBox->setFocus(); } /** * Set focus on directory list. */ void Kid3Form::setFocusDirList() { m_dirListBox->setFocus(); } /** * Get the items from a combo box. * * @param comboBox combo box * * @return item texts from combo box. */ static QStringList getItemsFromComboBox(const QComboBox* comboBox) { QStringList lst; for (int i = 0; i < comboBox->count(); ++i) { lst += comboBox->itemText(i); } return lst; } /** * Save the local settings to the configuration. */ void Kid3Form::saveConfig() { GuiConfig::instance().m_splitterSizes = sizes(); GuiConfig::instance().m_vSplitterSizes = m_vSplitter->sizes(); FileConfig::instance().m_formatItem = m_formatComboBox->currentIndex(); FileConfig::instance().m_formatText = m_formatComboBox->currentText(); FileConfig::instance().m_formatItems = getItemsFromComboBox(m_formatComboBox); FileConfig::instance().m_formatFromFilenameItem = m_formatFromFilenameComboBox->currentIndex(); FileConfig::instance().m_formatFromFilenameText = m_formatFromFilenameComboBox->currentText(); FileConfig::instance().m_formatFromFilenameItems = getItemsFromComboBox(m_formatFromFilenameComboBox); if (!GuiConfig::instance().m_autoHideTags) { GuiConfig::instance().m_hideFile = m_fileWidget->isHidden(); GuiConfig::instance().m_hideV1 = m_tag1Widget->isHidden(); GuiConfig::instance().m_hideV2 = m_tag2Widget->isHidden(); } } /** * Read the local settings from the configuration. */ void Kid3Form::readConfig() { if (!GuiConfig::instance().m_splitterSizes.empty()) { setSizes(GuiConfig::instance().m_splitterSizes); } else { setSizes(QList() << 307 << 601); } if (!GuiConfig::instance().m_vSplitterSizes.empty()) { m_vSplitter->setSizes(GuiConfig::instance().m_vSplitterSizes); } else { m_vSplitter->setSizes(QList() << 451 << 109); } if (!FileConfig::instance().m_formatItems.isEmpty()) { m_formatComboBox->clear(); m_formatComboBox->addItems(FileConfig::instance().m_formatItems); } if (!FileConfig::instance().m_formatFromFilenameItems.isEmpty()) { m_formatFromFilenameComboBox->clear(); m_formatFromFilenameComboBox->addItems(FileConfig::instance().m_formatFromFilenameItems); } m_formatComboBox->setItemText(FileConfig::instance().m_formatItem, FileConfig::instance().m_formatText); m_formatComboBox->setCurrentIndex(FileConfig::instance().m_formatItem); m_formatFromFilenameComboBox->setItemText( FileConfig::instance().m_formatFromFilenameItem, FileConfig::instance().m_formatFromFilenameText); m_formatFromFilenameComboBox->setCurrentIndex( FileConfig::instance().m_formatFromFilenameItem); if (!GuiConfig::instance().m_autoHideTags) { hideFile(GuiConfig::instance().m_hideFile); hideV1(GuiConfig::instance().m_hideV1); hideV2(GuiConfig::instance().m_hideV2); } hidePicture(GuiConfig::instance().m_hidePicture); } /** * Set preview picture data. * @param data picture data, 0 if no picture is available */ void Kid3Form::setPictureData(const QByteArray* data) { m_pictureLabel->setData(data); } /** * Set details info text. * * @param info detail information */ void Kid3Form::setDetailInfo(const TaggedFile::DetailInfo& info) { QString str = info.toString(); if (!str.isEmpty()) { str = tr("F&ile") + QLatin1String(": ") + str; } else { str = tr("F&ile"); } m_fileLabel->setText(str); } /** * Select all files. */ void Kid3Form::selectAllFiles() { m_fileListBox->selectAll(); } /** * Deselect all files. */ void Kid3Form::deselectAllFiles() { m_fileListBox->clearSelection(); } /** * Select all files in the current directory. */ void Kid3Form::selectAllInDirectory() { QModelIndex parent = m_fileListBox->currentIndex(); const QAbstractItemModel* model = parent.model(); if (parent.isValid() && model) { if (!model->hasChildren(parent)) { parent = parent.parent(); } QItemSelection selection; for (int row = 0; row < model->rowCount(parent); ++row) { QModelIndex index = model->index(row, 0, parent); if (!model->hasChildren(index)) { selection.append(QItemSelectionRange(index)); } } if (QItemSelectionModel* selModel = m_fileListBox->selectionModel()) { selModel->select(selection, QItemSelectionModel::Select); } } } /** * Set the next file as the current file. * * @param select true to select the file * * @return true if a next file exists. */ bool Kid3Form::nextFile(bool select) { FrameTable* editingFrameTable = getEditingFrameTable(); bool ok = m_app->nextFile(select); if (ok && editingFrameTable) { editingFrameTable->edit(editingFrameTable->currentIndex()); } return ok; } /** * Set the previous file as the current file. * * @param select true to select the file * * @return true if a previous file exists. */ bool Kid3Form::previousFile(bool select) { FrameTable* editingFrameTable = getEditingFrameTable(); bool ok = m_app->previousFile(select); if (ok && editingFrameTable) { editingFrameTable->edit(editingFrameTable->currentIndex()); } return ok; } /** * Get frame table which is currently in editing state. * The returned frame table can be used to restore the editing state after * changing the current file. * @return frame table which is in editing state, 0 if none. */ FrameTable* Kid3Form::getEditingFrameTable() const { if (QWidget* focusWidget = QApplication::focusWidget()) { if (m_framesV1Table->getCurrentEditor() == focusWidget) { return m_framesV1Table; } else if (m_framesV2Table->getCurrentEditor() == focusWidget) { return m_framesV2Table; } } return 0; } /** * Set the root index of the directory and file lists. * * @param directoryIndex root index of directory in file proxy model * @param fileIndexes indexes of files to select in file proxy model */ void Kid3Form::setDirectoryIndex(const QPersistentModelIndex& directoryIndex, const QList& fileIndexes) { m_fileListBox->readDir(directoryIndex, fileIndexes); m_dirListBox->readDir( m_app->getFileProxyModel()->mapToSource(directoryIndex)); } kid3-3.0.2/src/gui/forms/kid3form.h000066400000000000000000000176721224603511300167500ustar00rootroot00000000000000/** * \file kid3form.h * GUI for kid3. * * \b Project: Kid3 * \author Urs Fleisch * \date 8 Apr 2003 * * Copyright (C) 2003-2011 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KID3FORM_H #define KID3FORM_H #include #include #include "taggedfile.h" #include "config.h" class QLabel; class QCheckBox; class QPushButton; class QToolButton; class QSpinBox; class QGridLayout; class QGroupBox; class QPixmap; class QComboBox; class FormatConfig; class FrameTable; class FrameTableModel; class Kid3Application; class FileList; class DirList; class PictureLabel; class BaseMainWindowImpl; /** * Main widget. */ class Kid3Form : public QSplitter { Q_OBJECT public: /** * Constructs an Id3Form as a child of 'parent', with the * name 'name' and widget flags set to 'f'. * @param app application * @param mainWin main window * @param parent parent widget */ Kid3Form(Kid3Application* app, BaseMainWindowImpl* mainWin, QWidget* parent = 0); /** * Destructor. */ virtual ~Kid3Form(); /** * Enable or disable controls requiring ID3v1 tags. * * @param enable true to enable */ void enableControlsV1(bool enable); /** * Display the format of tag 1. * * @param str string describing format, e.g. "ID3v1.1" */ void setTagFormatV1(const QString& str); /** * Display the format of tag 2. * * @param str string describing format, e.g. "ID3v2.4" */ void setTagFormatV2(const QString& str); /** * Adjust the size of the right half box. */ void adjustRightHalfBoxSize(); /** * Hide or show file controls. * * @param hide true to hide, false to show */ void hideFile(bool hide); /** * Hide or show tag 1 controls. * * @param hide true to hide, false to show */ void hideV1(bool hide); /** * Hide or show tag 2 controls. * * @param hide true to hide, false to show */ void hideV2(bool hide); /** * Hide or show picture. * * @param hide true to hide, false to show */ void hidePicture(bool hide); /** * Save the local settings to the configuration. */ void saveConfig(); /** * Read the local settings from the configuration. */ void readConfig(); /** * Get filename. */ QString getFilename() const { return m_nameLineEdit->text(); } /** * Set filename. * @param fn filename */ void setFilename(const QString& fn) { m_nameLineEdit->setText(fn); } /** * Check if the filename line edit is enabled. * @return true if the filename line edit is enabled. */ bool isFilenameEditEnabled() const { return m_nameLineEdit->isEnabled(); } /** * Enable or disable the filename line edit. * @param en true to enable */ void setFilenameEditEnabled(bool en) { m_nameLineEdit->setEnabled(en); } /** * Mark the filename as changed. * @param en true to mark as changed */ void markChangedFilename(bool en); /** * Set preview picture data. * @param data picture data, 0 if no picture is available */ void setPictureData(const QByteArray* data); /** * Set details info text. * * @param info detail information */ void setDetailInfo(const TaggedFile::DetailInfo& info); /** * Get file list. * @return file list. */ FileList* getFileList() { return m_fileListBox; } /** * Get tag 1 frame table. * @return frame table. */ FrameTable* frameTableV1() { return m_framesV1Table; } /** * Get tag 2 frame table. * @return frame table. */ FrameTable* frameTableV2() { return m_framesV2Table; } public slots: /** * Frame list button Edit. */ void editFrame(); /** * Frame list button Add. */ void addFrame(); /** * Frame list button Delete. */ void deleteFrame(); /** * Set filename according to ID3v1 tags. */ void fnFromID3V1(); /** * Set filename according to ID3v1 tags. */ void fnFromID3V2(); /** * Filename line edit is changed. * @param txt contents of line edit */ void nameLineEditChanged(const QString& txt); /** * Directory list box directory selected. * * @param index selected item */ void dirSelected(const QModelIndex& index); /** * Set focus on filename controls. */ void setFocusFilename(); /** * Set focus on tag 1 controls. */ void setFocusV1(); /** * Set focus on tag 2 controls. */ void setFocusV2(); /** * Set focus on file list. */ void setFocusFileList(); /** * Set focus on directory list. */ void setFocusDirList(); /** * Select all files. */ void selectAllFiles(); /** * Deselect all files. */ void deselectAllFiles(); /** * Select all files in the current directory. */ void selectAllInDirectory(); /** * Set the next file as the current file. * * @param select true to select the file * * @return true if a next file exists. */ bool nextFile(bool select = true); /** * Set the previous file as the current file. * * @param select true to select the file * * @return true if a previous file exists. */ bool previousFile(bool select = true); /** * Set the root index of the directory and file lists. * * @param directoryIndex root index of directory in file system model * @param fileIndexes indexes of files to select */ void setDirectoryIndex(const QPersistentModelIndex& directoryIndex, const QList& fileIndexes); private: /** * Format string within line edit. * * @param le line edit * @param txt text in line edit * @param fcfg format configuration */ void formatLineEdit(QLineEdit* le, const QString& txt, const FormatConfig* fcfg); /** * Get frame table which is currently in editing state. * The returned frame table can be used to restore the editing state after * changing the current file. * @return frame table which is in editing state, 0 if none. */ FrameTable* getEditingFrameTable() const; FileList* m_fileListBox; QComboBox* m_formatComboBox; QComboBox* m_formatFromFilenameComboBox; QLabel* m_nameLabel; QLineEdit* m_nameLineEdit; DirList* m_dirListBox; FrameTable* m_framesV1Table; FrameTable* m_framesV2Table; QSplitter* m_vSplitter; QWidget* m_fileWidget; QWidget* m_tag1Widget; QWidget* m_tag2Widget; QToolButton* m_fileButton; QToolButton* m_tag1Button; QToolButton* m_tag2Button; QLabel* m_fileLabel; QLabel* m_tag1Label; QLabel* m_tag2Label; QPushButton* m_fnV1Button; QPushButton* m_toTagV1Button; QPushButton* m_id3V2PushButton; QWidget* m_rightHalfVBox; PictureLabel* m_pictureLabel; Kid3Application* m_app; BaseMainWindowImpl* m_mainWin; /** Collapse pixmap, will be allocated in constructor */ static QPixmap* s_collapsePixmap; /** Expand pixmap, will be allocated in constructor */ static QPixmap* s_expandPixmap; private slots: /** * Accept drag. * * @param ev drag event. */ void dragEnterEvent(QDragEnterEvent* ev); /** * Handle drop event. * * @param ev drop event. */ void dropEvent(QDropEvent* ev); /** * Toggle visibility of file controls. */ void showHideFile(); /** * Toggle visibility of tag 1 controls. */ void showHideTag1(); /** * Toggle visibility of tag 2 controls. */ void showHideTag2(); }; #endif // KID3FORM_H kid3-3.0.2/src/gui/widgets/000077500000000000000000000000001224603511300153645ustar00rootroot00000000000000kid3-3.0.2/src/gui/widgets/Sources.cmake000066400000000000000000000010051224603511300200050ustar00rootroot00000000000000set(widgets_SRCS widgets/abstractlistedit.cpp widgets/comboboxdelegate.cpp widgets/configtable.cpp widgets/formatbox.cpp widgets/formatlistedit.cpp widgets/frametable.cpp widgets/imageviewer.cpp widgets/picturelabel.cpp widgets/playtoolbar.cpp widgets/stringlistedit.cpp ) set(widgets_MOC_HDRS widgets/abstractlistedit.h widgets/configtable.h widgets/formatbox.h widgets/formatlistedit.h widgets/frametable.h widgets/imageviewer.h widgets/playtoolbar.h widgets/stringlistedit.h ) kid3-3.0.2/src/gui/widgets/abstractlistedit.cpp000066400000000000000000000137051224603511300214430ustar00rootroot00000000000000/** * \file abstractlistedit.cpp * Widget to edit a list. * * \b Project: Kid3 * \author Urs Fleisch * \date 2 Jan 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "abstractlistedit.h" #include #include #include /** * Constructor. * * @param itemView item view, e.g. a QListView * @param model item model, e.g. a QStringListModel * @param parent parent widget */ AbstractListEdit::AbstractListEdit(QAbstractItemView* itemView, QAbstractItemModel* model, QWidget* parent) : QWidget(parent) { setObjectName(QLatin1String("AbstractListEdit")); QHBoxLayout* hlayout = new QHBoxLayout(this); m_itemView = itemView; m_itemView->setModel(model); hlayout->setContentsMargins(0, 0, 0, 0); hlayout->addWidget(m_itemView); QVBoxLayout* vlayout = new QVBoxLayout; m_addPushButton = new QPushButton(tr("&Add..."), this); m_moveUpPushButton = new QPushButton(tr("Move &Up"), this); m_moveDownPushButton = new QPushButton(tr("Move &Down"), this); m_editPushButton = new QPushButton(tr("&Edit..."), this); m_removePushButton = new QPushButton(tr("&Remove"), this); vlayout->addWidget(m_addPushButton); vlayout->addWidget(m_moveUpPushButton); vlayout->addWidget(m_moveDownPushButton); vlayout->addWidget(m_editPushButton); vlayout->addWidget(m_removePushButton); vlayout->addStretch(); connect(m_addPushButton, SIGNAL(clicked()), this, SLOT(addItem())); connect(m_moveUpPushButton, SIGNAL(clicked()), this, SLOT(moveUpItem())); connect(m_moveDownPushButton, SIGNAL(clicked()), this, SLOT(moveDownItem())); connect(m_editPushButton, SIGNAL(clicked()), this, SLOT(editItem())); connect(m_removePushButton, SIGNAL(clicked()), this, SLOT(removeItem())); connect(m_itemView->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(setButtonEnableState())); setButtonEnableState(); hlayout->addLayout(vlayout); } /** * Destructor. */ AbstractListEdit::~AbstractListEdit() { } /** * Disable editing of items. * When editing is disabled, the Add, Edit and Remove buttons are hidden. * @param disable true to disable, false (default) to enable editing. */ void AbstractListEdit::setEditingDisabled(bool disable) { m_addPushButton->setHidden(disable); m_editPushButton->setHidden(disable); m_removePushButton->setHidden(disable); } /** * Remove the selected item. */ void AbstractListEdit::removeItem() { QModelIndex index = m_itemView->currentIndex(); if (index.isValid()) { QAbstractItemModel* model = m_itemView->model(); model->removeRow(index.row()); setButtonEnableState(); } } /** * Move the selected item up. */ void AbstractListEdit::moveUpItem() { QModelIndex index = m_itemView->currentIndex(); if (index.isValid() && index.row() > 0) { int row = index.row(); QAbstractItemModel* model = m_itemView->model(); const int numColumns = model->columnCount(); QVector editValues(numColumns); QVector checkValues(numColumns); for (int column = 0; column < numColumns; ++column) { QModelIndex idx = model->index(row, column); editValues[column] = idx.data(Qt::EditRole); checkValues[column] = idx.data(Qt::CheckStateRole); } model->removeRow(row); model->insertRow(row - 1); for (int column = 0; column < numColumns; ++column) { QModelIndex idx = model->index(row - 1, column); model->setData(idx, editValues.at(column), Qt::EditRole); model->setData(idx, checkValues.at(column), Qt::CheckStateRole); } QModelIndex newIndex = model->index(row - 1, index.column()); m_itemView->setCurrentIndex(newIndex); } } /** * Move the selected item down. */ void AbstractListEdit::moveDownItem() { QModelIndex index = m_itemView->currentIndex(); QAbstractItemModel* model = m_itemView->model(); if (index.isValid() && index.row() < model->rowCount() - 1) { const int numColumns = model->columnCount(); int row = index.row(); QVector editValues(numColumns); QVector checkValues(numColumns); for (int column = 0; column < numColumns; ++column) { QModelIndex idx = model->index(row, column); editValues[column] = idx.data(Qt::EditRole); checkValues[column] = idx.data(Qt::CheckStateRole); } model->removeRow(row); model->insertRow(row + 1); for (int column = 0; column < numColumns; ++column) { QModelIndex idx = model->index(row + 1, column); model->setData(idx, editValues.at(column), Qt::EditRole); model->setData(idx, checkValues.at(column), Qt::CheckStateRole); } QModelIndex newIndex = model->index(row + 1, index.column()); m_itemView->setCurrentIndex(newIndex); } } /** * Change state of buttons according to the current item and the count. */ void AbstractListEdit::setButtonEnableState() { QModelIndex index = m_itemView->currentIndex(); QAbstractItemModel* model = m_itemView->model(); int idx = -1; if (index.isValid()) idx = index.row(); m_moveUpPushButton->setEnabled(idx > 0); m_moveDownPushButton->setEnabled( idx >= 0 && idx < model->rowCount() - 1); m_editPushButton->setEnabled(idx >= 0); m_removePushButton->setEnabled(idx >= 0); } /** * Hide the Edit button. */ void AbstractListEdit::hideEditButton() { m_editPushButton->hide(); } kid3-3.0.2/src/gui/widgets/abstractlistedit.h000066400000000000000000000050261224603511300211050ustar00rootroot00000000000000/** * \file abstractlistedit.h * Widget to edit a list. * * \b Project: Kid3 * \author Urs Fleisch * \date 2 Jan 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef ABSTRACTLISTEDIT_H #define ABSTRACTLISTEDIT_H #include class QPushButton; class QAbstractItemView; class QAbstractItemModel; /** * Widget to edit a string list. */ class AbstractListEdit : public QWidget { Q_OBJECT public: /** * Constructor. * * @param itemView item view, e.g. a QListView * @param model item model, e.g. a QStringListModel * @param parent parent widget */ AbstractListEdit(QAbstractItemView* itemView, QAbstractItemModel* model, QWidget* parent = 0); /** * Destructor. */ virtual ~AbstractListEdit(); /** * Disable editing of items. * When editing is disabled, the Add, Edit and Remove buttons are hidden. * @param disable true to disable, false (default) to enable editing. */ void setEditingDisabled(bool disable); public slots: /** * Add a new item. */ virtual void addItem() = 0; /** * Remove the selected item. */ void removeItem(); /** * Edit the selected item. */ virtual void editItem() = 0; /** * Move the selected item up. */ void moveUpItem(); /** * Move the selected item down. */ void moveDownItem(); /** * Change state of buttons according to the current item and the count. */ void setButtonEnableState(); protected: /** * Get item view. * @return item view. */ const QAbstractItemView* getItemView() const { return m_itemView; } /** * Hide the Edit button. */ void hideEditButton(); private: QAbstractItemView* m_itemView; QPushButton* m_addPushButton; QPushButton* m_moveUpPushButton; QPushButton* m_moveDownPushButton; QPushButton* m_editPushButton; QPushButton* m_removePushButton; }; #endif // ABSTRACTLISTEDIT_H kid3-3.0.2/src/gui/widgets/comboboxdelegate.cpp000066400000000000000000000056311224603511300214000ustar00rootroot00000000000000/** * \file comboboxdelegate.cpp * Delegate to select with combo box between items in Qt:UserRole data. * * \b Project: Kid3 * \author Urs Fleisch * \date 28 May 2011 * * Copyright (C) 2011-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "comboboxdelegate.h" #include /** * Constructor. * @param parent parent object */ ComboBoxDelegate::ComboBoxDelegate(QObject* parent) : QStyledItemDelegate(parent) { setObjectName(QLatin1String("ComboBoxDelegate")); } /** * Destructor. */ ComboBoxDelegate::~ComboBoxDelegate() { } /** * Create an editor to edit the cells contents. * @param parent parent widget * @param option style * @param index index of item * @return combo box editor widget. */ QWidget* ComboBoxDelegate::createEditor( QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const { QVariant itemsData(index.data(Qt::UserRole)); if (itemsData.isValid() && itemsData.type() == QVariant::StringList) { QStringList items(itemsData.toStringList()); int itemIndex = items.indexOf(index.data(Qt::EditRole).toString()); QComboBox* cb = new QComboBox(parent); cb->addItems(itemsData.toStringList()); if (itemIndex >= 0) cb->setCurrentIndex(itemIndex); return cb; } else { return QStyledItemDelegate::createEditor(parent, option, index); } } /** * Set data to be edited by the editor. * @param editor editor widget * @param index index of item */ void ComboBoxDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const { if (QComboBox* cb = qobject_cast(editor)) { int itemIndex = cb->findText(index.data(Qt::EditRole).toString()); if (itemIndex >= 0) cb->setCurrentIndex(itemIndex); } else { QStyledItemDelegate::setEditorData(editor, index); } } /** * Set model data supplied by editor. * @param editor editor widget * @param model model * @param index index of item */ void ComboBoxDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const { if (QComboBox* cb = qobject_cast(editor)) model->setData(index, cb->currentText(), Qt::EditRole); else QStyledItemDelegate::setModelData(editor, model, index); } kid3-3.0.2/src/gui/widgets/comboboxdelegate.h000066400000000000000000000037531224603511300210500ustar00rootroot00000000000000/** * \file comboboxdelegate.cpp * Delegate to select with combo box between items in Qt:UserRole data. * * \b Project: Kid3 * \author Urs Fleisch * \date 28 May 2011 * * Copyright (C) 2011 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef COMBOBOXDELEGATE_H #define COMBOBOXDELEGATE_H #include /** * Delegate to select with combo box between items in Qt:UserRole data. */ class ComboBoxDelegate : public QStyledItemDelegate { public: /** * Constructor. * @param parent parent object */ explicit ComboBoxDelegate(QObject* parent = 0); /** * Destructor. */ virtual ~ComboBoxDelegate(); /** * Create an editor to edit the cells contents. * @param parent parent widget * @param option style * @param index index of item * @return combo box editor widget. */ virtual QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index ) const; /** * Set data to be edited by the editor. * @param editor editor widget * @param index index of item */ virtual void setEditorData(QWidget* editor, const QModelIndex& index) const; virtual void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const; }; #endif // COMBOBOXDELEGATE_H kid3-3.0.2/src/gui/widgets/configtable.cpp000066400000000000000000000102331224603511300203440ustar00rootroot00000000000000/** * \file configtable.cpp * Table with context menu to add, delete and clear rows. * * \b Project: Kid3 * \author Urs Fleisch * \date 10 Oct 2005 * * Copyright (C) 2005-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "configtable.h" #include #include /** * Constructor. * * @param parent parent widget */ ConfigTable::ConfigTable(QAbstractItemModel* model, QWidget* parent) : AbstractListEdit(m_tableView = new QTableView, model, parent) { setObjectName(QLatin1String("ConfigTable")); hideEditButton(); m_tableView->setContextMenuPolicy(Qt::CustomContextMenu); connect(m_tableView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(customContextMenu(QPoint))); } /** * Destructor. */ ConfigTable::~ConfigTable() {} /** * Set the resize modes to be used for the columns. * @param resizeModes list of resize modes for the columns */ void ConfigTable::setHorizontalResizeModes( const QList& resizeModes) { QHeaderView* header = m_tableView->horizontalHeader(); int col = 0; foreach (QHeaderView::ResizeMode mode, resizeModes) #if QT_VERSION >= 0x050000 header->setSectionResizeMode(col++, mode); #else header->setResizeMode(col++, mode); #endif } /** * Insert a new row into the table. * * @param row the new row is inserted after this row */ void ConfigTable::addRow(int row) { m_tableView->model()->insertRow(row + 1); } /** * Delete a row from the table. * * @param row row to delete */ void ConfigTable::deleteRow(int row) { if (m_tableView->model()->rowCount() <= 1) return; m_tableView->model()->removeRow(row); } /** * Clear a row in the table. * * @param row row to clear */ void ConfigTable::clearRow(int row) { if (row < m_tableView->model()->rowCount() && m_tableView->model()->removeRow(row)) m_tableView->model()->insertRow(row); } /** * Execute a context menu action. * * @param action action of selected menu */ void ConfigTable::executeAction(QAction* action) { if (action) { int row = action->data().toInt(); int cmd = row & 3; row >>= 2; switch (cmd) { case 0: addRow(row); break; case 1: deleteRow(row); break; case 2: default: clearRow(row); break; } } } /** * Display context menu. * * @param row row at which context menu is displayed * @param col column at which context menu is displayed * @param pos position where context menu is drawn on screen */ void ConfigTable::contextMenu(int row, int /* col */, const QPoint& pos) { QMenu menu(this); QAction* action; if (row >= -1) { action = menu.addAction(tr("&Insert row")); if (action) action->setData((row << 2) | 0); } if (row >= 0) { action = menu.addAction(tr("&Delete row")); if (action) action->setData((row << 2) | 1); } if (row >= 0) { action = menu.addAction(tr("&Clear row")); if (action) action->setData((row << 2) | 2); } connect(&menu, SIGNAL(triggered(QAction*)), this, SLOT(executeAction(QAction*))); menu.setMouseTracking(true); menu.exec(pos); } /** * Display custom context menu. * * @param pos position where context menu is drawn on screen */ void ConfigTable::customContextMenu(const QPoint& pos) { QModelIndex index = m_tableView->indexAt(pos); if (index.isValid()) { contextMenu(index.row(), index.column(), mapToGlobal(pos)); } } /** * Add a new item. */ void ConfigTable::addItem() { addRow(getItemView()->model()->rowCount()); } /** * Edit the selected item. */ void ConfigTable::editItem() { } kid3-3.0.2/src/gui/widgets/configtable.h000066400000000000000000000052011224603511300200100ustar00rootroot00000000000000/** * \file configtable.h * Table with context menu to add, delete and clear rows. * * \b Project: Kid3 * \author Urs Fleisch * \date 10 Oct 2005 * * Copyright (C) 2005-2007 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef CONFIGTABLE_H #define CONFIGTABLE_H #include #include #include #include "abstractlistedit.h" /** * Context menu commands configuration table. */ class ConfigTable : public AbstractListEdit { Q_OBJECT public: /** * Constructor. * * @param model item model * @param parent parent widget */ explicit ConfigTable(QAbstractItemModel* model, QWidget* parent = 0); /** * Destructor. */ virtual ~ConfigTable(); /** * Set the resize modes to be used for the columns. * @param resizeModes list of resize modes for the columns */ void setHorizontalResizeModes(const QList& resizeModes); public slots: /** * Add a new item. */ virtual void addItem(); /** * Edit the selected item. */ virtual void editItem(); /** * Insert a new row into the table. * * @param row the new row is inserted after this row */ void addRow(int row); /** * Delete a row from the table. * * @param row row to delete */ void deleteRow(int row); /** * Clear a row in the table. * * @param row row to clear */ void clearRow(int row); /** * Execute a context menu action. * * @param action action of selected menu */ void executeAction(QAction* action); /** * Display context menu. * * @param row row at which context menu is displayed * @param col column at which context menu is displayed * @param pos position where context menu is drawn on screen */ void contextMenu(int row, int col, const QPoint& pos); /** * Display custom context menu. * * @param pos position where context menu is drawn on screen */ void customContextMenu(const QPoint& pos); private: QTableView* m_tableView; }; #endif // CONFIGTABLE_H kid3-3.0.2/src/gui/widgets/formatbox.cpp000066400000000000000000000110721224603511300200720ustar00rootroot00000000000000/** * \file formatbox.cpp * Group box containing format options. * * \b Project: Kid3 * \author Urs Fleisch * \date 17 Sep 2003 * * Copyright (C) 2003-2012 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "formatbox.h" #include #include #include #include #include #include #include "formatconfig.h" #include "configtable.h" #include "configtablemodel.h" /** * Constructor. * * @param title title * @param parent parent widget */ FormatBox::FormatBox(const QString& title, QWidget* parent) : QGroupBox(title, parent) { m_formatEditingCheckBox = new QCheckBox(tr("Format while editing"), this); QLabel* caseConvLabel = new QLabel(this); caseConvLabel->setText(tr("Case conversion:")); m_caseConvComboBox = new QComboBox(this); m_caseConvComboBox->setEditable(false); m_caseConvComboBox->clear(); m_caseConvComboBox->insertItem(FormatConfig::NoChanges, tr("No changes")); m_caseConvComboBox->insertItem(FormatConfig::AllLowercase, tr("All lowercase")); m_caseConvComboBox->insertItem(FormatConfig::AllUppercase, tr("All uppercase")); m_caseConvComboBox->insertItem(FormatConfig::FirstLetterUppercase, tr("First letter uppercase")); m_caseConvComboBox->insertItem(FormatConfig::AllFirstLettersUppercase, tr("All first letters uppercase")); #if QT_VERSION >= 0x040800 QHBoxLayout* localeLayout = new QHBoxLayout; QLabel* localeLabel = new QLabel(tr("Locale:")); localeLayout->addWidget(localeLabel); m_localeComboBox = new QComboBox(this); m_localeComboBox->addItem(tr("None")); m_localeComboBox->addItems(QLocale().uiLanguages()); localeLabel->setBuddy(m_localeComboBox); localeLayout->addWidget(m_localeComboBox); #endif m_strRepCheckBox = new QCheckBox(this); m_strRepCheckBox->setText(tr("String replacement:")); m_strReplTableModel = new ConfigTableModel(this); m_strReplTableModel->setLabels( QStringList() << tr("From") << tr("To")); m_strReplTable = new ConfigTable(m_strReplTableModel, this); m_strReplTable->setHorizontalResizeModes( m_strReplTableModel->getHorizontalResizeModes()); QVBoxLayout* vbox = new QVBoxLayout; vbox->addWidget(m_formatEditingCheckBox); vbox->addWidget(caseConvLabel); vbox->addWidget(m_caseConvComboBox); #if QT_VERSION >= 0x040800 vbox->addLayout(localeLayout); #endif vbox->addWidget(m_strRepCheckBox); vbox->addWidget(m_strReplTable); setLayout(vbox); } /** * Destructor. */ FormatBox::~FormatBox() {} /** * Set the values from a format configuration. * * @param cfg format configuration */ void FormatBox::fromFormatConfig(const FormatConfig& cfg) { m_formatEditingCheckBox->setChecked(cfg.m_formatWhileEditing); m_caseConvComboBox->setCurrentIndex(cfg.m_caseConversion); #if QT_VERSION >= 0x040800 int localeIndex = m_localeComboBox->findText(cfg.getLocaleName()); if (localeIndex == -1) { localeIndex = 0; } m_localeComboBox->setCurrentIndex(localeIndex); #endif m_strRepCheckBox->setChecked(cfg.m_strRepEnabled); m_strReplTableModel->setMap(cfg.m_strRepMap); } /** * Store the values in a format configuration. * * @param cfg format configuration */ void FormatBox::toFormatConfig(FormatConfig& cfg) const { cfg.m_formatWhileEditing = m_formatEditingCheckBox->isChecked(); cfg.m_caseConversion = (FormatConfig::CaseConversion)m_caseConvComboBox->currentIndex(); if (cfg.m_caseConversion >= FormatConfig::NumCaseConversions) { cfg.m_caseConversion = FormatConfig::NoChanges; } #if QT_VERSION >= 0x040800 cfg.setLocaleName(m_localeComboBox->currentIndex() > 0 ? m_localeComboBox->currentText() : QString()); #endif cfg.m_strRepEnabled = m_strRepCheckBox->isChecked(); cfg.m_strRepMap = m_strReplTableModel->getMap(); } kid3-3.0.2/src/gui/widgets/formatbox.h000066400000000000000000000035461224603511300175460ustar00rootroot00000000000000/** * \file formatbox.h * Group box containing format options. * * \b Project: Kid3 * \author Urs Fleisch * \date 17 Sep 2003 * * Copyright (C) 2003-2012 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef FORMATBOX_H #define FORMATBOX_H #include class QComboBox; class QCheckBox; class QString; class FormatConfig; class ConfigTable; class ConfigTableModel; /** * Group box containing format options. */ class FormatBox : public QGroupBox { Q_OBJECT public: /** * Constructor. * * @param title title * @param parent parent widget */ explicit FormatBox(const QString& title, QWidget* parent = 0); /** * Destructor. */ ~FormatBox(); /** * Set the values from a format configuration. * * @param cfg format configuration */ void fromFormatConfig(const FormatConfig& cfg); /** * Store the values in a format configuration. * * @param cfg format configuration */ void toFormatConfig(FormatConfig& cfg) const; private: QComboBox* m_caseConvComboBox; #if QT_VERSION >= 0x040800 QComboBox* m_localeComboBox; #endif QCheckBox* m_strRepCheckBox; ConfigTable* m_strReplTable; ConfigTableModel* m_strReplTableModel; QCheckBox* m_formatEditingCheckBox; }; #endif kid3-3.0.2/src/gui/widgets/formatlistedit.cpp000066400000000000000000000172471224603511300211350ustar00rootroot00000000000000/** * \file formatlistedit.cpp * Widget to edit a format list. * * \b Project: Kid3 * \author Urs Fleisch * \date 13 Aug 2011 * * Copyright (C) 2011-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "formatlistedit.h" #include #include #include #include #include #include /** * Constructor. * * @param labels list of label texts for fields in a single format * @param tooltips list of tooltips, one string per label, empty if not used * @param parent parent widget */ FormatListEdit::FormatListEdit(const QStringList& labels, const QStringList& tooltips, QWidget* parent) : QWidget(parent) { setObjectName(QLatin1String("FormatListEdit")); QHBoxLayout* hlayout = new QHBoxLayout(this); hlayout->setContentsMargins(0, 0, 0, 0); QFormLayout* formatLayout = new QFormLayout; formatLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); bool comboBoxCreated = false; for (int i = 0; i < labels.size(); ++i) { const QString& label = labels.at(i); const QString& toolTip = tooltips.at(i); if (!comboBoxCreated) { m_formatComboBox = new QComboBox; m_formatComboBox->setEditable(true); m_formatComboBox->setInsertPolicy(QComboBox::NoInsert); connect(m_formatComboBox, SIGNAL(activated(int)), this, SLOT(updateLineEdits(int))); connect(m_formatComboBox->lineEdit(), SIGNAL(editingFinished()), this, SLOT(commitCurrentEdits())); if (!toolTip.isEmpty()) m_formatComboBox->setToolTip(toolTip); formatLayout->addRow(label, m_formatComboBox); comboBoxCreated = true; } else { QLineEdit* ed = new QLineEdit; connect(ed, SIGNAL(returnPressed()), this, SIGNAL(formatChanged())); if (!toolTip.isEmpty()) ed->setToolTip(toolTip); formatLayout->addRow(label, ed); m_lineEdits.append(ed); } } hlayout->addLayout(formatLayout); QVBoxLayout* vlayout = new QVBoxLayout; #ifdef Q_OS_MAC vlayout->setSpacing(6); #endif m_addPushButton = new QPushButton(tr("&Add")); m_addPushButton->setAutoDefault(false); m_removePushButton = new QPushButton(tr("&Remove")); m_removePushButton->setAutoDefault(false); vlayout->addWidget(m_addPushButton); vlayout->addWidget(m_removePushButton); vlayout->addStretch(); hlayout->addLayout(vlayout); connect(m_addPushButton, SIGNAL(clicked()), this, SLOT(addItem())); connect(m_removePushButton, SIGNAL(clicked()), this, SLOT(removeItem())); setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum); } /** * Destructor. */ FormatListEdit::~FormatListEdit() { } /** * Set format strings. * * @param formats list of format stringlists, the first stringlist contains * the names, the second the corresponding string for the first line edit, * etc. * @param index index to select, -1 to keep current index */ void FormatListEdit::setFormats(const QList& formats, int index) { m_formats = formats; if (index >= 0) { m_formatComboBox->setCurrentIndex(index); updateComboBoxAndLineEdits(index); } } /** * Get format strings. * * @param index if not null, the current index is returned here * * @return list of format stringlists, the first stringlist contains * the names, the second the corresponding string for the first line edit, * etc. */ QList FormatListEdit::getFormats(int* index) { commitCurrentEdits(); if (index) { *index = m_formatComboBox->currentIndex(); } return m_formats; } /** * Get a format string from the format currently displayed in the GUI. * * @param formatNr index of the format stringlist, 0 is the format name * 1 the first line edit, etc. * * @return format string. */ QString FormatListEdit::getCurrentFormat(int formatNr) const { if (formatNr == 0) { return m_formatComboBox->currentText(); } else if (formatNr > 0 && formatNr - 1 < m_lineEdits.size()) { return m_lineEdits.at(formatNr - 1)->text(); } else { return QString(); } } /** * Update GUI controls from formats. * * @param index combo box index to set */ void FormatListEdit::updateComboBoxAndLineEdits(int index) { m_formatComboBox->clear(); if (!m_formats.isEmpty()) { m_formatComboBox->addItems(m_formats.first()); if (index >= 0 && index < m_formats.first().size()) { m_formatComboBox->setCurrentIndex(index); updateLineEdits(index); } } } /** * Set the currently selected format from the contents of the controls. */ void FormatListEdit::commitCurrentEdits() { int index = m_formatComboBox->currentIndex(); if (index < 0) return; if (m_formatComboBox->itemText(index) != m_formatComboBox->currentText()) { m_formatComboBox->setItemText(index, m_formatComboBox->currentText()); } for (int i = 0; i < m_formats.size() && i - 1 < m_lineEdits.size(); ++i) { QString text(i == 0 ? m_formatComboBox->currentText() : m_lineEdits.at(i - 1)->text()); QStringList& fmts = m_formats[i]; if (index < fmts.size()) { fmts[index] = text; } } } /** * Set the format lineedits to the format of the index. * * @param index selected item in combo box */ void FormatListEdit::updateLineEdits(int index) { for (int i = 0; i < m_lineEdits.size() && i + 1 < m_formats.size(); ++i) { QLineEdit* le = m_lineEdits.at(i); const QStringList& fmts = m_formats.at(i + 1); if (index < fmts.size()) { le->setText(fmts.at(index)); } else { le->clear(); } } emit formatChanged(); } /** * Add a new item. */ void FormatListEdit::addItem() { commitCurrentEdits(); if (!m_formats.isEmpty()) { // first search for an existing empty format int index = -1; for (int fmtIdx = m_formats.first().size() - 1; fmtIdx > 0; --fmtIdx) { bool allEmpty = true; for (int leIdx = 1; leIdx < m_formats.size(); ++leIdx) { const QStringList& fmts = m_formats.at(leIdx); if (fmtIdx < fmts.size() && !fmts.at(fmtIdx).isEmpty()) { allEmpty = false; break; } } if (allEmpty) { index = fmtIdx; break; } } if (index == -1) { // no empty format found, add a new one for (int i = 0; i < m_formats.size(); ++i) { m_formats[i].append(i == 0 ? tr("New") : QLatin1String("")); } index = m_formats.first().size() - 1; } updateComboBoxAndLineEdits(index); m_formatComboBox->lineEdit()->setFocus(); m_formatComboBox->lineEdit()->selectAll(); } } /** * Remove the selected item. */ void FormatListEdit::removeItem() { int index = m_formatComboBox->currentIndex(); if (index < 0) return; for (int i = 0; i < m_formats.size(); ++i) { if (index < m_formats[i].size()) { m_formats[i].removeAt(index); } } if (!m_formats.isEmpty()) { const QStringList& fmts = m_formats.first(); if (index >= fmts.size()) { index = fmts.size() - 1; } if (index < 0) { addItem(); } else { updateComboBoxAndLineEdits(index); } } } kid3-3.0.2/src/gui/widgets/formatlistedit.h000066400000000000000000000064071224603511300205760ustar00rootroot00000000000000/** * \file formatlistedit.h * Widget to edit a format list. * * \b Project: Kid3 * \author Urs Fleisch * \date 13 Aug 2011 * * Copyright (C) 2011 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef FORMATLISTEDIT_H #define FORMATLISTEDIT_H #include #include #include class QComboBox; class QLineEdit; class QPushButton; /** * Widget to edit a format list. */ class FormatListEdit : public QWidget { Q_OBJECT public: /** * Constructor. * * @param labels list of label texts for fields in a single format * @param tooltips list of tooltips, one string per label, empty if not used * @param parent parent widget */ FormatListEdit(const QStringList& labels, const QStringList& tooltips, QWidget* parent = 0); /** * Destructor. */ virtual ~FormatListEdit(); /** * Set format strings. * * @param formats list of format stringlists, the first stringlist contains * the names, the second the corresponding string for the first line edit, * etc. * @param index index to select, -1 to keep current index */ void setFormats(const QList& formats, int index = -1); /** * Get format strings. * * @param index if not null, the current index is returned here * * @return list of format stringlists, the first stringlist contains * the names, the second the corresponding string for the first line edit, * etc. */ QList getFormats(int* index = 0); /** * Get a format string from the format currently displayed in the GUI. * * @param formatNr index of the format stringlist, 0 is the format name * 1 the first line edit, etc. * * @return format string. */ QString getCurrentFormat(int formatNr) const; signals: /** * Emitted when another format is selected or return is pressed in a lineedit. */ void formatChanged(); private slots: /** * Set the currently selected format from the contents of the controls. */ void commitCurrentEdits(); /** * Set the format lineedits to the format of the index. * * @param index selected item in combo box */ void updateLineEdits(int index); /** * Add a new item. */ void addItem(); /** * Remove the selected item. */ void removeItem(); private: /** * Update GUI controls from formats. * * @param index combo box index to set */ void updateComboBoxAndLineEdits(int index); QList m_formats; QComboBox* m_formatComboBox; QList m_lineEdits; QPushButton* m_addPushButton; QPushButton* m_removePushButton; }; #endif // FORMATLISTEDIT_H kid3-3.0.2/src/gui/widgets/frametable.cpp000066400000000000000000000134311224603511300201740ustar00rootroot00000000000000/** * \file frametable.cpp * Table to edit frames. * * \b Project: Kid3 * \author Urs Fleisch * \date 05 Sep 2007 * * Copyright (C) 2007-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "frametable.h" #include #include #include #include #include #include "frametablemodel.h" /** * Constructor. * * @param model frame table model * @param parent parent widget */ FrameTable::FrameTable(FrameTableModel* model, QWidget* parent) : QTableView(parent), m_currentEditor(0) { setObjectName(QLatin1String("FrameTable")); setModel(model); setSelectionMode(SingleSelection); #if QT_VERSION >= 0x050000 horizontalHeader()->setSectionResizeMode(FrameTableModel::CI_Value, QHeaderView::Stretch); #else horizontalHeader()->setResizeMode(FrameTableModel::CI_Value, QHeaderView::Stretch); #endif // Set a small height instead of hiding the header, so that the column // widths can still be resized by the user. horizontalHeader()->setFixedHeight(2); horizontalHeader()->setStyleSheet(QLatin1String("color: rgba(0, 0, 0, 0);")); verticalHeader()->hide(); if (model->isId3v1()) { bool insertTemporaryRow = model->rowCount() < 1; if (insertTemporaryRow) model->insertRow(0); setMinimumHeight((Frame::FT_LastV1Frame + 1) * (rowHeight(0) + 1)); if (insertTemporaryRow) model->removeRow(0); } // Set width of first column int width = fontMetrics().width(tr("WWW Audio Source") + QLatin1String("WW")); QStyleOptionButton option; option.initFrom(this); width += style()->subElementRect( QStyle::SE_ViewItemCheckIndicator, &option, this).width(); setColumnWidth(FrameTableModel::CI_Enable, width); #if QT_VERSION >= 0x050000 horizontalHeader()->setSectionResizeMode(FrameTableModel::CI_Value, QHeaderView::Stretch); #else horizontalHeader()->setResizeMode(FrameTableModel::CI_Value, QHeaderView::Stretch); #endif setItemDelegate(new FrameItemDelegate(this)); setEditTriggers(AllEditTriggers); viewport()->installEventFilter(this); // keep track of editors setContextMenuPolicy(Qt::CustomContextMenu); connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(customContextMenu(QPoint))); } /** * Destructor. */ FrameTable::~FrameTable() {} /** * Filters events if this object has been installed as an event filter * for the watched object. * This method is reimplemented to keep track of the current open editor. * It has to be installed on the viewport of the table. * @param event event * @return true to filter event out. */ bool FrameTable::eventFilter(QObject*, QEvent* event) { if (event) { QEvent::Type type = event->type(); if (type == QEvent::ChildAdded) { QObject* obj = ((QChildEvent*)event)->child(); if (obj && obj->isWidgetType()) { m_currentEditor = (QWidget*)obj; } } else if (type == QEvent::ChildRemoved) { if (m_currentEditor == ((QChildEvent*)event)->child()) { m_currentEditor = 0; } } else if (type == QEvent::WindowDeactivate) { // this is done to avoid losing focus when the window is deactivated // while editing a cell (i.e. the cell is not closed by pressing Enter) if ((state() == QAbstractItemView::EditingState) && m_currentEditor) { commitData(m_currentEditor); closeEditor(m_currentEditor, QAbstractItemDelegate::EditPreviousItem); } } } return false; } /** * Commit data from the current editor. * This is used to avoid losing the changes in open editors e.g. when * the file is changed using Alt-Up or Alt-Down. * * @return true if data was committed. */ bool FrameTable::acceptEdit() { if ((state() == QAbstractItemView::EditingState) && m_currentEditor) { commitData(m_currentEditor); // close editor to avoid being stuck in QAbstractItemView::NoState closeEditor(m_currentEditor, QAbstractItemDelegate::NoHint); return true; } return false; } /** * Get current editor widget if the table is currently in edit state. * @return current editor widget, 0 if not in edit state. */ const QWidget* FrameTable::getCurrentEditor() const { return state() == EditingState ? m_currentEditor : 0; } /** * Display context menu. * * @param row row at which context menu is displayed * @param col column at which context menu is displayed * @param pos position where context menu is drawn on screen */ void FrameTable::contextMenu(int row, int col, const QPoint& pos) { const FrameTableModel* ftModel = qobject_cast(model()); if (ftModel && col == 0 && row >= 0) { QMenu menu(this); QAction* action = menu.addAction(tr("&Select all")); connect(action, SIGNAL(triggered()), ftModel, SLOT(selectAllFrames())); action = menu.addAction(tr("&Deselect all")); connect(action, SIGNAL(triggered()), ftModel, SLOT(deselectAllFrames())); menu.setMouseTracking(true); menu.exec(pos); } } /** * Display custom context menu. * * @param pos position where context menu is drawn on screen */ void FrameTable::customContextMenu(const QPoint& pos) { QModelIndex index = indexAt(pos); if (index.isValid()) { contextMenu(index.row(), index.column(), mapToGlobal(pos)); } } kid3-3.0.2/src/gui/widgets/frametable.h000066400000000000000000000051051224603511300176400ustar00rootroot00000000000000/** * \file frametable.h * Table to edit frames. * * \b Project: Kid3 * \author Urs Fleisch * \date 05 Sep 2007 * * Copyright (C) 2007-2011 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef FRAMETABLE_H #define FRAMETABLE_H #include class QAction; class QPoint; class FrameTableModel; /** * Table to edit frames. */ class FrameTable : public QTableView { Q_OBJECT public: /** * Constructor. * * @param model frame table model * @param parent parent widget */ explicit FrameTable(FrameTableModel* model, QWidget* parent = 0); /** * Destructor. */ virtual ~FrameTable(); /** * Filters events if this object has been installed as an event filter * for the watched object. * This method is reimplemented to keep track of the current open editor. * It has to be installed on the viewport of the table. * @param watched watched object * @param event event * @return true to filter event out. */ virtual bool eventFilter(QObject* watched, QEvent* event); /** * Commit data from the current editor. * This is used to avoid losing the changes in open editors e.g. when * the file is changed using Alt-Up or Alt-Down. * * @return true if data was committed. */ bool acceptEdit(); /** * Get current editor widget if the table is currently in edit state. * @return current editor widget, 0 if not in edit state. */ const QWidget* getCurrentEditor() const; private slots: /** * Display context menu. * * @param row row at which context menu is displayed * @param col column at which context menu is displayed * @param pos position where context menu is drawn on screen */ void contextMenu(int row, int col, const QPoint& pos); /** * Display custom context menu. * * @param pos position where context menu is drawn on screen */ void customContextMenu(const QPoint& pos); private: QWidget* m_currentEditor; }; #endif // FRAMETABLE_H kid3-3.0.2/src/gui/widgets/imageviewer.cpp000066400000000000000000000042611224603511300203770ustar00rootroot00000000000000/** * \file imageviewer.cpp * Window to view image. * * \b Project: Kid3 * \author Urs Fleisch * \date 10 Jun 2009 * * Copyright (C) 2003-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "imageviewer.h" #include #include #include #include #include #include /** * Constructor. * * @param parent parent widget * @param img image to display in window */ ImageViewer::ImageViewer(QWidget* parent, const QImage& img) : QDialog(parent) { setObjectName(QLatin1String("ImageViewer")); setModal(true); setWindowTitle(tr("View Picture")); QVBoxLayout* vlayout = new QVBoxLayout(this); QHBoxLayout* hlayout = new QHBoxLayout; QSpacerItem* hspacer = new QSpacerItem(16, 0, QSizePolicy::Expanding, QSizePolicy::Minimum); m_image = new QLabel(this); QPushButton* closeButton = new QPushButton(tr("&Close"), this); m_image->setScaledContents(true); QSize imageSize(img.size()); QSize desktopSize(QApplication::desktop()->availableGeometry().size()); desktopSize -= QSize(12, 12); if (imageSize.width() > desktopSize.width() || imageSize.height() > desktopSize.height()) { m_image->setPixmap(QPixmap::fromImage(img.scaled(desktopSize, Qt::KeepAspectRatio))); } else { m_image->setPixmap(QPixmap::fromImage(img)); } vlayout->addWidget(m_image); hlayout->addItem(hspacer); hlayout->addWidget(closeButton); connect(closeButton, SIGNAL(clicked()), this, SLOT(accept())); vlayout->addLayout(hlayout); } kid3-3.0.2/src/gui/widgets/imageviewer.h000066400000000000000000000024401224603511300200410ustar00rootroot00000000000000/** * \file imageviewer.h * Window to view image. * * \b Project: Kid3 * \author Urs Fleisch * \date 10 Jun 2009 * * Copyright (C) 2003-2007 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef IMAGEVIEWER_H #define IMAGEVIEWER_H #include class QImage; class QLabel; /** Window to view image */ class ImageViewer : public QDialog { Q_OBJECT public: /** * Constructor. * * @param parent parent widget * @param img image to display in window */ ImageViewer(QWidget* parent, const QImage& img); /** * Destructor. */ virtual ~ImageViewer() {} private: /** image to view */ QLabel* m_image; }; #endif // IMAGEVIEWER_H kid3-3.0.2/src/gui/widgets/picturelabel.cpp000066400000000000000000000043241224603511300205460ustar00rootroot00000000000000/** * \file picturelabel.cpp * Label for picture preview. * * \b Project: Kid3 * \author Urs Fleisch * \date 04 Jan 2009 * * Copyright (C) 2009-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "picturelabel.h" #include #include #include #include /** * Constructor. * * @param parent parent widget */ PictureLabel::PictureLabel(QWidget* parent) : QLabel(parent), m_pixmapHash(0) { setObjectName(QLatin1String("PictureLabel")); setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); setWordWrap(true); clearPicture(); } /** * Destructor. */ PictureLabel::~PictureLabel() { } /** * Get preferred height for a given width. * @return height. */ int PictureLabel::heightForWidth(int w) const { return w; } /** * Clear picture. */ void PictureLabel::clearPicture() { const char* const msg = QT_TRANSLATE_NOOP("@default", "Drag album\nartwork\nhere"); setText(QCoreApplication::translate("@default", msg)); } /** * Set picture data. * * @param data picture data, 0 if no picture is available */ void PictureLabel::setData(const QByteArray* data) { if (data && !data->isEmpty()) { uint hash = qHash(*data); if (pixmap() && hash == m_pixmapHash) return; // keep existing pixmap // creating new pixmap QPixmap pm; if (pm.loadFromData(*data)) { QPixmap scaledPm = pm.scaled(width(), height(), Qt::KeepAspectRatio); if (!scaledPm.isNull()) { m_pixmapHash = hash; setContentsMargins(0, 0, 0, 0); setPixmap(scaledPm); return; } } } clearPicture(); } kid3-3.0.2/src/gui/widgets/picturelabel.h000066400000000000000000000030211224603511300202040ustar00rootroot00000000000000/** * \file picturelabel.h * Label for picture preview. * * \b Project: Kid3 * \author Urs Fleisch * \date 04 Jan 2009 * * Copyright (C) 2009-2012 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef PICTURELABEL_H #define PICTURELABEL_H #include class QByteArray; /** * Label for picture preview. */ class PictureLabel : public QLabel { public: /** * Constructor. * * @param parent parent widget */ explicit PictureLabel(QWidget* parent); /** * Destructor. */ virtual ~PictureLabel(); /** * Get preferred height for a given width. * @return height. */ virtual int heightForWidth(int w) const; /** * Set picture data. * * @param data picture data, 0 if no picture is available */ void setData(const QByteArray* data); private: /** * Clear picture. */ void clearPicture(); uint m_pixmapHash; }; #endif // PICTURELABEL_H kid3-3.0.2/src/gui/widgets/playtoolbar.cpp000066400000000000000000000242451224603511300204270ustar00rootroot00000000000000/** * \file playtoolbar.cpp * Audio player toolbar. * * \b Project: Kid3 * \author Urs Fleisch * \date 24-Aug-2010 * * Copyright (C) 2010-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "playtoolbar.h" #if defined HAVE_PHONON || QT_VERSION >= 0x050000 #include #include #include #include #include #include #include #include #ifdef HAVE_PHONON #include #include #include #else #include #include #endif #include "audioplayer.h" static const QString zeroTime(QLatin1String(" 0:00")); /** * Constructor. * * @param player audio player * @param parent parent widget */ PlayToolBar::PlayToolBar(AudioPlayer* player, QWidget* parent) : QToolBar(parent), m_player(player) { setObjectName(QLatin1String("Kid3Player")); setWindowTitle(tr("Play")); m_playIcon = style()->standardIcon(QStyle::SP_MediaPlay); m_pauseIcon = style()->standardIcon(QStyle::SP_MediaPause); m_playOrPauseAction = new QAction(m_playIcon, tr("Play/Pause"), this); m_stopAction = new QAction( style()->standardIcon(QStyle::SP_MediaStop), tr("Stop playback"), this); m_previousAction = new QAction( style()->standardIcon(QStyle::SP_MediaSkipBackward), tr("Previous Track"), this); m_nextAction = new QAction( style()->standardIcon(QStyle::SP_MediaSkipForward), tr("Next Track"), this); QAction* closeAction = new QAction( style()->standardIcon(QStyle::SP_TitleBarCloseButton), tr("Close"), this); QSplitter* splitter = new QSplitter(this); m_titleLabel = new QLabel(splitter); #ifdef HAVE_PHONON Phonon::MediaObject* mediaObject = m_player->mediaObject(); Phonon::SeekSlider* seekSlider = new Phonon::SeekSlider(splitter); seekSlider->setMediaObject(mediaObject); seekSlider->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); seekSlider->setIconVisible(false); Phonon::VolumeSlider* volumeSlider = new Phonon::VolumeSlider(this); volumeSlider->setAudioOutput(m_player->audioOutput()); volumeSlider->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); #else QMediaPlayer* mediaPlayer = m_player->mediaPlayer(); m_seekSlider = new QSlider(Qt::Horizontal, splitter); m_seekSlider->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); m_seekSlider->setRange(0, mediaPlayer->duration() / 1000); connect(m_seekSlider, SIGNAL(actionTriggered(int)), this, SLOT(seekAction(int))); m_muteAction = new QAction( style()->standardIcon(QStyle::SP_MediaVolume), tr("Mute"), this); m_volumeSlider = new QSlider(Qt::Horizontal, this); m_volumeSlider->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); m_volumeSlider->setRange(0, 100); int volume = mediaPlayer->volume(); m_volumeSlider->setValue(volume); setVolumeToolTip(volume); connect(m_volumeSlider, SIGNAL(actionTriggered(int)), this, SLOT(volumeAction(int))); #endif m_timeLcd = new QLCDNumber(this); m_timeLcd->setSegmentStyle(QLCDNumber::Flat); m_timeLcd->setFrameStyle(QFrame::NoFrame); m_timeLcd->display(zeroTime); addAction(m_playOrPauseAction); addAction(m_stopAction); addAction(m_previousAction); addAction(m_nextAction); addWidget(splitter); #ifdef HAVE_PHONON addWidget(volumeSlider); #else addAction(m_muteAction); addWidget(m_volumeSlider); #endif addWidget(m_timeLcd); addAction(closeAction); #ifdef HAVE_PHONON connect(mediaObject, SIGNAL(tick(qint64)), this, SLOT(tick(qint64))); connect(mediaObject, SIGNAL(stateChanged(Phonon::State,Phonon::State)), this, SLOT(stateChanged(Phonon::State))); #else connect(mediaPlayer, SIGNAL(positionChanged(qint64)), this, SLOT(tick(qint64))); connect(mediaPlayer, SIGNAL(stateChanged(QMediaPlayer::State)), this, SLOT(stateChanged(QMediaPlayer::State))); connect(mediaPlayer, SIGNAL(error(QMediaPlayer::Error)), this, SLOT(error(QMediaPlayer::Error))); connect(mediaPlayer, SIGNAL(durationChanged(qint64)), this, SLOT(durationChanged(qint64))); connect(mediaPlayer, SIGNAL(volumeChanged(int)), this, SLOT(setVolumeToolTip(int))); connect(m_muteAction, SIGNAL(triggered()), this, SLOT(toggleMute())); #endif connect(m_player, SIGNAL(trackChanged(QString,bool,bool)), this, SLOT(trackChanged(QString,bool,bool))); connect(m_playOrPauseAction, SIGNAL(triggered()), m_player, SLOT(playOrPause())); connect(m_stopAction, SIGNAL(triggered()), m_player, SLOT(stop())); connect(m_previousAction, SIGNAL(triggered()), m_player, SLOT(previous())); connect(m_nextAction, SIGNAL(triggered()), m_player, SLOT(next())); connect(closeAction, SIGNAL(triggered()), this, SLOT(close())); #if defined Q_OS_MAC && QT_VERSION >= 0x050000 setStyleSheet(QLatin1String("QToolButton { border: 0; }")); #endif } /** * Destructor. */ PlayToolBar::~PlayToolBar() { m_player->stop(); } /** * Stop sound when window is closed. */ void PlayToolBar::closeEvent(QCloseEvent*) { m_player->stop(); } /** * Update displayed time. * * @param msec time in ms */ void PlayToolBar::tick(qint64 msec) { int minutes = (msec / (60 * 1000)) % 60; int seconds = (msec / 1000) % 60; m_timeLcd->display(QString(QLatin1String("%1:%2")).arg(minutes, 2, 10, QLatin1Char(' ')) .arg(seconds, 2, 10, QLatin1Char('0'))); #ifndef HAVE_PHONON if (!m_seekSlider->isSliderDown()) { m_seekSlider->setValue(msec / 1000); } #endif } #ifdef HAVE_PHONON /** * Update button states when the Phonon state changed. * * @param newState new Phonon state */ void PlayToolBar::stateChanged(Phonon::State newState) { switch (newState) { case Phonon::ErrorState: m_playOrPauseAction->setEnabled(false); m_stopAction->setEnabled(false); emit errorMessage(m_player->mediaObject()->errorString()); break; case Phonon::PlayingState: m_playOrPauseAction->setEnabled(true); m_playOrPauseAction->setIcon(m_pauseIcon); m_stopAction->setEnabled(true); break; case Phonon::PausedState: m_playOrPauseAction->setEnabled(true); m_playOrPauseAction->setIcon(m_playIcon); m_stopAction->setEnabled(true); break; case Phonon::StoppedState: m_playOrPauseAction->setEnabled(true); m_playOrPauseAction->setIcon(m_playIcon); m_stopAction->setEnabled(false); m_timeLcd->display(zeroTime); break; default: m_playOrPauseAction->setEnabled(false); break; } } #else /** * Update button states when the Phonon state changed. * * @param newState new Phonon state */ void PlayToolBar::stateChanged(QMediaPlayer::State newState) { switch (newState) { case QMediaPlayer::PlayingState: m_playOrPauseAction->setEnabled(true); m_playOrPauseAction->setIcon(m_pauseIcon); m_stopAction->setEnabled(true); break; case QMediaPlayer::PausedState: m_playOrPauseAction->setEnabled(true); m_playOrPauseAction->setIcon(m_playIcon); m_stopAction->setEnabled(true); break; case QMediaPlayer::StoppedState: m_playOrPauseAction->setEnabled(true); m_playOrPauseAction->setIcon(m_playIcon); m_stopAction->setEnabled(false); m_timeLcd->display(zeroTime); break; default: m_playOrPauseAction->setEnabled(false); break; } } /** * Update states when a media player error occurs. * * @param err error */ void PlayToolBar::error(QMediaPlayer::Error err) { Q_UNUSED(err) m_playOrPauseAction->setEnabled(false); m_stopAction->setEnabled(false); emit errorMessage(m_player->mediaPlayer()->errorString()); } /** * Called when the duration changes. * @param duration duration in miliseconds */ void PlayToolBar::durationChanged(qint64 duration) { m_seekSlider->setMaximum(duration / 1000); } /** * Called when the volume changes. * @param volume current volume (0..100) */ void PlayToolBar::setVolumeToolTip(int volume) { m_volumeSlider->setToolTip(tr("Volume: %1%").arg(volume)); } /** * Set current position in track when slider position is changed. * @param action slider action */ void PlayToolBar::seekAction(int action) { Q_UNUSED(action); m_player->mediaPlayer()->setPosition(m_seekSlider->sliderPosition() * 1000); } /** * Set volume when slider position is changed. * @param action slider action */ void PlayToolBar::volumeAction(int action) { Q_UNUSED(action); m_player->mediaPlayer()->setVolume(m_volumeSlider->sliderPosition()); } /** * Toggle muted state. */ void PlayToolBar::toggleMute() { bool muted = !m_player->mediaPlayer()->isMuted(); m_player->mediaPlayer()->setMuted(muted); m_muteAction->setIcon(style()->standardIcon(muted ? QStyle::SP_MediaVolumeMuted : QStyle::SP_MediaVolume)); } #endif /** * Update display and button state when the current track is changed. * * @param filePath path of currently played audio file * @param hasPrevious true if a previous track is available * @param hasNext true if a next track is available */ void PlayToolBar::trackChanged(const QString& filePath, bool hasPrevious, bool hasNext) { #ifdef HAVE_PHONON m_playOrPauseAction->setIcon(m_pauseIcon); m_timeLcd->display(zeroTime); QFileInfo fi(filePath); m_titleLabel->setText(fi.fileName()); m_previousAction->setEnabled(hasPrevious); m_nextAction->setEnabled(hasNext); #else QFileInfo fi(filePath); m_titleLabel->setText(fi.fileName()); m_previousAction->setEnabled(hasPrevious); m_nextAction->setEnabled(hasNext); m_seekSlider->setMaximum(m_player->mediaPlayer()->duration() / 1000); #endif } #endif // HAVE_PHONON kid3-3.0.2/src/gui/widgets/playtoolbar.h000066400000000000000000000074431224603511300200750ustar00rootroot00000000000000/** * \file playtoolbar.h * Audio player toolbar. * * \b Project: Kid3 * \author Urs Fleisch * \date 24-Aug-2010 * * Copyright (C) 2010 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef PLAYTOOLBAR_H #define PLAYTOOLBAR_H #include #include "config.h" #if defined HAVE_PHONON || QT_VERSION >= 0x050000 #include #include class QAction; class QLCDNumber; class QLabel; class AudioPlayer; #ifdef HAVE_PHONON namespace Phonon { class SeekSlider; class VolumeSlider; } #include #else #include class QSlider; #endif /** * Audio player toolbar. */ class PlayToolBar : public QToolBar { Q_OBJECT public: /** * Constructor. * * @param player audio player * @param parent parent widget */ explicit PlayToolBar(AudioPlayer* player, QWidget* parent); /** * Destructor. */ virtual ~PlayToolBar(); signals: /** * Emitted when an error occurs. * Parameter: description of error */ void errorMessage(const QString& msg); private slots: /** * Update displayed time. * * @param msec time in ms */ void tick(qint64 msec); #ifdef HAVE_PHONON /** * Update button states when the Phonon state changed. * * @param newState new Phonon state */ void stateChanged(Phonon::State newState); #else /** * Update button states when the media player state changed. * * @param newState new Phonon state */ void stateChanged(QMediaPlayer::State newState); /** * Update states when a media player error occurs. * * @param err error */ void error(QMediaPlayer::Error err); /** * Called when the duration changes. * @param duration duration in miliseconds */ void durationChanged(qint64 duration); /** * Set the tool tip for the volume slider. * @param volume current volume (0..100) */ void setVolumeToolTip(int volume); /** * Set current position in track when slider position is changed. * @param action slider action */ void seekAction(int action); /** * Set volume when slider position is changed. * @param action slider action */ void volumeAction(int action); /** * Toggle muted state. */ void toggleMute(); #endif /** * Update display and button state when the current track is changed. * * @param filePath path of currently played audio file * @param hasPrevious true if a previous track is available * @param hasNext true if a next track is available */ void trackChanged(const QString& filePath, bool hasPrevious, bool hasNext); protected: /** * Stop sound when window is closed. */ virtual void closeEvent(QCloseEvent*); private: QIcon m_playIcon; QIcon m_pauseIcon; QAction* m_playOrPauseAction; QAction* m_stopAction; QAction* m_previousAction; QAction* m_nextAction; QLCDNumber* m_timeLcd; QLabel* m_titleLabel; AudioPlayer* m_player; #ifndef HAVE_PHONON QAction* m_muteAction; QSlider* m_seekSlider; QSlider* m_volumeSlider; #endif }; #else // HAVE_PHONON // Just to suppress moc "No relevant classes found" warning. class PlayToolBar : public QToolBar { Q_OBJECT }; #endif // HAVE_PHONON #endif // PLAYTOOLBAR_H kid3-3.0.2/src/gui/widgets/stringlistedit.cpp000066400000000000000000000041471224603511300211460ustar00rootroot00000000000000/** * \file stringlistedit.cpp * Widget to edit a string list. * * \b Project: Kid3 * \author Urs Fleisch * \date 14 Apr 2007 * * Copyright (C) 2007-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "stringlistedit.h" #include #include #include #include /** * Constructor. * * @param model item model, e.g. a QStringListModel * @param parent parent widget */ StringListEdit::StringListEdit(QAbstractItemModel* model, QWidget* parent) : AbstractListEdit(new QListView, model, parent) { setObjectName(QLatin1String("StringListEdit")); } /** * Destructor. */ StringListEdit::~StringListEdit() { } /** * Add a new item. */ void StringListEdit::addItem() { bool ok; QString txt = QInputDialog::getText( this, tr("Add Item"), QString(), QLineEdit::Normal, QString(), &ok); if (ok && !txt.isEmpty()) { QAbstractItemModel* model = getItemView()->model(); int row = model->rowCount(); model->insertRow(row); model->setData(model->index(row, 0), txt); } } /** * Edit the selected item. */ void StringListEdit::editItem() { QModelIndex index = getItemView()->currentIndex(); if (index.isValid()) { QAbstractItemModel* model = getItemView()->model(); bool ok; QString txt = QInputDialog::getText( this, tr("Edit Item"), QString(), QLineEdit::Normal, model->data(index, Qt::EditRole).toString(), &ok); if (ok && !txt.isEmpty()) { model->setData(index, txt); } } } kid3-3.0.2/src/gui/widgets/stringlistedit.h000066400000000000000000000026641224603511300206150ustar00rootroot00000000000000/** * \file stringlistedit.h * Widget to edit a string list. * * \b Project: Kid3 * \author Urs Fleisch * \date 14 Apr 2007 * * Copyright (C) 2007 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef STRINGLISTEDIT_H #define STRINGLISTEDIT_H #include "abstractlistedit.h" /** * Widget to edit a string list. */ class StringListEdit : public AbstractListEdit { Q_OBJECT public: /** * Constructor. * * @param model item model, e.g. a QStringListModel * @param parent parent widget */ explicit StringListEdit(QAbstractItemModel* model, QWidget* parent = 0); /** * Destructor. */ virtual ~StringListEdit(); public slots: /** * Add a new item. */ virtual void addItem(); /** * Edit the selected item. */ virtual void editItem(); }; #endif // STRINGLISTEDIT_H kid3-3.0.2/src/plugins/000077500000000000000000000000001224603511300146135ustar00rootroot00000000000000kid3-3.0.2/src/plugins/CMakeLists.txt000066400000000000000000000031261224603511300173550ustar00rootroot00000000000000project(kid3_plugins) # Install a library _target as a plugin with _name. macro (INSTALL_KID3_PLUGIN _target _name) if (BUILD_SHARED_LIBS) install(TARGETS ${_target} LIBRARY DESTINATION ${WITH_PLUGINSDIR} RUNTIME DESTINATION ${WITH_PLUGINSDIR}) set_target_properties( ${_target} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${kid3_plugins_BINARY_DIR} LIBRARY_OUTPUT_DIRECTORY ${kid3_plugins_BINARY_DIR} ) else (BUILD_SHARED_LIBS) set_property( TARGET ${_target} APPEND PROPERTY COMPILE_DEFINITIONS QT_STATICPLUGIN ) set(PLUGIN_LIBRARIES ${PLUGIN_LIBRARIES} ${_target} PARENT_SCOPE) set(PLUGIN_NAMES ${PLUGIN_NAMES} ${_name}Plugin PARENT_SCOPE) endif (BUILD_SHARED_LIBS) endmacro (INSTALL_KID3_PLUGIN _target _name) add_definitions(-DKID3_BUILD_PLUGIN_LIB) if (NOT BUILD_SHARED_LIBS) add_definitions(${QT_EXECUTABLE_COMPILE_FLAGS}) endif (NOT BUILD_SHARED_LIBS) include_directories(../core/utils ../core/model ../core/tags ../core/config ../core/import) add_subdirectory(amazonimport) add_subdirectory(discogsimport) add_subdirectory(freedbimport) add_subdirectory(musicbrainzimport) add_subdirectory(acoustidimport) add_subdirectory(id3libmetadata) add_subdirectory(taglibmetadata) add_subdirectory(oggflacmetadata) add_subdirectory(mp4v2metadata) set(PLUGIN_LIBRARIES ${PLUGIN_LIBRARIES} PARENT_SCOPE) set(CFG_IMPORT_PLUGIN_CALLS) foreach(_pluginName ${PLUGIN_NAMES}) set(CFG_IMPORT_PLUGIN_CALLS "${CFG_IMPORT_PLUGIN_CALLS}Q_IMPORT_PLUGIN(${_pluginName})\n") endforeach() configure_file(importplugins.h.cmake ${kid3_BINARY_DIR}/importplugins.h) kid3-3.0.2/src/plugins/acoustidimport/000077500000000000000000000000001224603511300176615ustar00rootroot00000000000000kid3-3.0.2/src/plugins/acoustidimport/CMakeLists.txt000066400000000000000000000137071224603511300224310ustar00rootroot00000000000000if(WITH_CHROMAPRINT) macro(find_gstreamer) find_package(PkgConfig) set(WITH_GSTREAMER_VERSION "" CACHE STRING "GStreamer version to use or list of versions to check") if (WITH_GSTREAMER_VERSION) set(_checkedGstVersions ${WITH_GSTREAMER_VERSION}) else (WITH_GSTREAMER_VERSION) # Prefer version 0.10 as long as Phonon and QtMultimedia # are using it. set(_checkedGstVersions "0.10;1.0") endif (WITH_GSTREAMER_VERSION) foreach (_gstVersion ${_checkedGstVersions}) pkg_check_modules(GSTREAMER gstreamer-${_gstVersion}) if (GSTREAMER_FOUND) if (NOT WITH_GSTREAMER_VERSION) message(STATUS "GStreamer ${_gstVersion} selected, " "use WITH_GSTREAMER_VERSION for another version.") endif (NOT WITH_GSTREAMER_VERSION) if (_gstVersion STREQUAL "1.0" AND WITH_PHONON) message(WARNING "If Phonon uses a different GStreamer version " "it will probably hang when playing.") endif () break() endif (GSTREAMER_FOUND) endforeach (_gstVersion ${WITH_GSTREAMER_VERSION}) endmacro(find_gstreamer) set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules) find_package(Chromaprint REQUIRED) if(CHROMAPRINT_INCLUDE_DIR AND CHROMAPRINT_LIBRARIES) set(HAVE_CHROMAPRINT 1) endif(CHROMAPRINT_INCLUDE_DIR AND CHROMAPRINT_LIBRARIES) set(CHROMAPRINT_INCLUDES ${CHROMAPRINT_INCLUDE_DIR}) set(CHROMAPRINT_LIBS ${CHROMAPRINT_LIBRARIES}) if(WITH_FFMPEG) find_package(FFmpeg REQUIRED) elseif(WITH_GSTREAMER) find_gstreamer() if (NOT GSTREAMER_FOUND) message(FATAL_ERROR "Could not find GStreamer") endif (NOT GSTREAMER_FOUND) elseif(HAVE_QT5 AND WITH_QAUDIODECODER) message(STATUS "QAudioDecoder selected for decoding.") else(WITH_FFMPEG) # Neither FFmpeg nor GStreamer is forced, select FFmpeg if Chromaprint # depends on it or if on Windows or Mac OS X. include(GetPrerequisites) get_prerequisites(${CHROMAPRINT_LIBRARIES} _chromaprintDeps 0 0 "" "") if(_chromaprintDeps MATCHES "libavcodec" OR APPLE OR WIN32) message(STATUS "FFmpeg selected for decoding, use WITH_GSTREAMER=ON to use GStreamer instead.") find_package(FFmpeg REQUIRED) else() message(STATUS "GStreamer selected for decoding, use WITH_FFMPEG=ON to use FFmpeg instead.") find_gstreamer() endif() endif(WITH_FFMPEG) if(GSTREAMER_FOUND) set(HAVE_GSTREAMER 1) set(CHROMAPRINT_CFLAGS ${GSTREAMER_CFLAGS_OTHER}) set(CHROMAPRINT_INCLUDES ${CHROMAPRINT_INCLUDES} ${GSTREAMER_INCLUDE_DIRS}) set(CHROMAPRINT_LIBS ${CHROMAPRINT_LIBS} ${GSTREAMER_LDFLAGS}) elseif(FFMPEG_LIBAVFORMAT_FOUND AND FFMPEG_LIBAVCODEC_FOUND AND FFMPEG_LIBAVUTIL_FOUND) if (FFMPEG_LIBAVRESAMPLE_FOUND) set(HAVE_AVRESAMPLE 1) set(CHROMAPRINT_INCLUDES ${CHROMAPRINT_INCLUDES} ${FFMPEG_LIBAVRESAMPLE_INCLUDE_DIRS}) set(CHROMAPRINT_LIBS ${CHROMAPRINT_LIBS} ${FFMPEG_LIBAVRESAMPLE_LIBRARIES}) else (FFMPEG_LIBAVRESAMPLE_FOUND) include(CheckFunctionExists) set(_CMAKE_REQUIRED_LIBRARIES_TMP ${CMAKE_REQUIRED_LIBRARIES}) set(CMAKE_REQUIRED_LIBRARIES ${FFMPEG_LIBAVFORMAT_LIBRARIES} ${FFMPEG_LIBAVCODEC_LIBRARIES} ${FFMPEG_LIBAVUTIL_LIBRARIES}) check_function_exists(av_audio_convert HAVE_AV_AUDIO_CONVERT) set(CMAKE_REQUIRED_LIBRARIES ${_CMAKE_REQUIRED_LIBRARIES_TMP}) endif (FFMPEG_LIBAVRESAMPLE_FOUND) set(HAVE_FFMPEG 1) set(CHROMAPRINT_INCLUDES ${CHROMAPRINT_INCLUDES} ${FFMPEG_LIBAVCODEC_INCLUDE_DIRS} ${FFMPEG_LIBAVFORMAT_INCLUDE_DIRS} ${FFMPEG_LIBAVUTIL_INCLUDE_DIRS}) set(CHROMAPRINT_LIBS ${CHROMAPRINT_LIBS} ${FFMPEG_LIBAVFORMAT_LIBRARIES} ${FFMPEG_LIBAVCODEC_LIBRARIES} ${FFMPEG_LIBAVUTIL_LIBRARIES}) if(WIN32 OR APPLE) set(CHROMAPRINT_LIBS ${CHROMAPRINT_LIBS} ${ZLIB_LIBRARIES}) endif(WIN32 OR APPLE) elseif(WITH_CHROMAPRINT_FFMPEG) find_package(FFmpeg REQUIRED) set(CHROMAPRINT_LIBS ${CHROMAPRINT_LIBS} ${FFMPEG_LIBAVCODEC_LIBRARIES} ${FFMPEG_LIBAVUTIL_LIBRARIES}) endif(GSTREAMER_FOUND) if (APPLE) find_library(ACCELERATE_LIBRARIES Accelerate) set(CHROMAPRINT_LIBS ${CHROMAPRINT_LIBS} ${ACCELERATE_LIBRARIES}) endif (APPLE) configure_file(acoustidconfig.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/acoustidconfig.h) add_definitions(${CHROMAPRINT_CFLAGS}) include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CHROMAPRINT_INCLUDES}) if (WIN32 AND HAVE_CHROMAPRINT) add_definitions(-DCHROMAPRINT_NODLL) endif (WIN32 AND HAVE_CHROMAPRINT) set(plugin_SRCS abstractfingerprintdecoder.cpp fingerprintcalculator.cpp musicbrainzclient.cpp acoustidimportplugin.cpp ) set(plugin_MOC_HDRS abstractfingerprintdecoder.h fingerprintcalculator.h musicbrainzclient.h acoustidimportplugin.h ) if (HAVE_GSTREAMER) set(plugin_SRCS ${plugin_SRCS} gstfingerprintdecoder.cpp) elseif (HAVE_FFMPEG) set(plugin_SRCS ${plugin_SRCS} ffmpegfingerprintdecoder.cpp) elseif (HAVE_QT5) set(plugin_SRCS ${plugin_SRCS} qtfingerprintdecoder.cpp) set(plugin_MOC_HDRS ${plugin_MOC_HDRS} qtfingerprintdecoder.h) endif () set(plugin_NAME AcoustidImport) if (WITH_GCC_PCH) add_definitions(${GCC_PCH_COMPILE_FLAGS}) endif (WITH_GCC_PCH) string(TOLOWER ${plugin_NAME} plugin_TARGET) qt4_wrap_cpp(plugin_GEN_MOC_SRCS ${plugin_MOC_HDRS}) add_library(${plugin_TARGET} ${plugin_SRCS} ${plugin_GEN_MOC_SRCS}) target_link_libraries(${plugin_TARGET} kid3-core ${BASIC_LIBRARIES} ${CHROMAPRINT_LIBS}) if (APPLE AND CMAKE_EXE_LINKER_FLAGS MATCHES "-arch i386") # To suppress linker error # ld: illegal text-relocation to non_lazy_ptr in ../libavformat.a # (allformats.o) from _av_register_all in ../libavformat.a(allformats.o) # for architecture i386 SET_TARGET_PROPERTIES(${plugin_TARGET} PROPERTIES LINK_FLAGS -Wl,-read_only_relocs,suppress) endif (APPLE AND CMAKE_EXE_LINKER_FLAGS MATCHES "-arch i386") INSTALL_KID3_PLUGIN(${plugin_TARGET} ${plugin_NAME}) endif(WITH_CHROMAPRINT) kid3-3.0.2/src/plugins/acoustidimport/abstractfingerprintdecoder.cpp000066400000000000000000000031351224603511300257700ustar00rootroot00000000000000/** * \file abstractfingerprintdecoder.h * Abstract base class for Chromaprint fingerprint decoder. * * \b Project: Kid3 * \author Urs Fleisch * \date 15 Feb 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "abstractfingerprintdecoder.h" /** * Constructor. * @param parent parent object */ AbstractFingerprintDecoder::AbstractFingerprintDecoder(QObject* parent) : QObject(parent), m_stopped(false) { } /** * Destructor. */ AbstractFingerprintDecoder::~AbstractFingerprintDecoder() { } /** * Run decoder on audio file. */ void AbstractFingerprintDecoder::start(const QString&) { m_stopped = false; } /** * Stop decoder. * Can be used to stop the decoder when an error is found after * getting bufferReady() data. */ void AbstractFingerprintDecoder::stop() { m_stopped = true; } /** * Check if decoding has been stopped. * @return true if stopped. */ bool AbstractFingerprintDecoder::isStopped() const { return m_stopped; } kid3-3.0.2/src/plugins/acoustidimport/abstractfingerprintdecoder.h000066400000000000000000000053431224603511300254400ustar00rootroot00000000000000/** * \file abstractfingerprintdecoder.h * Abstract base class for Chromaprint fingerprint decoder. * * \b Project: Kid3 * \author Urs Fleisch * \date 15 Feb 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef ABSTRACTFINGERPRINTDECODER_H #define ABSTRACTFINGERPRINTDECODER_H #include /** * Abstract base class for Chromaprint fingerprint decoder. */ class AbstractFingerprintDecoder : public QObject { Q_OBJECT public: /** * Constructor. * @param parent parent object */ explicit AbstractFingerprintDecoder(QObject* parent = 0); /** * Destructor. */ virtual ~AbstractFingerprintDecoder() = 0; /** * Run decoder on audio file. * @param filePath path to audio file */ virtual void start(const QString& filePath); /** * Stop decoder. * Can be used to stop the decoder when an error is found after * getting bufferReady() data. */ virtual void stop(); /** * Check if decoding has been stopped. * @return true if stopped. */ virtual bool isStopped() const; /** * Create concrete fingerprint decoder. * @param parent parent object * @return fingerprint decoder instance. * @remarks This static method will be implemented by the concrete * fingerprint decoder which is used. */ static AbstractFingerprintDecoder* createFingerprintDecoder(QObject* parent); signals: /** * Emitted when decoding starts. * @param sampleRate sample rate of the audio stream (in Hz) * @param channelCount numbers of channels in the audio stream (1 or 2) */ void started(int sampleRate, int channelCount); /** * Emitted when decoded data is available. * @param data 16-bit signed integers in native byte-order */ void bufferReady(QByteArray data); /** * Emitted when an error occurs. * @param code error code, enum FingerprintCalculator::Error */ void error(int code); /** * Emitted when decoding finished successfully. * @param duration duration of stream in seconds */ void finished(int duration); private: bool m_stopped; }; #endif // ABSTRACTFINGERPRINTDECODER_H kid3-3.0.2/src/plugins/acoustidimport/acoustidconfig.h.cmake000066400000000000000000000005061224603511300241130ustar00rootroot00000000000000/* acoustidconfig.h. Generated by cmake from acoustidconfig.h.cmake */ #ifndef ACOUSTIDCONFIG_H #define ACOUSTIDCONFIG_H /* Define if avresample is available */ #cmakedefine HAVE_AVRESAMPLE ${HAVE_AVRESAMPLE} /* Define if av_audio_convert is available */ #cmakedefine HAVE_AV_AUDIO_CONVERT ${HAVE_AV_AUDIO_CONVERT} #endif kid3-3.0.2/src/plugins/acoustidimport/acoustidimportplugin.cpp000066400000000000000000000040411224603511300246510ustar00rootroot00000000000000/** * \file acoustidimportplugin.cpp * AcoustID import plugin. * * \b Project: Kid3 * \author Urs Fleisch * \date 21 Jul 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "acoustidimportplugin.h" #include "musicbrainzclient.h" #if QT_VERSION < 0x050000 Q_EXPORT_PLUGIN2(AcoustidImportPlugin, AcoustidImportPlugin) #endif static const QLatin1String IMPORTER_NAME("AcoustidImport"); /*! * Constructor. * @param parent parent object */ AcoustidImportPlugin::AcoustidImportPlugin(QObject* parent) : QObject(parent) { setObjectName(QLatin1String("AcoustidImport")); } /** * Destructor. */ AcoustidImportPlugin::~AcoustidImportPlugin() { } /** * Get keys of available server importers. * @return list of keys. */ QStringList AcoustidImportPlugin::serverTrackImporterKeys() const { return QStringList() << IMPORTER_NAME; } /** * Create server importer. * @param key server importer key * @param netMgr network access manager * @param trackDataModel track data to be filled with imported values * @return server importer instance, 0 if key unknown. * @remarks The caller takes ownership of the returned instance. */ ServerTrackImporter* AcoustidImportPlugin::createServerTrackImporter( const QString& key, QNetworkAccessManager* netMgr, TrackDataModel* trackDataModel) { if (key == IMPORTER_NAME) { return new MusicBrainzClient(netMgr, trackDataModel); } return 0; } kid3-3.0.2/src/plugins/acoustidimport/acoustidimportplugin.h000066400000000000000000000040711224603511300243210ustar00rootroot00000000000000/** * \file acoustidimportplugin.h * AcoustID import plugin. * * \b Project: Kid3 * \author Urs Fleisch * \date 21 Jul 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef ACOUSTIDIMPORTPLUGIN_H #define ACOUSTIDIMPORTPLUGIN_H #include #include "iservertrackimporterfactory.h" /** * AcoustID import plugin. */ class KID3_PLUGIN_EXPORT AcoustidImportPlugin : public QObject, public IServerTrackImporterFactory { Q_OBJECT #if QT_VERSION >= 0x050000 Q_PLUGIN_METADATA(IID "net.sourceforge.kid3.IServerTrackImporterFactory") #endif Q_INTERFACES(IServerTrackImporterFactory) public: /*! * Constructor. * @param parent parent object */ explicit AcoustidImportPlugin(QObject* parent = 0); /** * Destructor. */ virtual ~AcoustidImportPlugin(); /** * Get keys of available server importers. * @return list of keys. */ virtual QStringList serverTrackImporterKeys() const; /** * Create server importer. * @param key server importer key * @param netMgr network access manager * @param trackDataModel track data to be filled with imported values * @return server importer instance, 0 if key unknown. * @remarks The caller takes ownership of the returned instance. */ virtual ServerTrackImporter* createServerTrackImporter( const QString& key, QNetworkAccessManager* netMgr, TrackDataModel* trackDataModel); }; #endif // ACOUSTIDIMPORTPLUGIN_H kid3-3.0.2/src/plugins/acoustidimport/cmake/000077500000000000000000000000001224603511300207415ustar00rootroot00000000000000kid3-3.0.2/src/plugins/acoustidimport/cmake/modules/000077500000000000000000000000001224603511300224115ustar00rootroot00000000000000kid3-3.0.2/src/plugins/acoustidimport/cmake/modules/FindChromaprint.cmake000066400000000000000000000010011224603511300264720ustar00rootroot00000000000000INCLUDE(FindPackageHandleStandardArgs) FIND_PACKAGE(PkgConfig) PKG_CHECK_MODULES(PKG_LIBCHROMAPRINT libchromaprint) FIND_PATH(CHROMAPRINT_INCLUDE_DIR chromaprint.h ${PKG_LIBCHROMAPRINT_INCLUDE_DIRS} /usr/include /usr/local/include ) FIND_LIBRARY(CHROMAPRINT_LIBRARIES NAMES chromaprint chromaprint.dll PATHS ${PKG_LIBCHROMAPRINT_LIBRARY_DIRS} /usr/lib /usr/local/lib ) FIND_PACKAGE_HANDLE_STANDARD_ARGS(Chromaprint DEFAULT_MSG CHROMAPRINT_LIBRARIES CHROMAPRINT_INCLUDE_DIR) kid3-3.0.2/src/plugins/acoustidimport/cmake/modules/FindFFmpeg.cmake000066400000000000000000000077731224603511300253760ustar00rootroot00000000000000# Locate ffmpeg # This module defines # FFMPEG_LIBRARIES # FFMPEG_FOUND, if false, do not try to link to ffmpeg # FFMPEG_INCLUDE_DIR, where to find the headers # # $FFMPEG_DIR is an environment variable that would # correspond to the ./configure --prefix=$FFMPEG_DIR # # Created by Robert Osfield. # Modified by Lukas Lalinsky. #In ffmpeg code, old version use "#include " and newer use "#include " #In OSG ffmpeg plugin, we use "#include " for compatibility with old version of ffmpeg #We have to search the path which contain the header.h (useful for old version) #and search the path which contain the libname/header.h (useful for new version) #Then we need to include ${FFMPEG_libname_INCLUDE_DIRS} (in old version case, use by ffmpeg header and osg plugin code) # (in new version case, use by ffmpeg header) #and ${FFMPEG_libname_INCLUDE_DIRS/libname} (in new version case, use by osg plugin code) # Macro to find header and lib directories # example: FFMPEG_FIND(AVFORMAT avformat avformat.h) MACRO(FFMPEG_FIND varname shortname headername) FIND_PATH(FFMPEG_${varname}_INCLUDE_DIRS lib${shortname}/${headername} PATHS ${FFMPEG_ROOT}/include $ENV{FFMPEG_DIR}/include ~/Library/Frameworks /Library/Frameworks /usr/local/include /usr/include/ /sw/include # Fink /opt/local/include # DarwinPorts /opt/csw/include # Blastwave /opt/include /usr/freeware/include NO_DEFAULT_PATH PATH_SUFFIXES ffmpeg DOC "Location of FFMPEG Headers" ) FIND_PATH(FFMPEG_${varname}_INCLUDE_DIRS lib${shortname}/${headername} PATH_SUFFIXES ffmpeg DOC "Location of FFMPEG Headers" ) FIND_LIBRARY(FFMPEG_${varname}_LIBRARIES NAMES ${shortname} PATHS ${FFMPEG_ROOT}/lib $ENV{FFMPEG_DIR}/lib ~/Library/Frameworks /Library/Frameworks /usr/local/lib /usr/local/lib64 /usr/lib /usr/lib64 /sw/lib /opt/local/lib /opt/csw/lib /opt/lib /usr/freeware/lib64 NO_DEFAULT_PATH DOC "Location of FFMPEG Libraries" ) FIND_LIBRARY(FFMPEG_${varname}_LIBRARIES NAMES ${shortname} DOC "Location of FFMPEG Libraries" ) IF (FFMPEG_${varname}_LIBRARIES AND FFMPEG_${varname}_INCLUDE_DIRS) SET(FFMPEG_${varname}_FOUND 1) ENDIF(FFMPEG_${varname}_LIBRARIES AND FFMPEG_${varname}_INCLUDE_DIRS) ENDMACRO(FFMPEG_FIND) SET(FFMPEG_ROOT "$ENV{FFMPEG_DIR}" CACHE PATH "Location of FFMPEG") # find stdint.h FIND_PATH(FFMPEG_STDINT_INCLUDE_DIR stdint.h PATHS ${FFMPEG_ROOT}/include $ENV{FFMPEG_DIR}/include ~/Library/Frameworks /Library/Frameworks /usr/local/include /usr/include /sw/include # Fink /opt/local/include # DarwinPorts /opt/csw/include # Blastwave /opt/include /usr/freeware/include PATH_SUFFIXES ffmpeg DOC "Location of FFMPEG stdint.h Header" ) FFMPEG_FIND(LIBAVFORMAT avformat avformat.h) FFMPEG_FIND(LIBAVDEVICE avdevice avdevice.h) FFMPEG_FIND(LIBAVCODEC avcodec avcodec.h) FFMPEG_FIND(LIBAVCODEC_FFT avcodec avfft.h) FFMPEG_FIND(LIBAVUTIL avutil avutil.h) FFMPEG_FIND(LIBSWSCALE swscale swscale.h) # not sure about the header to look for here. FFMPEG_FIND(LIBAVRESAMPLE avresample avresample.h) SET(FFMPEG_FOUND "NO") # Note we don't check FFMPEG_LIBSWSCALE_FOUND here, it's optional. IF (FFMPEG_LIBAVFORMAT_FOUND AND FFMPEG_LIBAVDEVICE_FOUND AND FFMPEG_LIBAVCODEC_FOUND AND FFMPEG_LIBAVUTIL_FOUND AND FFMPEG_STDINT_INCLUDE_DIR) SET(FFMPEG_FOUND "YES") SET(FFMPEG_INCLUDE_DIRS ${FFMPEG_LIBAVFORMAT_INCLUDE_DIRS}) SET(FFMPEG_LIBRARY_DIRS ${FFMPEG_LIBAVFORMAT_LIBRARY_DIRS}) # Note we don't add FFMPEG_LIBSWSCALE_LIBRARIES here, it will be added if found later. SET(FFMPEG_LIBRARIES ${FFMPEG_LIBAVFORMAT_LIBRARIES} ${FFMPEG_LIBAVDEVICE_LIBRARIES} ${FFMPEG_LIBAVCODEC_LIBRARIES} ${FFMPEG_LIBAVUTIL_LIBRARIES}) ELSE () # MESSAGE(STATUS "Could not find FFMPEG") ENDIF() kid3-3.0.2/src/plugins/acoustidimport/ffmpegfingerprintdecoder.cpp000066400000000000000000000311121224603511300254250ustar00rootroot00000000000000/** * \file ffmpegfingerprintdecoder.cpp * Chromaprint fingerprint decoder using FFmpeg. * * \b Project: Kid3 * \author Urs Fleisch * \date 15 Feb 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #define __STDC_CONSTANT_MACROS #include "ffmpegfingerprintdecoder.h" #include "acoustidconfig.h" #include #include extern "C" { #include #include #ifdef HAVE_AVRESAMPLE #include #include #elif defined HAVE_AV_AUDIO_CONVERT #include #include /* * Declarations taken from "ffmpeg/audioconvert.h", "ffmpeg/samplefmt.h". */ //! @cond struct AVAudioConvert; typedef struct AVAudioConvert AVAudioConvert; AVAudioConvert *av_audio_convert_alloc(enum AVSampleFormat out_fmt, int out_channels, enum AVSampleFormat in_fmt, int in_channels, const float *matrix, int flags); void av_audio_convert_free(AVAudioConvert *ctx); int av_audio_convert(AVAudioConvert *ctx, void * const out[6], const int out_stride[6], const void * const in[6], const int in_stride[6], int len); //! @endcond #endif } #include #include "fingerprintcalculator.h" #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(52, 94, 1) #define AV_SAMPLE_FMT_S16 SAMPLE_FMT_S16 #define AVMEDIA_TYPE_AUDIO CODEC_TYPE_AUDIO #endif #ifdef AVCODEC_MAX_AUDIO_FRAME_SIZE #define MAX_AUDIO_FRAME_SIZE AVCODEC_MAX_AUDIO_FRAME_SIZE #else #define MAX_AUDIO_FRAME_SIZE 192000 // 1 second of 48khz 32bit audio #endif namespace { const int BUFFER_SIZE = MAX_AUDIO_FRAME_SIZE * 2; /* * The following classes are used to benefit from the C++ * "Resource Acquisition Is Initialization" (RAII) idiom when dealing with * AV resources. */ class Packet { public: explicit Packet(AVPacket* packet) : m_ptr(packet) { } AVPacket* data() { return m_ptr; } ~Packet() { if (m_ptr && m_ptr->data) { ::av_free_packet(m_ptr); } } private: AVPacket* m_ptr; }; class Format { public: Format(const char* fileName) : m_ptr(0), m_hasError(false) { if ( #if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 2, 0) ::av_open_input_file(&m_ptr, fileName, 0, 0, 0) != 0 #else ::avformat_open_input(&m_ptr, fileName, 0, 0) != 0 #endif || #if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 5, 0) ::av_find_stream_info(m_ptr) < 0 #else ::avformat_find_stream_info(m_ptr, 0) < 0 #endif ) m_hasError = true; } ~Format() { if (m_ptr) #if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 21, 0) ::av_close_input_file(m_ptr); #else ::avformat_close_input(&m_ptr); #endif } bool hasError() const { return m_hasError; } unsigned int numStreams() const { return m_ptr->nb_streams; } AVStream** streams() const { return m_ptr->streams; } bool readFrame(Packet& packet) { return ::av_read_frame(m_ptr, packet.data()) >= 0; } private: AVFormatContext* m_ptr; bool m_hasError; }; class Codec { public: explicit Codec(AVCodecContext* ptr = 0) : m_ptr(ptr), m_opened(false) { } ~Codec() { if (m_opened) ::avcodec_close(m_ptr); } bool isNull() const { return m_ptr == 0; } void assign(AVCodecContext* ptr) { m_ptr = ptr; } bool codecTypeIsAudio() const { return m_ptr && m_ptr->codec_type == AVMEDIA_TYPE_AUDIO; } bool open() { AVCodec* codec; m_opened = false; if (m_ptr && (codec = ::avcodec_find_decoder(m_ptr->codec_id)) != 0) { m_ptr->request_sample_fmt = AV_SAMPLE_FMT_S16; m_opened = #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(53, 5, 0) ::avcodec_open(m_ptr, codec) >= 0 #else ::avcodec_open2(m_ptr, codec, 0) >= 0 #endif ; } return m_opened; } int channels() const { return m_ptr->channels; } AVSampleFormat sampleFormat() const { return m_ptr->sample_fmt; } int sampleRate() const { return m_ptr->sample_rate; } uint64_t channelLayout() const { return m_ptr->channel_layout; } int decode(int16_t* samples, int* frameSize, AVPacket* pkt) { #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(52, 23, 0) return ::avcodec_decode_audio2(m_ptr, samples, frameSize, pkt->data, pkt->size); #elif LIBAVCODEC_VERSION_INT < AV_VERSION_INT(53, 25, 0) return ::avcodec_decode_audio3(m_ptr, samples, frameSize, pkt); #else AVFrame frame; ::memset(&frame, 0, sizeof(frame)); ::avcodec_get_frame_defaults(&frame); int decoded = 0; int len = ::avcodec_decode_audio4(m_ptr, &frame, &decoded, pkt); if (len >= 0 && decoded) { int planar = ::av_sample_fmt_is_planar(m_ptr->sample_fmt); int planeSize; int dataSize = ::av_samples_get_buffer_size(&planeSize, m_ptr->channels, frame.nb_samples, m_ptr->sample_fmt, 1); if (*frameSize < dataSize) return -1; ::memcpy(samples, frame.extended_data[0], planeSize); if (planar && m_ptr->channels > 1) { uint8_t* out = reinterpret_cast(samples) + planeSize; for (int ch = 1; ch < m_ptr->channels; ++ch) { ::memcpy(out, frame.extended_data[ch], planeSize); out += planeSize; } } *frameSize = dataSize; } else { *frameSize = 0; } return len; #endif } private: AVCodecContext* m_ptr; bool m_opened; }; #ifdef HAVE_AVRESAMPLE class Converter { public: Converter() : m_ptr(0), m_isOpen(false) {} ~Converter() { if (m_ptr) { if (m_isOpen) { ::avresample_close(m_ptr); } ::avresample_close(m_ptr); } } bool createForCodec(const Codec& codecCtx) { if ((m_ptr = ::avresample_alloc_context()) != 0) { ::av_opt_set_int(m_ptr, "in_channel_layout", codecCtx.channelLayout(), 0); ::av_opt_set_int(m_ptr, "in_sample_fmt", codecCtx.sampleFormat(), 0); ::av_opt_set_int(m_ptr, "in_sample_rate", codecCtx.sampleRate(), 0); ::av_opt_set_int(m_ptr, "out_channel_layout", AV_CH_LAYOUT_STEREO, 0); ::av_opt_set_int(m_ptr, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0); ::av_opt_set_int(m_ptr, "out_sample_rate", 44100, 0); m_isOpen = ::avresample_open(m_ptr) >= 0; return m_isOpen; } return false; } int16_t* convert(const Codec& codecCtx, int16_t* buffer1, int16_t* buffer2, int& bufferSize) { if (m_ptr) { int bytesPerSample = ::av_get_bytes_per_sample(codecCtx.sampleFormat()); int numSamplesIn = bufferSize / bytesPerSample; int linesizeIn; ::av_samples_get_buffer_size(&linesizeIn, codecCtx.channels(), numSamplesIn / codecCtx.channels(), codecCtx.sampleFormat(), 0); #if LIBAVRESAMPLE_VERSION_INT < AV_VERSION_INT(1, 0, 0) int numSamplesOut = ::avresample_convert( m_ptr, reinterpret_cast(&buffer2), 0, BUFFER_SIZE, reinterpret_cast(&buffer1), linesizeIn, numSamplesIn); #else int numSamplesOut = ::avresample_convert( m_ptr, reinterpret_cast(&buffer2), 0, BUFFER_SIZE, reinterpret_cast(&buffer1), linesizeIn, numSamplesIn); #endif if (numSamplesOut < 0) { return 0; } bufferSize = numSamplesOut * 2; return buffer2; } else { return buffer1; } } private: AVAudioResampleContext* m_ptr; bool m_isOpen; }; #elif defined HAVE_AV_AUDIO_CONVERT class Converter { public: Converter() : m_ptr(0) {} ~Converter() { if (m_ptr) ::av_audio_convert_free(m_ptr); } bool createForCodec(const Codec& codecCtx) { m_ptr = ::av_audio_convert_alloc(AV_SAMPLE_FMT_S16, codecCtx.channels(), codecCtx.sampleFormat(), codecCtx.channels(), 0, 0); return m_ptr != 0; } int16_t* convert(const Codec& codecCtx, int16_t* buffer1, int16_t* buffer2, int& bufferSize) { if (m_ptr) { const void *ibuf[6] = { buffer1 }; void *obuf[6] = { buffer2 }; #if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(51, 4, 0) int istride[6] = { ::av_get_bits_per_sample_format(codecCtx.sampleFormat()) / 8 }; #else int istride[6] = { ::av_get_bytes_per_sample(codecCtx.sampleFormat()) }; #endif int ostride[6] = { 2 }; int len = bufferSize / istride[0]; if (::av_audio_convert(m_ptr, obuf, ostride, ibuf, istride, len) < 0) { return 0; } bufferSize = len * ostride[0]; return buffer2; } else { return buffer1; } } private: AVAudioConvert* m_ptr; }; #else class Converter { public: bool createForCodec(const Codec&) { return false; } int16_t* convert(const Codec&, int16_t* buffer1, int16_t*, int&) { return buffer1; } }; #endif } FFmpegFingerprintDecoder::FFmpegFingerprintDecoder(QObject* parent) : AbstractFingerprintDecoder(parent) { ::av_register_all(); ::av_log_set_level(AV_LOG_ERROR); m_buffer1 = reinterpret_cast(::av_malloc(BUFFER_SIZE + 16)); m_buffer2 = reinterpret_cast(::av_malloc(BUFFER_SIZE + 16)); } FFmpegFingerprintDecoder::~FFmpegFingerprintDecoder() { ::av_free(m_buffer1); ::av_free(m_buffer2); } void FFmpegFingerprintDecoder::start(const QString& filePath) { AbstractFingerprintDecoder::start(filePath); /* * The code here is based on fpcalc.c from chromaprint-0.6/examples. */ FingerprintCalculator::Error err = FingerprintCalculator::Ok; int duration = 0; QByteArray fileName(QFile::encodeName(filePath)); Format format(fileName.constData()); if (format.hasError()) { err = FingerprintCalculator::NoStreamFound; emit error(err); return; } AVStream* stream = 0; Codec codec; for (unsigned i = 0; i < format.numStreams(); ++i) { codec.assign(format.streams()[i]->codec); if (codec.codecTypeIsAudio()) { stream = format.streams()[i]; break; } } if (!stream) { err = FingerprintCalculator::NoStreamFound; emit error(err); return; } if (!codec.open() || codec.channels() <= 0) { err = FingerprintCalculator::NoCodecFound; emit error(err); return; } Converter converter; if (codec.sampleFormat() != AV_SAMPLE_FMT_S16) { if (!converter.createForCodec(codec)) { err = FingerprintCalculator::NoConverterFound; emit error(err); return; } } duration = stream->time_base.num * stream->duration / stream->time_base.den; AVPacket packet, packetTemp; ::av_init_packet(&packet); ::av_init_packet(&packetTemp); const int MAX_LENGTH = 120; int remaining = MAX_LENGTH * codec.channels() * codec.sampleRate(); emit started(codec.sampleRate(), codec.channels()); while (remaining > 0) { Packet pkt(&packet); if (!format.readFrame(pkt)) break; packetTemp.data = packet.data; packetTemp.size = packet.size; while (packetTemp.size > 0) { int bufferSize = BUFFER_SIZE; int consumed = codec.decode(m_buffer1, &bufferSize, &packetTemp); if (consumed < 0) { break; } packetTemp.data += consumed; packetTemp.size -= consumed; if (bufferSize <= 0 || bufferSize > BUFFER_SIZE) { continue; } int16_t *buffer = converter.convert(codec, m_buffer1, m_buffer2, bufferSize); if (!buffer) break; int length = qMin(remaining, bufferSize / 2); emit bufferReady(QByteArray(reinterpret_cast(buffer), length * 2)); if (isStopped()) { err = FingerprintCalculator::FingerprintCalculationFailed; emit error(err); return; } remaining -= length; if (remaining <= 0) { break; } } } emit finished(duration); } /** * Create concrete fingerprint decoder. * @param parent parent object * @return fingerprint decoder instance. * @remarks This static method will be implemented by the concrete * fingerprint decoder which is used. */ AbstractFingerprintDecoder* AbstractFingerprintDecoder::createFingerprintDecoder(QObject* parent) { return new FFmpegFingerprintDecoder(parent); } kid3-3.0.2/src/plugins/acoustidimport/ffmpegfingerprintdecoder.h000066400000000000000000000027661224603511300251070ustar00rootroot00000000000000/** * \file ffmpegfingerprintdecoder.h * Chromaprint fingerprint decoder using FFmpeg. * * \b Project: Kid3 * \author Urs Fleisch * \date 15 Feb 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef FFMPEGFINGERPRINTDECODER_H #define FFMPEGFINGERPRINTDECODER_H #include "abstractfingerprintdecoder.h" /** * Chromaprint fingerprint decoder using FFmpeg. */ class FFmpegFingerprintDecoder : public AbstractFingerprintDecoder { public: /** * Constructor. * @param parent parent object */ explicit FFmpegFingerprintDecoder(QObject* parent = 0); /** * Destructor. */ virtual ~FFmpegFingerprintDecoder(); /** * Run decoder on audio file. * @param filePath path to audio file */ virtual void start(const QString& filePath); private: qint16* m_buffer1; qint16* m_buffer2; }; #endif // FFMPEGFINGERPRINTDECODER_H kid3-3.0.2/src/plugins/acoustidimport/fingerprintcalculator.cpp000066400000000000000000000070111224603511300247650ustar00rootroot00000000000000/** * \file fingerprintcalculator.cpp * Chromaprint fingerprint calculator. * * \b Project: Kid3 * \author Urs Fleisch * \date 21 Jan 2012 * * Copyright (C) 2012 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #define __STDC_CONSTANT_MACROS #include "fingerprintcalculator.h" #include #include "config.h" #include "abstractfingerprintdecoder.h" /** * Constructor. */ FingerprintCalculator::FingerprintCalculator(QObject* parent) : QObject(parent), m_chromaprintCtx(0), m_decoder(AbstractFingerprintDecoder::createFingerprintDecoder(this)) { connect(m_decoder, SIGNAL(started(int,int)), this, SLOT(startChromaprint(int,int))); connect(m_decoder, SIGNAL(bufferReady(QByteArray)), this, SLOT(feedChromaprint(QByteArray))); connect(m_decoder, SIGNAL(error(int)), this, SLOT(receiveError(int))); connect(m_decoder, SIGNAL(finished(int)), this, SLOT(finishChromaprint(int))); } /** * Destructor. */ FingerprintCalculator::~FingerprintCalculator() { if (m_chromaprintCtx) { ::chromaprint_free(m_chromaprintCtx); } } /** * Calculate audio fingerprint for audio file. * When the calculation is finished, finished() is emitted. * * @param fileName path to audio file */ void FingerprintCalculator::start(const QString& fileName) { if (!m_chromaprintCtx) { // Lazy initialization to save resources if not used m_chromaprintCtx = ::chromaprint_new(CHROMAPRINT_ALGORITHM_DEFAULT); } m_decoder->start(fileName); } /** * Stop decoder. */ void FingerprintCalculator::stop() { m_decoder->stop(); } /** * Called when decoding starts. * @param sampleRate sample rate of the audio stream (in Hz) * @param channelCount numbers of channels in the audio stream (1 or 2) */ void FingerprintCalculator::startChromaprint(int sampleRate, int channelCount) { ::chromaprint_start(m_chromaprintCtx, sampleRate, channelCount); } /** * Called when decoded data is available. * @param data 16-bit signed integers in native byte-order */ void FingerprintCalculator::feedChromaprint(QByteArray data) { if (!::chromaprint_feed(m_chromaprintCtx, data.data(), data.size() / 2)) { m_decoder->stop(); emit finished(QString(), 0, FingerprintCalculationFailed); } } /** * Called when an error occurs. * @param err error code, enum FingerprintCalculator::Error */ void FingerprintCalculator::receiveError(int err) { emit finished(QString(), 0, err); } /** * Called when decoding finished successfully. * @param duration duration of stream in seconds */ void FingerprintCalculator::finishChromaprint(int duration) { int err = Ok; QString fingerprint; char* fp; if (::chromaprint_finish(m_chromaprintCtx) && ::chromaprint_get_fingerprint(m_chromaprintCtx, &fp)) { fingerprint = QString::fromLatin1(fp); ::chromaprint_dealloc(fp); } else { err = FingerprintCalculationFailed; } emit finished(fingerprint, duration, err); } kid3-3.0.2/src/plugins/acoustidimport/fingerprintcalculator.h000066400000000000000000000062041224603511300244350ustar00rootroot00000000000000/** * \file fingerprintcalculator.h * Chromaprint fingerprint calculator. * * \b Project: Kid3 * \author Urs Fleisch * \date 21 Jan 2012 * * Copyright (C) 2012 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef FINGERPRINTCALCULATOR_H #define FINGERPRINTCALCULATOR_H #include #include class AbstractFingerprintDecoder; /** * Calculate Chromaprint audio fingerprints for audio files. */ class FingerprintCalculator : public QObject { Q_OBJECT public: /** Types of error occurring in fingerprint calculation. */ enum Error { Ok, /**< Fingerprint calculation OK */ Pending, /**< Not started */ NoStreamFound, /**< Format not recognized or no audio stream found */ NoCodecFound, /**< No codec found */ NoConverterFound, /**< Sample rate conversion failed or unavailable */ FingerprintCalculationFailed, /**< Chromaprint error */ Timeout, /**< Operation timeout */ DecoderError /**< Error while decoding */ }; /** * Constructor. */ explicit FingerprintCalculator(QObject* parent = 0); /** * Destructor. */ virtual ~FingerprintCalculator(); /** * Calculate audio fingerprint for audio file. * When the calculation is finished, finished() is emitted. * * @param fileName path to audio file */ void start(const QString& fileName); /** * Stop decoder. */ void stop(); signals: /** * Emitted when the fingerprint calculation is finished. * * @param fingerprint Chromaprint fingerprint * @param duration duration in seconds * @param error error code, enum FingerprintCalculator::Error */ void finished(const QString& fingerprint, int duration, int error); private slots: /** * Called when decoding starts. * @param sampleRate sample rate of the audio stream (in Hz) * @param channelCount numbers of channels in the audio stream (1 or 2) */ void startChromaprint(int sampleRate, int channelCount); /** * Called when decoded data is available. * @param data 16-bit signed integers in native byte-order */ void feedChromaprint(QByteArray data); /** * Called when an error occurs. * @param err error code, enum FingerprintCalculator::Error */ void receiveError(int err); /** * Called when decoding finished successfully. * @param duration duration of stream in seconds */ void finishChromaprint(int duration); private: void** m_chromaprintCtx; AbstractFingerprintDecoder* m_decoder; }; #endif // FINGERPRINTCALCULATOR_H kid3-3.0.2/src/plugins/acoustidimport/gstfingerprintdecoder.cpp000066400000000000000000000244541224603511300247710ustar00rootroot00000000000000/** * \file gstfingerprintdecoder.cpp * Chromaprint fingerprint decoder using GStreamer. * * \b Project: Kid3 * \author Urs Fleisch * \date 15 Feb 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "gstfingerprintdecoder.h" #include #include #include /** * Constructor. * @param parent parent object */ GstFingerprintDecoder::GstFingerprintDecoder(QObject* parent) : AbstractFingerprintDecoder(parent), m_error(FingerprintCalculator::Ok), m_duration(0), m_channels(0), m_rate(0), m_gotPad(false) { gst_init(NULL, NULL); // gst_debug_set_default_threshold(GST_LEVEL_INFO); // gst_debug_set_colored(FALSE); m_loop = g_main_loop_new(NULL, FALSE); m_pipeline = gst_pipeline_new("pipeline"); m_dec = gst_element_factory_make("uridecodebin", "dec"); m_conv = gst_element_factory_make("audioconvert", "conv"); GstElement* sink = gst_element_factory_make("appsink", "sink"); if (m_loop && m_pipeline && m_dec && m_conv && sink) { if (GstBus* bus = gst_pipeline_get_bus(GST_PIPELINE(m_pipeline))) { gst_bus_add_signal_watch(bus); g_signal_connect(bus, "message::eos", G_CALLBACK(cb_message), this); g_signal_connect(bus, "message::error", G_CALLBACK(cb_message), this); gst_object_unref(GST_OBJECT(bus)); } g_signal_connect(m_dec, "pad-added", G_CALLBACK(cb_pad_added), this); g_signal_connect(m_dec, "no-more-pads", G_CALLBACK(cb_no_more_pads), this); g_signal_connect(m_dec, "unknown-type", G_CALLBACK(cb_unknown_type), this); if (GstCaps* sinkcaps = gst_caps_new_simple( #if GST_CHECK_VERSION(1, 0, 0) "audio/x-raw", "format", G_TYPE_STRING, "S16LE", "layout", G_TYPE_STRING, "interleaved", "rate", G_TYPE_INT, 44100, "channels", G_TYPE_INT, 2, "channel-mask", GST_TYPE_BITMASK, (gint64)0x3, #else "audio/x-raw-int", "width", G_TYPE_INT, 16, "depth", G_TYPE_INT, 16, "signed", G_TYPE_BOOLEAN, TRUE, #endif NULL)) { g_object_set(G_OBJECT(sink), "caps", sinkcaps, NULL); gst_caps_unref(sinkcaps); } g_object_set(G_OBJECT(sink), "drop", FALSE, "max-buffers", BUFFER_SIZE, "sync", FALSE, "emit-signals", TRUE, NULL); #if GST_CHECK_VERSION(1, 0, 0) g_signal_connect(sink, "new-sample", G_CALLBACK(cb_new_buffer), this); #else g_signal_connect(sink, "new-buffer", G_CALLBACK(cb_new_buffer), this); #endif if (GstPad* pad = gst_element_get_static_pad(sink, "sink")) { g_signal_connect(pad, "notify::caps", G_CALLBACK(cb_notify_caps), this); gst_object_unref(pad); } gst_bin_add_many(GST_BIN(m_pipeline), m_dec, m_conv, sink, NULL); gst_element_link_many(m_conv, sink, NULL); } else { if (m_loop) { g_main_loop_unref(m_loop); m_loop = NULL; } else { g_print("Failed to create main loop.\n"); } if (m_pipeline) { gst_object_unref(m_pipeline); m_pipeline = NULL; } else { g_print("Failed to create pipeline.\n"); } if (m_dec) { gst_object_unref(m_dec); m_dec = NULL; } else { g_print("Failed to create uridecodebin.\n"); } if (m_conv) { gst_object_unref(m_conv); m_conv = NULL; } else { g_print("Failed to create audioconvert.\n"); } if (sink) { gst_object_unref(sink); } else { g_print("Failed to create appsink.\n"); } } } /** * Destructor. */ GstFingerprintDecoder::~GstFingerprintDecoder() { if (m_pipeline) { gst_element_set_state(m_pipeline, GST_STATE_NULL); gst_object_unref(m_pipeline); } if (m_loop) { g_main_loop_unref(m_loop); } } void GstFingerprintDecoder::raiseError( FingerprintCalculator::Error error) { m_error = error; g_main_loop_quit(m_loop); } gboolean GstFingerprintDecoder::cb_timeout(gpointer data) { GstFingerprintDecoder* self = reinterpret_cast(data); self->raiseError(FingerprintCalculator::Timeout); return FALSE; } void GstFingerprintDecoder::cb_message(GstBus*, GstMessage* message, GstFingerprintDecoder* self) { switch (GST_MESSAGE_TYPE(message)) { case GST_MESSAGE_ERROR: { GError *err; gchar *debug; gst_message_parse_error(message, &err, &debug); g_print("Error: %s\n", err->message); g_error_free(err); g_free(debug); self->raiseError(FingerprintCalculator::DecoderError); break; } case GST_MESSAGE_EOS: // end-of-stream g_main_loop_quit(self->m_loop); break; default: break; } } void GstFingerprintDecoder::cb_pad_added(GstElement*, GstPad* pad, GstFingerprintDecoder* self) { if (GstCaps* caps = #if GST_CHECK_VERSION(1, 0, 0) gst_pad_query_caps(pad, 0) #else gst_pad_get_caps(pad) #endif ) { const GstStructure* str = gst_caps_get_structure(caps, 0); const gchar* name = gst_structure_get_name(str); if (name && strncmp(name, "audio/x-raw", 11) == 0) { if (GstPad* nextpad = gst_element_get_static_pad(self->m_conv, "sink")) { if (!gst_pad_is_linked(nextpad)) { if (gst_pad_link(pad, nextpad) == GST_PAD_LINK_OK) { self->m_gotPad = true; } else { g_print("Failed to link pads\n"); } } gst_object_unref(nextpad); } } gst_caps_unref(caps); } } void GstFingerprintDecoder::cb_no_more_pads(GstElement*, GstFingerprintDecoder* self) { if (!self->m_gotPad) { self->raiseError(FingerprintCalculator::NoStreamFound); } } void GstFingerprintDecoder::cb_notify_caps(GstPad *pad, GParamSpec*, GstFingerprintDecoder* self) { if (GstCaps* caps = #if GST_CHECK_VERSION(1, 0, 0) gst_pad_get_current_caps(pad) #else gst_pad_get_negotiated_caps(pad) #endif ) { const GstStructure* str = gst_caps_get_structure(caps, 0); if (gst_structure_get_int(str, "channels", &self->m_channels) && gst_structure_get_int(str, "rate", &self->m_rate)) { emit self->started(self->m_rate, self->m_channels); } else { g_print("No channels/rate available\n"); } gst_caps_unref(caps); } if (GstQuery* query = gst_query_new_duration(GST_FORMAT_TIME)) { if (GstPad* peer = gst_pad_get_peer(pad)) { if (gst_pad_query(peer, query)) { GstFormat format; gint64 length; gst_query_parse_duration(query, &format, &length); if (format == GST_FORMAT_TIME) { self->m_duration = length / 1000000000; } } gst_object_unref(peer); } gst_query_unref(query); } } void GstFingerprintDecoder::cb_unknown_type(GstElement*, GstPad*, GstCaps* caps, GstFingerprintDecoder* self) { bool isAudio = false; if (gchar* streaminfo = gst_caps_to_string(caps)) { isAudio = strncmp(streaminfo, "audio/", 6) == 0; g_free(streaminfo); } if (!isAudio) return; self->raiseError(FingerprintCalculator::NoCodecFound); } void GstFingerprintDecoder::cb_new_buffer(GstElement* sink, GstFingerprintDecoder* self) { #if GST_CHECK_VERSION(1, 0, 0) GstSample* sample = 0; g_signal_emit_by_name(sink, "pull-sample", &sample); if (sample) { GstBuffer* buffer = gst_sample_get_buffer(sample); gint64 buf_pos = GST_BUFFER_TIMESTAMP(buffer); GstMapInfo mapinfo; ::memset(&mapinfo, 0, sizeof(mapinfo)); gst_buffer_map(buffer, &mapinfo, GST_MAP_READ); emit self->bufferReady(QByteArray(reinterpret_cast(mapinfo.data), mapinfo.size)); gst_buffer_unmap(buffer, &mapinfo); gst_sample_unref(sample); #else GstBuffer* buffer = 0; g_signal_emit_by_name(sink, "pull-buffer", &buffer); if (buffer) { gint64 buf_pos = GST_BUFFER_TIMESTAMP(buffer); size_t len = GST_BUFFER_SIZE(buffer); guint8* data = GST_BUFFER_DATA(buffer); emit self->bufferReady(QByteArray(reinterpret_cast(data), len)); gst_buffer_unref(buffer); #endif if (self->isStopped()) { self->raiseError(FingerprintCalculator::FingerprintCalculationFailed); } if (buf_pos >= MAX_LENGTH_NS) { g_main_loop_quit(self->m_loop); } } } /** * Run decoder on audio file. * @param filePath path to audio file */ void GstFingerprintDecoder::start(const QString& filePath) { AbstractFingerprintDecoder::start(filePath); if (!m_loop) { // Initialization failed m_error = FingerprintCalculator::DecoderError; emit error(m_error); return; } m_error = FingerprintCalculator::Ok; m_duration = 0; m_channels = 0; m_rate = 0; m_gotPad = false; QByteArray url( QUrl::fromLocalFile(QFileInfo(filePath).absoluteFilePath()).toEncoded()); g_object_set(G_OBJECT(m_dec), "uri", url.constData(), NULL); gst_element_set_state(GST_ELEMENT(m_pipeline), GST_STATE_PLAYING); guint timeoutFuncId = g_timeout_add(TIMEOUT_MS, cb_timeout, this); g_main_loop_run(m_loop); g_source_remove(timeoutFuncId); gst_element_set_state(m_pipeline, GST_STATE_READY); if (m_error == FingerprintCalculator::Ok) { emit finished(m_duration); } else { emit error(m_error); } } /** * Stop decoder. * Can be used to stop the decoder when an error is found after * getting bufferReady() data. */ void GstFingerprintDecoder::stop() { AbstractFingerprintDecoder::stop(); if (m_loop) { g_main_loop_quit(m_loop); } } /** * Create concrete fingerprint decoder. * @param parent parent object * @return fingerprint decoder instance. * @remarks This static method will be implemented by the concrete * fingerprint decoder which is used. */ AbstractFingerprintDecoder* AbstractFingerprintDecoder::createFingerprintDecoder(QObject* parent) { return new GstFingerprintDecoder(parent); } kid3-3.0.2/src/plugins/acoustidimport/gstfingerprintdecoder.h000066400000000000000000000051111224603511300244230ustar00rootroot00000000000000/** * \file gstfingerprintdecoder.h * Chromaprint fingerprint decoder using GStreamer. * * \b Project: Kid3 * \author Urs Fleisch * \date 15 Feb 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef GSTFINGERPRINTDECODER_H #define GSTFINGERPRINTDECODER_H #include #include "abstractfingerprintdecoder.h" #include "fingerprintcalculator.h" /** * Chromaprint fingerprint decoder using GStreamer. */ class GstFingerprintDecoder : public AbstractFingerprintDecoder { public: /** * Constructor. * @param parent parent object */ explicit GstFingerprintDecoder(QObject* parent = 0); /** * Destructor. */ virtual ~GstFingerprintDecoder(); /** * Run decoder on audio file. * @param filePath path to audio file */ virtual void start(const QString& filePath); /** * Stop decoder. * Can be used to stop the decoder when an error is found after * getting bufferReady() data. */ virtual void stop(); private: static const int BUFFER_SIZE = 10; static const gint64 MAX_LENGTH_NS = 120000000000; static const guint TIMEOUT_MS = 5000; void raiseError(FingerprintCalculator::Error error); static gboolean cb_timeout(gpointer data); static void cb_message(GstBus* bus, GstMessage* message, GstFingerprintDecoder* self); static void cb_pad_added(GstElement* dec, GstPad* pad, GstFingerprintDecoder* self); static void cb_no_more_pads(GstElement* dec, GstFingerprintDecoder* self); static void cb_notify_caps(GstPad *pad, GParamSpec* spec, GstFingerprintDecoder* self); static void cb_unknown_type(GstElement* dec, GstPad* pad, GstCaps* caps, GstFingerprintDecoder* self); static void cb_new_buffer(GstElement* sink, GstFingerprintDecoder* self); GMainLoop* m_loop; GstElement* m_pipeline; GstElement* m_dec; GstElement* m_conv; FingerprintCalculator::Error m_error; int m_duration; gint m_channels; gint m_rate; bool m_gotPad; }; #endif // GSTFINGERPRINTDECODER_H kid3-3.0.2/src/plugins/acoustidimport/musicbrainzclient.cpp000066400000000000000000000272161224603511300241220ustar00rootroot00000000000000/** * \file musicbrainzclient.cpp * MusicBrainz client. * * \b Project: Kid3 * \author Urs Fleisch * \date 15 Sep 2005 * * Copyright (C) 2005-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "musicbrainzclient.h" #include #include #include "httpclient.h" #include "trackdatamodel.h" #include "fingerprintcalculator.h" namespace { /** * Parse response from acoustid.org. * @param bytes response in JSON format * @return list of MusicBrainz IDs */ QStringList parseAcoustidIds(const QByteArray& bytes) { /* * The response from acoustid.org is in JSON format and looks like this: * { * "status": "ok", * "results": [{ * "recordings": [{"id": "14fef9a4-9b50-4e9f-9e22-490fd86d1861"}], * "score": 0.938621, "id": "29bf7ce3-0182-40da-b840-5420203369c4" * }] * } */ QStringList ids; if (bytes.indexOf("\"status\": \"ok\"") >= 0) { int startPos = bytes.indexOf("\"recordings\": ["); if (startPos >= 0) { startPos += 15; int endPos = bytes.indexOf(']', startPos); if (endPos > startPos) { QRegExp idRe(QLatin1String("\"id\":\\s*\"([^\"]+)\"")); QString recordings(QString::fromLatin1(bytes.mid(startPos, endPos - startPos))); int pos = 0; while ((pos = idRe.indexIn(recordings, pos)) != -1) { ids.append(idRe.cap(1)); pos += idRe.matchedLength(); } } } } return ids; } /** * Parse response from MusicBrainz server. * * @param bytes XML response from MusicBrainz * @param trackDataVector the resulting track data will be appended to this * vector */ void parseMusicBrainzMetadata(const QByteArray& bytes, ImportTrackDataVector& trackDataVector) { /* * The XML response from MusicBrainz looks like this (simplified): * * * * Trip the Darkness * 192000 * * * * Lacuna Coil * * * * * * Dark Adrenaline * 2012-01-24 * * * * * 1 * * * * * * * * */ int start = bytes.indexOf(""); QByteArray xmlStr = start >= 0 && end > start ? bytes.mid(start, end + 11 - start) : bytes; QDomDocument doc; if (doc.setContent(xmlStr, false)) { QDomElement recording = doc.namedItem(QLatin1String("metadata")).namedItem(QLatin1String("recording")).toElement(); if (!recording.isNull()) { bool ok; ImportTrackData frames; frames.setTitle(recording.namedItem(QLatin1String("title")).toElement().text()); int length = recording.namedItem(QLatin1String("length")).toElement().text().toInt(&ok); if (ok) { frames.setImportDuration(length / 1000); } QDomNode artistNode = recording.namedItem(QLatin1String("artist-credit")); if (!artistNode.isNull()) { QString artist(artistNode.namedItem(QLatin1String("name-credit")).namedItem(QLatin1String("artist")). namedItem(QLatin1String("name")).toElement().text()); frames.setArtist(artist); } QDomNode releaseNode = recording.namedItem(QLatin1String("release-list")). namedItem(QLatin1String("release")); if (!releaseNode.isNull()) { frames.setAlbum(releaseNode.namedItem(QLatin1String("title")).toElement().text()); QString date(releaseNode.namedItem(QLatin1String("date")).toElement().text()); if (!date.isEmpty()) { QRegExp dateRe(QLatin1String("(\\d{4})(?:-\\d{2})?(?:-\\d{2})?")); int year = 0; if (dateRe.exactMatch(date)) { year = dateRe.cap(1).toInt(); } else { year = date.toInt(); } if (year != 0) { frames.setYear(year); } } QDomNode trackNode = releaseNode.namedItem(QLatin1String("medium-list")). namedItem(QLatin1String("medium")).namedItem(QLatin1String("track-list")).namedItem(QLatin1String("track")); if (!trackNode.isNull()) { int trackNr = trackNode.namedItem(QLatin1String("position")).toElement().text(). toInt(&ok); if (ok) { frames.setTrack(trackNr); } } } trackDataVector.append(frames); } } } } /** * Constructor. * * @param netMgr network access manager * @param trackDataModel track data to be filled with imported values, * is passed with filenames set */ MusicBrainzClient::MusicBrainzClient(QNetworkAccessManager* netMgr, TrackDataModel *trackDataModel) : ServerTrackImporter(netMgr, trackDataModel), m_fingerprintCalculator(new FingerprintCalculator(this)), m_state(Idle), m_currentIndex(-1) { connect(httpClient(), SIGNAL(bytesReceived(QByteArray)), this, SLOT(receiveBytes(QByteArray))); connect(m_fingerprintCalculator, SIGNAL(finished(QString,int,int)), this, SLOT(receiveFingerprint(QString,int,int))); } /** * Destructor. */ MusicBrainzClient::~MusicBrainzClient() { } /** * Name of import source. * @return name. */ const char* MusicBrainzClient::name() const { return QT_TRANSLATE_NOOP("@default", "MusicBrainz Fingerprint"); } /** NULL-terminated array of server strings, 0 if not used */ const char** MusicBrainzClient::serverList() const { return 0; } /** default server, 0 to disable */ const char* MusicBrainzClient::defaultServer() const { return 0; } /** anchor to online help, 0 to disable */ const char* MusicBrainzClient::helpAnchor() const { return "import-musicbrainz"; } /** configuration, 0 if not used */ ServerImporterConfig* MusicBrainzClient::config() const { return 0; } /** * Verify if m_currentIndex is in range of m_idsOfTrack. * @return true if index OK, false if index was invalid and state is reset. */ bool MusicBrainzClient::verifyIdIndex() { if (m_currentIndex < 0 || m_currentIndex >= m_idsOfTrack.size()) { qWarning("Invalid index %d for IDs (size %d)", m_currentIndex, m_idsOfTrack.size()); stop(); return false; } return true; } /** * Verify if m_currentIndex is in range of m_filenameOfTrack. * @return true if index OK, false if index was invalid and state is reset. */ bool MusicBrainzClient::verifyTrackIndex() { if (m_currentIndex < 0 || m_currentIndex >= m_filenameOfTrack.size()) { qWarning("Invalid index %d for track (size %d)", m_currentIndex, m_filenameOfTrack.size()); stop(); return false; } return true; } /** * Reset the state to Idle and no track. */ void MusicBrainzClient::stop() { m_fingerprintCalculator->stop(); m_currentIndex = -1; m_state = Idle; } /** * Receive response from web service. * @param bytes bytes received */ void MusicBrainzClient::receiveBytes(const QByteArray& bytes) { switch (m_state) { case GettingIds: if (!verifyIdIndex()) return; m_idsOfTrack[m_currentIndex] = parseAcoustidIds(bytes); if (m_idsOfTrack.at(m_currentIndex).isEmpty()) { emit statusChanged(m_currentIndex, tr("Unrecognized")); } m_state = GettingMetadata; processNextStep(); break; case GettingMetadata: parseMusicBrainzMetadata(bytes, m_currentTrackData); if (!verifyIdIndex()) return; if (m_idsOfTrack.at(m_currentIndex).isEmpty()) { emit statusChanged(m_currentIndex, m_currentTrackData.size() == 1 ? tr("Recognized") : tr("User Selection")); emit resultsReceived(m_currentIndex, m_currentTrackData); } processNextStep(); break; default: ; } } /** * Receive fingerprint from decoder. * * @param fingerprint Chromaprint fingerprint * @param duration duration in seconds * @param error error code */ void MusicBrainzClient::receiveFingerprint(const QString& fingerprint, int duration, int error) { if (error == FingerprintCalculator::Ok) { m_state = GettingIds; emit statusChanged(m_currentIndex, tr("ID Lookup")); QString path(QLatin1String("/v2/lookup?client=LxDbFAXo&meta=recordingids&duration=") + QString::number(duration) + QLatin1String("&fingerprint=") + fingerprint); httpClient()->sendRequest(QLatin1String("api.acoustid.org"), path); } else { emit statusChanged(m_currentIndex, tr("Error")); if (m_state != Idle) { processNextTrack(); } } } /** * Process next step in importing from fingerprints. */ void MusicBrainzClient::processNextStep() { switch (m_state) { case Idle: break; case CalculatingFingerprint: { if (!verifyTrackIndex()) return; emit statusChanged(m_currentIndex, tr("Fingerprint")); m_fingerprintCalculator->start(m_filenameOfTrack.at(m_currentIndex)); break; } case GettingMetadata: { if (!verifyIdIndex()) return; QStringList& ids = m_idsOfTrack[m_currentIndex]; if (!ids.isEmpty()) { emit statusChanged(m_currentIndex, tr("Metadata Lookup")); QString path(QLatin1String("/ws/2/recording/") + ids.takeFirst() + QLatin1String("?inc=artists+releases+media")); httpClient()->sendRequest(QLatin1String("musicbrainz.org:80"), path); } else { processNextTrack(); } break; } case GettingIds: qWarning("processNextStep() called in state GettingIds"); stop(); } } /** * Process next track. * If all tracks have been processed, the state is reset to Idle. */ void MusicBrainzClient::processNextTrack() { if (m_currentIndex < m_filenameOfTrack.size() - 1) { ++m_currentIndex; m_state = CalculatingFingerprint; } else { stop(); } m_currentTrackData.clear(); processNextStep(); } /** * Set configuration. * * @param cfg import server configuration, 0 if not used */ void MusicBrainzClient::setConfig(const ServerImporterConfig* cfg) { Q_UNUSED(cfg) } /** * Add the files in the file list. */ void MusicBrainzClient::start() { m_filenameOfTrack.clear(); m_idsOfTrack.clear(); const ImportTrackDataVector& trackDataVector(trackDataModel()->trackData()); for (ImportTrackDataVector::const_iterator it = trackDataVector.constBegin(); it != trackDataVector.constEnd(); ++it) { if (it->isEnabled()) { m_filenameOfTrack.append(it->getAbsFilename()); m_idsOfTrack.append(QStringList()); } } stop(); processNextTrack(); } kid3-3.0.2/src/plugins/acoustidimport/musicbrainzclient.h000066400000000000000000000061301224603511300235570ustar00rootroot00000000000000/** * \file musicbrainzclient.h * MusicBrainz client. * * \b Project: Kid3 * \author Urs Fleisch * \date 15 Sep 2005 * * Copyright (C) 2005-2012 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MUSICBRAINZCLIENT_H #define MUSICBRAINZCLIENT_H #include #include "servertrackimporter.h" #include "trackdata.h" class QByteArray; class FingerprintCalculator; /** * MusicBrainz client. */ class MusicBrainzClient : public ServerTrackImporter { Q_OBJECT public: /** * Constructor. * * @param netMgr network access manager * @param trackDataModel track data to be filled with imported values, * is passed with filenames set */ MusicBrainzClient(QNetworkAccessManager* netMgr, TrackDataModel* trackDataModel); /** * Destructor. */ virtual ~MusicBrainzClient(); /** * Name of import source. * @return name. */ virtual const char* name() const; /** NULL-terminated array of server strings, 0 if not used */ virtual const char** serverList() const; /** default server, 0 to disable */ virtual const char* defaultServer() const; /** anchor to online help, 0 to disable */ virtual const char* helpAnchor() const; /** configuration, 0 if not used */ virtual ServerImporterConfig* config() const; /** * Set configuration. * * @param cfg import server configuration, 0 if not used */ virtual void setConfig(const ServerImporterConfig* cfg); /** * Add the files in the file list. */ virtual void start(); /** * Reset the client state. */ virtual void stop(); signals: /** * Emitted when status of a file changed. * Parameter: index of file, status text */ void statusChanged(int, QString); /** * Emitted when results for a file are received. * Parameter index of file, track data list */ void resultsReceived(int, ImportTrackDataVector&); private slots: void receiveBytes(const QByteArray& bytes); void receiveFingerprint(const QString& fingerprint, int duration, int error); private: enum State { Idle, CalculatingFingerprint, GettingIds, GettingMetadata }; bool verifyIdIndex(); bool verifyTrackIndex(); void processNextStep(); void processNextTrack(); FingerprintCalculator* m_fingerprintCalculator; State m_state; QVector m_filenameOfTrack; QVector m_idsOfTrack; int m_currentIndex; ImportTrackDataVector m_currentTrackData; }; #endif // MUSICBRAINZCLIENT_H kid3-3.0.2/src/plugins/acoustidimport/qtfingerprintdecoder.cpp000066400000000000000000000074141224603511300246150ustar00rootroot00000000000000/** * \file qtfingerprintdecoder.cpp * Chromaprint fingerprint decoder using QAudioDecoder. * * \b Project: Kid3 * \author Urs Fleisch * \date 15 Feb 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "qtfingerprintdecoder.h" #include #include #include "fingerprintcalculator.h" /** * Constructor. * @param parent parent object */ QtFingerprintDecoder::QtFingerprintDecoder(QObject* parent) : AbstractFingerprintDecoder(parent), m_decoder(new QAudioDecoder(this)), m_timer(new QTimer(this)) { QAudioFormat desiredFormat; desiredFormat.setChannelCount(2); desiredFormat.setCodec(QLatin1String("audio/x-raw-int")); desiredFormat.setSampleType(QAudioFormat::SignedInt); desiredFormat.setSampleRate(44100); desiredFormat.setSampleSize(16); m_decoder->setAudioFormat(desiredFormat); connect(m_decoder, SIGNAL(bufferReady()), this, SLOT(receiveBuffer())); connect(m_decoder, SIGNAL(error(QAudioDecoder::Error)), this, SLOT(receiveError())); connect(m_decoder, SIGNAL(finished()), this, SLOT(finishDecoding())); m_timer->setSingleShot(true); m_timer->setInterval(5000); connect(m_timer, SIGNAL(timeout()), this, SLOT(receiveTimeout())); } /** * Destructor. */ QtFingerprintDecoder::~QtFingerprintDecoder() { } /** * Run decoder on audio file. * @param filePath path to audio file */ void QtFingerprintDecoder::start(const QString& filePath) { AbstractFingerprintDecoder::start(filePath); m_decoder->setSourceFilename(filePath); QAudioFormat format = m_decoder->audioFormat(); emit started(format.sampleRate(), format.channelCount()); m_timer->start(); m_decoder->start(); } /** * Stop decoder. * Can be used to stop the decoder when an error is found after * getting bufferReady() data. */ void QtFingerprintDecoder::stop() { AbstractFingerprintDecoder::stop(); m_timer->stop(); m_decoder->stop(); } /** * Receive a buffer with decoded audio data. */ void QtFingerprintDecoder::receiveBuffer() { m_timer->stop(); QAudioBuffer buffer = m_decoder->read(); if (!buffer.isValid()) { return; } if (buffer.startTime() > 120000000LL) { finishDecoding(); return; } emit bufferReady(QByteArray(buffer.constData(), buffer.byteCount())); m_timer->start(); } /** * Receive an error from the decoder. */ void QtFingerprintDecoder::receiveError() { m_timer->stop(); m_decoder->stop(); emit error(FingerprintCalculator::DecoderError); } /** * Receive a timeout. */ void QtFingerprintDecoder::receiveTimeout() { m_decoder->stop(); emit error(FingerprintCalculator::Timeout); } /** * Finish decoding. */ void QtFingerprintDecoder::finishDecoding() { m_timer->stop(); int duration = m_decoder->duration() / 1000LL; m_decoder->stop(); emit finished(duration); } /** * Create concrete fingerprint decoder. * @param parent parent object * @return fingerprint decoder instance. * @remarks This static method will be implemented by the concrete * fingerprint decoder which is used. */ AbstractFingerprintDecoder* AbstractFingerprintDecoder::createFingerprintDecoder(QObject* parent) { return new QtFingerprintDecoder(parent); } kid3-3.0.2/src/plugins/acoustidimport/qtfingerprintdecoder.h000066400000000000000000000037631224603511300242650ustar00rootroot00000000000000/** * \file qtfingerprintdecoder.h * Chromaprint fingerprint decoder using QAudioDecoder. * * \b Project: Kid3 * \author Urs Fleisch * \date 15 Feb 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef QTFINGERPRINTDECODER_H #define QTFINGERPRINTDECODER_H #include #include "abstractfingerprintdecoder.h" class QAudioDecoder; class QTimer; /** * Chromaprint fingerprint decoder using QAudioDecoder. */ class QtFingerprintDecoder : public AbstractFingerprintDecoder { Q_OBJECT public: /** * Constructor. * @param parent parent object */ explicit QtFingerprintDecoder(QObject* parent = 0); /** * Destructor. */ virtual ~QtFingerprintDecoder(); /** * Run decoder on audio file. * @param filePath path to audio file */ virtual void start(const QString& filePath); /** * Stop decoder. * Can be used to stop the decoder when an error is found after * getting bufferReady() data. */ virtual void stop(); private slots: /** * Receive a buffer with decoded audio data. */ void receiveBuffer(); /** * Receive an error from the decoder. */ void receiveError(); /** * Receive a timeout. */ void receiveTimeout(); /** * Finish decoding. */ void finishDecoding(); private: QAudioDecoder* m_decoder; QTimer* m_timer; }; #endif // QTFINGERPRINTDECODER_H kid3-3.0.2/src/plugins/amazonimport/000077500000000000000000000000001224603511300173335ustar00rootroot00000000000000kid3-3.0.2/src/plugins/amazonimport/CMakeLists.txt000066400000000000000000000010301224603511300220650ustar00rootroot00000000000000set(plugin_SRCS amazonimportplugin.cpp amazonimporter.cpp amazonconfig.cpp ) set(plugin_MOC_HDRS amazonimportplugin.h ) set(plugin_NAME AmazonImport) if (WITH_GCC_PCH) add_definitions(${GCC_PCH_COMPILE_FLAGS}) endif (WITH_GCC_PCH) string(TOLOWER ${plugin_NAME} plugin_TARGET) qt4_wrap_cpp(plugin_GEN_MOC_SRCS ${plugin_MOC_HDRS}) add_library(${plugin_TARGET} ${plugin_SRCS} ${plugin_GEN_MOC_SRCS}) target_link_libraries(${plugin_TARGET} kid3-core ${BASIC_LIBRARIES}) INSTALL_KID3_PLUGIN(${plugin_TARGET} ${plugin_NAME}) kid3-3.0.2/src/plugins/amazonimport/amazonconfig.cpp000066400000000000000000000022741224603511300225170ustar00rootroot00000000000000/** * \file amazonconfig.cpp * Amazon configuration. * * \b Project: Kid3 * \author Urs Fleisch * \date 13 Dec 2009 * * Copyright (C) 2009-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "amazonconfig.h" #include int AmazonConfig::s_index = -1; /** * Constructor. */ AmazonConfig::AmazonConfig() : StoredConfig(QLatin1String("Amazon")) { m_cgiPathUsed = false; m_additionalTagsUsed = true; m_server = QLatin1String("www.amazon.com:80"); } /** * Destructor. */ AmazonConfig::~AmazonConfig() {} kid3-3.0.2/src/plugins/amazonimport/amazonconfig.h000066400000000000000000000023141224603511300221570ustar00rootroot00000000000000/** * \file amazonconfig.h * Amazon configuration. * * \b Project: Kid3 * \author Urs Fleisch * \date 13 Dec 2009 * * Copyright (C) 2009 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef AMAZONCONFIG_H #define AMAZONCONFIG_H #include "serverimporterconfig.h" /** * Amazon configuration. */ class AmazonConfig : public StoredConfig { public: /** * Constructor. */ AmazonConfig(); /** * Destructor. */ virtual ~AmazonConfig(); /** Index in configuration storage */ static int s_index; }; #endif // AMAZONCONFIG_H kid3-3.0.2/src/plugins/amazonimport/amazonimporter.cpp000066400000000000000000000414351224603511300231150ustar00rootroot00000000000000/** * \file amazonimporter.cpp * Amazon database importer. * * \b Project: Kid3 * \author Urs Fleisch * \date 13 Dec 2009 * * Copyright (C) 2009-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "amazonimporter.h" #include #include #include "trackdatamodel.h" #include "amazonconfig.h" /** * Constructor. * * @param netMgr network access manager * @param trackDataModel track data to be filled with imported values */ AmazonImporter::AmazonImporter( QNetworkAccessManager* netMgr, TrackDataModel* trackDataModel) : ServerImporter(netMgr, trackDataModel) { setObjectName(QLatin1String("AmazonImporter")); } /** * Destructor. */ AmazonImporter::~AmazonImporter() { } /** * Name of import source. * @return name. */ const char* AmazonImporter::name() const { return QT_TRANSLATE_NOOP("@default", "Amazon"); } /** NULL-terminated array of server strings, 0 if not used */ const char** AmazonImporter::serverList() const { static const char* servers[] = { // Parsing only works with English text "www.amazon.com:80", "www.amazon.co.uk:80", 0 // end of StrList }; return servers; } /** default server, 0 to disable */ const char* AmazonImporter::defaultServer() const { return "www.amazon.com:80"; } /** anchor to online help, 0 to disable */ const char* AmazonImporter::helpAnchor() const { return "import-amazon"; } /** configuration, 0 if not used */ ServerImporterConfig* AmazonImporter::config() const { return &AmazonConfig::instance(); } /** additional tags option, false if not used */ bool AmazonImporter::additionalTags() const { return true; } /** * Process finished findCddbAlbum request. * * @param searchStr search data received */ void AmazonImporter::parseFindResults(const QByteArray& searchStr) { /* products have the following format: The Avenger by Amon Amarth and Amon Amarth */ QString str = QString::fromLatin1(searchStr); QRegExp catIdTitleArtistRe(QLatin1String( "" "([^<]+)<.*>\\s*by\\s*(?:<[^>]+>)?([^<]+)<")); QStringList lines = str.remove(QLatin1Char('\r')).split(QRegExp(QLatin1String("\\n{2,}"))); m_albumListModel->clear(); for (QStringList::const_iterator it = lines.begin(); it != lines.end(); ++it) { QString line(*it); line.remove(QLatin1Char('\n')); if (catIdTitleArtistRe.indexIn(line) != -1) { m_albumListModel->appendRow(new AlbumListItem( removeHtml(catIdTitleArtistRe.cap(4)) + QLatin1String(" - ") + removeHtml(catIdTitleArtistRe.cap(3)), catIdTitleArtistRe.cap(1), catIdTitleArtistRe.cap(2))); } } } /** * Parse result of album request and populate m_trackDataModel with results. * * @param albumStr album data received */ void AmazonImporter::parseAlbumResults(const QByteArray& albumStr) { /* title (empty lines removed): details (empty lines removed):
*/ QString str = QString::fromLatin1(albumStr); FrameCollection framesHdr; const bool standardTags = getStandardTags(); // search for 'id="btAsinTitle"', text after '>' until ' [' or '<' => album int end = 0; int start = str.indexOf(QLatin1String("id=\"btAsinTitle\"")); if (start >= 0 && standardTags) { start = str.indexOf(QLatin1Char('>'), start); if (start >= 0) { end = str.indexOf(QLatin1Char('<'), start); if (end > start) { int bracketPos = str.indexOf(QLatin1String(" ["), start); if (bracketPos >= 0 && bracketPos < end) { end = bracketPos; } framesHdr.setAlbum( replaceHtmlEntities(str.mid(start + 1, end - start - 1))); // next '' until '<' => artist start = str.indexOf(QLatin1String("Product Details<")); if (start >= 0) { int detailStart = str.indexOf(QLatin1String(">Original Release Date:<"), start); if (detailStart < 0) { detailStart = str.indexOf(QLatin1String(">Audio CD<"), start); } if (detailStart >= 0 && standardTags) { int detailEnd = str.indexOf(QLatin1Char('\n'), detailStart + 10); if (detailEnd > detailStart + 10) { QRegExp yearRe(QLatin1String("(\\d{4})")); if (yearRe.indexIn( str.mid(detailStart + 10, detailEnd - detailStart - 11)) >= 0) { framesHdr.setYear(yearRe.cap(1).toInt()); } } } if (additionalTags) { detailStart = str.indexOf(QLatin1String(">Label:<"), start); if (detailStart > 0) { int detailEnd = str.indexOf(QLatin1Char('\n'), detailStart + 8); if (detailEnd > detailStart + 8) { QRegExp labelRe(QLatin1String(">\\s*([^<]+)<")); if (labelRe.indexIn( str.mid(detailStart + 8, detailEnd - detailStart - 9)) >= 0) { framesHdr.setValue(Frame::FT_Publisher, removeHtml(labelRe.cap(1))); } } } detailStart = str.indexOf(QLatin1String(">Performer:<"), start); if (detailStart > 0) { int detailEnd = str.indexOf(QLatin1String(""), detailStart + 12); if (detailEnd > detailStart + 12) { framesHdr.setValue( Frame::FT_Performer, removeHtml(str.mid(detailStart + 11, detailEnd - detailStart - 11))); } } detailStart = str.indexOf(QLatin1String(">Orchestra:<"), start); if (detailStart > 0) { int detailEnd = str.indexOf(QLatin1String(""), detailStart + 12); if (detailEnd > detailStart + 12) { albumArtist = removeHtml(str.mid(detailStart + 11, detailEnd - detailStart - 11)); } } detailStart = str.indexOf(QLatin1String(">Conductor:<"), start); if (detailStart > 0) { int detailEnd = str.indexOf(QLatin1String(""), detailStart + 12); if (detailEnd > detailStart + 12) { framesHdr.setValue( Frame::FT_Conductor, removeHtml(str.mid(detailStart + 11, detailEnd - detailStart - 11))); } } detailStart = str.indexOf(QLatin1String(">Composer:<"), start); if (detailStart > 0) { int detailEnd = str.indexOf(QLatin1String(""), detailStart + 11); if (detailEnd > detailStart + 11) { framesHdr.setValue( Frame::FT_Composer, removeHtml(str.mid(detailStart + 10, detailEnd - detailStart - 10))); } } } } ImportTrackDataVector trackDataVector(m_trackDataModel->getTrackData()); trackDataVector.setCoverArtUrl(QString()); if (getCoverArt()) { // start = str.indexOf(QLatin1String("id=\"ASIN\"")); if (start > 0) { start = str.indexOf(QLatin1String("value=\""), start); if (start > 0) { end = str.indexOf(QLatin1Char('"'), start + 7); if (end > start) { trackDataVector.setCoverArtUrl( QLatin1String("http://www.amazon.com/dp/") + str.mid(start + 7, end - start - 7)); } } } } bool hasTitleCol = false; bool hasArtist = str.indexOf(QLatin1String("")) != -1; // search 'class="titleCol"', next '' until '<' // => title // if not found: alternatively look for 'class="listRow' start = str.indexOf(QLatin1String("class=\"titleCol\"")); if (start >= 0) { hasTitleCol = true; } else { start = str.indexOf(QLatin1String("class=\"listRow")); } if (start >= 0) { QRegExp durationRe(QLatin1String("(\\d+):(\\d+)")); QRegExp nrTitleRe(QLatin1String("\\s*\\d+\\.\\s+(.*\\S)")); FrameCollection frames(framesHdr); ImportTrackDataVector::iterator it = trackDataVector.begin(); bool atTrackDataListEnd = (it == trackDataVector.end()); int trackNr = 1; while (start >= 0) { QString title; QString artist; int duration = 0; if (hasTitleCol) { end = str.indexOf(QLatin1Char('\n'), start); if (end > start) { QString line = str.mid(start, end - start); int titleStart = line.indexOf(QLatin1String("= 0) { artistStart = line.indexOf(QLatin1String("' until '<' => duration int runtimeStart = line.indexOf(QLatin1String("class=\"runtimeCol\""), titleEnd); if (runtimeStart >= 0) { runtimeStart = line.indexOf(QLatin1Char('>'), runtimeStart + 18); if (runtimeStart >= 0) { int runtimeEnd = line.indexOf(QLatin1Char('<'), runtimeStart); if (runtimeEnd > runtimeStart) { if (durationRe.indexIn( line.mid(runtimeStart + 1, runtimeEnd - runtimeStart - 1)) >= 0) { duration = durationRe.cap(1).toInt() * 60 + durationRe.cap(2).toInt(); } } } } start = str.indexOf(QLatin1String("class=\"titleCol\""), end); } else { start = -1; } } } } } else { // 'class="listRow' found start = str.indexOf(QLatin1String(""), start); if (end > start && nrTitleRe.indexIn(str.mid(start + 4, end - start - 4)) >= 0) { title = nrTitleRe.cap(1); start = str.indexOf(QLatin1String("class=\"listRow"), end); } else { start = -1; } } } if (!title.isEmpty()) { if (standardTags) { frames.setTitle(replaceHtmlEntities(title)); if (!artist.isEmpty()) { frames.setArtist(replaceHtmlEntities(artist)); } frames.setTrack(trackNr); } if (!albumArtist.isEmpty() && additionalTags) { frames.setValue(Frame::FT_AlbumArtist, albumArtist); } if (atTrackDataListEnd) { ImportTrackData trackData; trackData.setFrameCollection(frames); trackData.setImportDuration(duration); trackDataVector.push_back(trackData); } else { while (!atTrackDataListEnd && !it->isEnabled()) { ++it; atTrackDataListEnd = (it == trackDataVector.end()); } if (!atTrackDataListEnd) { (*it).setFrameCollection(frames); (*it).setImportDuration(duration); ++it; atTrackDataListEnd = (it == trackDataVector.end()); } } ++trackNr; frames = framesHdr; } } // handle redundant tracks frames.clear(); while (!atTrackDataListEnd) { if (it->isEnabled()) { if ((*it).getFileDuration() == 0) { it = trackDataVector.erase(it); } else { (*it).setFrameCollection(frames); (*it).setImportDuration(0); ++it; } } else { ++it; } atTrackDataListEnd = (it == trackDataVector.end()); } } else if (!framesHdr.empty()) { // if there are no track data, fill frame header data for (ImportTrackDataVector::iterator it = trackDataVector.begin(); it != trackDataVector.end(); ++it) { if (it->isEnabled()) { (*it).setFrameCollection(framesHdr); } } } m_trackDataModel->setTrackData(trackDataVector); } /** * Send a query command to search on the server. * * @param cfg import source configuration * @param artist artist to search * @param album album to search */ void AmazonImporter::sendFindQuery( const ServerImporterConfig* cfg, const QString& artist, const QString& album) { /* * Query looks like this: * http://www.amazon.com/gp/search/ref=sr_adv_m_pop/?search-alias=popular&field-artist=amon+amarth&field-title=the+avenger */ sendRequest(cfg->m_server, QLatin1String("/gp/search/ref=sr_adv_m_pop/" "?search-alias=popular&field-artist=") + encodeUrlQuery(artist) + QLatin1String("&field-title=") + encodeUrlQuery(album)); } /** * Send a query command to fetch the track list * from the server. * * @param cfg import source configuration * @param cat category * @param id ID */ void AmazonImporter::sendTrackListQuery( const ServerImporterConfig* cfg, const QString& cat, const QString& id) { /* * Query looks like this: * http://www.amazon.com/dp/B001VROVHO */ sendRequest(cfg->m_server, QLatin1Char('/') + cat + QLatin1Char('/') + id); } kid3-3.0.2/src/plugins/amazonimport/amazonimporter.h000066400000000000000000000054511224603511300225600ustar00rootroot00000000000000/** * \file amazonimporter.h * Amazon database importer. * * \b Project: Kid3 * \author Urs Fleisch * \date 13 Dec 2009 * * Copyright (C) 2009-2011 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef AMAZONIMPORTER_H #define AMAZONIMPORTER_H #include "serverimporter.h" /** * Amazon importer. */ class AmazonImporter : public ServerImporter { public: /** * Constructor. * * @param netMgr network access manager * @param trackDataModel track data to be filled with imported values */ AmazonImporter(QNetworkAccessManager* netMgr, TrackDataModel *trackDataModel); /** * Destructor. */ virtual ~AmazonImporter(); /** * Name of import source. * @return name. */ virtual const char* name() const; /** NULL-terminated array of server strings, 0 if not used */ virtual const char** serverList() const; /** default server, 0 to disable */ virtual const char* defaultServer() const; /** anchor to online help, 0 to disable */ virtual const char* helpAnchor() const; /** configuration, 0 if not used */ virtual ServerImporterConfig* config() const; /** additional tags option, false if not used */ virtual bool additionalTags() const; /** * Process finished findCddbAlbum request. * * @param searchStr search data received */ virtual void parseFindResults(const QByteArray& searchStr); /** * Parse result of album request and populate m_trackDataModel with results. * * @param albumStr album data received */ virtual void parseAlbumResults(const QByteArray& albumStr); /** * Send a query command to search on the server. * * @param cfg import source configuration * @param artist artist to search * @param album album to search */ virtual void sendFindQuery( const ServerImporterConfig* cfg, const QString& artist, const QString& album); /** * Send a query command to fetch the track list * from the server. * * @param cfg import source configuration * @param cat category * @param id ID */ virtual void sendTrackListQuery( const ServerImporterConfig* cfg, const QString& cat, const QString& id); }; #endif kid3-3.0.2/src/plugins/amazonimport/amazonimportplugin.cpp000066400000000000000000000037651224603511300240110ustar00rootroot00000000000000/** * \file amazonimportplugin.cpp * Amazon importer plug-in. * * \b Project: Kid3 * \author Urs Fleisch * \date 13 Jul 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "amazonimportplugin.h" #include "amazonimporter.h" #if QT_VERSION < 0x050000 Q_EXPORT_PLUGIN2(AmazonImportPlugin, AmazonImportPlugin) #endif static const QLatin1String IMPORTER_NAME("AmazonImport"); /*! * Constructor. * @param parent parent object */ AmazonImportPlugin::AmazonImportPlugin(QObject* parent) : QObject(parent) { setObjectName(QLatin1String("AmazonImport")); } /** * Destructor. */ AmazonImportPlugin::~AmazonImportPlugin() { } /** * Get keys of available server importers. * @return list of keys. */ QStringList AmazonImportPlugin::serverImporterKeys() const { return QStringList() << IMPORTER_NAME; } /** * Create server importer. * @param key server importer key * @param netMgr network access manager * @param trackDataModel track data to be filled with imported values * @return server importer instance, 0 if key unknown. * @remarks The caller takes ownership of the returned instance. */ ServerImporter* AmazonImportPlugin::createServerImporter( const QString& key, QNetworkAccessManager* netMgr, TrackDataModel* trackDataModel) { if (key == IMPORTER_NAME) { return new AmazonImporter(netMgr, trackDataModel); } return 0; } kid3-3.0.2/src/plugins/amazonimport/amazonimportplugin.h000066400000000000000000000040301224603511300234400ustar00rootroot00000000000000/** * \file amazonimportplugin.h * Amazon importer plug-in. * * \b Project: Kid3 * \author Urs Fleisch * \date 13 Jul 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef AMAZONIMPORTPLUGIN_H #define AMAZONIMPORTPLUGIN_H #include #include "iserverimporterfactory.h" /** * Interface for server importer factory. */ class KID3_PLUGIN_EXPORT AmazonImportPlugin : public QObject, public IServerImporterFactory { Q_OBJECT #if QT_VERSION >= 0x050000 Q_PLUGIN_METADATA(IID "net.sourceforge.kid3.IServerImporterFactory") #endif Q_INTERFACES(IServerImporterFactory) public: /*! * Constructor. * @param parent parent object */ explicit AmazonImportPlugin(QObject* parent = 0); /** * Destructor. */ virtual ~AmazonImportPlugin(); /** * Get keys of available server importers. * @return list of keys. */ virtual QStringList serverImporterKeys() const; /** * Create server importer. * @param key server importer key * @param netMgr network access manager * @param trackDataModel track data to be filled with imported values * @return server importer instance, 0 if key unknown. * @remarks The caller takes ownership of the returned instance. */ virtual ServerImporter* createServerImporter( const QString& key, QNetworkAccessManager* netMgr, TrackDataModel* trackDataModel); }; #endif // AMAZONIMPORTPLUGIN_H kid3-3.0.2/src/plugins/discogsimport/000077500000000000000000000000001224603511300175015ustar00rootroot00000000000000kid3-3.0.2/src/plugins/discogsimport/CMakeLists.txt000066400000000000000000000010351224603511300222400ustar00rootroot00000000000000set(plugin_SRCS discogsimportplugin.cpp discogsimporter.cpp discogsconfig.cpp ) set(plugin_MOC_HDRS discogsimportplugin.h ) set(plugin_NAME DiscogsImport) if (WITH_GCC_PCH) add_definitions(${GCC_PCH_COMPILE_FLAGS}) endif (WITH_GCC_PCH) string(TOLOWER ${plugin_NAME} plugin_TARGET) qt4_wrap_cpp(plugin_GEN_MOC_SRCS ${plugin_MOC_HDRS}) add_library(${plugin_TARGET} ${plugin_SRCS} ${plugin_GEN_MOC_SRCS}) target_link_libraries(${plugin_TARGET} kid3-core ${BASIC_LIBRARIES}) INSTALL_KID3_PLUGIN(${plugin_TARGET} ${plugin_NAME}) kid3-3.0.2/src/plugins/discogsimport/discogsconfig.cpp000066400000000000000000000023071224603511300230300ustar00rootroot00000000000000/** * \file discogsconfig.cpp * Discogs configuration. * * \b Project: Kid3 * \author Urs Fleisch * \date 13 Sep 2005 * * Copyright (C) 2005-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "discogsconfig.h" #include int DiscogsConfig::s_index = -1; /** * Constructor. */ DiscogsConfig::DiscogsConfig() : StoredConfig(QLatin1String("Discogs")) { m_cgiPathUsed = false; m_additionalTagsUsed = true; m_server = QLatin1String("www.discogs.com:80"); } /** * Destructor. */ DiscogsConfig::~DiscogsConfig() {} kid3-3.0.2/src/plugins/discogsimport/discogsconfig.h000066400000000000000000000023331224603511300224740ustar00rootroot00000000000000/** * \file discogsconfig.h * Discogs configuration. * * \b Project: Kid3 * \author Urs Fleisch * \date 13 Sep 2005 * * Copyright (C) 2005-2007 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef DISCOGSCONFIG_H #define DISCOGSCONFIG_H #include "serverimporterconfig.h" /** * Discogs configuration. */ class DiscogsConfig : public StoredConfig { public: /** * Constructor. */ DiscogsConfig(); /** * Destructor. */ virtual ~DiscogsConfig(); /** Index in configuration storage */ static int s_index; }; #endif // DISCOGSCONFIG_H kid3-3.0.2/src/plugins/discogsimport/discogsimporter.cpp000066400000000000000000000461201224603511300234250ustar00rootroot00000000000000/** * \file discogsimporter.cpp * Discogs importer. * * \b Project: Kid3 * \author Urs Fleisch * \date 13 Oct 2006 * * Copyright (C) 2006-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "discogsimporter.h" #include #include "serverimporterconfig.h" #include "trackdatamodel.h" #include "discogsconfig.h" #include "config.h" #include "genres.h" #include "jsonparser.h" /** * Stores information about extra artists. * The information can be used to add frames to the appropriate tracks. */ class ExtraArtist { public: /** * Constructor. * @param varMap variant map containing extra artist information */ explicit ExtraArtist(const QVariantMap& varMap); /** * Add extra artist information to frames. * @param frames frame collection * @param trackPos optional position, the extra artist information will * only be added if this track position is listed in the * track restrictions or is empty */ void addToFrames(FrameCollection& frames, const QString& trackPos = QString()) const; /** * Check if extra artist information is only valid for a subset of the tracks. * @return true if extra artist has track restriction. */ bool hasTrackRestriction() const { return !m_tracks.isEmpty(); } private: QString m_name; QString m_role; QStringList m_tracks; }; namespace { const char discogsServer[] = "api.discogs.com:80"; /** * Replace unicode escape sequences (e.g. "\u2022") by unicode characters. * @param str string containing unicode escape sequences * @return string with replaced unicode escape sequences. */ QString replaceEscapedUnicodeCharacters(QString str) { QRegExp unicodeRe(QLatin1String("\\\\u([0-9a-fA-F]{4})")); int offset = 0; while (offset >= 0) { offset = unicodeRe.indexIn(str, offset); if (offset >= 0) { str.replace(offset, unicodeRe.matchedLength(), QChar(unicodeRe.cap(1).toUInt(0, 16))); ++offset; } } return str; } /** * Remove trailing stars and numbers like (2) from a string. * * @param str string * * @return fixed up string. */ QString fixUpArtist(QString str) { str.remove(QRegExp(QLatin1String("[*\\s]*\\(\\d+\\)"))); str.replace(QRegExp(QLatin1String("\\*($| - |, | / )")), QLatin1String("\\1")); return str; } /** * Create a string with artists contained in an artist list. * @param artists list containing artist maps * @return string with artists joined appropriately. */ QString getArtistString(const QVariantList& artists) { QString artist; if (!artists.isEmpty()) { QString join; foreach (const QVariant& var, artists) { QVariantMap varMap = var.toMap(); if (!artist.isEmpty()) { artist += join; } artist += fixUpArtist(varMap.value(QLatin1String("name")).toString()); join = varMap.value(QLatin1String("join")).toString(); if (join.isEmpty() || join == QLatin1String(",")) { join = QLatin1String(", "); } else { join = QLatin1Char(' ') + join + QLatin1Char(' '); } } } return artist; } /** * Add involved people to a frame. * The format used is (should be converted according to tag specifications): * involvee 1 (involvement 1)\n * involvee 2 (involvement 2)\n * ... * involvee n (involvement n) * * @param frames frame collection * @param type type of frame * @param involvement involvement (e.g. instrument) * @param involvee name of involvee (e.g. musician) */ void addInvolvedPeople( FrameCollection& frames, Frame::Type type, const QString& involvement, const QString& involvee) { QString value = frames.getValue(type); if (!value.isEmpty()) value += Frame::stringListSeparator(); value += involvement; value += Frame::stringListSeparator(); value += involvee; frames.setValue(type, value); } /** * Add name to frame with credits. * @param frames frame collection * @param type type of frame * @param name name of person to credit */ void addCredit(FrameCollection& frames, Frame::Type type, const QString& name) { QString value = frames.getValue(type); if (!value.isEmpty()) value += QLatin1String(", "); value += name; frames.setValue(type, value); } } /** * Constructor. * @param varMap variant map containing extra artist information */ ExtraArtist::ExtraArtist(const QVariantMap& varMap) : m_name(fixUpArtist(varMap.value(QLatin1String("name")).toString())), m_role(varMap.value(QLatin1String("role")).toString()) { static const QRegExp tracksSepRe(QLatin1String(",\\s*")); QString tracks = varMap.value(QLatin1String("tracks")).toString(); if (!tracks.isEmpty()) { m_tracks = tracks.split(tracksSepRe); } } /** * Add extra artist information to frames. * @param frames frame collection * @param trackPos optional position, the extra artist information will * only be added if this track position is listed in the * track restrictions or is empty */ void ExtraArtist::addToFrames(FrameCollection& frames, const QString& trackPos) const { if (!trackPos.isEmpty() && !m_tracks.contains(trackPos)) return; if (m_role.contains(QLatin1String("Composed By")) || m_role.contains(QLatin1String("Music By")) || m_role.contains(QLatin1String("Songwriter"))) { addCredit(frames, Frame::FT_Composer, m_name); } if (m_role.contains(QLatin1String("Written-By")) || m_role.contains(QLatin1String("Written By"))) { addCredit(frames, Frame::FT_Author, m_name); } if (m_role.contains(QLatin1String("Lyrics By"))) { addCredit(frames, Frame::FT_Lyricist, m_name); } if (m_role.contains(QLatin1String("Conductor"))) { addCredit(frames, Frame::FT_Conductor, m_name); } if (m_role.contains(QLatin1String("Orchestra"))) { addCredit(frames, Frame::FT_AlbumArtist, m_name); } if (m_role.contains(QLatin1String("Remix"))) { addCredit(frames, Frame::FT_Remixer, m_name); } if (m_role.contains(QLatin1String("Arranged By"))) { addInvolvedPeople(frames, Frame::FT_Arranger, QLatin1String("Arranger"), m_name); } if (m_role.contains(QLatin1String("Mixed By"))) { addInvolvedPeople(frames, Frame::FT_Arranger, QLatin1String("Mixer"), m_name); } if (m_role.contains(QLatin1String("DJ Mix")) || m_role.contains(QLatin1String("Dj Mix"))) { addInvolvedPeople(frames, Frame::FT_Arranger, QLatin1String("DJMixer"), m_name); } if (m_role.contains(QLatin1String("Engineer")) || m_role.contains(QLatin1String("Mastered By"))) { addInvolvedPeople(frames, Frame::FT_Arranger, QLatin1String("Engineer"), m_name); } if (m_role.contains(QLatin1String("Producer")) || m_role.contains(QLatin1String("Co-producer")) || m_role.contains(QLatin1String("Executive Producer"))) { addInvolvedPeople(frames, Frame::FT_Arranger, QLatin1String("Producer"), m_name); } static const char* const instruments[] = { "Performer", "Vocals", "Voice", "Featuring", "Choir", "Chorus", "Baritone", "Tenor", "Rap", "Scratches", "Drums", "Percussion", "Keyboards", "Cello", "Piano", "Organ", "Synthesizer", "Keys", "Wurlitzer", "Rhodes", "Harmonica", "Xylophone", "Guitar", "Bass", "Strings", "Violin", "Viola", "Banjo", "Harp", "Mandolin", "Clarinet", "Horn", "Cornet", "Flute", "Oboe", "Saxophone", "Trumpet", "Tuba", "Trombone" }; for (unsigned i = 0; i < sizeof(instruments) / sizeof(instruments[0]); ++i) { if (m_role.contains(QString::fromLatin1(instruments[i]))) { addInvolvedPeople(frames, Frame::FT_Performer, m_role, m_name); break; } } } /** * Constructor. * * @param netMgr network access manager * @param trackDataModel track data to be filled with imported values */ DiscogsImporter::DiscogsImporter(QNetworkAccessManager* netMgr, TrackDataModel* trackDataModel) : ServerImporter(netMgr, trackDataModel) { setObjectName(QLatin1String("DiscogsImporter")); m_discogsHeaders["User-Agent"] = "Kid3/" VERSION " +http://kid3.sourceforge.net"; } /** * Destructor. */ DiscogsImporter::~DiscogsImporter() { } /** * Name of import source. * @return name. */ const char* DiscogsImporter::name() const { return QT_TRANSLATE_NOOP("@default", "Discogs"); } /** anchor to online help, 0 to disable */ const char* DiscogsImporter::helpAnchor() const { return "import-discogs"; } /** configuration, 0 if not used */ ServerImporterConfig* DiscogsImporter::config() const { return &DiscogsConfig::instance(); } /** additional tags option, false if not used */ bool DiscogsImporter::additionalTags() const { return true; } /** * Process finished findCddbAlbum request. * * @param searchStr search data received */ void DiscogsImporter::parseFindResults(const QByteArray& searchStr) { // search results have the format (JSON, simplified): // {"results": [{"style": ["Heavy Metal"], "title": "Wizard (23) - Odin", // "type": "release", "id": 2487778}]} QString str = replaceEscapedUnicodeCharacters(QString::fromUtf8(searchStr)); QVariantMap map = JsonParser::deserialize(str).toMap(); m_albumListModel->clear(); foreach (const QVariant& var, map.value(QLatin1String("results")).toList()) { QVariantMap result = var.toMap(); QString title = fixUpArtist(result.value(QLatin1String("title")).toString()); if (!title.isEmpty()) { m_albumListModel->appendRow(new AlbumListItem( title, QLatin1String("releases"), QString::number(result.value(QLatin1String("id")).toInt()))); } } } /** * Parse result of album request and populate m_trackDataModel with results. * * @param albumStr album data received */ void DiscogsImporter::parseAlbumResults(const QByteArray& albumStr) { // releases have the format (JSON, simplified): // { "styles": ["Heavy Metal"], // "labels": [{"name": "LMP"}], // "year": 2003, // "artists": [{"name": "Wizard (23)"}], // "images": [ // { "uri": "http://api.discogs.com/image/R-2487778-1293847958.jpeg", // "type": "primary" }, // { "uri": "http://api.discogs.com/image/R-2487778-1293847967.jpeg", // "type": "secondary" }], // "id": 2487778, // "genres": ["Rock"], // "thumb": "http://api.discogs.com/image/R-150-2487778-1293847958.jpeg", // "extraartists": [], // "title": "Odin", // "tracklist": [ // {"duration": "5:19", "position": "1", "title": "The Prophecy"}, // {"duration": "", "position": "Video", "title": "Betrayer"} // ], // "released": "2003", // "formats": [{"name": "CD"}] // } QRegExp discTrackPosRe(QLatin1String("(\\d+)-(\\d+)")); QRegExp yearRe(QLatin1String("^\\d{4}-\\d{2}")); QString str = replaceEscapedUnicodeCharacters(QString::fromUtf8(albumStr)); QVariantMap map = JsonParser::deserialize(str).toMap(); QList trackExtraArtists; ImportTrackDataVector trackDataVector(m_trackDataModel->getTrackData()); FrameCollection framesHdr; const bool standardTags = getStandardTags(); if (standardTags) { framesHdr.setAlbum(map.value(QLatin1String("title")).toString()); framesHdr.setArtist(getArtistString(map.value(QLatin1String("artists")).toList())); // The year can be found in "released". QString released(map.value(QLatin1String("released")).toString()); if (yearRe.indexIn(released) == 0) { released.truncate(4); } framesHdr.setYear(released.toInt()); // The genre can be found in "genre" or "style". // All genres found are checked for an ID3v1 number, starting with those // in the style field. QVariantList genreList(map.value(QLatin1String("styles")).toList() + map.value(QLatin1String("genres")).toList()); int genreNum = 255; foreach (const QVariant& var, genreList) { genreNum = Genres::getNumber(var.toString()); if (genreNum != 255) { break; } } if (genreNum != 255) { framesHdr.setGenre(QString::fromLatin1(Genres::getName(genreNum))); } else if (!genreList.isEmpty()) { framesHdr.setGenre(genreList.first().toString()); } } trackDataVector.setCoverArtUrl(QString()); const bool coverArt = getCoverArt(); if (coverArt) { // Cover art can be found in "images" QVariantList images = map.value(QLatin1String("images")).toList(); if (!images.isEmpty()) { trackDataVector.setCoverArtUrl(images.first().toMap().value(QLatin1String("uri")). toString()); } } const bool additionalTags = getAdditionalTags(); if (additionalTags) { // Publisher can be found in "label" QVariantList labels = map.value(QLatin1String("labels")).toList(); if (!labels.isEmpty()) { QVariantMap firstLabelMap = labels.first().toMap(); framesHdr.setValue(Frame::FT_Publisher, fixUpArtist(firstLabelMap.value(QLatin1String("name")).toString())); QString catNo = firstLabelMap.value(QLatin1String("catno")).toString(); if (!catNo.isEmpty() && catNo.toLower() != QLatin1String("none")) { framesHdr.setValue(Frame::FT_CatalogNumber, catNo); } } // Media can be found in "formats" QVariantList formats = map.value(QLatin1String("formats")).toList(); if (!formats.isEmpty()) { framesHdr.setValue(Frame::FT_Media, formats.first().toMap().value(QLatin1String("name")).toString()); } // Credits can be found in "extraartists" QVariantList extraartists = map.value(QLatin1String("extraartists")).toList(); if (!extraartists.isEmpty()) { foreach (const QVariant& var, extraartists) { ExtraArtist extraArtist(var.toMap()); if (extraArtist.hasTrackRestriction()) { trackExtraArtists.append(extraArtist); } else { extraArtist.addToFrames(framesHdr); } } } // Release country can be found in "country" QString country(map.value(QLatin1String("country")).toString()); if (!country.isEmpty()) { framesHdr.setValue(Frame::FT_ReleaseCountry, country); } } FrameCollection frames(framesHdr); ImportTrackDataVector::iterator it = trackDataVector.begin(); bool atTrackDataListEnd = (it == trackDataVector.end()); int trackNr = 1; QVariantList trackList = map.value(QLatin1String("tracklist")).toList(); // Check if all positions are empty. bool allPositionsEmpty = true; foreach (const QVariant& var, trackList) { if (!var.toMap().value(QLatin1String("position")).toString().isEmpty()) { allPositionsEmpty = false; break; } } foreach (const QVariant& var, trackList) { QVariantMap track = var.toMap(); QString position(track.value(QLatin1String("position")).toString()); bool ok; int pos = position.toInt(&ok); if (!ok) { if (discTrackPosRe.exactMatch(position)) { if (additionalTags) { frames.setValue(Frame::FT_Disc, discTrackPosRe.cap(1)); } pos = discTrackPosRe.cap(2).toInt(); } else { pos = trackNr; } } QString title(track.value(QLatin1String("title")).toString()); QStringList durationHms = track.value(QLatin1String("duration")).toString().split(QLatin1Char(':')); int duration = 0; foreach (const QString& var, durationHms) { duration *= 60; duration += var.toInt(); } if (!allPositionsEmpty && position.isEmpty()) { if (additionalTags) { framesHdr.setValue(Frame::FT_Part, title); } } else if (!title.isEmpty() || duration != 0) { if (standardTags) { frames.setTrack(pos); frames.setTitle(title); } QVariantList artists(track.value(QLatin1String("artists")).toList()); if (!artists.isEmpty()) { if (standardTags) { frames.setArtist(getArtistString(artists)); } if (additionalTags) { frames.setValue(Frame::FT_AlbumArtist, framesHdr.getArtist()); } } if (additionalTags) { QVariantList extraartists(track.value(QLatin1String("extraartists")).toList()); if (!extraartists.isEmpty()) { foreach (const QVariant& var, extraartists) { ExtraArtist extraArtist(var.toMap()); extraArtist.addToFrames(frames); } } } foreach (const ExtraArtist& extraArtist, trackExtraArtists) { extraArtist.addToFrames(frames, position); } if (atTrackDataListEnd) { ImportTrackData trackData; trackData.setFrameCollection(frames); trackData.setImportDuration(duration); trackDataVector.append(trackData); } else { while (!atTrackDataListEnd && !it->isEnabled()) { ++it; atTrackDataListEnd = (it == trackDataVector.end()); } if (!atTrackDataListEnd) { (*it).setFrameCollection(frames); (*it).setImportDuration(duration); ++it; atTrackDataListEnd = (it == trackDataVector.end()); } } ++trackNr; } frames = framesHdr; } // handle redundant tracks frames.clear(); while (!atTrackDataListEnd) { if (it->isEnabled()) { if ((*it).getFileDuration() == 0) { it = trackDataVector.erase(it); } else { (*it).setFrameCollection(frames); (*it).setImportDuration(0); ++it; } } else { ++it; } atTrackDataListEnd = (it == trackDataVector.end()); } m_trackDataModel->setTrackData(trackDataVector); } /** * Send a query command to search on the server. * * @param cfg import source configuration * @param artist artist to search * @param album album to search */ void DiscogsImporter::sendFindQuery( const ServerImporterConfig*, const QString& artist, const QString& album) { /* * Query looks like this: * http://api.discogs.com//database/search?type=release&title&q=amon+amarth+avenger */ sendRequest(QString::fromLatin1(discogsServer), QLatin1String("/database/search?type=release&title&q=") + encodeUrlQuery(artist + QLatin1Char(' ') + album), m_discogsHeaders); } /** * Send a query command to fetch the track list * from the server. * * @param cfg import source configuration * @param cat category * @param id ID */ void DiscogsImporter::sendTrackListQuery( const ServerImporterConfig*, const QString& cat, const QString& id) { /* * Query looks like this: * http://api.discogs.com/releases/761529 */ sendRequest(QString::fromLatin1(discogsServer), QLatin1Char('/') + QString::fromLatin1(QUrl::toPercentEncoding(cat)) + QLatin1Char('/') + id, m_discogsHeaders); } kid3-3.0.2/src/plugins/discogsimport/discogsimporter.h000066400000000000000000000052711224603511300230740ustar00rootroot00000000000000/** * \file discogsimporter.h * Discogs importer. * * \b Project: Kid3 * \author Urs Fleisch * \date 13 Oct 2006 * * Copyright (C) 2006-2012 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef DISCOGSIMPORTER_H #define DISCOGSIMPORTER_H #include "serverimporter.h" #include "kid3api.h" /** * Discogs importer. */ class DiscogsImporter : public ServerImporter { public: /** * Constructor. * * @param netMgr network access manager * @param trackDataModel track data to be filled with imported values */ DiscogsImporter(QNetworkAccessManager* netMgr, TrackDataModel *trackDataModel); /** * Destructor. */ virtual ~DiscogsImporter(); /** * Name of import source. * @return name. */ virtual const char* name() const; /** anchor to online help, 0 to disable */ virtual const char* helpAnchor() const; /** configuration, 0 if not used */ virtual ServerImporterConfig* config() const; /** additional tags option, false if not used */ virtual bool additionalTags() const; /** * Process finished findCddbAlbum request. * * @param searchStr search data received */ virtual void parseFindResults(const QByteArray& searchStr); /** * Parse result of album request and populate m_trackDataModel with results. * * @param albumStr album data received */ virtual void parseAlbumResults(const QByteArray& albumStr); /** * Send a query command to search on the server. * * @param cfg import source configuration * @param artist artist to search * @param album album to search */ virtual void sendFindQuery( const ServerImporterConfig* cfg, const QString& artist, const QString& album); /** * Send a query command to fetch the track list * from the server. * * @param cfg import source configuration * @param cat category * @param id ID */ virtual void sendTrackListQuery( const ServerImporterConfig* cfg, const QString& cat, const QString& id); private: QMap m_discogsHeaders; }; #endif kid3-3.0.2/src/plugins/discogsimport/discogsimportplugin.cpp000066400000000000000000000040011224603511300243050ustar00rootroot00000000000000/** * \file discogsimportplugin.cpp * Discogs import plugin. * * \b Project: Kid3 * \author Urs Fleisch * \date 13 Jul 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "discogsimportplugin.h" #include "discogsimporter.h" #if QT_VERSION < 0x050000 Q_EXPORT_PLUGIN2(DiscogsImportPlugin, DiscogsImportPlugin) #endif static const QLatin1String IMPORTER_NAME("DiscogsImport"); /*! * Constructor. * @param parent parent object */ DiscogsImportPlugin::DiscogsImportPlugin(QObject* parent) : QObject(parent) { setObjectName(QLatin1String("DiscogsImport")); } /** * Destructor. */ DiscogsImportPlugin::~DiscogsImportPlugin() { } /** * Get keys of available server importers. * @return list of keys. */ QStringList DiscogsImportPlugin::serverImporterKeys() const { return QStringList() << IMPORTER_NAME; } /** * Create server importer. * @param key server importer key * @param netMgr network access manager * @param trackDataModel track data to be filled with imported values * @return server importer instance, 0 if key unknown. * @remarks The caller takes ownership of the returned instance. */ ServerImporter* DiscogsImportPlugin::createServerImporter( const QString& key, QNetworkAccessManager* netMgr, TrackDataModel* trackDataModel) { if (key == IMPORTER_NAME) { return new DiscogsImporter(netMgr, trackDataModel); } return 0; } kid3-3.0.2/src/plugins/discogsimport/discogsimportplugin.h000066400000000000000000000040351224603511300237610ustar00rootroot00000000000000/** * \file discogsimportplugin.h * Discogs import plugin. * * \b Project: Kid3 * \author Urs Fleisch * \date 13 Jul 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef DISCOGSIMPORTPLUGIN_H #define DISCOGSIMPORTPLUGIN_H #include #include "iserverimporterfactory.h" /** * Interface for server importer factory. */ class KID3_PLUGIN_EXPORT DiscogsImportPlugin : public QObject, public IServerImporterFactory { Q_OBJECT #if QT_VERSION >= 0x050000 Q_PLUGIN_METADATA(IID "net.sourceforge.kid3.IServerImporterFactory") #endif Q_INTERFACES(IServerImporterFactory) public: /*! * Constructor. * @param parent parent object */ explicit DiscogsImportPlugin(QObject* parent = 0); /** * Destructor. */ virtual ~DiscogsImportPlugin(); /** * Get keys of available server importers. * @return list of keys. */ virtual QStringList serverImporterKeys() const; /** * Create server importer. * @param key server importer key * @param netMgr network access manager * @param trackDataModel track data to be filled with imported values * @return server importer instance, 0 if key unknown. * @remarks The caller takes ownership of the returned instance. */ virtual ServerImporter* createServerImporter( const QString& key, QNetworkAccessManager* netMgr, TrackDataModel* trackDataModel); }; #endif // DISCOGSIMPORTPLUGIN_H kid3-3.0.2/src/plugins/freedbimport/000077500000000000000000000000001224603511300172755ustar00rootroot00000000000000kid3-3.0.2/src/plugins/freedbimport/CMakeLists.txt000066400000000000000000000010601224603511300220320ustar00rootroot00000000000000set(plugin_SRCS freedbimportplugin.cpp freedbimporter.cpp tracktypeimporter.cpp freedbconfig.cpp ) set(plugin_MOC_HDRS freedbimportplugin.h ) set(plugin_NAME FreedbImport) if (WITH_GCC_PCH) add_definitions(${GCC_PCH_COMPILE_FLAGS}) endif (WITH_GCC_PCH) string(TOLOWER ${plugin_NAME} plugin_TARGET) qt4_wrap_cpp(plugin_GEN_MOC_SRCS ${plugin_MOC_HDRS}) add_library(${plugin_TARGET} ${plugin_SRCS} ${plugin_GEN_MOC_SRCS}) target_link_libraries(${plugin_TARGET} kid3-core ${BASIC_LIBRARIES}) INSTALL_KID3_PLUGIN(${plugin_TARGET} ${plugin_NAME}) kid3-3.0.2/src/plugins/freedbimport/freedbconfig.cpp000066400000000000000000000041501224603511300224160ustar00rootroot00000000000000/** * \file freedbconfig.cpp * freedb.org import configuration. * * \b Project: Kid3 * \author Urs Fleisch * \date 17 Sep 2003 * * Copyright (C) 2003-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "freedbconfig.h" class QString; int FreedbConfig::s_index = -1; /** * Constructor. * Set default configuration. * * @param grp configuration group */ FreedbConfig::FreedbConfig(const QString& grp) : StoredConfig(grp) { m_server = QLatin1String("gnudb.gnudb.org:80"); m_cgiPath = QLatin1String("/~cddb/cddb.cgi"); } /** * Destructor. */ FreedbConfig::~FreedbConfig() {} /** * Read persisted configuration. * * @param config KDE configuration */ void FreedbConfig::readFromConfig(ISettings* config) { ServerImporterConfig::readFromConfig(config); if (m_server == QLatin1String("freedb2.org:80")) { m_server = QLatin1String("www.gnudb.org:80"); // replace old default } } int TrackTypeConfig::s_index = -1; /** * Constructor. */ TrackTypeConfig::TrackTypeConfig() : StoredConfig(QLatin1String("TrackType")) { } /** * Destructor. */ TrackTypeConfig::~TrackTypeConfig() { } /** * Read persisted configuration. * * @param config KDE configuration */ void TrackTypeConfig::readFromConfig(ISettings* config) { ServerImporterConfig::readFromConfig(config); if (m_server == QLatin1String("gnudb.gnudb.org:80")) { m_server = QLatin1String("tracktype.org:80"); // replace default } } kid3-3.0.2/src/plugins/freedbimport/freedbconfig.h000066400000000000000000000036531224603511300220720ustar00rootroot00000000000000/** * \file freedbconfig.h * Freedb configuration. * * \b Project: Kid3 * \author Urs Fleisch * \date 18 Jan 2004 * * Copyright (C) 2004-2007 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef FREEDBCONFIG_H #define FREEDBCONFIG_H #include "serverimporterconfig.h" /** * Freedb configuration. */ class FreedbConfig : public StoredConfig { public: /** * Constructor. * Set default configuration. * * @param grp configuration group */ explicit FreedbConfig(const QString& grp = QLatin1String("Freedb")); /** * Destructor. */ virtual ~FreedbConfig(); /** * Read persisted configuration. * * @param config KDE configuration */ virtual void readFromConfig(ISettings* config); /** Index in configuration storage */ static int s_index; }; /** * FreedbConfig subclass for stored TrackType configuration instance. */ class TrackTypeConfig : public StoredConfig { public: /** * Constructor. */ TrackTypeConfig(); /** * Destructor. */ virtual ~TrackTypeConfig(); /** * Read persisted configuration. * * @param config KDE configuration */ virtual void readFromConfig(ISettings* config); /** Index in configuration storage */ static int s_index; }; #endif kid3-3.0.2/src/plugins/freedbimport/freedbimporter.cpp000066400000000000000000000232401224603511300230130ustar00rootroot00000000000000/** * \file freedbimporter.cpp * freedb.org importer. * * \b Project: Kid3 * \author Urs Fleisch * \date 18 Jan 2004 * * Copyright (C) 2004-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "freedbimporter.h" #include "serverimporterconfig.h" #include "trackdatamodel.h" #include "freedbconfig.h" #include "config.h" #include "genres.h" static const char gnudbServer[] = "www.gnudb.org:80"; /** * Constructor. * * @param netMgr network access manager * @param trackDataModel track data to be filled with imported values */ FreedbImporter::FreedbImporter(QNetworkAccessManager* netMgr, TrackDataModel *trackDataModel) : ServerImporter(netMgr, trackDataModel) { setObjectName(QLatin1String("FreedbImporter")); } /** * Destructor. */ FreedbImporter::~FreedbImporter() { } /** * Name of import source. * @return name. */ const char* FreedbImporter::name() const { return QT_TRANSLATE_NOOP("@default", "gnudb.org"); } /** NULL-terminated array of server strings, 0 if not used */ const char** FreedbImporter::serverList() const { static const char* servers[] = { "www.gnudb.org:80", "gnudb.gnudb.org:80", "freedb.org:80", "freedb.freedb.org:80", "at.freedb.org:80", "au.freedb.org:80", "ca.freedb.org:80", "es.freedb.org:80", "fi.freedb.org:80", "lu.freedb.org:80", "ru.freedb.org:80", "uk.freedb.org:80", "us.freedb.org:80", 0 // end of StrList }; return servers; } /** default server, 0 to disable */ const char* FreedbImporter::defaultServer() const { return "www.gnudb.org:80"; } /** default CGI path, 0 to disable */ const char* FreedbImporter::defaultCgiPath() const { return "/~cddb/cddb.cgi"; } /** anchor to online help, 0 to disable */ const char* FreedbImporter::helpAnchor() const { return "import-freedb"; } /** configuration, 0 if not used */ ServerImporterConfig* FreedbImporter::config() const { return &FreedbConfig::instance(); } /** * Process finished findCddbAlbum request. * * @param searchStr search data received */ void FreedbImporter::parseFindResults(const QByteArray& searchStr) { /*

Search Results, 1 albums found:



Catharsis / Imago
Tracks: 12, total time: 49:07, year: 2002, genre: Metal
Discid: rock / 920b810c
*/ bool isUtf8 = false; int charSetPos = searchStr.indexOf("charset="); if (charSetPos != -1) { charSetPos += 8; QByteArray charset(searchStr.mid(charSetPos, 5)); isUtf8 = charset == "utf-8" || charset == "UTF-8"; } QString str = isUtf8 ? QString::fromUtf8(searchStr) : QString::fromLatin1(searchStr); QRegExp titleRe(QLatin1String("([^<]+)")); QRegExp catIdRe(QLatin1String("Discid: ([a-z]+)[\\s/]+([0-9a-f]+)")); QStringList lines = str.split(QRegExp(QLatin1String("[\\r\\n]+"))); QString title; bool inEntries = false; m_albumListModel->clear(); for (QStringList::const_iterator it = lines.begin(); it != lines.end(); ++it) { if (inEntries) { if (titleRe.indexIn(*it) != -1) { title = titleRe.cap(1); } if (catIdRe.indexIn(*it) != -1) { m_albumListModel->appendRow(new AlbumListItem( title, catIdRe.cap(1), catIdRe.cap(2))); } } else if ((*it).indexOf(QLatin1String(" albums found:")) != -1) { inEntries = true; } } } /** * Parse the track durations from freedb.org. * * @param text text buffer containing data from freedb.org * @param trackDuration list for results */ static void parseFreedbTrackDurations( const QString& text, QList& trackDuration) { /* Example freedb format: # Track frame offsets: # 150 # 2390 # 23387 # 44650 # 61322 # 94605 # 121710 # 144637 # 176820 # 187832 # 218930 # # Disc length: 3114 seconds */ trackDuration.clear(); QRegExp discLenRe(QLatin1String("Disc length:\\s*\\d+")); int discLenPos = discLenRe.indexIn(text, 0); if (discLenPos != -1) { int len = discLenRe.matchedLength(); discLenPos += 12; int discLen = text.mid(discLenPos, len - 12).toInt(); int trackOffsetPos = text.indexOf(QLatin1String("Track frame offsets"), 0); if (trackOffsetPos != -1) { QRegExp re(QLatin1String("#\\s*\\d+")); int lastOffset = -1; while ((trackOffsetPos = re.indexIn(text, trackOffsetPos)) != -1 && trackOffsetPos < discLenPos) { len = re.matchedLength(); trackOffsetPos += 1; int trackOffset = text.mid(trackOffsetPos, len - 1).toInt(); if (lastOffset != -1) { int duration = (trackOffset - lastOffset) / 75; trackDuration.append(duration); } lastOffset = trackOffset; } if (lastOffset != -1) { int duration = (discLen * 75 - lastOffset) / 75; trackDuration.append(duration); } } } } /** * Parse the album specific data (artist, album, year, genre) from freedb.org. * * @param text text buffer containing data from freedb.org * @param frames tags to put result */ static void parseFreedbAlbumData(const QString& text, FrameCollection& frames) { QRegExp fdre(QLatin1String("DTITLE=\\s*(\\S[^\\r\\n]*\\S)\\s*/\\s*(\\S[^\\r\\n]*\\S)[\\r\\n]")); if (fdre.indexIn(text) != -1) { frames.setArtist(fdre.cap(1)); frames.setAlbum(fdre.cap(2)); } fdre.setPattern(QLatin1String("EXTD=[^\\r\\n]*YEAR:\\s*(\\d+)\\D")); if (fdre.indexIn(text) != -1) { frames.setYear(fdre.cap(1).toInt()); } fdre.setPattern(QLatin1String("EXTD=[^\\r\\n]*ID3G:\\s*(\\d+)\\D")); if (fdre.indexIn(text) != -1) { frames.setGenre(QString::fromLatin1(Genres::getName(fdre.cap(1).toInt()))); } } /** * Parse result of album request and populate m_trackDataModel with results. * * @param albumStr album data received */ void FreedbImporter::parseAlbumResults(const QByteArray& albumStr) { QString text = QString::fromUtf8(albumStr); FrameCollection framesHdr; QList trackDuration; parseFreedbTrackDurations(text, trackDuration); parseFreedbAlbumData(text, framesHdr); ImportTrackDataVector trackDataVector(m_trackDataModel->getTrackData()); trackDataVector.setCoverArtUrl(QString()); FrameCollection frames(framesHdr); ImportTrackDataVector::iterator it = trackDataVector.begin(); QList::const_iterator tdit = trackDuration.begin(); bool atTrackDataListEnd = (it == trackDataVector.end()); int pos = 0; int oldpos = pos; int tracknr = 0; for (;;) { QRegExp fdre(QString(QLatin1String("TTITLE%1=([^\\r\\n]+)[\\r\\n]")).arg(tracknr)); QString title; int idx; while ((idx = fdre.indexIn(text, pos)) != -1) { title += fdre.cap(1); pos = idx + fdre.matchedLength(); } if (pos > oldpos) { frames.setTrack(tracknr + 1); frames.setTitle(title); } else { break; } int duration = (tdit != trackDuration.end()) ? *tdit++ : 0; if (atTrackDataListEnd) { ImportTrackData trackData; trackData.setFrameCollection(frames); trackData.setImportDuration(duration); trackDataVector.push_back(trackData); } else { while (!atTrackDataListEnd && !it->isEnabled()) { ++it; atTrackDataListEnd = (it == trackDataVector.end()); } if (!atTrackDataListEnd) { (*it).setFrameCollection(frames); (*it).setImportDuration(duration); ++it; atTrackDataListEnd = (it == trackDataVector.end()); } } frames = framesHdr; oldpos = pos; ++tracknr; } frames.clear(); while (!atTrackDataListEnd) { if (it->isEnabled()) { if ((*it).getFileDuration() == 0) { it = trackDataVector.erase(it); } else { (*it).setFrameCollection(frames); (*it).setImportDuration(0); ++it; } } else { ++it; } atTrackDataListEnd = (it == trackDataVector.end()); } m_trackDataModel->setTrackData(trackDataVector); } /** * Send a query command in to search on the server. * * @param cfg import source configuration * @param artist artist to search * @param album album to search */ void FreedbImporter::sendFindQuery( const ServerImporterConfig*, const QString& artist, const QString& album) { // At the moment, only www.gnudb.org has a working search // so we always use this server for find queries. sendRequest(QString::fromLatin1(gnudbServer), QLatin1String("/search/") + encodeUrlQuery(artist + QLatin1Char(' ') + album)); } /** * Send a query command to fetch the track list * from the server. * * @param cfg import source configuration * @param cat category * @param id ID */ void FreedbImporter::sendTrackListQuery( const ServerImporterConfig* cfg, const QString& cat, const QString& id) { sendRequest(cfg->m_server, cfg->m_cgiPath + QLatin1String("?cmd=cddb+read+") + cat + QLatin1Char('+') + id + QLatin1String("&hello=noname+localhost+Kid3+" VERSION "&proto=6")); } kid3-3.0.2/src/plugins/freedbimport/freedbimporter.h000066400000000000000000000054441224603511300224660ustar00rootroot00000000000000/** * \file freedbimporter.h * freedb.org importer. * * \b Project: Kid3 * \author Urs Fleisch * \date 18 Jan 2004 * * Copyright (C) 2004-2011 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef FREEDBIMPORTER_H #define FREEDBIMPORTER_H #include "serverimporter.h" /** * freedb.org importer. */ class FreedbImporter : public ServerImporter { public: /** * Constructor. * * @param netMgr network access manager * @param trackDataModel track data to be filled with imported values */ FreedbImporter(QNetworkAccessManager* netMgr, TrackDataModel *trackDataModel); /** * Destructor. */ virtual ~FreedbImporter(); /** * Name of import source. * @return name. */ virtual const char* name() const; /** NULL-terminated array of server strings, 0 if not used */ virtual const char** serverList() const; /** default server, 0 to disable */ virtual const char* defaultServer() const; /** default CGI path, 0 to disable */ virtual const char* defaultCgiPath() const; /** anchor to online help, 0 to disable */ virtual const char* helpAnchor() const; /** configuration, 0 if not used */ virtual ServerImporterConfig* config() const; /** * Process finished findCddbAlbum request. * * @param searchStr search data received */ virtual void parseFindResults(const QByteArray& searchStr); /** * Parse result of album request and populate m_trackDataModel with results. * * @param albumStr album data received */ virtual void parseAlbumResults(const QByteArray& albumStr); /** * Send a query command to search on the server. * * @param cfg import source configuration * @param artist artist to search * @param album album to search */ virtual void sendFindQuery( const ServerImporterConfig* cfg, const QString& artist, const QString& album); /** * Send a query command to fetch the track list * from the server. * * @param cfg import source configuration * @param cat category * @param id ID */ virtual void sendTrackListQuery( const ServerImporterConfig* cfg, const QString& cat, const QString& id); }; #endif kid3-3.0.2/src/plugins/freedbimport/freedbimportplugin.cpp000066400000000000000000000044101224603511300237010ustar00rootroot00000000000000/** * \file freedbimportplugin.cpp * Freedb import plugin. * * \b Project: Kid3 * \author Urs Fleisch * \date 13 Jul 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "freedbimportplugin.h" #include "freedbimporter.h" #include "tracktypeimporter.h" #if QT_VERSION < 0x050000 Q_EXPORT_PLUGIN2(FreedbImportPlugin, FreedbImportPlugin) #endif static const QLatin1String FREEDB_IMPORTER_NAME("FreedbImport"); static const QLatin1String TRACKTYPE_IMPORTER_NAME("TrackTypeImport"); /*! * Constructor. * @param parent parent object */ FreedbImportPlugin::FreedbImportPlugin(QObject* parent) : QObject(parent) { setObjectName(QLatin1String("FreedbImport")); } /** * Destructor. */ FreedbImportPlugin::~FreedbImportPlugin() { } /** * Get keys of available server importers. * @return list of keys. */ QStringList FreedbImportPlugin::serverImporterKeys() const { return QStringList() << FREEDB_IMPORTER_NAME << TRACKTYPE_IMPORTER_NAME; } /** * Create server importer. * @param key server importer key * @param netMgr network access manager * @param trackDataModel track data to be filled with imported values * @return server importer instance, 0 if key unknown. * @remarks The caller takes ownership of the returned instance. */ ServerImporter* FreedbImportPlugin::createServerImporter( const QString& key, QNetworkAccessManager* netMgr, TrackDataModel* trackDataModel) { if (key == FREEDB_IMPORTER_NAME) { return new FreedbImporter(netMgr, trackDataModel); } else if (key == TRACKTYPE_IMPORTER_NAME) { return new TrackTypeImporter(netMgr, trackDataModel); } return 0; } kid3-3.0.2/src/plugins/freedbimport/freedbimportplugin.h000066400000000000000000000040251224603511300233500ustar00rootroot00000000000000/** * \file freedbimportplugin.h * Freedb import plugin. * * \b Project: Kid3 * \author Urs Fleisch * \date 15 Jul 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef FREEDBIMPORTPLUGIN_H #define FREEDBIMPORTPLUGIN_H #include #include "iserverimporterfactory.h" /** * Interface for server importer factory. */ class KID3_PLUGIN_EXPORT FreedbImportPlugin : public QObject, public IServerImporterFactory { Q_OBJECT #if QT_VERSION >= 0x050000 Q_PLUGIN_METADATA(IID "net.sourceforge.kid3.IServerImporterFactory") #endif Q_INTERFACES(IServerImporterFactory) public: /*! * Constructor. * @param parent parent object */ explicit FreedbImportPlugin(QObject* parent = 0); /** * Destructor. */ virtual ~FreedbImportPlugin(); /** * Get keys of available server importers. * @return list of keys. */ virtual QStringList serverImporterKeys() const; /** * Create server importer. * @param key server importer key * @param netMgr network access manager * @param trackDataModel track data to be filled with imported values * @return server importer instance, 0 if key unknown. * @remarks The caller takes ownership of the returned instance. */ virtual ServerImporter* createServerImporter( const QString& key, QNetworkAccessManager* netMgr, TrackDataModel* trackDataModel); }; #endif // FREEDBIMPORTPLUGIN_H kid3-3.0.2/src/plugins/freedbimport/tracktypeimporter.cpp000066400000000000000000000101631224603511300235720ustar00rootroot00000000000000/** * \file tracktypeimporter.cpp * TrackType.org importer. * * \b Project: Kid3 * \author Urs Fleisch * \date 26 Apr 2007 * * Copyright (C) 2007-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "tracktypeimporter.h" #include "serverimporterconfig.h" #include "freedbconfig.h" #include "config.h" static const char trackTypeServer[] = "tracktype.org:80"; /** * Constructor. * * @param netMgr network access manager * @param trackDataModel track data to be filled with imported values */ TrackTypeImporter::TrackTypeImporter(QNetworkAccessManager* netMgr, TrackDataModel *trackDataModel) : FreedbImporter(netMgr, trackDataModel) { setObjectName(QLatin1String("TrackTypeImporter")); } /** * Destructor. */ TrackTypeImporter::~TrackTypeImporter() { } /** * Name of import source. * @return name. */ const char* TrackTypeImporter::name() const { return QT_TRANSLATE_NOOP("@default", "TrackType.org"); } /** NULL-terminated array of server strings, 0 if not used */ const char** TrackTypeImporter::serverList() const { static const char* servers[] = { "tracktype.org:80", 0 // end of StrList }; return servers; } /** default server, 0 to disable */ const char* TrackTypeImporter::defaultServer() const { return "tracktype.org:80"; } /** configuration, 0 if not used */ ServerImporterConfig* TrackTypeImporter::config() const { return &TrackTypeConfig::instance(); } /** * Process finished findCddbAlbum request. * * @param searchStr search data received */ void TrackTypeImporter::parseFindResults(const QByteArray& searchStr) { /* 210 exact matches found categ discid dtitle (more matches...) . or 211 close matches found rock 920b810c Catharsis / Imago . theoretically, but never seen 200 categ discid dtitle */ QString str = QString::fromUtf8(searchStr); QRegExp catIdTitleRe(QLatin1String("([a-z]+)\\s+([0-9a-f]+)\\s+([^/]+ / .+)")); QStringList lines = str.split(QRegExp(QLatin1String("[\\r\\n]+"))); bool inEntries = false; m_albumListModel->clear(); for (QStringList::const_iterator it = lines.begin(); it != lines.end(); ++it) { if (*it == QLatin1String(".")) { break; } if (inEntries) { if (catIdTitleRe.exactMatch(*it)) { m_albumListModel->appendRow(new AlbumListItem( catIdTitleRe.cap(3), catIdTitleRe.cap(1), catIdTitleRe.cap(2))); } } else { if ((*it).startsWith(QLatin1String("21")) && (*it).indexOf(QLatin1String(" match")) != -1) { inEntries = true; } else if ((*it).startsWith(QLatin1String("200 "))) { if (catIdTitleRe.exactMatch((*it).mid(4))) { m_albumListModel->appendRow(new AlbumListItem( catIdTitleRe.cap(3), catIdTitleRe.cap(1), catIdTitleRe.cap(2))); } } } } } /** * Send a query command to search on the server. * * @param cfg import source configuration * @param artist artist to search * @param album album to search */ void TrackTypeImporter::sendFindQuery( const ServerImporterConfig* cfg, const QString& artist, const QString& album) { // At the moment, only TrackType.org recognizes cddb album commands, // so we always use this server for find queries. sendRequest(QString::fromLatin1(trackTypeServer), cfg->m_cgiPath + QLatin1String("?cmd=cddb+album+") + encodeUrlQuery(artist + QLatin1String(" / ") + album) + QLatin1String("&hello=noname+localhost+Kid3+" VERSION "&proto=6")); } kid3-3.0.2/src/plugins/freedbimport/tracktypeimporter.h000066400000000000000000000042271224603511300232430ustar00rootroot00000000000000/** * \file tracktypeimporter.h * TrackType.org importer. * * \b Project: Kid3 * \author Urs Fleisch * \date 26 Apr 2007 * * Copyright (C) 2007-2011 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef TRACKTYPEIMPORTER_H #define TRACKTYPEIMPORTER_H #include "freedbimporter.h" /** * TrackType.org importer. */ class TrackTypeImporter : public FreedbImporter { public: /** * Constructor. * * @param netMgr network access manager * @param trackDataModel track data to be filled with imported values */ TrackTypeImporter(QNetworkAccessManager* netMgr, TrackDataModel *trackDataModel); /** * Destructor. */ virtual ~TrackTypeImporter(); /** * Name of import source. * @return name. */ virtual const char* name() const; /** NULL-terminated array of server strings, 0 if not used */ virtual const char** serverList() const; /** default server, 0 to disable */ virtual const char* defaultServer() const; /** configuration, 0 if not used */ virtual ServerImporterConfig* config() const; /** * Process finished findCddbAlbum request. * * @param searchStr search data received */ virtual void parseFindResults(const QByteArray& searchStr); /** * Send a query command to search on the server. * * @param cfg import source configuration * @param artist artist to search * @param album album to search */ virtual void sendFindQuery( const ServerImporterConfig* cfg, const QString& artist, const QString& album); }; #endif kid3-3.0.2/src/plugins/id3libmetadata/000077500000000000000000000000001224603511300174625ustar00rootroot00000000000000kid3-3.0.2/src/plugins/id3libmetadata/CMakeLists.txt000066400000000000000000000047461224603511300222350ustar00rootroot00000000000000if(WITH_ID3LIB) find_path(ID3LIB_INCLUDE_DIR id3/tag.h) find_library(ID3LIB_LIBRARIES NAMES id3 id3lib) if(ID3LIB_INCLUDE_DIR AND ID3LIB_LIBRARIES) message(STATUS "id3lib found: ${ID3LIB_LIBRARIES}") set(HAVE_ID3LIB 1) set(ID3LIB_LIBRARIES ${ID3LIB_LIBRARIES} ${ZLIB_LIBRARIES}) if (NOT MSVC) set(ID3LIB_LIBRARIES ${ID3LIB_LIBRARIES} -lstdc++) endif (NOT MSVC) if (WIN32) set(ID3LIB_DEFINITIONS -DID3LIB_LINKOPTION=1) if (HAVE_QT5) set(ID3LIB_LIBRARIES ${ID3LIB_LIBRARIES} -liconv) endif (HAVE_QT5) elseif (APPLE) set(ID3LIB_LIBRARIES ${ID3LIB_LIBRARIES} -liconv) endif (WIN32) set(_CMAKE_REQUIRED_LIBRARIES_TMP ${CMAKE_REQUIRED_LIBRARIES}) set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} ${ID3LIB_LIBRARIES}) set(_CMAKE_REQUIRED_INCLUDES_TMP ${CMAKE_REQUIRED_INCLUDES}) set(CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES} ${ID3LIB_INCLUDE_DIR}) set(_CMAKE_REQUIRED_DEFINITIONS_TMP ${CMAKE_REQUIRED_DEFINITIONS}) set(CMAKE_REQUIRED_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} ${ID3LIB_DEFINITIONS}) CHECK_CXX_SOURCE_COMPILES("#include \nint main() {\n Mp3_Headerinfo info;\n info.vbr_bitrate = 0;\n return 0;\n}\n" ID3LIB_VBR_SUPPORT_FOUND) set(CMAKE_REQUIRED_LIBRARIES ${_CMAKE_REQUIRED_LIBRARIES_TMP}) set(CMAKE_REQUIRED_INCLUDES ${_CMAKE_REQUIRED_INCLUDES_TMP}) set(CMAKE_REQUIRED_DEFINITIONS ${_CMAKE_REQUIRED_DEFINITIONS_TMP}) if(NOT ID3LIB_VBR_SUPPORT_FOUND) set(HAVE_NO_ID3LIB_VBR 1) endif(NOT ID3LIB_VBR_SUPPORT_FOUND) else(ID3LIB_INCLUDE_DIR AND ID3LIB_LIBRARIES) message(FATAL_ERROR "Could not find id3lib") endif(ID3LIB_INCLUDE_DIR AND ID3LIB_LIBRARIES) configure_file(id3libconfig.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/id3libconfig.h) add_definitions(${ID3LIB_DEFINITIONS}) include_directories(${CMAKE_CURRENT_BINARY_DIR} ${ID3LIB_INCLUDE_DIR} ../../core/tags) set(plugin_SRCS id3libmetadataplugin.cpp mp3file.cpp ) set(plugin_MOC_HDRS id3libmetadataplugin.h ) set(plugin_NAME Id3libMetadata) if (WITH_GCC_PCH) add_definitions(${GCC_PCH_COMPILE_FLAGS}) endif (WITH_GCC_PCH) string(TOLOWER ${plugin_NAME} plugin_TARGET) qt4_wrap_cpp(plugin_GEN_MOC_SRCS ${plugin_MOC_HDRS}) add_library(${plugin_TARGET} ${plugin_SRCS} ${plugin_GEN_MOC_SRCS}) target_link_libraries(${plugin_TARGET} kid3-core ${BASIC_LIBRARIES} ${ID3LIB_LIBRARIES}) INSTALL_KID3_PLUGIN(${plugin_TARGET} ${plugin_NAME}) endif(WITH_ID3LIB) kid3-3.0.2/src/plugins/id3libmetadata/id3libconfig.h.cmake000066400000000000000000000003041224603511300232430ustar00rootroot00000000000000/* id3libconfig.h. Generated by cmake from id3libconfig.h.cmake */ #ifndef ID3LIBCONFIG_H #define ID3LIBCONFIG_H /* Define id3lib does not support VBR */ #cmakedefine HAVE_NO_ID3LIB_VBR #endif kid3-3.0.2/src/plugins/id3libmetadata/id3libmetadataplugin.cpp000066400000000000000000000072601224603511300242610ustar00rootroot00000000000000/** * \file id3libmetadataplugin.cpp * id3lib metadata plugin. * * \b Project: Kid3 * \author Urs Fleisch * \date 23 Jul 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "id3libmetadataplugin.h" #include "mp3file.h" #if QT_VERSION < 0x050000 Q_EXPORT_PLUGIN2(Id3libMetadataPlugin, Id3libMetadataPlugin) #endif static const QLatin1String TAGGEDFILE_KEY("Id3libMetadata"); /*! * Constructor. * @param parent parent object */ Id3libMetadataPlugin::Id3libMetadataPlugin(QObject* parent) : QObject(parent) { setObjectName(QLatin1String("Id3libMetadata")); } /** * Destructor. */ Id3libMetadataPlugin::~Id3libMetadataPlugin() { } /** * Get name of factory, the same as the QObject::objectName() of the plugin. * @return factory name. */ QString Id3libMetadataPlugin::name() const { return objectName(); } /** * Get keys of available tagged file formats. * @return list of keys. */ QStringList Id3libMetadataPlugin::taggedFileKeys() const { return QStringList() << TAGGEDFILE_KEY; } /** * Get features supported. * @param key tagged file key * @return bit mask with TaggedFile::Feature flags set. */ int Id3libMetadataPlugin::taggedFileFeatures(const QString& key) const { if (key == TAGGEDFILE_KEY) { return TaggedFile::TF_ID3v11 | TaggedFile::TF_ID3v23; } return 0; } /** * Initialize tagged file factory. * * @param key tagged file key */ void Id3libMetadataPlugin::initialize(const QString& key) { Q_UNUSED(key); } /** * Create a tagged file. * * @param key tagged file key * @param dirName directory name * @param fileName filename * @param idx model index * @param features optional tagged file features (TaggedFile::Feature flags) * to activate at creation * * @return tagged file, 0 if type not supported. */ TaggedFile* Id3libMetadataPlugin::createTaggedFile( const QString& key, const QString& dirName, const QString& fileName, const QPersistentModelIndex& idx, int features) { if (key == TAGGEDFILE_KEY) { QString ext = fileName.right(4).toLower(); if ((ext == QLatin1String(".mp3") || ext == QLatin1String(".mp2") || ext == QLatin1String(".aac")) && (TagConfig::instance().id3v2Version() == TagConfig::ID3v2_3_0 || (features & TaggedFile::TF_ID3v23) != 0)) { return new Mp3File(dirName, fileName, idx); } } return 0; } /** * Get a list with all extensions (e.g. ".mp3") supported by TaggedFile subclass. * * @param key tagged file key * * @return list of file extensions. */ QStringList Id3libMetadataPlugin::supportedFileExtensions(const QString& key) const { if (key == TAGGEDFILE_KEY) { return QStringList() << QLatin1String(".mp3") << QLatin1String(".mp2") << QLatin1String(".aac"); } return QStringList(); } /** * Notify about configuration change. * This method shall be called when the configuration changes. * * @param key tagged file key */ void Id3libMetadataPlugin::notifyConfigurationChange(const QString& key) { if (key == TAGGEDFILE_KEY) { Mp3File::notifyConfigurationChange(); } } kid3-3.0.2/src/plugins/id3libmetadata/id3libmetadataplugin.h000066400000000000000000000057211224603511300237260ustar00rootroot00000000000000/** * \file id3libmetadataplugin.h * id3lib metadata plugin. * * \b Project: Kid3 * \author Urs Fleisch * \date 23 Jul 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef ID3LIBMETADATAPLUGIN_H #define ID3LIBMETADATAPLUGIN_H #include #include "itaggedfilefactory.h" /** * id3lib metadata plugin. */ class KID3_PLUGIN_EXPORT Id3libMetadataPlugin : public QObject, public ITaggedFileFactory { Q_OBJECT #if QT_VERSION >= 0x050000 Q_PLUGIN_METADATA(IID "net.sourceforge.kid3.ITaggedFileFactory") #endif Q_INTERFACES(ITaggedFileFactory) public: /*! * Constructor. * @param parent parent object */ explicit Id3libMetadataPlugin(QObject* parent = 0); /** * Destructor. */ virtual ~Id3libMetadataPlugin(); /** * Get name of factory, the same as the QObject::objectName() of the plugin. * @return factory name. */ virtual QString name() const; /** * Get keys of available tagged file formats. * @return list of keys. */ virtual QStringList taggedFileKeys() const; /** * Get features supported. * @param key tagged file key * @return bit mask with TaggedFile::Feature flags set. */ virtual int taggedFileFeatures(const QString& key) const; /** * Initialize tagged file factory. * * @param key tagged file key */ virtual void initialize(const QString& key); /** * Create a tagged file. * * @param dirName directory name * @param fileName filename * @param idx model index * @param features optional tagged file features (TaggedFile::Feature flags) * to activate at creation * * @return tagged file, 0 if type not supported. */ virtual TaggedFile* createTaggedFile( const QString& key, const QString& dirName, const QString& fileName, const QPersistentModelIndex& idx, int features = 0); /** * Get a list with all extensions (e.g. ".mp3") supported by TaggedFile subclass. * * @param key tagged file key * * @return list of file extensions. */ virtual QStringList supportedFileExtensions(const QString& key) const; /** * Notify about configuration change. * This method shall be called when the configuration changes. * * @param key tagged file key */ virtual void notifyConfigurationChange(const QString& key); }; #endif // ID3LIBMETADATAPLUGIN_H kid3-3.0.2/src/plugins/id3libmetadata/mp3file.cpp000066400000000000000000002062341224603511300215340ustar00rootroot00000000000000/** * \file mp3file.cpp * Handling of tagged MP3 files. * * \b Project: Kid3 * \author Urs Fleisch * \date 9 Jan 2003 * * Copyright (C) 2003-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "mp3file.h" #include #include #include #include #include #include #include #include #ifdef Q_OS_WIN32 #include #include #else #include #endif #include "id3libconfig.h" #include "genres.h" #include "attributedata.h" #ifdef Q_OS_WIN32 /** * This will be set for id3lib versions with Unicode bugs. * ID3LIB_ symbols cannot be found on Windows ?! */ #define UNICODE_SUPPORT_BUGGY 1 #else /** This will be set for id3lib versions with Unicode bugs. */ #define UNICODE_SUPPORT_BUGGY ((((ID3LIB_MAJOR_VERSION) << 16) + ((ID3LIB_MINOR_VERSION) << 8) + (ID3LIB_PATCH_VERSION)) <= 0x030803) #endif #if defined __GNUC__ && (__GNUC__ * 100 + __GNUC_MINOR__) >= 407 /** Defined if GCC is used and supports diagnostic pragmas */ #define GCC_HAS_DIAGNOSTIC_PRAGMA #endif namespace { /** Text codec for ID3v1 tags, 0 to use default (ISO 8859-1) */ const QTextCodec* s_textCodecV1 = 0; /** Default text encoding */ ID3_TextEnc s_defaultTextEncoding = ID3TE_ISO8859_1; /** * Get the default text encoding. * @return default text encoding. */ ID3_TextEnc getDefaultTextEncoding() { return s_defaultTextEncoding; } } /** * Constructor. * * @param dn directory name * @param fn filename * @param idx model index */ Mp3File::Mp3File(const QString& dn, const QString& fn, const QPersistentModelIndex& idx) : TaggedFile(dn, fn, idx), m_tagV1(0), m_tagV2(0) { } /** * Destructor. */ Mp3File::~Mp3File() { if (m_tagV1) { delete m_tagV1; } if (m_tagV2) { delete m_tagV2; } } /** * Get key of tagged file format. * @return "Id3libMetadata". */ QString Mp3File::taggedFileKey() const { return QLatin1String("Id3libMetadata"); } /** * Get features supported. * @return bit mask with Feature flags set. */ int Mp3File::taggedFileFeatures() const { return TF_ID3v11 | TF_ID3v23; } /** * Read tags from file. * * @param force true to force reading even if tags were already read. */ void Mp3File::readTags(bool force) { QByteArray fn = QFile::encodeName(getDirname() + QDir::separator() + currentFilename()); if (force && m_tagV1) { m_tagV1->Clear(); m_tagV1->Link(fn, ID3TT_ID3V1); markTag1Unchanged(); } if (!m_tagV1) { m_tagV1 = new ID3_Tag; m_tagV1->Link(fn, ID3TT_ID3V1); markTag1Unchanged(); } if (force && m_tagV2) { m_tagV2->Clear(); m_tagV2->Link(fn, ID3TT_ID3V2); markTag2Unchanged(); } if (!m_tagV2) { m_tagV2 = new ID3_Tag; m_tagV2->Link(fn, ID3TT_ID3V2); markTag2Unchanged(); } if (force) { setFilename(currentFilename()); } } /** * Write tags to file and rename it if necessary. * * @param force true to force writing even if file was not changed. * @param renamed will be set to true if the file was renamed, * i.e. the file name is no longer valid, else *renamed * is left unchanged * @param preserve true to preserve file time stamps * * @return true if ok, false if the file could not be written or renamed. */ bool Mp3File::writeTags(bool force, bool* renamed, bool preserve) { QString fnStr(getDirname() + QDir::separator() + currentFilename()); if (isChanged() && !QFileInfo(fnStr).isWritable()) { return false; } // store time stamp if it has to be preserved QByteArray fn; bool setUtime = false; struct utimbuf times; if (preserve) { fn = QFile::encodeName(fnStr); struct stat fileStat; if (::stat(fn, &fileStat) == 0) { times.actime = fileStat.st_atime; times.modtime = fileStat.st_mtime; setUtime = true; } } // There seems to be a bug in id3lib: The V1 genre is not // removed. So we check here and strip the whole header // if there are no frames. if (m_tagV1 && (force || isTag1Changed()) && (m_tagV1->NumFrames() == 0)) { m_tagV1->Strip(ID3TT_ID3V1); markTag1Unchanged(); } // Even after removing all frames, HasV2Tag() still returns true, // so we strip the whole header. if (m_tagV2 && (force || isTag2Changed()) && (m_tagV2->NumFrames() == 0)) { m_tagV2->Strip(ID3TT_ID3V2); markTag2Unchanged(); } // There seems to be a bug in id3lib: If I update an ID3v1 and then // strip the ID3v2 the ID3v1 is removed too and vice versa, so I // first make any stripping and then the updating. if (m_tagV1 && (force || isTag1Changed()) && (m_tagV1->NumFrames() > 0)) { m_tagV1->Update(ID3TT_ID3V1); markTag1Unchanged(); } if (m_tagV2 && (force || isTag2Changed()) && (m_tagV2->NumFrames() > 0)) { m_tagV2->Update(ID3TT_ID3V2); markTag2Unchanged(); } // restore time stamp if (setUtime) { ::utime(fn, ×); } if (getFilename() != currentFilename()) { if (!renameFile(currentFilename(), getFilename())) { return false; } updateCurrentFilename(); // link tags to new file name readTags(true); *renamed = true; } return true; } /** * Remove ID3v1 frames. * * @param flt filter specifying which frames to remove */ void Mp3File::deleteFramesV1(const FrameFilter& flt) { if (m_tagV1) { if (flt.areAllEnabled()) { ID3_Tag::Iterator* iter = m_tagV1->CreateIterator(); ID3_Frame* frame; while ((frame = iter->GetNext()) != NULL) { m_tagV1->RemoveFrame(frame); } #ifdef Q_OS_WIN32 /* allocated in Windows DLL => must be freed in the same DLL */ ID3TagIterator_Delete(reinterpret_cast(iter)); #else #ifdef GCC_HAS_DIAGNOSTIC_PRAGMA #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor" /* Is safe because iterator implementation has default destructor */ #endif delete iter; #ifdef GCC_HAS_DIAGNOSTIC_PRAGMA #pragma GCC diagnostic pop #endif #endif markTag1Changed(Frame::FT_UnknownFrame); clearTrunctionFlags(); } else { TaggedFile::deleteFramesV1(flt); } } } /** * Fix up a unicode string from id3lib. * * @param str unicode string * @param numChars number of characters in str * * @return string as QString. */ static QString fixUpUnicode(const unicode_t* str, size_t numChars) { QString text; if (numChars > 0 && str && *str) { QChar* qcarray = new QChar[numChars]; // Unfortunately, Unicode support in id3lib is rather buggy // in the current version: The codes are mirrored. // In the hope that my patches will be included, I try here // to work around these bugs. size_t numZeroes = 0; for (size_t i = 0; i < numChars; i++) { qcarray[i] = UNICODE_SUPPORT_BUGGY ? (ushort)(((str[i] & 0x00ff) << 8) | ((str[i] & 0xff00) >> 8)) : (ushort)str[i]; if (qcarray[i].isNull()) { ++numZeroes; } } // remove a single trailing zero character if (numZeroes == 1 && qcarray[numChars - 1].isNull()) { --numChars; } text = QString(qcarray, numChars); delete [] qcarray; } return text; } /** * Get string from text field. * * @param field field * @param codec text codec to use, 0 for default * * @return string, * "" if the field does not exist. */ static QString getString(ID3_Field* field, const QTextCodec* codec = 0) { QString text(QLatin1String("")); if (field != NULL) { ID3_TextEnc enc = field->GetEncoding(); if (enc == ID3TE_UTF16 || enc == ID3TE_UTF16BE) { size_t numItems = field->GetNumTextItems(); if (numItems <= 1) { text = fixUpUnicode(field->GetRawUnicodeText(), field->Size() / sizeof(unicode_t)); } else { // if there are multiple items, put them into one string // separated by a special separator. // ID3_Field::GetRawUnicodeTextItem() returns a pointer to a temporary // object, so I do not use it. text = fixUpUnicode(field->GetRawUnicodeText(), field->Size() / sizeof(unicode_t)); text.replace(QLatin1Char('\0'), Frame::stringListSeparator()); } } else { // (ID3TE_IS_SINGLE_BYTE_ENC(enc)) // (enc == ID3TE_ISO8859_1 || enc == ID3TE_UTF8) size_t numItems = field->GetNumTextItems(); if (numItems <= 1) { text = codec ? codec->toUnicode(field->GetRawText(), field->Size()) : QString::fromLatin1(field->GetRawText()); } else { // if there are multiple items, put them into one string // separated by a special separator. for (size_t itemNr = 0; itemNr < numItems; ++itemNr) { if (itemNr == 0) { text = QString::fromLatin1(field->GetRawTextItem(0)); } else { text += Frame::stringListSeparator(); text += QString::fromLatin1(field->GetRawTextItem(itemNr)); } } } } } return text; } /** * Get text field. * * @param tag ID3 tag * @param id frame ID * @param codec text codec to use, 0 for default * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ static QString getTextField(const ID3_Tag* tag, ID3_FrameID id, const QTextCodec* codec = 0) { if (!tag) { return QString(); } QString str(QLatin1String("")); ID3_Field* fld; ID3_Frame* frame = tag->Find(id); if (frame && ((fld = frame->GetField(ID3FN_TEXT)) != NULL)) { str = getString(fld, codec); } return str; } /** * Get year. * * @param tag ID3 tag * @return number, * 0 if the field does not exist, * -1 if the tags do not exist. */ static int getYear(const ID3_Tag* tag) { QString str = getTextField(tag, ID3FID_YEAR); if (str.isNull()) return -1; if (str.isEmpty()) return 0; return str.toInt(); } /** * Get track. * * @param tag ID3 tag * @return number, * 0 if the field does not exist, * -1 if the tags do not exist. */ static int getTrackNum(const ID3_Tag* tag) { QString str = getTextField(tag, ID3FID_TRACKNUM); if (str.isNull()) return -1; if (str.isEmpty()) return 0; // handle "track/total number of tracks" format int slashPos = str.indexOf(QLatin1Char('/')); if (slashPos != -1) { str.truncate(slashPos); } return str.toInt(); } /** * Get genre. * * @param tag ID3 tag * @return number, * 0xff if the field does not exist, * -1 if the tags do not exist. */ static int getGenreNum(const ID3_Tag* tag) { QString str = getTextField(tag, ID3FID_CONTENTTYPE); if (str.isNull()) return -1; if (str.isEmpty()) return 0xff; int cpPos = 0, n = 0xff; if ((str[0] == QLatin1Char('(')) && ((cpPos = str.indexOf(QLatin1Char(')'), 2)) > 1)) { bool ok; n = str.mid(1, cpPos - 1).toInt(&ok); if (!ok || n > 0xff) { n = 0xff; } } else { // ID3v2 genres can be stored as "(9)", "(9)Metal" or "Metal". // If the string does not start with '(', try to get the genre number // from a string containing a genre text. n = Genres::getNumber(str); } return n; } /** * Allocate a fixed up a unicode string for id3lib. * * @param text string * * @return new allocated unicode string, has to be freed with delete []. */ static unicode_t* newFixedUpUnicode(const QString& text) { // Unfortunately, Unicode support in id3lib is rather buggy in the // current version: The codes are mirrored, a second different // BOM may be added, if the LSB >= 0x80, the MSB is set to 0xff. // If iconv is used (id3lib on Linux), the character do not come // back mirrored, but with a second (different)! BOM 0xfeff and // they are still written in the wrong order (big endian). // In the hope that my patches will be included, I try here to // work around these bugs, but there is no solution for the // LSB >= 0x80 bug. const QChar* qcarray = text.unicode(); uint unicode_size = text.length(); unicode_t* unicode = new unicode_t[unicode_size + 1]; for (uint i = 0; i < unicode_size; i++) { unicode[i] = (ushort)qcarray[i].unicode(); if (UNICODE_SUPPORT_BUGGY) { unicode[i] = (ushort)(((unicode[i] & 0x00ff) << 8) | ((unicode[i] & 0xff00) >> 8)); } } unicode[unicode_size] = 0; return unicode; } /** * Set string list in text field. * * @param field field * @param lst string list to set */ static void setStringList(ID3_Field* field, const QStringList& lst) { ID3_TextEnc enc = field->GetEncoding(); bool first = true; for (QStringList::const_iterator it = lst.begin(); it != lst.end(); ++it) { if (first) { if (enc == ID3TE_UTF16 || enc == ID3TE_UTF16BE) { unicode_t* unicode = newFixedUpUnicode(*it); if (unicode) { field->Set(unicode); delete [] unicode; } } else if (enc == ID3TE_UTF8) { field->Set((*it).toUtf8().data()); } else { // enc == ID3TE_ISO8859_1 field->Set((*it).toLatin1().data()); } first = false; } else { // This will not work with buggy id3lib. A BOM 0xfffe is written before // the first string, but not before the subsequent strings. Prepending a // BOM or changing the byte order does not help when id3lib rewrites // this field when another frame is changed. So you cannot use // string lists with Unicode encoding. if (enc == ID3TE_UTF16 || enc == ID3TE_UTF16BE) { unicode_t* unicode = newFixedUpUnicode(*it); if (unicode) { field->Add(unicode); delete [] unicode; } } else if (enc == ID3TE_UTF8) { field->Add((*it).toUtf8().data()); } else { // enc == ID3TE_ISO8859_1 field->Add((*it).toLatin1().data()); } } } } /** * Set string in text field. * * @param field field * @param text text to set * @param codec text codec to use, 0 for default */ static void setString(ID3_Field* field, const QString& text, const QTextCodec* codec = 0) { if (text.indexOf(Frame::stringListSeparator()) == -1) { ID3_TextEnc enc = field->GetEncoding(); // (ID3TE_IS_DOUBLE_BYTE_ENC(enc)) if (enc == ID3TE_UTF16 || enc == ID3TE_UTF16BE) { unicode_t* unicode = newFixedUpUnicode(text); if (unicode) { field->Set(unicode); delete [] unicode; } } else if (enc == ID3TE_UTF8) { field->Set(text.toUtf8().data()); } else { // enc == ID3TE_ISO8859_1 field->Set(codec ? codec->fromUnicode(text).data() : text.toLatin1().data()); } } else { setStringList(field, text.split(Frame::stringListSeparator())); } } /** * Set text field. * * @param tag ID3 tag * @param id frame ID * @param text text to set * @param allowUnicode true to allow setting of Unicode encoding if necessary * @param replace true to replace an existing field * @param removeEmpty true to remove a field if text is empty * @param codec text codec to use, 0 for default * * @return true if the field was changed. */ static bool setTextField(ID3_Tag* tag, ID3_FrameID id, const QString& text, bool allowUnicode = false, bool replace = true, bool removeEmpty = true, const QTextCodec* codec = 0) { bool changed = false; if (tag && !text.isNull()) { ID3_Frame* frame = NULL; bool removeOnly = removeEmpty && text.isEmpty(); if (replace || removeOnly) { if (id == ID3FID_COMMENT && tag->HasV2Tag()) { frame = tag->Find(ID3FID_COMMENT, ID3FN_DESCRIPTION, ""); } else { frame = tag->Find(id); } if (frame) { frame = tag->RemoveFrame(frame); delete frame; changed = true; } } if (!removeOnly && (replace || tag->Find(id) == NULL)) { frame = new ID3_Frame(id); if (frame) { ID3_Field* fld = frame->GetField(ID3FN_TEXT); if (fld) { ID3_TextEnc enc = tag->HasV2Tag() ? getDefaultTextEncoding() : ID3TE_ISO8859_1; if (allowUnicode && enc == ID3TE_ISO8859_1) { // check if information is lost if the string is not unicode uint i, unicode_size = text.length(); const QChar* qcarray = text.unicode(); for (i = 0; i < unicode_size; i++) { char ch = qcarray[i].toLatin1(); if (ch == 0 || (ch & 0x80) != 0) { enc = ID3TE_UTF16; break; } } } ID3_Field* encfld = frame->GetField(ID3FN_TEXTENC); if (encfld) { encfld->Set(enc); } fld->SetEncoding(enc); setString(fld, text, codec); tag->AttachFrame(frame); } } changed = true; } } return changed; } /** * Set year. * * @param tag ID3 tag * @param num number to set, 0 to remove field. * * @return true if the field was changed. */ static bool setYear(ID3_Tag* tag, int num) { bool changed = false; if (num >= 0) { QString str; if (num != 0) { str.setNum(num); } else { str.clear(); } changed = getTextField(tag, ID3FID_YEAR) != str && setTextField(tag, ID3FID_YEAR, str); } return changed; } /** * Set track. * * @param tag ID3 tag * @param num number to set, 0 to remove field. * @param numTracks total number of tracks, <=0 to ignore * * @return true if the field was changed. */ bool Mp3File::setTrackNum(ID3_Tag* tag, int num, int numTracks) const { bool changed = false; if (num >= 0 && getTrackNum(tag) != num) { QString str = trackNumberString(num, numTracks); changed = getTextField(tag, ID3FID_TRACKNUM) != str && setTextField(tag, ID3FID_TRACKNUM, str); } return changed; } /** * Set genre. * * @param tag ID3 tag * @param num number to set, 0xff to remove field. * * @return true if the field was changed. */ static bool setGenreNum(ID3_Tag* tag, int num) { bool changed = false; if (num >= 0) { QString str; if (num != 0xff) { str = QString(QLatin1String("(%1)")).arg(num); } else { str.clear(); } changed = getTextField(tag, ID3FID_CONTENTTYPE) != str && setTextField(tag, ID3FID_CONTENTTYPE, str); } return changed; } /** * Get ID3v1 title. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ QString Mp3File::getTitleV1() { return getTextField(m_tagV1, ID3FID_TITLE, s_textCodecV1); } /** * Get ID3v1 artist. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ QString Mp3File::getArtistV1() { return getTextField(m_tagV1, ID3FID_LEADARTIST, s_textCodecV1); } /** * Get ID3v1 album. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ QString Mp3File::getAlbumV1() { return getTextField(m_tagV1, ID3FID_ALBUM, s_textCodecV1); } /** * Get ID3v1 comment. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ QString Mp3File::getCommentV1() { return getTextField(m_tagV1, ID3FID_COMMENT, s_textCodecV1); } /** * Get ID3v1 year. * * @return number, * 0 if the field does not exist, * -1 if the tags do not exist. */ int Mp3File::getYearV1() { return getYear(m_tagV1); } /** * Get ID3v1 track. * * @return number, * 0 if the field does not exist, * -1 if the tags do not exist. */ int Mp3File::getTrackNumV1() { return getTrackNum(m_tagV1); } /** * Get ID3v1 genre. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ QString Mp3File::getGenreV1() { int num = getGenreNum(m_tagV1); if (num == -1) { return QString(); } else if (num == 0xff) { return QLatin1String(""); } else { return QString::fromLatin1(Genres::getName(num)); } } /** * Get ID3v2 title. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ QString Mp3File::getTitleV2() { return getTextField(m_tagV2, ID3FID_TITLE); } /** * Get ID3v2 artist. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ QString Mp3File::getArtistV2() { return getTextField(m_tagV2, ID3FID_LEADARTIST); } /** * Get ID3v2 album. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ QString Mp3File::getAlbumV2() { return getTextField(m_tagV2, ID3FID_ALBUM); } /** * Get ID3v2 comment. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ QString Mp3File::getCommentV2() { return getTextField(m_tagV2, ID3FID_COMMENT); } /** * Get ID3v2 year. * * @return number, * 0 if the field does not exist, * -1 if the tags do not exist. */ int Mp3File::getYearV2() { return getYear(m_tagV2); } /** * Get ID3v2 track. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ QString Mp3File::getTrackV2() { return getTextField(m_tagV2, ID3FID_TRACKNUM); } /** * Get ID3v2 genre as text. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ QString Mp3File::getGenreV2() { int num = getGenreNum(m_tagV2); if (num != 0xff && num != -1) { return QString::fromLatin1(Genres::getName(num)); } else { return getTextField(m_tagV2, ID3FID_CONTENTTYPE); } } /** * Set ID3v1 title. * * @param str string to set, "" to remove field. */ void Mp3File::setTitleV1(const QString& str) { if (getTextField(m_tagV1, ID3FID_TITLE, s_textCodecV1) != str && setTextField(m_tagV1, ID3FID_TITLE, str, false, true, true, s_textCodecV1)) { markTag1Changed(Frame::FT_Title); QString s = checkTruncation(str, 1ULL << Frame::FT_Title); if (!s.isNull()) setTextField(m_tagV1, ID3FID_TITLE, s, false, true, true, s_textCodecV1); } } /** * Set ID3v1 artist. * * @param str string to set, "" to remove field. */ void Mp3File::setArtistV1(const QString& str) { if (getTextField(m_tagV1, ID3FID_LEADARTIST, s_textCodecV1) != str && setTextField(m_tagV1, ID3FID_LEADARTIST, str, false, true, true, s_textCodecV1)) { markTag1Changed(Frame::FT_Artist); QString s = checkTruncation(str, 1ULL << Frame::FT_Artist); if (!s.isNull()) setTextField(m_tagV1, ID3FID_LEADARTIST, s, false, true, true, s_textCodecV1); } } /** * Set ID3v1 album. * * @param str string to set, "" to remove field. */ void Mp3File::setAlbumV1(const QString& str) { if (getTextField(m_tagV1, ID3FID_ALBUM, s_textCodecV1) != str && setTextField(m_tagV1, ID3FID_ALBUM, str, false, true, true, s_textCodecV1)) { markTag1Changed(Frame::FT_Album); QString s = checkTruncation(str, 1ULL << Frame::FT_Album); if (!s.isNull()) setTextField(m_tagV1, ID3FID_ALBUM, s, false, true, true, s_textCodecV1); } } /** * Set ID3v1 comment. * * @param str string to set, "" to remove field. */ void Mp3File::setCommentV1(const QString& str) { if (getTextField(m_tagV1, ID3FID_COMMENT, s_textCodecV1) != str && setTextField(m_tagV1, ID3FID_COMMENT, str, false, true, true, s_textCodecV1)) { markTag1Changed(Frame::FT_Comment); QString s = checkTruncation(str, 1ULL << Frame::FT_Comment, 28); if (!s.isNull()) setTextField(m_tagV1, ID3FID_COMMENT, s, false, true, true, s_textCodecV1); } } /** * Set ID3v1 year. * * @param num number to set, 0 to remove field. */ void Mp3File::setYearV1(int num) { if (setYear(m_tagV1, num)) { markTag1Changed(Frame::FT_Date); } } /** * Set ID3v1 track. * * @param num number to set, 0 to remove field. */ void Mp3File::setTrackNumV1(int num) { if (setTrackNum(m_tagV1, num)) { markTag1Changed(Frame::FT_Track); int n = checkTruncation(num, 1ULL << Frame::FT_Track); if (n != -1) setTrackNum(m_tagV1, n); } } /** * Set ID3v1 genre as text. * * @param str string to set, "" to remove field, QString::null to ignore. */ void Mp3File::setGenreV1(const QString& str) { if (!str.isNull()) { int num = Genres::getNumber(str); if (setGenreNum(m_tagV1, num)) { markTag1Changed(Frame::FT_Genre); } // if the string cannot be converted to a number, set the truncation flag checkTruncation(num == 0xff && !str.isEmpty() ? 1 : 0, 1ULL << Frame::FT_Genre, 0); } } /** * Set ID3v2 title. * * @param str string to set, "" to remove field. */ void Mp3File::setTitleV2(const QString& str) { if (getTextField(m_tagV2, ID3FID_TITLE) != str && setTextField(m_tagV2, ID3FID_TITLE, str, true)) { markTag2Changed(Frame::FT_Title); } } /** * Set ID3v2 artist. * * @param str string to set, "" to remove field. */ void Mp3File::setArtistV2(const QString& str) { if (getTextField(m_tagV2, ID3FID_LEADARTIST) != str && setTextField(m_tagV2, ID3FID_LEADARTIST, str, true)) { markTag2Changed(Frame::FT_Artist); } } /** * Set ID3v2 album. * * @param str string to set, "" to remove field. */ void Mp3File::setAlbumV2(const QString& str) { if (getTextField(m_tagV2, ID3FID_ALBUM) != str && setTextField(m_tagV2, ID3FID_ALBUM, str, true)) { markTag2Changed(Frame::FT_Album); } } /** * Set ID3v2 comment. * * @param str string to set, "" to remove field. */ void Mp3File::setCommentV2(const QString& str) { if (getTextField(m_tagV2, ID3FID_COMMENT) != str && setTextField(m_tagV2, ID3FID_COMMENT, str, true)) { markTag2Changed(Frame::FT_Comment); } } /** * Set ID3v2 year. * * @param num number to set, 0 to remove field. */ void Mp3File::setYearV2(int num) { if (setYear(m_tagV2, num)) { markTag2Changed(Frame::FT_Date); } } /** * Set ID3v2 track. * * @param track string to set, "" to remove field, QString::null to ignore. */ void Mp3File::setTrackV2(const QString& track) { int numTracks; int num = splitNumberAndTotal(track, &numTracks); if (setTrackNum(m_tagV2, num, numTracks)) { markTag2Changed(Frame::FT_Track); } } /** * Set ID3v2 genre as text. * * @param str string to set, "" to remove field, QString::null to ignore. */ void Mp3File::setGenreV2(const QString& str) { if (!str.isNull()) { int num = 0xff; if (!TagConfig::instance().genreNotNumeric()) { num = Genres::getNumber(str); } if (num >= 0 && num != 0xff) { if (getGenreNum(m_tagV2) != num && setGenreNum(m_tagV2, num)) { markTag2Changed(Frame::FT_Genre); } } else { if (getTextField(m_tagV2, ID3FID_CONTENTTYPE) != str && setTextField(m_tagV2, ID3FID_CONTENTTYPE, str, true)) { markTag2Changed(Frame::FT_Genre); } } } } /** * Check if tag information has already been read. * * @return true if information is available, * false if the tags have not been read yet, in which case * hasTagV1() and hasTagV2() do not return meaningful information. */ bool Mp3File::isTagInformationRead() const { return m_tagV1 || m_tagV2; } /** * Check if file has an ID3v1 tag. * * @return true if a V1 tag is available. * @see isTagInformationRead() */ bool Mp3File::hasTagV1() const { return m_tagV1 && m_tagV1->HasV1Tag(); } /** * Check if ID3v1 tags are supported by the format of this file. * * @return true. */ bool Mp3File::isTagV1Supported() const { return true; } /** * Check if file has an ID3v2 tag. * * @return true if a V2 tag is available. * @see isTagInformationRead() */ bool Mp3File::hasTagV2() const { return m_tagV2 && m_tagV2->HasV2Tag(); } /** * Get technical detail information. * * @param info the detail information is returned here */ void Mp3File::getDetailInfo(DetailInfo& info) const { if (getFilename().right(4).toLower() == QLatin1String(".aac")) { info.valid = true; info.format = QLatin1String("AAC"); return; } const Mp3_Headerinfo* headerInfo = 0; if (m_tagV2) { headerInfo = m_tagV2->GetMp3HeaderInfo(); } if (!headerInfo && m_tagV1) { headerInfo = m_tagV1->GetMp3HeaderInfo(); } if (headerInfo) { info.valid = true; switch (headerInfo->version) { case MPEGVERSION_1: info.format = QLatin1String("MPEG 1 "); break; case MPEGVERSION_2: info.format = QLatin1String("MPEG 2 "); break; case MPEGVERSION_2_5: info.format = QLatin1String("MPEG 2.5 "); break; default: ; // nothing } switch (headerInfo->layer) { case MPEGLAYER_I: info.format += QLatin1String("Layer 1"); break; case MPEGLAYER_II: info.format += QLatin1String("Layer 2"); break; case MPEGLAYER_III: info.format += QLatin1String("Layer 3"); break; default: ; // nothing } info.bitrate = headerInfo->bitrate / 1000; #ifndef HAVE_NO_ID3LIB_VBR if (headerInfo->vbr_bitrate > 1000) { info.vbr = true; info.bitrate = headerInfo->vbr_bitrate / 1000; } #endif info.sampleRate = headerInfo->frequency; switch (headerInfo->channelmode) { case MP3CHANNELMODE_STEREO: info.channelMode = DetailInfo::CM_Stereo; info.channels = 2; break; case MP3CHANNELMODE_JOINT_STEREO: info.channelMode = DetailInfo::CM_JointStereo; info.channels = 2; break; case MP3CHANNELMODE_DUAL_CHANNEL: info.channels = 2; break; case MP3CHANNELMODE_SINGLE_CHANNEL: info.channels = 1; break; default: ; // nothing } info.duration = headerInfo->time; } else { info.valid = false; } } /** * Get duration of file. * * @return duration in seconds, * 0 if unknown. */ unsigned Mp3File::getDuration() const { unsigned duration = 0; const Mp3_Headerinfo* info = NULL; if (m_tagV2) { info = m_tagV2->GetMp3HeaderInfo(); } if (!info && m_tagV1) { info = m_tagV1->GetMp3HeaderInfo(); } if (info && info->time > 0) { duration = info->time; } return duration; } /** * Get file extension including the dot. * * @return file extension ".mp3". */ QString Mp3File::getFileExtension() const { QString ext(getFilename().right(4).toLower()); if (ext == QLatin1String(".aac") || ext == QLatin1String(".mp2")) return ext; else return QLatin1String(".mp3"); } /** * Get the format of tag 1. * * @return string describing format of tag 1, * e.g. "ID3v1.1". */ QString Mp3File::getTagFormatV1() const { return hasTagV1() ? QString(QLatin1String("ID3v1.1")) : QString(); } /** * Get the format of tag 2. * * @return string describing format of tag 2, * e.g. "ID3v2.3", "ID3v2.4". */ QString Mp3File::getTagFormatV2() const { if (m_tagV2 && m_tagV2->HasV2Tag()) { switch (m_tagV2->GetSpec()) { case ID3V2_3_0: return QLatin1String("ID3v2.3.0"); case ID3V2_4_0: return QLatin1String("ID3v2.4.0"); case ID3V2_2_0: return QLatin1String("ID3v2.2.0"); case ID3V2_2_1: return QLatin1String("ID3v2.2.1"); default: break; } } return QString(); } /** Types and descriptions for id3lib frame IDs */ static const struct TypeStrOfId { Frame::Type type; const char* str; } typeStrOfId[] = { { Frame::FT_UnknownFrame, 0 }, /* ???? */ { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "AENC - Audio encryption") }, /* AENC */ { Frame::FT_Picture, QT_TRANSLATE_NOOP("@default", "APIC - Attached picture") }, /* APIC */ { Frame::FT_Other, 0 }, /* ASPI */ { Frame::FT_Comment, QT_TRANSLATE_NOOP("@default", "COMM - Comments") }, /* COMM */ { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "COMR - Commercial") }, /* COMR */ { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "ENCR - Encryption method registration") }, /* ENCR */ { Frame::FT_Other, 0 }, /* EQU2 */ { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "EQUA - Equalization") }, /* EQUA */ { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "ETCO - Event timing codes") }, /* ETCO */ { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "GEOB - General encapsulated object") }, /* GEOB */ { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "GRID - Group identification registration") }, /* GRID */ { Frame::FT_Arranger, QT_TRANSLATE_NOOP("@default", "IPLS - Involved people list") }, /* IPLS */ { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "LINK - Linked information") }, /* LINK */ { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "MCDI - Music CD identifier") }, /* MCDI */ { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "MLLT - MPEG location lookup table") }, /* MLLT */ { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "OWNE - Ownership frame") }, /* OWNE */ { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "PRIV - Private frame") }, /* PRIV */ { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "PCNT - Play counter") }, /* PCNT */ { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "POPM - Popularimeter") }, /* POPM */ { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "POSS - Position synchronisation frame") }, /* POSS */ { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "RBUF - Recommended buffer size") }, /* RBUF */ { Frame::FT_Other, 0 }, /* RVA2 */ { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "RVAD - Relative volume adjustment") }, /* RVAD */ { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "RVRB - Reverb") }, /* RVRB */ { Frame::FT_Other, 0 }, /* SEEK */ { Frame::FT_Other, 0 }, /* SIGN */ { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "SYLT - Synchronized lyric/text") }, /* SYLT */ { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "SYTC - Synchronized tempo codes") }, /* SYTC */ { Frame::FT_Album, QT_TRANSLATE_NOOP("@default", "TALB - Album/Movie/Show title") }, /* TALB */ { Frame::FT_Bpm, QT_TRANSLATE_NOOP("@default", "TBPM - BPM (beats per minute)") }, /* TBPM */ { Frame::FT_Composer, QT_TRANSLATE_NOOP("@default", "TCOM - Composer") }, /* TCOM */ { Frame::FT_Genre, QT_TRANSLATE_NOOP("@default", "TCON - Content type") }, /* TCON */ { Frame::FT_Copyright, QT_TRANSLATE_NOOP("@default", "TCOP - Copyright message") }, /* TCOP */ { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "TDAT - Date") }, /* TDAT */ { Frame::FT_Other, 0 }, /* TDEN */ { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "TDLY - Playlist delay") }, /* TDLY */ { Frame::FT_Other, 0 }, /* TDOR */ { Frame::FT_Other, 0 }, /* TDRC */ { Frame::FT_Other, 0 }, /* TDRL */ { Frame::FT_Other, 0 }, /* TDTG */ { Frame::FT_Other, 0 }, /* TIPL */ { Frame::FT_EncodedBy, QT_TRANSLATE_NOOP("@default", "TENC - Encoded by") }, /* TENC */ { Frame::FT_Lyricist, QT_TRANSLATE_NOOP("@default", "TEXT - Lyricist/Text writer") }, /* TEXT */ { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "TFLT - File type") }, /* TFLT */ { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "TIME - Time") }, /* TIME */ { Frame::FT_Grouping, QT_TRANSLATE_NOOP("@default", "TIT1 - Content group description") }, /* TIT1 */ { Frame::FT_Title, QT_TRANSLATE_NOOP("@default", "TIT2 - Title/songname/content description") }, /* TIT2 */ { Frame::FT_Subtitle, QT_TRANSLATE_NOOP("@default", "TIT3 - Subtitle/Description refinement") }, /* TIT3 */ { Frame::FT_InitialKey, QT_TRANSLATE_NOOP("@default", "TKEY - Initial key") }, /* TKEY */ { Frame::FT_Language, QT_TRANSLATE_NOOP("@default", "TLAN - Language(s)") }, /* TLAN */ { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "TLEN - Length") }, /* TLEN */ { Frame::FT_Other, 0 }, /* TMCL */ { Frame::FT_Media, QT_TRANSLATE_NOOP("@default", "TMED - Media type") }, /* TMED */ { Frame::FT_Other, 0 }, /* TMOO */ { Frame::FT_OriginalAlbum, QT_TRANSLATE_NOOP("@default", "TOAL - Original album/movie/show title") }, /* TOAL */ { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "TOFN - Original filename") }, /* TOFN */ { Frame::FT_Author, QT_TRANSLATE_NOOP("@default", "TOLY - Original lyricist(s)/text writer(s)") }, /* TOLY */ { Frame::FT_OriginalArtist, QT_TRANSLATE_NOOP("@default", "TOPE - Original artist(s)/performer(s)") }, /* TOPE */ { Frame::FT_OriginalDate, QT_TRANSLATE_NOOP("@default", "TORY - Original release year") }, /* TORY */ { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "TOWN - File owner/licensee") }, /* TOWN */ { Frame::FT_Artist, QT_TRANSLATE_NOOP("@default", "TPE1 - Lead performer(s)/Soloist(s)") }, /* TPE1 */ { Frame::FT_AlbumArtist, QT_TRANSLATE_NOOP("@default", "TPE2 - Band/orchestra/accompaniment") }, /* TPE2 */ { Frame::FT_Conductor, QT_TRANSLATE_NOOP("@default", "TPE3 - Conductor/performer refinement") }, /* TPE3 */ { Frame::FT_Remixer, QT_TRANSLATE_NOOP("@default", "TPE4 - Interpreted, remixed, or otherwise modified by") }, /* TPE4 */ { Frame::FT_Disc, QT_TRANSLATE_NOOP("@default", "TPOS - Part of a set") }, /* TPOS */ { Frame::FT_Other, 0 }, /* TPRO */ { Frame::FT_Publisher, QT_TRANSLATE_NOOP("@default", "TPUB - Publisher") }, /* TPUB */ { Frame::FT_Track, QT_TRANSLATE_NOOP("@default", "TRCK - Track number/Position in set") }, /* TRCK */ { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "TRDA - Recording dates") }, /* TRDA */ { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "TRSN - Internet radio station name") }, /* TRSN */ { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "TRSO - Internet radio station owner") }, /* TRSO */ { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "TSIZ - Size") }, /* TSIZ */ { Frame::FT_Other, 0 }, /* TSOA */ { Frame::FT_Other, 0 }, /* TSOP */ { Frame::FT_Other, 0 }, /* TSOT */ { Frame::FT_Isrc, QT_TRANSLATE_NOOP("@default", "TSRC - ISRC (international standard recording code)") }, /* TSRC */ { Frame::FT_EncoderSettings,QT_TRANSLATE_NOOP("@default", "TSSE - Software/Hardware and settings used for encoding") },/* TSSE */ { Frame::FT_Part, 0 }, /* TSST */ { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "TXXX - User defined text information") }, /* TXXX */ { Frame::FT_Date, QT_TRANSLATE_NOOP("@default", "TYER - Year") }, /* TYER */ { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "UFID - Unique file identifier") }, /* UFID */ { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "USER - Terms of use") }, /* USER */ { Frame::FT_Lyrics, QT_TRANSLATE_NOOP("@default", "USLT - Unsynchronized lyric/text transcription") }, /* USLT */ { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "WCOM - Commercial information") }, /* WCOM */ { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "WCOP - Copyright/Legal information") }, /* WCOP */ { Frame::FT_WWWAudioFile, QT_TRANSLATE_NOOP("@default", "WOAF - Official audio file webpage") }, /* WOAF */ { Frame::FT_Website, QT_TRANSLATE_NOOP("@default", "WOAR - Official artist/performer webpage") }, /* WOAR */ { Frame::FT_WWWAudioSource, QT_TRANSLATE_NOOP("@default", "WOAS - Official audio source webpage") }, /* WOAS */ { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "WORS - Official internet radio station homepage") }, /* WORS */ { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "WPAY - Payment") }, /* WPAY */ { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "WPUB - Official publisher webpage") }, /* WPUB */ { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "WXXX - User defined URL link") } /* WXXX */ }; /** Not instantiated struct to check array size at compilation time. */ struct not_used { int array_size_check[ sizeof(typeStrOfId) / sizeof(typeStrOfId[0]) == ID3FID_WWWUSER + 1 ? 1 : -1 ]; /**< not used */ }; /** * Get type and description of frame. * * @param id ID of frame * @param type the type is returned here * @param str the description is returned here, 0 if not available */ static void getTypeStringForId3libFrameId(ID3_FrameID id, Frame::Type& type, const char*& str) { const TypeStrOfId& ts = typeStrOfId[id <= ID3FID_WWWUSER ? id : 0]; type = ts.type; str = ts.str; } /** * Get id3lib frame ID for frame type. * * @param type frame type * * @return id3lib frame ID or ID3FID_NOFRAME if type not found. */ static ID3_FrameID getId3libFrameIdForType(Frame::Type type) { // IPLS is mapped to FT_Arranger and FT_Performer if (type == Frame::FT_Performer) { return ID3FID_INVOLVEDPEOPLE; } else if (type == Frame::FT_CatalogNumber || type == Frame::FT_ReleaseCountry) { return ID3FID_USERTEXT; } static int typeIdMap[Frame::FT_LastFrame + 1] = { -1, }; if (typeIdMap[0] == -1) { // first time initialization for (int i = 0; i <= ID3FID_WWWUSER; ++i) { int t = typeStrOfId[i].type; if (t <= Frame::FT_LastFrame) { typeIdMap[t] = i; } } } return type <= Frame::FT_LastFrame ? static_cast(typeIdMap[type]) : ID3FID_NOFRAME; } /** * Get id3lib frame ID for frame name. * * @param name frame name * * @return id3lib frame ID or ID3FID_NOFRAME if name not found. */ static ID3_FrameID getId3libFrameIdForName(const QString& name) { if (name.length() >= 4) { QByteArray nameBytes = name.toLatin1(); const char* nameStr = nameBytes.constData(); for (int i = 0; i <= ID3FID_WWWUSER; ++i) { const char* s = typeStrOfId[i].str; if (s && ::strncmp(s, nameStr, 4) == 0) { return static_cast(i); } } } return ID3FID_NOFRAME; } /** * Get the fields from an ID3v2 tag. * * @param id3Frame frame * @param fields the fields are appended to this list * * @return text representation of fields (Text or URL). */ static QString getFieldsFromId3Frame(ID3_Frame* id3Frame, Frame::FieldList& fields) { QString text; ID3_Frame::Iterator* iter = id3Frame->CreateIterator(); ID3_FrameID id3Id = id3Frame->GetID(); ID3_Field* id3Field; Frame::Field field; while ((id3Field = iter->GetNext()) != 0) { ID3_FieldID id = id3Field->GetID(); ID3_FieldType type = id3Field->GetType(); field.m_id = id; if (type == ID3FTY_INTEGER) { field.m_value = id3Field->Get(); } else if (type == ID3FTY_BINARY) { QByteArray ba((const char *)id3Field->GetRawBinary(), (unsigned int)id3Field->Size()); field.m_value = ba; } else if (type == ID3FTY_TEXTSTRING) { if (id == ID3FN_TEXT || id == ID3FN_DESCRIPTION || id == ID3FN_URL) { text = getString(id3Field); if (id3Id == ID3FID_CONTENTTYPE) { text = Genres::getNameString(text); } field.m_value = text; } else { field.m_value = getString(id3Field); } } else { field.m_value.clear(); } fields.push_back(field); } #ifdef Q_OS_WIN32 /* allocated in Windows DLL => must be freed in the same DLL */ ID3TagIterator_Delete(reinterpret_cast(iter)); #else #ifdef GCC_HAS_DIAGNOSTIC_PRAGMA #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor" /* Is safe because iterator implementation has default destructor */ #endif delete iter; #ifdef GCC_HAS_DIAGNOSTIC_PRAGMA #pragma GCC diagnostic pop #endif #endif return text; } /** * Get ID3v2 frame by index. * * @param tag ID3v2 tag * @param index index * * @return frame, 0 if index invalid. */ static ID3_Frame* getId3v2Frame(ID3_Tag* tag, int index) { ID3_Frame* result = 0; if (tag) { ID3_Frame* frame; ID3_Tag::Iterator* iter = tag->CreateIterator(); int i = 0; while ((frame = iter->GetNext()) != 0) { if (i == index) { result = frame; break; } ++i; } #ifdef Q_OS_WIN32 /* allocated in Windows DLL => must be freed in the same DLL */ ID3TagIterator_Delete(reinterpret_cast(iter)); #else #ifdef GCC_HAS_DIAGNOSTIC_PRAGMA #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor" /* Is safe because iterator implementation has default destructor */ #endif delete iter; #ifdef GCC_HAS_DIAGNOSTIC_PRAGMA #pragma GCC diagnostic pop #endif #endif } return result; } /** * Set the fields in an id3lib frame from the field in the frame. * * @param id3Frame id3lib frame * @param frame frame with fields */ void Mp3File::setId3v2Frame(ID3_Frame* id3Frame, const Frame& frame) const { ID3_Frame::Iterator* iter = id3Frame->CreateIterator(); ID3_FrameID id3Id = id3Frame->GetID(); ID3_TextEnc enc = ID3TE_NONE; for (Frame::FieldList::const_iterator fldIt = frame.getFieldList().begin(); fldIt != frame.getFieldList().end(); ++fldIt) { ID3_Field* id3Field = iter->GetNext(); if (!id3Field) { qDebug("early end of ID3 fields"); break; } const Frame::Field& fld = *fldIt; switch (fld.m_value.type()) { case QVariant::Int: case QVariant::UInt: { int intVal = fld.m_value.toInt(); if (fld.m_id == ID3FN_TEXTENC) { if (intVal == ID3TE_UTF8) intVal = ID3TE_UTF16; enc = static_cast(intVal); } id3Field->Set(intVal); break; } case QVariant::String: { if (enc != ID3TE_NONE) { id3Field->SetEncoding(enc); } QString value(fld.m_value.toString()); if (id3Id == ID3FID_CONTENTTYPE) { if (!TagConfig::instance().genreNotNumeric()) { value = Genres::getNumberString(value, true); } } else if (id3Id == ID3FID_TRACKNUM) { formatTrackNumberIfEnabled(value, true); } setString(id3Field, value); break; } case QVariant::ByteArray: { const QByteArray& ba = fld.m_value.toByteArray(); id3Field->Set(reinterpret_cast(ba.data()), ba.size()); break; } default: qDebug("Unknown type %d in field %d", fld.m_value.type(), fld.m_id); } } #ifdef Q_OS_WIN32 /* allocated in Windows DLL => must be freed in the same DLL */ ID3TagIterator_Delete(reinterpret_cast(iter)); #else #ifdef GCC_HAS_DIAGNOSTIC_PRAGMA #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor" /* Is safe because iterator implementation has default destructor */ #endif delete iter; #ifdef GCC_HAS_DIAGNOSTIC_PRAGMA #pragma GCC diagnostic pop #endif #endif } /** * Set a frame in the tags 2. * * @param frame frame to set * * @return true if ok. */ bool Mp3File::setFrameV2(const Frame& frame) { // If the frame has an index, change that specific frame int index = frame.getIndex(); if (index != -1 && m_tagV2) { ID3_Frame* id3Frame = getId3v2Frame(m_tagV2, index); if (id3Frame) { // If value is changed or field list is empty, // set from value, else from FieldList. if (frame.isValueChanged() || frame.getFieldList().empty()) { QString value(frame.getValue()); ID3_Field* fld; if ((fld = id3Frame->GetField(ID3FN_URL)) != 0) { if (getString(fld) != value) { fld->Set(value.toLatin1().data()); markTag2Changed(frame.getType()); } return true; } else if ((fld = id3Frame->GetField(ID3FN_TEXT)) != 0 || (fld = id3Frame->GetField(ID3FN_DESCRIPTION)) != 0) { if (id3Frame->GetID() == ID3FID_CONTENTTYPE) { if (!TagConfig::instance().genreNotNumeric()) { value = Genres::getNumberString(value, true); } } else if (id3Frame->GetID() == ID3FID_TRACKNUM) { formatTrackNumberIfEnabled(value, true); } bool hasEnc; const ID3_TextEnc enc = fld->GetEncoding(); ID3_TextEnc newEnc = static_cast( frame.getFieldValue(Frame::Field::ID_TextEnc).toInt(&hasEnc)); if (!hasEnc) { newEnc = enc; } if (newEnc != ID3TE_ISO8859_1 && newEnc != ID3TE_UTF16) { // only ISO-8859-1 and UTF16 are allowed for ID3v2.3.0 newEnc = ID3TE_UTF16; } if (newEnc == ID3TE_ISO8859_1) { // check if information is lost if the string is not unicode uint i, unicode_size = value.length(); const QChar* qcarray = value.unicode(); for (i = 0; i < unicode_size; i++) { char ch = qcarray[i].toLatin1(); if (ch == 0 || (ch & 0x80) != 0) { newEnc = ID3TE_UTF16; break; } } } if (enc != newEnc) { ID3_Field* encfld = id3Frame->GetField(ID3FN_TEXTENC); if (encfld) { encfld->Set(newEnc); } fld->SetEncoding(newEnc); markTag2Changed(frame.getType()); } if (getString(fld) != value) { setString(fld, value); markTag2Changed(frame.getType()); } return true; } else if (id3Frame->GetID() == ID3FID_PRIVATE && (fld = id3Frame->GetField(ID3FN_DATA)) != 0) { ID3_Field* ownerFld = id3Frame->GetField(ID3FN_OWNER); QString owner; QByteArray newData, oldData; if (ownerFld && !(owner = getString(ownerFld)).isEmpty() && AttributeData(owner).toByteArray(value, newData)) { oldData = QByteArray((const char *)fld->GetRawBinary(), (unsigned int)fld->Size()); if (newData != oldData) { fld->Set(reinterpret_cast(newData.data()), newData.size()); markTag2Changed(frame.getType()); } return true; } } else if (id3Frame->GetID() == ID3FID_CDID && (fld = id3Frame->GetField(ID3FN_DATA)) != 0) { QByteArray newData, oldData; if (AttributeData::isHexString(value, 'F', QLatin1String("+")) && AttributeData(AttributeData::Utf16).toByteArray(value, newData)) { oldData = QByteArray((const char *)fld->GetRawBinary(), (unsigned int)fld->Size()); if (newData != oldData) { fld->Set(reinterpret_cast(newData.data()), newData.size()); markTag2Changed(frame.getType()); } return true; } } else if (id3Frame->GetID() == ID3FID_UNIQUEFILEID && (fld = id3Frame->GetField(ID3FN_DATA)) != 0) { QByteArray newData, oldData; if (AttributeData::isHexString(value, 'Z')) { newData = (value + QLatin1Char('\0')).toLatin1(); oldData = QByteArray((const char *)fld->GetRawBinary(), (unsigned int)fld->Size()); if (newData != oldData) { fld->Set(reinterpret_cast(newData.data()), newData.size()); markTag2Changed(frame.getType()); } return true; } } else if (id3Frame->GetID() == ID3FID_POPULARIMETER && (fld = id3Frame->GetField(ID3FN_RATING)) != 0) { if (getString(fld) != value) { fld->Set(value.toInt()); markTag2Changed(frame.getType()); } return true; } } else { setId3v2Frame(id3Frame, frame); markTag2Changed(frame.getType()); return true; } } } // Try the superclass method return TaggedFile::setFrameV2(frame); } /** * Add a frame in the tags 2. * * @param frame frame to add, a field list may be added by this method * * @return true if ok. */ bool Mp3File::addFrameV2(Frame& frame) { // Add a new frame. ID3_FrameID id; if (frame.getType() != Frame::FT_Other) { id = getId3libFrameIdForType(frame.getType()); } else { id = getId3libFrameIdForName(frame.getName()); if (id == ID3FID_NOFRAME) { if (frame.getName() == QLatin1String("AverageLevel") || frame.getName() == QLatin1String("PeakValue") || frame.getName().startsWith(QLatin1String("WM/"))) { id = ID3FID_PRIVATE; } else if (frame.getName().startsWith(QLatin1String("iTun"))) { id = ID3FID_COMMENT; } else { id = ID3FID_USERTEXT; } } } if (id != ID3FID_NOFRAME && id != ID3FID_SETSUBTITLE && m_tagV2) { ID3_Frame* id3Frame = new ID3_Frame(id); ID3_Field* fld = id3Frame->GetField(ID3FN_TEXT); if (fld) { ID3_TextEnc enc = getDefaultTextEncoding(); ID3_Field* encfld = id3Frame->GetField(ID3FN_TEXTENC); if (encfld) { encfld->Set(enc); } fld->SetEncoding(enc); } if (id == ID3FID_USERTEXT && !frame.getName().startsWith(QLatin1String("TXXX"))) { fld = id3Frame->GetField(ID3FN_DESCRIPTION); if (fld) { QString description; if (frame.getType() == Frame::FT_CatalogNumber) { description = QLatin1String("CATALOGNUMBER"); } else if (frame.getType() == Frame::FT_ReleaseCountry) { description = QLatin1String("RELEASECOUNTRY"); } else { description = frame.getName(); } setString(fld, description); } } else if (id == ID3FID_COMMENT && frame.getType() == Frame::FT_Other) { fld = id3Frame->GetField(ID3FN_DESCRIPTION); if (fld) { setString(fld, frame.getName()); } } else if (id == ID3FID_PRIVATE && !frame.getName().startsWith(QLatin1String("PRIV"))) { fld = id3Frame->GetField(ID3FN_OWNER); if (fld) { setString(fld, frame.getName()); QByteArray data; if (AttributeData(frame.getName()).toByteArray(frame.getValue(), data)) { fld = id3Frame->GetField(ID3FN_DATA); if (fld) { fld->Set(reinterpret_cast(data.data()), data.size()); } } } } else if (id == ID3FID_UNIQUEFILEID) { QByteArray data; if (AttributeData::isHexString(frame.getValue(), 'Z')) { data = (frame.getValue() + QLatin1Char('\0')).toLatin1(); fld = id3Frame->GetField(ID3FN_DATA); if (fld) { fld->Set(reinterpret_cast(data.data()), data.size()); } } } else if (id == ID3FID_PICTURE) { fld = id3Frame->GetField(ID3FN_MIMETYPE); if (fld) { setString(fld, QLatin1String("image/jpeg")); } fld = id3Frame->GetField(ID3FN_PICTURETYPE); if (fld) { fld->Set(ID3PT_COVERFRONT); } } if (!frame.fieldList().empty()) { setId3v2Frame(id3Frame, frame); } Frame::Type type; const char* name; getTypeStringForId3libFrameId(id, type, name); m_tagV2->AttachFrame(id3Frame); frame.setExtendedType(Frame::ExtendedType(type, QString::fromLatin1(name))); frame.setIndex(m_tagV2->NumFrames() - 1); if (frame.fieldList().empty()) { // add field list to frame getFieldsFromId3Frame(id3Frame, frame.fieldList()); frame.setFieldListFromValue(); } markTag2Changed(frame.getType()); return true; } // Try the superclass method return TaggedFile::addFrameV2(frame); } /** * Delete a frame in the tags 2. * * @param frame frame to delete. * * @return true if ok. */ bool Mp3File::deleteFrameV2(const Frame& frame) { // If the frame has an index, delete that specific frame int index = frame.getIndex(); if (index != -1 && m_tagV2) { ID3_Frame* id3Frame = getId3v2Frame(m_tagV2, index); if (id3Frame) { m_tagV2->RemoveFrame(id3Frame); markTag2Changed(frame.getType()); return true; } } // Try the superclass method return TaggedFile::deleteFrameV2(frame); } /** * Remove ID3v2 frames. * * @param flt filter specifying which frames to remove */ void Mp3File::deleteFramesV2(const FrameFilter& flt) { if (m_tagV2) { if (flt.areAllEnabled()) { ID3_Tag::Iterator* iter = m_tagV2->CreateIterator(); ID3_Frame* frame; while ((frame = iter->GetNext()) != NULL) { m_tagV2->RemoveFrame(frame); } #ifdef Q_OS_WIN32 /* allocated in Windows DLL => must be freed in the same DLL */ ID3TagIterator_Delete(reinterpret_cast(iter)); #else #ifdef GCC_HAS_DIAGNOSTIC_PRAGMA #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor" /* Is safe because iterator implementation has default destructor */ #endif delete iter; #ifdef GCC_HAS_DIAGNOSTIC_PRAGMA #pragma GCC diagnostic pop #endif #endif markTag2Changed(Frame::FT_UnknownFrame); } else { ID3_Tag::Iterator* iter = m_tagV2->CreateIterator(); ID3_Frame* frame; while ((frame = iter->GetNext()) != NULL) { Frame::Type type; const char* name; getTypeStringForId3libFrameId(frame->GetID(), type, name); if (flt.isEnabled(type, QString::fromLatin1(name))) { m_tagV2->RemoveFrame(frame); } } #ifdef Q_OS_WIN32 /* allocated in Windows DLL => must be freed in the same DLL */ ID3TagIterator_Delete(reinterpret_cast(iter)); #else #ifdef GCC_HAS_DIAGNOSTIC_PRAGMA #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor" /* Is safe because iterator implementation has default destructor */ #endif delete iter; #ifdef GCC_HAS_DIAGNOSTIC_PRAGMA #pragma GCC diagnostic pop #endif #endif markTag2Changed(Frame::FT_UnknownFrame); } } } /** * Get all frames in tag 2. * * @param frames frame collection to set. */ void Mp3File::getAllFramesV2(FrameCollection& frames) { frames.clear(); if (m_tagV2) { ID3_Tag::Iterator* iter = m_tagV2->CreateIterator(); ID3_Frame* id3Frame; int i = 0; Frame::Type type; const char* name; while ((id3Frame = iter->GetNext()) != 0) { getTypeStringForId3libFrameId(id3Frame->GetID(), type, name); Frame frame(type, QLatin1String(""), QString::fromLatin1(name), i++); frame.setValue(getFieldsFromId3Frame(id3Frame, frame.fieldList())); if (id3Frame->GetID() == ID3FID_USERTEXT || id3Frame->GetID() == ID3FID_WWWUSER || id3Frame->GetID() == ID3FID_COMMENT) { QVariant fieldValue = frame.getFieldValue(Frame::Field::ID_Description); if (fieldValue.isValid()) { QString description = fieldValue.toString(); if (!description.isEmpty()) { if (description == QLatin1String("CATALOGNUMBER")) { frame.setType(Frame::FT_CatalogNumber); } else if (description == QLatin1String("RELEASECOUNTRY")) { frame.setType(Frame::FT_ReleaseCountry); } else { frame.setExtendedType(Frame::ExtendedType(Frame::FT_Other, QString::fromLatin1(name) + QLatin1Char('\n') + description)); } } } } else if (id3Frame->GetID() == ID3FID_PRIVATE) { const Frame::FieldList& fields = frame.getFieldList(); QString owner; QByteArray data; for (Frame::FieldList::const_iterator it = fields.begin(); it != fields.end(); ++it) { if ((*it).m_id == Frame::Field::ID_Owner) { owner = (*it).m_value.toString(); if (!owner.isEmpty()) { frame.setExtendedType(Frame::ExtendedType(Frame::FT_Other, QString::fromLatin1(name) + QLatin1Char('\n') + owner)); } } else if ((*it).m_id == Frame::Field::ID_Data) { data = (*it).m_value.toByteArray(); } } if (!owner.isEmpty() && !data.isEmpty()) { QString str; if (AttributeData(owner).toString(data, str)) { frame.setValue(str); } } } else if (id3Frame->GetID() == ID3FID_CDID) { QVariant fieldValue = frame.getFieldValue(Frame::Field::ID_Data); if (fieldValue.isValid()) { QString str; if (AttributeData(AttributeData::Utf16).toString(fieldValue.toByteArray(), str) && AttributeData::isHexString(str, 'F', QLatin1String("+"))) { frame.setValue(str); } } } else if (id3Frame->GetID() == ID3FID_UNIQUEFILEID) { QVariant fieldValue = frame.getFieldValue(Frame::Field::ID_Data); if (fieldValue.isValid()) { QByteArray ba(fieldValue.toByteArray()); QString str(QString::fromLatin1(ba)); if (ba.size() - str.length() <= 1 && AttributeData::isHexString(str, 'Z')) { frame.setValue(str); } } } else if (id3Frame->GetID() == ID3FID_POPULARIMETER) { QVariant fieldValue = frame.getFieldValue(Frame::Field::ID_Rating); if (fieldValue.isValid()) { QString str(fieldValue.toString()); if (!str.isEmpty()) { frame.setValue(str); } } } frames.insert(frame); } #ifdef Q_OS_WIN32 /* allocated in Windows DLL => must be freed in the same DLL */ ID3TagIterator_Delete(reinterpret_cast(iter)); #else #ifdef GCC_HAS_DIAGNOSTIC_PRAGMA #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor" /* Is safe because iterator implementation has default destructor */ #endif delete iter; #ifdef GCC_HAS_DIAGNOSTIC_PRAGMA #pragma GCC diagnostic pop #endif #endif } frames.addMissingStandardFrames(); } /** * Get a list of frame IDs which can be added. * * @return list with frame IDs. */ QStringList Mp3File::getFrameIds() const { QStringList lst; for (int type = Frame::FT_FirstFrame; type <= Frame::FT_LastFrame; ++type) { if (type != Frame::FT_Part) { lst.append(Frame::ExtendedType(static_cast(type), QLatin1String("")). getTranslatedName()); } } for (int i = 0; i <= ID3FID_WWWUSER; ++i) { if (typeStrOfId[i].type == Frame::FT_Other) { const char* s = typeStrOfId[i].str; if (s) { lst.append(QCoreApplication::translate("@default", s)); } } } return lst; } /** * Set the text codec to be used for tag 1. * * @param codec text codec, 0 to use default (ISO 8859-1) */ void Mp3File::setTextCodecV1(const QTextCodec* codec) { s_textCodecV1 = codec; } /** * Set the default text encoding. * * @param textEnc default text encoding */ void Mp3File::setDefaultTextEncoding(TagConfig::TextEncoding textEnc) { // UTF8 encoding is buggy, so UTF16 is used when UTF8 is configured s_defaultTextEncoding = textEnc == TagConfig::TE_ISO8859_1 ? ID3TE_ISO8859_1 : ID3TE_UTF16; } /** * Notify about configuration change. * This method shall be called when the configuration changes. */ void Mp3File::notifyConfigurationChange() { const QTextCodec* id3v1TextCodec = TagConfig::instance().textEncodingV1() != QLatin1String("ISO-8859-1") ? QTextCodec::codecForName(TagConfig::instance().textEncodingV1().toLatin1().data()) : 0; setDefaultTextEncoding( static_cast(TagConfig::instance().textEncoding())); setTextCodecV1(id3v1TextCodec); } kid3-3.0.2/src/plugins/id3libmetadata/mp3file.h000066400000000000000000000251021224603511300211720ustar00rootroot00000000000000/** * \file mp3file.h * Handling of tagged MP3 files. * * \b Project: Kid3 * \author Urs Fleisch * \date 9 Jan 2003 * * Copyright (C) 2003-2011 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MP3FILE_H #define MP3FILE_H #include "taggedfile.h" #include "tagconfig.h" class ID3_Tag; class ID3_Field; class ID3_Frame; class QTextCodec; /** List box item containing MP3 file */ class Mp3File : public TaggedFile { public: /** * Constructor. * * @param dn directory name * @param fn filename * @param idx model index */ Mp3File(const QString& dn, const QString& fn, const QPersistentModelIndex& idx); /** * Destructor. */ virtual ~Mp3File(); /** * Get key of tagged file format. * @return "Id3libMetadata". */ virtual QString taggedFileKey() const; /** * Get features supported. * @return bit mask with Feature flags set. */ virtual int taggedFileFeatures() const; /** * Read tags from file. * * @param force true to force reading even if tags were already read. */ virtual void readTags(bool force); /** * Write tags to file and rename it if necessary. * * @param force true to force writing even if file was not changed. * @param renamed will be set to true if the file was renamed, * i.e. the file name is no longer valid, else *renamed * is left unchanged * @param preserve true to preserve file time stamps * * @return true if ok, false if the file could not be written or renamed. */ virtual bool writeTags(bool force, bool* renamed, bool preserve); /** * Remove ID3v1 frames. * * @param flt filter specifying which frames to remove */ virtual void deleteFramesV1(const FrameFilter& flt); /** * Remove ID3v2 frames. * * @param flt filter specifying which frames to remove */ virtual void deleteFramesV2(const FrameFilter& flt); /** * Get ID3v1 title. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ virtual QString getTitleV1(); /** * Get ID3v1 artist. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ virtual QString getArtistV1(); /** * Get ID3v1 album. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ virtual QString getAlbumV1(); /** * Get ID3v1 comment. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ virtual QString getCommentV1(); /** * Get ID3v1 year. * * @return number, * 0 if the field does not exist, * -1 if the tags do not exist. */ virtual int getYearV1(); /** * Get ID3v1 track. * * @return number, * 0 if the field does not exist, * -1 if the tags do not exist. */ virtual int getTrackNumV1(); /** * Get ID3v1 genre. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ virtual QString getGenreV1(); /** * Get ID3v2 title. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ virtual QString getTitleV2(); /** * Get ID3v2 artist. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ virtual QString getArtistV2(); /** * Get ID3v2 album. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ virtual QString getAlbumV2(); /** * Get ID3v2 comment. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ virtual QString getCommentV2(); /** * Get ID3v2 year. * * @return number, * 0 if the field does not exist, * -1 if the tags do not exist. */ virtual int getYearV2(); /** * Get ID3v2 track. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ virtual QString getTrackV2(); /** * Get ID3v2 genre as text. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ virtual QString getGenreV2(); /** * Set ID3v1 title. * * @param str string to set, "" to remove field. */ virtual void setTitleV1(const QString& str); /** * Set ID3v1 artist. * * @param str string to set, "" to remove field. */ virtual void setArtistV1(const QString& str); /** * Set ID3v1 album. * * @param str string to set, "" to remove field. */ virtual void setAlbumV1(const QString& str); /** * Set ID3v1 comment. * * @param str string to set, "" to remove field. */ virtual void setCommentV1(const QString& str); /** * Set ID3v1 year. * * @param num number to set, 0 to remove field. */ virtual void setYearV1(int num); /** * Set ID3v1 track. * * @param num number to set, 0 to remove field. */ virtual void setTrackNumV1(int num); /** * Set ID3v1 genre as text. * * @param str string to set, "" to remove field, QString::null to ignore. */ virtual void setGenreV1(const QString& str); /** * Set ID3v2 title. * * @param str string to set, "" to remove field. */ virtual void setTitleV2(const QString& str); /** * Set ID3v2 artist. * * @param str string to set, "" to remove field. */ virtual void setArtistV2(const QString& str); /** * Set ID3v2 album. * * @param str string to set, "" to remove field. */ virtual void setAlbumV2(const QString& str); /** * Set ID3v2 comment. * * @param str string to set, "" to remove field. */ virtual void setCommentV2(const QString& str); /** * Set ID3v2 year. * * @param num number to set, 0 to remove field. */ virtual void setYearV2(int num); /** * Set ID3v2 track. * * @param track string to set, "" to remove field, QString::null to ignore. */ virtual void setTrackV2(const QString& track); /** * Set ID3v2 genre as text. * * @param str string to set, "" to remove field, QString::null to ignore. */ virtual void setGenreV2(const QString& str); /** * Check if tag information has already been read. * * @return true if information is available, * false if the tags have not been read yet, in which case * hasTagV1() and hasTagV2() do not return meaningful information. */ virtual bool isTagInformationRead() const; /** * Check if file has an ID3v1 tag. * * @return true if a V1 tag is available. * @see isTagInformationRead() */ virtual bool hasTagV1() const; /** * Check if file has an ID3v2 tag. * * @return true if a V2 tag is available. * @see isTagInformationRead() */ virtual bool hasTagV2() const; /** * Check if ID3v1 tags are supported by the format of this file. * * @return true. */ virtual bool isTagV1Supported() const; /** * Get technical detail information. * * @param info the detail information is returned here */ virtual void getDetailInfo(DetailInfo& info) const; /** * Get duration of file. * * @return duration in seconds, * 0 if unknown. */ virtual unsigned getDuration() const; /** * Get file extension including the dot. * * @return file extension ".mp3". */ virtual QString getFileExtension() const; /** * Get the format of tag 1. * * @return string describing format of tag 1, * e.g. "ID3v1.1". */ virtual QString getTagFormatV1() const; /** * Get the format of tag 2. * * @return string describing format of tag 2, * e.g. "ID3v2.3", "ID3v2.4". */ virtual QString getTagFormatV2() const; /** * Set a frame in the tags 2. * * @param frame frame to set * * @return true if ok. */ virtual bool setFrameV2(const Frame& frame); /** * Add a frame in the tags 2. * * @param frame frame to add, a field list may be added by this method * * @return true if ok. */ virtual bool addFrameV2(Frame& frame); /** * Delete a frame in the tags 2. * * @param frame frame to delete. * * @return true if ok. */ virtual bool deleteFrameV2(const Frame& frame); /** * Get all frames in tag 2. * * @param frames frame collection to set. */ virtual void getAllFramesV2(FrameCollection& frames); /** * Get a list of frame IDs which can be added. * * @return list with frame IDs. */ virtual QStringList getFrameIds() const; /** * Notify about configuration change. * This method shall be called when the configuration changes. */ static void notifyConfigurationChange(); private: /** * Set track. * * @param tag ID3 tag * @param num number to set, 0 to remove field. * @param numTracks total number of tracks, <=0 to ignore * * @return true if the field was changed. */ bool setTrackNum(ID3_Tag* tag, int num, int numTracks = 0) const; /** * Set the fields in an id3lib frame from the field in the frame. * * @param id3Frame id3lib frame * @param frame frame with fields */ void setId3v2Frame(ID3_Frame* id3Frame, const Frame& frame) const; /** * Set the text codec to be used for tag 1. * * @param codec text codec, 0 to use default (ISO 8859-1) */ static void setTextCodecV1(const QTextCodec* codec); /** * Set the default text encoding. * * @param textEnc default text encoding */ static void setDefaultTextEncoding(TagConfig::TextEncoding textEnc); Mp3File(const Mp3File&); Mp3File& operator=(const Mp3File&); /** ID3v1 tags */ ID3_Tag* m_tagV1; /** ID3v2 tags */ ID3_Tag* m_tagV2; }; #endif // MP3FILE_H kid3-3.0.2/src/plugins/importplugins.h.cmake000066400000000000000000000002301224603511300207520ustar00rootroot00000000000000/* importplugins.h. Generated by cmake from importplugins.h.cmake */ #ifndef IMPORTPLUGINS_H #define IMPORTPLUGINS_H @CFG_IMPORT_PLUGIN_CALLS@ #endif kid3-3.0.2/src/plugins/mp4v2metadata/000077500000000000000000000000001224603511300172645ustar00rootroot00000000000000kid3-3.0.2/src/plugins/mp4v2metadata/CMakeLists.txt000066400000000000000000000050731224603511300220310ustar00rootroot00000000000000if(WITH_MP4V2) find_path(MP4V2_INCLUDE_DIR mp4.h /usr/include/ /usr/local/include/ ) find_path(MP4V2_MP4V2_INCLUDE_DIR mp4v2/mp4v2.h /usr/include/ /usr/local/include/ ) find_library(MP4V2_LIBRARIES NAMES mp4v2 PATHS /usr/lib /usr/local/lib ) if (MP4V2_MP4V2_INCLUDE_DIR) set(MP4V2_INCLUDE_DIR ${MP4V2_MP4V2_INCLUDE_DIR}) set(HAVE_MP4V2_MP4V2_H 1) endif (MP4V2_MP4V2_INCLUDE_DIR) if (MP4V2_INCLUDE_DIR AND MP4V2_LIBRARIES) message(STATUS "mp4v2 found: ${MP4V2_LIBRARIES}") set(HAVE_MP4V2 1) set(MP4V2_FOUND TRUE) set(_CMAKE_REQUIRED_LIBRARIES_TMP ${CMAKE_REQUIRED_LIBRARIES}) set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} ${MP4V2_LIBRARIES}) set(_CMAKE_REQUIRED_INCLUDES_TMP ${CMAKE_REQUIRED_INCLUDES}) set(CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES} ${MP4V2_INCLUDE_DIR}) if (MP4V2_MP4V2_INCLUDE_DIR) CHECK_CXX_SOURCE_COMPILES("#include \nint main() {\n MP4FileHandle hFile;\n uint32_t index;\n char* ppName;\n uint8_t* ppValue;\n uint32_t pValueSize;\n MP4GetMetadataByIndex(hFile, index, &ppName, &ppValue, &pValueSize);\n return 0;\n}\n" HAVE_MP4V2_MP4GETMETADATABYINDEX_CHARPP_ARG) else (MP4V2_MP4V2_INCLUDE_DIR) CHECK_CXX_SOURCE_COMPILES("#include \nint main() {\n MP4FileHandle hFile;\n u_int32_t index;\n char* ppName;\n u_int8_t* ppValue;\n u_int32_t pValueSize;\n MP4GetMetadataByIndex(hFile, index, &ppName, &ppValue, &pValueSize);\n return 0;\n}\n" HAVE_MP4V2_MP4GETMETADATABYINDEX_CHARPP_ARG) endif (MP4V2_MP4V2_INCLUDE_DIR) set(CMAKE_REQUIRED_LIBRARIES ${_CMAKE_REQUIRED_LIBRARIES_TMP}) set(CMAKE_REQUIRED_INCLUDES ${_CMAKE_REQUIRED_INCLUDES_TMP}) else (MP4V2_INCLUDE_DIR AND MP4V2_LIBRARIES) message(STATUS "Could not find mp4v2") endif (MP4V2_INCLUDE_DIR AND MP4V2_LIBRARIES) configure_file(mp4v2config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/mp4v2config.h) include_directories(${CMAKE_CURRENT_BINARY_DIR} ${MP4V2_INCLUDE_DIR} ../../core/tags) set(plugin_SRCS mp4v2metadataplugin.cpp m4afile.cpp ) set(plugin_MOC_HDRS mp4v2metadataplugin.h ) set(plugin_NAME Mp4v2Metadata) if (WITH_GCC_PCH) add_definitions(${GCC_PCH_COMPILE_FLAGS}) endif (WITH_GCC_PCH) string(TOLOWER ${plugin_NAME} plugin_TARGET) qt4_wrap_cpp(plugin_GEN_MOC_SRCS ${plugin_MOC_HDRS}) add_library(${plugin_TARGET} ${plugin_SRCS} ${plugin_GEN_MOC_SRCS}) target_link_libraries(${plugin_TARGET} kid3-core ${BASIC_LIBRARIES} ${MP4V2_LIBRARIES}) INSTALL_KID3_PLUGIN(${plugin_TARGET} ${plugin_NAME}) endif(WITH_MP4V2) kid3-3.0.2/src/plugins/mp4v2metadata/m4afile.cpp000066400000000000000000001237661224603511300213300ustar00rootroot00000000000000/** * \file m4afile.cpp * Handling of MPEG-4 audio files. * * \b Project: Kid3 * \author Urs Fleisch * \date 25 Oct 2007 * * Copyright (C) 2007-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "m4afile.h" #include "mp4v2config.h" #include #include #include #include #ifdef Q_OS_WIN32 #include #else #include #endif #include #ifdef HAVE_MP4V2_MP4V2_H #include #else #include #endif #include #include #include "genres.h" #include "pictureframe.h" /** MPEG4IP version as 16-bit hex number with major and minor version. */ #if defined MP4V2_PROJECT_version_major && defined MP4V2_PROJECT_version_minor #define MPEG4IP_MAJOR_MINOR_VERSION ((MP4V2_PROJECT_version_major << 8) | MP4V2_PROJECT_version_minor) #elif defined MPEG4IP_MAJOR_VERSION && defined MPEG4IP_MINOR_VERSION #define MPEG4IP_MAJOR_MINOR_VERSION ((MPEG4IP_MAJOR_VERSION << 8) | MPEG4IP_MINOR_VERSION) #else #define MPEG4IP_MAJOR_MINOR_VERSION 0x0009 #endif #if MPEG4IP_MAJOR_MINOR_VERSION < 0x0200 /** Set content ID. */ #define MP4TagsSetContentID MP4TagsSetCNID /** Set artist ID. */ #define MP4TagsSetArtistID MP4TagsSetATID /** Set playlist ID. */ #define MP4TagsSetPlaylistID MP4TagsSetPLID /** Set genre ID. */ #define MP4TagsSetGenreID MP4TagsSetGEID #endif /** * Constructor. * * @param dn directory name * @param fn filename * @param idx model index */ M4aFile::M4aFile(const QString& dn, const QString& fn, const QPersistentModelIndex& idx) : TaggedFile(dn, fn, idx), m_fileRead(false) { } /** * Destructor. */ M4aFile::~M4aFile() { } /** Mapping between frame types and field names. */ static const struct { const char* name; Frame::Type type; } nameTypes[] = { { "\251nam", Frame::FT_Title }, { "\251ART", Frame::FT_Artist }, { "\251wrt", Frame::FT_Composer }, { "\251alb", Frame::FT_Album }, { "\251day", Frame::FT_Date }, { "\251enc", Frame::FT_EncodedBy }, { "\251cmt", Frame::FT_Comment }, { "\251gen", Frame::FT_Genre }, { "trkn", Frame::FT_Track }, { "disk", Frame::FT_Disc }, { "gnre", Frame::FT_Genre }, { "cpil", Frame::FT_Compilation }, { "tmpo", Frame::FT_Bpm }, #if MPEG4IP_MAJOR_MINOR_VERSION >= 0x0105 { "\251grp", Frame::FT_Grouping }, #endif #if MPEG4IP_MAJOR_MINOR_VERSION >= 0x0106 { "aART", Frame::FT_AlbumArtist }, { "pgap", Frame::FT_Other }, #endif #if MPEG4IP_MAJOR_MINOR_VERSION >= 0x0109 { "cprt", Frame::FT_Copyright }, { "\251lyr", Frame::FT_Lyrics }, { "tvsh", Frame::FT_Other }, { "tvnn", Frame::FT_Other }, { "tven", Frame::FT_Other }, { "tvsn", Frame::FT_Other }, { "tves", Frame::FT_Other }, { "desc", Frame::FT_Other }, { "ldes", Frame::FT_Other }, { "sonm", Frame::FT_SortName }, { "soar", Frame::FT_SortArtist }, { "soaa", Frame::FT_SortAlbumArtist }, { "soal", Frame::FT_SortAlbum }, { "soco", Frame::FT_SortComposer }, { "sosn", Frame::FT_Other }, { "\251too", Frame::FT_EncoderSettings }, { "purd", Frame::FT_Other }, { "pcst", Frame::FT_Other }, { "keyw", Frame::FT_Other }, { "catg", Frame::FT_Other }, { "hdvd", Frame::FT_Other }, { "stik", Frame::FT_Other }, { "rtng", Frame::FT_Other }, { "apID", Frame::FT_Other }, { "akID", Frame::FT_Other }, { "sfID", Frame::FT_Other }, { "cnID", Frame::FT_Other }, { "atID", Frame::FT_Other }, { "plID", Frame::FT_Other }, { "geID", Frame::FT_Other }, #endif { "covr", Frame::FT_Picture } }, freeFormNameTypes[] = { #if !(MPEG4IP_MAJOR_MINOR_VERSION >= 0x0105) { "GROUPING", Frame::FT_Grouping }, #endif #if !(MPEG4IP_MAJOR_MINOR_VERSION >= 0x0106) { "ALBUMARTIST", Frame::FT_AlbumArtist }, #endif { "ARRANGER", Frame::FT_Arranger }, { "AUTHOR", Frame::FT_Author }, { "CATALOGNUMBER", Frame::FT_CatalogNumber }, { "CONDUCTOR", Frame::FT_Conductor }, { "ENCODINGTIME", Frame::FT_EncodingTime }, { "INITIALKEY", Frame::FT_InitialKey }, #if !(MPEG4IP_MAJOR_MINOR_VERSION >= 0x0109) { "COPYRIGHT", Frame::FT_Copyright }, #endif { "ISRC", Frame::FT_Isrc }, { "LANGUAGE", Frame::FT_Language }, { "LYRICIST", Frame::FT_Lyricist }, #if !(MPEG4IP_MAJOR_MINOR_VERSION >= 0x0109) { "LYRICS", Frame::FT_Lyrics }, #endif { "MOOD", Frame::FT_Mood }, { "SOURCEMEDIA", Frame::FT_Media }, { "ORIGINALALBUM", Frame::FT_OriginalAlbum }, { "ORIGINALARTIST", Frame::FT_OriginalArtist }, { "ORIGINALDATE", Frame::FT_OriginalDate }, { "PART", Frame::FT_Part }, { "PERFORMER", Frame::FT_Performer }, { "PUBLISHER", Frame::FT_Publisher }, { "RELEASECOUNTRY", Frame::FT_ReleaseCountry }, { "REMIXER", Frame::FT_Remixer }, { "SUBTITLE", Frame::FT_Subtitle }, { "WEBSITE", Frame::FT_Website }, { "WWWAUDIOFILE", Frame::FT_WWWAudioFile }, { "WWWAUDIOSOURCE", Frame::FT_WWWAudioSource } }; /** * Get the predefined field name for a type. * * @param type frame type * * @return field name, QString::null if not defined. */ static QString getNameForType(Frame::Type type) { static QMap typeNameMap; if (typeNameMap.empty()) { // first time initialization for (unsigned i = 0; i < sizeof(nameTypes) / sizeof(nameTypes[0]); ++i) { if (nameTypes[i].type != Frame::FT_Other) { typeNameMap.insert(nameTypes[i].type, QString::fromLatin1(nameTypes[i].name)); } } for (unsigned i = 0; i < sizeof(freeFormNameTypes) / sizeof(freeFormNameTypes[0]); ++i) { typeNameMap.insert(freeFormNameTypes[i].type, QString::fromLatin1(freeFormNameTypes[i].name)); } } if (type != Frame::FT_Other) { QMap::const_iterator it = typeNameMap.find(type); if (it != typeNameMap.end()) { return *it; } } return QString(); } /** * Get the type for a predefined field name. * * @param name field name * @param onlyPredefined if true, FT_Unknown is returned for fields which * are not predefined, else FT_Other * * @return type, FT_Other for "cpil", * FT_Unknown or FT_Other if not predefined field. */ static Frame::Type getTypeForName(const QString& name, bool onlyPredefined = false) { if (name.length() == 4) { static QMap nameTypeMap; if (nameTypeMap.empty()) { // first time initialization for (unsigned i = 0; i < sizeof(nameTypes) / sizeof(nameTypes[0]); ++i) { nameTypeMap.insert(QString::fromLatin1(nameTypes[i].name), nameTypes[i].type); } } QMap::const_iterator it = nameTypeMap.find(name); if (it != nameTypeMap.end()) { return *it; } } if (!onlyPredefined) { static QMap freeFormNameTypeMap; if (freeFormNameTypeMap.empty()) { // first time initialization for (unsigned i = 0; i < sizeof(freeFormNameTypes) / sizeof(freeFormNameTypes[0]); ++i) { freeFormNameTypeMap.insert(QString::fromLatin1(freeFormNameTypes[i].name), freeFormNameTypes[i].type); } } QMap::const_iterator it = freeFormNameTypeMap.find(name); if (it != freeFormNameTypeMap.end()) { return *it; } return Frame::FT_Other; } return Frame::FT_UnknownFrame; } #if MPEG4IP_MAJOR_MINOR_VERSION >= 0x0109 #elif defined HAVE_MP4V2_MP4GETMETADATABYINDEX_CHARPP_ARG #else /** * Check if a name is a free form field. * * @param hFile handle * @param name field name * * @return true if a free form field. */ static bool isFreeFormMetadata(MP4FileHandle hFile, const char* name) { bool result = false; if (getTypeForName(name, true) == Frame::FT_UnknownFrame) { uint8_t* pValue = 0; uint32_t valueSize = 0; result = MP4GetMetadataFreeForm(hFile, const_cast(name), &pValue, &valueSize); if (pValue && valueSize > 0) { free(pValue); } } return result; } #endif /** * Get a byte array for a value. * * @param name field name * @param value field value * @param size size of value in bytes * * @return byte array with string representation. */ static QByteArray getValueByteArray(const char* name, const uint8_t* value, uint32_t size) { QByteArray str; if (name[0] == '\251') { str = QByteArray(reinterpret_cast(value), size); } else if (std::strcmp(name, "trkn") == 0) { if (size >= 6) { unsigned track = value[3] + (value[2] << 8); unsigned totalTracks = value[5] + (value[4] << 8); str.setNum(track); if (totalTracks > 0) { str += '/'; str += QByteArray().setNum(totalTracks); } } } else if (std::strcmp(name, "disk") == 0) { if (size >= 6) { unsigned disk = value[3] + (value[2] << 8); unsigned totalDisks = value[5] + (value[4] << 8); str.setNum(disk); if (totalDisks > 0) { str += '/'; str += QByteArray().setNum(totalDisks); } } } else if (std::strcmp(name, "gnre") == 0) { if (size >= 2) { unsigned genreNum = value[1] + (value[0] << 8); if (genreNum > 0) { str = Genres::getName(genreNum - 1); } } } else if (std::strcmp(name, "cpil") == 0) { if (size >= 1) { str.setNum(value[0]); } } else if (std::strcmp(name, "tmpo") == 0) { if (size >= 2) { unsigned bpm = value[1] + (value[0] << 8); if (bpm > 0) { str.setNum(bpm); } } } else if (std::strcmp(name, "covr") == 0) { QByteArray ba; ba = QByteArray(reinterpret_cast(value), size); return ba; #if MPEG4IP_MAJOR_MINOR_VERSION >= 0x0106 } else if (std::strcmp(name, "pgap") == 0) { if (size >= 1) { str.setNum(value[0]); } #endif #if MPEG4IP_MAJOR_MINOR_VERSION >= 0x0109 } else if (std::strcmp(name, "tvsn") == 0) { if (size >= 4) { uint val = value[3] + (value[2] << 8) + (value[1] << 16) + (value[0] << 24); if (val > 0) { str.setNum(val); } } } else if (std::strcmp(name, "tves") == 0) { if (size >= 4) { uint val = value[3] + (value[2] << 8) + (value[1] << 16) + (value[0] << 24); if (val > 0) { str.setNum(val); } } } else if (std::strcmp(name, "pcst") == 0) { if (size >= 1) { str.setNum(value[0]); } } else if (std::strcmp(name, "hdvd") == 0) { if (size >= 1) { str.setNum(value[0]); } } else if (std::strcmp(name, "stik") == 0) { if (size >= 1) { str.setNum(value[0]); } } else if (std::strcmp(name, "rtng") == 0) { if (size >= 1) { str.setNum(value[0]); } } else if (std::strcmp(name, "akID") == 0) { if (size >= 1) { str.setNum(value[0]); } } else if (std::strcmp(name, "sfID") == 0) { if (size >= 4) { uint val = value[3] + (value[2] << 8) + (value[1] << 16) + (value[0] << 24); if (val > 0) { str.setNum(val); } } } else if (std::strcmp(name, "cnID") == 0) { if (size >= 4) { uint val = value[3] + (value[2] << 8) + (value[1] << 16) + (value[0] << 24); if (val > 0) { str.setNum(val); } } } else if (std::strcmp(name, "atID") == 0) { if (size >= 4) { uint val = value[3] + (value[2] << 8) + (value[1] << 16) + (value[0] << 24); if (val > 0) { str.setNum(val); } } } else if (std::strcmp(name, "plID") == 0) { if (size >= 8) { qulonglong val = (qulonglong)value[7] + ((qulonglong)value[6] << 8) + ((qulonglong)value[5] << 16) + ((qulonglong)value[4] << 24) + ((qulonglong)value[3] << 32) + ((qulonglong)value[2] << 40) + ((qulonglong)value[1] << 48) + ((qulonglong)value[0] << 56); if (val > 0) { str.setNum(val); } } } else if (std::strcmp(name, "geID") == 0) { if (size >= 4) { uint val = value[3] + (value[2] << 8) + (value[1] << 16) + (value[0] << 24); if (val > 0) { str.setNum(val); } } #endif } else { str = QByteArray(reinterpret_cast(value), size); } return str; } /** * Get key of tagged file format. * @return "Mp4v2Metadata". */ QString M4aFile::taggedFileKey() const { return QLatin1String("Mp4v2Metadata"); } /** * Read tags from file. * * @param force true to force reading even if tags were already read. */ void M4aFile::readTags(bool force) { if (force || !m_fileRead) { m_metadata.clear(); markTag2Unchanged(); m_fileRead = true; QByteArray fnIn = QFile::encodeName( getDirname() + QDir::separator() + currentFilename()); MP4FileHandle handle = MP4Read(fnIn); if (handle != MP4_INVALID_FILE_HANDLE) { m_fileInfo.read(handle); #if MPEG4IP_MAJOR_MINOR_VERSION >= 0x0109 MP4ItmfItemList* list = MP4ItmfGetItems(handle); if (list) { for (uint32_t i = 0; i < list->size; ++i) { MP4ItmfItem& item = list->elements[i]; const char* key = 0; if (memcmp(item.code, "----", 4) == 0) { // free form tagfield if (item.name) { key = item.name; } } else { key = item.code; } if (key) { QByteArray ba; if (item.dataList.size > 0 && item.dataList.elements[0].value && item.dataList.elements[0].valueSize > 0) { ba = getValueByteArray(key, item.dataList.elements[0].value, item.dataList.elements[0].valueSize); } m_metadata[QString::fromLatin1(key)] = ba; } } MP4ItmfItemListFree(list); } #elif defined HAVE_MP4V2_MP4GETMETADATABYINDEX_CHARPP_ARG static char notFreeFormStr[] = "NOFF"; static char freeFormStr[] = "----"; char* ppName; uint8_t* ppValue = 0; uint32_t pValueSize = 0; uint32_t index = 0; unsigned numEmptyEntries = 0; for (index = 0; index < 64; ++index) { ppName = notFreeFormStr; bool ok = MP4GetMetadataByIndex(handle, index, &ppName, &ppValue, &pValueSize); if (ok && ppName && memcmp(ppName, "----", 4) == 0) { // free form tagfield free(ppName); free(ppValue); ppName = freeFormStr; ppValue = 0; pValueSize = 0; ok = MP4GetMetadataByIndex(handle, index, &ppName, &ppValue, &pValueSize); } if (ok) { numEmptyEntries = 0; if (ppName) { QString key(ppName); QByteArray ba; if (ppValue && pValueSize > 0) { ba = getValueByteArray(ppName, ppValue, pValueSize); } m_metadata[key] = ba; free(ppName); } free(ppValue); ppName = 0; ppValue = 0; pValueSize = 0; } else { // There are iTunes files with invalid fields in between, // so we stop after 3 invalid indices. if (++numEmptyEntries >= 3) { break; } } } #else const char* ppName = 0; uint8_t* ppValue = 0; uint32_t pValueSize = 0; uint32_t index = 0; unsigned numEmptyEntries = 0; for (index = 0; index < 64; ++index) { if (MP4GetMetadataByIndex(handle, index, &ppName, &ppValue, &pValueSize)) { numEmptyEntries = 0; if (ppName) { QString key(ppName); QByteArray ba; if (ppValue && pValueSize > 0) { ba = getValueByteArray(ppName, ppValue, pValueSize); } m_metadata[key] = ba; // If the field is free form, there are two memory leaks in mp4v2. // The first is not accessible, the second can be freed. if (isFreeFormMetadata(handle, ppName)) { free(const_cast(ppName)); } } free(ppValue); ppName = 0; ppValue = 0; pValueSize = 0; } else { // There are iTunes files with invalid fields in between, // so we stop after 3 invalid indices. if (++numEmptyEntries >= 3) { break; } } } #endif MP4Close(handle); } } if (force) { setFilename(currentFilename()); } } /** * Write tags to file and rename it if necessary. * * @param force true to force writing even if file was not changed. * @param renamed will be set to true if the file was renamed, * i.e. the file name is no longer valid, else *renamed * is left unchanged * @param preserve true to preserve file time stamps * * @return true if ok, false if the file could not be written or renamed. */ bool M4aFile::writeTags(bool force, bool* renamed, bool preserve) { bool ok = true; QString fnStr(getDirname() + QDir::separator() + currentFilename()); if (isChanged() && !QFileInfo(fnStr).isWritable()) { return false; } if (m_fileRead && (force || isTag2Changed())) { QByteArray fn = QFile::encodeName(fnStr); // store time stamp if it has to be preserved bool setUtime = false; struct utimbuf times; if (preserve) { struct stat fileStat; if (::stat(fn, &fileStat) == 0) { times.actime = fileStat.st_atime; times.modtime = fileStat.st_mtime; setUtime = true; } } MP4FileHandle handle = MP4Modify(fn); if (handle != MP4_INVALID_FILE_HANDLE) { #if MPEG4IP_MAJOR_MINOR_VERSION >= 0x0109 MP4ItmfItemList* list = MP4ItmfGetItems(handle); if (list) { for (uint32_t i = 0; i < list->size; ++i) { MP4ItmfRemoveItem(handle, &list->elements[i]); } MP4ItmfItemListFree(list); } const MP4Tags* tags = MP4TagsAlloc(); #else // return code is not checked because it will fail if no metadata exists MP4MetadataDelete(handle); #endif for (MetadataMap::const_iterator it = m_metadata.begin(); it != m_metadata.end(); ++it) { const QByteArray& value = *it; if (!value.isEmpty()) { const QString& name = it.key(); const QByteArray& str = value; #if MPEG4IP_MAJOR_MINOR_VERSION >= 0x0109 if (name == QLatin1String("\251nam")) { MP4TagsSetName(tags, str); } else if (name == QLatin1String("\251ART")) { MP4TagsSetArtist(tags, str); } else if (name == QLatin1String("\251wrt")) { MP4TagsSetComposer(tags, str); } else if (name == QLatin1String("\251cmt")) { MP4TagsSetComments(tags, str); } else if (name == QLatin1String("\251too")) { MP4TagsSetEncodingTool(tags, str); } else if (name == QLatin1String("\251day")) { MP4TagsSetReleaseDate(tags, str); } else if (name == QLatin1String("\251alb")) { MP4TagsSetAlbum(tags, str); } else if (name == QLatin1String("trkn")) { MP4TagTrack indexTotal; int slashPos = str.indexOf('/'); if (slashPos != -1) { indexTotal.total = str.mid(slashPos + 1).toUShort(); indexTotal.index = str.mid(0, slashPos).toUShort(); } else { indexTotal.total = 0; indexTotal.index = str.toUShort(); } MP4TagsSetTrack(tags, &indexTotal); } else if (name == QLatin1String("disk")) { MP4TagDisk indexTotal; int slashPos = str.indexOf('/'); if (slashPos != -1) { indexTotal.total = str.mid(slashPos + 1).toUShort(); indexTotal.index = str.mid(0, slashPos).toUShort(); } else { indexTotal.total = 0; indexTotal.index = str.toUShort(); } MP4TagsSetDisk(tags, &indexTotal); } else if (name == QLatin1String("\251gen") || name == QLatin1String("gnre")) { MP4TagsSetGenre(tags, str); } else if (name == QLatin1String("tmpo")) { uint16_t tempo = str.toUShort(); MP4TagsSetTempo(tags, &tempo); } else if (name == QLatin1String("cpil")) { uint8_t cpl = str.toUShort(); MP4TagsSetCompilation(tags, &cpl); } else if (name == QLatin1String("covr")) { MP4TagArtwork artwork; artwork.data = (void *)value.data(); artwork.size = value.size(); artwork.type = MP4_ART_UNDEFINED; MP4TagsAddArtwork(tags, &artwork); } else if (name == QLatin1String("\251grp")) { MP4TagsSetGrouping(tags, str); } else if (name == QLatin1String("aART")) { MP4TagsSetAlbumArtist(tags, str); } else if (name == QLatin1String("pgap")) { uint8_t pgap = str.toUShort(); MP4TagsSetGapless(tags, &pgap); } else if (name == QLatin1String("tvsh")) { MP4TagsSetTVShow(tags, str); } else if (name == QLatin1String("tvnn")) { MP4TagsSetTVNetwork(tags, str); } else if (name == QLatin1String("tven")) { MP4TagsSetTVEpisodeID(tags, str); } else if (name == QLatin1String("tvsn")) { uint32_t val = str.toULong(); MP4TagsSetTVSeason(tags, &val); } else if (name == QLatin1String("tves")) { uint32_t val = str.toULong(); MP4TagsSetTVEpisode(tags, &val); } else if (name == QLatin1String("desc")) { MP4TagsSetDescription(tags, str); } else if (name == QLatin1String("ldes")) { MP4TagsSetLongDescription(tags, str); } else if (name == QLatin1String("\251lyr")) { MP4TagsSetLyrics(tags, str); } else if (name == QLatin1String("sonm")) { MP4TagsSetSortName(tags, str); } else if (name == QLatin1String("soar")) { MP4TagsSetSortArtist(tags, str); } else if (name == QLatin1String("soaa")) { MP4TagsSetSortAlbumArtist(tags, str); } else if (name == QLatin1String("soal")) { MP4TagsSetSortAlbum(tags, str); } else if (name == QLatin1String("soco")) { MP4TagsSetSortComposer(tags, str); } else if (name == QLatin1String("sosn")) { MP4TagsSetSortTVShow(tags, str); } else if (name == QLatin1String("cprt")) { MP4TagsSetCopyright(tags, str); } else if (name == QLatin1String("\251enc")) { MP4TagsSetEncodedBy(tags, str); } else if (name == QLatin1String("purd")) { MP4TagsSetPurchaseDate(tags, str); } else if (name == QLatin1String("pcst")) { uint8_t val = str.toUShort(); MP4TagsSetPodcast(tags, &val); } else if (name == QLatin1String("keyw")) { MP4TagsSetKeywords(tags, str); } else if (name == QLatin1String("catg")) { MP4TagsSetCategory(tags, str); } else if (name == QLatin1String("hdvd")) { uint8_t val = str.toUShort(); MP4TagsSetHDVideo(tags, &val); } else if (name == QLatin1String("stik")) { uint8_t val = str.toUShort(); MP4TagsSetMediaType(tags, &val); } else if (name == QLatin1String("rtng")) { uint8_t val = str.toUShort(); MP4TagsSetContentRating(tags, &val); } else if (name == QLatin1String("apID")) { MP4TagsSetITunesAccount(tags, str); } else if (name == QLatin1String("akID")) { uint8_t val = str.toUShort(); MP4TagsSetITunesAccountType(tags, &val); } else if (name == QLatin1String("sfID")) { uint32_t val = str.toULong(); MP4TagsSetITunesCountry(tags, &val); } else if (name == QLatin1String("cnID")) { uint32_t val = str.toULong(); MP4TagsSetContentID(tags, &val); } else if (name == QLatin1String("atID")) { uint32_t val = str.toULong(); MP4TagsSetArtistID(tags, &val); } else if (name == QLatin1String("plID")) { uint64_t val = str.toULongLong(); MP4TagsSetPlaylistID(tags, &val); } else if (name == QLatin1String("geID")) { uint32_t val = str.toULong(); MP4TagsSetGenreID(tags, &val); } else { MP4ItmfItem* item = MP4ItmfItemAlloc("----", 1); item->mean = strdup("com.apple.iTunes"); item->name = strdup(name.toUtf8().data()); MP4ItmfData& data = item->dataList.elements[0]; data.typeCode = MP4_ITMF_BT_UTF8; data.valueSize = value.size(); data.value = reinterpret_cast(malloc(data.valueSize)); memcpy(data.value, value.data(), data.valueSize); MP4ItmfAddItem(handle, item); MP4ItmfItemFree(item); } #else bool setOk; if (name == QLatin1String("\251nam")) { setOk = MP4SetMetadataName(handle, str); } else if (name == QLatin1String("\251ART")) { setOk = MP4SetMetadataArtist(handle, str); } else if (name == QLatin1String("\251wrt")) { setOk = MP4SetMetadataWriter(handle, str); } else if (name == QLatin1String("\251cmt")) { setOk = MP4SetMetadataComment(handle, str); } else if (name == QLatin1String("\251too")) { setOk = MP4SetMetadataTool(handle, str); } else if (name == QLatin1String("\251day")) { unsigned short year = str.toUShort(); if (year > 0) { if (year < 1000) year += 2000; else if (year > 9999) year = 9999; setOk = MP4SetMetadataYear(handle, QByteArray().setNum(year)); if (setOk) setYearV2(year); } else { setOk = true; } } else if (name == QLatin1String("\251alb")) { setOk = MP4SetMetadataAlbum(handle, str); } else if (name == QLatin1String("trkn")) { uint16_t track = 0, totalTracks = 0; int slashPos = str.indexOf('/'); if (slashPos != -1) { totalTracks = str.mid(slashPos + 1).toUShort(); track = str.mid(0, slashPos).toUShort(); } else { track = str.toUShort(); } setOk = MP4SetMetadataTrack(handle, track, totalTracks); } else if (name == QLatin1String("disk")) { uint16_t disk = 0, totalDisks = 0; int slashPos = str.indexOf('/'); if (slashPos != -1) { totalDisks = str.mid(slashPos + 1).toUShort(); disk = str.mid(0, slashPos).toUShort(); } else { disk = str.toUShort(); } setOk = MP4SetMetadataDisk(handle, disk, totalDisks); } else if (name == QLatin1String("\251gen") || name == QLatin1String("gnre")) { setOk = MP4SetMetadataGenre(handle, str); } else if (name == QLatin1String("tmpo")) { uint16_t tempo = str.toUShort(); setOk = MP4SetMetadataTempo(handle, tempo); } else if (name == QLatin1String("cpil")) { uint8_t cpl = str.toUShort(); setOk = MP4SetMetadataCompilation(handle, cpl); } else if (name == QLatin1String("covr")) { setOk = MP4SetMetadataCoverArt( handle, reinterpret_cast(const_cast(value.data())), value.size()); // While this works on Debian Etch with libmp4v2-dev 1.5.0.1-0.3 from // www.debian-multimedia.org, linking on OpenSUSE 10.3 with // libmp4v2-devel-1.5.0.1-6 from packman.links2linux.de fails with // undefined reference to MP4SetMetadataGrouping. To avoid this, // in the line below, 0x105 is replaced by 0x106. #if MPEG4IP_MAJOR_MINOR_VERSION >= 0x0106 } else if (name == QLatin1String("\251grp")) { setOk = MP4SetMetadataGrouping(handle, str); #endif #if MPEG4IP_MAJOR_MINOR_VERSION >= 0x0106 } else if (name == QLatin1String("aART")) { setOk = MP4SetMetadataAlbumArtist(handle, str); } else if (name == QLatin1String("pgap")) { uint8_t pgap = str.toUShort(); setOk = MP4SetMetadataPartOfGaplessAlbum(handle, pgap); #endif } else { setOk = MP4SetMetadataFreeForm( handle, const_cast(name.toUtf8().data()), reinterpret_cast(const_cast(value.data())), value.size()); } if (!setOk) { qDebug("MP4SetMetadata %s failed", name.toLatin1().data()); ok = false; } #endif } } #if MPEG4IP_MAJOR_MINOR_VERSION >= 0x0109 MP4TagsStore(tags, handle); MP4TagsFree(tags); #endif MP4Close(handle); if (ok) { // without this, old tags stay in the file marked as free MP4Optimize(fn); markTag2Unchanged(); } // restore time stamp if (setUtime) { ::utime(fn, ×); } } else { qDebug("MP4Modify failed"); ok = false; } } if (getFilename() != currentFilename()) { if (!renameFile(currentFilename(), getFilename())) { return false; } updateCurrentFilename(); // link tags to new file name readTags(true); *renamed = true; } return ok; } /** * Remove ID3v2 frames. * * @param flt filter specifying which frames to remove */ void M4aFile::deleteFramesV2(const FrameFilter& flt) { if (flt.areAllEnabled()) { m_metadata.clear(); markTag2Changed(Frame::FT_UnknownFrame); } else { bool changed = false; for (MetadataMap::iterator it = m_metadata.begin(); it != m_metadata.end();) { QString name(it.key()); Frame::Type type = getTypeForName(name); if (flt.isEnabled(type, name)) { it = m_metadata.erase(it); changed = true; } else { ++it; } } if (changed) { markTag2Changed(Frame::FT_UnknownFrame); } } } /** * Get metadata field as string. * * @param name field name * * @return value as string, "" if not found, * QString::null if the tags have not been read yet. */ QString M4aFile::getTextField(const QString& name) const { if (m_fileRead) { MetadataMap::const_iterator it = m_metadata.find(name); if (it != m_metadata.end()) { return QString::fromUtf8((*it).data(), (*it).size()); } return QLatin1String(""); } return QString(); } /** * Set text field. * If value is null if the tags have not been read yet, nothing is changed. * If value is different from the current value, tag 2 is marked as changed. * * @param name name * @param value value, "" to remove, QString::null to do nothing * @param type frame type */ void M4aFile::setTextField(const QString& name, const QString& value, Frame::Type type) { if (m_fileRead && !value.isNull()) { QByteArray str = value.toUtf8(); MetadataMap::iterator it = m_metadata.find(name); if (it != m_metadata.end()) { if (QString::fromUtf8((*it).data(), (*it).size()) != value) { *it = str; markTag2Changed(type); } } else { m_metadata.insert(name, str); markTag2Changed(type); } } } /** * Get ID3v2 title. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ QString M4aFile::getTitleV2() { return getTextField(QLatin1String("\251nam")); } /** * Get ID3v2 artist. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ QString M4aFile::getArtistV2() { return getTextField(QLatin1String("\251ART")); } /** * Get ID3v2 album. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ QString M4aFile::getAlbumV2() { return getTextField(QLatin1String("\251alb")); } /** * Get ID3v2 comment. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ QString M4aFile::getCommentV2() { return getTextField(QLatin1String("\251cmt")); } /** * Get ID3v2 year. * * @return number, * 0 if the field does not exist, * -1 if the tags do not exist. */ int M4aFile::getYearV2() { QString str(getTextField(QLatin1String("\251day"))); if (!str.isNull()) { return str.toInt(); } return -1; } /** * Get ID3v2 track. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ QString M4aFile::getTrackV2() { return getTextField(QLatin1String("trkn")); } /** * Get ID3v2 genre as text. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ QString M4aFile::getGenreV2() { QString str(getTextField(QLatin1String("\251gen"))); if (str.isEmpty()) { str = getTextField(QLatin1String("gnre")); } return str; } /** * Set ID3v2 title. * * @param str string to set, "" to remove field, QString::null to ignore. */ void M4aFile::setTitleV2(const QString& str) { setTextField(QLatin1String("\251nam"), str, Frame::FT_Title); } /** * Set ID3v2 artist. * * @param str string to set, "" to remove field, QString::null to ignore. */ void M4aFile::setArtistV2(const QString& str) { setTextField(QLatin1String("\251ART"), str, Frame::FT_Artist); } /** * Set ID3v2 album. * * @param str string to set, "" to remove field, QString::null to ignore. */ void M4aFile::setAlbumV2(const QString& str) { setTextField(QLatin1String("\251alb"), str, Frame::FT_Album); } /** * Set ID3v2 comment. * * @param str string to set, "" to remove field, QString::null to ignore. */ void M4aFile::setCommentV2(const QString& str) { setTextField(QLatin1String("\251cmt"), str, Frame::FT_Comment); } /** * Set ID3v2 year. * * @param num number to set, 0 to remove field, < 0 to ignore. */ void M4aFile::setYearV2(int num) { if (num >= 0) { setTextField(QLatin1String("\251day"), num != 0 ? QString::number(num) : QLatin1String(""), Frame::FT_Date); } } /** * Set ID3v2 track. * * @param track string to set, "" to remove field, QString::null to ignore. */ void M4aFile::setTrackV2(const QString& track) { int numTracks; int num = splitNumberAndTotal(track, &numTracks); if (num >= 0) { QString str; if (num != 0) { str.setNum(num); if (numTracks == 0) numTracks = getTotalNumberOfTracksIfEnabled(); if (numTracks > 0) { str += QLatin1Char('/'); str += QString::number(numTracks); } } else { str = QLatin1String(""); } setTextField(QLatin1String("trkn"), str, Frame::FT_Track); } } /** * Set ID3v2 genre as text. * * @param str string to set, "" to remove field, QString::null to ignore. */ void M4aFile::setGenreV2(const QString& str) { if (str != getGenreV2()) { int genreNum = Genres::getNumber(str); if (genreNum != 255) { setTextField(QLatin1String("gnre"), str, Frame::FT_Genre); m_metadata.remove(QLatin1String("\251gen")); } else { setTextField(QLatin1String("\251gen"), str, Frame::FT_Genre); m_metadata.remove(QLatin1String("gnre")); } } } /** * Check if tag information has already been read. * * @return true if information is available, * false if the tags have not been read yet, in which case * hasTagV1() and hasTagV2() do not return meaningful information. */ bool M4aFile::isTagInformationRead() const { return m_fileRead; } /** * Check if file has an ID3v2 tag. * * @return true if a V2 tag is available. * @see isTagInformationRead() */ bool M4aFile::hasTagV2() const { return !m_metadata.empty(); } /** * Get file extension including the dot. * * @return file extension ".m4a". */ QString M4aFile::getFileExtension() const { return QLatin1String(".m4a"); } /** * Get technical detail information. * * @param info the detail information is returned here */ void M4aFile::getDetailInfo(DetailInfo& info) const { if (m_fileRead && m_fileInfo.valid) { info.valid = true; info.format = QLatin1String("MP4"); info.bitrate = m_fileInfo.bitrate; info.sampleRate = m_fileInfo.sampleRate; info.channels = m_fileInfo.channels; info.duration = m_fileInfo.duration; } else { info.valid = false; } } /** * Get duration of file. * * @return duration in seconds, * 0 if unknown. */ unsigned M4aFile::getDuration() const { if (m_fileRead && m_fileInfo.valid) { return m_fileInfo.duration; } return 0; } /** * Get the format of tag 2. * * @return "Vorbis". */ QString M4aFile::getTagFormatV2() const { return hasTagV2() ? QLatin1String("MP4") : QString(); } /** * Set a frame in the tags 2. * * @param frame frame to set * * @return true if ok. */ bool M4aFile::setFrameV2(const Frame& frame) { QString name(frame.getInternalName()); MetadataMap::iterator it = m_metadata.find(name); if (it != m_metadata.end()) { if (frame.getType() != Frame::FT_Picture) { QByteArray str = frame.getValue().toUtf8(); if (*it != str) { *it = str; markTag2Changed(frame.getType()); } } else { if (PictureFrame::getData(frame, *it)) { markTag2Changed(Frame::FT_Picture); } } return true; } // Try the superclass method return TaggedFile::setFrameV2(frame); } /** * Add a frame in the tags 2. * * @param frame frame to add * * @return true if ok. */ bool M4aFile::addFrameV2(Frame& frame) { Frame::Type type = frame.getType(); QString name; if (type != Frame::FT_Other) { name = getNameForType(type); if (!name.isEmpty()) { frame.setExtendedType(Frame::ExtendedType(type, name)); } } name = frame.getInternalName(); if (type == Frame::FT_Picture) { if (!PictureFrame::getData(frame, m_metadata[name])) { PictureFrame::setFields(frame); m_metadata[name] = QByteArray(); } } else { m_metadata[name] = frame.getValue().toUtf8(); } markTag2Changed(type); return true; } /** * Delete a frame in the tags 2. * * @param frame frame to delete. * * @return true if ok. */ bool M4aFile::deleteFrameV2(const Frame& frame) { QString name(frame.getInternalName()); MetadataMap::iterator it = m_metadata.find(name); if (it != m_metadata.end()) { m_metadata.erase(it); markTag2Changed(frame.getType()); return true; } // Try the superclass method return TaggedFile::deleteFrameV2(frame); } /** * Get all frames in tag 2. * * @param frames frame collection to set. */ void M4aFile::getAllFramesV2(FrameCollection& frames) { frames.clear(); QString name; QString value; for (MetadataMap::const_iterator it = m_metadata.begin(); it != m_metadata.end(); ++it) { name = it.key(); Frame::Type type = getTypeForName(name); if (type != Frame::FT_Picture) { value = QString::fromUtf8((*it).data(), (*it).size()); frames.insert(Frame(type, value, name, -1)); } else { PictureFrame frame(*it); frame.setExtendedType(Frame::ExtendedType(Frame::FT_Picture, name)); frames.insert(frame); } } frames.addMissingStandardFrames(); } /** * Get a list of frame IDs which can be added. * * @return list with frame IDs. */ QStringList M4aFile::getFrameIds() const { static const Frame::Type types[] = { Frame::FT_Title, Frame::FT_Artist, Frame::FT_Album, Frame::FT_Comment, Frame::FT_Date, Frame::FT_Track, Frame::FT_Genre, #if MPEG4IP_MAJOR_MINOR_VERSION >= 0x0106 Frame::FT_AlbumArtist, #endif Frame::FT_Bpm, Frame::FT_Composer, #if MPEG4IP_MAJOR_MINOR_VERSION >= 0x0109 Frame::FT_Copyright, #endif Frame::FT_Disc, Frame::FT_EncodedBy, #if MPEG4IP_MAJOR_MINOR_VERSION >= 0x0105 Frame::FT_Grouping, #endif #if MPEG4IP_MAJOR_MINOR_VERSION >= 0x0109 Frame::FT_Lyrics, #endif Frame::FT_Picture }; QStringList lst; for (unsigned i = 0; i < sizeof(types) / sizeof(types[0]); ++i) { lst.append(Frame::ExtendedType(static_cast(i), QLatin1String("")). getTranslatedName()); } lst << QLatin1String("cpil"); #if MPEG4IP_MAJOR_MINOR_VERSION >= 0x0106 lst << QLatin1String("pgap"); #endif #if MPEG4IP_MAJOR_MINOR_VERSION >= 0x0109 lst << QLatin1String("akID") << QLatin1String("apID") << QLatin1String("atID") << QLatin1String("catg") << QLatin1String("cnID") << QLatin1String("desc") << QLatin1String("\251too") << QLatin1String("geID") << QLatin1String("hdvd") << QLatin1String("keyw") << QLatin1String("ldes") << QLatin1String("pcst") << QLatin1String("plID") << QLatin1String("purd") << QLatin1String("rtng") << QLatin1String("sfID") << QLatin1String("soaa") << QLatin1String("soal") << QLatin1String("soar") << QLatin1String("soco") << QLatin1String("sonm") << QLatin1String("sosn") << QLatin1String("stik") << QLatin1String("tven") << QLatin1String("tves") << QLatin1String("tvnn") << QLatin1String("tvsh") << QLatin1String("tvsn"); #endif return lst; } /** * Read information about an MPEG-4 file. * @param fn file name * @return true if ok. */ bool M4aFile::FileInfo::read(MP4FileHandle handle) { valid = false; uint32_t numTracks = MP4GetNumberOfTracks(handle); for (uint32_t i = 0; i < numTracks; ++i) { MP4TrackId trackId = MP4FindTrackId(handle, i); const char* trackType = MP4GetTrackType(handle, trackId); if (std::strcmp(trackType, MP4_AUDIO_TRACK_TYPE) == 0) { valid = true; bitrate = (MP4GetTrackBitRate(handle, trackId) + 500) / 1000; sampleRate = MP4GetTrackTimeScale(handle, trackId); duration = MP4ConvertFromTrackDuration( handle, trackId, MP4GetTrackDuration(handle, trackId), MP4_MSECS_TIME_SCALE) / 1000; #if MPEG4IP_MAJOR_MINOR_VERSION >= 0x0109 channels = MP4GetTrackAudioChannels(handle, trackId); #else channels = 2; #endif break; } } return valid; } kid3-3.0.2/src/plugins/mp4v2metadata/m4afile.h000066400000000000000000000174331224603511300207660ustar00rootroot00000000000000/** * \file m4afile.h * Handling of MPEG-4 audio files. * * \b Project: Kid3 * \author Urs Fleisch * \date 25 Oct 2007 * * Copyright (C) 2007-2011 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef M4AFILE_H #define M4AFILE_H #include "taggedfile.h" #include /** MPEG-4 audio file */ class M4aFile : public TaggedFile { public: /** * Constructor. * * @param dn directory name * @param fn filename * @param idx model index */ M4aFile(const QString& dn, const QString& fn, const QPersistentModelIndex& idx); /** * Destructor. */ virtual ~M4aFile(); /** * Get key of tagged file format. * @return "Mp4v2Metadata". */ virtual QString taggedFileKey() const; /** * Read tags from file. * * @param force true to force reading even if tags were already read. */ virtual void readTags(bool force); /** * Write tags to file and rename it if necessary. * * @param force true to force writing even if file was not changed. * @param renamed will be set to true if the file was renamed, * i.e. the file name is no longer valid, else *renamed * is left unchanged * @param preserve true to preserve file time stamps * * @return true if ok, false if the file could not be written or renamed. */ virtual bool writeTags(bool force, bool* renamed, bool preserve); /** * Remove ID3v2 frames. * * @param flt filter specifying which frames to remove */ virtual void deleteFramesV2(const FrameFilter& flt); /** * Get ID3v2 title. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ virtual QString getTitleV2(); /** * Get ID3v2 artist. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ virtual QString getArtistV2(); /** * Get ID3v2 album. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ virtual QString getAlbumV2(); /** * Get ID3v2 comment. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ virtual QString getCommentV2(); /** * Get ID3v2 year. * * @return number, * 0 if the field does not exist, * -1 if the tags do not exist. */ virtual int getYearV2(); /** * Get ID3v2 track. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ virtual QString getTrackV2(); /** * Get ID3v2 genre as text. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ virtual QString getGenreV2(); /** * Set ID3v2 title. * * @param str string to set, "" to remove field. */ virtual void setTitleV2(const QString& str); /** * Set ID3v2 artist. * * @param str string to set, "" to remove field. */ virtual void setArtistV2(const QString& str); /** * Set ID3v2 album. * * @param str string to set, "" to remove field. */ virtual void setAlbumV2(const QString& str); /** * Set ID3v2 comment. * * @param str string to set, "" to remove field. */ virtual void setCommentV2(const QString& str); /** * Set ID3v2 year. * * @param num number to set, 0 to remove field. */ virtual void setYearV2(int num); /** * Set ID3v2 track. * * @param track string to set, "" to remove field, QString::null to ignore. */ virtual void setTrackV2(const QString& track); /** * Set ID3v2 genre as text. * * @param str string to set, "" to remove field, QString::null to ignore. */ virtual void setGenreV2(const QString& str); /** * Check if tag information has already been read. * * @return true if information is available, * false if the tags have not been read yet, in which case * hasTagV1() and hasTagV2() do not return meaningful information. */ virtual bool isTagInformationRead() const; /** * Check if file has an ID3v2 tag. * * @return true if a V2 tag is available. * @see isTagInformationRead() */ virtual bool hasTagV2() const; /** * Get technical detail information. * * @param info the detail information is returned here */ virtual void getDetailInfo(DetailInfo& info) const; /** * Get duration of file. * * @return duration in seconds, * 0 if unknown. */ virtual unsigned getDuration() const; /** * Get file extension including the dot. * * @return file extension ".m4a". */ virtual QString getFileExtension() const; /** * Get the format of tag 2. * * @return "Vorbis". */ virtual QString getTagFormatV2() const; /** * Set a frame in the tags 2. * * @param frame frame to set * * @return true if ok. */ virtual bool setFrameV2(const Frame& frame); /** * Add a frame in the tags 2. * * @param frame frame to add * * @return true if ok. */ virtual bool addFrameV2(Frame& frame); /** * Delete a frame in the tags 2. * * @param frame frame to delete. * * @return true if ok. */ virtual bool deleteFrameV2(const Frame& frame); /** * Get all frames in tag 2. * * @param frames frame collection to set. */ virtual void getAllFramesV2(FrameCollection& frames); /** * Get a list of frame IDs which can be added. * * @return list with frame IDs. */ virtual QStringList getFrameIds() const; private: M4aFile(const M4aFile&); M4aFile& operator=(const M4aFile&); /** * Get metadata field as string. * * @param name field name * * @return value as string, "" if not found, * QString::null if the tags have not been read yet. */ QString getTextField(const QString& name) const; /** * Set text field. * If value is null if the tags have not been read yet, nothing is changed. * If value is different from the current value, tag 2 is marked as changed. * * @param name name * @param value value, "" to remove, QString::null to do nothing * @param type frame type */ void setTextField(const QString& name, const QString& value, Frame::Type type); /** true if file has been read. */ bool m_fileRead; /** Information about MPEG-4 file. */ struct FileInfo { /** * Constructor. */ FileInfo() : valid(false), channels(0), sampleRate(0), bitrate(0), duration(0) {} /** * Read information about an MPEG-4 file. * @param handle MP4 handle * @return true if ok. */ bool read(void* handle); bool valid; /**< true if read() was successful */ int channels; /**< number of channels */ long sampleRate; /**< sample rate in Hz */ long bitrate; /**< bitrate in bits/s */ long duration; /**< duration in seconds */ }; /** Info about file. */ FileInfo m_fileInfo; /** Map with metadata. */ typedef QMap MetadataMap; /** Metadata. */ MetadataMap m_metadata; }; #endif // M4AFILE_H kid3-3.0.2/src/plugins/mp4v2metadata/mp4v2config.h.cmake000066400000000000000000000005071224603511300226540ustar00rootroot00000000000000/* mp4v2config.h. Generated by cmake from mp4v2config.h.cmake */ #ifndef MP4V2CONFIG_H #define MP4V2CONFIG_H /* Define if you have mp4v2/mp4v2.h installed */ #cmakedefine HAVE_MP4V2_MP4V2_H 1 /* Define to build with MP4GetMetadataByIndex char** argument */ #cmakedefine HAVE_MP4V2_MP4GETMETADATABYINDEX_CHARPP_ARG 1 #endif kid3-3.0.2/src/plugins/mp4v2metadata/mp4v2metadataplugin.cpp000066400000000000000000000070251224603511300236640ustar00rootroot00000000000000/** * \file mp4v2metadataplugin.cpp * Mp4v2 metadata plugin. * * \b Project: Kid3 * \author Urs Fleisch * \date 27 Jul 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "mp4v2metadataplugin.h" #include "m4afile.h" #if QT_VERSION < 0x050000 Q_EXPORT_PLUGIN2(Mp4v2MetadataPlugin, Mp4v2MetadataPlugin) #endif static const QLatin1String TAGGEDFILE_KEY("Mp4v2Metadata"); /*! * Constructor. * @param parent parent object */ Mp4v2MetadataPlugin::Mp4v2MetadataPlugin(QObject* parent) : QObject(parent) { setObjectName(QLatin1String("Mp4v2Metadata")); } /** * Destructor. */ Mp4v2MetadataPlugin::~Mp4v2MetadataPlugin() { } /** * Get name of factory, the same as the QObject::objectName() of the plugin. * @return factory name. */ QString Mp4v2MetadataPlugin::name() const { return objectName(); } /** * Get keys of available tagged file formats. * @return list of keys. */ QStringList Mp4v2MetadataPlugin::taggedFileKeys() const { return QStringList() << TAGGEDFILE_KEY; } /** * Get features supported. * @param key tagged file key * @return bit mask with Features flags set. */ int Mp4v2MetadataPlugin::taggedFileFeatures(const QString& key) const { Q_UNUSED(key) return 0; } /** * Initialize tagged file factory. * * @param key tagged file key */ void Mp4v2MetadataPlugin::initialize(const QString& key) { Q_UNUSED(key) } /** * Create a tagged file. * * @param key tagged file key * @param dirName directory name * @param fileName filename * @param idx model index * @param features optional tagged file features (TaggedFile::Feature flags) * to activate at creation * * @return tagged file, 0 if type not supported. */ TaggedFile* Mp4v2MetadataPlugin::createTaggedFile( const QString& key, const QString& dirName, const QString& fileName, const QPersistentModelIndex& idx, int features) { Q_UNUSED(features) if (key == TAGGEDFILE_KEY) { QString ext = fileName.right(4).toLower(); if (ext == QLatin1String(".m4a") || ext == QLatin1String(".m4b") || ext == QLatin1String(".m4p") || ext == QLatin1String(".mp4") || ext == QLatin1String(".m4v") || ext == QLatin1String("mp4v")) return new M4aFile(dirName, fileName, idx); } return 0; } /** * Get a list with all extensions (e.g. ".mp3") supported by TaggedFile subclass. * * @param key tagged file key * * @return list of file extensions. */ QStringList Mp4v2MetadataPlugin::supportedFileExtensions(const QString& key) const { if (key == TAGGEDFILE_KEY) { return QStringList() << QLatin1String(".m4a") << QLatin1String(".m4b") << QLatin1String(".m4p") << QLatin1String(".mp4"); } return QStringList(); } /** * Notify about configuration change. * This method shall be called when the configuration changes. * * @param key tagged file key */ void Mp4v2MetadataPlugin::notifyConfigurationChange(const QString& key) { Q_UNUSED(key) } kid3-3.0.2/src/plugins/mp4v2metadata/mp4v2metadataplugin.h000066400000000000000000000056751224603511300233420ustar00rootroot00000000000000/** * \file mp4v2metadataplugin.h * Mp4v2 metadata plugin. * * \b Project: Kid3 * \author Urs Fleisch * \date 27 Jul 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MP4V2METADATAPLUGIN_H #define MP4V2METADATAPLUGIN_H #include #include "itaggedfilefactory.h" /** * Mp4v2 metadata plugin. */ class KID3_PLUGIN_EXPORT Mp4v2MetadataPlugin : public QObject, public ITaggedFileFactory { Q_OBJECT #if QT_VERSION >= 0x050000 Q_PLUGIN_METADATA(IID "net.sourceforge.kid3.ITaggedFileFactory") #endif Q_INTERFACES(ITaggedFileFactory) public: /*! * Constructor. * @param parent parent object */ explicit Mp4v2MetadataPlugin(QObject* parent = 0); /** * Destructor. */ virtual ~Mp4v2MetadataPlugin(); /** * Get name of factory, the same as the QObject::objectName() of the plugin. * @return factory name. */ virtual QString name() const; /** * Get keys of available tagged file formats. * @return list of keys. */ virtual QStringList taggedFileKeys() const; /** * Get features supported. * @param key tagged file key * @return bit mask with Features flags set. */ virtual int taggedFileFeatures(const QString& key) const; /** * Initialize tagged file factory. * * @param key tagged file key */ virtual void initialize(const QString& key); /** * Create a tagged file. * * @param dirName directory name * @param fileName filename * @param idx model index * @param features optional tagged file features (TaggedFile::Feature flags) * to activate at creation * * @return tagged file, 0 if type not supported. */ virtual TaggedFile* createTaggedFile( const QString& key, const QString& dirName, const QString& fileName, const QPersistentModelIndex& idx, int features = 0); /** * Get a list with all extensions (e.g. ".mp3") supported by TaggedFile subclass. * * @param key tagged file key * * @return list of file extensions. */ virtual QStringList supportedFileExtensions(const QString& key) const; /** * Notify about configuration change. * This method shall be called when the configuration changes. * * @param key tagged file key */ virtual void notifyConfigurationChange(const QString& key); }; #endif // MP4V2METADATAPLUGIN_H kid3-3.0.2/src/plugins/musicbrainzimport/000077500000000000000000000000001224603511300203745ustar00rootroot00000000000000kid3-3.0.2/src/plugins/musicbrainzimport/CMakeLists.txt000066400000000000000000000010611224603511300231320ustar00rootroot00000000000000set(plugin_SRCS musicbrainzimportplugin.cpp musicbrainzimporter.cpp musicbrainzconfig.cpp ) set(plugin_MOC_HDRS musicbrainzimportplugin.h ) set(plugin_NAME MusicBrainzImport) if (WITH_GCC_PCH) add_definitions(${GCC_PCH_COMPILE_FLAGS}) endif (WITH_GCC_PCH) string(TOLOWER ${plugin_NAME} plugin_TARGET) qt4_wrap_cpp(plugin_GEN_MOC_SRCS ${plugin_MOC_HDRS}) add_library(${plugin_TARGET} ${plugin_SRCS} ${plugin_GEN_MOC_SRCS}) target_link_libraries(${plugin_TARGET} kid3-core ${BASIC_LIBRARIES}) INSTALL_KID3_PLUGIN(${plugin_TARGET} ${plugin_NAME}) kid3-3.0.2/src/plugins/musicbrainzimport/musicbrainzconfig.cpp000066400000000000000000000023641224603511300246210ustar00rootroot00000000000000/** * \file musicbrainzconfig.cpp * MusicBrainz configuration. * * \b Project: Kid3 * \author Urs Fleisch * \date 13 Sep 2005 * * Copyright (C) 2005-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "musicbrainzconfig.h" #include int MusicBrainzConfig::s_index = -1; /** * Constructor. */ MusicBrainzConfig::MusicBrainzConfig() : StoredConfig( QLatin1String("MusicBrainz")) { m_cgiPathUsed = false; m_additionalTagsUsed = true; m_server = QLatin1String("musicbrainz.org:80"); } /** * Destructor. */ MusicBrainzConfig::~MusicBrainzConfig() {} kid3-3.0.2/src/plugins/musicbrainzimport/musicbrainzconfig.h000066400000000000000000000024031224603511300242600ustar00rootroot00000000000000/** * \file musicbrainzconfig.h * MusicBrainz configuration. * * \b Project: Kid3 * \author Urs Fleisch * \date 13 Sep 2005 * * Copyright (C) 2005-2007 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MUSICBRAINZCONFIG_H #define MUSICBRAINZCONFIG_H #include "serverimporterconfig.h" /** * MusicBrainz configuration. */ class MusicBrainzConfig : public StoredConfig { public: /** * Constructor. */ MusicBrainzConfig(); /** * Destructor. */ virtual ~MusicBrainzConfig(); /** Index in configuration storage */ static int s_index; }; #endif // MUSICBRAINZCONFIG_H kid3-3.0.2/src/plugins/musicbrainzimport/musicbrainzimporter.cpp000066400000000000000000000453341224603511300252210ustar00rootroot00000000000000/** * \file musicbrainzimporter.cpp * MusicBrainz release database importer. * * \b Project: Kid3 * \author Urs Fleisch * \date 13 Oct 2006 * * Copyright (C) 2006-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "musicbrainzimporter.h" #include #include #include "serverimporterconfig.h" #include "trackdatamodel.h" #include "musicbrainzconfig.h" /** * Constructor. * * @param netMgr network access manager * @param trackDataModel track data to be filled with imported values */ MusicBrainzImporter::MusicBrainzImporter( QNetworkAccessManager* netMgr, TrackDataModel *trackDataModel) : ServerImporter(netMgr, trackDataModel) { setObjectName(QLatin1String("MusicBrainzImporter")); } /** * Destructor. */ MusicBrainzImporter::~MusicBrainzImporter() { } /** * Name of import source. * @return name. */ const char* MusicBrainzImporter::name() const { return QT_TRANSLATE_NOOP("@default", "MusicBrainz Release"); } /** NULL-terminated array of server strings, 0 if not used */ const char** MusicBrainzImporter::serverList() const { return 0; } /** default server, 0 to disable */ const char* MusicBrainzImporter::defaultServer() const { return 0; } /** anchor to online help, 0 to disable */ const char* MusicBrainzImporter::helpAnchor() const { return "import-musicbrainzrelease"; } /** configuration, 0 if not used */ ServerImporterConfig* MusicBrainzImporter::config() const { return &MusicBrainzConfig::instance(); } /** additional tags option, false if not used */ bool MusicBrainzImporter::additionalTags() const { return true; } /** * Process finished findCddbAlbum request. * * @param searchStr search data received */ void MusicBrainzImporter::parseFindResults(const QByteArray& searchStr) { /* simplified XML result: Odin Wizard Wizard */ int start = searchStr.indexOf(""); QByteArray xmlStr = searchStr; if (start >= 0 && end > start) { xmlStr = xmlStr.mid(start, end + 11 - start); } QDomDocument doc; if (doc.setContent(xmlStr, false)) { m_albumListModel->clear(); QDomElement releaseList = doc.namedItem(QLatin1String("metadata")).toElement().namedItem(QLatin1String("release-list")).toElement(); for (QDomNode releaseNode = releaseList.namedItem(QLatin1String("release")); !releaseNode.isNull(); releaseNode = releaseNode.nextSibling()) { QDomElement release = releaseNode.toElement(); QString id = release.attribute(QLatin1String("id")); QString title = release.namedItem(QLatin1String("title")).toElement().text(); QDomElement artist = release.namedItem(QLatin1String("artist-credit")).toElement(). namedItem(QLatin1String("name-credit")).toElement().namedItem(QLatin1String("artist")).toElement(); QString name = artist.namedItem(QLatin1String("name")).toElement().text(); m_albumListModel->appendRow(new AlbumListItem( name + QLatin1String(" - ") + title, QLatin1String("release"), id)); } } } /** * Uppercase the first characters of each word in a string. * * @param str string with words to uppercase * * @return string with first letters in uppercase. */ static QString upperCaseFirstLetters(const QString& str) { QString result(str); int len = result.length(); int pos = 0; while (pos < len) { result[pos] = result.at(pos).toUpper(); pos = result.indexOf(QLatin1Char(' '), pos); if (pos++ == -1) { break; } } return result; } /** * Add involved people to a frame. * The format used is (should be converted according to tag specifications): * involvee 1 (involvement 1)\n * involvee 2 (involvement 2)\n * ... * involvee n (involvement n) * * @param frames frame collection * @param type type of frame * @param involvement involvement (e.g. instrument) * @param involvee name of involvee (e.g. musician) */ static void addInvolvedPeople( FrameCollection& frames, Frame::Type type, const QString& involvement, const QString& involvee) { QString value = frames.getValue(type); if (!value.isEmpty()) value += Frame::stringListSeparator(); value += upperCaseFirstLetters(involvement); value += Frame::stringListSeparator(); value += involvee; frames.setValue(type, value); } /** * Set tags from an XML node with a relation list. * * @param relationList relation-list with target-type Artist * @param frames tags will be added to these frames * * @return true if credits found. */ static bool parseCredits(const QDomElement& relationList, FrameCollection& frames) { bool result = false; QDomNode relation(relationList.firstChild()); while (!relation.isNull()) { QString artist(relation.toElement().namedItem(QLatin1String("artist")).toElement(). namedItem(QLatin1String("name")).toElement().text()); if (!artist.isEmpty()) { QString type(relation.toElement().attribute(QLatin1String("type"))); if (type == QLatin1String("instrument")) { QDomNode attributeList(relation.toElement().namedItem(QLatin1String("attribute-list"))); if (!attributeList.isNull()) { addInvolvedPeople(frames, Frame::FT_Performer, attributeList.firstChild().toElement().text(), artist); } } else if (type == QLatin1String("vocal")) { addInvolvedPeople(frames, Frame::FT_Performer, type, artist); } else { static const struct { const char* credit; Frame::Type type; } creditToType[] = { { "composer", Frame::FT_Composer }, { "conductor", Frame::FT_Conductor }, { "performing orchestra", Frame::FT_AlbumArtist }, { "lyricist", Frame::FT_Lyricist }, { "publisher", Frame::FT_Publisher }, { "remixer", Frame::FT_Remixer } }; bool found = false; for (unsigned i = 0; i < sizeof(creditToType) / sizeof(creditToType[0]); ++i) { if (type == QString::fromLatin1(creditToType[i].credit)) { frames.setValue(creditToType[i].type, artist); found = true; break; } } if (!found && type != QLatin1String("tribute")) { addInvolvedPeople(frames, Frame::FT_Arranger, type, artist); } } } result = true; relation = relation.nextSibling(); } return result; } /** * Parse result of album request and populate m_trackDataModel with results. * * @param albumStr album data received */ void MusicBrainzImporter::parseAlbumResults(const QByteArray& albumStr) { /* Odin Wizard Wizard 2003-08-19 B00008OUEN 1 1 The Prophecy 319173 */ int start = albumStr.indexOf(""); QByteArray xmlStr = start >= 0 && end > start ? albumStr.mid(start, end + 11 - start) : albumStr; QDomDocument doc; if (doc.setContent(xmlStr, false)) { QDomElement release = doc.namedItem(QLatin1String("metadata")).toElement().namedItem(QLatin1String("release")).toElement(); FrameCollection framesHdr; const bool standardTags = getStandardTags(); if (standardTags) { framesHdr.setAlbum(release.namedItem(QLatin1String("title")).toElement().text()); framesHdr.setArtist(release.namedItem(QLatin1String("artist-credit")).toElement(). namedItem(QLatin1String("name-credit")).toElement(). namedItem(QLatin1String("artist")).toElement(). namedItem(QLatin1String("name")).toElement().text()); QString date(release.namedItem(QLatin1String("date")).toElement().text()); if (!date.isEmpty()) { QRegExp dateRe(QLatin1String("(\\d{4})(?:-\\d{2})?(?:-\\d{2})?")); int year = 0; if (dateRe.exactMatch(date)) { year = dateRe.cap(1).toInt(); } else { year = date.toInt(); } if (year != 0) { framesHdr.setYear(year); } } } ImportTrackDataVector trackDataVector(m_trackDataModel->getTrackData()); trackDataVector.setCoverArtUrl(QString()); const bool coverArt = getCoverArt(); if (coverArt) { QString asin(release.namedItem(QLatin1String("asin")).toElement().text()); if (!asin.isEmpty()) { trackDataVector.setCoverArtUrl( QLatin1String("http://www.amazon.com/dp/") + asin); } } const bool additionalTags = getAdditionalTags(); if (additionalTags) { // label can be found in the label-info-list QDomElement labelInfoList(release.namedItem(QLatin1String("label-info-list")).toElement()); if (!labelInfoList.isNull()) { QDomElement labelInfo((labelInfoList.namedItem(QLatin1String("label-info")).toElement())); if (!labelInfo.isNull()) { QString label(labelInfo.namedItem(QLatin1String("label")).namedItem(QLatin1String("name")).toElement().text()); if (!label.isEmpty()) { framesHdr.setValue(Frame::FT_Publisher, label); } QString catNo(labelInfo.namedItem(QLatin1String("catalog-number")).toElement().text()); if (!catNo.isEmpty()) { framesHdr.setValue(Frame::FT_CatalogNumber, catNo); } } } // Release country can be found in "country" QString country(release.namedItem(QLatin1String("country")).toElement().text()); if (!country.isEmpty()) { framesHdr.setValue(Frame::FT_ReleaseCountry, country); } } if (additionalTags || coverArt) { QDomNode relationListNode(release.firstChild()); while (!relationListNode.isNull()) { if (relationListNode.nodeName() == QLatin1String("relation-list")) { QDomElement relationList(relationListNode.toElement()); if (!relationList.isNull()) { QString targetType(relationList.attribute(QLatin1String("target-type"))); if (targetType == QLatin1String("artist")) { if (additionalTags) { parseCredits(relationList, framesHdr); } } else if (targetType == QLatin1String("url")) { if (coverArt) { QDomNode relationNode(relationList.firstChild()); while (!relationNode.isNull()) { if (relationNode.nodeName() == QLatin1String("relation")) { QDomElement relation(relationNode.toElement()); if (!relation.isNull()) { QString type(relation.attribute(QLatin1String("type"))); if (type == QLatin1String("cover art link") || type == QLatin1String("amazon asin")) { trackDataVector.setCoverArtUrl( relation.namedItem(QLatin1String("target")).toElement().text()); } } } relationNode = relationNode.nextSibling(); } } } } } relationListNode = relationListNode.nextSibling(); } } ImportTrackDataVector::iterator it = trackDataVector.begin(); bool atTrackDataListEnd = (it == trackDataVector.end()); int discNr = 1, trackNr = 1; bool ok; FrameCollection frames(framesHdr); QDomElement mediumList = release.namedItem(QLatin1String("medium-list")).toElement(); int mediumCount = mediumList.attribute(QLatin1String("count")).toInt(); for (QDomNode mediumNode = mediumList.namedItem(QLatin1String("medium")); !mediumNode.isNull(); mediumNode = mediumNode.nextSibling()) { int position = mediumNode.namedItem(QLatin1String("position")).toElement().text().toInt(&ok); if (ok) { discNr = position; } QDomElement trackList = mediumNode.namedItem(QLatin1String("track-list")).toElement(); for (QDomNode trackNode = trackList.namedItem(QLatin1String("track")); !trackNode.isNull(); trackNode = trackNode.nextSibling()) { if (mediumCount > 1 && additionalTags) { frames.setValue(Frame::FT_Disc, QString::number(discNr)); } QDomElement track = trackNode.toElement(); position = track.namedItem(QLatin1String("position")).toElement().text().toInt(&ok); if (ok) { trackNr = position; } if (standardTags) { frames.setTrack(trackNr); } int duration = track.namedItem(QLatin1String("length")).toElement().text().toInt(); QDomElement recording = track.namedItem(QLatin1String("recording")).toElement(); if (!recording.isNull()) { if (standardTags) { frames.setTitle(recording.namedItem(QLatin1String("title")).toElement().text()); } int length = recording.namedItem(QLatin1String("length")).toElement().text().toInt(&ok); if (ok) { duration = length; } QDomNode artistNode = recording.namedItem(QLatin1String("artist-credit")); if (!artistNode.isNull()) { QString artist(artistNode.toElement(). namedItem(QLatin1String("name-credit")).toElement(). namedItem(QLatin1String("artist")).toElement(). namedItem(QLatin1String("name")).toElement().text()); if (!artist.isEmpty()) { // use the artist in the header as the album artist // and the artist in the track as the artist if (standardTags) { frames.setArtist(artist); } if (additionalTags) { frames.setValue(Frame::FT_AlbumArtist, framesHdr.getArtist()); } } } if (additionalTags) { QDomElement relationList = recording.namedItem(QLatin1String("relation-list")).toElement(); if (!relationList.isNull() && relationList.attribute(QLatin1String("target-type")) == QLatin1String("artist")) { parseCredits(relationList, frames); } } } duration /= 1000; if (atTrackDataListEnd) { ImportTrackData trackData; trackData.setFrameCollection(frames); trackData.setImportDuration(duration); trackDataVector.push_back(trackData); } else { while (!atTrackDataListEnd && !it->isEnabled()) { ++it; atTrackDataListEnd = (it == trackDataVector.end()); } if (!atTrackDataListEnd) { (*it).setFrameCollection(frames); (*it).setImportDuration(duration); ++it; atTrackDataListEnd = (it == trackDataVector.end()); } } ++trackNr; frames = framesHdr; } ++discNr; } // handle redundant tracks frames.clear(); while (!atTrackDataListEnd) { if (it->isEnabled()) { if ((*it).getFileDuration() == 0) { it = trackDataVector.erase(it); } else { (*it).setFrameCollection(frames); (*it).setImportDuration(0); ++it; } } else { ++it; } atTrackDataListEnd = (it == trackDataVector.end()); } m_trackDataModel->setTrackData(trackDataVector); } } /** * Send a query command to search on the server. * * @param cfg import source configuration * @param artist artist to search * @param album album to search */ void MusicBrainzImporter::sendFindQuery( const ServerImporterConfig* cfg, const QString& artist, const QString& album) { Q_UNUSED(cfg) /* * Query looks like this: * http://musicbrainz.org/ws/2/release?query=artist:wizard%20AND%20release:odin */ QString path(QLatin1String("/ws/2/release?query=")); if (!artist.isEmpty()) { QString artistQuery(artist.contains(QLatin1Char(' ')) ? QLatin1Char('"') + artist + QLatin1Char('"') : artist); if (!album.isEmpty()) { artistQuery += QLatin1String(" AND "); } path += QLatin1String("artist:"); path += QString::fromLatin1(QUrl::toPercentEncoding(artistQuery)); } if (!album.isEmpty()) { QString albumQuery(album.contains(QLatin1Char(' ')) ? QLatin1Char('"') + album + QLatin1Char('"') : album); path += QLatin1String("release:"); path += QString::fromLatin1(QUrl::toPercentEncoding(albumQuery)); } sendRequest(QLatin1String("musicbrainz.org:80"), path); } /** * Send a query command to fetch the track list * from the server. * * @param cfg import source configuration * @param cat category * @param id ID */ void MusicBrainzImporter::sendTrackListQuery( const ServerImporterConfig* cfg, const QString& cat, const QString& id) { /* * Query looks like this: * http://musicbrainz.org/ws/2/release/978c7ed1-a854-4ef2-bd4e-e7c1317be854?inc=artists+recordings */ QString path(QLatin1String("/ws/2/")); path += cat; path += QLatin1Char('/'); path += id; path += QLatin1String("?inc=artists+recordings"); if (cfg->m_additionalTags) { path += QLatin1String("+artist-rels+artist-credits+release-rels+recording-rels+" "recording-level-rels+labels"); } if (cfg->m_coverArt) { path += QLatin1String("+url-rels"); } sendRequest(QLatin1String("musicbrainz.org:80"), path); } kid3-3.0.2/src/plugins/musicbrainzimport/musicbrainzimporter.h000066400000000000000000000055571224603511300246710ustar00rootroot00000000000000/** * \file musicbrainzimporter.h * MusicBrainz release database importer. * * \b Project: Kid3 * \author Urs Fleisch * \date 13 Oct 2006 * * Copyright (C) 2006-2011 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MUSICBRAINZIMPORTER_H #define MUSICBRAINZIMPORTER_H #include "serverimporter.h" /** * MusicBrainz release database importer. */ class MusicBrainzImporter : public ServerImporter { public: /** * Constructor. * * @param netMgr network access manager * @param trackDataModel track data to be filled with imported values */ MusicBrainzImporter(QNetworkAccessManager* netMgr, TrackDataModel* trackDataModel); /** * Destructor. */ virtual ~MusicBrainzImporter(); /** * Name of import source. * @return name. */ virtual const char* name() const; /** NULL-terminated array of server strings, 0 if not used */ virtual const char** serverList() const; /** default server, 0 to disable */ virtual const char* defaultServer() const; /** anchor to online help, 0 to disable */ virtual const char* helpAnchor() const; /** configuration, 0 if not used */ virtual ServerImporterConfig* config() const; /** additional tags option, false if not used */ virtual bool additionalTags() const; /** * Process finished findCddbAlbum request. * * @param searchStr search data received */ virtual void parseFindResults(const QByteArray& searchStr); /** * Parse result of album request and populate m_trackDataModel with results. * * @param albumStr album data received */ virtual void parseAlbumResults(const QByteArray& albumStr); /** * Send a query command to search on the server. * * @param cfg import source configuration * @param artist artist to search * @param album album to search */ virtual void sendFindQuery( const ServerImporterConfig* cfg, const QString& artist, const QString& album); /** * Send a query command to fetch the track list * from the server. * * @param cfg import source configuration * @param cat category * @param id ID */ virtual void sendTrackListQuery( const ServerImporterConfig* cfg, const QString& cat, const QString& id); }; #endif kid3-3.0.2/src/plugins/musicbrainzimport/musicbrainzimportplugin.cpp000066400000000000000000000040751224603511300261060ustar00rootroot00000000000000/** * \file musicbrainzimportplugin.cpp * MusicBrainz import plugin. * * \b Project: Kid3 * \author Urs Fleisch * \date 15 Jul 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "musicbrainzimportplugin.h" #include "musicbrainzimporter.h" #if QT_VERSION < 0x050000 Q_EXPORT_PLUGIN2(MusicBrainzImportPlugin, MusicBrainzImportPlugin) #endif static const QLatin1String IMPORTER_NAME("MusicBrainzImport"); /*! * Constructor. * @param parent parent object */ MusicBrainzImportPlugin::MusicBrainzImportPlugin(QObject* parent) : QObject(parent) { setObjectName(QLatin1String("MusicBrainzImport")); } /** * Destructor. */ MusicBrainzImportPlugin::~MusicBrainzImportPlugin() { } /** * Get keys of available server importers. * @return list of keys. */ QStringList MusicBrainzImportPlugin::serverImporterKeys() const { return QStringList() << IMPORTER_NAME; } /** * Create server importer. * @param key server importer key * @param netMgr network access manager * @param trackDataModel track data to be filled with imported values * @return server importer instance, 0 if key unknown. * @remarks The caller takes ownership of the returned instance. */ ServerImporter* MusicBrainzImportPlugin::createServerImporter( const QString& key, QNetworkAccessManager* netMgr, TrackDataModel* trackDataModel) { if (key == IMPORTER_NAME) { return new MusicBrainzImporter(netMgr, trackDataModel); } return 0; } kid3-3.0.2/src/plugins/musicbrainzimport/musicbrainzimportplugin.h000066400000000000000000000040751224603511300255530ustar00rootroot00000000000000/** * \file musicbrainzimportplugin.h * MusicBrainz import plugin. * * \b Project: Kid3 * \author Urs Fleisch * \date 15 Jul 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MUSICBRAINZIMPORTPLUGIN_H #define MUSICBRAINZIMPORTPLUGIN_H #include #include "iserverimporterfactory.h" /** * Interface for server importer factory. */ class KID3_PLUGIN_EXPORT MusicBrainzImportPlugin : public QObject, public IServerImporterFactory { Q_OBJECT #if QT_VERSION >= 0x050000 Q_PLUGIN_METADATA(IID "net.sourceforge.kid3.IServerImporterFactory") #endif Q_INTERFACES(IServerImporterFactory) public: /*! * Constructor. * @param parent parent object */ explicit MusicBrainzImportPlugin(QObject* parent = 0); /** * Destructor. */ virtual ~MusicBrainzImportPlugin(); /** * Get keys of available server importers. * @return list of keys. */ virtual QStringList serverImporterKeys() const; /** * Create server importer. * @param key server importer key * @param netMgr network access manager * @param trackDataModel track data to be filled with imported values * @return server importer instance, 0 if key unknown. * @remarks The caller takes ownership of the returned instance. */ virtual ServerImporter* createServerImporter( const QString& key, QNetworkAccessManager* netMgr, TrackDataModel* trackDataModel); }; #endif // MUSICBRAINZIMPORTPLUGIN_H kid3-3.0.2/src/plugins/oggflacmetadata/000077500000000000000000000000001224603511300177165ustar00rootroot00000000000000kid3-3.0.2/src/plugins/oggflacmetadata/CMakeLists.txt000066400000000000000000000073331224603511300224640ustar00rootroot00000000000000### Check for Vorbis set(VORBIS_LIBRARIES) if(WITH_VORBIS) find_path(VORBIS_INCLUDE_DIR vorbis/vorbisfile.h) find_library(OGG_LIBRARY NAMES ogg libogg_static) find_library(VORBIS_LIBRARY NAMES vorbis libvorbis_static) find_library(VORBISFILE_LIBRARY NAMES vorbisfile libvorbisfile_static) if(VORBIS_INCLUDE_DIR AND OGG_LIBRARY AND VORBIS_LIBRARY AND VORBISFILE_LIBRARY) set(VORBIS_LIBRARIES ${VORBISFILE_LIBRARY} ${VORBIS_LIBRARY} ${OGG_LIBRARY}) message(STATUS "Vorbis found: ${VORBIS_LIBRARIES}") set(HAVE_VORBIS 1) else(VORBIS_INCLUDE_DIR AND OGG_LIBRARY AND VORBIS_LIBRARY AND VORBISFILE_LIBRARY) message(FATAL_ERROR "Could not find Vorbis") endif(VORBIS_INCLUDE_DIR AND OGG_LIBRARY AND VORBIS_LIBRARY AND VORBISFILE_LIBRARY) endif(WITH_VORBIS) ### Check for FLAC++ set(FLAC_LIBRARIES) set(FLAC_DEFINITIONS) if(WITH_FLAC) find_path(FLACPP_INCLUDE_DIR FLAC++/metadata.h) find_library(FLAC_LIBRARY NAMES FLAC) find_library(FLACPP_LIBRARY NAMES FLAC++) if(FLACPP_INCLUDE_DIR AND FLAC_LIBRARY AND FLACPP_LIBRARY) set(FLAC_LIBRARIES ${FLACPP_LIBRARY} ${FLAC_LIBRARY}) message(STATUS "FLAC++ found: ${FLAC_LIBRARIES}") set(HAVE_FLAC 1) if (WIN32) set(FLAC_DEFINITIONS -DFLAC__NO_DLL) set(FLAC_LIBRARIES ${FLAC_LIBRARIES} ws2_32) endif (WIN32) set(_CMAKE_REQUIRED_LIBRARIES_TMP ${CMAKE_REQUIRED_LIBRARIES}) set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} ${FLAC_LIBRARIES} ${OGG_LIBRARY}) set(_CMAKE_REQUIRED_INCLUDES_TMP ${CMAKE_REQUIRED_INCLUDES}) set(CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES} ${FLACPP_INCLUDE_DIR}) set(_CMAKE_REQUIRED_DEFINITIONS_TMP ${CMAKE_REQUIRED_DEFINITIONS}) set(CMAKE_REQUIRED_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} ${FLAC_DEFINITIONS}) CHECK_CXX_SOURCE_COMPILES("#include \nint main() {\n FLAC::Metadata::VorbisComment vc;\n const ::FLAC__StreamMetadata* fsmd = vc;\n return 0;\n}\n" FLAC_STREAMMETADATA_OPERATOR_FOUND) CHECK_CXX_SOURCE_COMPILES("#include \nint main() {\n FLAC::Metadata::Picture pic;\n return 0;\n}\n" FLAC_METADATA_PICTURE_FOUND) set(CMAKE_REQUIRED_LIBRARIES ${_CMAKE_REQUIRED_LIBRARIES_TMP}) set(CMAKE_REQUIRED_INCLUDES ${_CMAKE_REQUIRED_INCLUDES_TMP}) set(CMAKE_REQUIRED_DEFINITIONS ${_CMAKE_REQUIRED_DEFINITIONS_TMP}) if(NOT FLAC_STREAMMETADATA_OPERATOR_FOUND) set(HAVE_NO_FLAC_STREAMMETADATA_OPERATOR 1) endif(NOT FLAC_STREAMMETADATA_OPERATOR_FOUND) if(FLAC_METADATA_PICTURE_FOUND) set(HAVE_FLAC_PICTURE 1) endif(FLAC_METADATA_PICTURE_FOUND) else(FLACPP_INCLUDE_DIR AND FLAC_LIBRARY AND FLACPP_LIBRARY) message(FATAL_ERROR "Could not find FLAC++") endif(FLACPP_INCLUDE_DIR AND FLAC_LIBRARY AND FLACPP_LIBRARY) endif(WITH_FLAC) if(HAVE_VORBIS OR HAVE_FLAC) configure_file(oggflacconfig.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/oggflacconfig.h) include_directories(${CMAKE_CURRENT_BINARY_DIR} ${VORBIS_INCLUDE_DIR} ../../core/tags) set(plugin_SRCS oggflacmetadataplugin.cpp oggfile.cpp vcedit.c ) if(HAVE_FLAC) add_definitions(${FLAC_DEFINITIONS}) include_directories(${FLACPP_INCLUDE_DIR}) set(plugin_SRCS ${plugin_SRCS} flacfile.cpp) endif(HAVE_FLAC) set(plugin_MOC_HDRS oggflacmetadataplugin.h ) set(plugin_NAME OggFlacMetadata) if (WITH_GCC_PCH) add_definitions(${GCC_PCH_COMPILE_FLAGS}) endif (WITH_GCC_PCH) string(TOLOWER ${plugin_NAME} plugin_TARGET) qt4_wrap_cpp(plugin_GEN_MOC_SRCS ${plugin_MOC_HDRS}) add_library(${plugin_TARGET} ${plugin_SRCS} ${plugin_GEN_MOC_SRCS}) target_link_libraries(${plugin_TARGET} kid3-core ${BASIC_LIBRARIES} ${VORBIS_LIBRARIES} ${FLAC_LIBRARIES}) INSTALL_KID3_PLUGIN(${plugin_TARGET} ${plugin_NAME}) endif(HAVE_VORBIS OR HAVE_FLAC) kid3-3.0.2/src/plugins/oggflacmetadata/flacfile.cpp000066400000000000000000000375571224603511300222100ustar00rootroot00000000000000/** * \file flacfile.cpp * Handling of FLAC files. * * \b Project: Kid3 * \author Urs Fleisch * \date 04 Oct 2005 * * Copyright (C) 2005-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "flacfile.hpp" #include "genres.h" #include "pictureframe.h" #include #include #include #include #include #ifdef Q_OS_WIN32 #include #else #include #endif #include #include #include /** * Constructor. * * @param dn directory name * @param fn filename * @param idx model index */ FlacFile::FlacFile(const QString& dn, const QString& fn, const QPersistentModelIndex& idx) : OggFile(dn, fn, idx), m_chain(0) { } /** * Destructor. */ FlacFile::~FlacFile() { if (m_chain) { delete m_chain; } } #ifdef HAVE_FLAC_PICTURE /** * Get the picture block as a picture frame. * * @param frame frame to set * @param pic picture block to get */ static void getPicture(Frame& frame, const FLAC::Metadata::Picture* pic) { QByteArray ba(reinterpret_cast(pic->get_data()), pic->get_data_length()); PictureFrame::setFields( frame, Frame::Field::TE_ISO8859_1, QLatin1String(""), QString::fromLatin1(pic->get_mime_type()), static_cast(pic->get_type()), QString::fromUtf8( reinterpret_cast(pic->get_description())), ba); frame.setExtendedType(Frame::ExtendedType(Frame::FT_Picture, QLatin1String("Picture"))); } /** * Set the picture block with the picture frame. * * @param frame frame to get * @param pic picture block to set */ static void setPicture(const Frame& frame, FLAC::Metadata::Picture* pic) { Frame::Field::TextEncoding enc; PictureFrame::PictureType pictureType = PictureFrame::PT_CoverFront; QString imgFormat, mimeType, description; QByteArray ba; PictureFrame::getFields(frame, enc, imgFormat, mimeType, pictureType, description, ba); QImage image; if (image.loadFromData(ba)) { pic->set_width(image.width()); pic->set_height(image.height()); pic->set_depth(image.depth()); #if QT_VERSION >= 0x040600 pic->set_colors(image.colorCount()); #else pic->set_colors(image.numColors()); #endif } pic->set_mime_type(mimeType.toLatin1()); pic->set_type( static_cast(pictureType)); pic->set_description( reinterpret_cast( static_cast(description.toUtf8()))); pic->set_data(reinterpret_cast(ba.data()), ba.size()); } #endif // HAVE_FLAC_PICTURE /** * Get key of tagged file format. * @return "FlacMetadata". */ QString FlacFile::taggedFileKey() const { return QLatin1String("FlacMetadata"); } /** * Read tags from file. * * @param force true to force reading even if tags were already read. */ void FlacFile::readTags(bool force) { if (force || !m_fileRead) { m_comments.clear(); markTag2Unchanged(); m_fileRead = true; QByteArray fnIn = QFile::encodeName(getDirname() + QDir::separator() + currentFilename()); m_fileInfo.read(0); // just to start invalid if (!m_chain) { m_chain = new FLAC::Metadata::Chain; } if (m_chain && m_chain->is_valid()) { if (m_chain->read(fnIn)) { #ifdef HAVE_FLAC_PICTURE m_pictures.clear(); int pictureNr = 0; #endif FLAC::Metadata::Iterator mdit; mdit.init(*m_chain); while (mdit.is_valid()) { ::FLAC__MetadataType mdt = mdit.get_block_type(); if (mdt == FLAC__METADATA_TYPE_STREAMINFO) { FLAC::Metadata::Prototype* proto = mdit.get_block(); if (proto) { FLAC::Metadata::StreamInfo* si = dynamic_cast(proto); m_fileInfo.read(si); delete proto; } } else if (mdt == FLAC__METADATA_TYPE_VORBIS_COMMENT) { FLAC::Metadata::Prototype* proto = mdit.get_block(); if (proto) { FLAC::Metadata::VorbisComment* vc = dynamic_cast(proto); if (vc && vc->is_valid()) { unsigned numComments = vc->get_num_comments(); for (unsigned i = 0; i < numComments; ++i) { FLAC::Metadata::VorbisComment::Entry entry = vc->get_comment(i); if (entry.is_valid()) { QString name = QString::fromUtf8(entry.get_field_name(), entry.get_field_name_length()). trimmed().toUpper(); QString value = QString::fromUtf8(entry.get_field_value(), entry.get_field_value_length()). trimmed(); if (!value.isEmpty()) { m_comments.push_back( CommentField(name, value)); } } } } delete proto; } } #ifdef HAVE_FLAC_PICTURE else if (mdt == FLAC__METADATA_TYPE_PICTURE) { FLAC::Metadata::Prototype* proto = mdit.get_block(); if (proto) { FLAC::Metadata::Picture* pic = dynamic_cast(proto); if (pic) { Frame frame(Frame::FT_Picture, QLatin1String(""), QLatin1String(""), pictureNr++); getPicture(frame, pic); m_pictures.push_back(frame); } delete proto; } } #endif if (!mdit.next()) { break; } } } } } if (force) { setFilename(currentFilename()); } } /** * Write tags to file and rename it if necessary. * * @param force true to force writing even if file was not changed. * @param renamed will be set to true if the file was renamed, * i.e. the file name is no longer valid, else *renamed * is left unchanged * @param preserve true to preserve file time stamps * * @return true if ok, false if the file could not be written or renamed. */ bool FlacFile::writeTags(bool force, bool* renamed, bool preserve) { if (isChanged() && !QFileInfo(getDirname() + QDir::separator() + currentFilename()).isWritable()) { return false; } if (m_fileRead && (force || isTag2Changed()) && m_chain && m_chain->is_valid()) { bool commentsSet = false; #ifdef HAVE_FLAC_PICTURE bool pictureSet = false; bool pictureRemoved = false; PictureList::iterator pictureIt = m_pictures.begin(); #endif if (m_chain->status() == FLAC__METADATA_CHAIN_STATUS_NOT_A_FLAC_FILE) { // This check is done because of a crash in mdit.get_block_type() with an // empty file with flac extension. m_chain->status() will set the status // to FLAC__METADATA_CHAIN_STATUS_OK (!?), so we have to delete the // chain to avoid a crash with the next call to writeTags(). delete m_chain; m_chain = 0; return false; } m_chain->sort_padding(); FLAC::Metadata::Iterator mdit; mdit.init(*m_chain); while (mdit.is_valid()) { ::FLAC__MetadataType mdt = mdit.get_block_type(); if (mdt == FLAC__METADATA_TYPE_VORBIS_COMMENT) { if (commentsSet) { mdit.delete_block(true); } else { FLAC::Metadata::Prototype* proto = mdit.get_block(); if (proto) { FLAC::Metadata::VorbisComment* vc = dynamic_cast(proto); if (vc && vc->is_valid()) { setVorbisComment(vc); commentsSet = true; } delete proto; } } } #ifdef HAVE_FLAC_PICTURE else if (mdt == FLAC__METADATA_TYPE_PICTURE) { if (pictureIt != m_pictures.end()) { FLAC::Metadata::Prototype* proto = mdit.get_block(); if (proto) { FLAC::Metadata::Picture* pic = dynamic_cast(proto); if (pic) { setPicture(*pictureIt++, pic); pictureSet = true; } delete proto; } } else { mdit.delete_block(false); pictureRemoved = true; } } else if (mdt == FLAC__METADATA_TYPE_PADDING) { if (pictureIt != m_pictures.end()) { FLAC::Metadata::Picture* pic = new FLAC::Metadata::Picture; setPicture(*pictureIt, pic); if (mdit.set_block(pic)) { ++pictureIt; pictureSet = true; } else { delete pic; } } else if (pictureRemoved) { mdit.delete_block(false); } } #endif if (!mdit.next()) { if (!commentsSet) { FLAC::Metadata::VorbisComment* vc = new FLAC::Metadata::VorbisComment; if (vc->is_valid()) { setVorbisComment(vc); if (mdit.insert_block_after(vc)) { commentsSet = true; } } if (!commentsSet) { delete vc; } } #ifdef HAVE_FLAC_PICTURE while (pictureIt != m_pictures.end()) { FLAC::Metadata::Picture* pic = new FLAC::Metadata::Picture; setPicture(*pictureIt, pic); if (mdit.insert_block_after(pic)) { pictureSet = true; } else { delete pic; } ++pictureIt; } #endif break; } } #ifdef HAVE_FLAC_PICTURE if ((commentsSet || pictureSet) && m_chain->write(!pictureRemoved, preserve)) { markTag2Unchanged(); } #else if (commentsSet && m_chain->write(true, preserve)) { markTag2Unchanged(); } #endif else { return false; } } if (getFilename() != currentFilename()) { if (!renameFile(currentFilename(), getFilename())) { return false; } updateCurrentFilename(); // link tags to new file name readTags(true); *renamed = true; } return true; } #ifdef HAVE_FLAC_PICTURE /** * Check if file has an ID3v2 tag. * * @return true if a V2 tag is available. * @see isTagInformationRead() */ bool FlacFile::hasTagV2() const { return OggFile::hasTagV2() || !m_pictures.empty(); } /** * Set a frame in the tags 2. * * @param frame frame to set * * @return true if ok. */ bool FlacFile::setFrameV2(const Frame& frame) { if (frame.getType() == Frame::FT_Picture) { int index = frame.getIndex(); if (index != -1 && index < static_cast(m_pictures.size())) { PictureList::iterator it = m_pictures.begin() + index; if (it != m_pictures.end()) { Frame newFrame(frame); PictureFrame::setDescription(newFrame, frame.getValue()); if (PictureFrame::areFieldsEqual(*it, newFrame)) { it->setValueChanged(false); } else { *it = newFrame; markTag2Changed(Frame::FT_Picture); } return true; } } } return OggFile::setFrameV2(frame); } /** * Add a frame in the tags 2. * * @param frame frame to add * * @return true if ok. */ bool FlacFile::addFrameV2(Frame& frame) { if (frame.getType() == Frame::FT_Picture) { if (frame.getFieldList().empty()) { PictureFrame::setFields( frame, Frame::Field::TE_ISO8859_1, QLatin1String("JPG"), QLatin1String("image/jpeg"), PictureFrame::PT_CoverFront, QLatin1String(""), QByteArray()); } PictureFrame::setDescription(frame, frame.getValue()); frame.setIndex(m_pictures.size()); m_pictures.push_back(frame); markTag2Changed(Frame::FT_Picture); return true; } return OggFile::addFrameV2(frame); } /** * Delete a frame in the tags 2. * * @param frame frame to delete. * * @return true if ok. */ bool FlacFile::deleteFrameV2(const Frame& frame) { if (frame.getType() == Frame::FT_Picture) { int index = frame.getIndex(); if (index != -1 && index < static_cast(m_pictures.size())) { m_pictures.removeAt(index); markTag2Changed(Frame::FT_Picture); return true; } } return OggFile::deleteFrameV2(frame); } /** * Remove ID3v2 frames. * * @param flt filter specifying which frames to remove */ void FlacFile::deleteFramesV2(const FrameFilter& flt) { if (flt.areAllEnabled() || flt.isEnabled(Frame::FT_Picture)) { m_pictures.clear(); markTag2Changed(Frame::FT_Picture); } OggFile::deleteFramesV2(flt); } /** * Get all frames in tag 2. * * @param frames frame collection to set. */ void FlacFile::getAllFramesV2(FrameCollection& frames) { OggFile::getAllFramesV2(frames); int i = 0; for (PictureList::iterator it = m_pictures.begin(); it != m_pictures.end(); ++it) { (*it).setIndex(i++); frames.insert(*it); } } #endif // HAVE_FLAC_PICTURE /** * Set the vorbis comment block with the comments. * * @param vc vorbis comment block to set */ void FlacFile::setVorbisComment(FLAC::Metadata::VorbisComment* vc) { // first all existing comments are deleted #ifndef HAVE_NO_FLAC_STREAMMETADATA_OPERATOR // the C++ API is not complete const ::FLAC__StreamMetadata* fsmd = *vc; FLAC__metadata_object_vorbiscomment_resize_comments( const_cast(fsmd), 0); #else const unsigned numComments = vc->get_num_comments(); for (unsigned i = 0; i < numComments; ++i) { vc->delete_comment(0); } #endif // then our comments are appended CommentList::iterator it = m_comments.begin(); while (it != m_comments.end()) { QString name((*it).getName()); QString value((*it).getValue()); if (!value.isEmpty()) { // The number of bytes - not characters - has to be passed to the // Entry constructor, thus qstrlen is used. QByteArray valueCStr = value.toUtf8(); vc->insert_comment(vc->get_num_comments(), FLAC::Metadata::VorbisComment::Entry( name.toLatin1().data(), valueCStr, qstrlen(valueCStr))); ++it; } else { it = m_comments.erase(it); } } } /** * Get technical detail information. * * @param info the detail information is returned here */ void FlacFile::getDetailInfo(DetailInfo& info) const { if (m_fileRead && m_fileInfo.valid) { info.valid = true; info.format = QLatin1String("FLAC"); info.bitrate = m_fileInfo.bitrate / 1000; info.sampleRate = m_fileInfo.sampleRate; info.channels = m_fileInfo.channels; info.duration = m_fileInfo.duration; } else { info.valid = false; } } /** * Get duration of file. * * @return duration in seconds, * 0 if unknown. */ unsigned FlacFile::getDuration() const { if (m_fileRead && m_fileInfo.valid) { return m_fileInfo.duration; } return 0; } /** * Get file extension including the dot. * * @return file extension ".flac". */ QString FlacFile::getFileExtension() const { return QLatin1String(".flac"); } /** * Read information about a FLAC file. * @param fn file name * @return true if ok. */ bool FlacFile::FileInfo::read(FLAC::Metadata::StreamInfo* si) { if (si && si->is_valid()) { valid = true; channels = si->get_channels(); sampleRate = si->get_sample_rate(); duration = si->get_total_samples() / sampleRate; bitrate = si->get_bits_per_sample() * sampleRate; } else { valid = false; } return valid; } kid3-3.0.2/src/plugins/oggflacmetadata/flacfile.hpp000066400000000000000000000112661224603511300222020ustar00rootroot00000000000000/** * \file flacfile.hpp * Handling of FLAC files. * * \b Project: Kid3 * \author Urs Fleisch * \date 04 Oct 2005 * * Copyright (C) 2005-2011 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef FLACFILE_H #define FLACFILE_H #include "oggflacconfig.h" #include "oggfile.hpp" namespace FLAC { namespace Metadata { class Chain; class VorbisComment; class StreamInfo; class Picture; }; }; /** List box item containing FLAC file */ class FlacFile : public OggFile { public: /** * Constructor. * * @param dn directory name * @param fn filename * @param idx model index */ FlacFile(const QString& dn, const QString& fn, const QPersistentModelIndex& idx); /** * Destructor. */ virtual ~FlacFile(); /** * Get key of tagged file format. * @return "FlacMetadata". */ virtual QString taggedFileKey() const; /** * Read tags from file. * * @param force true to force reading even if tags were already read. */ virtual void readTags(bool force); /** * Write tags to file and rename it if necessary. * * @param force true to force writing even if file was not changed. * @param renamed will be set to true if the file was renamed, * i.e. the file name is no longer valid, else *renamed * is left unchanged * @param preserve true to preserve file time stamps * * @return true if ok, false if the file could not be written or renamed. */ virtual bool writeTags(bool force, bool* renamed, bool preserve); /** * Get technical detail information. * * @param info the detail information is returned here */ virtual void getDetailInfo(DetailInfo& info) const; /** * Get duration of file. * * @return duration in seconds, * 0 if unknown. */ virtual unsigned getDuration() const; /** * Get file extension including the dot. * * @return file extension ".flac". */ virtual QString getFileExtension() const; #ifdef HAVE_FLAC_PICTURE /** * Check if file has an ID3v2 tag. * * @return true if a V2 tag is available. * @see isTagInformationRead() */ virtual bool hasTagV2() const; /** * Set a frame in the tags 2. * * @param frame frame to set * * @return true if ok. */ virtual bool setFrameV2(const Frame& frame); /** * Add a frame in the tags 2. * * @param frame frame to add * * @return true if ok. */ virtual bool addFrameV2(Frame& frame); /** * Delete a frame in the tags 2. * * @param frame frame to delete. * * @return true if ok. */ virtual bool deleteFrameV2(const Frame& frame); /** * Remove ID3v2 frames. * * @param flt filter specifying which frames to remove */ virtual void deleteFramesV2(const FrameFilter& flt); /** * Get all frames in tag 2. * * @param frames frame collection to set. */ virtual void getAllFramesV2(FrameCollection& frames); #endif // HAVE_FLAC_PICTURE private: FlacFile(const FlacFile&); FlacFile& operator=(const FlacFile&); /** Information about a FLAC file. */ struct FileInfo { FileInfo() : valid(false), channels(0), sampleRate(0), bitrate(0), duration(0) {} /** * Read information about a FLAC file. * @param si stream info * @return true if ok. */ bool read(FLAC::Metadata::StreamInfo* si); bool valid; /**< true if read() was successful */ unsigned channels; /**< number of channels */ unsigned sampleRate; /**< sample rate in Hz */ unsigned long bitrate; /**< bitrate in bits/s */ unsigned long duration; /**< duration in seconds */ }; /** * Set the vorbis comment block with the comments. * * @param vc vorbis comment block to set */ void setVorbisComment(FLAC::Metadata::VorbisComment* vc); #ifdef HAVE_FLAC_PICTURE /** Pictures */ typedef QList PictureList; PictureList m_pictures; #endif // HAVE_FLAC_PICTURE /** Info about file. */ FileInfo m_fileInfo; /** FLAC metadata chain. */ FLAC::Metadata::Chain* m_chain; }; #endif // FLACFILE_H kid3-3.0.2/src/plugins/oggflacmetadata/oggfile.cpp000066400000000000000000000632221224603511300220430ustar00rootroot00000000000000/** * \file oggfile.cpp * Handling of Ogg files. * * \b Project: Kid3 * \author Urs Fleisch * \date 26 Sep 2005 * * Copyright (C) 2005-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "oggfile.hpp" #include #include #include #include #ifdef Q_OS_WIN32 #include #else #include #endif #include #include #ifdef HAVE_VORBIS #include #include "vcedit.h" #endif #include "pictureframe.h" #include "tagconfig.h" namespace { /* * The following functions are used to access an Ogg/Vorbis file * using a QIODevice. They are used by vcedit_open_callbacks() and * ov_open_callbacks(). */ /** * Read from a QIODevice using an fread() like interface. * @param ptr location to store data read * @param size size of one element in bytes * @param nmemb number of elements to read * @param stream QIODevice* to read from * @return number of elements read. */ size_t oggread(void* ptr, size_t size, size_t nmemb, void* stream) { if (!stream || !size) return 0; QIODevice* iodev = reinterpret_cast(stream); qint64 len = iodev->read(reinterpret_cast(ptr), size * nmemb); return len / size; } /** * Write to a QIODevice using an fwrite() like interface. * @param ptr location of data to write * @param size size of one element in bytes * @param nmemb number of elements to write * @param stream QIODevice* to write to * @return number of elements written. */ size_t oggwrite(const void* ptr, size_t size, size_t nmemb, void* stream) { if (!stream || !size) return 0; QIODevice* iodev = reinterpret_cast(stream); qint64 len = iodev->write(reinterpret_cast(ptr), size * nmemb); return len / size; } /** * Seek in a QIODevice using an fseek() like interface. * @param stream QIODevice* to seek * @param offset byte position * @param whence SEEK_SET, SEEK_CUR, or SEEK_END * @return 0 if ok, -1 on error. */ int oggseek(void* stream, ogg_int64_t offset, int whence) { QIODevice* iodev = reinterpret_cast(stream); if (!iodev || iodev->isSequential()) return -1; qint64 pos = offset; if (whence == SEEK_END) { pos += iodev->size(); } else if (whence == SEEK_CUR) { pos += iodev->pos(); } if (iodev->seek(pos)) return 0; return -1; } /** * Close QIODevice using an fclose() like interface. * @param stream QIODevice* to close * @return 0 if ok. */ int oggclose(void* stream) { if (QIODevice* iodev = reinterpret_cast(stream)) { iodev->close(); return 0; } return -1; } /** * Get position in QIODevice using an ftell() like interface. * @param stream QIODevice* * @return current position, -1 on error. */ long oggtell(void* stream) { if (QIODevice* iodev = reinterpret_cast(stream)) { return iodev->pos(); } return -1; } } /** * Constructor. * * @param dn directory name * @param fn filename * @param idx model index */ OggFile::OggFile(const QString& dn, const QString& fn, const QPersistentModelIndex& idx) : TaggedFile(dn, fn, idx), m_fileRead(false) { } /** * Destructor. */ OggFile::~OggFile() { } /** * Get key of tagged file format. * @return "OggMetadata". */ QString OggFile::taggedFileKey() const { return QLatin1String("OggMetadata"); } #ifdef HAVE_VORBIS /** * Get features supported. * @return bit mask with Feature flags set. */ int OggFile::taggedFileFeatures() const { return TF_OggPictures; } /** * Read tags from file. * * @param force true to force reading even if tags were already read. */ void OggFile::readTags(bool force) { if (force || !m_fileRead) { m_comments.clear(); markTag2Unchanged(); m_fileRead = true; QString fnIn = getDirname() + QDir::separator() + currentFilename(); if (m_fileInfo.read(fnIn)) { QFile fpIn(fnIn); if (fpIn.open(QIODevice::ReadOnly)) { vcedit_state* state = ::vcedit_new_state(); if (state) { if (::vcedit_open_callbacks(state, &fpIn, oggread, oggwrite) >= 0) { vorbis_comment* vc = ::vcedit_comments(state); if (vc) { for (int i = 0; i < vc->comments; ++i) { QString userComment = QString::fromUtf8(vc->user_comments[i], vc->comment_lengths[i]); int equalPos = userComment.indexOf(QLatin1Char('=')); if (equalPos != -1) { QString name( userComment.left(equalPos).trimmed().toUpper()); QString value( userComment.mid(equalPos + 1).trimmed()); if (!value.isEmpty()) { m_comments.push_back(CommentField(name, value)); } } } } } ::vcedit_clear(state); } fpIn.close(); } } } if (force) { setFilename(currentFilename()); } } /** * Write tags to file and rename it if necessary. * * @param force true to force writing even if file was not changed. * @param renamed will be set to true if the file was renamed, * i.e. the file name is no longer valid, else *renamed * is left unchanged * @param preserve true to preserve file time stamps * * @return true if ok, false if the file could not be written or renamed. */ bool OggFile::writeTags(bool force, bool* renamed, bool preserve) { QString dirname = getDirname(); if (isChanged() && !QFileInfo(dirname + QDir::separator() + currentFilename()).isWritable()) { return false; } if (m_fileRead && (force || isTag2Changed())) { bool writeOk = false; // we have to rename the original file and delete it afterwards QString tempFilename(currentFilename() + QLatin1String("_KID3")); if (!renameFile(currentFilename(), tempFilename)) { return false; } QString fnIn = dirname + QDir::separator() + tempFilename; QString fnOut = dirname + QDir::separator() + getFilename(); QFile fpIn(fnIn); if (fpIn.open(QIODevice::ReadOnly)) { // store time stamp if it has to be preserved bool setUtime = false; struct utimbuf times; if (preserve) { int fd = fpIn.handle(); if (fd >= 0) { struct stat fileStat; if (::fstat(fd, &fileStat) == 0) { times.actime = fileStat.st_atime; times.modtime = fileStat.st_mtime; setUtime = true; } } } QFile fpOut(fnOut); if (fpOut.open(QIODevice::WriteOnly)) { vcedit_state* state = ::vcedit_new_state(); if (state) { if (::vcedit_open_callbacks(state, &fpIn, oggread, oggwrite) >= 0) { vorbis_comment* vc = ::vcedit_comments(state); if (vc) { ::vorbis_comment_clear(vc); ::vorbis_comment_init(vc); CommentList::iterator it = m_comments.begin(); while (it != m_comments.end()) { QString name((*it).getName()); QString value((*it).getValue()); if (!value.isEmpty()) { ::vorbis_comment_add_tag( vc, const_cast(name.toLatin1().data()), const_cast((const char*)value.toUtf8().data())); ++it; } else { it = m_comments.erase(it); } } if (::vcedit_write(state, &fpOut) >= 0) { writeOk = true; } } } ::vcedit_clear(state); } fpOut.close(); } fpIn.close(); // restore time stamp if (setUtime) { ::utime(QFile::encodeName(fnOut), ×); } } if (!writeOk) { return false; } markTag2Unchanged(); QDir(dirname).remove(tempFilename); if (getFilename() != currentFilename()) { updateCurrentFilename(); *renamed = true; } } else if (getFilename() != currentFilename()) { // tags not changed, but file name if (!renameFile(currentFilename(), getFilename())) { return false; } updateCurrentFilename(); *renamed = true; } return true; } #else // HAVE_VORBIS void OggFile::readTags(bool) {} bool OggFile::writeTags(bool, bool*, bool) { return false; } #endif // HAVE_VORBIS /** * Get name of frame from type. * * @param type type * * @return name. */ static const char* getVorbisNameFromType(Frame::Type type) { static const char* const names[] = { "TITLE", // FT_Title, "ARTIST", // FT_Artist, "ALBUM", // FT_Album, "COMMENT", // FT_Comment, "DATE", // FT_Date, "TRACKNUMBER", // FT_Track, "GENRE", // FT_Genre, // FT_LastV1Frame = FT_Track, "ALBUMARTIST", // FT_AlbumArtist, "ARRANGER", // FT_Arranger, "AUTHOR", // FT_Author, "BPM", // FT_Bpm, "CATALOGNUMBER", // FT_CatalogNumber, "COMPILATION", // FT_Compilation, "COMPOSER", // FT_Composer, "CONDUCTOR", // FT_Conductor, "COPYRIGHT", // FT_Copyright, "DISCNUMBER", // FT_Disc, "ENCODED-BY", // FT_EncodedBy, "ENCODERSETTINGS", // FT_EncoderSettings, "ENCODINGTIME", // FT_EncodingTime, "GROUPING", // FT_Grouping, "INITIALKEY", // FT_InitialKey, "ISRC", // FT_Isrc, "LANGUAGE", // FT_Language, "LYRICIST", // FT_Lyricist, "LYRICS", // FT_Lyrics, "SOURCEMEDIA", // FT_Media, "MOOD", // FT_Mood, "ORIGINALALBUM", // FT_OriginalAlbum, "ORIGINALARTIST", // FT_OriginalArtist, "ORIGINALDATE", // FT_OriginalDate, "PART", // FT_Part, "PERFORMER", // FT_Performer, "METADATA_BLOCK_PICTURE", // FT_Picture, "PUBLISHER", // FT_Publisher, "RELEASECOUNTRY", // FT_ReleaseCountry, "REMIXER", // FT_Remixer, "ALBUMSORT", // FT_SortAlbum, "ALBUMARTISTSORT", // FT_SortAlbumArtist, "ARTISTSORT", // FT_SortArtist, "COMPOSERSORT", // FT_SortComposer, "TITLESORT", // FT_SortName, "SUBTITLE", // FT_Subtitle, "WEBSITE", // FT_Website, "WWWAUDIOFILE", // FT_WWWAudioFile, "WWWAUDIOSOURCE" // FT_WWWAudioSource, // FT_LastFrame = FT_WWWAudioSource }; struct not_used { int array_size_check[ sizeof(names) / sizeof(names[0]) == Frame::FT_LastFrame + 1 ? 1 : -1 ]; }; if (type == Frame::FT_Picture && TagConfig::instance().pictureNameItem() == TagConfig::VP_COVERART) { return "COVERART"; } return type <= Frame::FT_LastFrame ? names[type] : "UNKNOWN"; } /** * Get the frame type for a Vorbis name. * * @param name Vorbis tag name * * @return frame type. */ static Frame::Type getTypeFromVorbisName(QString name) { static QMap strNumMap; if (strNumMap.empty()) { // first time initialization for (int i = 0; i <= Frame::FT_LastFrame; ++i) { Frame::Type type = static_cast(i); strNumMap.insert(QString::fromLatin1(getVorbisNameFromType(type)), type); } strNumMap.insert(QLatin1String("DESCRIPTION"), Frame::FT_Comment); strNumMap.insert(QLatin1String("COVERART"), Frame::FT_Picture); } QMap::const_iterator it = strNumMap.find(name.remove(QLatin1Char('=')).toUpper()); if (it != strNumMap.end()) { return static_cast(*it); } return Frame::FT_Other; } /** * Get internal name of a Vorbis frame. * * @param frame frame * * @return Vorbis key. */ static QString getVorbisName(const Frame& frame) { Frame::Type type = frame.getType(); if (type <= Frame::FT_LastFrame) { return QString::fromLatin1(getVorbisNameFromType(type)); } else { return frame.getName().remove(QLatin1Char('=')).toUpper(); } } /** * Remove ID3v2 frames. * * @param flt filter specifying which frames to remove */ void OggFile::deleteFramesV2(const FrameFilter& flt) { if (flt.areAllEnabled()) { m_comments.clear(); markTag2Changed(Frame::FT_UnknownFrame); } else { bool changed = false; for (OggFile::CommentList::iterator it = m_comments.begin(); it != m_comments.end();) { QString name((*it).getName()); if (flt.isEnabled(getTypeFromVorbisName(name), name)) { it = m_comments.erase(it); changed = true; } else { ++it; } } if (changed) { markTag2Changed(Frame::FT_UnknownFrame); } } } /** * Get ID3v2 title. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ QString OggFile::getTitleV2() { return getTextField(QLatin1String("TITLE")); } /** * Get ID3v2 artist. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ QString OggFile::getArtistV2() { return getTextField(QLatin1String("ARTIST")); } /** * Get ID3v2 album. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ QString OggFile::getAlbumV2() { return getTextField(QLatin1String("ALBUM")); } /** * Get ID3v2 comment. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ QString OggFile::getCommentV2() { return getTextField(getCommentFieldName()); } /** * Get ID3v2 year. * * @return number, * 0 if the field does not exist, * -1 if the tags do not exist. */ int OggFile::getYearV2() { QString str = getTextField(QLatin1String("DATE")); if (str.isNull()) return -1; if (str.isEmpty()) return 0; return str.toInt(); } /** * Get ID3v2 track. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ QString OggFile::getTrackV2() { return getTextField(QLatin1String("TRACKNUMBER")); } /** * Get ID3v2 genre as text. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ QString OggFile::getGenreV2() { return getTextField(QLatin1String("GENRE")); } /** * Get text field. * * @param name name * @return value, "" if not found, * QString::null if the tags have not been read yet. */ QString OggFile::getTextField(const QString& name) const { if (m_fileRead) { return m_comments.getValue(name); } return QString(); } /** * Set text field. * If value is null if the tags have not been read yet, nothing is changed. * If value is different from the current value, tag 2 is marked as changed. * * @param name name * @param value value, "" to remove, QString::null to do nothing * @param type frame type */ void OggFile::setTextField(const QString& name, const QString& value, Frame::Type type) { if (m_fileRead && !value.isNull() && m_comments.setValue(name, value)) { markTag2Changed(type); } } /** * Set ID3v2 title. * * @param str string to set, "" to remove field, QString::null to ignore. */ void OggFile::setTitleV2(const QString& str) { setTextField(QLatin1String("TITLE"), str, Frame::FT_Title); } /** * Set ID3v2 artist. * * @param str string to set, "" to remove field, QString::null to ignore. */ void OggFile::setArtistV2(const QString& str) { setTextField(QLatin1String("ARTIST"), str, Frame::FT_Artist); } /** * Set ID3v2 album. * * @param str string to set, "" to remove field, QString::null to ignore. */ void OggFile::setAlbumV2(const QString& str) { setTextField(QLatin1String("ALBUM"), str, Frame::FT_Album); } /** * Set ID3v2 comment. * * @param str string to set, "" to remove field, QString::null to ignore. */ void OggFile::setCommentV2(const QString& str) { setTextField(getCommentFieldName(), str, Frame::FT_Comment); } /** * Set ID3v2 year. * * @param num number to set, 0 to remove field, < 0 to ignore. */ void OggFile::setYearV2(int num) { if (num >= 0) { QString str; if (num != 0) { str.setNum(num); } else { str = QLatin1String(""); } setTextField(QLatin1String("DATE"), str, Frame::FT_Date); } } /** * Set ID3v2 track. * * @param track string to set, "" to remove field, QString::null to ignore. */ void OggFile::setTrackV2(const QString& track) { int numTracks; int num = splitNumberAndTotal(track, &numTracks); if (num >= 0) { QString str; if (num != 0) { str.setNum(num); formatTrackNumberIfEnabled(str, false); } else { str = QLatin1String(""); } setTextField(QLatin1String("TRACKNUMBER"), str, Frame::FT_Track); if (numTracks > 0) { str.setNum(numTracks); formatTrackNumberIfEnabled(str, false); setTextField(QLatin1String("TRACKTOTAL"), str, Frame::FT_Other); } } } /** * Set ID3v2 genre as text. * * @param str string to set, "" to remove field, QString::null to ignore. */ void OggFile::setGenreV2(const QString& str) { setTextField(QLatin1String("GENRE"), str, Frame::FT_Genre); } /** * Check if tag information has already been read. * * @return true if information is available, * false if the tags have not been read yet, in which case * hasTagV1() and hasTagV2() do not return meaningful information. */ bool OggFile::isTagInformationRead() const { return m_fileRead; } /** * Check if file has an ID3v2 tag. * * @return true if a V2 tag is available. * @see isTagInformationRead() */ bool OggFile::hasTagV2() const { return !m_comments.empty(); } /** * Get file extension including the dot. * * @return file extension ".ogg". */ QString OggFile::getFileExtension() const { return QLatin1String(".ogg"); } #ifdef HAVE_VORBIS /** * Get technical detail information. * * @param info the detail information is returned here */ void OggFile::getDetailInfo(DetailInfo& info) const { if (m_fileRead && m_fileInfo.valid) { info.valid = true; info.format = QLatin1String("Ogg Vorbis"); info.bitrate = m_fileInfo.bitrate / 1000; info.sampleRate = m_fileInfo.sampleRate; info.channels = m_fileInfo.channels; info.duration = m_fileInfo.duration; } else { info.valid = false; } } /** * Get duration of file. * * @return duration in seconds, * 0 if unknown. */ unsigned OggFile::getDuration() const { if (m_fileRead && m_fileInfo.valid) { return m_fileInfo.duration; } return 0; } #else // HAVE_VORBIS void OggFile::getDetailInfo(DetailInfo& info) const { info.valid = false; } unsigned OggFile::getDuration() const { return 0; } #endif // HAVE_VORBIS /** * Get the format of tag 2. * * @return "Vorbis". */ QString OggFile::getTagFormatV2() const { return hasTagV2() ? QLatin1String("Vorbis") : QString(); } /** * Set a frame in the tags 2. * * @param frame frame to set * * @return true if ok. */ bool OggFile::setFrameV2(const Frame& frame) { if (frame.getType() == Frame::FT_Track) { int numTracks = getTotalNumberOfTracksIfEnabled(); if (numTracks > 0) { QString numTracksStr = QString::number(numTracks); formatTrackNumberIfEnabled(numTracksStr, false); if (getTextField(QLatin1String("TRACKTOTAL")) != numTracksStr) { setTextField(QLatin1String("TRACKTOTAL"), numTracksStr, Frame::FT_Other); markTag2Changed(Frame::FT_Other); } } } // If the frame has an index, change that specific frame int index = frame.getIndex(); if (index != -1 && index < static_cast(m_comments.size())) { QString value = frame.getValue(); if (frame.getType() == Frame::FT_Picture) { PictureFrame::getFieldsToBase64(frame, value); if (!value.isEmpty() && frame.getInternalName() == QLatin1String("COVERART")) { QString mimeType; PictureFrame::getMimeType(frame, mimeType); setTextField(QLatin1String("COVERARTMIME"), mimeType, Frame::FT_Other); } } else if (frame.getType() == Frame::FT_Track) { formatTrackNumberIfEnabled(value, false); } if (m_comments[index].getValue() != value) { m_comments[index].setValue(value); markTag2Changed(frame.getType()); } return true; } // Try the superclass method return TaggedFile::setFrameV2(frame); } /** * Add a frame in the tags 2. * * @param frame frame to add * * @return true if ok. */ bool OggFile::addFrameV2(Frame& frame) { // Add a new frame. QString name(getVorbisName(frame)); QString value(frame.getValue()); if (frame.getType() == Frame::FT_Picture) { if (frame.getFieldList().empty()) { PictureFrame::setFields( frame, Frame::Field::TE_ISO8859_1, QLatin1String(""), QLatin1String("image/jpeg"), PictureFrame::PT_CoverFront, QLatin1String(""), QByteArray()); } frame.setExtendedType(Frame::ExtendedType(Frame::FT_Picture, name)); PictureFrame::getFieldsToBase64(frame, value); } m_comments.push_back(OggFile::CommentField(name, value)); frame.setExtendedType(Frame::ExtendedType(frame.getType(), name)); frame.setIndex(m_comments.size() - 1); markTag2Changed(frame.getType()); return true; } /** * Delete a frame in the tags 2. * * @param frame frame to delete. * * @return true if ok. */ bool OggFile::deleteFrameV2(const Frame& frame) { // If the frame has an index, delete that specific frame int index = frame.getIndex(); if (index != -1 && index < static_cast(m_comments.size())) { m_comments.removeAt(index); markTag2Changed(frame.getType()); return true; } // Try the superclass method return TaggedFile::deleteFrameV2(frame); } /** * Get all frames in tag 2. * * @param frames frame collection to set. */ void OggFile::getAllFramesV2(FrameCollection& frames) { frames.clear(); QString name; int i = 0; for (OggFile::CommentList::const_iterator it = m_comments.begin(); it != m_comments.end(); ++it) { name = (*it).getName(); Frame::Type type = getTypeFromVorbisName(name); if (type == Frame::FT_Picture) { Frame frame(type, QLatin1String(""), name, i++); PictureFrame::setFieldsFromBase64(frame, (*it).getValue()); if (name == QLatin1String("COVERART")) { PictureFrame::setMimeType(frame, getTextField(QLatin1String("COVERARTMIME"))); } frames.insert(frame); } else { frames.insert(Frame(type, (*it).getValue(), name, i++)); } } frames.addMissingStandardFrames(); } /** * Get a list of frame IDs which can be added. * * @return list with frame IDs. */ QStringList OggFile::getFrameIds() const { static const char* const fieldNames[] = { "CONTACT", "DESCRIPTION", "EAN/UPN", "ENCODING", "ENGINEER", "ENSEMBLE", "GUESTARTIST", "LABEL", "LABELNO", "LICENSE", "LOCATION", "OPUS", "ORGANIZATION", "PARTNUMBER", "PRODUCER", "PRODUCTNUMBER", "RECORDINGDATE", "RELEASEDATE", "SOURCEARTIST", "SOURCEMEDIUM", "SOURCEWORK", "SPARS", "TRACKTOTAL", "VERSION", "VOLUME" }; QStringList lst; for (int k = Frame::FT_FirstFrame; k <= Frame::FT_LastFrame; ++k) { lst.append(Frame::ExtendedType(static_cast(k), QLatin1String("")). getTranslatedName()); } for (unsigned i = 0; i < sizeof(fieldNames) / sizeof(fieldNames[0]); ++i) { lst.append(QString::fromLatin1(fieldNames[i])); } return lst; } #ifdef HAVE_VORBIS /** * Read information about an Ogg/Vorbis file. * @param fn file name * @return true if ok. */ bool OggFile::FileInfo::read(const QString& fn) { static ::ov_callbacks ovcb = { oggread, oggseek, oggclose, oggtell }; valid = false; QFile fp(fn); if (fp.open(QIODevice::ReadOnly)) { OggVorbis_File vf; if (::ov_open_callbacks(&fp, &vf, 0, 0, ovcb) == 0) { vorbis_info* vi = ::ov_info(&vf, -1); if (vi) { valid = true; version = vi->version; channels = vi->channels; sampleRate = vi->rate; bitrate = vi->bitrate_nominal; if (bitrate <= 0) { bitrate = vi->bitrate_upper; } if (bitrate <= 0) { bitrate = vi->bitrate_lower; } } duration = static_cast(::ov_time_total(&vf, -1)); ::ov_clear(&vf); // closes file, do not use ::fclose() } else { fp.close(); } } return valid; } #endif // HAVE_VORBIS /** * Get value. * @param name name * @return value, "" if not found. */ QString OggFile::CommentList::getValue(const QString& name) const { for (const_iterator it = begin(); it != end(); ++it) { if ((*it).getName() == name) { return (*it).getValue(); } } return QLatin1String(""); } /** * Set value. * @param name name * @param value value * @return true if value was changed. */ bool OggFile::CommentList::setValue(const QString& name, const QString& value) { for (iterator it = begin(); it != end(); ++it) { if ((*it).getName() == name) { QString oldValue = (*it).getValue(); if (value != oldValue) { (*it).setValue(value); return true; } else { return false; } } } if (!value.isEmpty()) { CommentField cf(name, value); push_back(cf); return true; } else { return false; } } kid3-3.0.2/src/plugins/oggflacmetadata/oggfile.hpp000066400000000000000000000221261224603511300220460ustar00rootroot00000000000000/** * \file oggfile.hpp * Handling of Ogg files. * * \b Project: Kid3 * \author Urs Fleisch * \date 26 Sep 2005 * * Copyright (C) 2005-2011 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef OGGFILE_H #define OGGFILE_H #include "oggflacconfig.h" #include #include "taggedfile.h" /** List box item containing OGG file */ class OggFile : public TaggedFile { public: /** * Constructor. * * @param dn directory name * @param fn filename * @param idx model index */ OggFile(const QString& dn, const QString& fn, const QPersistentModelIndex& idx); /** * Destructor. */ virtual ~OggFile(); /** * Get key of tagged file format. * @return "OggMetadata". */ virtual QString taggedFileKey() const; /** * Get features supported. * @return bit mask with Feature flags set. */ virtual int taggedFileFeatures() const; /** * Read tags from file. * * @param force true to force reading even if tags were already read. */ virtual void readTags(bool force); /** * Write tags to file and rename it if necessary. * * @param force true to force writing even if file was not changed. * @param renamed will be set to true if the file was renamed, * i.e. the file name is no longer valid, else *renamed * is left unchanged * @param preserve true to preserve file time stamps * * @return true if ok, false if the file could not be written or renamed. */ virtual bool writeTags(bool force, bool* renamed, bool preserve); /** * Remove ID3v2 frames. * * @param flt filter specifying which frames to remove */ virtual void deleteFramesV2(const FrameFilter& flt); /** * Get ID3v2 title. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ virtual QString getTitleV2(); /** * Get ID3v2 artist. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ virtual QString getArtistV2(); /** * Get ID3v2 album. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ virtual QString getAlbumV2(); /** * Get ID3v2 comment. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ virtual QString getCommentV2(); /** * Get ID3v2 year. * * @return number, * 0 if the field does not exist, * -1 if the tags do not exist. */ virtual int getYearV2(); /** * Get ID3v2 track. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ virtual QString getTrackV2(); /** * Get ID3v2 genre as text. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ virtual QString getGenreV2(); /** * Set ID3v2 title. * * @param str string to set, "" to remove field. */ virtual void setTitleV2(const QString& str); /** * Set ID3v2 artist. * * @param str string to set, "" to remove field. */ virtual void setArtistV2(const QString& str); /** * Set ID3v2 album. * * @param str string to set, "" to remove field. */ virtual void setAlbumV2(const QString& str); /** * Set ID3v2 comment. * * @param str string to set, "" to remove field. */ virtual void setCommentV2(const QString& str); /** * Set ID3v2 year. * * @param num number to set, 0 to remove field. */ virtual void setYearV2(int num); /** * Set ID3v2 track. * * @param track string to set, "" to remove field, QString::null to ignore. */ virtual void setTrackV2(const QString& track); /** * Set ID3v2 genre as text. * * @param str string to set, "" to remove field, QString::null to ignore. */ virtual void setGenreV2(const QString& str); /** * Check if tag information has already been read. * * @return true if information is available, * false if the tags have not been read yet, in which case * hasTagV1() and hasTagV2() do not return meaningful information. */ virtual bool isTagInformationRead() const; /** * Check if file has an ID3v2 tag. * * @return true if a V2 tag is available. * @see isTagInformationRead() */ virtual bool hasTagV2() const; /** * Get technical detail information. * * @param info the detail information is returned here */ virtual void getDetailInfo(DetailInfo& info) const; /** * Get duration of file. * * @return duration in seconds, * 0 if unknown. */ virtual unsigned getDuration() const; /** * Get file extension including the dot. * * @return file extension ".ogg". */ virtual QString getFileExtension() const; /** * Get the format of tag 2. * * @return "Vorbis". */ virtual QString getTagFormatV2() const; /** * Set a frame in the tags 2. * * @param frame frame to set * * @return true if ok. */ virtual bool setFrameV2(const Frame& frame); /** * Add a frame in the tags 2. * * @param frame frame to add * * @return true if ok. */ virtual bool addFrameV2(Frame& frame); /** * Delete a frame in the tags 2. * * @param frame frame to delete. * * @return true if ok. */ virtual bool deleteFrameV2(const Frame& frame); /** * Get all frames in tag 2. * * @param frames frame collection to set. */ virtual void getAllFramesV2(FrameCollection& frames); /** * Get a list of frame IDs which can be added. * * @return list with frame IDs. */ virtual QStringList getFrameIds() const; protected: /** Vorbis comment field. */ class CommentField { public: /** Constructor. */ CommentField(const QString& name = QString(), const QString& value = QString()) : m_name(name), m_value(value) {} /** Destructor. */ ~CommentField() {} /** * Get name. * @return name. */ QString getName() const { return m_name; } /** * Get value. * @return value. */ QString getValue() const { return m_value; } /** * Set value. * @param value value */ void setValue(const QString& value) { m_value = value; } private: QString m_name; QString m_value; }; /** Vorbis comment list. */ class CommentList : public QList { public: /** Constructor. */ CommentList() {} /** Destructor. */ ~CommentList() {} /** * Get value. * @param name name * @return value, "" if not found. */ QString getValue(const QString& name) const; /** * Set value. * @param name name * @param value value * @return true if value was changed. */ bool setValue(const QString& name, const QString& value); }; /** * Get text field. * * @param name name * @return value, "" if not found, * QString::null if the tags have not been read yet. */ QString getTextField(const QString& name) const; /** * Set text field. * If value is null or the tags have not been read yet, nothing is changed. * If value is different from the current value, changedV2 is set. * * @param name name * @param value value, "" to remove, QString::null to do nothing * @param type frame type */ void setTextField(const QString& name, const QString& value, Frame::Type type); /** Comments of this file. */ CommentList m_comments; /** true if file has been read. */ bool m_fileRead; private: OggFile(const OggFile&); OggFile& operator=(const OggFile&); #ifdef HAVE_VORBIS /** Information about Ogg/Vorbis file. */ struct FileInfo { /** Constructor. */ FileInfo() : valid(false), version(0), channels(0), sampleRate(0), bitrate(0), duration(0) {} /** * Read information about an Ogg/Vorbis file. * @param fn file name * @return true if ok. */ bool read(const QString& fn); bool valid; /**< true if read() was successful */ int version; /**< vorbis encoder version */ int channels; /**< number of channels */ long sampleRate; /**< sample rate in Hz */ long bitrate; /**< bitrate in bits/s */ long duration; /**< duration in seconds */ }; /** Info about file. */ FileInfo m_fileInfo; #endif // HAVE_VORBIS }; #endif // OGGFILE_H kid3-3.0.2/src/plugins/oggflacmetadata/oggflacconfig.h.cmake000066400000000000000000000005371224603511300237430ustar00rootroot00000000000000/* oggflacconfig.h. Generated by cmake from oggflacconfig.h.cmake */ #ifndef OGGFLACCONFIG_H #define OGGFLACCONFIG_H /* Define if you have ogg/vorbis installed */ #cmakedefine HAVE_VORBIS 1 /* Define if you have FLAC++ installed */ #cmakedefine HAVE_FLAC 1 #cmakedefine HAVE_NO_FLAC_STREAMMETADATA_OPERATOR #cmakedefine HAVE_FLAC_PICTURE 1 #endif kid3-3.0.2/src/plugins/oggflacmetadata/oggflacmetadataplugin.cpp000066400000000000000000000077461224603511300247620ustar00rootroot00000000000000/** * \file oggflacmetadataplugin.cpp * Ogg/Vorbis & FLAC metadata plugin. * * \b Project: Kid3 * \author Urs Fleisch * \date 27 Jul 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "oggflacmetadataplugin.h" #include "oggfile.hpp" #include "flacfile.hpp" #include "tagconfig.h" #if QT_VERSION < 0x050000 Q_EXPORT_PLUGIN2(OggFlacMetadataPlugin, OggFlacMetadataPlugin) #endif #ifdef HAVE_VORBIS static const QLatin1String OGG_KEY("OggMetadata"); #endif #ifdef HAVE_FLAC static const QLatin1String FLAC_KEY("FlacMetadata"); #endif /*! * Constructor. * @param parent parent object */ OggFlacMetadataPlugin::OggFlacMetadataPlugin(QObject* parent) : QObject(parent) { setObjectName(QLatin1String("OggFlacMetadata")); } /** * Destructor. */ OggFlacMetadataPlugin::~OggFlacMetadataPlugin() { } /** * Get name of factory, the same as the QObject::objectName() of the plugin. * @return factory name. */ QString OggFlacMetadataPlugin::name() const { return objectName(); } /** * Get keys of available tagged file formats. * @return list of keys. */ QStringList OggFlacMetadataPlugin::taggedFileKeys() const { return QStringList() #ifdef HAVE_VORBIS << OGG_KEY #endif #ifdef HAVE_FLAC << FLAC_KEY #endif ; } /** * Get features supported. * @param key tagged file key * @return bit mask with TaggedFile::Feature flags set. */ int OggFlacMetadataPlugin::taggedFileFeatures(const QString& key) const { #ifdef HAVE_VORBIS if (key == OGG_KEY) { return TaggedFile::TF_OggPictures; } #else Q_UNUSED(key) #endif return 0; } /** * Initialize tagged file factory. * * @param key tagged file key */ void OggFlacMetadataPlugin::initialize(const QString& key) { Q_UNUSED(key) } /** * Create a tagged file. * * @param key tagged file key * @param dirName directory name * @param fileName filename * @param idx model index * @param features optional tagged file features (TaggedFile::Feature flags) * to activate at creation * * @return tagged file, 0 if type not supported. */ TaggedFile* OggFlacMetadataPlugin::createTaggedFile( const QString& key, const QString& dirName, const QString& fileName, const QPersistentModelIndex& idx, int features) { Q_UNUSED(features) #ifdef HAVE_VORBIS if (key == OGG_KEY) { QString ext = fileName.right(4).toLower(); if (ext == QLatin1String(".oga") || ext == QLatin1String(".ogg")) return new OggFile(dirName, fileName, idx); } #endif #ifdef HAVE_FLAC if (key == FLAC_KEY) { if (fileName.right(5).toLower() == QLatin1String(".flac")) return new FlacFile(dirName, fileName, idx); } #endif return 0; } /** * Get a list with all extensions (e.g. ".mp3") supported by TaggedFile subclass. * * @param key tagged file key * * @return list of file extensions. */ QStringList OggFlacMetadataPlugin::supportedFileExtensions(const QString& key) const { #ifdef HAVE_VORBIS if (key == OGG_KEY) { return QStringList() << QLatin1String(".oga") << QLatin1String(".ogg"); } #endif #ifdef HAVE_FLAC if (key == FLAC_KEY) { return QStringList() << QLatin1String(".flac"); } #endif return QStringList(); } /** * Notify about configuration change. * This method shall be called when the configuration changes. * * @param key tagged file key */ void OggFlacMetadataPlugin::notifyConfigurationChange(const QString& key) { Q_UNUSED(key) } kid3-3.0.2/src/plugins/oggflacmetadata/oggflacmetadataplugin.h000066400000000000000000000057561224603511300244260ustar00rootroot00000000000000/** * \file oggflacmetadataplugin.h * Ogg/Vorbis & FLAC metadata plugin. * * \b Project: Kid3 * \author Urs Fleisch * \date 27 Jul 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef OGGFLACMETADATAPLUGIN_H #define OGGFLACMETADATAPLUGIN_H #include #include "itaggedfilefactory.h" /** * Ogg/Vorbis & FLAC metadata plugin. */ class KID3_PLUGIN_EXPORT OggFlacMetadataPlugin : public QObject, public ITaggedFileFactory { Q_OBJECT #if QT_VERSION >= 0x050000 Q_PLUGIN_METADATA(IID "net.sourceforge.kid3.ITaggedFileFactory") #endif Q_INTERFACES(ITaggedFileFactory) public: /*! * Constructor. * @param parent parent object */ explicit OggFlacMetadataPlugin(QObject* parent = 0); /** * Destructor. */ virtual ~OggFlacMetadataPlugin(); /** * Get name of factory, the same as the QObject::objectName() of the plugin. * @return factory name. */ virtual QString name() const; /** * Get keys of available tagged file formats. * @return list of keys. */ virtual QStringList taggedFileKeys() const; /** * Get features supported. * @param key tagged file key * @return bit mask with TaggedFile::Feature flags set. */ virtual int taggedFileFeatures(const QString& key) const; /** * Initialize tagged file factory. * * @param key tagged file key */ virtual void initialize(const QString& key); /** * Create a tagged file. * * @param dirName directory name * @param fileName filename * @param idx model index * @param features optional tagged file features (TaggedFile::Feature flags) * to activate at creation * * @return tagged file, 0 if type not supported. */ virtual TaggedFile* createTaggedFile( const QString& key, const QString& dirName, const QString& fileName, const QPersistentModelIndex& idx, int features = 0); /** * Get a list with all extensions (e.g. ".mp3") supported by TaggedFile subclass. * * @param key tagged file key * * @return list of file extensions. */ virtual QStringList supportedFileExtensions(const QString& key) const; /** * Notify about configuration change. * This method shall be called when the configuration changes. * * @param key tagged file key */ virtual void notifyConfigurationChange(const QString& key); }; #endif // OGGFLACMETADATAPLUGIN_H kid3-3.0.2/src/plugins/oggflacmetadata/vcedit.c000066400000000000000000000514211224603511300213430ustar00rootroot00000000000000/* This program is licensed under the GNU Library General Public License, version 2, * a copy of which is included with this program (LICENCE.LGPL). * * (c) 2000-2001 Michael Smith * * * Comment editing backend, suitable for use by nice frontend interfaces. * * last modified: $Id$ */ /* Handle muxed streams and the Vorbis renormalization without having * to understand remuxing: * Linked list of buffers (buffer_chain). Start a link and whenever * you encounter an unknown page from the current stream (ie we found * its bos in the bos section) push it onto the current buffer. Whenever * you encounter the stream being renormalized create a new link in the * chain. * On writing, write the contents of the first link before every Vorbis * page written, and move to the next link. Assuming the Vorbis pages * in match vorbis pages out, the order of pages from different logical * streams will be unchanged. * Special case: header. After writing the vorbis headers, and before * starting renormalization, flush accumulated links (takes care of * situations where number of secondary vorbis header pages changes due * to remuxing. Similarly flush links at the end of renormalization * and before the start of the next chain is written. * */ /* This file is taken from vorbis-tools SVN * (http://svn.xiph.org/trunk/vorbis-tools/vorbiscomment) Rev. 16716 2009-11-22, * modifications are marked with kid3. */ /* kid3 */ #include "oggflacconfig.h" #ifdef HAVE_VORBIS #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include "vcedit.h" /* kid3 */ /*#include "vceditaux.h"*/ typedef struct vcedit_page_buffer { char *data; size_t data_len; } vcedit_page_buffer; typedef struct vcedit_buffer_chain { struct vcedit_buffer_chain *next; struct vcedit_page_buffer buffer; } vcedit_buffer_chain; /* kid3 */ /*#include "i18n.h"*/ #define _(str) ((char*)(str)) #define CHUNKSIZE 4096 #define BUFFERCHUNK CHUNKSIZE /* Helper function, shouldn't need to call directly */ static int page_buffer_push(vcedit_buffer_chain *bufferlink, ogg_page *og) { int result=0; char *tmp; vcedit_page_buffer *buffer; buffer = &bufferlink->buffer; tmp = realloc(buffer->data, buffer->data_len + og->header_len + og->body_len); if(tmp) { buffer->data = tmp; memcpy(buffer->data + buffer->data_len, og->header, og->header_len); buffer->data_len += og->header_len; memcpy(buffer->data + buffer->data_len, og->body, og->body_len); result = 1; buffer->data_len += og->body_len; } else { result = -1; } return result; } /* Write and free the first link using callbacks */ static int buffer_chain_writelink(vcedit_state *state, void *out) { int result = 0; vcedit_buffer_chain *tmpchain; vcedit_page_buffer *tmpbuffer; tmpchain = state->sidebuf; tmpbuffer = &tmpchain->buffer; if(tmpbuffer->data_len) { if(state->write(tmpbuffer->data,1,tmpbuffer->data_len, out) != (size_t) tmpbuffer->data_len) result = -1; else result = 1; } free(tmpbuffer->data); state->sidebuf = tmpchain->next; free(tmpchain); return result; } static int buffer_chain_newlink(vcedit_state *state) { int result = 1; vcedit_buffer_chain *bufferlink; if(!state->sidebuf) { state->sidebuf = malloc (sizeof *state->sidebuf); if(state->sidebuf) { bufferlink = state->sidebuf; } else { result = -1; } } else { bufferlink=state->sidebuf; while(bufferlink->next) { bufferlink = bufferlink->next; } bufferlink->next = malloc (sizeof *bufferlink->next); if(bufferlink->next) { bufferlink = bufferlink->next; } else { result = -1; } } if(result > 0 ) { bufferlink->next = 0; bufferlink->buffer.data = 0; bufferlink->buffer.data_len = 0; } else state->lasterror = _("Couldn't get enough memory for input buffering."); return result; } /* Push page onto the end of the buffer chain */ static int buffer_chain_push(vcedit_state *state, ogg_page *og) { /* If there is no sidebuffer yet we need to create one, otherwise * traverse to the last buffer and push the new page onto it. */ int result=1; vcedit_buffer_chain *bufferlink; if(!state->sidebuf) { result = buffer_chain_newlink(state); } if(result > 0) { bufferlink = state->sidebuf; while(bufferlink->next) { bufferlink = bufferlink->next; } result = page_buffer_push(bufferlink, og); } if(result < 0) state->lasterror = _("Couldn't get enough memory for input buffering."); return result; } static int vcedit_supported_stream(vcedit_state *state, ogg_page *og) { ogg_stream_state os; vorbis_info vi; vorbis_comment vc; ogg_packet header; int result = 0; ogg_stream_init(&os, ogg_page_serialno(og)); vorbis_info_init(&vi); vorbis_comment_init(&vc); if( !ogg_page_bos(og) ) result = -1; if(result >= 0 && ogg_stream_pagein(&os, og) < 0) { state->lasterror = _("Error reading first page of Ogg bitstream."); result = -1; } if(result >= 0 && ogg_stream_packetout(&os, &header) != 1) { state->lasterror = _("Error reading initial header packet."); result = -1; } if(result >= 0 && vorbis_synthesis_headerin(&vi, &vc, &header) >= 0) { result = 1; } else { /* Not vorbis, may eventually become a chain of checks (Speex, * Theora), but for the moment return 0, bos scan will push * the current page onto the buffer. */ } ogg_stream_clear(&os); vorbis_info_clear(&vi); vorbis_comment_clear(&vc); return result; } static int vcedit_contains_serial (vcedit_state *state, int serialno) { int result = 0; size_t count; for( count=0; count < state->serials.streams_len; count++ ) { if ( *(state->serials.streams + count ) == serialno ) result = 1; } return result; } static int vcedit_add_serial (vcedit_state *state, long serial) { int result = 0; long *tmp; if( vcedit_contains_serial(state, serial) ) { result = 1; } else { tmp = realloc(state->serials.streams, (state->serials.streams_len + 1) * sizeof *tmp); if(tmp) { state->serials.streams = tmp; *(state->serials.streams + state->serials.streams_len) = serial; state->serials.streams_len += 1; result = 1; } else { state->lasterror = _("Couldn't get enough memory to register new stream serial number."); result = -1; } } return result; } /* For the benefit of the secondary header read only. Quietly creates * newlinks and pushes pages onto the buffer in the right way */ static int vcedit_target_pageout (vcedit_state *state, ogg_page *og) { int result = 0; int pageout_result; pageout_result = ogg_sync_pageout(state->oy, og); if(pageout_result > 0) { if(state->serial == ogg_page_serialno(og)) result = buffer_chain_newlink(state); else result = buffer_chain_push(state, og); } else if (pageout_result < 0) { /* Vorbis comment traditionally ignores the not-synced * error from pageout, so give it a different code. */ result = -2; } return result; } /* (I'm paranoid about memset(x,0,len) not giving null pointers */ vcedit_state *vcedit_new_state(void) { vcedit_state *state = malloc(sizeof(vcedit_state)); if(state) { memset(state, 0, sizeof(vcedit_state)); state->sidebuf = 0; state->serials.streams = 0; state->serials.streams_len = 0; } return state; } char *vcedit_error(vcedit_state *state) { return state->lasterror; } vorbis_comment *vcedit_comments(vcedit_state *state) { return state->vc; } static void vcedit_clear_internals(vcedit_state *state) { char *tmp; if(state->vc) { vorbis_comment_clear(state->vc); free(state->vc); } if(state->os) { ogg_stream_clear(state->os); free(state->os); } if(state->oy) { ogg_sync_clear(state->oy); free(state->oy); } if(state->serials.streams_len) { free(state->serials.streams); state->serials.streams_len = 0; state->serials.streams = 0; } while(state->sidebuf) { vcedit_buffer_chain *tmpbuffer; tmpbuffer = state->sidebuf; state->sidebuf = tmpbuffer->next; free(tmpbuffer->buffer.data); free(tmpbuffer); } if(state->vendor) free(state->vendor); if(state->mainbuf) free(state->mainbuf); if(state->bookbuf) free(state->bookbuf); if(state->vi) { vorbis_info_clear(state->vi); free(state->vi); } tmp = state->lasterror; memset(state, 0, sizeof(*state)); state->lasterror = tmp; } void vcedit_clear(vcedit_state *state) { if(state) { vcedit_clear_internals(state); free(state); } } /* Next two functions pulled straight from libvorbis, apart from one change * - we don't want to overwrite the vendor string. */ /* kid3: changed type from char* to const char* */ static void _v_writestring(oggpack_buffer *o, const char *s, int len) { while(len--) { oggpack_write(o,*s++,8); } } static int _commentheader_out(vorbis_comment *vc, char *vendor, ogg_packet *op) { oggpack_buffer opb; oggpack_writeinit(&opb); /* preamble */ oggpack_write(&opb,0x03,8); _v_writestring(&opb,"vorbis", 6); /* vendor */ oggpack_write(&opb,strlen(vendor),32); _v_writestring(&opb,vendor, strlen(vendor)); /* comments */ oggpack_write(&opb,vc->comments,32); if(vc->comments){ int i; for(i=0;icomments;i++){ if(vc->user_comments[i]){ oggpack_write(&opb,vc->comment_lengths[i],32); _v_writestring(&opb,vc->user_comments[i], vc->comment_lengths[i]); }else{ oggpack_write(&opb,0,32); } } } oggpack_write(&opb,1,1); op->packet = malloc(oggpack_bytes(&opb)); memcpy(op->packet, opb.buffer, oggpack_bytes(&opb)); op->bytes=oggpack_bytes(&opb); op->b_o_s=0; op->e_o_s=0; op->granulepos=0; oggpack_writeclear(&opb); return 0; } static int _blocksize(vcedit_state *s, ogg_packet *p) { int this = vorbis_packet_blocksize(s->vi, p); int ret = (this + s->prevW)/4; if(!s->prevW) { s->prevW = this; return 0; } s->prevW = this; return ret; } static int _fetch_next_packet(vcedit_state *s, ogg_packet *p, ogg_page *page) { int result; char *buffer; int bytes; int serialno; result = ogg_stream_packetout(s->os, p); if(result > 0) return 1; else { while(1) { if(s->eosin) return 0; while(ogg_sync_pageout(s->oy, page) <= 0) { buffer = ogg_sync_buffer(s->oy, CHUNKSIZE); bytes = s->read(buffer,1, CHUNKSIZE, s->in); ogg_sync_wrote(s->oy, bytes); if(bytes == 0) return 0; } serialno = ogg_page_serialno(page); if(ogg_page_serialno(page) != s->serial) { if(vcedit_contains_serial(s, serialno)) { result = buffer_chain_push(s, page); if(result < 0) return result; } else { s->eosin = 1; s->extrapage = 1; return 0; } } else { ogg_stream_pagein(s->os, page); result = buffer_chain_newlink(s); if (result < 0) return result; if(ogg_page_eos(page)) s->eosin = 1; } result = ogg_stream_packetout(s->os, p); if(result > 0) return 1; } /* Here == trouble */ return 0; } } /* kid3 vcedit_open() removed */ int vcedit_open_callbacks(vcedit_state *state, void *in, vcedit_read_func read_func, vcedit_write_func write_func) { char *buffer; int bytes,i; int chunks = 0; int read_bos, test_supported, page_pending; int have_vorbis; size_t vendor_size; ogg_packet *header; ogg_packet header_main; ogg_packet header_comments; ogg_packet header_codebooks; ogg_page og; state->in = in; state->read = read_func; state->write = write_func; state->oy = malloc(sizeof(ogg_sync_state)); ogg_sync_init(state->oy); while(1) { buffer = ogg_sync_buffer(state->oy, CHUNKSIZE); bytes = state->read(buffer, 1, CHUNKSIZE, state->in); ogg_sync_wrote(state->oy, bytes); if(ogg_sync_pageout(state->oy, &og) == 1) break; if(chunks++ >= 10) /* Bail if we don't find data in the first 40 kB */ { if(byteslasterror = _("Input truncated or empty."); else state->lasterror = _("Input is not an Ogg bitstream."); goto err; } } /* BOS loop, starting with a loaded ogg page. */ if(buffer_chain_newlink(state) < 0) goto err; for( read_bos = 1, have_vorbis = 0 ; read_bos; ) { test_supported = vcedit_supported_stream(state, &og); if(test_supported < 0) { goto err; } else if (test_supported == 0 || have_vorbis ) { if(vcedit_add_serial ( state, ogg_page_serialno(&og)) < 0) goto err; if( buffer_chain_push(state, &og) < 0) goto err; } else if (test_supported > 0) { if(buffer_chain_newlink(state) < 0) goto err; state->serial = ogg_page_serialno(&og); if(vcedit_add_serial ( state, ogg_page_serialno(&og)) < 0) goto err; state->os = malloc(sizeof(ogg_stream_state)); ogg_stream_init(state->os, state->serial); state->vi = malloc(sizeof(vorbis_info)); vorbis_info_init(state->vi); state->vc = malloc(sizeof(vorbis_comment)); vorbis_comment_init(state->vc); if(ogg_stream_pagein(state->os, &og) < 0) { state->lasterror = _("Error reading first page of Ogg bitstream."); goto err; } if(ogg_stream_packetout(state->os, &header_main) != 1) { state->lasterror = _("Error reading initial header packet."); goto err; } if(vorbis_synthesis_headerin(state->vi, state->vc, &header_main) < 0) { state->lasterror = _("Ogg bitstream does not contain Vorbis data."); goto err; } have_vorbis = 1; } while(1) { buffer = ogg_sync_buffer(state->oy, CHUNKSIZE); bytes = state->read(buffer, 1, CHUNKSIZE, state->in); if(bytes == 0) { state->lasterror = _("EOF before recognised stream."); goto err; } ogg_sync_wrote(state->oy, bytes); if(ogg_sync_pageout(state->oy, &og) == 1) break; } if(!ogg_page_bos(&og)) { read_bos = 0; page_pending = 1; } } if(!state->os) { state->lasterror = _("Ogg bitstream does not contain a supported data-type."); goto err; } state->mainlen = header_main.bytes; state->mainbuf = malloc(state->mainlen); memcpy(state->mainbuf, header_main.packet, header_main.bytes); if(ogg_page_serialno(&og) == state->serial) { if(buffer_chain_newlink(state) < 0) goto err; } else { if(buffer_chain_push(state, &og) < 0) goto err; page_pending = 0; } i = 0; header = &header_comments; while(i<2) { while(i<2) { int result; if(!page_pending) result = vcedit_target_pageout(state, &og); else { result = 1; page_pending = 0; } if(result == 0 || result == -2) break; /* Too little data so far */ else if(result == -1) goto err; else if(result == 1) { ogg_stream_pagein(state->os, &og); while(i<2) { result = ogg_stream_packetout(state->os, header); if(result == 0) break; if(result == -1) { state->lasterror = _("Corrupt secondary header."); goto err; } vorbis_synthesis_headerin(state->vi, state->vc, header); if(i==1) { state->booklen = header->bytes; state->bookbuf = malloc(state->booklen); memcpy(state->bookbuf, header->packet, header->bytes); } i++; header = &header_codebooks; } } } buffer = ogg_sync_buffer(state->oy, CHUNKSIZE); bytes = state->read(buffer, 1, CHUNKSIZE, state->in); if(bytes == 0 && i < 2) { state->lasterror = _("EOF before end of Vorbis headers."); goto err; } ogg_sync_wrote(state->oy, bytes); } /* Copy the vendor tag */ /* kid3 */ vendor_size = strlen(state->vc->vendor) +1; state->vendor = malloc(vendor_size); memcpy(state->vendor, state->vc->vendor, vendor_size); /* Headers are done! */ return 0; err: vcedit_clear_internals(state); return -1; } int vcedit_write(vcedit_state *state, void *out) { ogg_stream_state streamout; ogg_packet header_main; ogg_packet header_comments; ogg_packet header_codebooks; ogg_page ogout, ogin; ogg_packet op; ogg_int64_t granpos = 0; int result; char *buffer; int bytes; int needflush=0, needout=0; state->eosin = 0; state->extrapage = 0; header_main.bytes = state->mainlen; header_main.packet = state->mainbuf; header_main.b_o_s = 1; header_main.e_o_s = 0; header_main.granulepos = 0; header_codebooks.bytes = state->booklen; header_codebooks.packet = state->bookbuf; header_codebooks.b_o_s = 0; header_codebooks.e_o_s = 0; header_codebooks.granulepos = 0; ogg_stream_init(&streamout, state->serial); _commentheader_out(state->vc, state->vendor, &header_comments); ogg_stream_packetin(&streamout, &header_main); ogg_stream_packetin(&streamout, &header_comments); ogg_stream_packetin(&streamout, &header_codebooks); while((result = ogg_stream_flush(&streamout, &ogout))) { if(state->sidebuf && buffer_chain_writelink(state, out) < 0) goto cleanup; if(state->write(ogout.header,1,ogout.header_len, out) != (size_t) ogout.header_len) goto cleanup; if(state->write(ogout.body,1,ogout.body_len, out) != (size_t) ogout.body_len) goto cleanup; } while(state->sidebuf) { if(buffer_chain_writelink(state, out) < 0) goto cleanup; } if(buffer_chain_newlink(state) < 0) goto cleanup; while(_fetch_next_packet(state, &op, &ogin)) { int size; size = _blocksize(state, &op); granpos += size; if(needflush) { if(ogg_stream_flush(&streamout, &ogout)) { if(state->sidebuf && buffer_chain_writelink(state, out) < 0) goto cleanup; if(state->write(ogout.header,1,ogout.header_len, out) != (size_t) ogout.header_len) goto cleanup; if(state->write(ogout.body,1,ogout.body_len, out) != (size_t) ogout.body_len) goto cleanup; } } else if(needout) { if(ogg_stream_pageout(&streamout, &ogout)) { if(state->sidebuf && buffer_chain_writelink(state, out) < 0) goto cleanup; if(state->write(ogout.header,1,ogout.header_len, out) != (size_t) ogout.header_len) goto cleanup; if(state->write(ogout.body,1,ogout.body_len, out) != (size_t) ogout.body_len) goto cleanup; } } needflush=needout=0; if(op.granulepos == -1) { op.granulepos = granpos; ogg_stream_packetin(&streamout, &op); } else /* granulepos is set, validly. Use it, and force a flush to account for shortened blocks (vcut) when appropriate */ { if(granpos > op.granulepos) { granpos = op.granulepos; ogg_stream_packetin(&streamout, &op); needflush=1; } else { ogg_stream_packetin(&streamout, &op); needout=1; } } } streamout.e_o_s = 1; while(ogg_stream_flush(&streamout, &ogout)) { if(state->sidebuf && buffer_chain_writelink(state, out) < 0) goto cleanup; if(state->write(ogout.header,1,ogout.header_len, out) != (size_t) ogout.header_len) goto cleanup; if(state->write(ogout.body,1,ogout.body_len, out) != (size_t) ogout.body_len) goto cleanup; } if (state->extrapage) { /* This is the first page of a new chain, get rid of the * sidebuffer */ while(state->sidebuf) if(buffer_chain_writelink(state, out) < 0) goto cleanup; if(state->write(ogin.header,1,ogin.header_len, out) != (size_t) ogin.header_len) goto cleanup; if (state->write(ogin.body,1,ogin.body_len, out) != (size_t) ogin.body_len) goto cleanup; } state->eosin=0; /* clear it, because not all paths to here do */ while(!state->eosin) /* We reached eos, not eof */ { /* We copy the rest of the stream (other logical streams) * through, a page at a time. */ while(1) { result = ogg_sync_pageout(state->oy, &ogout); if(result==0) break; if(result<0) state->lasterror = _("Corrupt or missing data, continuing..."); else { /* Don't bother going through the rest, we can just * write the page out now */ if(state->write(ogout.header,1,ogout.header_len, out) != (size_t) ogout.header_len) { goto cleanup; } if(state->write(ogout.body,1,ogout.body_len, out) != (size_t) ogout.body_len) { goto cleanup; } } } buffer = ogg_sync_buffer(state->oy, CHUNKSIZE); bytes = state->read(buffer,1, CHUNKSIZE, state->in); ogg_sync_wrote(state->oy, bytes); if(bytes == 0) { state->eosin = 1; break; } } cleanup: ogg_stream_clear(&streamout); /* We don't ogg_packet_clear() this, because the memory was allocated in _commentheader_out(), so we mirror that here */ _ogg_free(header_comments.packet); free(state->mainbuf); free(state->bookbuf); state->mainbuf = state->bookbuf = NULL; if(!state->eosin) { state->lasterror = _("Error writing stream to output. " "Output stream may be corrupted or truncated."); return -1; } return 0; } /* kid3 */ #endif /* HAVE_VORBIS */ kid3-3.0.2/src/plugins/oggflacmetadata/vcedit.h000066400000000000000000000030471224603511300213510ustar00rootroot00000000000000/* This program is licensed under the GNU Library General Public License, version 2, * a copy of which is included with this program (with filename LICENSE.LGPL). * * (c) 2000-2001 Michael Smith * * VCEdit header. * * last modified: $ID:$ */ #ifndef __VCEDIT_H #define __VCEDIT_H #ifdef __cplusplus extern "C" { #endif #include #include #include typedef size_t (*vcedit_read_func)(void *, size_t, size_t, void *); typedef size_t (*vcedit_write_func)(const void *, size_t, size_t, void *); typedef struct { long *streams; size_t streams_len; } vcedit_serial_nos; typedef struct { ogg_sync_state *oy; ogg_stream_state *os; vorbis_comment *vc; vorbis_info *vi; vcedit_read_func read; vcedit_write_func write; void *in; int serial; vcedit_serial_nos serials; unsigned char *mainbuf; unsigned char *bookbuf; int mainlen; int booklen; char *lasterror; char *vendor; int prevW; int extrapage; int eosin; struct vcedit_buffer_chain *sidebuf; } vcedit_state; extern vcedit_state * vcedit_new_state(void); extern void vcedit_clear(vcedit_state *state); extern vorbis_comment * vcedit_comments(vcedit_state *state); /* kid3 vcedit_open() removed */ extern int vcedit_open_callbacks(vcedit_state *state, void *in, vcedit_read_func read_func, vcedit_write_func write_func); extern int vcedit_write(vcedit_state *state, void *out); extern char * vcedit_error(vcedit_state *state); #ifdef __cplusplus } #endif #endif /* __VCEDIT_H */ kid3-3.0.2/src/plugins/taglibmetadata/000077500000000000000000000000001224603511300175565ustar00rootroot00000000000000kid3-3.0.2/src/plugins/taglibmetadata/CMakeLists.txt000066400000000000000000000077021224603511300223240ustar00rootroot00000000000000set(TAGLIB_LIBRARIES) set(TAGLIB_CFLAGS) if(WITH_TAGLIB) if (WIN32) find_library(TAGLIB_LIBRARIES NAMES tag) find_path(TAGLIB_INCLUDE_DIR taglib/taglib.h) if (TAGLIB_LIBRARIES AND TAGLIB_INCLUDE_DIR) set(TAGLIB_CFLAGS -I${TAGLIB_INCLUDE_DIR}/taglib -I${TAGLIB_INCLUDE_DIR} -DTAGLIB_STATIC) set(HAVE_TAGLIB 1) message(STATUS "TagLib found: ${TAGLIB_LIBRARIES}") set(TAGLIB_LIBRARIES ${TAGLIB_LIBRARIES} ${ZLIB_LIBRARIES}) endif (TAGLIB_LIBRARIES AND TAGLIB_INCLUDE_DIR) else (WIN32) find_program(TAGLIBCONFIG_EXECUTABLE NAMES taglib-config PATHS /usr/bin /usr/local/bin ${BIN_INSTALL_DIR}) if(TAGLIBCONFIG_EXECUTABLE) exec_program(${TAGLIBCONFIG_EXECUTABLE} ARGS --libs RETURN_VALUE _return_VALUE OUTPUT_VARIABLE TAGLIB_LIBRARIES) exec_program(${TAGLIBCONFIG_EXECUTABLE} ARGS --cflags RETURN_VALUE _return_VALUE OUTPUT_VARIABLE TAGLIB_CFLAGS) exec_program(${TAGLIBCONFIG_EXECUTABLE} ARGS --version RETURN_VALUE _return_VALUE OUTPUT_VARIABLE TAGLIB_VERSION) if(TAGLIB_LIBRARIES AND TAGLIB_CFLAGS AND TAGLIB_VERSION) if(NOT ${TAGLIB_VERSION} VERSION_LESS 1.4) set(HAVE_TAGLIB 1) message(STATUS "TagLib found: ${TAGLIB_LIBRARIES}") set(TAGLIB_LIBRARIES ${TAGLIB_LIBRARIES} ${ZLIB_LIBRARIES}) endif(NOT ${TAGLIB_VERSION} VERSION_LESS 1.4) endif(TAGLIB_LIBRARIES AND TAGLIB_CFLAGS AND TAGLIB_VERSION) endif(TAGLIBCONFIG_EXECUTABLE) endif (WIN32) if (NOT HAVE_TAGLIB) message(FATAL_ERROR "Could not find Taglib") endif (NOT HAVE_TAGLIB) endif(WITH_TAGLIB) if (TAGLIB_LIBRARIES AND TAGLIB_CFLAGS) set(_CMAKE_REQUIRED_LIBRARIES_TMP ${CMAKE_REQUIRED_LIBRARIES}) set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} ${TAGLIB_LIBRARIES}) set(_CMAKE_REQUIRED_DEFINITIONS_TMP ${CMAKE_REQUIRED_DEFINITIONS}) set(CMAKE_REQUIRED_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} ${TAGLIB_CFLAGS}) CHECK_CXX_SOURCE_COMPILES("#include \nint main() {\n TagLib::MPEG::File file(\"somefile.mp3\");\n file.save(3, false, 3);\n return 0;\n}\n" HAVE_TAGLIB_ID3V23_SUPPORT) CHECK_CXX_SOURCE_COMPILES("#include \n#include \nint main() {\n TagLib::MPEG::File file(\"somefile.mp3\");\n return dynamic_cast(file.audioProperties()) != 0;\n}\n" HAVE_TAGLIB_XM_SUPPORT) set(CMAKE_REQUIRED_LIBRARIES ${_CMAKE_REQUIRED_LIBRARIES_TMP}) set(CMAKE_REQUIRED_DEFINITIONS ${_CMAKE_REQUIRED_DEFINITIONS_TMP}) configure_file(taglibconfig.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/taglibconfig.h) add_definitions(${TAGLIB_CFLAGS}) include_directories(${CMAKE_CURRENT_BINARY_DIR} ../../core/tags) set(plugin_SRCS taglibmetadataplugin.cpp taglibfile.cpp taglibext/aac/aacfiletyperesolver.cpp taglibext/mp2/mp2filetyperesolver.cpp ) if (TAGLIB_VERSION STREQUAL "1.4") set(plugin_SRCS ${plugin_SRCS} taglibext/urllinkframe.cpp taglibext/unsynchronizedlyricsframe.cpp taglibext/generalencapsulatedobjectframe.cpp taglibext/speex/speexfile.cpp taglibext/speex/speexproperties.cpp taglibext/speex/taglib_speexfiletyperesolver.cpp taglibext/trueaudio/taglib_trueaudiofiletyperesolver.cpp taglibext/trueaudio/ttafile.cpp taglibext/trueaudio/ttaproperties.cpp taglibext/wavpack/taglib_wavpackfiletyperesolver.cpp taglibext/wavpack/wvfile.cpp taglibext/wavpack/wvproperties.cpp ) endif (TAGLIB_VERSION STREQUAL "1.4") set(plugin_MOC_HDRS taglibmetadataplugin.h ) set(plugin_NAME TaglibMetadata) if (WITH_GCC_PCH) add_definitions(${GCC_PCH_COMPILE_FLAGS}) endif (WITH_GCC_PCH) string(TOLOWER ${plugin_NAME} plugin_TARGET) qt4_wrap_cpp(plugin_GEN_MOC_SRCS ${plugin_MOC_HDRS}) add_library(${plugin_TARGET} ${plugin_SRCS} ${plugin_GEN_MOC_SRCS}) target_link_libraries(${plugin_TARGET} kid3-core ${BASIC_LIBRARIES} ${TAGLIB_LIBRARIES}) INSTALL_KID3_PLUGIN(${plugin_TARGET} ${plugin_NAME}) endif (TAGLIB_LIBRARIES AND TAGLIB_CFLAGS) kid3-3.0.2/src/plugins/taglibmetadata/taglibconfig.h.cmake000066400000000000000000000003651224603511300234420ustar00rootroot00000000000000/* taglibconfig.h. Generated by cmake from taglibconfig.h.cmake */ #ifndef TAGLIBCONFIG_H #define TAGLIBCONFIG_H /* Define if you have TagLib installed */ #cmakedefine HAVE_TAGLIB_ID3V23_SUPPORT 1 #cmakedefine HAVE_TAGLIB_XM_SUPPORT 1 #endif kid3-3.0.2/src/plugins/taglibmetadata/taglibext/000077500000000000000000000000001224603511300215415ustar00rootroot00000000000000kid3-3.0.2/src/plugins/taglibmetadata/taglibext/aac/000077500000000000000000000000001224603511300222655ustar00rootroot00000000000000kid3-3.0.2/src/plugins/taglibmetadata/taglibext/aac/aacfiletyperesolver.cpp000066400000000000000000000056321224603511300270470ustar00rootroot00000000000000/*************************************************************************** copyright : (C) 2006 by Martin Aumueller email : aumuell@reserv.at ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * * MA 02110-1301 USA * ***************************************************************************/ #if defined __GNUC__ && (__GNUC__ * 100 + __GNUC_MINOR__) >= 407 /** Defined if GCC is used and supports diagnostic pragmas */ #define GCC_HAS_DIAGNOSTIC_PRAGMA #endif #ifdef GCC_HAS_DIAGNOSTIC_PRAGMA #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wnon-virtual-dtor" #endif #include "aacfiletyperesolver.h" #include #include #if (((TAGLIB_MAJOR_VERSION) << 16) + ((TAGLIB_MINOR_VERSION) << 8) + (TAGLIB_PATCH_VERSION)) > 0x010400 && defined _WIN32 TagLib::File *AACFileTypeResolver::createFile(TagLib::FileName fileName, bool readProperties, TagLib::AudioProperties::ReadStyle propertiesStyle) const { const wchar_t* wstr = static_cast(fileName); const char* str = static_cast(fileName); const wchar_t* wext; const char* ext; if ((wstr && (wext = wcsrchr(fileName, L'.')) != 0 && !wcsicmp(wext, L".aac")) || (str && (ext = strrchr(fileName, '.')) != 0 && !stricmp(ext, ".aac"))) { return new TagLib::MPEG::File(fileName, readProperties, propertiesStyle); } return 0; } #else TagLib::File *AACFileTypeResolver::createFile(const char *fileName, bool readProperties, TagLib::AudioProperties::ReadStyle propertiesStyle) const { const char *ext = strrchr(fileName, '.'); if(ext && !strcasecmp(ext, ".aac")) { return new TagLib::MPEG::File(fileName, readProperties, propertiesStyle); } return 0; } #endif #ifdef GCC_HAS_DIAGNOSTIC_PRAGMA #pragma GCC diagnostic pop #endif kid3-3.0.2/src/plugins/taglibmetadata/taglibext/aac/aacfiletyperesolver.h000066400000000000000000000043361224603511300265140ustar00rootroot00000000000000/*************************************************************************** copyright : (C) 2006 by Martin Aumueller email : aumuell@reserv.at ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * * MA 02110-1301 USA * ***************************************************************************/ #ifndef TAGLIB_AACFILETYPERESOLVER_H #define TAGLIB_AACFILETYPERESOLVER_H #include #include #if (((TAGLIB_MAJOR_VERSION) << 16) + ((TAGLIB_MINOR_VERSION) << 8) + (TAGLIB_PATCH_VERSION)) > 0x010400 && defined _WIN32 class AACFileTypeResolver : public TagLib::FileRef::FileTypeResolver { public: virtual TagLib::File *createFile(TagLib::FileName fileName, bool readAudioProperties, TagLib::AudioProperties::ReadStyle audioPropertiesStyle) const; virtual ~AACFileTypeResolver() {} }; #else class AACFileTypeResolver : public TagLib::FileRef::FileTypeResolver { public: virtual TagLib::File *createFile(const char *fileName, bool readAudioProperties, TagLib::AudioProperties::ReadStyle audioPropertiesStyle) const; virtual ~AACFileTypeResolver() {} }; #endif #endif kid3-3.0.2/src/plugins/taglibmetadata/taglibext/generalencapsulatedobjectframe.cpp000066400000000000000000000142671224603511300304670ustar00rootroot00000000000000/*************************************************************************** copyright : (C) 2004 by Scott Wheeler email : wheeler@kde.org copyright : (C) 2006 by Aaron VonderHaar email : avh4@users.sourceforge.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * * USA * ***************************************************************************/ #include #include "generalencapsulatedobjectframe.h" using namespace TagLib; using namespace ID3v2; class GeneralEncapsulatedObjectFrame::GeneralEncapsulatedObjectFramePrivate { public: GeneralEncapsulatedObjectFramePrivate() : textEncoding(String::Latin1) {} String::Type textEncoding; String mimeType; String fileName; String description; ByteVector data; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// GeneralEncapsulatedObjectFrame::GeneralEncapsulatedObjectFrame() : Frame("GEOB") { d = new GeneralEncapsulatedObjectFramePrivate; } GeneralEncapsulatedObjectFrame::GeneralEncapsulatedObjectFrame(const ByteVector &data) : Frame(data) { d = new GeneralEncapsulatedObjectFramePrivate; setData(data); } GeneralEncapsulatedObjectFrame::~GeneralEncapsulatedObjectFrame() { delete d; } String GeneralEncapsulatedObjectFrame::toString() const { String text = "[" + d->mimeType + "]"; if(!d->fileName.isEmpty()) text += " " + d->fileName; if(!d->description.isEmpty()) text += " \"" + d->description + "\""; return text; } String::Type GeneralEncapsulatedObjectFrame::textEncoding() const { return d->textEncoding; } void GeneralEncapsulatedObjectFrame::setTextEncoding(String::Type encoding) { d->textEncoding = encoding; } String GeneralEncapsulatedObjectFrame::mimeType() const { return d->mimeType; } void GeneralEncapsulatedObjectFrame::setMimeType(const String &type) { d->mimeType = type; } String GeneralEncapsulatedObjectFrame::fileName() const { return d->fileName; } void GeneralEncapsulatedObjectFrame::setFileName(const String &name) { d->fileName = name; } String GeneralEncapsulatedObjectFrame::description() const { return d->description; } void GeneralEncapsulatedObjectFrame::setDescription(const String &desc) { d->description = desc; } ByteVector GeneralEncapsulatedObjectFrame::object() const { return d->data; } void GeneralEncapsulatedObjectFrame::setObject(const ByteVector &data) { d->data = data; } //////////////////////////////////////////////////////////////////////////////// // protected members //////////////////////////////////////////////////////////////////////////////// void GeneralEncapsulatedObjectFrame::parseFields(const ByteVector &data) { if(data.size() < 4) { debug("An object frame must contain at least 4 bytes."); return; } int fieldStart = 0; d->textEncoding = String::Type(data[fieldStart]); fieldStart += 1; int fieldEnd = data.find(textDelimiter(String::Latin1), fieldStart); if(fieldEnd < fieldStart) return; d->mimeType = String(data.mid(fieldStart, fieldEnd - fieldStart), String::Latin1); fieldStart = fieldEnd + 1; if (d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8) { fieldEnd = data.find(textDelimiter(d->textEncoding), fieldStart); if(fieldEnd < fieldStart) return; d->fileName = String(data.mid(fieldStart, fieldEnd - fieldStart), d->textEncoding); fieldStart = fieldEnd + 1; fieldEnd = data.find(textDelimiter(d->textEncoding), fieldStart); if(fieldEnd < fieldStart) return; d->description = String(data.mid(fieldStart, fieldEnd - fieldStart), d->textEncoding); fieldStart = fieldEnd + 1; } else { // use byteAlign = 2 to find $00 00 text delimiter // find in subvector so that it works too when pos is odd int len = data.mid(fieldStart).find(textDelimiter(d->textEncoding), 0, 2); if (len < 0) return; d->fileName = String(data.mid(fieldStart, len), d->textEncoding); fieldStart += len + 2; len = data.mid(fieldStart).find(textDelimiter(d->textEncoding), 0, 2); if (len < 0) return; d->description = String(data.mid(fieldStart, len), d->textEncoding); fieldStart += len + 2; } d->data = data.mid(fieldStart); } ByteVector GeneralEncapsulatedObjectFrame::renderFields() const { ByteVector data; data.append(char(d->textEncoding)); data.append(d->mimeType.data(String::Latin1)); data.append(textDelimiter(String::Latin1)); data.append(d->fileName.data(d->textEncoding)); data.append(textDelimiter(d->textEncoding)); data.append(d->description.data(d->textEncoding)); data.append(textDelimiter(d->textEncoding)); data.append(d->data); return data; } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// GeneralEncapsulatedObjectFrame::GeneralEncapsulatedObjectFrame(const ByteVector &data, Header *h) : Frame(h) { d = new GeneralEncapsulatedObjectFramePrivate; parseFields(fieldData(data)); } kid3-3.0.2/src/plugins/taglibmetadata/taglibext/generalencapsulatedobjectframe.h000066400000000000000000000131261224603511300301250ustar00rootroot00000000000000/*************************************************************************** copyright : (C) 2004 by Scott Wheeler email : wheeler@kde.org copyright : (C) 2006 by Aaron VonderHaar email : avh4@users.sourceforge.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * * USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_GENERALENCAPSULATEDOBJECT_H #define TAGLIB_GENERALENCAPSULATEDOBJECT_H #include #include namespace TagLib { namespace ID3v2 { //! An ID3v2 general encapsulated object frame implementation /*! * This is an implementation of ID3v2 general encapsulated objects. * Arbitrary binary data may be included in tags, stored in GEOB frames. * There may be multiple GEOB frames in a single tag. Each GEOB it * labelled with a content description (which may be blank), a required * mime-type, and a file name (may be blank). The content description * uniquely identifies the GEOB frame in the tag. */ class GeneralEncapsulatedObjectFrame : public Frame { friend class FrameFactory; public: /*! * Constructs an empty object frame. The description, file name and text * encoding should be set manually. */ GeneralEncapsulatedObjectFrame(); /*! * Constructs a GeneralEncapsulatedObjectFrame frame based on \a data. */ explicit GeneralEncapsulatedObjectFrame(const ByteVector &data); /*! * Destroys the GeneralEncapsulatedObjectFrame instance. */ virtual ~GeneralEncapsulatedObjectFrame(); /*! * Returns a string containing the description, file name and mime-type */ virtual String toString() const; /*! * Returns the text encoding used for the description and file name. * * \see setTextEncoding() * \see description() * \see fileName() */ String::Type textEncoding() const; /*! * Set the text encoding used for the description and file name. * * \see description() * \see fileName() */ void setTextEncoding(String::Type encoding); /*! * Returns the mime type of the object. */ String mimeType() const; /*! * Sets the mime type of the object. */ void setMimeType(const String &type); /*! * Returns the file name of the object. * * \see setFileName() */ String fileName() const; /*! * Sets the file name for the object. * * \see fileName() */ void setFileName(const String &name); /*! * Returns the content description of the object. * * \see setDescription() * \see textEncoding() * \see setTextEncoding() */ String description() const; /*! * Sets the content description of the object to \a desc. * * \see description() * \see textEncoding() * \see setTextEncoding() */ void setDescription(const String &desc); /*! * Returns the object data as a ByteVector. * * \note ByteVector has a data() method that returns a const char * which * should make it easy to export this data to external programs. * * \see setObject() * \see mimeType() */ ByteVector object() const; /*! * Sets the object data to \a data. \a data should be of the type specified in * this frame's mime-type specification. * * \see object() * \see mimeType() * \see setMimeType() */ void setObject(const ByteVector &object); protected: virtual void parseFields(const ByteVector &data); virtual ByteVector renderFields() const; private: GeneralEncapsulatedObjectFrame(const ByteVector &data, Header *h); GeneralEncapsulatedObjectFrame(const GeneralEncapsulatedObjectFrame &); GeneralEncapsulatedObjectFrame &operator=(const GeneralEncapsulatedObjectFrame &); class GeneralEncapsulatedObjectFramePrivate; GeneralEncapsulatedObjectFramePrivate *d; }; } } #endif kid3-3.0.2/src/plugins/taglibmetadata/taglibext/mp2/000077500000000000000000000000001224603511300222375ustar00rootroot00000000000000kid3-3.0.2/src/plugins/taglibmetadata/taglibext/mp2/mp2filetyperesolver.cpp000066400000000000000000000052301224603511300267650ustar00rootroot00000000000000/*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * * MA 02110-1301 USA * ***************************************************************************/ #if defined __GNUC__ && (__GNUC__ * 100 + __GNUC_MINOR__) >= 407 /** Defined if GCC is used and supports diagnostic pragmas */ #define GCC_HAS_DIAGNOSTIC_PRAGMA #endif #ifdef GCC_HAS_DIAGNOSTIC_PRAGMA #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wnon-virtual-dtor" #endif #include "mp2filetyperesolver.h" #include #include #if (((TAGLIB_MAJOR_VERSION) << 16) + ((TAGLIB_MINOR_VERSION) << 8) + (TAGLIB_PATCH_VERSION)) > 0x010400 && defined _WIN32 TagLib::File *MP2FileTypeResolver::createFile(TagLib::FileName fileName, bool readProperties, TagLib::AudioProperties::ReadStyle propertiesStyle) const { const wchar_t* wstr = static_cast(fileName); const char* str = static_cast(fileName); const wchar_t* wext; const char* ext; if ((wstr && (wext = wcsrchr(fileName, L'.')) != 0 && !wcsicmp(wext, L".mp2")) || (str && (ext = strrchr(fileName, '.')) != 0 && !stricmp(ext, ".mp2"))) { return new TagLib::MPEG::File(fileName, readProperties, propertiesStyle); } return 0; } #else TagLib::File *MP2FileTypeResolver::createFile(const char *fileName, bool readProperties, TagLib::AudioProperties::ReadStyle propertiesStyle) const { const char *ext = strrchr(fileName, '.'); if(ext && !strcasecmp(ext, ".mp2")) { return new TagLib::MPEG::File(fileName, readProperties, propertiesStyle); } return 0; } #endif #ifdef GCC_HAS_DIAGNOSTIC_PRAGMA #pragma GCC diagnostic pop #endif kid3-3.0.2/src/plugins/taglibmetadata/taglibext/mp2/mp2filetyperesolver.h000066400000000000000000000037351224603511300264420ustar00rootroot00000000000000/*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * * MA 02110-1301 USA * ***************************************************************************/ #ifndef TAGLIB_MP2FILETYPERESOLVER_H #define TAGLIB_MP2FILETYPERESOLVER_H #include #include #if (((TAGLIB_MAJOR_VERSION) << 16) + ((TAGLIB_MINOR_VERSION) << 8) + (TAGLIB_PATCH_VERSION)) > 0x010400 && defined _WIN32 class MP2FileTypeResolver : public TagLib::FileRef::FileTypeResolver { public: virtual TagLib::File *createFile(TagLib::FileName fileName, bool readAudioProperties, TagLib::AudioProperties::ReadStyle audioPropertiesStyle) const; virtual ~MP2FileTypeResolver() {} }; #else class MP2FileTypeResolver : public TagLib::FileRef::FileTypeResolver { public: virtual TagLib::File *createFile(const char *fileName, bool readAudioProperties, TagLib::AudioProperties::ReadStyle audioPropertiesStyle) const; virtual ~MP2FileTypeResolver() {} }; #endif #endif kid3-3.0.2/src/plugins/taglibmetadata/taglibext/speex/000077500000000000000000000000001224603511300226655ustar00rootroot00000000000000kid3-3.0.2/src/plugins/taglibmetadata/taglibext/speex/speexfile.cpp000066400000000000000000000064061224603511300253630ustar00rootroot00000000000000/*************************************************************************** copyright : (C) 2006 by Lukáš Lalinský email : lalinsky@gmail.com copyright : (C) 2002 by Scott Wheeler email : wheeler@kde.org (original Vorbis implementation) ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * * MA 02110-1301 USA * ***************************************************************************/ #include #include #if 0 #include #endif #include "speexfile.h" using namespace TagLib; using namespace TagLib::Ogg; class Speex::File::FilePrivate { public: FilePrivate() : comment(0), properties(0) {} ~FilePrivate() { delete comment; delete properties; } Ogg::XiphComment *comment; Properties *properties; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// Speex::File::File(const char *file, bool readProperties, Properties::ReadStyle propertiesStyle) : Ogg::File(file) { d = new FilePrivate; read(readProperties, propertiesStyle); } Speex::File::~File() { delete d; } Ogg::XiphComment *Speex::File::tag() const { return d->comment; } Speex::Properties *Speex::File::audioProperties() const { return d->properties; } bool Speex::File::save() { if(!d->comment) d->comment = new Ogg::XiphComment; setPacket(1, d->comment->render()); return Ogg::File::save(); } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// void Speex::File::read(bool readProperties, Properties::ReadStyle propertiesStyle) { ByteVector speexHeaderData = packet(0); if(!speexHeaderData.startsWith("Speex ")) { #if 0 debug("Speex::File::read() -- invalid Speex identification header"); #endif return; } ByteVector commentHeaderData = packet(1); d->comment = new Ogg::XiphComment(commentHeaderData); if(readProperties) d->properties = new Properties(this, propertiesStyle); } kid3-3.0.2/src/plugins/taglibmetadata/taglibext/speex/speexfile.h000066400000000000000000000066661224603511300250400ustar00rootroot00000000000000/*************************************************************************** copyright : (C) 2006 by Lukáš Lalinský email : lalinsky@gmail.com copyright : (C) 2002 by Scott Wheeler email : wheeler@kde.org (original Vorbis implementation) ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * * MA 02110-1301 USA * ***************************************************************************/ #ifndef TAGLIB_SPEEXFILE_H #define TAGLIB_SPEEXFILE_H #include #include #include "speexproperties.h" namespace TagLib { namespace Ogg { //! A namespace containing classes for Speex metadata namespace Speex { //! An implementation of Ogg::File with Speex specific methods /*! * This is the central class in the Ogg Speex metadata processing collection * of classes. It's built upon Ogg::File which handles processing of the Ogg * logical bitstream and breaking it down into pages which are handled by * the codec implementations, in this case Speex specifically. */ class File : public Ogg::File { public: /*! * Contructs a Speex file from \a file. If \a readProperties is true the * file's audio properties will also be read using \a propertiesStyle. If * false, \a propertiesStyle is ignored. */ explicit File(const char *file, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average); /*! * Destroys this instance of the File. */ virtual ~File(); /*! * Returns the XiphComment for this file. XiphComment implements the tag * interface, so this serves as the reimplementation of * TagLib::File::tag(). */ virtual Ogg::XiphComment *tag() const; /*! * Returns the Speex::Properties for this file. If no audio properties * were read then this will return a null pointer. */ virtual Properties *audioProperties() const; virtual bool save(); private: File(const File &); File &operator=(const File &); void read(bool readProperties, Properties::ReadStyle propertiesStyle); class FilePrivate; FilePrivate *d; }; } } } #endif kid3-3.0.2/src/plugins/taglibmetadata/taglibext/speex/speexproperties.cpp000066400000000000000000000120421224603511300266310ustar00rootroot00000000000000/*************************************************************************** copyright : (C) 2006 by Lukáš Lalinský email : lalinsky@gmail.com copyright : (C) 2002 by Scott Wheeler email : wheeler@kde.org (original Vorbis implementation) ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * * MA 02110-1301 USA * ***************************************************************************/ #include #if 0 #include #endif #include #include "speexproperties.h" #include "speexfile.h" using namespace TagLib; using namespace TagLib::Ogg; class Speex::Properties::PropertiesPrivate { public: PropertiesPrivate(File *f, ReadStyle s) : file(f), style(s), length(0), bitrate(0), sampleRate(0), channels(0), speexVersion(0), vbr(false), mode(0) {} File *file; ReadStyle style; int length; int bitrate; int sampleRate; int channels; int speexVersion; bool vbr; int mode; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// Speex::Properties::Properties(File *file, ReadStyle style) : AudioProperties(style) { d = new PropertiesPrivate(file, style); read(); } Speex::Properties::~Properties() { delete d; } int Speex::Properties::length() const { return d->length; } int Speex::Properties::bitrate() const { return int(float(d->bitrate) / float(1000) + 0.5); } int Speex::Properties::sampleRate() const { return d->sampleRate; } int Speex::Properties::channels() const { return d->channels; } int Speex::Properties::speexVersion() const { return d->speexVersion; } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// void Speex::Properties::read() { // Get the identification header from the Ogg implementation. ByteVector data = d->file->packet(0); int pos = 28; // speex_version_id; /**< Version for Speex (for checking compatibility) */ d->speexVersion = data.mid(pos, 4).toUInt(false); pos += 4; // header_size; /**< Total size of the header ( sizeof(SpeexHeader) ) */ pos += 4; // rate; /**< Sampling rate used */ d->sampleRate = data.mid(pos, 4).toUInt(false); pos += 4; // mode; /**< Mode used (0 for narrowband, 1 for wideband) */ d->mode = data.mid(pos, 4).toUInt(false); pos += 4; // mode_bitstream_version; /**< Version ID of the bit-stream */ pos += 4; // nb_channels; /**< Number of channels encoded */ d->channels = data.mid(pos, 4).toUInt(false); pos += 4; // bitrate; /**< Bit-rate used */ d->bitrate = data.mid(pos, 4).toUInt(false); pos += 4; // frame_size; /**< Size of frames */ //unsigned int frameSize = data.mid(pos, 4).toUInt(false); pos += 4; // vbr; /**< 1 for a VBR encoding, 0 otherwise */ d->vbr = data.mid(pos, 4).toUInt(false) == 1; //pos += 4; // frames_per_packet; /**< Number of frames stored per Ogg packet */ //unsigned int framesPerPacket = data.mid(pos, 4).toUInt(false); const Ogg::PageHeader *first = d->file->firstPageHeader(); const Ogg::PageHeader *last = d->file->lastPageHeader(); if(first && last) { long long start = first->absoluteGranularPosition(); long long end = last->absoluteGranularPosition(); if(start >= 0 && end >= 0 && d->sampleRate > 0) d->length = (end - start) / (long long) d->sampleRate; #if 0 else debug("Speex::Properties::read() -- Either the PCM values for the start or " "end of this file was incorrect or the sample rate is zero."); #endif } #if 0 else debug("Speex::Properties::read() -- Could not find valid first and last Ogg pages."); #endif } kid3-3.0.2/src/plugins/taglibmetadata/taglibext/speex/speexproperties.h000066400000000000000000000054621224603511300263060ustar00rootroot00000000000000/*************************************************************************** copyright : (C) 2006 by Lukáš Lalinský email : lalinsky@gmail.com copyright : (C) 2002 by Scott Wheeler email : wheeler@kde.org (original Vorbis implementation) ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * * MA 02110-1301 USA * ***************************************************************************/ #ifndef TAGLIB_SPEEXPROPERTIES_H #define TAGLIB_SPEEXPROPERTIES_H #include namespace TagLib { namespace Ogg { namespace Speex { class File; //! An implementation of audio property reading for Ogg Speex /*! * This reads the data from an Ogg Speex stream found in the AudioProperties * API. */ class Properties : public AudioProperties { public: /*! * Create an instance of Vorbis::Properties with the data read from the * Vorbis::File \a file. */ explicit Properties(File *file, ReadStyle style = Average); /*! * Destroys this VorbisProperties instance. */ virtual ~Properties(); // Reimplementations. virtual int length() const; virtual int bitrate() const; virtual int sampleRate() const; virtual int channels() const; /*! * Returns the Vorbis version, currently "0" (as specified by the spec). */ int speexVersion() const; private: Properties(const Properties &); Properties &operator=(const Properties &); void read(); class PropertiesPrivate; PropertiesPrivate *d; }; } } } #endif kid3-3.0.2/src/plugins/taglibmetadata/taglibext/speex/taglib_speexfiletyperesolver.cpp000066400000000000000000000037141224603511300313700ustar00rootroot00000000000000/*************************************************************************** copyright : (C) 2006 by Martin Aumueller email : aumuell@reserv.at ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * * MA 02110-1301 USA * ***************************************************************************/ #include "taglib_speexfiletyperesolver.h" #include "speexfile.h" #include TagLib::File *SpeexFileTypeResolver::createFile(const char *fileName, bool readProperties, TagLib::AudioProperties::ReadStyle propertiesStyle) const { const char *ext = strrchr(fileName, '.'); if(ext && !strcasecmp(ext, ".spx")) { TagLib::Ogg::Speex::File *f = new TagLib::Ogg::Speex::File(fileName, readProperties, propertiesStyle); if(f->isValid()) return f; else { delete f; } } return 0; } kid3-3.0.2/src/plugins/taglibmetadata/taglibext/speex/taglib_speexfiletyperesolver.h000066400000000000000000000034601224603511300310330ustar00rootroot00000000000000/*************************************************************************** copyright : (C) 2006 by Martin Aumueller email : aumuell@reserv.at ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * * MA 02110-1301 USA * ***************************************************************************/ #ifndef TAGLIB_SPEEXFILETYPERESOLVER_H #define TAGLIB_SPEEXFILETYPERESOLVER_H #include #include class SpeexFileTypeResolver : public TagLib::FileRef::FileTypeResolver { public: virtual TagLib::File *createFile(const char *fileName, bool readAudioProperties, TagLib::AudioProperties::ReadStyle audioPropertiesStyle) const; virtual ~SpeexFileTypeResolver() {} }; #endif kid3-3.0.2/src/plugins/taglibmetadata/taglibext/tdebug.h000066400000000000000000000027711224603511300231730ustar00rootroot00000000000000/*************************************************************************** copyright : (C) 2002, 2003 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * * USA * ***************************************************************************/ #ifndef TAGLIB_DEBUG_H #define TAGLIB_DEBUG_H namespace TagLib { #define debug(x) #define debugData(x) } #endif kid3-3.0.2/src/plugins/taglibmetadata/taglibext/trueaudio/000077500000000000000000000000001224603511300235425ustar00rootroot00000000000000kid3-3.0.2/src/plugins/taglibmetadata/taglibext/trueaudio/combinedtag.h000066400000000000000000000104501224603511300261670ustar00rootroot00000000000000/*************************************************************************** copyright : (C) 2004 by Allan Sandfeld Jensen email : kde@carewolf.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * * MA 02110-1301 USA * ***************************************************************************/ #ifndef DO_NOT_DOCUMENT // Tell Doxygen not to document this header #ifndef TAGLIB_COMBINEDTAG_H #define TAGLIB_COMBINEDTAG_H //////////////////////////////////////////////////////////////////////////////// // Note that this header is not installed. //////////////////////////////////////////////////////////////////////////////// #include namespace TagLib { /*! * A union of two TagLib::Tags. */ class CombinedTag : public TagLib::Tag { public: explicit CombinedTag(Tag *tag1 = 0, Tag *tag2 = 0) : TagLib::Tag(), tag1(tag1), tag2(tag2) {} virtual String title() const { if(tag1 && !tag1->title().isEmpty()) return tag1->title(); if(tag2) return tag2->title(); return String::null; } virtual String artist() const { if(tag1 && !tag1->artist().isEmpty()) return tag1->artist(); if(tag2) return tag2->artist(); return String::null; } virtual String album() const { if(tag1 && !tag1->album().isEmpty()) return tag1->album(); if(tag2) return tag2->album(); return String::null; } virtual String comment() const { if(tag1 && !tag1->comment().isEmpty()) return tag1->comment(); if(tag2) return tag2->comment(); return String::null; } virtual String genre() const { if(tag1 && !tag1->genre().isEmpty()) return tag1->genre(); if(tag2) return tag2->genre(); return String::null; } virtual TagLib::uint year() const { if(tag1 && tag1->year() > 0) return tag1->year(); if(tag2) return tag2->year(); return 0; } virtual TagLib::uint track() const { if(tag1 && tag1->track() > 0) return tag1->track(); if(tag2) return tag2->track(); return 0; } virtual void setTitle(const String &s) { if(tag1) tag1->setTitle(s); if(tag2) tag2->setTitle(s); } virtual void setArtist(const String &s) { if(tag1) tag1->setArtist(s); if(tag2) tag2->setArtist(s); } virtual void setAlbum(const String &s) { if(tag1) tag1->setAlbum(s); if(tag2) tag2->setAlbum(s); } virtual void setComment(const String &s) { if(tag1) tag1->setComment(s); if(tag2) tag2->setComment(s); } virtual void setGenre(const String &s) { if(tag1) tag1->setGenre(s); if(tag2) tag2->setGenre(s); } virtual void setYear(uint i) { if(tag1) tag1->setYear(i); if(tag2) tag2->setYear(i); } virtual void setTrack(uint i) { if(tag1) tag1->setTrack(i); if(tag2) tag2->setTrack(i); } private: Tag *tag1; Tag *tag2; }; } #endif #endif kid3-3.0.2/src/plugins/taglibmetadata/taglibext/trueaudio/taglib_trueaudiofiletyperesolver.cpp000066400000000000000000000037121224603511300331200ustar00rootroot00000000000000/*************************************************************************** copyright : (C) 2006 by Martin Aumueller email : aumuell@reserv.at ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * * MA 02110-1301 USA * ***************************************************************************/ #include "taglib_trueaudiofiletyperesolver.h" #include "ttafile.h" #include TagLib::File *TTAFileTypeResolver::createFile(const char *fileName, bool readProperties, TagLib::AudioProperties::ReadStyle propertiesStyle) const { const char *ext = strrchr(fileName, '.'); if(ext && !strcasecmp(ext, ".tta")) { TagLib::TrueAudio::File *f = new TagLib::TrueAudio::File(fileName, readProperties, propertiesStyle); if(f->isValid()) return f; else { delete f; } } return 0; } kid3-3.0.2/src/plugins/taglibmetadata/taglibext/trueaudio/taglib_trueaudiofiletyperesolver.h000066400000000000000000000034641224603511300325710ustar00rootroot00000000000000/*************************************************************************** copyright : (C) 2006 by Martin Aumueller email : aumuell@reserv.at ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * * MA 02110-1301 USA * ***************************************************************************/ #ifndef TAGLIB_TRUEAUDIOFILETYPERESOLVER_H #define TAGLIB_TRUEAUDIOFILETYPERESOLVER_H #include #include class TTAFileTypeResolver : public TagLib::FileRef::FileTypeResolver { public: virtual TagLib::File *createFile(const char *fileName, bool readAudioProperties, TagLib::AudioProperties::ReadStyle audioPropertiesStyle) const; virtual ~TTAFileTypeResolver() {} }; #endif kid3-3.0.2/src/plugins/taglibmetadata/taglibext/trueaudio/ttafile.cpp000066400000000000000000000174751224603511300257140ustar00rootroot00000000000000/*************************************************************************** copyright : (C) 2006 by Lukáš Lalinský email : lalinsky@gmail.com copyright : (C) 2004 by Allan Sandfeld Jensen email : kde@carewolf.org (original MPC implementation) ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * * MA 02110-1301 USA * ***************************************************************************/ #include #include #if 0 #include #endif #include "ttafile.h" #include "id3v1tag.h" #include "id3v2tag.h" #include "id3v2header.h" #include "combinedtag.h" using namespace TagLib; class TrueAudio::File::FilePrivate { public: FilePrivate(const ID3v2::FrameFactory *frameFactory = ID3v2::FrameFactory::instance()) : ID3v2FrameFactory(frameFactory), ID3v2Tag(0), ID3v2Location(-1), ID3v2OriginalSize(0), ID3v1Tag(0), ID3v1Location(-1), tag(0), properties(0), scanned(false), hasID3v1(false), hasID3v2(false) {} ~FilePrivate() { if (tag != ID3v1Tag && tag != ID3v2Tag) delete tag; delete ID3v1Tag; delete ID3v2Tag; delete properties; } const ID3v2::FrameFactory *ID3v2FrameFactory; ID3v2::Tag *ID3v2Tag; long ID3v2Location; TagLib::uint ID3v2OriginalSize; ID3v1::Tag *ID3v1Tag; long ID3v1Location; Tag *tag; Properties *properties; bool scanned; // These indicate whether the file *on disk* has these tags, not if // this data structure does. This is used in computing offsets. bool hasID3v1; bool hasID3v2; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// TrueAudio::File::File(const char *file, bool readProperties, Properties::ReadStyle propertiesStyle) : TagLib::File(file) { d = new FilePrivate; if(isOpen()) read(readProperties, propertiesStyle); } TrueAudio::File::File(const char *file, ID3v2::FrameFactory *frameFactory, bool readProperties, Properties::ReadStyle propertiesStyle) : TagLib::File(file) { d = new FilePrivate(frameFactory); if(isOpen()) read(readProperties, propertiesStyle); } TrueAudio::File::~File() { delete d; } TagLib::Tag *TrueAudio::File::tag() const { return d->tag; } TrueAudio::Properties *TrueAudio::File::audioProperties() const { return d->properties; } void TrueAudio::File::setID3v2FrameFactory(const ID3v2::FrameFactory *factory) { d->ID3v2FrameFactory = factory; } bool TrueAudio::File::save() { if(readOnly()) { #if 0 debug("TrueAudio::File::save() -- File is read only."); #endif return false; } // Update ID3v2 tag if(d->ID3v2Tag && !d->ID3v2Tag->isEmpty()) { if(!d->hasID3v2) { d->ID3v2Location = 0; d->ID3v2OriginalSize = 0; } insert(d->ID3v2Tag->render(), d->ID3v2Location, d->ID3v2OriginalSize); d->hasID3v2 = true; // v1 tag location has changed, update if it exists if(d->hasID3v1) d->ID3v1Location = findID3v1(); // update v2 size d->ID3v2OriginalSize = d->ID3v2Tag->header()->completeTagSize(); } else if(d->hasID3v2) { removeBlock(d->ID3v2Location, d->ID3v2OriginalSize); d->ID3v2Location = -1; d->ID3v2OriginalSize = 0; d->hasID3v2 = false; // v1 tag location has changed, update if it exists if(d->hasID3v1) d->ID3v1Location = findID3v1(); } // Update ID3v1 tag if(d->ID3v1Tag && !d->ID3v1Tag->isEmpty()) { if(!d->hasID3v1) { seek(0, End); d->ID3v1Location = tell(); } else seek(d->ID3v1Location); writeBlock(d->ID3v1Tag->render()); d->hasID3v1 = true; } else if(d->hasID3v1) { removeBlock(d->ID3v1Location, 128); d->ID3v1Location = -1; d->hasID3v1 = false; } return true; } ID3v1::Tag *TrueAudio::File::ID3v1Tag(bool create) { if(!create || d->ID3v1Tag) return d->ID3v1Tag; // no ID3v1 tag exists and we've been asked to create one d->ID3v1Tag = new ID3v1::Tag; if(d->ID3v2Tag) d->tag = new CombinedTag(d->ID3v2Tag, d->ID3v1Tag); else d->tag = d->ID3v1Tag; return d->ID3v1Tag; } ID3v2::Tag *TrueAudio::File::ID3v2Tag(bool create) { if(!create || d->ID3v2Tag) return d->ID3v2Tag; // no ID3v2 tag exists and we've been asked to create one d->ID3v2Tag = new ID3v2::Tag; if(d->ID3v1Tag) d->tag = new CombinedTag(d->ID3v2Tag, d->ID3v1Tag); else d->tag = d->ID3v2Tag; return d->ID3v2Tag; } void TrueAudio::File::strip(int tags) { if(tags & ID3v1) { delete d->ID3v1Tag; d->ID3v1Tag = 0; if(d->ID3v2Tag) d->tag = d->ID3v2Tag; else d->tag = d->ID3v2Tag = new ID3v2::Tag; } if(tags & ID3v2) { delete d->ID3v2Tag; d->ID3v2Tag = 0; if(d->ID3v1Tag) d->tag = d->ID3v1Tag; else d->tag = d->ID3v2Tag = new ID3v2::Tag; } } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// void TrueAudio::File::read(bool readProperties, Properties::ReadStyle /* propertiesStyle */) { // Look for an ID3v2 tag d->ID3v2Location = findID3v2(); if(d->ID3v2Location >= 0) { d->ID3v2Tag = new ID3v2::Tag(this, d->ID3v2Location, d->ID3v2FrameFactory); d->ID3v2OriginalSize = d->ID3v2Tag->header()->completeTagSize(); if(d->ID3v2Tag->header()->tagSize() <= 0) { delete d->ID3v2Tag; d->ID3v2Tag = 0; } else d->hasID3v2 = true; } // Look for an ID3v1 tag d->ID3v1Location = findID3v1(); if(d->ID3v1Location >= 0) { d->ID3v1Tag = new ID3v1::Tag(this, d->ID3v1Location); d->hasID3v1 = true; } if(d->hasID3v1 && d->hasID3v2) d->tag = new CombinedTag(d->ID3v2Tag, d->ID3v1Tag); else { if(d->hasID3v1) d->tag = d->ID3v1Tag; else { if(d->hasID3v2) d->tag = d->ID3v2Tag; else d->tag = d->ID3v2Tag = new ID3v2::Tag; } } // Look for TTA metadata if(readProperties) { if(d->ID3v2Location >= 0) { seek(d->ID3v2Location + d->ID3v2OriginalSize); d->properties = new Properties(readBlock(TrueAudio::HeaderSize), length() - d->ID3v2OriginalSize); } else { seek(0); d->properties = new Properties(readBlock(TrueAudio::HeaderSize), length()); } } } long TrueAudio::File::findID3v1() { if(!isValid()) return -1; seek(-128, End); long p = tell(); if(readBlock(3) == ID3v1::Tag::fileIdentifier()) return p; return -1; } long TrueAudio::File::findID3v2() { if(!isValid()) return -1; seek(0); if(readBlock(3) == ID3v2::Header::fileIdentifier()) return 0; return -1; } kid3-3.0.2/src/plugins/taglibmetadata/taglibext/trueaudio/ttafile.h000066400000000000000000000142611224603511300253470ustar00rootroot00000000000000/*************************************************************************** copyright : (C) 2006 by Lukáš Lalinský email : lalinsky@gmail.com copyright : (C) 2004 by Allan Sandfeld Jensen email : kde@carewolf.org (original MPC implementation) ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * * MA 02110-1301 USA * ***************************************************************************/ #ifndef TAGLIB_TTAFILE_H #define TAGLIB_TTAFILE_H #include "tfile.h" #include "ttaproperties.h" namespace TagLib { class Tag; namespace ID3v2 { class Tag; class FrameFactory; } namespace ID3v1 { class Tag; } //! An implementation of TTA metadata /*! * This is implementation of TTA metadata. * * This supports ID3v1 and ID3v2 tags as well as reading stream * properties from the file. */ namespace TrueAudio { //! An implementation of TagLib::File with TTA specific methods /*! * This implements and provides an interface for TTA files to the * TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing * the abstract TagLib::File API as well as providing some additional * information specific to TTA files. */ class File : public TagLib::File { public: /*! * This set of flags is used for various operations and is suitable for * being OR-ed together. */ enum TagTypes { //! Empty set. Matches no tag types. NoTags = 0x0000, //! Matches ID3v1 tags. ID3v1 = 0x0001, //! Matches ID3v2 tags. ID3v2 = 0x0002, //! Matches all tag types. AllTags = 0xffff }; /*! * Contructs an MPC file from \a file. If \a readProperties is true the * file's audio properties will also be read using \a propertiesStyle. If * false, \a propertiesStyle is ignored. */ explicit File(const char *file, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average); /*! * Contructs an MPC file from \a file. If \a readProperties is true the * file's audio properties will also be read using \a propertiesStyle. If * false, \a propertiesStyle is ignored. The frames will be created using * \a frameFactory. */ File(const char *file, ID3v2::FrameFactory *frameFactory, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average); /*! * Destroys this instance of the File. */ virtual ~File(); /*! * Returns the Tag for this file. This will be an APE tag, an ID3v1 tag * or a combination of the two. */ virtual TagLib::Tag *tag() const; /*! * Returns the MPC::Properties for this file. If no audio properties * were read then this will return a null pointer. */ virtual Properties *audioProperties() const; /*! * Set the ID3v2::FrameFactory to something other than the default. * * \see ID3v2FrameFactory */ void setID3v2FrameFactory(const ID3v2::FrameFactory *factory); /*! * Saves the file. */ virtual bool save(); /*! * Returns a pointer to the ID3v2 tag of the file. * * If \a create is false (the default) this will return a null pointer * if there is no valid ID3v2 tag. If \a create is true it will create * an ID3v1 tag if one does not exist. If there is already an APE tag, the * new ID3v1 tag will be placed after it. * * \note The Tag is still owned by the TrueAudio::File and should not be * deleted by the user. It will be deleted when the file (object) is * destroyed. */ ID3v1::Tag *ID3v1Tag(bool create = false); /*! * Returns a pointer to the ID3v1 tag of the file. * * If \a create is false (the default) this will return a null pointer * if there is no valid ID3v1 tag. If \a create is true it will create * an ID3v1 tag if one does not exist. If there is already an APE tag, the * new ID3v1 tag will be placed after it. * * \note The Tag is still owned by the TrueAudio::File and should not be * deleted by the user. It will be deleted when the file (object) is * destroyed. */ ID3v2::Tag *ID3v2Tag(bool create = false); /*! * This will remove the tags that match the OR-ed together TagTypes from the * file. By default it removes all tags. * * \note This will also invalidate pointers to the tags * as their memory will be freed. * \note In order to make the removal permanent save() still needs to be called */ void strip(int tags = AllTags); private: File(const File &); File &operator=(const File &); void read(bool readProperties, Properties::ReadStyle propertiesStyle); void scan(); long findID3v1(); long findID3v2(); class FilePrivate; FilePrivate *d; }; } } #endif kid3-3.0.2/src/plugins/taglibmetadata/taglibext/trueaudio/ttaproperties.cpp000066400000000000000000000072661224603511300271660ustar00rootroot00000000000000/*************************************************************************** copyright : (C) 2006 by Lukáš Lalinský email : lalinsky@gmail.com copyright : (C) 2004 by Allan Sandfeld Jensen email : kde@carewolf.org (original MPC implementation) ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * * MA 02110-1301 USA * ***************************************************************************/ #include #if 0 #include #endif #include #include "ttaproperties.h" #include "ttafile.h" using namespace TagLib; class TrueAudio::Properties::PropertiesPrivate { public: PropertiesPrivate(const ByteVector &d, long length, ReadStyle s) : data(d), streamLength(length), style(s), version(0), length(0), bitrate(0), sampleRate(0), channels(0), bitsPerSample(0) {} ByteVector data; long streamLength; ReadStyle style; int version; int length; int bitrate; int sampleRate; int channels; int bitsPerSample; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// TrueAudio::Properties::Properties(const ByteVector &data, long streamLength, ReadStyle style) : AudioProperties(style) { d = new PropertiesPrivate(data, streamLength, style); read(); } TrueAudio::Properties::~Properties() { delete d; } int TrueAudio::Properties::length() const { return d->length; } int TrueAudio::Properties::bitrate() const { return d->bitrate; } int TrueAudio::Properties::sampleRate() const { return d->sampleRate; } int TrueAudio::Properties::bitsPerSample() const { return d->bitsPerSample; } int TrueAudio::Properties::channels() const { return d->channels; } int TrueAudio::Properties::ttaVersion() const { return d->version; } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// void TrueAudio::Properties::read() { if(!d->data.startsWith("TTA")) return; int pos = 3; d->version = d->data[pos] - '0'; pos += 1 + 2; d->channels = d->data.mid(pos, 2).toShort(false); pos += 2; d->bitsPerSample = d->data.mid(pos, 2).toShort(false); pos += 2; d->sampleRate = d->data.mid(pos, 4).toUInt(false); pos += 4; unsigned long samples = d->data.mid(pos, 4).toUInt(false); d->length = samples / d->sampleRate; d->bitrate = d->length > 0 ? ((d->streamLength * 8L) / d->length) / 1000 : 0; } kid3-3.0.2/src/plugins/taglibmetadata/taglibext/trueaudio/ttaproperties.h000066400000000000000000000053651224603511300266310ustar00rootroot00000000000000/*************************************************************************** copyright : (C) 2006 by Lukáš Lalinský email : lalinsky@gmail.com copyright : (C) 2004 by Allan Sandfeld Jensen email : kde@carewolf.org (original MPC implementation) ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * * MA 02110-1301 USA * ***************************************************************************/ #ifndef TAGLIB_TTAPROPERTIES_H #define TAGLIB_TTAPROPERTIES_H #include "audioproperties.h" namespace TagLib { namespace TrueAudio { static const TagLib::uint HeaderSize = 18; //! An implementation of audio property reading for TTA /*! * This reads the data from an TTA stream found in the AudioProperties * API. */ class Properties : public AudioProperties { public: /*! * Create an instance of TrueAudio::Properties with the data read from the * ByteVector \a data. */ Properties(const ByteVector &data, long streamLength, ReadStyle style = Average); /*! * Destroys this TrueAudio::Properties instance. */ virtual ~Properties(); // Reimplementations. virtual int length() const; virtual int bitrate() const; virtual int sampleRate() const; virtual int channels() const; /*! * Returns number of bits per sample. */ int bitsPerSample() const; /*! * Returns the major version number. */ int ttaVersion() const; private: void read(); class PropertiesPrivate; PropertiesPrivate *d; }; } } #endif kid3-3.0.2/src/plugins/taglibmetadata/taglibext/unsynchronizedlyricsframe.cpp000066400000000000000000000112301224603511300275650ustar00rootroot00000000000000/*************************************************************************** copyright : (C) 2002, 2003 by Scott Wheeler email : wheeler@kde.org copyright : (C) 2006 by Urs Fleisch email : ufleisch@users.sourceforge.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * * USA * ***************************************************************************/ #include "unsynchronizedlyricsframe.h" #include #include using namespace TagLib; using namespace ID3v2; class UnsynchronizedLyricsFrame::UnsynchronizedLyricsFramePrivate { public: UnsynchronizedLyricsFramePrivate() : textEncoding(String::Latin1) {} String::Type textEncoding; ByteVector language; String description; String text; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// UnsynchronizedLyricsFrame::UnsynchronizedLyricsFrame(String::Type encoding) : Frame("USLT") { d = new UnsynchronizedLyricsFramePrivate; d->textEncoding = encoding; } UnsynchronizedLyricsFrame::UnsynchronizedLyricsFrame(const ByteVector &data) : Frame(data) { d = new UnsynchronizedLyricsFramePrivate; setData(data); } UnsynchronizedLyricsFrame::~UnsynchronizedLyricsFrame() { delete d; } String UnsynchronizedLyricsFrame::toString() const { return d->text; } ByteVector UnsynchronizedLyricsFrame::language() const { return d->language; } String UnsynchronizedLyricsFrame::description() const { return d->description; } String UnsynchronizedLyricsFrame::text() const { return d->text; } void UnsynchronizedLyricsFrame::setLanguage(const ByteVector &languageEncoding) { d->language = languageEncoding.mid(0, 3); } void UnsynchronizedLyricsFrame::setDescription(const String &s) { d->description = s; } void UnsynchronizedLyricsFrame::setText(const String &s) { d->text = s; } String::Type UnsynchronizedLyricsFrame::textEncoding() const { return d->textEncoding; } void UnsynchronizedLyricsFrame::setTextEncoding(String::Type encoding) { d->textEncoding = encoding; } //////////////////////////////////////////////////////////////////////////////// // protected members //////////////////////////////////////////////////////////////////////////////// void UnsynchronizedLyricsFrame::parseFields(const ByteVector &data) { if(data.size() < 5) { debug("An unsynchronized lyrics frame must contain at least 5 bytes."); return; } d->textEncoding = String::Type(data[0]); d->language = data.mid(1, 3); int byteAlign = d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8 ? 1 : 2; ByteVectorList l = ByteVectorList::split(data.mid(4), textDelimiter(d->textEncoding), byteAlign, 2); if(l.size() == 2) { d->description = String(l.front(), d->textEncoding); d->text = String(l.back(), d->textEncoding); } } ByteVector UnsynchronizedLyricsFrame::renderFields() const { ByteVector v; v.append(char(d->textEncoding)); v.append(d->language.size() == 3 ? d->language : " "); v.append(d->description.data(d->textEncoding)); v.append(textDelimiter(d->textEncoding)); v.append(d->text.data(d->textEncoding)); return v; } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// UnsynchronizedLyricsFrame::UnsynchronizedLyricsFrame(const ByteVector &data, Header *h) : Frame(h) { d = new UnsynchronizedLyricsFramePrivate(); parseFields(fieldData(data)); } kid3-3.0.2/src/plugins/taglibmetadata/taglibext/unsynchronizedlyricsframe.h000066400000000000000000000114641224603511300272430ustar00rootroot00000000000000/*************************************************************************** copyright : (C) 2002, 2003 by Scott Wheeler email : wheeler@kde.org copyright : (C) 2006 by Urs Fleisch email : ufleisch@users.sourceforge.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * * USA * ***************************************************************************/ #ifndef TAGLIB_UNSYNCHRONIZEDLYRICSFRAME_H #define TAGLIB_UNSYNCHRONIZEDLYRICSFRAME_H #include namespace TagLib { namespace ID3v2 { /*! * An implementation of ID3v2 unsynchronized lyrics. */ class UnsynchronizedLyricsFrame : public Frame { friend class FrameFactory; public: /*! * Construct an empty unsynchronized lyrics frame that will use the text encoding * \a encoding. */ explicit UnsynchronizedLyricsFrame(String::Type encoding = String::Latin1); /*! * Construct a unsynchronized lyrics frame based on the data in \a data. */ explicit UnsynchronizedLyricsFrame(const ByteVector &data); /*! * Destroys this UnsynchronizedLyricsFrame instance. */ virtual ~UnsynchronizedLyricsFrame(); /*! * Returns the text of this unsynchronized lyrics frame. * * \see text() */ virtual String toString() const; /*! * Returns the language encoding as a 3 byte encoding as specified by * ISO-639-2. * * \note Most taggers simply ignore this value. * * \see setLanguage() */ ByteVector language() const; /*! * Returns the description of this unsynchronized lyrics frame. * * \note Most taggers simply ignore this value. * * \see setDescription() */ String description() const; /*! * Returns the text of this unsynchronized lyrics frame. * * \see setText() */ String text() const; /*! * Set the language using the 3 byte language code from * ISO-639-2 to * \a languageCode. * * \see language() */ void setLanguage(const ByteVector &languageCode); /*! * Sets the description of the unsynchronized lyrics frame to \a s. * * \see decription() */ void setDescription(const String &s); /*! * Sets the text portion of the unsynchronized lyrics frame to \a s. * * \see text() */ virtual void setText(const String &s); /*! * Returns the text encoding that will be used in rendering this frame. * This defaults to the type that was either specified in the constructor * or read from the frame when parsed. * * \see setTextEncoding() * \see render() */ String::Type textEncoding() const; /*! * Sets the text encoding to be used when rendering this frame to * \a encoding. * * \see textEncoding() * \see render() */ void setTextEncoding(String::Type encoding); protected: // Reimplementations. virtual void parseFields(const ByteVector &data); virtual ByteVector renderFields() const; private: /*! * The constructor used by the FrameFactory. */ UnsynchronizedLyricsFrame(const ByteVector &data, Header *h); UnsynchronizedLyricsFrame(const UnsynchronizedLyricsFrame &); UnsynchronizedLyricsFrame &operator=(const UnsynchronizedLyricsFrame &); class UnsynchronizedLyricsFramePrivate; UnsynchronizedLyricsFramePrivate *d; }; } } #endif kid3-3.0.2/src/plugins/taglibmetadata/taglibext/urllinkframe.cpp000066400000000000000000000106731224603511300247470ustar00rootroot00000000000000/*************************************************************************** copyright : (C) 2002, 2003 by Scott Wheeler email : wheeler@kde.org copyright : (C) 2006 by Urs Fleisch email : ufleisch@users.sourceforge.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * * USA * ***************************************************************************/ #include "urllinkframe.h" #include using namespace TagLib; using namespace ID3v2; class UrlLinkFrame::UrlLinkFramePrivate { public: String url; }; class UserUrlLinkFrame::UserUrlLinkFramePrivate { public: UserUrlLinkFramePrivate() : textEncoding(String::Latin1) {} String::Type textEncoding; String description; }; UrlLinkFrame::UrlLinkFrame(const ByteVector &data) : Frame(data) { d = new UrlLinkFramePrivate; setData(data); } UrlLinkFrame::~UrlLinkFrame() { delete d; } void UrlLinkFrame::setUrl(const String &s) { d->url = s; } String UrlLinkFrame::url() const { return d->url; } void UrlLinkFrame::setText(const String &s) { setUrl(s); } String UrlLinkFrame::toString() const { return url(); } void UrlLinkFrame::parseFields(const ByteVector &data) { d->url = String(data); } ByteVector UrlLinkFrame::renderFields() const { return d->url.data(String::Latin1); } UrlLinkFrame::UrlLinkFrame(const ByteVector &data, Header *h) : Frame(h) { d = new UrlLinkFramePrivate; parseFields(fieldData(data)); } UserUrlLinkFrame::UserUrlLinkFrame(String::Type encoding) : UrlLinkFrame("WXXX") { d = new UserUrlLinkFramePrivate; d->textEncoding = encoding; } UserUrlLinkFrame::UserUrlLinkFrame(const ByteVector &data) : UrlLinkFrame(data) { d = new UserUrlLinkFramePrivate; setData(data); } UserUrlLinkFrame::~UserUrlLinkFrame() { delete d; } String UserUrlLinkFrame::toString() const { return "[" + description() + "] " + url(); } String::Type UserUrlLinkFrame::textEncoding() const { return d->textEncoding; } void UserUrlLinkFrame::setTextEncoding(String::Type encoding) { d->textEncoding = encoding; } String UserUrlLinkFrame::description() const { return d->description; } void UserUrlLinkFrame::setDescription(const String &s) { d->description = s; } void UserUrlLinkFrame::parseFields(const ByteVector &data) { if (data.size() < 2) { debug("A user URL link frame must contain at least 2 bytes."); return; } int pos = 0; d->textEncoding = String::Type(data[0]); pos += 1; if (d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8) { int offset = data.find(textDelimiter(d->textEncoding), pos); if (offset < pos) return; d->description = String(data.mid(pos, offset - pos), d->textEncoding); pos = offset + 1; } else { int len = data.mid(pos).find(textDelimiter(d->textEncoding), 0, 2); if (len < 0) return; d->description = String(data.mid(pos, len), d->textEncoding); pos += len + 2; } setUrl(String(data.mid(pos))); } ByteVector UserUrlLinkFrame::renderFields() const { ByteVector v; v.append(char(d->textEncoding)); v.append(d->description.data(d->textEncoding)); v.append(textDelimiter(d->textEncoding)); v.append(url().data(String::Latin1)); return v; } UserUrlLinkFrame::UserUrlLinkFrame(const ByteVector &data, Header *h) : UrlLinkFrame(data, h) { d = new UserUrlLinkFramePrivate; parseFields(fieldData(data)); } kid3-3.0.2/src/plugins/taglibmetadata/taglibext/urllinkframe.h000066400000000000000000000117751224603511300244200ustar00rootroot00000000000000/*************************************************************************** copyright : (C) 2002, 2003 by Scott Wheeler email : wheeler@kde.org copyright : (C) 2006 by Urs Fleisch email : ufleisch@users.sourceforge.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * * USA * ***************************************************************************/ #ifndef TAGLIB_URLLINKFRAME_H #define TAGLIB_URLLINKFRAME_H #include namespace TagLib { namespace ID3v2 { /*! * An implementation of ID3v2 URL link frames. */ class UrlLinkFrame : public Frame { friend class FrameFactory; public: /*! * This is a dual purpose constructor. \a data can either be binary data * that should be parsed or (at a minimum) the frame ID. */ explicit UrlLinkFrame(const ByteVector &data); /*! * Destroys this UrlLinkFrame instance. */ virtual ~UrlLinkFrame(); /*! * Returns the URL. */ virtual String url() const; /*! * Sets the URL to \a s. */ virtual void setUrl(const String &s); // Reimplementations. virtual void setText(const String &s); virtual String toString() const; protected: virtual void parseFields(const ByteVector &data); virtual ByteVector renderFields() const; /*! * The constructor used by the FrameFactory. */ UrlLinkFrame(const ByteVector &data, Header *h); private: UrlLinkFrame(const UrlLinkFrame &); UrlLinkFrame &operator=(const UrlLinkFrame &); class UrlLinkFramePrivate; UrlLinkFramePrivate *d; }; /*! * This is a specialization of URL link frames that allows for * user defined entries. Each entry has a description in addition to the * normal list of fields that a URL link frame has. * * This description identifies the frame and must be unique. */ class UserUrlLinkFrame : public UrlLinkFrame { friend class FrameFactory; public: /*! * Constructs an empty user defined URL link frame. For this to be * a useful frame both a description and text must be set. */ explicit UserUrlLinkFrame(String::Type encoding = String::Latin1); /*! * This is a dual purpose constructor. \a data can either be binary data * that should be parsed or (at a minimum) the frame ID. */ explicit UserUrlLinkFrame(const ByteVector &data); /*! * Destroys this UserUrlLinkFrame instance. */ virtual ~UserUrlLinkFrame(); // Reimplementations. virtual String toString() const; /*! * Returns the text encoding that will be used in rendering this frame. * This defaults to the type that was either specified in the constructor * or read from the frame when parsed. * * \see setTextEncoding() * \see render() */ String::Type textEncoding() const; /*! * Sets the text encoding to be used when rendering this frame to * \a encoding. * * \see textEncoding() * \see render() */ void setTextEncoding(String::Type encoding); /*! * Returns the description for this frame. */ String description() const; /*! * Sets the description of the frame to \a s. \a s must be unique. */ void setDescription(const String &s); protected: virtual void parseFields(const ByteVector &data); virtual ByteVector renderFields() const; /*! * The constructor used by the FrameFactory. */ UserUrlLinkFrame(const ByteVector &data, Header *h); private: UserUrlLinkFrame(const UserUrlLinkFrame &); UserUrlLinkFrame &operator=(const UserUrlLinkFrame &); class UserUrlLinkFramePrivate; UserUrlLinkFramePrivate *d; }; } } #endif kid3-3.0.2/src/plugins/taglibmetadata/taglibext/wavpack/000077500000000000000000000000001224603511300231755ustar00rootroot00000000000000kid3-3.0.2/src/plugins/taglibmetadata/taglibext/wavpack/combinedtag.h000066400000000000000000000104501224603511300256220ustar00rootroot00000000000000/*************************************************************************** copyright : (C) 2004 by Allan Sandfeld Jensen email : kde@carewolf.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * * MA 02110-1301 USA * ***************************************************************************/ #ifndef DO_NOT_DOCUMENT // Tell Doxygen not to document this header #ifndef TAGLIB_COMBINEDTAG_H #define TAGLIB_COMBINEDTAG_H //////////////////////////////////////////////////////////////////////////////// // Note that this header is not installed. //////////////////////////////////////////////////////////////////////////////// #include namespace TagLib { /*! * A union of two TagLib::Tags. */ class CombinedTag : public TagLib::Tag { public: explicit CombinedTag(Tag *tag1 = 0, Tag *tag2 = 0) : TagLib::Tag(), tag1(tag1), tag2(tag2) {} virtual String title() const { if(tag1 && !tag1->title().isEmpty()) return tag1->title(); if(tag2) return tag2->title(); return String::null; } virtual String artist() const { if(tag1 && !tag1->artist().isEmpty()) return tag1->artist(); if(tag2) return tag2->artist(); return String::null; } virtual String album() const { if(tag1 && !tag1->album().isEmpty()) return tag1->album(); if(tag2) return tag2->album(); return String::null; } virtual String comment() const { if(tag1 && !tag1->comment().isEmpty()) return tag1->comment(); if(tag2) return tag2->comment(); return String::null; } virtual String genre() const { if(tag1 && !tag1->genre().isEmpty()) return tag1->genre(); if(tag2) return tag2->genre(); return String::null; } virtual TagLib::uint year() const { if(tag1 && tag1->year() > 0) return tag1->year(); if(tag2) return tag2->year(); return 0; } virtual TagLib::uint track() const { if(tag1 && tag1->track() > 0) return tag1->track(); if(tag2) return tag2->track(); return 0; } virtual void setTitle(const String &s) { if(tag1) tag1->setTitle(s); if(tag2) tag2->setTitle(s); } virtual void setArtist(const String &s) { if(tag1) tag1->setArtist(s); if(tag2) tag2->setArtist(s); } virtual void setAlbum(const String &s) { if(tag1) tag1->setAlbum(s); if(tag2) tag2->setAlbum(s); } virtual void setComment(const String &s) { if(tag1) tag1->setComment(s); if(tag2) tag2->setComment(s); } virtual void setGenre(const String &s) { if(tag1) tag1->setGenre(s); if(tag2) tag2->setGenre(s); } virtual void setYear(uint i) { if(tag1) tag1->setYear(i); if(tag2) tag2->setYear(i); } virtual void setTrack(uint i) { if(tag1) tag1->setTrack(i); if(tag2) tag2->setTrack(i); } private: Tag *tag1; Tag *tag2; }; } #endif #endif kid3-3.0.2/src/plugins/taglibmetadata/taglibext/wavpack/taglib_wavpackfiletyperesolver.cpp000066400000000000000000000037061224603511300322110ustar00rootroot00000000000000/*************************************************************************** copyright : (C) 2006 by Martin Aumueller email : aumuell@reserv.at ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * * MA 02110-1301 USA * ***************************************************************************/ #include "taglib_wavpackfiletyperesolver.h" #include "wvfile.h" #include TagLib::File *WavPackFileTypeResolver::createFile(const char *fileName, bool readProperties, TagLib::AudioProperties::ReadStyle propertiesStyle) const { const char *ext = strrchr(fileName, '.'); if(ext && !strcasecmp(ext, ".wv")) { TagLib::WavPack::File *f = new TagLib::WavPack::File(fileName, readProperties, propertiesStyle); if(f->isValid()) return f; else { delete f; } } return 0; } kid3-3.0.2/src/plugins/taglibmetadata/taglibext/wavpack/taglib_wavpackfiletyperesolver.h000066400000000000000000000034701224603511300316540ustar00rootroot00000000000000/*************************************************************************** copyright : (C) 2006 by Martin Aumueller email : aumuell@reserv.at ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * * MA 02110-1301 USA * ***************************************************************************/ #ifndef TAGLIB_WAVPACKFILETYPERESOLVER_H #define TAGLIB_WAVPACKFILETYPERESOLVER_H #include #include class WavPackFileTypeResolver : public TagLib::FileRef::FileTypeResolver { public: virtual TagLib::File *createFile(const char *fileName, bool readAudioProperties, TagLib::AudioProperties::ReadStyle audioPropertiesStyle) const; virtual ~WavPackFileTypeResolver() {} }; #endif kid3-3.0.2/src/plugins/taglibmetadata/taglibext/wavpack/wvfile.cpp000066400000000000000000000162761224603511300252110ustar00rootroot00000000000000/*************************************************************************** copyright : (C) 2006 by Lukáš Lalinský email : lalinsky@gmail.com copyright : (C) 2004 by Allan Sandfeld Jensen email : kde@carewolf.org (original MPC implementation) ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * * MA 02110-1301 USA * ***************************************************************************/ #include #include #if 0 #include #endif #include "wvfile.h" #include "id3v1tag.h" #include "id3v2header.h" #include "apetag.h" #include "apefooter.h" #include "combinedtag.h" using namespace TagLib; class WavPack::File::FilePrivate { public: FilePrivate() : APETag(0), APELocation(-1), APESize(0), ID3v1Tag(0), ID3v1Location(-1), tag(0), properties(0), scanned(false), hasAPE(false), hasID3v1(false) {} ~FilePrivate() { if (tag != ID3v1Tag && tag != APETag) delete tag; delete ID3v1Tag; delete APETag; delete properties; } APE::Tag *APETag; long APELocation; TagLib::uint APESize; ID3v1::Tag *ID3v1Tag; long ID3v1Location; Tag *tag; Properties *properties; bool scanned; // These indicate whether the file *on disk* has these tags, not if // this data structure does. This is used in computing offsets. bool hasAPE; bool hasID3v1; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// WavPack::File::File(const char *file, bool readProperties, Properties::ReadStyle propertiesStyle) : TagLib::File(file) { d = new FilePrivate; read(readProperties, propertiesStyle); } WavPack::File::~File() { delete d; } TagLib::Tag *WavPack::File::tag() const { return d->tag; } WavPack::Properties *WavPack::File::audioProperties() const { return d->properties; } bool WavPack::File::save() { if(readOnly()) { #if 0 debug("WavPack::File::save() -- File is read only."); #endif return false; } // Update ID3v1 tag if(d->ID3v1Tag && !d->ID3v1Tag->isEmpty()) { if(d->hasID3v1) { seek(d->ID3v1Location); writeBlock(d->ID3v1Tag->render()); } else { seek(0, End); d->ID3v1Location = tell(); writeBlock(d->ID3v1Tag->render()); d->hasID3v1 = true; } } else if(d->hasID3v1) { removeBlock(d->ID3v1Location, 128); d->hasID3v1 = false; if(d->hasAPE) { if(d->APELocation > d->ID3v1Location) d->APELocation -= 128; } } // Update APE tag if(d->APETag && !d->APETag->isEmpty()) { if(d->hasAPE) insert(d->APETag->render(), d->APELocation, d->APESize); else { if(d->hasID3v1) { insert(d->APETag->render(), d->ID3v1Location, 0); d->APESize = d->APETag->footer()->completeTagSize(); d->hasAPE = true; d->APELocation = d->ID3v1Location; d->ID3v1Location += d->APESize; } else { seek(0, End); d->APELocation = tell(); writeBlock(d->APETag->render()); d->APESize = d->APETag->footer()->completeTagSize(); d->hasAPE = true; } } } else if(d->hasAPE) { removeBlock(d->APELocation, d->APESize); d->hasAPE = false; if(d->hasID3v1) { if (d->ID3v1Location > d->APELocation) d->ID3v1Location -= d->APESize; } } return true; } ID3v1::Tag *WavPack::File::ID3v1Tag(bool create) { if(!create || d->ID3v1Tag) return d->ID3v1Tag; // no ID3v1 tag exists and we've been asked to create one d->ID3v1Tag = new ID3v1::Tag; if(d->APETag) d->tag = new CombinedTag(d->APETag, d->ID3v1Tag); else d->tag = d->ID3v1Tag; return d->ID3v1Tag; } APE::Tag *WavPack::File::APETag(bool create) { if(!create || d->APETag) return d->APETag; // no APE tag exists and we've been asked to create one d->APETag = new APE::Tag; if(d->ID3v1Tag) d->tag = new CombinedTag(d->APETag, d->ID3v1Tag); else d->tag = d->APETag; return d->APETag; } void WavPack::File::strip(int tags) { if(tags & ID3v1) { delete d->ID3v1Tag; d->ID3v1Tag = 0; if(d->APETag) d->tag = d->APETag; else d->tag = d->APETag = new APE::Tag; } if(tags & APE) { delete d->APETag; d->APETag = 0; if(d->ID3v1Tag) d->tag = d->ID3v1Tag; else d->tag = d->APETag = new APE::Tag; } } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// void WavPack::File::read(bool readProperties, Properties::ReadStyle /* propertiesStyle */) { // Look for an ID3v1 tag d->ID3v1Location = findID3v1(); if(d->ID3v1Location >= 0) { d->ID3v1Tag = new ID3v1::Tag(this, d->ID3v1Location); d->hasID3v1 = true; } // Look for an APE tag d->APELocation = findAPE(); if(d->APELocation >= 0) { d->APETag = new APE::Tag(this, d->APELocation); d->APESize = d->APETag->footer()->completeTagSize(); d->APELocation = d->APELocation + d->APETag->footer()->size() - d->APESize; d->hasAPE = true; } if(d->hasID3v1 && d->hasAPE) d->tag = new CombinedTag(d->APETag, d->ID3v1Tag); else { if(d->hasID3v1) d->tag = d->ID3v1Tag; else { if(d->hasAPE) d->tag = d->APETag; else d->tag = d->APETag = new APE::Tag; } } // Look for WavPack audio properties if(readProperties) { seek(0); d->properties = new Properties(readBlock(WavPack::HeaderSize), length() - d->APESize); } } long WavPack::File::findAPE() { if(!isValid()) return -1; if(d->hasID3v1) seek(-160, End); else seek(-32, End); long p = tell(); if(readBlock(8) == APE::Tag::fileIdentifier()) return p; return -1; } long WavPack::File::findID3v1() { if(!isValid()) return -1; seek(-128, End); long p = tell(); if(readBlock(3) == ID3v1::Tag::fileIdentifier()) return p; return -1; } kid3-3.0.2/src/plugins/taglibmetadata/taglibext/wavpack/wvfile.h000066400000000000000000000126671224603511300246560ustar00rootroot00000000000000/*************************************************************************** copyright : (C) 2006 by Lukáš Lalinský email : lalinsky@gmail.com copyright : (C) 2004 by Allan Sandfeld Jensen email : kde@carewolf.org (original MPC implementation) ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * * MA 02110-1301 USA * ***************************************************************************/ #ifndef TAGLIB_WVFILE_H #define TAGLIB_WVFILE_H #include "tfile.h" #include "wvproperties.h" namespace TagLib { class Tag; namespace ID3v1 { class Tag; } namespace APE { class Tag; } //! An implementation of WavPack metadata /*! * This is implementation of WavPack metadata. * * This supports ID3v1 and APE (v1 and v2) style comments as well as reading stream * properties from the file. */ namespace WavPack { //! An implementation of TagLib::File with WavPack specific methods /*! * This implements and provides an interface for WavPack files to the * TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing * the abstract TagLib::File API as well as providing some additional * information specific to WavPack files. */ class File : public TagLib::File { public: /*! * This set of flags is used for various operations and is suitable for * being OR-ed together. */ enum TagTypes { //! Empty set. Matches no tag types. NoTags = 0x0000, //! Matches ID3v1 tags. ID3v1 = 0x0001, //! Matches APE tags. APE = 0x0002, //! Matches all tag types. AllTags = 0xffff }; /*! * Contructs an WavPack file from \a file. If \a readProperties is true the * file's audio properties will also be read using \a propertiesStyle. If * false, \a propertiesStyle is ignored. */ explicit File(const char *file, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average); /*! * Destroys this instance of the File. */ virtual ~File(); /*! * Returns the Tag for this file. This will be an APE tag, an ID3v1 tag * or a combination of the two. */ virtual TagLib::Tag *tag() const; /*! * Returns the MPC::Properties for this file. If no audio properties * were read then this will return a null pointer. */ virtual Properties *audioProperties() const; /*! * Saves the file. */ virtual bool save(); /*! * Returns a pointer to the ID3v1 tag of the file. * * If \a create is false (the default) this will return a null pointer * if there is no valid ID3v1 tag. If \a create is true it will create * an ID3v1 tag if one does not exist. If there is already an APE tag, the * new ID3v1 tag will be placed after it. * * \note The Tag is still owned by the APE::File and should not be * deleted by the user. It will be deleted when the file (object) is * destroyed. */ ID3v1::Tag *ID3v1Tag(bool create = false); /*! * Returns a pointer to the APE tag of the file. * * If \a create is false (the default) this will return a null pointer * if there is no valid APE tag. If \a create is true it will create * a APE tag if one does not exist. * * \note The Tag is still owned by the APE::File and should not be * deleted by the user. It will be deleted when the file (object) is * destroyed. */ APE::Tag *APETag(bool create = false); /*! * This will remove the tags that match the OR-ed together TagTypes from the * file. By default it removes all tags. * * \note This will also invalidate pointers to the tags * as their memory will be freed. * \note In order to make the removal permanent save() still needs to be called */ void strip(int tags = AllTags); private: File(const File &); File &operator=(const File &); void read(bool readProperties, Properties::ReadStyle propertiesStyle); void scan(); long findID3v1(); long findAPE(); class FilePrivate; FilePrivate *d; }; } } #endif kid3-3.0.2/src/plugins/taglibmetadata/taglibext/wavpack/wvproperties.cpp000066400000000000000000000101461224603511300264540ustar00rootroot00000000000000/*************************************************************************** copyright : (C) 2006 by Lukáš Lalinský email : lalinsky@gmail.com copyright : (C) 2004 by Allan Sandfeld Jensen email : kde@carewolf.org (original MPC implementation) ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * * MA 02110-1301 USA * ***************************************************************************/ #include #if 0 #include #endif #include #include "wvproperties.h" #include "wvfile.h" using namespace TagLib; class WavPack::Properties::PropertiesPrivate { public: PropertiesPrivate(const ByteVector &d, long length, ReadStyle s) : data(d), streamLength(length), style(s), length(0), bitrate(0), sampleRate(0), channels(0), version(0), bitsPerSample(0) {} ByteVector data; long streamLength; ReadStyle style; int length; int bitrate; int sampleRate; int channels; int version; int bitsPerSample; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// WavPack::Properties::Properties(const ByteVector &data, long streamLength, ReadStyle style) : AudioProperties(style) { d = new PropertiesPrivate(data, streamLength, style); read(); } WavPack::Properties::~Properties() { delete d; } int WavPack::Properties::length() const { return d->length; } int WavPack::Properties::bitrate() const { return d->bitrate; } int WavPack::Properties::sampleRate() const { return d->sampleRate; } int WavPack::Properties::channels() const { return d->channels; } int WavPack::Properties::version() const { return d->version; } int WavPack::Properties::bitsPerSample() const { return d->bitsPerSample; } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// static const unsigned int sample_rates[] = { 6000, 8000, 9600, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000, 192000 }; #define BYTES_STORED 3 #define MONO_FLAG 4 #define SHIFT_LSB 13 #define SHIFT_MASK (0x1fL << SHIFT_LSB) #define SRATE_LSB 23 #define SRATE_MASK (0xfL << SRATE_LSB) void WavPack::Properties::read() { if(!d->data.startsWith("wvpk")) return; d->version = d->data.mid(8, 2).toShort(false); unsigned int flags = d->data.mid(24, 4).toUInt(false); d->bitsPerSample = ((flags & BYTES_STORED) + 1) * 8 - ((flags & SHIFT_MASK) >> SHIFT_LSB); d->sampleRate = sample_rates[(flags & SRATE_MASK) >> SRATE_LSB]; d->channels = (flags & MONO_FLAG) ? 1 : 2; unsigned int samples = d->data.mid(12, 4).toUInt(false); d->length = d->sampleRate > 0 ? (samples + (d->sampleRate / 2)) / d->sampleRate : 0; d->bitrate = d->length > 0 ? ((d->streamLength * 8L) / d->length) / 1000 : 0; } kid3-3.0.2/src/plugins/taglibmetadata/taglibext/wavpack/wvproperties.h000066400000000000000000000053711224603511300261250ustar00rootroot00000000000000/*************************************************************************** copyright : (C) 2006 by Lukáš Lalinský email : lalinsky@gmail.com copyright : (C) 2004 by Allan Sandfeld Jensen email : kde@carewolf.org (original MPC implementation) ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * * MA 02110-1301 USA * ***************************************************************************/ #ifndef TAGLIB_WVPROPERTIES_H #define TAGLIB_WVPROPERTIES_H #include "audioproperties.h" namespace TagLib { namespace WavPack { static const TagLib::uint HeaderSize = 32; //! An implementation of audio property reading for WavPack /*! * This reads the data from an WavPack stream found in the AudioProperties * API. */ class Properties : public AudioProperties { public: /*! * Create an instance of WavPack::Properties with the data read from the * ByteVector \a data. */ Properties(const ByteVector &data, long streamLength, ReadStyle style = Average); /*! * Destroys this WavPack::Properties instance. */ virtual ~Properties(); // Reimplementations. virtual int length() const; virtual int bitrate() const; virtual int sampleRate() const; virtual int channels() const; /*! * Returns number of bits per sample. */ int bitsPerSample() const; /*! * Returns WavPack version. */ int version() const; private: void read(); class PropertiesPrivate; PropertiesPrivate *d; }; } } #endif kid3-3.0.2/src/plugins/taglibmetadata/taglibfile.cpp000066400000000000000000005224101224603511300223700ustar00rootroot00000000000000/** * \file taglibfile.cpp * Handling of tagged files using TagLib. * * \b Project: Kid3 * \author Urs Fleisch * \date 12 Sep 2006 * * Copyright (C) 2006-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "taglibfile.h" #include #include #include #include #include #include #include "genres.h" #include "attributedata.h" #include "pictureframe.h" #include #ifdef Q_OS_WIN32 #include #else #include #endif // Just using include , include as recommended in the // TagLib documentation does not work, as there are files with these names // in this directory. #include #include #include #include #include #include #include #include #include #include #include #include #include #if TAGLIB_VERSION <= 0x010400 #include "taglibext/generalencapsulatedobjectframe.h" #include "taglibext/urllinkframe.h" #include "taglibext/unsynchronizedlyricsframe.h" #include "taglibext/speex/speexfile.h" #include "taglibext/speex/taglib_speexfiletyperesolver.h" #include "taglibext/trueaudio/ttafile.h" #include "taglibext/trueaudio/taglib_trueaudiofiletyperesolver.h" #include "taglibext/wavpack/wvfile.h" #include "taglibext/wavpack/taglib_wavpackfiletyperesolver.h" #else #include #include #include #include #include #include #endif #if TAGLIB_VERSION >= 0x010600 #ifdef TAGLIB_WITH_MP4 #include #endif #ifdef TAGLIB_WITH_ASF #include #endif #include #include #include #include #endif #if TAGLIB_VERSION >= 0x010700 #include #endif #if TAGLIB_VERSION >= 0x010800 #include #include #include #include #ifdef HAVE_TAGLIB_XM_SUPPORT #include #endif #endif #if TAGLIB_VERSION >= 0x010900 #include #endif #include "taglibext/aac/aacfiletyperesolver.h" #include "taglibext/mp2/mp2filetyperesolver.h" namespace { #if TAGLIB_VERSION >= 0x010700 /** * Set a picture frame from a FLAC picture. * * @param pic FLAC picture * @param frame the picture frame is returned here */ void flacPictureToFrame(const TagLib::FLAC::Picture* pic, Frame& frame) { TagLib::ByteVector picData(pic->data()); PictureFrame::setFields( frame, Frame::Field::TE_ISO8859_1, QLatin1String("JPG"), TStringToQString(pic->mimeType()), static_cast(pic->type()), TStringToQString(pic->description()), QByteArray(picData.data(), picData.size())); } /** * Set a FLAC picture from a frame. * * @param frame picture frame * @param pic the FLAC picture to set */ void frameToFlacPicture(const Frame& frame, TagLib::FLAC::Picture* pic) { Frame::Field::TextEncoding enc; QString imgFormat; QString mimeType; PictureFrame::PictureType pictureType; QString description; QByteArray data; PictureFrame::getFields(frame, enc, imgFormat, mimeType, pictureType, description, data); pic->setType(static_cast(pictureType)); pic->setMimeType(QSTRING_TO_TSTRING(mimeType)); pic->setDescription(QSTRING_TO_TSTRING(description)); pic->setData(TagLib::ByteVector(data.data(), data.size())); QImage image; if (image.loadFromData(data)) { pic->setWidth(image.width()); pic->setHeight(image.height()); pic->setColorDepth(image.depth()); #if QT_VERSION >= 0x040600 pic->setNumColors(image.colorCount()); #else pic->setNumColors(image.numColors()); #endif } } #endif } /** * Data encoding in ID3v1 tags. */ class TextCodecStringHandler : public TagLib::ID3v1::StringHandler { public: /** * Constructor. */ TextCodecStringHandler() {} /** * Destructor. */ virtual ~TextCodecStringHandler() {} /** * Decode a string from data. * * @param data data to decode */ virtual TagLib::String parse(const TagLib::ByteVector& data) const; /** * Encode a byte vector with the data from a string. * * @param s string to encode */ virtual TagLib::ByteVector render(const TagLib::String& s) const; /** * Set text codec. * @param codec text codec, 0 for default behavior (ISO 8859-1) */ static void setTextCodec(const QTextCodec* codec) { s_codec = codec; } private: static const QTextCodec* s_codec; }; const QTextCodec* TextCodecStringHandler::s_codec = 0; /** * Decode a string from data. * * @param data data to decode */ TagLib::String TextCodecStringHandler::parse(const TagLib::ByteVector& data) const { return s_codec ? QSTRING_TO_TSTRING(s_codec->toUnicode(data.data(), data.size())).stripWhiteSpace() : TagLib::String(data, TagLib::String::Latin1).stripWhiteSpace(); } /** * Encode a byte vector with the data from a string. * * @param s string to encode */ TagLib::ByteVector TextCodecStringHandler::render(const TagLib::String& s) const { if (s_codec) { QByteArray ba(s_codec->fromUnicode(TStringToQString(s))); return TagLib::ByteVector(ba.data(), ba.size()); } else { return s.data(TagLib::String::Latin1); } } /** Default text encoding */ TagLib::String::Type TagLibFile::s_defaultTextEncoding = TagLib::String::Latin1; /** List of TagLib files with open file descriptor */ QList TagLibFile::s_openFiles; /** * Constructor. * * @param dn directory name * @param fn filename * @param idx model index */ TagLibFile::TagLibFile(const QString& dn, const QString& fn, const QPersistentModelIndex& idx) : TaggedFile(dn, fn, idx), m_tagV1(0), m_tagV2(0), #if TAGLIB_VERSION >= 0x010800 m_id3v2Version(0), #endif m_activatedFeatures(0), m_fileRead(false), m_tagInformationRead(false), m_hasTagV1(false), m_hasTagV2(false), m_isTagV1Supported(false), m_duration(0), m_tagTypeV1(TT_Unknown), m_tagTypeV2(TT_Unknown) { } /** * Destructor. */ TagLibFile::~TagLibFile() { closeFile(true); } /** * Get key of tagged file format. * @return "TaglibMetadata". */ QString TagLibFile::taggedFileKey() const { return QLatin1String("TaglibMetadata"); } /** * Get features supported. * @return bit mask with Feature flags set. */ int TagLibFile::taggedFileFeatures() const { return TF_ID3v11 | TF_ID3v22 | #if TAGLIB_VERSION >= 0x010800 TF_ID3v23 | #endif TF_ID3v24; } /** * Get currently active tagged file features. * @return active tagged file features (TF_ID3v23, TF_ID3v24, or 0). * @see setActiveTaggedFileFeatures() */ int TagLibFile::activeTaggedFileFeatures() const { return m_activatedFeatures; } /** * Activate some features provided by the tagged file. * TagLibFile provides the TF_ID3v23 and TF_ID3v24 features, which determine * the ID3v2 version used in writeTags() (the overload without id3v2Version). * If 0 is set, the default behavior applies, i.e. for new files, * TagConfig::id3v2Version() is used, else the existing version. * * @param features TF_ID3v23, TF_ID3v24, or 0 */ void TagLibFile::setActiveTaggedFileFeatures(int features) { m_activatedFeatures = features; } /** * Read tags from file. * * @param force true to force reading even if tags were already read. */ void TagLibFile::readTags(bool force) { QString fileName = getDirname() + QDir::separator() + currentFilename(); QByteArray fn = QFile::encodeName(fileName); if (force || m_fileRef.isNull()) { #if TAGLIB_VERSION > 0x010400 && defined Q_OS_WIN32 int fnLen = fileName.length(); wchar_t* fnWs = new wchar_t[fnLen + 1]; fnWs[fnLen] = 0; fileName.toWCharArray(fnWs); m_fileRef = TagLib::FileRef(TagLib::FileName(fnWs)); delete [] fnWs; #else m_fileRef = TagLib::FileRef(fn); #endif m_tagV1 = 0; m_tagV2 = 0; markTag1Unchanged(); markTag2Unchanged(); m_fileRead = true; registerOpenFile(this); #if TAGLIB_VERSION >= 0x010700 m_pictures.clear(); m_pictures.setRead(false); #endif } TagLib::File* file; if (!m_fileRef.isNull() && (file = m_fileRef.file()) != 0) { TagLib::MPEG::File* mpegFile; TagLib::FLAC::File* flacFile; #ifdef MPC_ID3V1 TagLib::MPC::File* mpcFile; TagLib::WavPack::File* wvFile; #endif TagLib::TrueAudio::File* ttaFile; #if TAGLIB_VERSION >= 0x010700 TagLib::APE::File* apeFile; #endif m_fileExtension = QLatin1String(".mp3"); m_isTagV1Supported = false; if ((mpegFile = dynamic_cast(file)) != 0) { m_fileExtension = QLatin1String(".mp3"); m_isTagV1Supported = true; if (!m_tagV1) { m_tagV1 = mpegFile->ID3v1Tag(); markTag1Unchanged(); } if (!m_tagV2) { #if TAGLIB_VERSION >= 0x010800 m_id3v2Version = 0; TagLib::ID3v2::Tag* id3v2Tag = mpegFile->ID3v2Tag(); if (id3v2Tag && !id3v2Tag->isEmpty()) { TagLib::ID3v2::Header* header = id3v2Tag->header(); if (header) { m_id3v2Version = header->majorVersion(); } } m_tagV2 = id3v2Tag; #else m_tagV2 = mpegFile->ID3v2Tag(); #endif markTag2Unchanged(); } } else if ((flacFile = dynamic_cast(file)) != 0) { m_fileExtension = QLatin1String(".flac"); m_isTagV1Supported = true; if (!m_tagV1) { m_tagV1 = flacFile->ID3v1Tag(); markTag1Unchanged(); } if (!m_tagV2) { m_tagV2 = flacFile->xiphComment(); markTag2Unchanged(); } #if TAGLIB_VERSION >= 0x010700 if (!m_pictures.isRead()) { TagLib::List pics(flacFile->pictureList()); int i = 0; for (TagLib::List::ConstIterator it = pics.begin(); it != pics.end(); ++it) { PictureFrame frame; flacPictureToFrame(*it, frame); frame.setIndex(i++); m_pictures.append(frame); } m_pictures.setRead(true); } #endif #ifdef MPC_ID3V1 } else if ((mpcFile = dynamic_cast(file)) != 0) { m_fileExtension = QLatin1String(".mpc"); m_isTagV1Supported = true; if (!m_tagV1) { m_tagV1 = mpcFile->ID3v1Tag(); markTag1Unchanged(); } if (!m_tagV2) { m_tagV2 = mpcFile->APETag(); markTag2Unchanged(); } } else if ((wvFile = dynamic_cast(file)) != 0) { m_fileExtension = QLatin1String(".wv"); m_isTagV1Supported = true; if (!m_tagV1) { m_tagV1 = wvFile->ID3v1Tag(); markTag1Unchanged(); } if (!m_tagV2) { m_tagV2 = wvFile->APETag(); markTag2Unchanged(); } #endif } else if ((ttaFile = dynamic_cast(file)) != 0) { m_fileExtension = QLatin1String(".tta"); m_isTagV1Supported = true; if (!m_tagV1) { m_tagV1 = ttaFile->ID3v1Tag(); markTag1Unchanged(); } if (!m_tagV2) { m_tagV2 = ttaFile->ID3v2Tag(); markTag2Unchanged(); } #if TAGLIB_VERSION >= 0x010700 } else if ((apeFile = dynamic_cast(file)) != 0) { m_fileExtension = QLatin1String(".ape"); m_isTagV1Supported = true; if (!m_tagV1) { m_tagV1 = apeFile->ID3v1Tag(); markTag1Unchanged(); } if (!m_tagV2) { m_tagV2 = apeFile->APETag(); markTag2Unchanged(); } #endif } else { if (dynamic_cast(file) != 0) { m_fileExtension = QLatin1String(".ogg"); } else if (dynamic_cast(file) != 0) { m_fileExtension = QLatin1String(".spx"); #ifndef MPC_ID3V1 } else if (dynamic_cast(file) != 0) { m_fileExtension = QLatin1String(".mpc"); } else if (dynamic_cast(file) != 0) { m_fileExtension = QLatin1String(".wv"); #endif #if TAGLIB_VERSION >= 0x010600 #ifdef TAGLIB_WITH_MP4 } else if (dynamic_cast(file) != 0) { m_fileExtension = QLatin1String(".m4a"); #endif #ifdef TAGLIB_WITH_ASF } else if (dynamic_cast(file) != 0) { m_fileExtension = QLatin1String(".wma"); #endif } else if (dynamic_cast(file) != 0) { m_fileExtension = QLatin1String(".aiff"); } else if (dynamic_cast(file) != 0) { m_fileExtension = QLatin1String(".wav"); #endif #if TAGLIB_VERSION >= 0x010800 } else if (dynamic_cast(file) != 0) { m_fileExtension = QLatin1String(".mod"); } else if (dynamic_cast(file) != 0) { m_fileExtension = QLatin1String(".s3m"); } else if (dynamic_cast(file) != 0) { m_fileExtension = QLatin1String(".it"); #ifdef HAVE_TAGLIB_XM_SUPPORT } else if (dynamic_cast(file) != 0) { m_fileExtension = QLatin1String(".xm"); #endif #endif #if TAGLIB_VERSION >= 0x010900 } else if (dynamic_cast(file) != 0) { m_fileExtension = QLatin1String(".opus"); #endif } m_tagV1 = 0; markTag1Unchanged(); if (!m_tagV2) { m_tagV2 = m_fileRef.tag(); markTag2Unchanged(); } } } // Cache information, so that it is available after file is closed. m_tagInformationRead = true; m_hasTagV1 = m_tagV1 && !m_tagV1->isEmpty(); m_hasTagV2 = m_tagV2 && !m_tagV2->isEmpty(); m_tagFormatV1 = getTagFormat(m_tagV1, m_tagTypeV1); m_tagFormatV2 = getTagFormat(m_tagV2, m_tagTypeV2); readAudioProperties(); if (force) { setFilename(currentFilename()); } } /** * Close file handle. * TagLib keeps the file handle open until the FileRef is destroyed. * This causes problems when the operating system has a limited number of * open file handles. This method closes the file by assigning a new file * reference. Note that this will also invalidate the tag pointers. * The file is only closed if there are no unsaved tag changes or if the * @a force parameter is set. * * @param force true to close the file even if tags are changed */ void TagLibFile::closeFile(bool force) { if (force || (!isTag1Changed() && !isTag2Changed())) { m_fileRef = TagLib::FileRef(); m_tagV1 = 0; m_tagV2 = 0; m_fileRead = false; deregisterOpenFile(this); } } /** * Make sure that file is open. * This method should be called before accessing m_fileRef, m_tagV1, m_tagV2. * * @param force true to force reopening of file even if it is already open */ void TagLibFile::makeFileOpen(bool force) { if (!m_fileRead || force) { readTags(force); } } /** * Write tags to file and rename it if necessary. * * @param force true to force writing even if file was not changed. * @param renamed will be set to true if the file was renamed, * i.e. the file name is no longer valid, else *renamed * is left unchanged * @param preserve true to preserve file time stamps * * @return true if ok, false if the file could not be written or renamed. */ bool TagLibFile::writeTags(bool force, bool* renamed, bool preserve) { int id3v2Version; if (m_activatedFeatures & TF_ID3v24) id3v2Version = 4; else if (m_activatedFeatures & TF_ID3v23) id3v2Version = 3; else id3v2Version = 0; return writeTags(force, renamed, preserve, id3v2Version); } /** * Write tags to file and rename it if necessary. * * @param force true to force writing even if file was not changed. * @param renamed will be set to true if the file was renamed, * i.e. the file name is no longer valid, else *renamed * is left unchanged * @param preserve true to preserve file time stamps * @param id3v2Version ID3v2 version to use, 0 to use existing or preferred, * 3 to force ID3v2.3.0, 4 to force ID3v2.4.0. Is ignored * if TagLib version is less than 1.8.0. * * @return true if ok, false if the file could not be written or renamed. */ bool TagLibFile::writeTags(bool force, bool* renamed, bool preserve, int id3v2Version) { QString fnStr(getDirname() + QDir::separator() + currentFilename()); if (isChanged() && !QFileInfo(fnStr).isWritable()) { return false; } // store time stamp if it has to be preserved QByteArray fn; bool setUtime = false; struct utimbuf times; if (preserve) { fn = QFile::encodeName(fnStr); struct stat fileStat; if (::stat(fn, &fileStat) == 0) { times.actime = fileStat.st_atime; times.modtime = fileStat.st_mtime; setUtime = true; } } bool fileChanged = false; TagLib::File* file; if (!m_fileRef.isNull() && (file = m_fileRef.file()) != 0) { TagLib::MPEG::File* mpegFile = dynamic_cast(file); if (mpegFile) { if (m_tagV1 && (force || isTag1Changed()) && m_tagV1->isEmpty()) { mpegFile->strip(TagLib::MPEG::File::ID3v1); fileChanged = true; markTag1Unchanged(); m_tagV1 = 0; } if (m_tagV2 && (force || isTag2Changed()) && m_tagV2->isEmpty()) { mpegFile->strip(TagLib::MPEG::File::ID3v2); fileChanged = true; markTag2Unchanged(); m_tagV2 = 0; } int saveMask = 0; if (m_tagV1 && (force || isTag1Changed()) && !m_tagV1->isEmpty()) { saveMask |= TagLib::MPEG::File::ID3v1; } if (m_tagV2 && (force || isTag2Changed()) && !m_tagV2->isEmpty()) { saveMask |= TagLib::MPEG::File::ID3v2; } if (saveMask != 0) { #if TAGLIB_VERSION >= 0x010800 if (id3v2Version == 3 || id3v2Version == 4) { m_id3v2Version = id3v2Version; } if (m_id3v2Version != 3 && m_id3v2Version != 4) { m_id3v2Version = TagConfig::instance().id3v2Version() == TagConfig::ID3v2_3_0 ? 3 : 4; } #else Q_UNUSED(id3v2Version); #endif if (mpegFile->save(saveMask, false #if TAGLIB_VERSION >= 0x010800 , m_id3v2Version #endif #if TAGLIB_VERSION >= 0x010900 , false #endif )) { fileChanged = true; if (saveMask & TagLib::MPEG::File::ID3v1) { markTag1Unchanged(); } if (saveMask & TagLib::MPEG::File::ID3v2) { markTag2Unchanged(); } } } } else { if ((m_tagV2 && (force || isTag2Changed())) || (m_tagV1 && (force || isTag1Changed()))) { TagLib::TrueAudio::File* ttaFile = dynamic_cast(file); #if TAGLIB_VERSION >= 0x010700 TagLib::APE::File* apeFile = dynamic_cast(file); #endif #ifndef MPC_ID3V1 // it does not work if there is also an ID3 tag (bug in TagLib?) TagLib::MPC::File* mpcFile = dynamic_cast(file); TagLib::WavPack::File* wvFile = dynamic_cast(file); if (mpcFile) { mpcFile->remove(TagLib::MPC::File::ID3v1 | TagLib::MPC::File::ID3v2); fileChanged = true; } else if (wvFile) { wvFile->strip(TagLib::WavPack::File::ID3v1); fileChanged = true; } else #endif if (ttaFile) { if (m_tagV1 && (force || isTag1Changed()) && m_tagV1->isEmpty()) { ttaFile->strip(TagLib::TrueAudio::File::ID3v1); fileChanged = true; markTag1Unchanged(); m_tagV1 = 0; } if (m_tagV2 && (force || isTag2Changed()) && m_tagV2->isEmpty()) { ttaFile->strip(TagLib::TrueAudio::File::ID3v2); fileChanged = true; markTag2Unchanged(); m_tagV2 = 0; } } #if TAGLIB_VERSION >= 0x010700 if (apeFile) { if (m_tagV1 && (force || isTag1Changed()) && m_tagV1->isEmpty()) { apeFile->strip(TagLib::APE::File::ID3v1); fileChanged = true; markTag1Unchanged(); m_tagV1 = 0; } if (m_tagV2 && (force || isTag2Changed()) && m_tagV2->isEmpty()) { apeFile->strip(TagLib::APE::File::APE); fileChanged = true; markTag2Unchanged(); m_tagV2 = 0; } } #endif #if TAGLIB_VERSION >= 0x010700 if (TagLib::FLAC::File* flacFile = dynamic_cast(file)) { flacFile->removePictures(); foreach (const Frame& frame, m_pictures) { TagLib::FLAC::Picture* pic = new TagLib::FLAC::Picture; frameToFlacPicture(frame, pic); flacFile->addPicture(pic); } } #endif if (m_fileRef.save()) { fileChanged = true; markTag1Unchanged(); markTag2Unchanged(); } } } } // If the file was changed, make sure it is written to disk. // This is done when the file is closed. Later the file is opened again. // If the file is not properly closed, doubled tags can be // written if the file is finally closed! // This can be reproduced with an untagged MP3 file, then add // an ID3v2 title, save, add an ID3v2 artist, save, reload // => double ID3v2 tags. // On Windows it is necessary to close the file before renaming it, // so it is done even if the file is not changed. #ifndef Q_OS_WIN32 if (fileChanged) #endif closeFile(true); // restore time stamp if (setUtime) { ::utime(fn, ×); } if (getFilename() != currentFilename()) { if (!renameFile(currentFilename(), getFilename())) { return false; } updateCurrentFilename(); *renamed = true; } #ifndef Q_OS_WIN32 if (fileChanged) #endif makeFileOpen(true); return true; } /** * Remove ID3v1 frames. * * @param flt filter specifying which frames to remove */ void TagLibFile::deleteFramesV1(const FrameFilter& flt) { makeFileOpen(); if (m_tagV1) { TaggedFile::deleteFramesV1(flt); } } /** * Get ID3v1 title. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ QString TagLibFile::getTitleV1() { makeFileOpen(); if (m_tagV1) { TagLib::String str = m_tagV1->title(); return str.isNull() ? QLatin1String("") : TStringToQString(str); } else { return QString(); } } /** * Get ID3v1 artist. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ QString TagLibFile::getArtistV1() { makeFileOpen(); if (m_tagV1) { TagLib::String str = m_tagV1->artist(); return str.isNull() ? QLatin1String("") : TStringToQString(str); } else { return QString(); } } /** * Get ID3v1 album. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ QString TagLibFile::getAlbumV1() { makeFileOpen(); if (m_tagV1) { TagLib::String str = m_tagV1->album(); return str.isNull() ? QLatin1String("") : TStringToQString(str); } else { return QString(); } } /** * Get ID3v1 comment. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ QString TagLibFile::getCommentV1() { makeFileOpen(); if (m_tagV1) { TagLib::String str = m_tagV1->comment(); if (str.isNull()) { return QLatin1String(""); } else { QString qstr(TStringToQString(str)); qstr.truncate(28); return qstr; } } else { return QString(); } } /** * Get ID3v1 year. * * @return number, * 0 if the field does not exist, * -1 if the tags do not exist. */ int TagLibFile::getYearV1() { makeFileOpen(); if (m_tagV1) { return m_tagV1->year(); } else { return -1; } } /** * Get ID3v1 track. * * @return number, * 0 if the field does not exist, * -1 if the tags do not exist. */ int TagLibFile::getTrackNumV1() { makeFileOpen(); if (m_tagV1) { return m_tagV1->track(); } else { return -1; } } /** * Get ID3v1 genre. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ QString TagLibFile::getGenreV1() { makeFileOpen(); if (m_tagV1) { TagLib::String str = m_tagV1->genre(); return str.isNull() ? QLatin1String("") : TStringToQString(str); } else { return QString(); } } /** * Get ID3v2 title. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ QString TagLibFile::getTitleV2() { makeFileOpen(); if (m_tagV2) { TagLib::String str = m_tagV2->title(); return str.isNull() ? QLatin1String("") : TStringToQString(str); } else { return QString(); } } /** * Get ID3v2 artist. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ QString TagLibFile::getArtistV2() { makeFileOpen(); if (m_tagV2) { TagLib::String str = m_tagV2->artist(); return str.isNull() ? QLatin1String("") : TStringToQString(str); } else { return QString(); } } /** * Get ID3v2 album. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ QString TagLibFile::getAlbumV2() { makeFileOpen(); if (m_tagV2) { TagLib::String str = m_tagV2->album(); return str.isNull() ? QLatin1String("") : TStringToQString(str); } else { return QString(); } } /** * Get ID3v2 comment. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ QString TagLibFile::getCommentV2() { makeFileOpen(); if (m_tagV2) { TagLib::String str = m_tagV2->comment(); return str.isNull() ? QLatin1String("") : TStringToQString(str); } else { return QString(); } } /** * Get ID3v2 year. * * @return number, * 0 if the field does not exist, * -1 if the tags do not exist. */ int TagLibFile::getYearV2() { makeFileOpen(); if (m_tagV2) { return m_tagV2->year(); } else { return -1; } } /** * Get ID3v2 track. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ QString TagLibFile::getTrackV2() { makeFileOpen(); if (m_tagV2) { int nr = m_tagV2->track(); if (nr == 0) return QLatin1String(""); else return QString::number(nr); } else { return QString(); } } /** * Get a genre string from a string which can contain the genre itself, * or only the genre number or the genre number in parenthesis. * * @param str genre string * * @return genre. */ static QString getGenreString(const TagLib::String& str) { if (!str.isNull()) { QString qs = TStringToQString(str); int cpPos = 0, n = 0xff; bool ok = false; if (qs[0] == QLatin1Char('(') && (cpPos = qs.indexOf(QLatin1Char(')'), 2)) > 1) { n = qs.mid(1, cpPos - 1).toInt(&ok); if (!ok || n > 0xff) { n = 0xff; } return QString::fromLatin1(Genres::getName(n)); } else if ((n = qs.toInt(&ok)) >= 0 && n <= 0xff && ok) { return QString::fromLatin1(Genres::getName(n)); } else { return qs; } } else { return QLatin1String(""); } } /** * Get ID3v2 genre as text. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ QString TagLibFile::getGenreV2() { makeFileOpen(); if (m_tagV2) { return getGenreString(m_tagV2->genre()); } else { return QString(); } } /** * Create m_tagV1 if it does not already exists so that it can be set. * * @return true if m_tagV1 can be set. */ bool TagLibFile::makeTagV1Settable() { makeFileOpen(); if (!m_tagV1) { TagLib::File* file; if (!m_fileRef.isNull() && (file = m_fileRef.file()) != 0) { TagLib::MPEG::File* mpegFile; TagLib::FLAC::File* flacFile; #ifdef MPC_ID3V1 TagLib::MPC::File* mpcFile; TagLib::WavPack::File* wvFile; #endif TagLib::TrueAudio::File* ttaFile; #if TAGLIB_VERSION >= 0x010700 TagLib::APE::File* apeFile; #endif if ((mpegFile = dynamic_cast(file)) != 0) { m_tagV1 = mpegFile->ID3v1Tag(true); } else if ((flacFile = dynamic_cast(file)) != 0) { m_tagV1 = flacFile->ID3v1Tag(true); #ifdef MPC_ID3V1 } else if ((mpcFile = dynamic_cast(file)) != 0) { m_tagV1 = mpcFile->ID3v1Tag(true); } else if ((wvFile = dynamic_cast(file)) != 0) { m_tagV1 = wvFile->ID3v1Tag(true); #endif } else if ((ttaFile = dynamic_cast(file)) != 0) { m_tagV1 = ttaFile->ID3v1Tag(true); #if TAGLIB_VERSION >= 0x010700 } else if ((apeFile = dynamic_cast(file)) != 0) { m_tagV1 = apeFile->ID3v1Tag(true); #endif } } } return (m_tagV1 != 0); } /** * Create m_tagV2 if it does not already exist so that it can be set. * * @return true if m_tagV2 can be set. */ bool TagLibFile::makeTagV2Settable() { makeFileOpen(); if (!m_tagV2) { TagLib::File* file; if (!m_fileRef.isNull() && (file = m_fileRef.file()) != 0) { TagLib::MPEG::File* mpegFile; TagLib::FLAC::File* flacFile; TagLib::MPC::File* mpcFile; TagLib::WavPack::File* wvFile; TagLib::TrueAudio::File* ttaFile; #if TAGLIB_VERSION >= 0x010700 TagLib::APE::File* apeFile; #endif if ((mpegFile = dynamic_cast(file)) != 0) { m_tagV2 = mpegFile->ID3v2Tag(true); } else if ((flacFile = dynamic_cast(file)) != 0) { m_tagV2 = flacFile->xiphComment(true); } else if ((mpcFile = dynamic_cast(file)) != 0) { m_tagV2 = mpcFile->APETag(true); } else if ((wvFile = dynamic_cast(file)) != 0) { m_tagV2 = wvFile->APETag(true); } else if ((ttaFile = dynamic_cast(file)) != 0) { m_tagV2 = ttaFile->ID3v2Tag(true); #if TAGLIB_VERSION >= 0x010700 } else if ((apeFile = dynamic_cast(file)) != 0) { m_tagV2 = apeFile->APETag(true); #endif } } } return (m_tagV2 != 0); } /** * Set ID3v1 title. * * @param str string to set, "" to remove field. */ void TagLibFile::setTitleV1(const QString& str) { if (makeTagV1Settable() && !str.isNull()) { TagLib::String tstr = str.isEmpty() ? TagLib::String::null : QSTRING_TO_TSTRING(str); if (!(tstr == m_tagV1->title())) { QString s = checkTruncation(str, 1ULL << Frame::FT_Title); if (!s.isNull()) m_tagV1->setTitle(QSTRING_TO_TSTRING(s)); else m_tagV1->setTitle(tstr); markTag1Changed(Frame::FT_Title); } } } /** * Set ID3v1 artist. * * @param str string to set, "" to remove field. */ void TagLibFile::setArtistV1(const QString& str) { if (makeTagV1Settable() && !str.isNull()) { TagLib::String tstr = str.isEmpty() ? TagLib::String::null : QSTRING_TO_TSTRING(str); if (!(tstr == m_tagV1->artist())) { QString s = checkTruncation(str, 1ULL << Frame::FT_Artist); if (!s.isNull()) m_tagV1->setArtist(QSTRING_TO_TSTRING(s)); else m_tagV1->setArtist(tstr); markTag1Changed(Frame::FT_Artist); } } } /** * Set ID3v1 album. * * @param str string to set, "" to remove field. */ void TagLibFile::setAlbumV1(const QString& str) { if (makeTagV1Settable() && !str.isNull()) { TagLib::String tstr = str.isEmpty() ? TagLib::String::null : QSTRING_TO_TSTRING(str); if (!(tstr == m_tagV1->album())) { QString s = checkTruncation(str, 1ULL << Frame::FT_Album); if (!s.isNull()) m_tagV1->setAlbum(QSTRING_TO_TSTRING(s)); else m_tagV1->setAlbum(tstr); markTag1Changed(Frame::FT_Album); } } } /** * Set ID3v1 comment. * * @param str string to set, "" to remove field. */ void TagLibFile::setCommentV1(const QString& str) { if (makeTagV1Settable() && !str.isNull()) { TagLib::String tstr = str.isEmpty() ? TagLib::String::null : QSTRING_TO_TSTRING(str); if (!(tstr == m_tagV1->comment())) { QString s = checkTruncation(str, 1ULL << Frame::FT_Comment, 28); if (!s.isNull()) m_tagV1->setComment(QSTRING_TO_TSTRING(s)); else m_tagV1->setComment(tstr); markTag1Changed(Frame::FT_Comment); } } } /** * Set ID3v1 year. * * @param num number to set, 0 to remove field. */ void TagLibFile::setYearV1(int num) { if (makeTagV1Settable() && num >= 0) { if (num != static_cast(m_tagV1->year())) { m_tagV1->setYear(num); markTag1Changed(Frame::FT_Date); } } } /** * Set ID3v1 track. * * @param num number to set, 0 to remove field. */ void TagLibFile::setTrackNumV1(int num) { if (makeTagV1Settable() && num >= 0) { if (num != static_cast(m_tagV1->track())) { int n = checkTruncation(num, 1ULL << Frame::FT_Track); if (n != -1) m_tagV1->setTrack(n); else m_tagV1->setTrack(num); markTag1Changed(Frame::FT_Track); } } } /** * Set ID3v1 genre as text. * * @param str string to set, "" to remove field, QString::null to ignore. */ void TagLibFile::setGenreV1(const QString& str) { if (makeTagV1Settable() && !str.isNull()) { TagLib::String tstr = str.isEmpty() ? TagLib::String::null : QSTRING_TO_TSTRING(str); if (!(tstr == m_tagV1->genre())) { m_tagV1->setGenre(tstr); markTag1Changed(Frame::FT_Genre); } // if the string cannot be converted to a number, set the truncation flag checkTruncation(!str.isEmpty() && Genres::getNumber(str) == 0xff ? 1 : 0, 1ULL << Frame::FT_Genre, 0); } } /** * Check if string needs Unicode encoding. * * @return true if Unicode needed, * false if Latin-1 sufficient. */ static bool needsUnicode(const QString& qstr) { bool result = false; uint unicodeSize = qstr.length(); const QChar* qcarray = qstr.unicode(); for (uint i = 0; i < unicodeSize; ++i) { char ch = qcarray[i].toLatin1(); if (ch == 0 || (ch & 0x80) != 0) { result = true; break; } } return result; } /** * Get the configured text encoding. * * @param unicode true if unicode is required * * @return text encoding. */ static TagLib::String::Type getTextEncodingConfig(bool unicode) { TagLib::String::Type enc = TagLibFile::getDefaultTextEncoding(); if (unicode && enc == TagLib::String::Latin1) { enc = TagLib::String::UTF8; } return enc; } /** * Remove the first COMM frame with an empty description. * * @param id3v2Tag ID3v2 tag */ static void removeCommentFrame(TagLib::ID3v2::Tag* id3v2Tag) { const TagLib::ID3v2::FrameList& frameList = id3v2Tag->frameList("COMM"); for (TagLib::ID3v2::FrameList::ConstIterator it = frameList.begin(); it != frameList.end(); ++it) { TagLib::ID3v2::CommentsFrame* id3Frame = dynamic_cast(*it); if (id3Frame && id3Frame->description().isEmpty()) { id3v2Tag->removeFrame(id3Frame, true); break; } } } /** * Write a Unicode field if the tag is ID3v2 and Latin-1 is not sufficient. * * @param tag tag * @param qstr text as QString * @param tstr text as TagLib::String * @param frameId ID3v2 frame ID * * @return true if an ID3v2 Unicode field was written. */ bool setId3v2Unicode(TagLib::Tag* tag, const QString& qstr, const TagLib::String& tstr, const char* frameId) { TagLib::ID3v2::Tag* id3v2Tag; if (tag && (id3v2Tag = dynamic_cast(tag)) != 0) { // first check if this string needs to be stored as unicode TagLib::String::Type enc = getTextEncodingConfig(needsUnicode(qstr)); TagLib::ByteVector id(frameId); if (enc != TagLib::String::Latin1 || id == "COMM") { if (id == "COMM") { removeCommentFrame(id3v2Tag); } else { id3v2Tag->removeFrames(id); } if (!tstr.isEmpty()) { TagLib::ID3v2::Frame* frame; if (frameId[0] != 'C') { frame = new TagLib::ID3v2::TextIdentificationFrame(id, enc); } else { TagLib::ID3v2::CommentsFrame* commFrame = new TagLib::ID3v2::CommentsFrame(enc); frame = commFrame; commFrame->setLanguage("eng"); // for compatibility with iTunes } frame->setText(tstr); #ifdef Q_OS_WIN32 // freed in Windows DLL => must be allocated in the same DLL TagLib::ID3v2::Frame* dllAllocatedFrame = TagLib::ID3v2::FrameFactory::instance()->createFrame(frame->render()); if (dllAllocatedFrame) { id3v2Tag->addFrame(dllAllocatedFrame); } delete frame; #else id3v2Tag->addFrame(frame); #endif } return true; } } return false; } /** * Set ID3v2 title. * * @param str string to set, "" to remove field. */ void TagLibFile::setTitleV2(const QString& str) { if (makeTagV2Settable() && !str.isNull()) { TagLib::String tstr = str.isEmpty() ? TagLib::String::null : QSTRING_TO_TSTRING(str); if (!(tstr == m_tagV2->title())) { if (!setId3v2Unicode(m_tagV2, str, tstr, "TIT2")) { m_tagV2->setTitle(tstr); } markTag2Changed(Frame::FT_Title); } } } /** * Set ID3v2 artist. * * @param str string to set, "" to remove field. */ void TagLibFile::setArtistV2(const QString& str) { if (makeTagV2Settable() && !str.isNull()) { TagLib::String tstr = str.isEmpty() ? TagLib::String::null : QSTRING_TO_TSTRING(str); if (!(tstr == m_tagV2->artist())) { if (!setId3v2Unicode(m_tagV2, str, tstr, "TPE1")) { m_tagV2->setArtist(tstr); } markTag2Changed(Frame::FT_Artist); } } } /** * Set ID3v2 album. * * @param str string to set, "" to remove field. */ void TagLibFile::setAlbumV2(const QString& str) { if (makeTagV2Settable() && !str.isNull()) { TagLib::String tstr = str.isEmpty() ? TagLib::String::null : QSTRING_TO_TSTRING(str); if (!(tstr == m_tagV2->album())) { if (!setId3v2Unicode(m_tagV2, str, tstr, "TALB")) { m_tagV2->setAlbum(tstr); } markTag2Changed(Frame::FT_Album); } } } /** * Set ID3v2 comment. * * @param str string to set, "" to remove field. */ void TagLibFile::setCommentV2(const QString& str) { if (makeTagV2Settable() && !str.isNull()) { TagLib::String tstr = str.isEmpty() ? TagLib::String::null : QSTRING_TO_TSTRING(str); if (!(tstr == m_tagV2->comment())) { if (!setId3v2Unicode(m_tagV2, str, tstr, "COMM")) { m_tagV2->setComment(tstr); } markTag2Changed(Frame::FT_Comment); } } } /** * Set ID3v2 year. * * @param num number to set, 0 to remove field. */ void TagLibFile::setYearV2(int num) { if (makeTagV2Settable() && num >= 0) { if (num != static_cast(m_tagV2->year())) { if (getDefaultTextEncoding() == TagLib::String::Latin1) { m_tagV2->setYear(num); } else { QString str; if (num != 0) { str.setNum(num); } else { str = QLatin1String(""); } TagLib::String tstr = str.isEmpty() ? TagLib::String::null : QSTRING_TO_TSTRING(str); if (!setId3v2Unicode(m_tagV2, str, tstr, "TDRC")) { m_tagV2->setYear(num); } } markTag2Changed(Frame::FT_Date); } } } /** * Set ID3v2 track. * * @param track string to set, "" to remove field, QString::null to ignore. */ void TagLibFile::setTrackV2(const QString& track) { int numTracks; int num = splitNumberAndTotal(track, &numTracks); if (makeTagV2Settable() && num >= 0) { QString str = trackNumberString(num, numTracks); if (num != static_cast(m_tagV2->track())) { TagLib::ID3v2::Tag* id3v2Tag = dynamic_cast(m_tagV2); if (id3v2Tag) { TagLib::String tstr = str.isEmpty() ? TagLib::String::null : QSTRING_TO_TSTRING(str); if (!setId3v2Unicode(m_tagV2, str, tstr, "TRCK")) { TagLib::ID3v2::TextIdentificationFrame* frame = new TagLib::ID3v2::TextIdentificationFrame( "TRCK", getDefaultTextEncoding()); frame->setText(tstr); id3v2Tag->removeFrames("TRCK"); #ifdef Q_OS_WIN32 // freed in Windows DLL => must be allocated in the same DLL TagLib::ID3v2::Frame* dllAllocatedFrame = TagLib::ID3v2::FrameFactory::instance()->createFrame(frame->render()); if (dllAllocatedFrame) { id3v2Tag->addFrame(dllAllocatedFrame); } delete frame; #else id3v2Tag->addFrame(frame); #endif } } else { m_tagV2->setTrack(num); } markTag2Changed(Frame::FT_Track); } } } /** * Set ID3v2 genre as text. * * @param str string to set, "" to remove field, QString::null to ignore. */ void TagLibFile::setGenreV2(const QString& str) { if (makeTagV2Settable() && !str.isNull()) { TagLib::String tstr = str.isEmpty() ? TagLib::String::null : QSTRING_TO_TSTRING(str); if (!(tstr == m_tagV2->genre())) { if (!setId3v2Unicode(m_tagV2, str, tstr, "TCON")) { TagLib::ID3v2::TextIdentificationFrame* frame; TagLib::ID3v2::Tag* id3v2Tag = dynamic_cast(m_tagV2); if (id3v2Tag && TagConfig::instance().genreNotNumeric() && (frame = new TagLib::ID3v2::TextIdentificationFrame( "TCON", getDefaultTextEncoding())) != 0) { frame->setText(tstr); id3v2Tag->removeFrames("TCON"); #ifdef Q_OS_WIN32 // freed in Windows DLL => must be allocated in the same DLL TagLib::ID3v2::Frame* dllAllocatedFrame = TagLib::ID3v2::FrameFactory::instance()->createFrame(frame->render()); if (dllAllocatedFrame) { id3v2Tag->addFrame(dllAllocatedFrame); } delete frame; #else id3v2Tag->addFrame(frame); #endif } else { m_tagV2->setGenre(tstr); } } markTag2Changed(Frame::FT_Genre); } } } /** * Check if tag information has already been read. * * @return true if information is available, * false if the tags have not been read yet, in which case * hasTagV1() and hasTagV2() do not return meaningful information. */ bool TagLibFile::isTagInformationRead() const { return m_tagInformationRead; } /** * Check if file has an ID3v1 tag. * * @return true if a V1 tag is available. * @see isTagInformationRead() */ bool TagLibFile::hasTagV1() const { return m_hasTagV1; } /** * Check if ID3v1 tags are supported by the format of this file. * * @return true. */ bool TagLibFile::isTagV1Supported() const { return m_isTagV1Supported; } /** * Check if file has an ID3v2 tag. * * @return true if a V2 tag is available. * @see isTagInformationRead() */ bool TagLibFile::hasTagV2() const { return m_hasTagV2; } /** * Get technical detail information. * * @param info the detail information is returned here */ void TagLibFile::getDetailInfo(DetailInfo& info) const { info = m_detailInfo; } /** * Cache technical detail information. */ void TagLibFile::readAudioProperties() { TagLib::AudioProperties* audioProperties; if (!m_fileRef.isNull() && (audioProperties = m_fileRef.audioProperties()) != 0) { TagLib::MPEG::Properties* mpegProperties; TagLib::Ogg::Speex::Properties* speexProperties; TagLib::TrueAudio::Properties* ttaProperties; TagLib::WavPack::Properties* wvProperties; #if TAGLIB_VERSION >= 0x010700 TagLib::APE::Properties* apeProperties; #endif #if TAGLIB_VERSION >= 0x010800 TagLib::Mod::Properties* modProperties; TagLib::S3M::Properties* s3mProperties; TagLib::IT::Properties* itProperties; #ifdef HAVE_TAGLIB_XM_SUPPORT TagLib::XM::Properties* xmProperties; #endif #endif #if TAGLIB_VERSION >= 0x010900 TagLib::Ogg::Opus::Properties* opusProperties; #endif m_detailInfo.valid = true; if ((mpegProperties = dynamic_cast(audioProperties)) != 0) { if (getFilename().right(4).toLower() == QLatin1String(".aac")) { m_detailInfo.format = QLatin1String("AAC"); return; } switch (mpegProperties->version()) { case TagLib::MPEG::Header::Version1: m_detailInfo.format = QLatin1String("MPEG 1 "); break; case TagLib::MPEG::Header::Version2: m_detailInfo.format = QLatin1String("MPEG 2 "); break; case TagLib::MPEG::Header::Version2_5: m_detailInfo.format = QLatin1String("MPEG 2.5 "); break; } int layer = mpegProperties->layer(); if (layer >= 1 && layer <= 3) { m_detailInfo.format += QLatin1String("Layer "); m_detailInfo.format += QString::number(layer); } switch (mpegProperties->channelMode()) { case TagLib::MPEG::Header::Stereo: m_detailInfo.channelMode = DetailInfo::CM_Stereo; m_detailInfo.channels = 2; break; case TagLib::MPEG::Header::JointStereo: m_detailInfo.channelMode = DetailInfo::CM_JointStereo; m_detailInfo.channels = 2; break; case TagLib::MPEG::Header::DualChannel: m_detailInfo.channels = 2; break; case TagLib::MPEG::Header::SingleChannel: m_detailInfo.channels = 1; break; } } else if (dynamic_cast(audioProperties) != 0) { m_detailInfo.format = QLatin1String("Ogg Vorbis"); } else if (dynamic_cast(audioProperties) != 0) { m_detailInfo.format = QLatin1String("FLAC"); } else if (dynamic_cast(audioProperties) != 0) { m_detailInfo.format = QLatin1String("MPC"); } else if ((speexProperties = dynamic_cast(audioProperties)) != 0) { m_detailInfo.format = QString(QLatin1String("Speex %1")).arg(speexProperties->speexVersion()); } else if ((ttaProperties = dynamic_cast(audioProperties)) != 0) { m_detailInfo.format = QLatin1String("True Audio "); m_detailInfo.format += QString::number(ttaProperties->ttaVersion()); m_detailInfo.format += QLatin1Char(' '); m_detailInfo.format += QString::number(ttaProperties->bitsPerSample()); m_detailInfo.format += QLatin1String(" bit"); } else if ((wvProperties = dynamic_cast(audioProperties)) != 0) { m_detailInfo.format = QLatin1String("WavPack "); m_detailInfo.format += QString::number(wvProperties->version(), 16); m_detailInfo.format += QLatin1Char(' '); m_detailInfo.format += QString::number(wvProperties->bitsPerSample()); m_detailInfo.format += QLatin1String(" bit"); #if TAGLIB_VERSION >= 0x010600 #ifdef TAGLIB_WITH_MP4 } else if (dynamic_cast(audioProperties) != 0) { m_detailInfo.format = QLatin1String("MP4"); #endif #ifdef TAGLIB_WITH_ASF } else if (dynamic_cast(audioProperties) != 0) { m_detailInfo.format = QLatin1String("ASF"); #endif } else if (dynamic_cast(audioProperties) != 0) { m_detailInfo.format = QLatin1String("AIFF"); } else if (dynamic_cast(audioProperties) != 0) { m_detailInfo.format = QLatin1String("WAV"); #endif #if TAGLIB_VERSION >= 0x010700 } else if ((apeProperties = dynamic_cast(audioProperties)) != 0) { m_detailInfo.format = QString(QLatin1String("APE %1.%2 %3 bit")). arg(apeProperties->version() / 1000). arg(apeProperties->version() % 1000). arg(apeProperties->bitsPerSample()); #endif #if TAGLIB_VERSION >= 0x010800 } else if ((modProperties = dynamic_cast(audioProperties)) != 0) { m_detailInfo.format = QString(QLatin1String("Mod %1 %2 Instruments")). arg(getTrackerName()). arg(modProperties->instrumentCount()); } else if ((s3mProperties = dynamic_cast(audioProperties)) != 0) { m_detailInfo.format = QString(QLatin1String("S3M %1 V%2 T%3")). arg(getTrackerName()). arg(s3mProperties->fileFormatVersion()). arg(s3mProperties->trackerVersion(), 0, 16); m_detailInfo.channelMode = s3mProperties->stereo() ? DetailInfo::CM_Stereo : DetailInfo::CM_None; } else if ((itProperties = dynamic_cast(audioProperties)) != 0) { m_detailInfo.format = QString(QLatin1String("IT %1 V%2 %3 Instruments")). arg(getTrackerName()). arg(itProperties->version(), 0, 16). arg(itProperties->instrumentCount()); m_detailInfo.channelMode = itProperties->stereo() ? DetailInfo::CM_Stereo : DetailInfo::CM_None; #ifdef HAVE_TAGLIB_XM_SUPPORT } else if ((xmProperties = dynamic_cast(audioProperties)) != 0) { m_detailInfo.format = QString(QLatin1String("XM %1 V%2 %3 Instruments")). arg(getTrackerName()). arg(xmProperties->version(), 0, 16). arg(xmProperties->instrumentCount()); #endif #endif #if TAGLIB_VERSION >= 0x010900 } else if ((opusProperties = dynamic_cast(audioProperties)) != 0) { m_detailInfo.format = QString(QLatin1String("Opus %1")). arg(opusProperties->opusVersion()); #endif } m_detailInfo.bitrate = audioProperties->bitrate(); m_detailInfo.sampleRate = audioProperties->sampleRate(); if (audioProperties->channels() > 0) { m_detailInfo.channels = audioProperties->channels(); } m_detailInfo.duration = audioProperties->length(); } else { m_detailInfo.valid = false; } } #if TAGLIB_VERSION >= 0x010800 /** * Get tracker name of a module file. * * @return tracker name, null if not found. */ QString TagLibFile::getTrackerName() const { QString trackerName; if (TagLib::Mod::Tag* modTag = dynamic_cast(m_tagV2)) { trackerName = TStringToQString(modTag->trackerName()).trimmed(); } return trackerName; } #endif /** * Get duration of file. * * @return duration in seconds, * 0 if unknown. */ unsigned TagLibFile::getDuration() const { return m_detailInfo.valid ? m_detailInfo.duration : 0; } /** * Get file extension including the dot. * * @return file extension ".mp3". */ QString TagLibFile::getFileExtension() const { return m_fileExtension; } /** * Get the format of a tag. * * @param tag tag, 0 if no tag available * @param type the tag type is returned here * * @return string describing format of tag, * e.g. "ID3v1.1", "ID3v2.3", "Vorbis", "APE", * QString::null if unknown. */ QString TagLibFile::getTagFormat(const TagLib::Tag* tag, TagType& type) { if (tag && !tag->isEmpty()) { const TagLib::ID3v2::Tag* id3v2Tag; if (dynamic_cast(tag) != 0) { type = TT_Id3v1; return QLatin1String("ID3v1.1"); } else if ((id3v2Tag = dynamic_cast(tag)) != 0) { type = TT_Id3v2; TagLib::ID3v2::Header* header = id3v2Tag->header(); if (header) { uint majorVersion = header->majorVersion(); uint revisionNumber = header->revisionNumber(); #if (TAGLIB_VERSION <= 0x010400) // A wrong majorVersion is returned if a new ID3v2.4.0 tag is created. if (majorVersion == 0 && revisionNumber == 0) { majorVersion = 4; } #endif return QString(QLatin1String("ID3v2.%1.%2")). arg(majorVersion).arg(revisionNumber); } else { return QLatin1String("ID3v2"); } } else if (dynamic_cast(tag) != 0) { type = TT_Vorbis; return QLatin1String("Vorbis"); } else if (dynamic_cast(tag) != 0) { type = TT_Ape; return QLatin1String("APE"); #if TAGLIB_VERSION >= 0x010600 #ifdef TAGLIB_WITH_MP4 } else if (dynamic_cast(tag) != 0) { type = TT_Mp4; return QLatin1String("MP4"); #endif #ifdef TAGLIB_WITH_ASF } else if (dynamic_cast(tag) != 0) { type = TT_Asf; return QLatin1String("ASF"); #endif #endif } } type = TT_Unknown; return QString(); } /** * Get the format of tag 1. * * @return string describing format of tag 1, * e.g. "ID3v1.1", "ID3v2.3", "Vorbis", "APE", * QString::null if unknown. */ QString TagLibFile::getTagFormatV1() const { return m_tagFormatV1; } /** * Get the format of tag 2. * * @return string describing format of tag 1, * e.g. "ID3v1.1", "ID3v2.3", "Vorbis", "APE", * QString::null if unknown. */ QString TagLibFile::getTagFormatV2() const { return m_tagFormatV2; } /** Types and descriptions for id3lib frame IDs */ static const struct TypeStrOfId { Frame::Type type; const char* str; bool supported; } typeStrOfId[] = { { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "AENC - Audio encryption"), false }, { Frame::FT_Picture, QT_TRANSLATE_NOOP("@default", "APIC - Attached picture"), true }, { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "ASPI - Audio seek point index"), false }, { Frame::FT_Comment, QT_TRANSLATE_NOOP("@default", "COMM - Comments"), true }, { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "COMR - Commercial"), false }, { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "ENCR - Encryption method registration"), false }, { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "EQU2 - Equalisation (2)"), false }, { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "ETCO - Event timing codes"), false }, { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "GEOB - General encapsulated object"), true }, { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "GRID - Group identification registration"), false }, { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "LINK - Linked information"), false }, { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "MCDI - Music CD identifier"), false }, { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "MLLT - MPEG location lookup table"), false }, { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "OWNE - Ownership frame"), #if TAGLIB_VERSION >= 0x010800 true #else false #endif }, { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "PRIV - Private frame"), #if TAGLIB_VERSION >= 0x010600 true #else false #endif }, { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "PCNT - Play counter"), false }, { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "POPM - Popularimeter"), #if TAGLIB_VERSION >= 0x010600 true #else false #endif }, { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "POSS - Position synchronisation frame"), false }, { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "RBUF - Recommended buffer size"), false }, { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "RVA2 - Relative volume adjustment (2)"), false }, { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "RVRB - Reverb"), false }, { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "SEEK - Seek frame"), false }, { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "SIGN - Signature frame"), false }, { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "SYLT - Synchronized lyric/text"), false }, { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "SYTC - Synchronized tempo codes"), false }, { Frame::FT_Album, QT_TRANSLATE_NOOP("@default", "TALB - Album/Movie/Show title"), true }, { Frame::FT_Bpm, QT_TRANSLATE_NOOP("@default", "TBPM - BPM (beats per minute)"), true }, { Frame::FT_Compilation, QT_TRANSLATE_NOOP("@default", "TCMP - iTunes compilation flag"), true }, { Frame::FT_Composer, QT_TRANSLATE_NOOP("@default", "TCOM - Composer"), true }, { Frame::FT_Genre, QT_TRANSLATE_NOOP("@default", "TCON - Content type"), true }, { Frame::FT_Copyright, QT_TRANSLATE_NOOP("@default", "TCOP - Copyright message"), true }, { Frame::FT_EncodingTime, QT_TRANSLATE_NOOP("@default", "TDEN - Encoding time"), true }, { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "TDLY - Playlist delay"), true }, { Frame::FT_OriginalDate, QT_TRANSLATE_NOOP("@default", "TDOR - Original release time"), true }, { Frame::FT_Date, QT_TRANSLATE_NOOP("@default", "TDRC - Recording time"), true }, { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "TDRL - Release time"), true }, { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "TDTG - Tagging time"), true }, { Frame::FT_EncodedBy, QT_TRANSLATE_NOOP("@default", "TENC - Encoded by"), true }, { Frame::FT_Lyricist, QT_TRANSLATE_NOOP("@default", "TEXT - Lyricist/Text writer"), true }, { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "TFLT - File type"), true }, { Frame::FT_Arranger, QT_TRANSLATE_NOOP("@default", "TIPL - Involved people list"), true }, { Frame::FT_Grouping, QT_TRANSLATE_NOOP("@default", "TIT1 - Content group description"), true }, { Frame::FT_Title, QT_TRANSLATE_NOOP("@default", "TIT2 - Title/songname/content description"), true }, { Frame::FT_Subtitle, QT_TRANSLATE_NOOP("@default", "TIT3 - Subtitle/Description refinement"), true }, { Frame::FT_InitialKey, QT_TRANSLATE_NOOP("@default", "TKEY - Initial key"), true }, { Frame::FT_Language, QT_TRANSLATE_NOOP("@default", "TLAN - Language(s)"), true }, { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "TLEN - Length"), true }, { Frame::FT_Performer, QT_TRANSLATE_NOOP("@default", "TMCL - Musician credits list"), true }, { Frame::FT_Media, QT_TRANSLATE_NOOP("@default", "TMED - Media type"), true }, { Frame::FT_Mood, QT_TRANSLATE_NOOP("@default", "TMOO - Mood"), true }, { Frame::FT_OriginalAlbum, QT_TRANSLATE_NOOP("@default", "TOAL - Original album/movie/show title"), true }, { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "TOFN - Original filename"), true }, { Frame::FT_Author, QT_TRANSLATE_NOOP("@default", "TOLY - Original lyricist(s)/text writer(s)"), true }, { Frame::FT_OriginalArtist, QT_TRANSLATE_NOOP("@default", "TOPE - Original artist(s)/performer(s)"), true }, { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "TOWN - File owner/licensee"), true }, { Frame::FT_Artist, QT_TRANSLATE_NOOP("@default", "TPE1 - Lead performer(s)/Soloist(s)"), true }, { Frame::FT_AlbumArtist, QT_TRANSLATE_NOOP("@default", "TPE2 - Band/orchestra/accompaniment"), true }, { Frame::FT_Conductor, QT_TRANSLATE_NOOP("@default", "TPE3 - Conductor/performer refinement"), true }, { Frame::FT_Remixer, QT_TRANSLATE_NOOP("@default", "TPE4 - Interpreted, remixed, or otherwise modified by"), true }, { Frame::FT_Disc, QT_TRANSLATE_NOOP("@default", "TPOS - Part of a set"), true }, { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "TPRO - Produced notice"), true }, { Frame::FT_Publisher, QT_TRANSLATE_NOOP("@default", "TPUB - Publisher"), true }, { Frame::FT_Track, QT_TRANSLATE_NOOP("@default", "TRCK - Track number/Position in set"), true }, { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "TRSN - Internet radio station name"), true }, { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "TRSO - Internet radio station owner"), true }, { Frame::FT_SortAlbumArtist,QT_TRANSLATE_NOOP("@default", "TSO2 - Album artist sort order"), true }, { Frame::FT_SortAlbum, QT_TRANSLATE_NOOP("@default", "TSOA - Album sort order"), true }, { Frame::FT_SortComposer, QT_TRANSLATE_NOOP("@default", "TSOC - Composer sort order"), true }, { Frame::FT_SortArtist, QT_TRANSLATE_NOOP("@default", "TSOP - Performer sort order"), true }, { Frame::FT_SortName, QT_TRANSLATE_NOOP("@default", "TSOT - Title sort order"), true }, { Frame::FT_Isrc, QT_TRANSLATE_NOOP("@default", "TSRC - ISRC (international standard recording code)"), true }, { Frame::FT_EncoderSettings,QT_TRANSLATE_NOOP("@default", "TSSE - Software/Hardware and settings used for encoding"), true }, { Frame::FT_Part, QT_TRANSLATE_NOOP("@default", "TSST - Set subtitle"), true }, { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "TXXX - User defined text information"), true }, { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "UFID - Unique file identifier"), true }, { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "USER - Terms of use"), false }, { Frame::FT_Lyrics, QT_TRANSLATE_NOOP("@default", "USLT - Unsynchronized lyric/text transcription"), true }, { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "WCOM - Commercial information"), true }, { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "WCOP - Copyright/Legal information"), true }, { Frame::FT_WWWAudioFile, QT_TRANSLATE_NOOP("@default", "WOAF - Official audio file webpage"), true }, { Frame::FT_Website, QT_TRANSLATE_NOOP("@default", "WOAR - Official artist/performer webpage"), true }, { Frame::FT_WWWAudioSource, QT_TRANSLATE_NOOP("@default", "WOAS - Official audio source webpage"), true }, { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "WORS - Official internet radio station homepage"), true }, { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "WPAY - Payment"), true }, { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "WPUB - Official publisher webpage"), true }, { Frame::FT_Other, QT_TRANSLATE_NOOP("@default", "WXXX - User defined URL link"), true } }; /** * Get type and description of frame. * * @param id ID of frame * @param type the type is returned here * @param str the description is returned here */ static void getTypeStringForFrameId(TagLib::ByteVector id, Frame::Type& type, const char*& str) { static TagLib::Map idIndexMap; if (idIndexMap.isEmpty()) { for (unsigned i = 0; i < sizeof(typeStrOfId) / sizeof(typeStrOfId[0]); ++i) { idIndexMap.insert(TagLib::ByteVector(typeStrOfId[i].str, 4), i); } } if (idIndexMap.contains(id)) { const TypeStrOfId& ts = typeStrOfId[idIndexMap[id]]; type = ts.type; str = ts.str; } else { type = Frame::FT_UnknownFrame; str = "????"; } } /** * Get string description starting with 4 bytes ID. * * @param type type of frame * * @return string. */ static const char* getStringForType(Frame::Type type) { if (type != Frame::FT_Other) { for (unsigned i = 0; i < sizeof(typeStrOfId) / sizeof(typeStrOfId[0]); ++i) { const TypeStrOfId& ts = typeStrOfId[i]; if (ts.type == type) { return ts.str; } } } return "????"; } /** * Get the fields from a text identification frame. * * @param tFrame text identification frame * @param fields the fields are appended to this list * @param type frame type * * @return text representation of fields (Text or URL). */ static QString getFieldsFromTextFrame( const TagLib::ID3v2::TextIdentificationFrame* tFrame, Frame::FieldList& fields, Frame::Type type) { QString text; Frame::Field field; field.m_id = Frame::Field::ID_TextEnc; field.m_value = tFrame->textEncoding(); fields.push_back(field); const TagLib::ID3v2::UserTextIdentificationFrame* txxxFrame; if ((txxxFrame = dynamic_cast(tFrame)) != 0) { field.m_id = Frame::Field::ID_Description; field.m_value = TStringToQString(txxxFrame->description()); fields.push_back(field); TagLib::StringList slText = tFrame->fieldList(); text = slText.size() > 1 ? TStringToQString(slText[1]) : QLatin1String(""); } else { // if there are multiple items, put them into one string // separated by a special separator. text = TStringToQString(tFrame->fieldList().toString(Frame::stringListSeparator().toLatin1())); } field.m_id = Frame::Field::ID_Text; if (type == Frame::FT_Genre) { text = Genres::getNameString(text); } field.m_value = text; fields.push_back(field); return text; } /** * Get the fields from an attached picture frame. * * @param apicFrame attached picture frame * @param fields the fields are appended to this list * * @return text representation of fields (Text or URL). */ static QString getFieldsFromApicFrame( const TagLib::ID3v2::AttachedPictureFrame* apicFrame, Frame::FieldList& fields) { QString text; Frame::Field field; field.m_id = Frame::Field::ID_TextEnc; field.m_value = apicFrame->textEncoding(); fields.push_back(field); // for compatibility with ID3v2.3 id3lib field.m_id = Frame::Field::ID_ImageFormat; field.m_value = QString(QLatin1String("")); fields.push_back(field); field.m_id = Frame::Field::ID_MimeType; field.m_value = TStringToQString(apicFrame->mimeType()); fields.push_back(field); field.m_id = Frame::Field::ID_PictureType; field.m_value = apicFrame->type(); fields.push_back(field); field.m_id = Frame::Field::ID_Description; text = TStringToQString(apicFrame->description()); field.m_value = text; fields.push_back(field); field.m_id = Frame::Field::ID_Data; TagLib::ByteVector pic = apicFrame->picture(); QByteArray ba; ba = QByteArray(pic.data(), pic.size()); field.m_value = ba; fields.push_back(field); return text; } /** * Get the fields from a comments frame. * * @param commFrame comments frame * @param fields the fields are appended to this list * * @return text representation of fields (Text or URL). */ static QString getFieldsFromCommFrame( const TagLib::ID3v2::CommentsFrame* commFrame, Frame::FieldList& fields) { QString text; Frame::Field field; field.m_id = Frame::Field::ID_TextEnc; field.m_value = commFrame->textEncoding(); fields.push_back(field); field.m_id = Frame::Field::ID_Language; TagLib::ByteVector bvLang = commFrame->language(); field.m_value = QString::fromLatin1(QByteArray(bvLang.data(), bvLang.size())); fields.push_back(field); field.m_id = Frame::Field::ID_Description; field.m_value = TStringToQString(commFrame->description()); fields.push_back(field); field.m_id = Frame::Field::ID_Text; text = TStringToQString(commFrame->toString()); field.m_value = text; fields.push_back(field); return text; } /** * Get the fields from a unique file identifier frame. * * @param ufidFrame unique file identifier frame * @param fields the fields are appended to this list * * @return text representation of fields (Text or URL). */ static QString getFieldsFromUfidFrame( const TagLib::ID3v2::UniqueFileIdentifierFrame* ufidFrame, Frame::FieldList& fields) { Frame::Field field; field.m_id = Frame::Field::ID_Owner; field.m_value = TStringToQString(ufidFrame->owner()); fields.push_back(field); field.m_id = Frame::Field::ID_Id; TagLib::ByteVector id = ufidFrame->identifier(); QByteArray ba; ba = QByteArray(id.data(), id.size()); field.m_value = ba; fields.push_back(field); if (!ba.isEmpty()) { QString text(QString::fromLatin1(ba)); if (ba.size() - text.length() <= 1 && AttributeData::isHexString(text, 'Z')) { return text; } } return QString(); } /** * Get the fields from a general encapsulated object frame. * * @param geobFrame general encapsulated object frame * @param fields the fields are appended to this list * * @return text representation of fields (Text or URL). */ static QString getFieldsFromGeobFrame( const TagLib::ID3v2::GeneralEncapsulatedObjectFrame* geobFrame, Frame::FieldList& fields) { QString text; Frame::Field field; field.m_id = Frame::Field::ID_TextEnc; field.m_value = geobFrame->textEncoding(); fields.push_back(field); field.m_id = Frame::Field::ID_MimeType; field.m_value = TStringToQString(geobFrame->mimeType()); fields.push_back(field); field.m_id = Frame::Field::ID_Filename; field.m_value = TStringToQString(geobFrame->fileName()); fields.push_back(field); field.m_id = Frame::Field::ID_Description; text = TStringToQString(geobFrame->description()); field.m_value = text; fields.push_back(field); field.m_id = Frame::Field::ID_Data; TagLib::ByteVector obj = geobFrame->object(); QByteArray ba; ba = QByteArray(obj.data(), obj.size()); field.m_value = ba; fields.push_back(field); return text; } /** * Get the fields from a URL link frame. * * @param wFrame URL link frame * @param fields the fields are appended to this list * * @return text representation of fields (Text or URL). */ static QString getFieldsFromUrlFrame( const TagLib::ID3v2::UrlLinkFrame* wFrame, Frame::FieldList& fields) { QString text; Frame::Field field; field.m_id = Frame::Field::ID_Url; text = TStringToQString(wFrame->url()); field.m_value = text; fields.push_back(field); return text; } /** * Get the fields from a user URL link frame. * * @param wxxxFrame user URL link frame * @param fields the fields are appended to this list * * @return text representation of fields (Text or URL). */ static QString getFieldsFromUserUrlFrame( const TagLib::ID3v2::UserUrlLinkFrame* wxxxFrame, Frame::FieldList& fields) { QString text; Frame::Field field; field.m_id = Frame::Field::ID_TextEnc; field.m_value = wxxxFrame->textEncoding(); fields.push_back(field); field.m_id = Frame::Field::ID_Description; field.m_value = TStringToQString(wxxxFrame->description()); fields.push_back(field); field.m_id = Frame::Field::ID_Url; text = TStringToQString(wxxxFrame->url()); field.m_value = text; fields.push_back(field); return text; } /** * Get the fields from an unsynchronized lyrics frame. * This is copy-pasted from editCommFrame(). * * @param usltFrame unsynchronized frame * @param fields the fields are appended to this list * * @return text representation of fields (Text or URL). */ static QString getFieldsFromUsltFrame( const TagLib::ID3v2::UnsynchronizedLyricsFrame* usltFrame, Frame::FieldList& fields) { QString text; Frame::Field field; field.m_id = Frame::Field::ID_TextEnc; field.m_value = usltFrame->textEncoding(); fields.push_back(field); field.m_id = Frame::Field::ID_Language; TagLib::ByteVector bvLang = usltFrame->language(); field.m_value = QString::fromLatin1(QByteArray(bvLang.data(), bvLang.size())); fields.push_back(field); field.m_id = Frame::Field::ID_Description; field.m_value = TStringToQString(usltFrame->description()); fields.push_back(field); field.m_id = Frame::Field::ID_Text; text = TStringToQString(usltFrame->toString()); field.m_value = text; fields.push_back(field); return text; } #if TAGLIB_VERSION >= 0x010600 /** * Get the fields from a private frame. * * @param privFrame private frame * @param fields the fields are appended to this list * * @return text representation of fields (Text or URL). */ static QString getFieldsFromPrivFrame( const TagLib::ID3v2::PrivateFrame* privFrame, Frame::FieldList& fields) { QString owner; Frame::Field field; field.m_id = Frame::Field::ID_Owner; owner = TStringToQString(privFrame->owner()); field.m_value = owner; fields.push_back(field); field.m_id = Frame::Field::ID_Data; TagLib::ByteVector data = privFrame->data(); QByteArray ba; ba = QByteArray(data.data(), data.size()); field.m_value = ba; fields.push_back(field); if (!owner.isEmpty() && !ba.isEmpty()) { QString str; if (AttributeData(owner).toString(ba, str)) { return str; } } return QString(); } /** * Get the fields from a popularimeter frame. * * @param popmFrame popularimeter frame * @param fields the fields are appended to this list * * @return text representation of fields (Text or URL). */ static QString getFieldsFromPopmFrame( const TagLib::ID3v2::PopularimeterFrame* popmFrame, Frame::FieldList& fields) { Frame::Field field; field.m_id = Frame::Field::ID_Email; field.m_value = TStringToQString(popmFrame->email()); fields.push_back(field); field.m_id = Frame::Field::ID_Rating; field.m_value = popmFrame->rating(); QString text(field.m_value.toString()); fields.push_back(field); field.m_id = Frame::Field::ID_Counter; field.m_value = popmFrame->counter(); fields.push_back(field); return text; } #endif #if TAGLIB_VERSION >= 0x010800 /** * Get the fields from an ownership frame. * * @param owneFrame ownership frame * @param fields the fields are appended to this list * * @return text representation of fields (Text or URL). */ static QString getFieldsFromOwneFrame( const TagLib::ID3v2::OwnershipFrame* owneFrame, Frame::FieldList& fields) { Frame::Field field; field.m_id = Frame::Field::ID_TextEnc; field.m_value = owneFrame->textEncoding(); fields.push_back(field); field.m_id = Frame::Field::ID_Date; field.m_value = TStringToQString(owneFrame->datePurchased()); fields.push_back(field); field.m_id = Frame::Field::ID_Price; field.m_value = TStringToQString(owneFrame->pricePaid()); fields.push_back(field); field.m_id = Frame::Field::ID_Seller; QString text(TStringToQString(owneFrame->seller())); field.m_value = text; fields.push_back(field); return text; } #endif /** * Get the fields from an unknown frame. * * @param unknownFrame unknown frame * @param fields the fields are appended to this list * * @return text representation of fields (Text or URL). */ static QString getFieldsFromUnknownFrame( const TagLib::ID3v2::Frame* unknownFrame, Frame::FieldList& fields) { Frame::Field field; field.m_id = Frame::Field::ID_Data; TagLib::ByteVector dat = unknownFrame->render(); QByteArray ba; ba = QByteArray(dat.data(), dat.size()); field.m_value = ba; fields.push_back(field); return QString(); } /** * Get the fields from an ID3v2 tag. * * @param frame frame * @param fields the fields are appended to this list * @param type frame type * * @return text representation of fields (Text or URL). */ static QString getFieldsFromId3Frame(const TagLib::ID3v2::Frame* frame, Frame::FieldList& fields, Frame::Type type) { if (frame) { const TagLib::ID3v2::TextIdentificationFrame* tFrame; const TagLib::ID3v2::AttachedPictureFrame* apicFrame; const TagLib::ID3v2::CommentsFrame* commFrame; const TagLib::ID3v2::UniqueFileIdentifierFrame* ufidFrame; const TagLib::ID3v2::GeneralEncapsulatedObjectFrame* geobFrame; const TagLib::ID3v2::UserUrlLinkFrame* wxxxFrame; const TagLib::ID3v2::UrlLinkFrame* wFrame; const TagLib::ID3v2::UnsynchronizedLyricsFrame* usltFrame; #if TAGLIB_VERSION >= 0x010600 const TagLib::ID3v2::PrivateFrame* privFrame; const TagLib::ID3v2::PopularimeterFrame* popmFrame; #endif #if TAGLIB_VERSION >= 0x010800 const TagLib::ID3v2::OwnershipFrame* owneFrame; #endif if ((tFrame = dynamic_cast(frame)) != 0) { return getFieldsFromTextFrame(tFrame, fields, type); } else if ((apicFrame = dynamic_cast(frame)) != 0) { return getFieldsFromApicFrame(apicFrame, fields); } else if ((commFrame = dynamic_cast( frame)) != 0) { return getFieldsFromCommFrame(commFrame, fields); } else if ((ufidFrame = dynamic_cast( frame)) != 0) { return getFieldsFromUfidFrame(ufidFrame, fields); } else if ((geobFrame = dynamic_cast( frame)) != 0) { return getFieldsFromGeobFrame(geobFrame, fields); } else if ((wxxxFrame = dynamic_cast( frame)) != 0) { return getFieldsFromUserUrlFrame(wxxxFrame, fields); } else if ((wFrame = dynamic_cast( frame)) != 0) { return getFieldsFromUrlFrame(wFrame, fields); } else if ((usltFrame = dynamic_cast( frame)) != 0) { return getFieldsFromUsltFrame(usltFrame, fields); #if TAGLIB_VERSION >= 0x010600 } else if ((privFrame = dynamic_cast( frame)) != 0) { return getFieldsFromPrivFrame(privFrame, fields); } else if ((popmFrame = dynamic_cast( frame)) != 0) { return getFieldsFromPopmFrame(popmFrame, fields); #endif #if TAGLIB_VERSION >= 0x010800 } else if ((owneFrame = dynamic_cast( frame)) != 0) { return getFieldsFromOwneFrame(owneFrame, fields); #endif } else { TagLib::ByteVector id = frame->frameID(); #ifndef TAGLIB_SUPPORTS_URLLINK_FRAMES if (id.startsWith("WXXX")) { TagLib::ID3v2::UserUrlLinkFrame userUrlLinkFrame(frame->render()); return getFieldsFromUserUrlFrame(&userUrlLinkFrame, fields); } else if (id.startsWith("W")) { TagLib::ID3v2::UrlLinkFrame urlLinkFrame(frame->render()); return getFieldsFromUrlFrame(&urlLinkFrame, fields); } else #endif #ifndef TAGLIB_SUPPORTS_USLT_FRAMES if (id.startsWith("USLT")) { TagLib::ID3v2::UnsynchronizedLyricsFrame usltFrame(frame->render()); return getFieldsFromUsltFrame(&usltFrame, fields); } else #endif #ifndef TAGLIB_SUPPORTS_GEOB_FRAMES if (id.startsWith("GEOB")) { TagLib::ID3v2::GeneralEncapsulatedObjectFrame geobFrame(frame->render()); return getFieldsFromGeobFrame(&geobFrame, fields); } else #endif return getFieldsFromUnknownFrame(frame, fields); } } return QString(); } /** * Convert a string to a language code byte vector. * * @param str string containing language code. * * @return 3 byte vector with language code. */ static TagLib::ByteVector languageCodeByteVector(QString str) { uint len = str.length(); if (len > 3) { str.truncate(3); } else if (len < 3) { for (uint i = len; i < 3; ++i) { str += QLatin1Char(' '); } } return TagLib::ByteVector(str.toLatin1().data(), str.length()); } /** * The following template functions are used to uniformly set fields * in the different ID3v2 frames. */ //! @cond template void setTextEncoding(T*, TagLib::String::Type) {} template <> void setTextEncoding(TagLib::ID3v2::TextIdentificationFrame* f, TagLib::String::Type enc) { f->setTextEncoding(enc); } template <> void setTextEncoding(TagLib::ID3v2::AttachedPictureFrame* f, TagLib::String::Type enc) { f->setTextEncoding(enc); } template <> void setTextEncoding(TagLib::ID3v2::CommentsFrame* f, TagLib::String::Type enc) { f->setTextEncoding(enc); } template <> void setTextEncoding(TagLib::ID3v2::GeneralEncapsulatedObjectFrame* f, TagLib::String::Type enc) { f->setTextEncoding(enc); } template <> void setTextEncoding(TagLib::ID3v2::UserUrlLinkFrame* f, TagLib::String::Type enc) { f->setTextEncoding(enc); } template <> void setTextEncoding(TagLib::ID3v2::UnsynchronizedLyricsFrame* f, TagLib::String::Type enc) { f->setTextEncoding(enc); } template void setDescription(T*, const Frame::Field&) {} template <> void setDescription(TagLib::ID3v2::UserTextIdentificationFrame* f, const Frame::Field& fld) { f->setDescription(QSTRING_TO_TSTRING(fld.m_value.toString())); } template <> void setDescription(TagLib::ID3v2::AttachedPictureFrame* f, const Frame::Field& fld) { f->setDescription(QSTRING_TO_TSTRING(fld.m_value.toString())); } template <> void setDescription(TagLib::ID3v2::CommentsFrame* f, const Frame::Field& fld) { f->setDescription(QSTRING_TO_TSTRING(fld.m_value.toString())); } template <> void setDescription(TagLib::ID3v2::GeneralEncapsulatedObjectFrame* f, const Frame::Field& fld) { f->setDescription(QSTRING_TO_TSTRING(fld.m_value.toString())); } template <> void setDescription(TagLib::ID3v2::UserUrlLinkFrame* f, const Frame::Field& fld) { f->setDescription(QSTRING_TO_TSTRING(fld.m_value.toString())); } template <> void setDescription(TagLib::ID3v2::UnsynchronizedLyricsFrame* f, const Frame::Field& fld) { f->setDescription(QSTRING_TO_TSTRING(fld.m_value.toString())); } template void setMimeType(T*, const Frame::Field&) {} template <> void setMimeType(TagLib::ID3v2::AttachedPictureFrame* f, const Frame::Field& fld) { f->setMimeType(QSTRING_TO_TSTRING(fld.m_value.toString())); } template <> void setMimeType(TagLib::ID3v2::GeneralEncapsulatedObjectFrame* f, const Frame::Field& fld) { f->setMimeType(QSTRING_TO_TSTRING(fld.m_value.toString())); } template void setPictureType(T*, const Frame::Field&) {} template <> void setPictureType(TagLib::ID3v2::AttachedPictureFrame* f, const Frame::Field& fld) { f->setType( static_cast( fld.m_value.toInt())); } template void setData(T*, const Frame::Field&) {} template <> void setData(TagLib::ID3v2::Frame* f, const Frame::Field& fld) { QByteArray ba(fld.m_value.toByteArray()); f->setData(TagLib::ByteVector(ba.data(), ba.size())); } template <> void setData(TagLib::ID3v2::AttachedPictureFrame* f, const Frame::Field& fld) { QByteArray ba(fld.m_value.toByteArray()); f->setPicture(TagLib::ByteVector(ba.data(), ba.size())); } template <> void setData(TagLib::ID3v2::GeneralEncapsulatedObjectFrame* f, const Frame::Field& fld) { QByteArray ba(fld.m_value.toByteArray()); f->setObject(TagLib::ByteVector(ba.data(), ba.size())); } template <> void setData(TagLib::ID3v2::UniqueFileIdentifierFrame* f, const Frame::Field& fld) { QByteArray ba(fld.m_value.toByteArray()); f->setIdentifier(TagLib::ByteVector(ba.data(), ba.size())); } template void setLanguage(T*, const Frame::Field&) {} template <> void setLanguage(TagLib::ID3v2::CommentsFrame* f, const Frame::Field& fld) { f->setLanguage(languageCodeByteVector(fld.m_value.toString())); } template <> void setLanguage(TagLib::ID3v2::UnsynchronizedLyricsFrame* f, const Frame::Field& fld) { f->setLanguage(languageCodeByteVector(fld.m_value.toString())); } template void setOwner(T*, const Frame::Field&) {} template <> void setOwner(TagLib::ID3v2::UniqueFileIdentifierFrame* f, const Frame::Field& fld) { f->setOwner(QSTRING_TO_TSTRING(fld.m_value.toString())); } #if TAGLIB_VERSION >= 0x010600 template <> void setOwner(TagLib::ID3v2::PrivateFrame* f, const Frame::Field& fld) { f->setOwner(QSTRING_TO_TSTRING(fld.m_value.toString())); } template <> void setData(TagLib::ID3v2::PrivateFrame* f, const Frame::Field& fld) { QByteArray ba(fld.m_value.toByteArray()); f->setData(TagLib::ByteVector(ba.data(), ba.size())); } #endif template void setIdentifier(T*, const Frame::Field&) {} template <> void setIdentifier(TagLib::ID3v2::UniqueFileIdentifierFrame* f, const Frame::Field& fld) { QByteArray ba(fld.m_value.toByteArray()); f->setIdentifier(TagLib::ByteVector(ba.data(), ba.size())); } template void setFilename(T*, const Frame::Field&) {} template <> void setFilename(TagLib::ID3v2::GeneralEncapsulatedObjectFrame* f, const Frame::Field& fld) { f->setFileName(QSTRING_TO_TSTRING(fld.m_value.toString())); } template void setUrl(T*, const Frame::Field&) {} template <> void setUrl(TagLib::ID3v2::UrlLinkFrame* f, const Frame::Field& fld) { f->setUrl(QSTRING_TO_TSTRING(fld.m_value.toString())); } template <> void setUrl(TagLib::ID3v2::UserUrlLinkFrame* f, const Frame::Field& fld) { f->setUrl(QSTRING_TO_TSTRING(fld.m_value.toString())); } template void setValue(T* f, const TagLib::String& text) { f->setText(text); } template <> void setValue(TagLib::ID3v2::AttachedPictureFrame* f, const TagLib::String& text) { f->setDescription(text); } template <> void setValue(TagLib::ID3v2::GeneralEncapsulatedObjectFrame* f, const TagLib::String& text) { f->setDescription(text); } static void setStringOrList(TagLib::ID3v2::TextIdentificationFrame* f, const TagLib::String& text) { if (text.find(Frame::stringListSeparator().toLatin1()) == -1) { f->setText(text); } else { f->setText(TagLib::StringList::split(text, Frame::stringListSeparator().toLatin1())); } } template <> void setValue(TagLib::ID3v2::TextIdentificationFrame* f, const TagLib::String& text) { setStringOrList(f, text); } template <> void setValue(TagLib::ID3v2::UniqueFileIdentifierFrame* f, const TagLib::String& text) { if (AttributeData::isHexString(TStringToQString(text), 'Z')) { TagLib::ByteVector data(text.data(TagLib::String::Latin1)); data.append('\0'); f->setIdentifier(data); } } #if TAGLIB_VERSION >= 0x010600 template <> void setValue(TagLib::ID3v2::PrivateFrame* f, const TagLib::String& text) { QByteArray newData; TagLib::String owner = f->owner(); if (!owner.isEmpty() && AttributeData(TStringToQString(owner)). toByteArray(TStringToQString(text), newData)) { f->setData(TagLib::ByteVector(newData.data(), newData.size())); } } template <> void setValue(TagLib::ID3v2::PopularimeterFrame* f, const TagLib::String& text) { f->setRating(text.toInt()); } #endif template void setText(T* f, const TagLib::String& text) { f->setText(text); } template <> void setText(TagLib::ID3v2::TextIdentificationFrame* f, const TagLib::String& text) { setStringOrList(f, text); } #if TAGLIB_VERSION >= 0x010600 template void setEmail(T*, const Frame::Field&) {} template <> void setEmail(TagLib::ID3v2::PopularimeterFrame* f, const Frame::Field& fld) { f->setEmail(QSTRING_TO_TSTRING(fld.m_value.toString())); } template void setRating(T*, const Frame::Field&) {} template <> void setRating(TagLib::ID3v2::PopularimeterFrame* f, const Frame::Field& fld) { f->setRating(fld.m_value.toInt()); } template void setCounter(T*, const Frame::Field&) {} template <> void setCounter(TagLib::ID3v2::PopularimeterFrame* f, const Frame::Field& fld) { f->setCounter(fld.m_value.toUInt()); } #endif #if TAGLIB_VERSION >= 0x010800 template void setDate(T*, const Frame::Field&) {} template <> void setDate(TagLib::ID3v2::OwnershipFrame* f, const Frame::Field& fld) { // The date string must have exactly 8 characters (should be YYYYMMDD) QString date(fld.m_value.toString().leftJustified(8, QLatin1Char(' '), true)); f->setDatePurchased(QSTRING_TO_TSTRING(date)); } template void setPrice(T*, const Frame::Field&) {} template <> void setPrice(TagLib::ID3v2::OwnershipFrame* f, const Frame::Field& fld) { f->setPricePaid(QSTRING_TO_TSTRING(fld.m_value.toString())); } template void setSeller(T*, const Frame::Field&) {} template <> void setSeller(TagLib::ID3v2::OwnershipFrame* f, const Frame::Field& fld) { f->setSeller(QSTRING_TO_TSTRING(fld.m_value.toString())); } template <> void setTextEncoding(TagLib::ID3v2::OwnershipFrame* f, TagLib::String::Type enc) { f->setTextEncoding(enc); } template <> void setValue(TagLib::ID3v2::OwnershipFrame* f, const TagLib::String& text) { f->setSeller(text); } #endif //! @endcond /** * Set the fields in a TagLib ID3v2 frame. * * @param self this TagLibFile instance * @param tFrame TagLib frame to set * @param frame frame with field values */ template void setTagLibFrame(const TagLibFile* self, T* tFrame, const Frame& frame) { // If value is changed or field list is empty, // set from value, else from FieldList. if (frame.isValueChanged() || frame.getFieldList().empty()) { QString text(frame.getValue()); if (frame.getType() == Frame::FT_Genre) { if (!TagConfig::instance().genreNotNumeric()) { text = Genres::getNumberString(text, false); } } else if (frame.getType() == Frame::FT_Track) { self->formatTrackNumberIfEnabled(text, true); } setValue(tFrame, QSTRING_TO_TSTRING(text)); setTextEncoding(tFrame, getTextEncodingConfig(needsUnicode(text))); } else { for (Frame::FieldList::const_iterator fldIt = frame.getFieldList().begin(); fldIt != frame.getFieldList().end(); ++fldIt) { const Frame::Field& fld = *fldIt; switch (fld.m_id) { case Frame::Field::ID_Text: { QString value(fld.m_value.toString()); if (frame.getType() == Frame::FT_Genre) { if (!TagConfig::instance().genreNotNumeric()) { value = Genres::getNumberString(value, false); } } else if (frame.getType() == Frame::FT_Track) { self->formatTrackNumberIfEnabled(value, true); } setText(tFrame, QSTRING_TO_TSTRING(value)); break; } case Frame::Field::ID_TextEnc: setTextEncoding(tFrame, static_cast( fld.m_value.toInt())); break; case Frame::Field::ID_Description: setDescription(tFrame, fld); break; case Frame::Field::ID_MimeType: setMimeType(tFrame, fld); break; case Frame::Field::ID_PictureType: setPictureType(tFrame, fld); break; case Frame::Field::ID_Data: setData(tFrame, fld); break; case Frame::Field::ID_Language: setLanguage(tFrame, fld); break; case Frame::Field::ID_Owner: setOwner(tFrame, fld); break; case Frame::Field::ID_Id: setIdentifier(tFrame, fld); break; case Frame::Field::ID_Filename: setFilename(tFrame, fld); break; case Frame::Field::ID_Url: setUrl(tFrame, fld); break; #if TAGLIB_VERSION >= 0x010600 case Frame::Field::ID_Email: setEmail(tFrame, fld); break; case Frame::Field::ID_Rating: setRating(tFrame, fld); break; case Frame::Field::ID_Counter: setCounter(tFrame, fld); break; #endif #if TAGLIB_VERSION >= 0x010800 case Frame::Field::ID_Price: setPrice(tFrame, fld); break; case Frame::Field::ID_Date: setDate(tFrame, fld); break; case Frame::Field::ID_Seller: setSeller(tFrame, fld); break; #endif } } } } /** * Modify an ID3v2 frame. * * @param id3Frame original ID3v2 frame * @param frame frame with fields to set in new frame */ void TagLibFile::setId3v2Frame( TagLib::ID3v2::Frame* id3Frame, const Frame& frame) const { if (id3Frame) { TagLib::ID3v2::TextIdentificationFrame* tFrame; TagLib::ID3v2::AttachedPictureFrame* apicFrame; TagLib::ID3v2::CommentsFrame* commFrame; TagLib::ID3v2::UniqueFileIdentifierFrame* ufidFrame; TagLib::ID3v2::GeneralEncapsulatedObjectFrame* geobFrame; TagLib::ID3v2::UserUrlLinkFrame* wxxxFrame; TagLib::ID3v2::UrlLinkFrame* wFrame; TagLib::ID3v2::UnsynchronizedLyricsFrame* usltFrame; #if TAGLIB_VERSION >= 0x010600 TagLib::ID3v2::PrivateFrame* privFrame; TagLib::ID3v2::PopularimeterFrame* popmFrame; #endif #if TAGLIB_VERSION >= 0x010800 TagLib::ID3v2::OwnershipFrame* owneFrame; #endif if ((tFrame = dynamic_cast(id3Frame)) != 0) { TagLib::ID3v2::UserTextIdentificationFrame* txxxFrame = dynamic_cast(id3Frame); if (txxxFrame) { setTagLibFrame(this, txxxFrame, frame); } else { setTagLibFrame(this, tFrame, frame); } } else if ((apicFrame = dynamic_cast(id3Frame)) != 0) { setTagLibFrame(this, apicFrame, frame); } else if ((commFrame = dynamic_cast( id3Frame)) != 0) { setTagLibFrame(this, commFrame, frame); } else if ((ufidFrame = dynamic_cast( id3Frame)) != 0) { setTagLibFrame(this, ufidFrame, frame); } else if ((geobFrame = dynamic_cast( id3Frame)) != 0) { setTagLibFrame(this, geobFrame, frame); } else if ((wxxxFrame = dynamic_cast( id3Frame)) != 0) { setTagLibFrame(this, wxxxFrame, frame); } else if ((wFrame = dynamic_cast( id3Frame)) != 0) { setTagLibFrame(this, wFrame, frame); } else if ((usltFrame = dynamic_cast( id3Frame)) != 0) { setTagLibFrame(this, usltFrame, frame); #if TAGLIB_VERSION >= 0x010600 } else if ((privFrame = dynamic_cast( id3Frame)) != 0) { setTagLibFrame(this, privFrame, frame); } else if ((popmFrame = dynamic_cast( id3Frame)) != 0) { setTagLibFrame(this, popmFrame, frame); #endif #if TAGLIB_VERSION >= 0x010800 } else if ((owneFrame = dynamic_cast( id3Frame)) != 0) { setTagLibFrame(this, owneFrame, frame); #endif } else { TagLib::ByteVector id(id3Frame->frameID()); // create temporary objects for frames not known by TagLib, // an UnknownFrame copy will be created by the edit method. #ifndef TAGLIB_SUPPORTS_URLLINK_FRAMES if (id.startsWith("WXXX")) { TagLib::ID3v2::UserUrlLinkFrame userUrlLinkFrame(id3Frame->render()); setTagLibFrame(this, &userUrlLinkFrame, frame); id3Frame->setData(userUrlLinkFrame.render()); } else if (id.startsWith("W")) { TagLib::ID3v2::UrlLinkFrame urlLinkFrame(id3Frame->render()); setTagLibFrame(this, &urlLinkFrame, frame); id3Frame->setData(urlLinkFrame.render()); } else #endif #ifndef TAGLIB_SUPPORTS_USLT_FRAMES if (id.startsWith("USLT")) { TagLib::ID3v2::UnsynchronizedLyricsFrame usltFrame(id3Frame->render()); setTagLibFrame(this, &usltFrame, frame); id3Frame->setData(usltFrame.render()); } else #endif #ifndef TAGLIB_SUPPORTS_GEOB_FRAMES if (id.startsWith("GEOB")) { TagLib::ID3v2::GeneralEncapsulatedObjectFrame geobFrame(id3Frame->render()); setTagLibFrame(this, &geobFrame, frame); id3Frame->setData(geobFrame.render()); } else #endif { setTagLibFrame(this, id3Frame, frame); } } } } /** * Get name of frame from type. * * @param type type * * @return name. */ static const char* getVorbisNameFromType(Frame::Type type) { static const char* const names[] = { "TITLE", // FT_Title, "ARTIST", // FT_Artist, "ALBUM", // FT_Album, "COMMENT", // FT_Comment, "DATE", // FT_Date, "TRACKNUMBER", // FT_Track, "GENRE", // FT_Genre, // FT_LastV1Frame = FT_Track, "ALBUMARTIST", // FT_AlbumArtist, "ARRANGER", // FT_Arranger, "AUTHOR", // FT_Author, "BPM", // FT_Bpm, "CATALOGNUMBER", // FT_CatalogNumber, "COMPILATION", // FT_Compilation, "COMPOSER", // FT_Composer, "CONDUCTOR", // FT_Conductor, "COPYRIGHT", // FT_Copyright, "DISCNUMBER", // FT_Disc, "ENCODED-BY", // FT_EncodedBy, "ENCODERSETTINGS", // FT_EncoderSettings, "ENCODINGTIME", // FT_EncodingTime, "GROUPING", // FT_Grouping, "INITIALKEY", // FT_InitialKey, "ISRC", // FT_Isrc, "LANGUAGE", // FT_Language, "LYRICIST", // FT_Lyricist, "LYRICS", // FT_Lyrics, "SOURCEMEDIA", // FT_Media, "MOOD", // FT_Mood, "ORIGINALALBUM", // FT_OriginalAlbum, "ORIGINALARTIST", // FT_OriginalArtist, "ORIGINALDATE", // FT_OriginalDate, "PART", // FT_Part, "PERFORMER", // FT_Performer, "UNKNOWN", // FT_Picture, "PUBLISHER", // FT_Publisher, "RELEASECOUNTRY", // FT_ReleaseCountry, "REMIXER", // FT_Remixer, "ALBUMSORT", // FT_SortAlbum, "ALBUMARTISTSORT", // FT_SortAlbumArtist, "ARTISTSORT", // FT_SortArtist, "COMPOSERSORT", // FT_SortComposer, "TITLESORT", // FT_SortName, "SUBTITLE", // FT_Subtitle, "WEBSITE", // FT_Website, "WWWAUDIOFILE", // FT_WWWAudioFile, "WWWAUDIOSOURCE" // FT_WWWAudioSource, // FT_LastFrame = FT_WWWAudioSource }; struct not_used { int array_size_check[ sizeof(names) / sizeof(names[0]) == Frame::FT_LastFrame + 1 ? 1 : -1 ]; }; return type <= Frame::FT_LastFrame ? names[type] : "UNKNOWN"; } /** * Get the frame type for a Vorbis name. * * @param name Vorbis tag name * * @return frame type. */ static Frame::Type getTypeFromVorbisName(QString name) { static QMap strNumMap; if (strNumMap.empty()) { // first time initialization for (int i = 0; i <= Frame::FT_LastFrame; ++i) { Frame::Type type = static_cast(i); strNumMap.insert(QString::fromLatin1(getVorbisNameFromType(type)), type); } strNumMap.insert(QLatin1String("DESCRIPTION"), Frame::FT_Comment); } QMap::const_iterator it = strNumMap.find(name.remove(QLatin1Char('=')).toUpper()); if (it != strNumMap.end()) { return static_cast(*it); } return Frame::FT_Other; } /** * Get the frame type for an APE name. * * @param name APE tag name * * @return frame type. */ static Frame::Type getTypeFromApeName(const QString& name) { Frame::Type type = getTypeFromVorbisName(name); if (type == Frame::FT_Other) { if (name == QLatin1String("YEAR")) { type = Frame::FT_Date; } else if (name == QLatin1String("TRACK")) { type = Frame::FT_Track; } else if (name == QLatin1String("ENCODED BY")) { type = Frame::FT_EncodedBy; } } return type; } /** * Get internal name of a Vorbis frame. * * @param frame frame * * @return Vorbis key. */ static QString getVorbisName(const Frame& frame) { Frame::Type type = frame.getType(); if (type == Frame::FT_Comment) { return QLatin1String("DESCRIPTION"); } else if (type <= Frame::FT_LastFrame) { return QString::fromLatin1(getVorbisNameFromType(type)); } else { return frame.getName().remove(QLatin1Char('=')).toUpper(); } } /** * Get internal name of an APE frame. * * @param frame frame * * @return APE key. */ static QString getApeName(const Frame& frame) { Frame::Type type = frame.getType(); if (type == Frame::FT_Date) { return QLatin1String("YEAR"); } else if (type == Frame::FT_Track) { return QLatin1String("TRACK"); } else if (type <= Frame::FT_LastFrame) { return QString::fromLatin1(getVorbisNameFromType(type)); } else { return frame.getName().toUpper(); } } #ifdef TAGLIB_WITH_MP4 /** Type of data in MP4 frame. */ enum Mp4ValueType { MVT_ByteArray, #if TAGLIB_VERSION >= 0x010602 MVT_CoverArt, #endif MVT_String, MVT_Bool, MVT_Int, MVT_IntPair, MVT_Byte, MVT_UInt, MVT_LongLong }; /** MP4 name, frame type and value type. */ struct Mp4NameTypeValue { const char* name; Frame::Type type; Mp4ValueType value; }; /** Mapping between frame types and field names. */ static const Mp4NameTypeValue mp4NameTypeValues[] = { { "\251nam", Frame::FT_Title, MVT_String }, { "\251ART", Frame::FT_Artist, MVT_String }, { "\251wrt", Frame::FT_Composer, MVT_String }, { "\251alb", Frame::FT_Album, MVT_String }, { "\251day", Frame::FT_Date, MVT_String }, { "\251enc", Frame::FT_EncodedBy, MVT_String }, { "\251cmt", Frame::FT_Comment, MVT_String }, { "gnre", Frame::FT_Genre, MVT_String }, // (c)gen is after gnre so that it is used in the maps because TagLib uses it { "\251gen", Frame::FT_Genre, MVT_String }, { "trkn", Frame::FT_Track, MVT_IntPair }, { "disk", Frame::FT_Disc, MVT_IntPair }, { "cpil", Frame::FT_Compilation, MVT_Bool }, { "tmpo", Frame::FT_Bpm, MVT_Int }, { "\251grp", Frame::FT_Grouping, MVT_String }, { "aART", Frame::FT_AlbumArtist, MVT_String }, { "pgap", Frame::FT_Other, MVT_Bool }, { "cprt", Frame::FT_Copyright, MVT_String }, { "\251lyr", Frame::FT_Lyrics, MVT_String }, { "tvsh", Frame::FT_Other, MVT_String }, { "tvnn", Frame::FT_Other, MVT_String }, { "tven", Frame::FT_Other, MVT_String }, { "tvsn", Frame::FT_Other, MVT_UInt }, { "tves", Frame::FT_Other, MVT_UInt }, { "desc", Frame::FT_Other, MVT_String }, { "ldes", Frame::FT_Other, MVT_String }, { "sonm", Frame::FT_SortName, MVT_String }, { "soar", Frame::FT_SortArtist, MVT_String }, { "soaa", Frame::FT_SortAlbumArtist, MVT_String }, { "soal", Frame::FT_SortAlbum, MVT_String }, { "soco", Frame::FT_SortComposer, MVT_String }, { "sosn", Frame::FT_Other, MVT_String }, { "\251too", Frame::FT_EncoderSettings, MVT_String }, { "purd", Frame::FT_Other, MVT_String }, { "pcst", Frame::FT_Other, MVT_Bool }, { "keyw", Frame::FT_Other, MVT_String }, { "catg", Frame::FT_Other, MVT_String }, { "hdvd", Frame::FT_Other, MVT_Bool }, { "stik", Frame::FT_Other, MVT_Byte }, { "rtng", Frame::FT_Other, MVT_Byte }, { "apID", Frame::FT_Other, MVT_String }, { "akID", Frame::FT_Other, MVT_Byte }, { "sfID", Frame::FT_Other, MVT_UInt }, { "cnID", Frame::FT_Other, MVT_UInt }, { "atID", Frame::FT_Other, MVT_UInt }, { "plID", Frame::FT_Other, MVT_LongLong }, { "geID", Frame::FT_Other, MVT_UInt }, #if TAGLIB_VERSION >= 0x010602 { "covr", Frame::FT_Picture, MVT_CoverArt }, #else { "covr", Frame::FT_Picture, MVT_ByteArray }, #endif { "ARRANGER", Frame::FT_Arranger, MVT_String }, { "AUTHOR", Frame::FT_Author, MVT_String }, { "CATALOGNUMBER", Frame::FT_CatalogNumber, MVT_String }, { "CONDUCTOR", Frame::FT_Conductor, MVT_String }, { "ENCODINGTIME", Frame::FT_EncodingTime, MVT_String }, { "INITIALKEY", Frame::FT_InitialKey, MVT_String }, { "ISRC", Frame::FT_Isrc, MVT_String }, { "LANGUAGE", Frame::FT_Language, MVT_String }, { "LYRICIST", Frame::FT_Lyricist, MVT_String }, { "MOOD", Frame::FT_Mood, MVT_String }, { "SOURCEMEDIA", Frame::FT_Media, MVT_String }, { "ORIGINALALBUM", Frame::FT_OriginalAlbum, MVT_String }, { "ORIGINALARTIST", Frame::FT_OriginalArtist, MVT_String }, { "ORIGINALDATE", Frame::FT_OriginalDate, MVT_String }, { "PART", Frame::FT_Part, MVT_String }, { "PERFORMER", Frame::FT_Performer, MVT_String }, { "PUBLISHER", Frame::FT_Publisher, MVT_String }, { "RELEASECOUNTRY", Frame::FT_ReleaseCountry, MVT_String }, { "REMIXER", Frame::FT_Remixer, MVT_String }, { "SUBTITLE", Frame::FT_Subtitle, MVT_String }, { "WEBSITE", Frame::FT_Website, MVT_String }, { "WWWAUDIOFILE", Frame::FT_WWWAudioFile, MVT_String }, { "WWWAUDIOSOURCE", Frame::FT_WWWAudioSource, MVT_String } }; /** * Get MP4 name and value type for a frame type. * * @param type frame type * @param name the MP4 name is returned here * @param value the MP4 value type is returned here */ static void getMp4NameForType(Frame::Type type, TagLib::String& name, Mp4ValueType& value) { static QMap typeNameMap; if (typeNameMap.empty()) { // first time initialization for (unsigned i = 0; i < sizeof(mp4NameTypeValues) / sizeof(mp4NameTypeValues[0]); ++i) { if (mp4NameTypeValues[i].type != Frame::FT_Other) { typeNameMap.insert(mp4NameTypeValues[i].type, i); } } } name = ""; value = MVT_String; if (type != Frame::FT_Other) { QMap::const_iterator it = typeNameMap.find(type); if (it != typeNameMap.end()) { name = mp4NameTypeValues[*it].name; value = mp4NameTypeValues[*it].value; } } } /** * Get MP4 value type and frame type for an MP4 name. * * @param name MP4 name * @param type the frame type is returned here * @param value the MP4 value type is returned here * * @return true if free-form frame. */ static bool getMp4TypeForName(const TagLib::String& name, Frame::Type& type, Mp4ValueType& value) { static QMap nameTypeMap; if (nameTypeMap.empty()) { // first time initialization for (unsigned i = 0; i < sizeof(mp4NameTypeValues) / sizeof(mp4NameTypeValues[0]); ++i) { nameTypeMap.insert(mp4NameTypeValues[i].name, i); } } QMap::const_iterator it = nameTypeMap.find(name); if (it != nameTypeMap.end()) { type = mp4NameTypeValues[*it].type; value = mp4NameTypeValues[*it].value; return name[0] >= 'A' && name[0] <= 'Z'; } else { type = Frame::FT_Other; value = MVT_String; return true; } } /** * Strip free form prefix from MP4 frame name. * * @param name MP4 frame name to be stripped */ static void stripMp4FreeFormName(TagLib::String& name) { if (name.startsWith("----")) { int nameStart = name.rfind(":"); if (nameStart == -1) { nameStart = 5; } else { ++nameStart; } name = name.substr(nameStart); Frame::Type type; Mp4ValueType valueType; if (!getMp4TypeForName(name, type, valueType)) { // not detected as free form => mark with ':' as first character name = ':' + name; } } } /** * Prepend free form prefix to MP4 frame name. * Only names starting with a capital letter or ':' are prefixed. * * @param name MP4 frame name to be prefixed. */ static void prefixMp4FreeFormName(TagLib::String& name) { if (!name.startsWith("----")) { Frame::Type type; Mp4ValueType valueType; if (getMp4TypeForName(name, type, valueType)) { // free form if (name[0] == ':') name = name.substr(1); name = "----:com.apple.iTunes:" + name; } } } /** * Get an MP4 type for a frame. * * @param frame frame * @param name the MP4 name is returned here * @param value the MP4 value type is returned here */ static void getMp4TypeForFrame(const Frame& frame, TagLib::String& name, Mp4ValueType& value) { if (frame.getType() != Frame::FT_Other) { getMp4NameForType(frame.getType(), name, value); if (name.isEmpty()) { name = QSTRING_TO_TSTRING(frame.getInternalName()); } } else { Frame::Type type; name = QSTRING_TO_TSTRING(frame.getInternalName()); getMp4TypeForName(name, type, value); } } /** * Get an MP4 item for a frame. * * @param frame frame * @param name the name for the item is returned here * * @return MP4 item, an invalid item is returned if not supported. */ static TagLib::MP4::Item getMp4ItemForFrame(const Frame& frame, TagLib::String& name) { Mp4ValueType valueType; getMp4TypeForFrame(frame, name, valueType); prefixMp4FreeFormName(name); switch (valueType) { case MVT_String: return TagLib::MP4::Item(QSTRING_TO_TSTRING(frame.getValue())); case MVT_Bool: return TagLib::MP4::Item(frame.getValue().toInt() != 0); case MVT_Int: return TagLib::MP4::Item(frame.getValue().toInt()); case MVT_IntPair: { QString str1 = frame.getValue(), str2 = QLatin1String("0"); int slashPos = str1.indexOf(QLatin1Char('/')); if (slashPos != -1) { str2 = str1.mid(slashPos + 1); str1.truncate(slashPos); } return TagLib::MP4::Item(str1.toInt(), str2.toInt()); } #if TAGLIB_VERSION >= 0x010602 case MVT_CoverArt: { QByteArray ba; TagLib::MP4::CoverArt::Format format = TagLib::MP4::CoverArt::JPEG; if (PictureFrame::getData(frame, ba)) { QString mimeType; if (PictureFrame::getMimeType(frame, mimeType) && mimeType == QLatin1String("image/png")) { format = TagLib::MP4::CoverArt::PNG; } } TagLib::MP4::CoverArt coverArt(format, TagLib::ByteVector(ba.data(), ba.size())); TagLib::MP4::CoverArtList coverArtList; coverArtList.append(coverArt); return TagLib::MP4::Item(coverArtList); } #endif #if TAGLIB_VERSION >= 0x010800 case MVT_Byte: return TagLib::MP4::Item(static_cast(frame.getValue().toInt())); case MVT_UInt: return TagLib::MP4::Item(frame.getValue().toUInt()); case MVT_LongLong: return TagLib::MP4::Item(frame.getValue().toLongLong()); #endif case MVT_ByteArray: default: // binary data and album art are not handled by TagLib return TagLib::MP4::Item(); } } #endif #ifdef TAGLIB_WITH_ASF /** Indices of fixed ASF frames. */ enum AsfFrameIndex { AFI_Title, AFI_Artist, AFI_Comment, AFI_Copyright, AFI_Rating, AFI_Attributes }; /** ASF name, frame type and value type. */ struct AsfNameTypeValue { const char* name; Frame::Type type; TagLib::ASF::Attribute::AttributeTypes value; }; /** Mapping between frame types and field names. */ static const AsfNameTypeValue asfNameTypeValues[] = { { "Title", Frame::FT_Title, TagLib::ASF::Attribute::UnicodeType }, { "Author", Frame::FT_Artist, TagLib::ASF::Attribute::UnicodeType }, { "WM/AlbumTitle", Frame::FT_Album, TagLib::ASF::Attribute::UnicodeType }, { "Description", Frame::FT_Comment, TagLib::ASF::Attribute::UnicodeType }, { "WM/Year", Frame::FT_Date, TagLib::ASF::Attribute::UnicodeType }, { "Copyright", Frame::FT_Copyright, TagLib::ASF::Attribute::UnicodeType }, { "Rating", Frame::FT_Other, TagLib::ASF::Attribute::UnicodeType }, { "WM/TrackNumber", Frame::FT_Track, TagLib::ASF::Attribute::UnicodeType }, { "WM/Track", Frame::FT_Track, TagLib::ASF::Attribute::UnicodeType }, { "WM/Genre", Frame::FT_Genre, TagLib::ASF::Attribute::UnicodeType }, { "WM/GenreID", Frame::FT_Genre, TagLib::ASF::Attribute::UnicodeType }, { "WM/AlbumArtist", Frame::FT_AlbumArtist, TagLib::ASF::Attribute::UnicodeType }, { "WM/AlbumSortOrder", Frame::FT_SortAlbum, TagLib::ASF::Attribute::UnicodeType }, { "WM/ArtistSortOrder", Frame::FT_SortArtist, TagLib::ASF::Attribute::UnicodeType }, { "WM/TitleSortOrder", Frame::FT_SortName, TagLib::ASF::Attribute::UnicodeType }, { "WM/Producer", Frame::FT_Arranger, TagLib::ASF::Attribute::UnicodeType }, { "WM/BeatsPerMinute", Frame::FT_Bpm, TagLib::ASF::Attribute::UnicodeType }, { "WM/Composer", Frame::FT_Composer, TagLib::ASF::Attribute::UnicodeType }, { "WM/Conductor", Frame::FT_Conductor, TagLib::ASF::Attribute::UnicodeType }, { "WM/PartOfSet", Frame::FT_Disc, TagLib::ASF::Attribute::UnicodeType }, { "WM/EncodedBy", Frame::FT_EncodedBy, TagLib::ASF::Attribute::UnicodeType }, { "WM/ContentGroupDescription", Frame::FT_Grouping, TagLib::ASF::Attribute::UnicodeType }, { "WM/ISRC", Frame::FT_Isrc, TagLib::ASF::Attribute::UnicodeType }, { "WM/Language", Frame::FT_Language, TagLib::ASF::Attribute::UnicodeType }, { "WM/Writer", Frame::FT_Lyricist, TagLib::ASF::Attribute::UnicodeType }, { "WM/Lyrics", Frame::FT_Lyrics, TagLib::ASF::Attribute::UnicodeType }, { "WM/AudioSourceURL", Frame::FT_WWWAudioSource, TagLib::ASF::Attribute::UnicodeType }, { "WM/OriginalAlbumTitle", Frame::FT_OriginalAlbum, TagLib::ASF::Attribute::UnicodeType }, { "WM/OriginalArtist", Frame::FT_OriginalArtist, TagLib::ASF::Attribute::UnicodeType }, { "WM/OriginalReleaseYear", Frame::FT_OriginalDate, TagLib::ASF::Attribute::UnicodeType }, { "WM/SubTitleDescription", Frame::FT_Part, TagLib::ASF::Attribute::UnicodeType }, { "WM/Picture", Frame::FT_Picture, TagLib::ASF::Attribute::BytesType }, { "WM/Publisher", Frame::FT_Publisher, TagLib::ASF::Attribute::UnicodeType }, { "WM/ModifiedBy", Frame::FT_Remixer, TagLib::ASF::Attribute::UnicodeType }, { "WM/SubTitle", Frame::FT_Subtitle, TagLib::ASF::Attribute::UnicodeType }, { "WM/AuthorURL", Frame::FT_Website, TagLib::ASF::Attribute::UnicodeType }, { "AverageLevel", Frame::FT_Other, TagLib::ASF::Attribute::DWordType }, { "PeakValue", Frame::FT_Other, TagLib::ASF::Attribute::DWordType }, { "WM/AudioFileURL", Frame::FT_WWWAudioFile, TagLib::ASF::Attribute::UnicodeType }, { "WM/EncodingSettings", Frame::FT_EncoderSettings, TagLib::ASF::Attribute::UnicodeType }, { "WM/EncodingTime", Frame::FT_EncodingTime, TagLib::ASF::Attribute::BytesType }, { "WM/InitialKey", Frame::FT_InitialKey, TagLib::ASF::Attribute::UnicodeType }, // incorrect WM/Lyrics_Synchronised data make file inaccessible in Windows // { "WM/Lyrics_Synchronised", Frame::FT_Other, TagLib::ASF::Attribute::BytesType }, { "WM/MCDI", Frame::FT_Other, TagLib::ASF::Attribute::BytesType }, { "WM/MediaClassPrimaryID", Frame::FT_Other, TagLib::ASF::Attribute::GuidType }, { "WM/MediaClassSecondaryID", Frame::FT_Other, TagLib::ASF::Attribute::GuidType }, { "WM/Mood", Frame::FT_Mood, TagLib::ASF::Attribute::UnicodeType }, { "WM/OriginalFilename", Frame::FT_Other, TagLib::ASF::Attribute::UnicodeType }, { "WM/OriginalLyricist", Frame::FT_Other, TagLib::ASF::Attribute::UnicodeType }, { "WM/PromotionURL", Frame::FT_Other, TagLib::ASF::Attribute::UnicodeType }, { "WM/SharedUserRating", Frame::FT_Other, TagLib::ASF::Attribute::UnicodeType }, { "WM/WMCollectionGroupID", Frame::FT_Other, TagLib::ASF::Attribute::GuidType }, { "WM/WMCollectionID", Frame::FT_Other, TagLib::ASF::Attribute::GuidType }, { "WM/WMContentID", Frame::FT_Other, TagLib::ASF::Attribute::GuidType } }; /** * Get ASF name and value type for a frame type. * * @param type frame type * @param name the ASF name is returned here * @param value the ASF value type is returned here */ static void getAsfNameForType(Frame::Type type, TagLib::String& name, TagLib::ASF::Attribute::AttributeTypes& value) { static QMap typeNameMap; if (typeNameMap.empty()) { // first time initialization for (unsigned i = 0; i < sizeof(asfNameTypeValues) / sizeof(asfNameTypeValues[0]); ++i) { if (asfNameTypeValues[i].type != Frame::FT_Other && !typeNameMap.contains(asfNameTypeValues[i].type)) { typeNameMap.insert(asfNameTypeValues[i].type, i); } } } name = ""; value = TagLib::ASF::Attribute::UnicodeType; if (type != Frame::FT_Other) { QMap::const_iterator it = typeNameMap.find(type); if (it != typeNameMap.end()) { name = asfNameTypeValues[*it].name; value = asfNameTypeValues[*it].value; } } } /** * Get ASF value type and frame type for an ASF name. * * @param name ASF name * @param type the frame type is returned here * @param value the ASF value type is returned here */ static void getAsfTypeForName(const TagLib::String& name, Frame::Type& type, TagLib::ASF::Attribute::AttributeTypes& value) { static QMap nameTypeMap; if (nameTypeMap.empty()) { // first time initialization for (unsigned i = 0; i < sizeof(asfNameTypeValues) / sizeof(asfNameTypeValues[0]); ++i) { nameTypeMap.insert(asfNameTypeValues[i].name, i); } } QMap::const_iterator it = nameTypeMap.find(name); if (it != nameTypeMap.end()) { type = asfNameTypeValues[*it].type; value = asfNameTypeValues[*it].value; } else { type = Frame::FT_Other; value = TagLib::ASF::Attribute::UnicodeType; } } /** * Get an ASF type for a frame. * * @param frame frame * @param name the name for the attribute is returned here * @param value the ASF value type is returned here */ static void getAsfTypeForFrame(const Frame& frame, TagLib::String& name, TagLib::ASF::Attribute::AttributeTypes& value) { if (frame.getType() != Frame::FT_Other) { getAsfNameForType(frame.getType(), name, value); if (name.isEmpty()) { name = QSTRING_TO_TSTRING(frame.getInternalName()); } } else { Frame::Type type; name = QSTRING_TO_TSTRING(frame.getInternalName()); getAsfTypeForName(name, type, value); } } #if TAGLIB_VERSION >= 0x010700 /** * Get a picture frame from a WM/Picture. * * @param picture ASF picture * @param frame the picture frame is returned here * * @return true if ok. */ static bool parseAsfPicture(const TagLib::ASF::Picture& picture, Frame& frame) { if (!picture.isValid()) return false; TagLib::ByteVector data = picture.picture(); QString description(TStringToQString(picture.description())); PictureFrame::setFields(frame, Frame::Field::TE_ISO8859_1, QLatin1String("JPG"), TStringToQString(picture.mimeType()), static_cast(picture.type()), description, QByteArray(data.data(), data.size())); frame.setType(Frame::FT_Picture); return true; } /** * Render the bytes of a WM/Picture from a picture frame. * * @param frame picture frame * @param picture the ASF picture is returned here */ static void renderAsfPicture(const Frame& frame, TagLib::ASF::Picture& picture) { Frame::Field::TextEncoding enc; PictureFrame::PictureType pictureType; QByteArray data; QString imgFormat, mimeType, description; PictureFrame::getFields(frame, enc, imgFormat, mimeType, pictureType, description, data); if (frame.isValueChanged()) { description = frame.getValue(); } picture.setMimeType(QSTRING_TO_TSTRING(mimeType)); picture.setType(static_cast(pictureType)); picture.setDescription(QSTRING_TO_TSTRING(description)); picture.setPicture(TagLib::ByteVector(data.data(), data.size())); } #elif TAGLIB_VERSION >= 0x010602 /** * Get a picture frame from the bytes in a WM/Picture frame. * The WM/Picture frame has the following data: * 1 byte picture type, 4 bytes (little endian) size of picture data, * UTF16_LE mime type, UTF16_LE description, picture data. * * @param data bytes in WM/Picture frame * @param frame the picture frame is returned here * * @return true if ok. */ static bool parseAsfPicture(const TagLib::ByteVector& data, Frame& frame) { uint len = data.size(); if (len < 10) return false; char pictureType = data[0]; if (pictureType < 0 || pictureType > 20) return false; uint picSize = ((unsigned char)data[1] & 0xff) | (((unsigned char)data[2] & 0xff) << 8) | (((unsigned char)data[3] & 0xff) << 16) | (((unsigned char)data[4] & 0xff) << 24); if (picSize > len - 9) return false; uint offset = 5; QString mimeType = QString::fromUtf16( reinterpret_cast(data.mid(offset, len - offset).data())); offset += mimeType.length() * 2 + 2; if (offset >= len - 1) return false; QString description = QString::fromUtf16( reinterpret_cast(data.mid(offset, len - offset).data())); offset += description.length() * 2 + 2; if (offset > len) return false; TagLib::ByteVector picture = data.mid(offset, len - offset); PictureFrame::setFields(frame, Frame::Field::TE_ISO8859_1, QLatin1String("JPG"), mimeType, static_cast(pictureType), description, QByteArray(picture.data(), picture.size())); frame.setType(Frame::FT_Picture); return true; } /** * Render the bytes of a WM/Picture frame from a picture frame. * * @param frame picture frame * @param data the bytes for the WM/Picture are returned here */ static void renderAsfPicture(const Frame& frame, TagLib::ByteVector& data) { Frame::Field::TextEncoding enc; PictureFrame::PictureType pictureType; QByteArray picture; QString imgFormat, mimeType, description; PictureFrame::getFields(frame, enc, imgFormat, mimeType, pictureType, description, picture); if (frame.isValueChanged()) { description = frame.getValue(); } data.resize(5); data[0] = pictureType; uint size = picture.size(); data[1] = size & 0xff; size >>= 8; data[2] = size & 0xff; size >>= 8; data[3] = size & 0xff; size >>= 8; data[4] = size & 0xff; data.append(QSTRING_TO_TSTRING(mimeType).data(TagLib::String::UTF16LE)); data.append(TagLib::ByteVector(2, 0)); data.append(QSTRING_TO_TSTRING(description).data(TagLib::String::UTF16LE)); data.append(TagLib::ByteVector(2, 0)); data.append(TagLib::ByteVector(picture.data(), picture.size())); } #endif /** * Get an ASF attribute for a frame. * * @param frame frame * @param valueType ASF value type * * @return ASF attribute, an empty attribute is returned if not supported. */ static TagLib::ASF::Attribute getAsfAttributeForFrame( const Frame& frame, TagLib::ASF::Attribute::AttributeTypes valueType) { switch (valueType) { case TagLib::ASF::Attribute::UnicodeType: return TagLib::ASF::Attribute(QSTRING_TO_TSTRING(frame.getValue())); case TagLib::ASF::Attribute::BoolType: return TagLib::ASF::Attribute(frame.getValue() == QLatin1String("1")); case TagLib::ASF::Attribute::WordType: return TagLib::ASF::Attribute(frame.getValue().toUShort()); case TagLib::ASF::Attribute::DWordType: return TagLib::ASF::Attribute(frame.getValue().toUInt()); case TagLib::ASF::Attribute::QWordType: return TagLib::ASF::Attribute(frame.getValue().toULongLong()); case TagLib::ASF::Attribute::BytesType: case TagLib::ASF::Attribute::GuidType: default: if (frame.getType() != Frame::FT_Picture) { QByteArray ba; if (AttributeData(frame.getInternalName()).toByteArray(frame.getValue(), ba)) { return TagLib::ASF::Attribute(TagLib::ByteVector(ba.data(), ba.size())); } QVariant fieldValue = frame.getFieldValue(Frame::Field::ID_Data); if (fieldValue.isValid()) { ba = fieldValue.toByteArray(); return TagLib::ASF::Attribute(TagLib::ByteVector(ba.data(), ba.size())); } } #if TAGLIB_VERSION >= 0x010700 else { TagLib::ASF::Picture picture; renderAsfPicture(frame, picture); return TagLib::ASF::Attribute(picture); } #elif TAGLIB_VERSION >= 0x010602 else { TagLib::ByteVector bv; renderAsfPicture(frame, bv); return TagLib::ASF::Attribute(bv); } #endif } return TagLib::ASF::Attribute(); } #endif /** * Set a frame in the tags 2. * * @param frame frame to set * * @return true if ok. */ bool TagLibFile::setFrameV2(const Frame& frame) { makeFileOpen(); // If the frame has an index, change that specific frame int index = frame.getIndex(); if (index != -1 && m_tagV2) { TagLib::ID3v2::Tag* id3v2Tag; TagLib::Ogg::XiphComment* oggTag; TagLib::APE::Tag* apeTag; #if TAGLIB_VERSION >= 0x010600 #ifdef TAGLIB_WITH_MP4 TagLib::MP4::Tag* mp4Tag; #endif #ifdef TAGLIB_WITH_ASF TagLib::ASF::Tag* asfTag; #endif #endif if ((id3v2Tag = dynamic_cast(m_tagV2)) != 0) { const TagLib::ID3v2::FrameList& frameList = id3v2Tag->frameList(); if (index < static_cast(frameList.size())) { // This is a hack. The frameList should not be modified directly. // However when removing the old frame and adding a new frame, // the indices of all frames get invalid. setId3v2Frame(frameList[index], frame); markTag2Changed(frame.getType()); return true; } } else if ((oggTag = dynamic_cast(m_tagV2)) != 0) { if (frame.getType() == Frame::FT_Picture) { #if TAGLIB_VERSION >= 0x010700 if (m_pictures.isRead()) { int index = frame.getIndex(); if (index >= 0 && index < m_pictures.size()) { Frame newFrame(frame); PictureFrame::setDescription(newFrame, frame.getValue()); if (PictureFrame::areFieldsEqual(m_pictures[index], newFrame)) { m_pictures[index].setValueChanged(false); } else { m_pictures[index] = newFrame; markTag2Changed(Frame::FT_Picture); } return true; } } #endif return false; } TagLib::String key = QSTRING_TO_TSTRING(getVorbisName(frame)); TagLib::String value = QSTRING_TO_TSTRING(frame.getValue()); #if TAGLIB_VERSION <= 0x010400 // Remove all fields with that key, because TagLib <= 1.4 crashes // using an invalidated iterator after calling erase(). oggTag->addField(key, value, true); #else const TagLib::Ogg::FieldListMap& fieldListMap = oggTag->fieldListMap(); if (fieldListMap.contains(key) && fieldListMap[key].size() > 1) { int i = 0; TagLib::String oldValue(TagLib::String::null); for (TagLib::Ogg::FieldListMap::ConstIterator it = fieldListMap.begin(); it != fieldListMap.end(); ++it) { TagLib::StringList stringList = (*it).second; for (TagLib::StringList::ConstIterator slit = stringList.begin(); slit != stringList.end(); ++slit) { if (i++ == index) { oldValue = *slit; break; } } } oggTag->removeField(key, oldValue); oggTag->addField(key, value, false); } else { oggTag->addField(key, value, true); } #endif if (frame.getType() == Frame::FT_Track) { int numTracks = getTotalNumberOfTracksIfEnabled(); if (numTracks > 0) { oggTag->addField("TRACKTOTAL", TagLib::String::number(numTracks), true); } } markTag2Changed(frame.getType()); return true; } else if ((apeTag = dynamic_cast(m_tagV2)) != 0) { if (frame.getType() == Frame::FT_Picture) { return false; } apeTag->addValue(QSTRING_TO_TSTRING(getApeName(frame)), QSTRING_TO_TSTRING(frame.getValue())); markTag2Changed(frame.getType()); return true; #if TAGLIB_VERSION >= 0x010600 #ifdef TAGLIB_WITH_MP4 } else if ((mp4Tag = dynamic_cast(m_tagV2)) != 0) { TagLib::String name; TagLib::MP4::Item item = getMp4ItemForFrame(frame, name); if (item.isValid()) { mp4Tag->itemListMap()[name] = item; markTag2Changed(frame.getType()); } return true; #endif #ifdef TAGLIB_WITH_ASF } else if ((asfTag = dynamic_cast(m_tagV2)) != 0) { #if !(TAGLIB_VERSION >= 0x010602) if (frame.getType() == Frame::FT_Picture) { return false; } #endif switch (index) { case AFI_Title: asfTag->setTitle(QSTRING_TO_TSTRING(frame.getValue())); break; case AFI_Artist: asfTag->setArtist(QSTRING_TO_TSTRING(frame.getValue())); break; case AFI_Comment: asfTag->setComment(QSTRING_TO_TSTRING(frame.getValue())); break; case AFI_Copyright: asfTag->setCopyright(QSTRING_TO_TSTRING(frame.getValue())); break; case AFI_Rating: asfTag->setRating(QSTRING_TO_TSTRING(frame.getValue())); break; case AFI_Attributes: default: { TagLib::String name; TagLib::ASF::Attribute::AttributeTypes valueType; getAsfTypeForFrame(frame, name, valueType); TagLib::ASF::Attribute attribute = getAsfAttributeForFrame(frame, valueType); TagLib::ASF::AttributeListMap& attrListMap = asfTag->attributeListMap(); if (attrListMap.contains(name) && attrListMap[name].size() > 1) { int i = AFI_Attributes; bool found = false; for (TagLib::ASF::AttributeListMap::Iterator it = attrListMap.begin(); it != attrListMap.end(); ++it) { TagLib::ASF::AttributeList& attrList = (*it).second; for (TagLib::ASF::AttributeList::Iterator ait = attrList.begin(); ait != attrList.end(); ++ait) { if (i++ == index) { found = true; *ait = attribute; break; } } if (found) { break; } } } else { asfTag->setAttribute(name, attribute); } } } markTag2Changed(frame.getType()); return true; #endif #endif } } // Try the superclass method return TaggedFile::setFrameV2(frame); } /** * Check if an ID3v2.4.0 frame ID is valid. * * @param frameId frame ID (4 characters) * * @return true if frame ID is valid. */ static bool isFrameIdValid(const QString& frameId) { Frame::Type type; const char* str; getTypeStringForFrameId(TagLib::ByteVector(frameId.toLatin1().data(), 4), type, str); return type != Frame::FT_UnknownFrame; } /** * Add a frame in the tags 2. * * @param frame frame to add, a field list may be added by this method * * @return true if ok. */ bool TagLibFile::addFrameV2(Frame& frame) { TagLib::String::Type enc = getDefaultTextEncoding(); // Add a new frame. if (makeTagV2Settable()) { TagLib::ID3v2::Tag* id3v2Tag; TagLib::Ogg::XiphComment* oggTag; TagLib::APE::Tag* apeTag; #if TAGLIB_VERSION >= 0x010600 #ifdef TAGLIB_WITH_MP4 TagLib::MP4::Tag* mp4Tag; #endif #ifdef TAGLIB_WITH_ASF TagLib::ASF::Tag* asfTag; #endif #endif if ((id3v2Tag = dynamic_cast(m_tagV2)) != 0) { QString name = frame.getType() != Frame::FT_Other ? QString::fromLatin1(getStringForType(frame.getType())) : frame.getName(); QString frameId = name; frameId.truncate(4); TagLib::ID3v2::Frame* id3Frame = 0; if (name == QLatin1String("AverageLevel") || name == QLatin1String("PeakValue") || name.startsWith(QLatin1String("WM/"))) { frameId = QLatin1String("PRIV"); } else if (name.startsWith(QLatin1String("iTun"))) { frameId = QLatin1String("COMM"); } if (frameId.startsWith(QLatin1String("T"))) { if (frameId == QLatin1String("TXXX")) { id3Frame = new TagLib::ID3v2::UserTextIdentificationFrame(enc); } else if (isFrameIdValid(frameId)) { id3Frame = new TagLib::ID3v2::TextIdentificationFrame( TagLib::ByteVector(frameId.toLatin1().data(), frameId.length()), enc); id3Frame->setText(""); // is necessary for createFrame() to work } } else if (frameId == QLatin1String("COMM")) { TagLib::ID3v2::CommentsFrame* commFrame = new TagLib::ID3v2::CommentsFrame(enc); id3Frame = commFrame; commFrame->setLanguage("eng"); // for compatibility with iTunes if (frame.getType() == Frame::FT_Other) { commFrame->setDescription(QSTRING_TO_TSTRING(frame.getName())); } } else if (frameId == QLatin1String("APIC")) { id3Frame = new TagLib::ID3v2::AttachedPictureFrame; ((TagLib::ID3v2::AttachedPictureFrame*)id3Frame)->setTextEncoding(enc); ((TagLib::ID3v2::AttachedPictureFrame*)id3Frame)->setMimeType( "image/jpeg"); ((TagLib::ID3v2::AttachedPictureFrame*)id3Frame)->setType( TagLib::ID3v2::AttachedPictureFrame::FrontCover); } else if (frameId == QLatin1String("UFID")) { // the bytevector must not be empty TagLib::ID3v2::UniqueFileIdentifierFrame* ufidFrame = new TagLib::ID3v2::UniqueFileIdentifierFrame( TagLib::String(), TagLib::ByteVector(" ")); id3Frame = ufidFrame; QByteArray data; if (AttributeData::isHexString(frame.getValue(), 'Z')) { data = (frame.getValue() + QLatin1Char('\0')).toLatin1(); ufidFrame->setIdentifier(TagLib::ByteVector(data.constData(), data.size())); } } else if (frameId == QLatin1String("GEOB")) { id3Frame = new TagLib::ID3v2::GeneralEncapsulatedObjectFrame; ((TagLib::ID3v2::GeneralEncapsulatedObjectFrame*)id3Frame)->setTextEncoding(enc); } else if (frameId.startsWith(QLatin1String("W"))) { if (frameId == QLatin1String("WXXX")) { id3Frame = new TagLib::ID3v2::UserUrlLinkFrame(enc); } else if (isFrameIdValid(frameId)) { id3Frame = new TagLib::ID3v2::UrlLinkFrame( TagLib::ByteVector(frameId.toLatin1().data(), frameId.length())); id3Frame->setText("http://"); // is necessary for createFrame() to work } } else if (frameId == QLatin1String("USLT")) { id3Frame = new TagLib::ID3v2::UnsynchronizedLyricsFrame(enc); ((TagLib::ID3v2::UnsynchronizedLyricsFrame*)id3Frame)->setLanguage("eng"); #if TAGLIB_VERSION >= 0x010600 } else if (frameId == QLatin1String("POPM")) { id3Frame = new TagLib::ID3v2::PopularimeterFrame; } else if (frameId == QLatin1String("PRIV")) { TagLib::ID3v2::PrivateFrame* privFrame = new TagLib::ID3v2::PrivateFrame; id3Frame = privFrame; if (!frame.getName().startsWith(QLatin1String("PRIV"))) { privFrame->setOwner(QSTRING_TO_TSTRING(frame.getName())); QByteArray data; if (AttributeData(frame.getName()).toByteArray(frame.getValue(), data)) { privFrame->setData(TagLib::ByteVector(data.constData(), data.size())); } } #endif #if TAGLIB_VERSION >= 0x010800 } else if (frameId == QLatin1String("OWNE")) { id3Frame = new TagLib::ID3v2::OwnershipFrame(enc); #endif } if (!id3Frame) { TagLib::ID3v2::UserTextIdentificationFrame* txxxFrame = new TagLib::ID3v2::UserTextIdentificationFrame(enc); TagLib::String description; if (frame.getType() == Frame::FT_CatalogNumber) { description = "CATALOGNUMBER"; } else if (frame.getType() == Frame::FT_ReleaseCountry) { description = "RELEASECOUNTRY"; } else { description = QSTRING_TO_TSTRING(frame.getName()); frame.setExtendedType(Frame::ExtendedType(Frame::FT_Other, QLatin1String("TXXX - User defined text information"))); } txxxFrame->setDescription(description); id3Frame = txxxFrame; } else { frame.setExtendedType(Frame::ExtendedType(frame.getType(), name)); } if (id3Frame) { if (!frame.fieldList().empty()) { frame.setValueFromFieldList(); setId3v2Frame(id3Frame, frame); } #ifdef Q_OS_WIN32 // freed in Windows DLL => must be allocated in the same DLL TagLib::ID3v2::Frame* dllAllocatedFrame = TagLib::ID3v2::FrameFactory::instance()->createFrame(id3Frame->render()); if (dllAllocatedFrame) { id3v2Tag->addFrame(dllAllocatedFrame); } #else id3v2Tag->addFrame(id3Frame); #endif frame.setIndex(id3v2Tag->frameList().size() - 1); if (frame.fieldList().empty()) { // add field list to frame getFieldsFromId3Frame(id3Frame, frame.fieldList(), frame.getType()); frame.setFieldListFromValue(); } #ifdef Q_OS_WIN32 delete id3Frame; #endif markTag2Changed(frame.getType()); return true; } } else if ((oggTag = dynamic_cast(m_tagV2)) != 0) { if (frame.getType() == Frame::FT_Picture) { #if TAGLIB_VERSION >= 0x010700 if (m_pictures.isRead()) { if (frame.getFieldList().empty()) { PictureFrame::setFields( frame, Frame::Field::TE_ISO8859_1, QLatin1String("JPG"), QLatin1String("image/jpeg"), PictureFrame::PT_CoverFront, QLatin1String(""), QByteArray()); } PictureFrame::setDescription(frame, frame.getValue()); frame.setIndex(m_pictures.size()); m_pictures.append(frame); markTag2Changed(Frame::FT_Picture); return true; } #endif return false; } QString name(getVorbisName(frame)); TagLib::String tname = QSTRING_TO_TSTRING(name); TagLib::String tvalue = QSTRING_TO_TSTRING(frame.getValue()); if (tvalue.isEmpty()) { tvalue = " "; // empty values are not added by TagLib } #if TAGLIB_VERSION <= 0x010400 oggTag->addField(tname, tvalue); #else oggTag->addField(tname, tvalue, false); #endif frame.setExtendedType(Frame::ExtendedType(frame.getType(), name)); const TagLib::Ogg::FieldListMap& fieldListMap = oggTag->fieldListMap(); int index = 0; bool found = false; for (TagLib::Ogg::FieldListMap::ConstIterator it = fieldListMap.begin(); it != fieldListMap.end(); ++it) { if ((*it).first == tname) { index += (*it).second.size() - 1; found = true; break; } index += (*it).second.size(); } frame.setIndex(found ? index : -1); markTag2Changed(frame.getType()); return true; } else if ((apeTag = dynamic_cast(m_tagV2)) != 0) { if (frame.getType() == Frame::FT_Picture) { return false; } QString name(getApeName(frame)); TagLib::String tname = QSTRING_TO_TSTRING(name); TagLib::String tvalue = QSTRING_TO_TSTRING(frame.getValue()); if (tvalue.isEmpty()) { tvalue = " "; // empty values are not added by TagLib } apeTag->addValue(tname, tvalue, true); frame.setExtendedType(Frame::ExtendedType(frame.getType(), name)); const TagLib::APE::ItemListMap& itemListMap = apeTag->itemListMap(); int index = 0; bool found = false; for (TagLib::APE::ItemListMap::ConstIterator it = itemListMap.begin(); it != itemListMap.end(); ++it) { if ((*it).first == tname) { found = true; break; } ++index; } frame.setIndex(found ? index : -1); markTag2Changed(frame.getType()); return true; #if TAGLIB_VERSION >= 0x010600 #ifdef TAGLIB_WITH_MP4 } else if ((mp4Tag = dynamic_cast(m_tagV2)) != 0) { #if TAGLIB_VERSION >= 0x010602 if (frame.getType() == Frame::FT_Picture && frame.getFieldList().empty()) { PictureFrame::setFields(frame); } #endif TagLib::String name; TagLib::MP4::Item item = getMp4ItemForFrame(frame, name); if (!item.isValid()) { return false; } frame.setExtendedType(Frame::ExtendedType(frame.getType(), TStringToQString(name))); prefixMp4FreeFormName(name); mp4Tag->itemListMap()[name] = item; const TagLib::MP4::ItemListMap& itemListMap = mp4Tag->itemListMap(); int index = 0; bool found = false; for (TagLib::MP4::ItemListMap::ConstIterator it = itemListMap.begin(); it != itemListMap.end(); ++it) { if ((*it).first == name) { found = true; break; } ++index; } frame.setIndex(found ? index : -1); markTag2Changed(frame.getType()); return true; #endif #ifdef TAGLIB_WITH_ASF } else if ((asfTag = dynamic_cast(m_tagV2)) != 0) { #if TAGLIB_VERSION >= 0x010602 if (frame.getType() == Frame::FT_Picture && frame.getFieldList().empty()) { PictureFrame::setFields(frame); } #else if (frame.getType() == Frame::FT_Picture) { return false; } #endif TagLib::String name; TagLib::ASF::Attribute::AttributeTypes valueType; getAsfTypeForFrame(frame, name, valueType); if (valueType == TagLib::ASF::Attribute::BytesType && frame.getType() != Frame::FT_Picture) { Frame::Field field; field.m_id = Frame::Field::ID_Data; field.m_value = QByteArray(); frame.fieldList().push_back(field); } TagLib::ASF::Attribute attribute = getAsfAttributeForFrame(frame, valueType); asfTag->addAttribute(name, attribute); frame.setExtendedType(Frame::ExtendedType(frame.getType(), TStringToQString(name))); const TagLib::ASF::AttributeListMap& attrListMap = asfTag->attributeListMap(); int index = AFI_Attributes; bool found = false; for (TagLib::ASF::AttributeListMap::ConstIterator it = attrListMap.begin(); it != attrListMap.end(); ++it) { if ((*it).first == name) { index += (*it).second.size() - 1; found = true; break; } index += (*it).second.size(); } frame.setIndex(found ? index : -1); markTag2Changed(frame.getType()); return true; #endif #endif } } // Try the superclass method return TaggedFile::addFrameV2(frame); } /** * Delete a frame in the tags 2. * * @param frame frame to delete. * * @return true if ok. */ bool TagLibFile::deleteFrameV2(const Frame& frame) { makeFileOpen(); // If the frame has an index, delete that specific frame int index = frame.getIndex(); if (index != -1 && m_tagV2) { TagLib::ID3v2::Tag* id3v2Tag; TagLib::Ogg::XiphComment* oggTag; TagLib::APE::Tag* apeTag; #if TAGLIB_VERSION >= 0x010600 #ifdef TAGLIB_WITH_MP4 TagLib::MP4::Tag* mp4Tag; #endif #ifdef TAGLIB_WITH_ASF TagLib::ASF::Tag* asfTag; #endif #endif if ((id3v2Tag = dynamic_cast(m_tagV2)) != 0) { const TagLib::ID3v2::FrameList& frameList = id3v2Tag->frameList(); if (index < static_cast(frameList.size())) { id3v2Tag->removeFrame(frameList[index]); markTag2Changed(frame.getType()); return true; } } else if ((oggTag = dynamic_cast(m_tagV2)) != 0) { #if TAGLIB_VERSION >= 0x010700 if (frame.getType() == Frame::FT_Picture) { if (m_pictures.isRead()) { int index = frame.getIndex(); if (index >= 0 && index < m_pictures.size()) { m_pictures.removeAt(index); markTag2Changed(Frame::FT_Picture); return true; } } } #endif TagLib::String key = QSTRING_TO_TSTRING(frame.getInternalName()); #if TAGLIB_VERSION <= 0x010400 // Remove all fields with that key, because TagLib <= 1.4 crashes // using an invalidated iterator after calling erase(). oggTag->removeField(key); #else oggTag->removeField(key, QSTRING_TO_TSTRING(frame.getValue())); #endif markTag2Changed(frame.getType()); return true; } else if ((apeTag = dynamic_cast(m_tagV2)) != 0) { TagLib::String key = QSTRING_TO_TSTRING(frame.getInternalName()); apeTag->removeItem(key); markTag2Changed(frame.getType()); return true; #if TAGLIB_VERSION >= 0x010600 #ifdef TAGLIB_WITH_MP4 } else if ((mp4Tag = dynamic_cast(m_tagV2)) != 0) { TagLib::String name = QSTRING_TO_TSTRING(frame.getInternalName()); prefixMp4FreeFormName(name); mp4Tag->itemListMap().erase(name); markTag2Changed(frame.getType()); return true; #endif #ifdef TAGLIB_WITH_ASF } else if ((asfTag = dynamic_cast(m_tagV2)) != 0) { switch (index) { case AFI_Title: asfTag->setTitle(""); break; case AFI_Artist: asfTag->setArtist(""); break; case AFI_Comment: asfTag->setComment(""); break; case AFI_Copyright: asfTag->setCopyright(""); break; case AFI_Rating: asfTag->setRating(""); break; case AFI_Attributes: default: { TagLib::String name = QSTRING_TO_TSTRING(frame.getInternalName()); TagLib::ASF::AttributeListMap& attrListMap = asfTag->attributeListMap(); if (attrListMap.contains(name) && attrListMap[name].size() > 1) { int i = AFI_Attributes; bool found = false; for (TagLib::ASF::AttributeListMap::Iterator it = attrListMap.begin(); it != attrListMap.end(); ++it) { TagLib::ASF::AttributeList& attrList = (*it).second; for (TagLib::ASF::AttributeList::Iterator ait = attrList.begin(); ait != attrList.end(); ++ait) { if (i++ == index) { found = true; attrList.erase(ait); break; } } if (found) { break; } } } else { asfTag->removeItem(name); } } } markTag2Changed(frame.getType()); return true; #endif #endif } } // Try the superclass method return TaggedFile::deleteFrameV2(frame); } /** * Remove ID3v2 frames. * * @param flt filter specifying which frames to remove */ void TagLibFile::deleteFramesV2(const FrameFilter& flt) { makeFileOpen(); if (m_tagV2) { TagLib::ID3v2::Tag* id3v2Tag; TagLib::Ogg::XiphComment* oggTag; TagLib::APE::Tag* apeTag; #if TAGLIB_VERSION >= 0x010600 #ifdef TAGLIB_WITH_MP4 TagLib::MP4::Tag* mp4Tag; #endif #ifdef TAGLIB_WITH_ASF TagLib::ASF::Tag* asfTag; #endif #endif if (flt.areAllEnabled()) { if ((id3v2Tag = dynamic_cast(m_tagV2)) != 0) { const TagLib::ID3v2::FrameList& frameList = id3v2Tag->frameList(); for (TagLib::ID3v2::FrameList::ConstIterator it = frameList.begin(); it != frameList.end();) { id3v2Tag->removeFrame(*it++, true); } markTag2Changed(Frame::FT_UnknownFrame); } else if ((oggTag = dynamic_cast(m_tagV2)) != 0) { const TagLib::Ogg::FieldListMap& fieldListMap = oggTag->fieldListMap(); for (TagLib::Ogg::FieldListMap::ConstIterator it = fieldListMap.begin(); it != fieldListMap.end();) { oggTag->removeField((*it++).first); } markTag2Changed(Frame::FT_UnknownFrame); } else if ((apeTag = dynamic_cast(m_tagV2)) != 0) { const TagLib::APE::ItemListMap& itemListMap = apeTag->itemListMap(); for (TagLib::APE::ItemListMap::ConstIterator it = itemListMap.begin(); it != itemListMap.end();) { apeTag->removeItem((*it++).first); } markTag2Changed(Frame::FT_UnknownFrame); #if TAGLIB_VERSION >= 0x010600 #ifdef TAGLIB_WITH_MP4 } else if ((mp4Tag = dynamic_cast(m_tagV2)) != 0) { mp4Tag->itemListMap().clear(); markTag2Changed(Frame::FT_UnknownFrame); #endif #ifdef TAGLIB_WITH_ASF } else if ((asfTag = dynamic_cast(m_tagV2)) != 0) { asfTag->setTitle(""); asfTag->setArtist(""); asfTag->setComment(""); asfTag->setCopyright(""); asfTag->setRating(""); asfTag->attributeListMap().clear(); markTag2Changed(Frame::FT_UnknownFrame); #endif #endif } else { TaggedFile::deleteFramesV2(flt); } } else { if ((id3v2Tag = dynamic_cast(m_tagV2)) != 0) { const TagLib::ID3v2::FrameList& frameList = id3v2Tag->frameList(); Frame::Type type; const char* name; for (TagLib::ID3v2::FrameList::ConstIterator it = frameList.begin(); it != frameList.end();) { getTypeStringForFrameId((*it)->frameID(), type, name); if (flt.isEnabled(type, QString::fromLatin1(name))) { id3v2Tag->removeFrame(*it++, true); } else { ++it; } } markTag2Changed(Frame::FT_UnknownFrame); } else if ((oggTag = dynamic_cast(m_tagV2)) != 0) { const TagLib::Ogg::FieldListMap& fieldListMap = oggTag->fieldListMap(); for (TagLib::Ogg::FieldListMap::ConstIterator it = fieldListMap.begin(); it != fieldListMap.end();) { QString name(TStringToQString((*it).first)); if (flt.isEnabled(getTypeFromVorbisName(name), name)) { oggTag->removeField((*it++).first); } else { ++it; } } markTag2Changed(Frame::FT_UnknownFrame); } else if ((apeTag = dynamic_cast(m_tagV2)) != 0) { const TagLib::APE::ItemListMap& itemListMap = apeTag->itemListMap(); for (TagLib::APE::ItemListMap::ConstIterator it = itemListMap.begin(); it != itemListMap.end();) { QString name(TStringToQString((*it).first)); if (flt.isEnabled(getTypeFromApeName(name), name)) { apeTag->removeItem((*it++).first); } else { ++it; } } markTag2Changed(Frame::FT_UnknownFrame); #if TAGLIB_VERSION >= 0x010600 #ifdef TAGLIB_WITH_MP4 } else if ((mp4Tag = dynamic_cast(m_tagV2)) != 0) { TagLib::MP4::ItemListMap& itemListMap = mp4Tag->itemListMap(); Frame::Type type; Mp4ValueType valueType; for (TagLib::MP4::ItemListMap::Iterator it = itemListMap.begin(); it != itemListMap.end();) { getMp4TypeForName((*it).first, type, valueType); QString name(TStringToQString((*it).first)); if (flt.isEnabled(type, name)) { itemListMap.erase(it++); } else { ++it; } } markTag2Changed(Frame::FT_UnknownFrame); #endif #ifdef TAGLIB_WITH_ASF } else if ((asfTag = dynamic_cast(m_tagV2)) != 0) { if (flt.isEnabled(Frame::FT_Title)) asfTag->setTitle(""); if (flt.isEnabled(Frame::FT_Artist)) asfTag->setArtist(""); if (flt.isEnabled(Frame::FT_Comment)) asfTag->setComment(""); if (flt.isEnabled(Frame::FT_Copyright)) asfTag->setCopyright(""); if (flt.isEnabled(Frame::FT_Other, QLatin1String("Rating"))) asfTag->setRating(""); TagLib::ASF::AttributeListMap& attrListMap = asfTag->attributeListMap(); Frame::Type type; TagLib::ASF::Attribute::AttributeTypes valueType; for (TagLib::ASF::AttributeListMap::Iterator it = attrListMap.begin(); it != attrListMap.end();) { getAsfTypeForName((*it).first, type, valueType); QString name(TStringToQString((*it).first)); if (flt.isEnabled(type, name)) { attrListMap.erase(it++); } else { ++it; } } markTag2Changed(Frame::FT_UnknownFrame); #endif #endif } else { TaggedFile::deleteFramesV2(flt); } } } } /** * Get all frames in tag 2. * * @param frames frame collection to set. */ void TagLibFile::getAllFramesV2(FrameCollection& frames) { makeFileOpen(); frames.clear(); if (m_tagV2) { TagLib::ID3v2::Tag* id3v2Tag; TagLib::Ogg::XiphComment* oggTag; TagLib::APE::Tag* apeTag; #if TAGLIB_VERSION >= 0x010600 #ifdef TAGLIB_WITH_MP4 TagLib::MP4::Tag* mp4Tag; #endif #ifdef TAGLIB_WITH_ASF TagLib::ASF::Tag* asfTag; #endif #endif if ((id3v2Tag = dynamic_cast(m_tagV2)) != 0) { const TagLib::ID3v2::FrameList& frameList = id3v2Tag->frameList(); int i = 0; Frame::Type type; const char* name; for (TagLib::ID3v2::FrameList::ConstIterator it = frameList.begin(); it != frameList.end(); ++it) { getTypeStringForFrameId((*it)->frameID(), type, name); Frame frame(type, TStringToQString((*it)->toString()), QString::fromLatin1(name), i++); frame.setValue(getFieldsFromId3Frame(*it, frame.fieldList(), type)); if ((*it)->frameID().mid(1, 3) == "XXX" || type == Frame::FT_Comment) { QVariant fieldValue = frame.getFieldValue(Frame::Field::ID_Description); if (fieldValue.isValid()) { QString description = fieldValue.toString(); if (!description.isEmpty()) { if (description == QLatin1String("CATALOGNUMBER")) { frame.setType(Frame::FT_CatalogNumber); } else if (description == QLatin1String("RELEASECOUNTRY")) { frame.setType(Frame::FT_ReleaseCountry); } else { if (description.startsWith(QLatin1String("QuodLibet::"))) { // remove ExFalso/QuodLibet "namespace" description = description.mid(11); } frame.setExtendedType(Frame::ExtendedType(Frame::FT_Other, QString::fromLatin1(name) + QLatin1Char('\n') + description)); } } } #if TAGLIB_VERSION >= 0x010600 } else if ((*it)->frameID().startsWith("PRIV")) { QVariant fieldValue = frame.getFieldValue(Frame::Field::ID_Owner); if (fieldValue.isValid()) { QString owner = fieldValue.toString(); if (!owner.isEmpty()) { frame.setExtendedType(Frame::ExtendedType(Frame::FT_Other, QString::fromLatin1(name) + QLatin1Char('\n') + owner)); } } #endif } frames.insert(frame); } } else if ((oggTag = dynamic_cast(m_tagV2)) != 0) { const TagLib::Ogg::FieldListMap& fieldListMap = oggTag->fieldListMap(); int i = 0; for (TagLib::Ogg::FieldListMap::ConstIterator it = fieldListMap.begin(); it != fieldListMap.end(); ++it) { QString name = TStringToQString((*it).first); Frame::Type type = getTypeFromVorbisName(name); TagLib::StringList stringList = (*it).second; for (TagLib::StringList::ConstIterator slit = stringList.begin(); slit != stringList.end(); ++slit) { frames.insert(Frame(type, TStringToQString(TagLib::String(*slit)), name, i++)); } } #if TAGLIB_VERSION >= 0x010700 if (m_pictures.isRead()) { foreach (const Frame& frame, m_pictures) { frames.insert(frame); } } #endif } else if ((apeTag = dynamic_cast(m_tagV2)) != 0) { const TagLib::APE::ItemListMap& itemListMap = apeTag->itemListMap(); int i = 0; for (TagLib::APE::ItemListMap::ConstIterator it = itemListMap.begin(); it != itemListMap.end(); ++it) { QString name = TStringToQString((*it).first); TagLib::StringList values = (*it).second.toStringList(); Frame::Type type = getTypeFromApeName(name); frames.insert( Frame(type, values.size() > 0 ? TStringToQString(values.front()) : QLatin1String(""), name, i++)); } #if TAGLIB_VERSION >= 0x010600 #ifdef TAGLIB_WITH_MP4 } else if ((mp4Tag = dynamic_cast(m_tagV2)) != 0) { const TagLib::MP4::ItemListMap& itemListMap = mp4Tag->itemListMap(); int i = 0; for (TagLib::MP4::ItemListMap::ConstIterator it = itemListMap.begin(); it != itemListMap.end(); ++it) { TagLib::String name = (*it).first; stripMp4FreeFormName(name); Frame::Type type; Mp4ValueType valueType; getMp4TypeForName(name, type, valueType); QString value; bool frameAlreadyInserted = false; switch (valueType) { case MVT_String: { TagLib::StringList strings = (*it).second.toStringList(); value = strings.size() > 0 ? TStringToQString(strings.front()) : QLatin1String(""); break; } case MVT_Bool: value = (*it).second.toBool() ? QLatin1String("1") : QLatin1String("0"); break; case MVT_Int: value.setNum((*it).second.toInt()); break; case MVT_IntPair: { TagLib::MP4::Item::IntPair intPair = (*it).second.toIntPair(); value.setNum(intPair.first); if (intPair.second != 0) { value += QLatin1Char('/'); value += QString::number(intPair.second); } break; } #if TAGLIB_VERSION >= 0x010602 case MVT_CoverArt: { TagLib::MP4::CoverArtList coverArtList = (*it).second.toCoverArtList(); if (coverArtList.size() > 0) { const TagLib::MP4::CoverArt& coverArt = coverArtList.front(); TagLib::ByteVector bv = coverArt.data(); Frame frame(type, QLatin1String(""), TStringToQString(name), i++); QByteArray ba; ba = QByteArray(bv.data(), bv.size()); PictureFrame::setFields( frame, Frame::Field::TE_ISO8859_1, coverArt.format() == TagLib::MP4::CoverArt::PNG ? QLatin1String("PNG") : QLatin1String("JPG"), coverArt.format() == TagLib::MP4::CoverArt::PNG ? QLatin1String("image/png") : QLatin1String("image/jpeg"), PictureFrame::PT_CoverFront, QLatin1String(""), ba); frames.insert(frame); frameAlreadyInserted = true; } break; } #endif #if TAGLIB_VERSION >= 0x010800 case MVT_Byte: value.setNum((*it).second.toByte()); break; case MVT_UInt: value.setNum((*it).second.toUInt()); break; case MVT_LongLong: value.setNum((*it).second.toLongLong()); break; #endif case MVT_ByteArray: default: // binary data and album art are not handled by TagLib value = QLatin1String(""); } if (!frameAlreadyInserted) frames.insert( Frame(type, value, TStringToQString(name), i++)); } #endif #ifdef TAGLIB_WITH_ASF } else if ((asfTag = dynamic_cast(m_tagV2)) != 0) { TagLib::String name; TagLib::ASF::Attribute::AttributeTypes valueType; Frame::Type type = Frame::FT_Title; getAsfNameForType(type, name, valueType); QString value = TStringToQString(asfTag->title()); frames.insert(Frame(type, value, TStringToQString(name), AFI_Title)); type = Frame::FT_Artist; getAsfNameForType(type, name, valueType); value = TStringToQString(asfTag->artist()); frames.insert(Frame(type, value, TStringToQString(name), AFI_Artist)); type = Frame::FT_Comment; getAsfNameForType(type, name, valueType); value = TStringToQString(asfTag->comment()); frames.insert(Frame(type, value, TStringToQString(name), AFI_Comment)); type = Frame::FT_Copyright; getAsfNameForType(type, name, valueType); value = TStringToQString(asfTag->copyright()); frames.insert(Frame(type, value, TStringToQString(name), AFI_Copyright)); name = "Rating"; getAsfTypeForName(name, type, valueType); value = TStringToQString(asfTag->rating()); frames.insert(Frame(type, value, TStringToQString(name), AFI_Rating)); int i = AFI_Attributes; QByteArray ba; const TagLib::ASF::AttributeListMap& attrListMap = asfTag->attributeListMap(); for (TagLib::ASF::AttributeListMap::ConstIterator it = attrListMap.begin(); it != attrListMap.end(); ++it) { name = (*it).first; getAsfTypeForName(name, type, valueType); for (TagLib::ASF::AttributeList::ConstIterator ait = (*it).second.begin(); ait != (*it).second.end(); ++ait) { switch ((*ait).type()) { case TagLib::ASF::Attribute::UnicodeType: value = TStringToQString((*ait).toString()); break; case TagLib::ASF::Attribute::BoolType: value = (*ait).toBool() ? QLatin1String("1") : QLatin1String("0"); break; case TagLib::ASF::Attribute::DWordType: value.setNum((*ait).toUInt()); break; case TagLib::ASF::Attribute::QWordType: value.setNum((*ait).toULongLong()); break; case TagLib::ASF::Attribute::WordType: value.setNum((*ait).toUShort()); break; case TagLib::ASF::Attribute::BytesType: case TagLib::ASF::Attribute::GuidType: default: { TagLib::ByteVector bv = (*ait).toByteVector(); ba = QByteArray(bv.data(), bv.size()); value = QLatin1String(""); AttributeData(TStringToQString(name)).toString(ba, value); } } Frame frame(type, value, TStringToQString(name), i); if ((*ait).type() == TagLib::ASF::Attribute::BytesType && valueType == TagLib::ASF::Attribute::BytesType) { Frame::Field field; field.m_id = Frame::Field::ID_Data; field.m_value = ba; frame.fieldList().push_back(field); } #if TAGLIB_VERSION >= 0x010700 ++i; if (type == Frame::FT_Picture) { parseAsfPicture((*ait).toPicture(), frame); } frames.insert(frame); #elif TAGLIB_VERSION >= 0x010602 ++i; if (type == Frame::FT_Picture) { parseAsfPicture((*ait).toByteVector(), frame); } frames.insert(frame); #else if (type != Frame::FT_Picture) { ++i; frames.insert(frame); } #endif } } #endif #endif } else { TaggedFile::getAllFramesV2(frames); } } frames.addMissingStandardFrames(); } /** * Close file handle which is held open by the TagLib object. */ void TagLibFile::closeFileHandle() { closeFile(false); } /** * Get a list of frame IDs which can be added. * * @return list with frame IDs. */ QStringList TagLibFile::getFrameIds() const { QStringList lst; if (m_tagTypeV2 == TT_Id3v2) { for (int k = Frame::FT_FirstFrame; k <= Frame::FT_LastFrame; ++k) { lst.append(Frame::ExtendedType(static_cast(k), QLatin1String("")). getTranslatedName()); } for (unsigned i = 0; i < sizeof(typeStrOfId) / sizeof(typeStrOfId[0]); ++i) { const TypeStrOfId& ts = typeStrOfId[i]; if (ts.type == Frame::FT_Other && ts.supported && ts.str) { lst.append(QCoreApplication::translate("@default", ts.str)); } } #if TAGLIB_VERSION >= 0x010600 #ifdef TAGLIB_WITH_MP4 } else if (m_tagTypeV2 == TT_Mp4) { TagLib::String name; Mp4ValueType valueType; Frame::Type type; for (int k = Frame::FT_FirstFrame; k <= Frame::FT_LastFrame; ++k) { name = ""; type = static_cast(k); getMp4NameForType(type, name, valueType); if (!name.isEmpty() && valueType != MVT_ByteArray && !(name[0] >= 'A' && name[0] <= 'Z')) { lst.append(Frame::ExtendedType(type, QLatin1String("")).getTranslatedName()); } } for (unsigned i = 0; i < sizeof(mp4NameTypeValues) / sizeof(mp4NameTypeValues[0]); ++i) { if (mp4NameTypeValues[i].type == Frame::FT_Other && mp4NameTypeValues[i].value != MVT_ByteArray && !(mp4NameTypeValues[i].name[0] >= 'A' && mp4NameTypeValues[i].name[0] <= 'Z')) { lst.append(QString::fromLatin1(mp4NameTypeValues[i].name)); } } #endif #ifdef TAGLIB_WITH_ASF } else if (m_tagTypeV2 == TT_Asf) { TagLib::String name; TagLib::ASF::Attribute::AttributeTypes valueType; Frame::Type type; for (int k = Frame::FT_FirstFrame; k <= Frame::FT_LastFrame; ++k) { name = ""; type = static_cast(k); getAsfNameForType(type, name, valueType); if (!name.isEmpty() #if !(TAGLIB_VERSION >= 0x010602) && type != Frame::FT_Picture #endif ) { lst.append(Frame::ExtendedType(type, QLatin1String("")).getTranslatedName()); } } for (unsigned i = 0; i < sizeof(asfNameTypeValues) / sizeof(asfNameTypeValues[0]); ++i) { if (asfNameTypeValues[i].type == Frame::FT_Other) { lst.append(QString::fromLatin1(asfNameTypeValues[i].name)); } } #endif #endif } else { static const char* const fieldNames[] = { "CONTACT", "DESCRIPTION", "EAN/UPN", "ENCODING", "ENGINEER", "ENSEMBLE", "GUESTARTIST", "LABEL", "LABELNO", "LICENSE", "LOCATION", "OPUS", "ORGANIZATION", "PARTNUMBER", "PRODUCER", "PRODUCTNUMBER", "RECORDINGDATE", "RELEASEDATE", "SOURCEARTIST", "SOURCEMEDIUM", "SOURCEWORK", "SPARS", "TRACKTOTAL", "VERSION", "VOLUME" }; #if TAGLIB_VERSION >= 0x010700 const bool picturesSupported = m_pictures.isRead(); #else const bool picturesSupported = false; #endif for (int k = Frame::FT_FirstFrame; k <= Frame::FT_LastFrame; ++k) { if (k != Frame::FT_Picture || picturesSupported) { lst.append(Frame::ExtendedType(static_cast(k), QLatin1String("")). getTranslatedName()); } } for (unsigned i = 0; i < sizeof(fieldNames) / sizeof(fieldNames[0]); ++i) { lst.append(QString::fromLatin1(fieldNames[i])); } } return lst; } /** * Set the text codec to be used for tag 1. * * @param codec text codec, 0 to use default (ISO 8859-1) */ void TagLibFile::setTextCodecV1(const QTextCodec* codec) { TextCodecStringHandler::setTextCodec(codec); } /** * Set the default text encoding. * * @param textEnc default text encoding */ void TagLibFile::setDefaultTextEncoding(TagConfig::TextEncoding textEnc) { // Do not use TagLib::ID3v2::FrameFactory::setDefaultTextEncoding(), // it will change the encoding of existing frames read in, not only // of newly created frames, which is really not what we want! switch (textEnc) { case TagConfig::TE_ISO8859_1: s_defaultTextEncoding = TagLib::String::Latin1; break; case TagConfig::TE_UTF16: s_defaultTextEncoding = TagLib::String::UTF16; break; case TagConfig::TE_UTF8: default: s_defaultTextEncoding = TagLib::String::UTF8; } } /** * Notify about configuration change. * This method shall be called when the configuration changes. */ void TagLibFile::notifyConfigurationChange() { const QTextCodec* id3v1TextCodec = TagConfig::instance().textEncodingV1() != QLatin1String("ISO-8859-1") ? QTextCodec::codecForName(TagConfig::instance().textEncodingV1().toLatin1().data()) : 0; setDefaultTextEncoding( static_cast(TagConfig::instance().textEncoding())); setTextCodecV1(id3v1TextCodec); } /** * Register open TagLib file, so that the number of open files can be limited. * If the number of open files exceeds a limit, files are closed. * * @param tagLibFile new open file to be registered */ void TagLibFile::registerOpenFile(TagLibFile* tagLibFile) { if (s_openFiles.contains(tagLibFile)) return; int numberOfFilesToClose = s_openFiles.size() - 15; if (numberOfFilesToClose > 5) { QList filesToClose; for (QList::iterator it = s_openFiles.begin(); it != s_openFiles.end(); ++it) { TagLibFile* tlf = *it; if (!tlf->isTag1Changed() && !tlf->isTag2Changed()) { filesToClose.append(tlf); if (--numberOfFilesToClose <= 0) { break; } } } for (QList::iterator it = filesToClose.begin(); it != filesToClose.end(); ++it) { (*it)->closeFile(false); } } s_openFiles.append(tagLibFile); } /** * Deregister open TagLib file. * * @param tagLibFile file which is no longer open */ void TagLibFile::deregisterOpenFile(TagLibFile* tagLibFile) { s_openFiles.removeAll(tagLibFile); } /** * Used to register file types at static initialization time. */ class TagLibInitializer { public: /** Constructor. */ TagLibInitializer(); /** Destructor. */ ~TagLibInitializer(); /** * Initialization. * Is deferred because it will crash on Mac OS X if done in the constructor. */ void init(); private: Q_DISABLE_COPY(TagLibInitializer) #if TAGLIB_VERSION <= 0x010400 SpeexFileTypeResolver* m_speexFileTypeResolver; WavPackFileTypeResolver* m_wavPackFileTypeResolver; TTAFileTypeResolver* m_ttaFileTypeResolver; #endif AACFileTypeResolver* m_aacFileTypeResolver; MP2FileTypeResolver* m_mp2FileTypeResolver; TextCodecStringHandler* m_textCodecStringHandler; }; TagLibInitializer::TagLibInitializer() : #if TAGLIB_VERSION <= 0x010400 m_speexFileTypeResolver(new SpeexFileTypeResolver), m_wavPackFileTypeResolver(new WavPackFileTypeResolver), m_ttaFileTypeResolver(new TTAFileTypeResolver), #endif m_aacFileTypeResolver(new AACFileTypeResolver), m_mp2FileTypeResolver(new MP2FileTypeResolver), m_textCodecStringHandler(new TextCodecStringHandler) { } void TagLibInitializer::init() { #if TAGLIB_VERSION <= 0x010400 TagLib::FileRef::addFileTypeResolver(m_speexFileTypeResolver); TagLib::FileRef::addFileTypeResolver(m_wavPackFileTypeResolver); TagLib::FileRef::addFileTypeResolver(m_ttaFileTypeResolver); #endif TagLib::FileRef::addFileTypeResolver(m_aacFileTypeResolver); TagLib::FileRef::addFileTypeResolver(m_mp2FileTypeResolver); TagLib::ID3v1::Tag::setStringHandler(m_textCodecStringHandler); } TagLibInitializer::~TagLibInitializer() { delete m_textCodecStringHandler; delete m_mp2FileTypeResolver; delete m_aacFileTypeResolver; #if TAGLIB_VERSION <= 0x010400 delete m_ttaFileTypeResolver; delete m_wavPackFileTypeResolver; delete m_speexFileTypeResolver; #endif } static TagLibInitializer tagLibInitializer; /** * Static initialization. * Registers file types. */ void TagLibFile::staticInit() { tagLibInitializer.init(); } kid3-3.0.2/src/plugins/taglibmetadata/taglibfile.h000066400000000000000000000403411224603511300220330ustar00rootroot00000000000000/** * \file taglibfile.h * Handling of tagged files using TagLib. * * \b Project: Kid3 * \author Urs Fleisch * \date 12 Sep 2006 * * Copyright (C) 2006-2012 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef TAGLIBFILE_H #define TAGLIBFILE_H #include "taglibconfig.h" #include #include "taggedfile.h" #include "tagconfig.h" #include #include #include #include /** TagLib version in with 8 bits for major, minor and patch version. */ #define TAGLIB_VERSION (((TAGLIB_MAJOR_VERSION) << 16) + \ ((TAGLIB_MINOR_VERSION) << 8) + (TAGLIB_PATCH_VERSION)) /* Workaround for the wrong TAGLIB_MINOR_VERSION in TagLib 1.8.0 */ #if defined HAVE_TAGLIB_ID3V23_SUPPORT && TAGLIB_VERSION == 0x010700 #undef TAGLIB_VERSION #define TAGLIB_VERSION 0x010800 #endif /** Convert QString @a s to a TagLib::String. */ #define QSTRING_TO_TSTRING(s) TagLib::String(s.toUtf8().data(), TagLib::String::UTF8) class QTextCodec; /** List box item containing tagged file. */ class TagLibFile : public TaggedFile { public: /** * Constructor. * * @param dn directory name * @param fn filename * @param idx model index */ TagLibFile(const QString& dn, const QString& fn, const QPersistentModelIndex& idx); /** * Destructor. */ virtual ~TagLibFile(); /** * Get key of tagged file format. * @return "TaglibMetadata". */ virtual QString taggedFileKey() const; /** * Get features supported. * @return bit mask with Feature flags set. */ virtual int taggedFileFeatures() const; /** * Get currently active tagged file features. * @return active tagged file features (TF_ID3v23, TF_ID3v24, or 0). * @see setActiveTaggedFileFeatures() */ virtual int activeTaggedFileFeatures() const; /** * Activate some features provided by the tagged file. * TagLibFile provides the TF_ID3v23 and TF_ID3v24 features, which determine * the ID3v2 version used in writeTags() (the overload without id3v2Version). * If 0 is set, the default behavior applies, i.e. for new files, * TagConfig::id3v2Version() is used, else the existing version. * * @param features TF_ID3v23, TF_ID3v24, or 0 */ virtual void setActiveTaggedFileFeatures(int features); /** * Read tags from file. * * @param force true to force reading even if tags were already read. */ virtual void readTags(bool force); /** * Write tags to file and rename it if necessary. * * @param force true to force writing even if file was not changed. * @param renamed will be set to true if the file was renamed, * i.e. the file name is no longer valid, else *renamed * is left unchanged * @param preserve true to preserve file time stamps * * @return true if ok, false if the file could not be written or renamed. */ virtual bool writeTags(bool force, bool* renamed, bool preserve); /** * Write tags to file and rename it if necessary. * * @param force true to force writing even if file was not changed. * @param renamed will be set to true if the file was renamed, * i.e. the file name is no longer valid, else *renamed * is left unchanged * @param preserve true to preserve file time stamps * @param id3v2Version ID3v2 version to use, 0 to use existing or preferred, * 3 to force ID3v2.3.0, 4 to force ID3v2.4.0. Is ignored * if TagLib version is less than 1.8.0. * * @return true if ok, false if the file could not be written or renamed. */ bool writeTags(bool force, bool* renamed, bool preserve, int id3v2Version); /** * Remove ID3v1 frames. * * @param flt filter specifying which frames to remove */ virtual void deleteFramesV1(const FrameFilter& flt); /** * Remove ID3v2 frames. * * @param flt filter specifying which frames to remove */ virtual void deleteFramesV2(const FrameFilter& flt); /** * Get ID3v1 title. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ virtual QString getTitleV1(); /** * Get ID3v1 artist. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ virtual QString getArtistV1(); /** * Get ID3v1 album. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ virtual QString getAlbumV1(); /** * Get ID3v1 comment. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ virtual QString getCommentV1(); /** * Get ID3v1 year. * * @return number, * 0 if the field does not exist, * -1 if the tags do not exist. */ virtual int getYearV1(); /** * Get ID3v1 track. * * @return number, * 0 if the field does not exist, * -1 if the tags do not exist. */ virtual int getTrackNumV1(); /** * Get ID3v1 genre. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ virtual QString getGenreV1(); /** * Get ID3v2 title. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ virtual QString getTitleV2(); /** * Get ID3v2 artist. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ virtual QString getArtistV2(); /** * Get ID3v2 album. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ virtual QString getAlbumV2(); /** * Get ID3v2 comment. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ virtual QString getCommentV2(); /** * Get ID3v2 year. * * @return number, * 0 if the field does not exist, * -1 if the tags do not exist. */ virtual int getYearV2(); /** * Get ID3v2 track. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ virtual QString getTrackV2(); /** * Get ID3v2 genre as text. * * @return string, * "" if the field does not exist, * QString::null if the tags do not exist. */ virtual QString getGenreV2(); /** * Set ID3v1 title. * * @param str string to set, "" to remove field. */ virtual void setTitleV1(const QString& str); /** * Set ID3v1 artist. * * @param str string to set, "" to remove field. */ virtual void setArtistV1(const QString& str); /** * Set ID3v1 album. * * @param str string to set, "" to remove field. */ virtual void setAlbumV1(const QString& str); /** * Set ID3v1 comment. * * @param str string to set, "" to remove field. */ virtual void setCommentV1(const QString& str); /** * Set ID3v1 year. * * @param num number to set, 0 to remove field. */ virtual void setYearV1(int num); /** * Set ID3v1 track. * * @param num number to set, 0 to remove field. */ virtual void setTrackNumV1(int num); /** * Set ID3v1 genre as text. * * @param str string to set, "" to remove field, QString::null to ignore. */ virtual void setGenreV1(const QString& str); /** * Set ID3v2 title. * * @param str string to set, "" to remove field. */ virtual void setTitleV2(const QString& str); /** * Set ID3v2 artist. * * @param str string to set, "" to remove field. */ virtual void setArtistV2(const QString& str); /** * Set ID3v2 album. * * @param str string to set, "" to remove field. */ virtual void setAlbumV2(const QString& str); /** * Set ID3v2 comment. * * @param str string to set, "" to remove field. */ virtual void setCommentV2(const QString& str); /** * Set ID3v2 year. * * @param num number to set, 0 to remove field. */ virtual void setYearV2(int num); /** * Set ID3v2 track. * * @param track string to set, "" to remove field, QString::null to ignore. */ virtual void setTrackV2(const QString& track); /** * Set ID3v2 genre as text. * * @param str string to set, "" to remove field, QString::null to ignore. */ virtual void setGenreV2(const QString& str); /** * Check if tag information has already been read. * * @return true if information is available, * false if the tags have not been read yet, in which case * hasTagV1() and hasTagV2() do not return meaningful information. */ virtual bool isTagInformationRead() const; /** * Check if file has an ID3v1 tag. * * @return true if a V1 tag is available. * @see isTagInformationRead() */ virtual bool hasTagV1() const; /** * Check if file has an ID3v2 tag. * * @return true if a V2 tag is available. * @see isTagInformationRead() */ virtual bool hasTagV2() const; /** * Check if ID3v1 tags are supported by the format of this file. * * @return true. */ virtual bool isTagV1Supported() const; /** * Get technical detail information. * * @param info the detail information is returned here */ virtual void getDetailInfo(DetailInfo& info) const; /** * Get duration of file. * * @return duration in seconds, * 0 if unknown. */ virtual unsigned getDuration() const; /** * Get file extension including the dot. * * @return file extension ".mp3". */ virtual QString getFileExtension() const; /** * Get the format of tag 1. * * @return string describing format of tag 1, * e.g. "ID3v1.1", "ID3v2.3", "Vorbis", "APE", * QString::null if unknown. */ virtual QString getTagFormatV1() const; /** * Get the format of tag 2. * * @return string describing format of tag 2, * e.g. "ID3v1.1", "ID3v2.3", "Vorbis", "APE", * QString::null if unknown. */ virtual QString getTagFormatV2() const; /** * Set a frame in the tags 2. * * @param frame frame to set * * @return true if ok. */ virtual bool setFrameV2(const Frame& frame); /** * Add a frame in the tags 2. * * @param frame frame to add, a field list may be added by this method * * @return true if ok. */ virtual bool addFrameV2(Frame& frame); /** * Delete a frame in the tags 2. * * @param frame frame to delete. * * @return true if ok. */ virtual bool deleteFrameV2(const Frame& frame); /** * Get all frames in tag 2. * * @param frames frame collection to set. */ virtual void getAllFramesV2(FrameCollection& frames); /** * Close file handle which is held open by the TagLib object. */ virtual void closeFileHandle(); /** * Get a list of frame IDs which can be added. * * @return list with frame IDs. */ virtual QStringList getFrameIds() const; /** * Static initialization. * Registers file types. */ static void staticInit(); /** * Get the default text encoding. * @return default text encoding. */ static TagLib::String::Type getDefaultTextEncoding() { return s_defaultTextEncoding; } /** * Notify about configuration change. * This method shall be called when the configuration changes. */ static void notifyConfigurationChange(); private: /** Tag type for cached information. */ enum TagType { TT_Unknown, TT_Id3v1, TT_Id3v2, TT_Vorbis, TT_Ape, TT_Mp4, TT_Asf }; TagLibFile(const TagLibFile&); TagLibFile& operator=(const TagLibFile&); /** * Modify an ID3v2 frame. * * @param id3Frame original ID3v2 frame * @param frame frame with fields to set in new frame */ void setId3v2Frame( TagLib::ID3v2::Frame* id3Frame, const Frame& frame) const; /** * Close file handle. * TagLib keeps the file handle open until the FileRef is destroyed. * This causes problems when the operating system has a limited number of * open file handles. This method closes the file by assigning a new file * reference. Note that this will also invalidate the tag pointers. * The file is only closed if there are no unsaved tag changes or if the * @a force parameter is set. * * @param force true to close the file even if tags are changed */ void closeFile(bool force = false); /** * Make sure that file is open. * This method should be called before accessing m_fileRef, m_tagV1, m_tagV2. * * @param force true to force reopening of file even if it is already open */ void makeFileOpen(bool force = false); /** * Create m_tagV1 if it does not already exist so that it can be set. * * @return true if m_tagV1 can be set. */ bool makeTagV1Settable(); /** * Create m_tagV2 if it does not already exist so that it can be set. * * @return true if m_tagV2 can be set. */ bool makeTagV2Settable(); /** * Cache technical detail information. */ void readAudioProperties(); #if TAGLIB_VERSION >= 0x010800 /** * Get tracker name of a module file. * * @return tracker name, null if not found. */ QString getTrackerName() const; #endif /** * Get the format of a tag. * * @param tag tag, 0 if no tag available * @param type the tag type is returned here * * @return string describing format of tag, * e.g. "ID3v1.1", "ID3v2.3", "Vorbis", "APE", * QString::null if unknown. */ static QString getTagFormat(const TagLib::Tag* tag, TagType& type); /** * Register open TagLib file, so that the number of open files can be limited. * If the number of open files exceeds a limit, files are closed. * * @param tagLibFile new open file to be registered */ static void registerOpenFile(TagLibFile* tagLibFile); /** * Deregister open TagLib file. * * @param tagLibFile file which is no longer open */ static void deregisterOpenFile(TagLibFile* tagLibFile); /** * Set the text codec to be used for tag 1. * * @param codec text codec, 0 to use default (ISO 8859-1) */ static void setTextCodecV1(const QTextCodec* codec); /** * Set the default text encoding. * * @param textEnc default text encoding */ static void setDefaultTextEncoding(TagConfig::TextEncoding textEnc); TagLib::FileRef m_fileRef; /**< file reference */ TagLib::Tag* m_tagV1; /**< ID3v1 tags */ TagLib::Tag* m_tagV2; /**< ID3v2 tags */ #if TAGLIB_VERSION >= 0x010800 int m_id3v2Version; /**< 3 for ID3v2.3, 4 for ID3v2.4, 0 if none */ #endif int m_activatedFeatures; /**< TF_ID3v23, TF_ID3v24, or 0 */ bool m_fileRead; /**< true if file has been read */ /* Cached information updated in readTags() */ bool m_tagInformationRead; bool m_hasTagV1; bool m_hasTagV2; bool m_isTagV1Supported; unsigned m_duration; TagType m_tagTypeV1; TagType m_tagTypeV2; QString m_tagFormatV1; QString m_tagFormatV2; QString m_fileExtension; DetailInfo m_detailInfo; #if TAGLIB_VERSION >= 0x010700 class Pictures : public QList { public: Pictures() : m_read(false) {} bool isRead() const { return m_read; } void setRead(bool read) { m_read = read; } private: bool m_read; }; Pictures m_pictures; #endif /** default text encoding */ static TagLib::String::Type s_defaultTextEncoding; /** list of TagLib files with open file descriptor */ static QList s_openFiles; }; #endif // TAGLIBFILE_H kid3-3.0.2/src/plugins/taglibmetadata/taglibmetadataplugin.cpp000066400000000000000000000134311224603511300244460ustar00rootroot00000000000000/** * \file taglibmetadataplugin.cpp * TagLib metadata plugin. * * \b Project: Kid3 * \author Urs Fleisch * \date 27 Jul 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "taglibmetadataplugin.h" #include "taglibfile.h" #if QT_VERSION < 0x050000 Q_EXPORT_PLUGIN2(TaglibMetadataPlugin, TaglibMetadataPlugin) #endif static const QLatin1String TAGGEDFILE_KEY("TaglibMetadata"); /*! * Constructor. * @param parent parent object */ TaglibMetadataPlugin::TaglibMetadataPlugin(QObject* parent) : QObject(parent) { setObjectName(QLatin1String("TaglibMetadata")); } /** * Destructor. */ TaglibMetadataPlugin::~TaglibMetadataPlugin() { } /** * Get name of factory, the same as the QObject::objectName() of the plugin. * @return factory name. */ QString TaglibMetadataPlugin::name() const { return objectName(); } /** * Get keys of available tagged file formats. * @return list of keys. */ QStringList TaglibMetadataPlugin::taggedFileKeys() const { return QStringList() << TAGGEDFILE_KEY; } /** * Get features supported. * @param key tagged file key * @return bit mask with TaggedFile::Feature flags set. */ int TaglibMetadataPlugin::taggedFileFeatures(const QString& key) const { if (key == TAGGEDFILE_KEY) { return TaggedFile::TF_ID3v11 | TaggedFile::TF_ID3v22 | #if TAGLIB_VERSION >= 0x010800 TaggedFile::TF_ID3v23 | #endif TaggedFile::TF_ID3v24; } return 0; } /** * Initialize tagged file factory. * * @param key tagged file key */ void TaglibMetadataPlugin::initialize(const QString& key) { if (key == TAGGEDFILE_KEY) { TagLibFile::staticInit(); } } /** * Create a tagged file. * * @param key tagged file key * @param dirName directory name * @param fileName filename * @param idx model index * @param features optional tagged file features (TaggedFile::Feature flags) * to activate at creation * * @return tagged file, 0 if type not supported. */ TaggedFile* TaglibMetadataPlugin::createTaggedFile( const QString& key, const QString& dirName, const QString& fileName, const QPersistentModelIndex& idx, int features) { #if TAGLIB_VERSION >= 0x010800 Q_UNUSED(features) #endif if (key == TAGGEDFILE_KEY) { QString ext = fileName.right(4).toLower(); QString ext2 = ext.right(3); if (((ext == QLatin1String(".mp3") || ext == QLatin1String(".mp2") || ext == QLatin1String(".aac")) #if TAGLIB_VERSION < 0x010800 && (TagConfig::instance().id3v2Version() == TagConfig::ID3v2_4_0 || (features & TaggedFile::TF_ID3v24) != 0) #endif ) || ext == QLatin1String(".mpc") || ext == QLatin1String(".oga") || ext == QLatin1String(".ogg") || ext == QLatin1String("flac") || ext == QLatin1String(".spx") || ext == QLatin1String(".tta") #if TAGLIB_VERSION >= 0x010600 #ifdef TAGLIB_WITH_MP4 || ext == QLatin1String(".m4a") || ext == QLatin1String(".m4b") || ext == QLatin1String(".m4p") || ext == QLatin1String(".mp4") #endif #ifdef TAGLIB_WITH_ASF || ext == QLatin1String(".wma") || ext == QLatin1String(".asf") #endif || ext == QLatin1String(".aif") || ext == QLatin1String("aiff") || ext == QLatin1String(".wav") #endif #if TAGLIB_VERSION >= 0x010700 || ext == QLatin1String(".ape") #endif #if TAGLIB_VERSION >= 0x010800 || ext == QLatin1String(".mod") || ext == QLatin1String(".s3m") || ext2 == QLatin1String(".it") #ifdef HAVE_TAGLIB_XM_SUPPORT || ext2 == QLatin1String(".xm") #endif #endif #if TAGLIB_VERSION >= 0x010900 || ext == QLatin1String("opus") #endif || ext2 == QLatin1String(".wv")) return new TagLibFile(dirName, fileName, idx); } return 0; } /** * Get a list with all extensions (e.g. ".mp3") supported by TaggedFile subclass. * * @param key tagged file key * * @return list of file extensions. */ QStringList TaglibMetadataPlugin::supportedFileExtensions(const QString& key) const { if (key == TAGGEDFILE_KEY) { return QStringList() << QLatin1String(".flac") << QLatin1String(".mp3") << QLatin1String(".mpc") << QLatin1String(".oga") << QLatin1String(".ogg") << QLatin1String(".spx") << QLatin1String(".tta") << QLatin1String(".aac") << QLatin1String(".mp2") << #if TAGLIB_VERSION >= 0x010600 #ifdef TAGLIB_WITH_MP4 QLatin1String(".m4a") << QLatin1String(".m4b") << QLatin1String(".m4p") << QLatin1String(".mp4") << #endif #ifdef TAGLIB_WITH_ASF QLatin1String(".wma") << QLatin1String(".asf") << #endif QLatin1String(".aif") << QLatin1String(".aiff") << QLatin1String(".wav") << #endif #if TAGLIB_VERSION >= 0x010700 QLatin1String(".ape") << #endif #if TAGLIB_VERSION >= 0x010800 QLatin1String(".mod") << QLatin1String(".s3m") << QLatin1String(".it") << #ifdef HAVE_TAGLIB_XM_SUPPORT QLatin1String(".xm") << #endif #endif #if TAGLIB_VERSION >= 0x010900 QLatin1String(".opus") << #endif QLatin1String(".wv"); } return QStringList(); } /** * Notify about configuration change. * This method shall be called when the configuration changes. * * @param key tagged file key */ void TaglibMetadataPlugin::notifyConfigurationChange(const QString& key) { if (key == TAGGEDFILE_KEY) { TagLibFile::notifyConfigurationChange(); } } kid3-3.0.2/src/plugins/taglibmetadata/taglibmetadataplugin.h000066400000000000000000000057211224603511300241160ustar00rootroot00000000000000/** * \file taglibmetadataplugin.h * TagLib metadata plugin. * * \b Project: Kid3 * \author Urs Fleisch * \date 27 Jul 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef TAGLIBMETADATAPLUGIN_H #define TAGLIBMETADATAPLUGIN_H #include #include "itaggedfilefactory.h" /** * TagLib metadata plugin. */ class KID3_PLUGIN_EXPORT TaglibMetadataPlugin : public QObject, public ITaggedFileFactory { Q_OBJECT #if QT_VERSION >= 0x050000 Q_PLUGIN_METADATA(IID "net.sourceforge.kid3.ITaggedFileFactory") #endif Q_INTERFACES(ITaggedFileFactory) public: /*! * Constructor. * @param parent parent object */ explicit TaglibMetadataPlugin(QObject* parent = 0); /** * Destructor. */ virtual ~TaglibMetadataPlugin(); /** * Get name of factory, the same as the QObject::objectName() of the plugin. * @return factory name. */ virtual QString name() const; /** * Get keys of available tagged file formats. * @return list of keys. */ virtual QStringList taggedFileKeys() const; /** * Get features supported. * @param key tagged file key * @return bit mask with TaggedFile::Feature flags set. */ virtual int taggedFileFeatures(const QString& key) const; /** * Initialize tagged file factory. * * @param key tagged file key */ virtual void initialize(const QString& key); /** * Create a tagged file. * * @param dirName directory name * @param fileName filename * @param idx model index * @param features optional tagged file features (TaggedFile::Feature flags) * to activate at creation * * @return tagged file, 0 if type not supported. */ virtual TaggedFile* createTaggedFile( const QString& key, const QString& dirName, const QString& fileName, const QPersistentModelIndex& idx, int features = 0); /** * Get a list with all extensions (e.g. ".mp3") supported by TaggedFile subclass. * * @param key tagged file key * * @return list of file extensions. */ virtual QStringList supportedFileExtensions(const QString& key) const; /** * Notify about configuration change. * This method shall be called when the configuration changes. * * @param key tagged file key */ virtual void notifyConfigurationChange(const QString& key); }; #endif // TAGLIBMETADATAPLUGIN_H kid3-3.0.2/src/test/000077500000000000000000000000001224603511300141115ustar00rootroot00000000000000kid3-3.0.2/src/test/CMakeLists.txt000066400000000000000000000014001224603511300166440ustar00rootroot00000000000000include_directories( ../core/utils ../core/model ../core/tags ../core/import ../core/config ) set(test_SRCS dummysettings.cpp testutils.cpp testjsonparser.cpp testserverimporterbase.cpp testmusicbrainzreleaseimporter.cpp testmusicbrainzreleaseimportparser.cpp testdiscogsimporter.cpp testdiscogsimportparser.cpp maintest.cpp ) set(test_MOC_HDRS testjsonparser.h testserverimporterbase.h testmusicbrainzreleaseimporter.h testmusicbrainzreleaseimportparser.h testdiscogsimporter.h testdiscogsimportparser.h ) qt4_wrap_cpp(test_GEN_MOC_SRCS ${test_MOC_HDRS}) add_definitions(${QT_DEFINITIONS} ${QT_EXECUTABLE_COMPILE_FLAGS}) add_executable(kid3-test ${test_SRCS} ${test_GEN_MOC_SRCS}) target_link_libraries(kid3-test kid3-core ${QT_QTTEST_LIBRARY} -lstdc++) kid3-3.0.2/src/test/dummysettings.cpp000066400000000000000000000041211224603511300175270ustar00rootroot00000000000000/** * \file dummysettings.cpp * Application settings stub for tests. * * \b Project: Kid3 * \author Urs Fleisch * \date 03 Jun 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "dummysettings.h" /** * Constructor. */ DummySettings::DummySettings() { } /** * Destructor. */ DummySettings::~DummySettings() { } /** * Use settings subgroup. * @param prefix group name */ void DummySettings::beginGroup(const QString& prefix) { Q_UNUSED(prefix) } /** * Finnish using settings subgroup. */ void DummySettings::endGroup() { } /** * Set value for setting. * @param key name of setting * @param value value for setting */ void DummySettings::setValue(const QString& key, const QVariant& value) { Q_UNUSED(key) Q_UNUSED(value) } /** * Get value for setting. * @param key name of setting * @param defaultValue default value * @return value of setting as variant. */ QVariant DummySettings::value(const QString& key, const QVariant& defaultValue) const { Q_UNUSED(key) return defaultValue; } /** * Remove setting. * @param key name of setting */ void DummySettings::remove(const QString& key) { Q_UNUSED(key) } /** * Check if setting exists. * @param key name of setting * @return true if setting exists. */ bool DummySettings::contains(const QString& key) const { Q_UNUSED(key) return false; } /** * Write unsaved changes to permanent storage. */ void DummySettings::sync() { } kid3-3.0.2/src/test/dummysettings.h000066400000000000000000000041631224603511300172020ustar00rootroot00000000000000/** * \file dummysettings.h * Application settings stub for tests. * * \b Project: Kid3 * \author Urs Fleisch * \date 03 Jun 2013 * * Copyright (C) 2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef DUMMYSETTINGS_H #define DUMMYSETTINGS_H #include "isettings.h" class QString; /** * Application settings stub for tests. */ class DummySettings : public ISettings { public: /** * Constructor. */ DummySettings(); /** * Destructor. */ virtual ~DummySettings(); /** * Use settings subgroup. * @param prefix group name */ virtual void beginGroup(const QString& prefix); /** * Finnish using settings subgroup. */ virtual void endGroup(); /** * Set value for setting. * @param key name of setting * @param value value for setting */ virtual void setValue(const QString& key, const QVariant& value); /** * Get value for setting. * @param key name of setting * @param defaultValue default value * @return value of setting as variant. */ virtual QVariant value(const QString& key, const QVariant& defaultValue) const; /** * Remove setting. * @param key name of setting */ virtual void remove(const QString& key); /** * Check if setting exists. * @param key name of setting * @return true if setting exists. */ virtual bool contains(const QString& key) const; /** * Write unsaved changes to permanent storage. */ virtual void sync(); }; #endif // DUMMYSETTINGS_H kid3-3.0.2/src/test/maintest.cpp000066400000000000000000000036441224603511300164500ustar00rootroot00000000000000/** * \file maintest.cpp * Main program for executing unit test cases. * Besides the standard QtTest options, this test runner also allows to select * the testcase with "-testcase" and list the testcases with "-testcases". * * \b Project: Kid3 * \author Urs Fleisch * \date 07 Oct 2012 * * Copyright (C) 2012 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include "testutils.h" #include "testjsonparser.h" #include "testmusicbrainzreleaseimportparser.h" #include "testmusicbrainzreleaseimporter.h" #include "testdiscogsimportparser.h" #include "testdiscogsimporter.h" /** * Main routine for test runner. * * @param argc number of arguments (including the command) * @param argv command and arguments * * @return 0 if OK, the number of failed tests if failed. */ int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); QStringList args = app.arguments(); static QObject* const testCases[] = { new TestJsonParser, new TestMusicBrainzReleaseImportParser, new TestMusicBrainzReleaseImporter, new TestDiscogsImportParser, new TestDiscogsImporter, 0 }; QObject ts; for (QObject* const* tcPtr = testCases; *tcPtr; ++tcPtr) { (*tcPtr)->setParent(&ts); } return TestUtils::runTestSuite(ts, args); } kid3-3.0.2/src/test/testdiscogsimporter.cpp000066400000000000000000000072651224603511300207440ustar00rootroot00000000000000/** * \file testdiscogsimporter.cpp * Test import from Discogs server. * * \b Project: Kid3 * \author Urs Fleisch * \date 09 Oct 2012 * * Copyright (C) 2012-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "testdiscogsimporter.h" #include #include "serverimporter.h" #include "trackdatamodel.h" void TestDiscogsImporter::initTestCase() { setServerImporter(QLatin1String("DiscogsImport")); } void TestDiscogsImporter::testQueryAlbums() { queryAlbums(QLatin1String("Wizard"), QLatin1String("Odin")); QStandardItemModel* albumModel = m_importer->getAlbumListModel(); QVERIFY(albumModel->rowCount() > 0); AlbumListItem* item = static_cast(albumModel->item(0, 0)); QVERIFY(item); QVERIFY(item->type() == AlbumListItem::Type); QCOMPARE(item->text(), QString(QLatin1String("Wizard - Odin"))); QCOMPARE(item->getCategory(), QString(QLatin1String("releases"))); QVERIFY(!item->getId().isEmpty()); } void TestDiscogsImporter::testQueryTracks() { queryTracks(QLatin1String("releases"), QLatin1String("2487778")); QStringList titles; titles << QLatin1String("The Prophecy") << QLatin1String("Betrayer") << QLatin1String("Dead Hope") << QLatin1String("Dark God") << QLatin1String("Lokis Punishment") << QLatin1String("Beginning Of The End") << QLatin1String("Thor's Hammer") << QLatin1String("Hall Of Odin") << QLatin1String("The Powergod") << QLatin1String("March Of The Einherjers") << QLatin1String("End Of All") << QLatin1String("Ultimate War (Bonus Track)") << QLatin1String("Golden Dawn (Bonus Track)") << QLatin1String("Betrayer"); QStringList lengths; lengths << QLatin1String("5:19") << QLatin1String("4:53") << QLatin1String("6:02") << QLatin1String("5:43") << QLatin1String("5:08") << QLatin1String("4:01") << QLatin1String("5:01") << QLatin1String("5:06") << QLatin1String("5:21") << QLatin1String("5:40") << QLatin1String("3:53") << QLatin1String("4:52") << QLatin1String("5:05") << QLatin1String(""); QCOMPARE(m_trackDataModel->rowCount(), 14); for (int row = 0; row < 11; ++row) { QCOMPARE(m_trackDataModel->index(row, 0).data().toString(), lengths.at(row)); QCOMPARE(m_trackDataModel->index(row, 3).data().toInt(), row + 1); QCOMPARE(m_trackDataModel->index(row, 4).data().toString(), titles.at(row)); QCOMPARE(m_trackDataModel->index(row, 5).data().toString(), QString(QLatin1String("Wizard"))); QCOMPARE(m_trackDataModel->index(row, 6).data().toString(), QString(QLatin1String("Odin"))); QCOMPARE(m_trackDataModel->index(row, 7).data().toInt(), 2003); QCOMPARE(m_trackDataModel->index(row, 8).data().toString(), QString(QLatin1String("Heavy Metal"))); QCOMPARE(m_trackDataModel->index(row, 10).data().toString(), QString(QLatin1String("LMP 0303-054 Ltd. CD"))); QCOMPARE(m_trackDataModel->index(row, 11).data().toString(), QString(QLatin1String("CD"))); QCOMPARE(m_trackDataModel->index(row, 12).data().toString(), QString(QLatin1String("LMP"))); QCOMPARE(m_trackDataModel->index(row, 13).data().toString(), QString(QLatin1String("Germany"))); } } kid3-3.0.2/src/test/testdiscogsimporter.h000066400000000000000000000021351224603511300204000ustar00rootroot00000000000000/** * \file testdiscogsimporter.h * Test import from Discogs server. * * \b Project: Kid3 * \author Urs Fleisch * \date 09 Oct 2012 * * Copyright (C) 2012 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef TESTDISCOGSIMPORTER_H #define TESTDISCOGSIMPORTER_H #include "testserverimporterbase.h" class TestDiscogsImporter : public TestServerImporterBase { Q_OBJECT private slots: void initTestCase(); void testQueryAlbums(); void testQueryTracks(); }; #endif kid3-3.0.2/src/test/testdiscogsimportparser.cpp000066400000000000000000000300711224603511300216210ustar00rootroot00000000000000/** * \file testdiscogsimportparser.cpp * Test parsing of import data from Discogs server. * * \b Project: Kid3 * \author Urs Fleisch * \date 09 Oct 2012 * * Copyright (C) 2012-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "testdiscogsimportparser.h" #include #include "serverimporter.h" #include "trackdatamodel.h" void TestDiscogsImportParser::initTestCase() { setServerImporter(QLatin1String("DiscogsImport")); } void TestDiscogsImportParser::testParseAlbums() { static const char searchStr[] = "{\"pagination\": {\"per_page\": 50, \"pages\": 1, \"page\": 1, \"urls\": {}, \"items\": 10}, \"results\": [{\"style\": [\"Heavy Metal\"], \"thumb\": \"http://api.discogs.com/image/R-90-2487778-1293847958.jpeg\", \"format\": [\"CD\", \"Album\", \"Limited Edition\", \"Enhanced\"], \"country\": \"Germany\", \"title\": \"Wizard (23) - Odin\", \"uri\": \"/Wizard-Odin/release/2487778\", \"label\": [\"LMP\"], \"catno\": \"LMP 0303-054 Ltd. CD\", \"year\": \"2003\", \"genre\": [\"Rock\"], \"resource_url\": \"http://api.discogs.com/releases/2487778\", \"type\": \"release\", \"id\": 2487778}, {\"style\": [\"Heavy Metal\"], \"thumb\": \"http://api.discogs.com/image/R-90-2487932-1293847561.jpeg\", \"format\": [\"CD\", \"Album\", \"Enhanced\"], \"country\": \"Japan\", \"title\": \"Wizard (23) - Odin\", \"uri\": \"/Wizard-Odin/release/2487932\", \"label\": [\"Soundholic\"], \"catno\": \"TKCS-85065\", \"year\": \"2003\", \"genre\": [\"Rock\"], \"resource_url\": \"http://api.discogs.com/releases/2487932\", \"type\": \"release\", \"id\": 2487932}, {\"style\": [\"Heavy Metal\"], \"thumb\": \"http://api.discogs.com/image/R-90-3312195-1339346316-2529.jpeg\", \"format\": [\"CD\", \"Album\", \"Promo\"], \"country\": \"Germany\", \"barcode\": [\"6 93723 65472 0\", \"GEMA\", \"LC 00924\", \"-LMP0303-054CD - Invictus - Black Heart 001225384 Limb Music Products\"], \"uri\": \"/Wizard-Odin/release/3312195\", \"label\": [\"LMP\", \"SPV GmbH\"], \"catno\": \"LMP 0303-054 CD\", \"year\": \"2003\", \"genre\": [\"Rock\"], \"title\": \"Wizard (23) - Odin\", \"resource_url\": \"http://api.discogs.com/releases/3312195\", \"type\": \"release\", \"id\": 3312195}, {\"style\": [\"Speed Metal\"], \"thumb\": \"http://s.discogss.com/images/record90.png\", \"format\": [\"Vinyl\", \"LP\", \"Picture Disc\", \"Limited Edition\"], \"country\": \"Germany\", \"barcode\": [\"6 93723 65471 3\", \"GEMA\"], \"uri\": \"/Wizard-Odin/release/3675993\", \"label\": [\"LMP\", \"SPV GmbH\"], \"catno\": \"LMP 0303-054 Ltd. PIC LP\", \"year\": \"2003\", \"genre\": [\"Rock\"], \"title\": \"Wizard (23) - Odin\", \"resource_url\": \"http://api.discogs.com/releases/3675993\", \"type\": \"release\", \"id\": 3675993}, {\"style\": [\"Heavy Metal\"], \"thumb\": \"http://api.discogs.com/image/R-90-2487767-1293847243.jpeg\", \"format\": [\"CD\", \"Album\", \"Enhanced\"], \"country\": \"Germany\", \"barcode\": [\"4 028466 116178\", \"GEMA\", \"LC 06398\"], \"uri\": \"/Wizard-Thor/release/2487767\", \"label\": [\"Massacre Records\", \"Massacre Records\", \"Massacre Records\", \"Soulfood\", \"The Red Room\", \"Magic Hall Studios\"], \"catno\": \"MAS DP0617\", \"year\": \"2009\", \"genre\": [\"Rock\"], \"title\": \"Wizard (23) - Thor\", \"resource_url\": \"http://api.discogs.com/releases/2487767\", \"type\": \"release\", \"id\": 2487767}, {\"style\": [\"Black Metal\"], \"thumb\": \"http://api.discogs.com/image/R-90-3573177-1335800447.jpeg\", \"format\": [\"Cassette\", \"Single Sided\"], \"country\": \"Germany\", \"title\": \"Desaster - Lost In The Ages\", \"uri\": \"/Desaster-Lost-In-The-Ages/release/3573177\", \"label\": [\"Not On Label (Desaster Self-released)\"], \"catno\": \"none\", \"year\": \"1994\", \"genre\": [\"Rock\"], \"resource_url\": \"http://api.discogs.com/releases/3573177\", \"type\": \"release\", \"id\": 3573177}, {\"style\": [\"New Wave\", \"Goth Rock\"], \"thumb\": \"http://api.discogs.com/image/R-90-1764235-1244486470.jpeg\", \"format\": [\"CD\", \"Album\"], \"country\": \"France\", \"barcode\": [\"3770001009247\"], \"uri\": \"/Babel-17-The-Ice-Wall/release/1764235\", \"label\": [\"Infrastition\"], \"catno\": \"Ino 001\", \"year\": \"2009\", \"genre\": [\"Electronic\", \"Rock\"], \"title\": \"Babel 17 - The Ice Wall\", \"resource_url\": \"http://api.discogs.com/releases/1764235\", \"type\": \"release\", \"id\": 1764235}, {\"style\": [\"Thrash\", \"Black Metal\"], \"thumb\": \"http://api.discogs.com/image/R-90-2894728-1306089742.jpeg\", \"format\": [\"Vinyl\", \"12\\\"\", \"Picture Disc\", \"Compilation\", \"Limited Edition\", \"Vinyl\", \"12\\\"\", \"Single Sided\", \"Etched\", \"Limited Edition\"], \"country\": \"Germany\", \"title\": \"Desaster - 20 Years Of Total Desaster\", \"uri\": \"/Desaster-20-Years-Of-Total-Desaster/release/2894728\", \"label\": [\"Kneel Before The Master's Throne Records\"], \"catno\": \"KNEEL 026\", \"year\": \"2009\", \"genre\": [\"Rock\"], \"resource_url\": \"http://api.discogs.com/releases/2894728\", \"type\": \"release\", \"id\": 2894728}, {\"style\": [\"Abstract\", \"Ambient\"], \"thumb\": \"http://api.discogs.com/image/R-90-1399903-1216441299.jpeg\", \"format\": [\"CD\", \"Album\"], \"country\": \"US\", \"title\": \"Mike Kelley - Day Is Done / Original Motion Picture Soundtrack\", \"uri\": \"/Mike-Kelley-Day-Is-Done-Original-Motion-Picture-Soundtrack/release/1399903\", \"label\": [\"Compound Annex Records\"], \"catno\": \"Compound#14\", \"year\": \"2005\", \"genre\": [\"Non-Music\", \"Stage & Screen\"], \"resource_url\": \"http://api.discogs.com/releases/1399903\", \"type\": \"release\", \"id\": 1399903}, {\"style\": [\"Black Metal\", \"Viking Metal\", \"Psychedelic Rock\", \"Prog Rock\"], \"thumb\": \"http://api.discogs.com/image/R-90-3045032-1313173110.jpeg\", \"format\": [\"CDr\", \"CD-ROM\", \"Compilation\", \"Unofficial Release\"], \"country\": \"Russia\", \"barcode\": [\"(03318103\", \"MB-UG-80GPM1\"], \"uri\": \"/Enslaved-Einherjer-Mp3-Collection/release/3045032\", \"label\": [\"MP3SERVICE\"], \"catno\": \"none\", \"year\": \"2004\", \"genre\": [\"Rock\"], \"title\": \"Enslaved & Einherjer - Mp3 Collection\", \"resource_url\": \"http://api.discogs.com/releases/3045032\", \"type\": \"release\", \"id\": 3045032}]}"; onFindFinished(searchStr); QStandardItemModel* albumModel = m_importer->getAlbumListModel(); QVERIFY(albumModel->rowCount() > 0); AlbumListItem* item = static_cast(albumModel->item(0, 0)); QVERIFY(item); QVERIFY(item->type() == AlbumListItem::Type); QCOMPARE(item->text(), QString(QLatin1String("Wizard - Odin"))); QCOMPARE(item->getCategory(), QString(QLatin1String("releases"))); QVERIFY(!item->getId().isEmpty()); } void TestDiscogsImportParser::testParseTracks() { static const char albumStr[] = "{\"styles\": [\"Heavy Metal\"], \"series\": [], \"labels\": [{\"id\": 34707, \"resource_url\": \"http://api.discogs.com/labels/34707\", \"catno\": \"LMP 0303-054 Ltd. CD\", \"name\": \"LMP\", \"entity_type\": \"\"}], \"year\": 2003, \"artists\": [{\"join\": \"\", \"name\": \"Wizard (23)\", \"anv\": \"\", \"tracks\": \"\", \"role\": \"\", \"resource_url\": \"http://api.discogs.com/artists/1746637\", \"id\": 1746637}], \"images\": [{\"uri\": \"http://api.discogs.com/image/R-2487778-1293847958.jpeg\", \"height\": 522, \"width\": 600, \"resource_url\": \"http://api.discogs.com/image/R-2487778-1293847958.jpeg\", \"type\": \"primary\", \"uri150\": \"http://api.discogs.com/image/R-150-2487778-1293847958.jpeg\"}, {\"uri\": \"http://api.discogs.com/image/R-2487778-1293847967.jpeg\", \"height\": 526, \"width\": 600, \"resource_url\": \"http://api.discogs.com/image/R-2487778-1293847967.jpeg\", \"type\": \"secondary\", \"uri150\": \"http://api.discogs.com/image/R-150-2487778-1293847967.jpeg\"}], \"id\": 2487778, \"genres\": [\"Rock\"], \"thumb\": \"http://api.discogs.com/image/R-150-2487778-1293847958.jpeg\", \"extraartists\": [], \"title\": \"Odin\", \"master_id\": 280805, \"tracklist\": [{\"duration\": \"5:19\", \"position\": \"1\", \"title\": \"The Prophecy\"}, {\"duration\": \"4:53\", \"position\": \"2\", \"title\": \"Betrayer\"}, {\"duration\": \"6:02\", \"position\": \"3\", \"title\": \"Dead Hope\"}, {\"duration\": \"5:43\", \"position\": \"4\", \"title\": \"Dark God\"}, {\"duration\": \"5:08\", \"position\": \"5\", \"title\": \"Lokis Punishment\"}, {\"duration\": \"4:01\", \"position\": \"6\", \"title\": \"Beginning Of The End\"}, {\"duration\": \"5:01\", \"position\": \"7\", \"title\": \"Thor's Hammer\"}, {\"duration\": \"5:06\", \"position\": \"8\", \"title\": \"Hall Of Odin\"}, {\"duration\": \"5:21\", \"position\": \"9\", \"title\": \"The Powergod\"}, {\"duration\": \"5:40\", \"position\": \"10\", \"title\": \"March Of The Einherjers\"}, {\"duration\": \"3:53\", \"position\": \"11\", \"title\": \"End Of All\"}, {\"duration\": \"4:52\", \"position\": \"12\", \"title\": \"Ultimate War (Bonus Track)\"}, {\"duration\": \"5:05\", \"position\": \"13\", \"title\": \"Golden Dawn (Bonus Track)\"}, {\"duration\": \"\", \"position\": \"Video\", \"title\": \"Betrayer\"}], \"status\": \"Accepted\", \"released_formatted\": \"2003\", \"master_url\": \"http://api.discogs.com/masters/280805\", \"released\": \"2003\", \"country\": \"Germany\", \"notes\": \"Only 4000 copies worldwide!\", \"companies\": [], \"uri\": \"http://www.discogs.com/Wizard-Odin/release/2487778\", \"formats\": [{\"descriptions\": [\"Album\", \"Limited Edition\", \"Enhanced\"], \"text\": \"Digipak\", \"name\": \"CD\", \"qty\": \"1\"}], \"resource_url\": \"http://api.discogs.com/releases/2487778\", \"data_quality\": \"Correct\"}"; onAlbumFinished(albumStr); QStringList titles; titles << QLatin1String("The Prophecy") << QLatin1String("Betrayer") << QLatin1String("Dead Hope") << QLatin1String("Dark God") << QLatin1String("Lokis Punishment") << QLatin1String("Beginning Of The End") << QLatin1String("Thor's Hammer") << QLatin1String("Hall Of Odin") << QLatin1String("The Powergod") << QLatin1String("March Of The Einherjers") << QLatin1String("End Of All") << QLatin1String("Ultimate War (Bonus Track)") << QLatin1String("Golden Dawn (Bonus Track)") << QLatin1String("Betrayer"); QStringList lengths; lengths << QLatin1String("5:19") << QLatin1String("4:53") << QLatin1String("6:02") << QLatin1String("5:43") << QLatin1String("5:08") << QLatin1String("4:01") << QLatin1String("5:01") << QLatin1String("5:06") << QLatin1String("5:21") << QLatin1String("5:40") << QLatin1String("3:53") << QLatin1String("4:52") << QLatin1String("5:05") << QLatin1String(""); QCOMPARE(m_trackDataModel->rowCount(), 14); for (int row = 0; row < 11; ++row) { QCOMPARE(m_trackDataModel->index(row, 0).data().toString(), lengths.at(row)); QCOMPARE(m_trackDataModel->index(row, 3).data().toInt(), row + 1); QCOMPARE(m_trackDataModel->index(row, 4).data().toString(), titles.at(row)); QCOMPARE(m_trackDataModel->index(row, 5).data().toString(), QString(QLatin1String("Wizard"))); QCOMPARE(m_trackDataModel->index(row, 6).data().toString(), QString(QLatin1String("Odin"))); QCOMPARE(m_trackDataModel->index(row, 7).data().toInt(), 2003); QCOMPARE(m_trackDataModel->index(row, 8).data().toString(), QString(QLatin1String("Heavy Metal"))); QCOMPARE(m_trackDataModel->index(row, 10).data().toString(), QString(QLatin1String("LMP 0303-054 Ltd. CD"))); QCOMPARE(m_trackDataModel->index(row, 11).data().toString(), QString(QLatin1String("CD"))); QCOMPARE(m_trackDataModel->index(row, 12).data().toString(), QString(QLatin1String("LMP"))); QCOMPARE(m_trackDataModel->index(row, 13).data().toString(), QString(QLatin1String("Germany"))); } } kid3-3.0.2/src/test/testdiscogsimportparser.h000066400000000000000000000022711224603511300212670ustar00rootroot00000000000000/** * \file testdiscogsimportparser.h * Test parsing of import data from Discogs server. * * \b Project: Kid3 * \author Urs Fleisch * \date 09 Oct 2012 * * Copyright (C) 2012 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef TESTDISCOGSIMPORTPARSER_H #define TESTDISCOGSIMPORTPARSER_H #include "testserverimporterbase.h" /** * Test parsing of import data from Discogs server. */ class TestDiscogsImportParser : public TestServerImporterBase { Q_OBJECT private slots: void initTestCase(); void testParseAlbums(); void testParseTracks(); }; #endif kid3-3.0.2/src/test/testjsonparser.cpp000066400000000000000000000127761224603511300177200ustar00rootroot00000000000000/** * \file testjsonparser.cpp * Test JSON serializer and deserializer. * * \b Project: Kid3 * \author Urs Fleisch * \date 09 Oct 2012 * * Copyright (C) 2012-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "testjsonparser.h" #include "jsonparser.h" namespace { QString numberedName(int nr) { return QString(QLatin1String("val%1")).arg(nr, 2, 10, QLatin1Char('0')); } } void TestJsonParser::serializeAndDeserializeMessage() { QMap map; map[QLatin1String("uri")] = QLatin1String("http://www.youtube.com/watch?v=QVdDhOnoR8k"); map[QLatin1String("duration")] = 334; map[QLatin1String("embed")] = true; map[QLatin1String("format")] = QLatin1String("12\""); QString str = JsonParser::serialize(map); QCOMPARE(str, QString(QLatin1String("{\"duration\": 334, \"embed\": true, \"format\": \"12\\\"\", \"uri\": \"http://www.youtube.com/watch?v=QVdDhOnoR8k\"}"))); bool ok; QVariant json = JsonParser::deserialize(str, &ok); QMap deser = json.toMap(); QVERIFY(ok); QVariant val = deser.value(QLatin1String("uri")); QCOMPARE(val.type(), QVariant::String); QCOMPARE(val.toString(), QString(QLatin1String("http://www.youtube.com/watch?v=QVdDhOnoR8k"))); val = deser.value(QLatin1String("duration")); QCOMPARE(val.type(), QVariant::Int); QCOMPARE(val.toInt(), 334); val = deser.value(QLatin1String("embed")); QCOMPARE(val.type(), QVariant::Bool); QCOMPARE(val.toBool(), true); val = deser.value(QLatin1String("format")); QCOMPARE(val.type(), QVariant::String); QCOMPARE(val.toString(), QString(QLatin1String("12\""))); QCOMPARE(JsonParser::serialize(deser), str); } void TestJsonParser::serializeAndDeserializeTypes() { QMap map; QDateTime valDateTime(QDate(2011, 8, 18), QTime(14, 2, 7)); int valInt(-7654321); qlonglong valLongLong(1234567890123456789LL); bool valBool1(false); bool valBool2(true); QVariant valNull; QString valString(QLatin1String("String with \"'\\{}[]")); double valDouble(3.141592653); QVariantMap valObject; valObject[QLatin1String("nested")] = true; valObject[QLatin1String("obj")] = 1e23; QVariantList valArray; QVariantMap valArrayElement; valArrayElement[QLatin1String("val")] = 2; valArray << 1 << valArrayElement << 3; bool ok; QVariantList values; values << valDateTime << valInt << valLongLong << valBool1 << valBool2 << valNull << valString << valDouble << valObject << QVariant(valArray); for (int i = 0; i < values.length(); ++i) { map.insert(numberedName(i), values.at(i)); } QString str = JsonParser::serialize(map); QCOMPARE(str, QString(QLatin1String( "{\"val00\": \"2011-08-18T14:02:07\", \"val01\": -7654321, " "\"val02\": 1234567890123456789, " "\"val03\": false, \"val04\": true, \"val05\": null, \"val06\": " "\"String with \\\"'\\\\{}[]\", \"val07\": 3.141592653, " "\"val08\": {\"nested\": true, " "\"obj\": 1e+23}, \"val09\": [1, {\"val\": 2}, 3]}" ))); QMap deser = JsonParser::deserialize(str, &ok).toMap(); QVERIFY(ok); for (int i = 0; i < values.length(); ++i) { QVariant val = deser.value(numberedName(i)); QCOMPARE(val.type(), values.at(i).type()); QCOMPARE(val, values.at(i)); } QCOMPARE(JsonParser::serialize(deser), str); } void TestJsonParser::deserializeMusicData() { QString musicStr(QLatin1String( "{\n" " \"id\": 1,\n" " \"title\": \"Stockholm\",\n" " \"styles\": [ \"Deep House\" ],\n" " \"labels\": [\n" " { \"entity_type\": \"1\", \"name\": \"Svek\" },\n" " { \"name\": \"LMP\" }\n" " ],\n" " \"results\": [\n" " {\"style\": [\"Heavy Metal\"]},\n" " {\"style\": [\"Hard Rock}]\"]}\n" " ]\n" "}\n")); bool ok = true; QVariantMap musicMap = JsonParser::deserialize(musicStr, &ok).toMap(); QVERIFY(ok); QCOMPARE(musicMap.value(QLatin1String("id")), QVariant(1)); QCOMPARE(musicMap.value(QLatin1String("title")), QVariant(QLatin1String("Stockholm"))); QVariantList styles; styles << QLatin1String("Deep House"); QCOMPARE(musicMap.value(QLatin1String("styles")), QVariant(styles)); QVariantMap label1, label2; label1[QLatin1String("entity_type")] = QLatin1String("1"); label1[QLatin1String("name")] = QLatin1String("Svek"); label2[QLatin1String("name")] = QLatin1String("LMP"); QVariantList labels; labels << label1 << label2; QCOMPARE(musicMap.value(QLatin1String("labels")), QVariant(labels)); QVariantMap result1, result2; QVariantList resultStyle1, resultStyle2; resultStyle1 << QLatin1String("Heavy Metal"); resultStyle2 << QLatin1String("Hard Rock}]"); result1[QLatin1String("style")] = resultStyle1; result2[QLatin1String("style")] = resultStyle2; QVariantList results; results << result1 << result2; QCOMPARE(musicMap.value(QLatin1String("results")), QVariant(results)); } kid3-3.0.2/src/test/testjsonparser.h000066400000000000000000000022041224603511300173460ustar00rootroot00000000000000/** * \file testjsonparser.h * Test JSON serializer and deserializer. * * \b Project: Kid3 * \author Urs Fleisch * \date 09 Oct 2012 * * Copyright (C) 2012 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef TESTJSONPARSER_H #define TESTJSONPARSER_H #include /** * Test JSON serializer and deserializer. */ class TestJsonParser : public QObject { Q_OBJECT private slots: void serializeAndDeserializeMessage(); void serializeAndDeserializeTypes(); void deserializeMusicData(); }; #endif kid3-3.0.2/src/test/testmusicbrainzreleaseimporter.cpp000066400000000000000000000067471224603511300232040ustar00rootroot00000000000000/** * \file testmusicbrainzreleaseimporter.cpp * Test import from MusicBrainz server. * * \b Project: Kid3 * \author Urs Fleisch * \date 07 Oct 2012 * * Copyright (C) 2012-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "testmusicbrainzreleaseimporter.h" #include #include "serverimporter.h" #include "trackdatamodel.h" void TestMusicBrainzReleaseImporter::initTestCase() { setServerImporter(QLatin1String("MusicBrainzImport")); } void TestMusicBrainzReleaseImporter::testQueryAlbums() { queryAlbums(QLatin1String("Wizard"), QLatin1String("Odin")); QStandardItemModel* albumModel = m_importer->getAlbumListModel(); QCOMPARE(albumModel->rowCount(), 3); AlbumListItem* item = static_cast(albumModel->item(0, 0)); QVERIFY(item); QVERIFY(item->type() == AlbumListItem::Type); QCOMPARE(item->text(), QString(QLatin1String("Wizard - Odin"))); QCOMPARE(item->getCategory(), QString(QLatin1String("release"))); QVERIFY(!item->getId().isEmpty()); } void TestMusicBrainzReleaseImporter::testQueryTracks() { queryTracks(QLatin1String("release"), QLatin1String("978c7ed1-a854-4ef2-bd4e-e7c1317be854")); QStringList titles; titles << QLatin1String("The Prophecy") << QLatin1String("Betrayer") << QLatin1String("Dead Hope") << QLatin1String("Dark God") << QLatin1String("Loki's Punishment") << QLatin1String("Beginning of the End") << QLatin1String("Thor's Hammer") << QLatin1String("Hall of Odin") << QLatin1String("The Powergod") << QLatin1String("March of the Einheriers") << QLatin1String("End of All"); QStringList lengths; lengths << QLatin1String("5:19") << QLatin1String("4:53") << QLatin1String("6:02") << QLatin1String("5:42") << QLatin1String("5:08") << QLatin1String("4:01") << QLatin1String("5:01") << QLatin1String("5:06") << QLatin1String("5:21") << QLatin1String("5:40") << QLatin1String("3:53"); QCOMPARE(m_trackDataModel->rowCount(), 11); for (int row = 0; row < 11; ++row) { QCOMPARE(m_trackDataModel->index(row, 0).data().toString(), lengths.at(row)); QCOMPARE(m_trackDataModel->index(row, 3).data().toInt(), row + 1); QCOMPARE(m_trackDataModel->index(row, 4).data().toString(), titles.at(row)); QCOMPARE(m_trackDataModel->index(row, 5).data().toString(), QString(QLatin1String("Wizard"))); QCOMPARE(m_trackDataModel->index(row, 6).data().toString(), QString(QLatin1String("Odin"))); QCOMPARE(m_trackDataModel->index(row, 7).data().toInt(), 2003); QCOMPARE(m_trackDataModel->index(row, 10).data().toString(), QString(QLatin1String("Wizard"))); QCOMPARE(m_trackDataModel->index(row, 11).data().toString(), QString(QLatin1String("LMP 0303-054 CD"))); QCOMPARE(m_trackDataModel->index(row, 12).data().toString(), QString(QLatin1String("Limb Music Products"))); QCOMPARE(m_trackDataModel->index(row, 13).data().toString(), QString(QLatin1String("DE"))); } } kid3-3.0.2/src/test/testmusicbrainzreleaseimporter.h000066400000000000000000000022151224603511300226330ustar00rootroot00000000000000/** * \file testmusicbrainzreleaseimporter.h * Test import from MusicBrainz server. * * \b Project: Kid3 * \author Urs Fleisch * \date 07 Oct 2012 * * Copyright (C) 2012 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef TESTMUSICBRAINZRELEASEIMPORTER_H #define TESTMUSICBRAINZRELEASEIMPORTER_H #include "testserverimporterbase.h" class TestMusicBrainzReleaseImporter : public TestServerImporterBase { Q_OBJECT private slots: void initTestCase(); void testQueryAlbums(); void testQueryTracks(); }; #endif kid3-3.0.2/src/test/testmusicbrainzreleaseimportparser.cpp000066400000000000000000000277211224603511300240650ustar00rootroot00000000000000/** * \file testmusicbrainzreleaseimportparser.cpp * Test parsing of import data from MusicBrainz server. * * \b Project: Kid3 * \author Urs Fleisch * \date 07 Oct 2012 * * Copyright (C) 2012-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "testmusicbrainzreleaseimportparser.h" #include #include "serverimporter.h" #include "trackdatamodel.h" void TestMusicBrainzReleaseImportParser::initTestCase() { setServerImporter(QLatin1String("MusicBrainzImport")); } void TestMusicBrainzReleaseImportParser::testParseAlbums() { static const char searchStr[] = "OdinOfficialengWizardWizardGerman power metalAlbum2003DE693723003023B00009VGKILMP 0303-05413CDOdinOfficialengWizardWizardGerman power metalAlbum2003-08-19DE693723654720B00008OUENLMP 0303-054 CD11CDOdinOfficialWizardWizardGerman power metalAlbum12"; onFindFinished(searchStr); QStandardItemModel* albumModel = m_importer->getAlbumListModel(); QCOMPARE(albumModel->rowCount(), 3); AlbumListItem* item = static_cast(albumModel->item(0, 0)); QVERIFY(item); QVERIFY(item->type() == AlbumListItem::Type); QCOMPARE(item->text(), QString(QLatin1String("Wizard - Odin"))); QCOMPARE(item->getCategory(), QString(QLatin1String("release"))); QVERIFY(!item->getId().isEmpty()); } void TestMusicBrainzReleaseImportParser::testParseTracks() { static const char albumStr[] = "OdinOfficialnormalengWizardWizardGerman power metal2003-08-19DE693723654720B00008OUENLMP 0303-054 CD111319173The Prophecy319173WizardWizardGerman power metal22293186Betrayer293186WizardWizardGerman power metal33362026Dead Hope362026WizardWizardGerman power metal44342946Dark God342946WizardWizardGerman power metal55308746Loki's Punishment308746WizardWizardGerman power metal66241600Beginning of the End241600WizardWizardGerman power metal77301573Thor's Hammer301573WizardWizardGerman power metal88306680Hall of Odin306680WizardWizardGerman power metal99321506The Powergod321506WizardWizardGerman power metal1010340400March of the Einheriers340400WizardWizardGerman power metal1111233720End of All233720WizardWizardGerman power metalhttp://www.amazon.de/gp/product/B00008OUEN"; onAlbumFinished(albumStr); QStringList titles; titles << QLatin1String("The Prophecy") << QLatin1String("Betrayer") << QLatin1String("Dead Hope") << QLatin1String("Dark God") << QLatin1String("Loki's Punishment") << QLatin1String("Beginning of the End") << QLatin1String("Thor's Hammer") << QLatin1String("Hall of Odin") << QLatin1String("The Powergod") << QLatin1String("March of the Einheriers") << QLatin1String("End of All"); QStringList lengths; lengths << QLatin1String("5:19") << QLatin1String("4:53") << QLatin1String("6:02") << QLatin1String("5:42") << QLatin1String("5:08") << QLatin1String("4:01") << QLatin1String("5:01") << QLatin1String("5:06") << QLatin1String("5:21") << QLatin1String("5:40") << QLatin1String("3:53"); QCOMPARE(m_trackDataModel->rowCount(), 11); for (int row = 0; row < 11; ++row) { QCOMPARE(m_trackDataModel->index(row, 0).data().toString(), lengths.at(row)); QCOMPARE(m_trackDataModel->index(row, 3).data().toInt(), row + 1); QCOMPARE(m_trackDataModel->index(row, 4).data().toString(), titles.at(row)); QCOMPARE(m_trackDataModel->index(row, 5).data().toString(), QString(QLatin1String("Wizard"))); QCOMPARE(m_trackDataModel->index(row, 6).data().toString(), QString(QLatin1String("Odin"))); QCOMPARE(m_trackDataModel->index(row, 7).data().toInt(), 2003); QCOMPARE(m_trackDataModel->index(row, 10).data().toString(), QString(QLatin1String("Wizard"))); QCOMPARE(m_trackDataModel->index(row, 11).data().toString(), QString(QLatin1String("LMP 0303-054 CD"))); QCOMPARE(m_trackDataModel->index(row, 12).data().toString(), QString(QLatin1String("Limb Music Products"))); QCOMPARE(m_trackDataModel->index(row, 13).data().toString(), QString(QLatin1String("DE"))); } } kid3-3.0.2/src/test/testmusicbrainzreleaseimportparser.h000066400000000000000000000023551224603511300235260ustar00rootroot00000000000000/** * \file testmusicbrainzreleaseimportparser.h * Test parsing of import data from MusicBrainz server. * * \b Project: Kid3 * \author Urs Fleisch * \date 07 Oct 2012 * * Copyright (C) 2012 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef TESTMUSICBRAINZRELEASEIMPORTPARSER_H #define TESTMUSICBRAINZRELEASEIMPORTPARSER_H #include "testserverimporterbase.h" /** * Test parsing of import data from MusicBrainz server. */ class TestMusicBrainzReleaseImportParser : public TestServerImporterBase { Q_OBJECT private slots: void initTestCase(); void testParseAlbums(); void testParseTracks(); }; #endif kid3-3.0.2/src/test/testserverimporterbase.cpp000066400000000000000000000073751224603511300214540ustar00rootroot00000000000000/** * \file testserverimporterbase.cpp * Base class for server importer tests. * * \b Project: Kid3 * \author Urs Fleisch * \date 07 Oct 2012 * * Copyright (C) 2012 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "testserverimporterbase.h" #include #include #include "dummysettings.h" #include "kid3application.h" #include "iserverimporterfactory.h" #include "serverimporter.h" #include "trackdatamodel.h" #include "configstore.h" TestServerImporterBase::TestServerImporterBase(QObject* parent) : QObject(parent), m_netMgr(new QNetworkAccessManager(this)), m_trackDataModel(new TrackDataModel(this)), m_importer(0), m_settings(0), m_configStore(0) { if (!ConfigStore::instance()) { m_settings = new DummySettings; m_configStore = new ConfigStore(m_settings); } } TestServerImporterBase::~TestServerImporterBase() { delete m_configStore; delete m_settings; } void TestServerImporterBase::onFindFinished(const QByteArray& searchStr) { if (m_importer) m_importer->parseFindResults(searchStr); emit albumsUpdated(); } void TestServerImporterBase::onAlbumFinished(const QByteArray& albumStr) { if (m_importer) { m_importer->setStandardTags(true); m_importer->setAdditionalTags(true); m_importer->parseAlbumResults(albumStr); } emit trackDataUpdated(); } void TestServerImporterBase::setServerImporter(ServerImporter* importer) { if (importer != m_importer) { m_importer = importer; if (m_importer) { connect(m_importer, SIGNAL(findFinished(QByteArray)), this, SLOT(onFindFinished(QByteArray))); connect(m_importer, SIGNAL(albumFinished(QByteArray)), this, SLOT(onAlbumFinished(QByteArray))); } } } void TestServerImporterBase::setServerImporter(const QString& key) { ServerImporter* serverImporter = 0; QObjectList plugins = Kid3Application::loadPlugins(); foreach (QObject* plugin, plugins) { if (IServerImporterFactory* importerFactory = qobject_cast(plugin)) { if (importerFactory->serverImporterKeys().contains(key)) { serverImporter = importerFactory->createServerImporter( key, m_netMgr, m_trackDataModel); break; } } } QVERIFY(serverImporter != 0); setServerImporter(serverImporter); } void TestServerImporterBase::queryAlbums(const QString& artist, const QString& album) { QEventLoop eventLoop; QTimer timer; timer.setSingleShot(true); connect(&timer, SIGNAL(timeout()), &eventLoop, SLOT(quit())); connect(this, SIGNAL(albumsUpdated()), &eventLoop, SLOT(quit())); m_importer->find(m_importer->config(), artist, album); timer.start(5000); eventLoop.exec(); QVERIFY(timer.isActive()); } void TestServerImporterBase::queryTracks(const QString& cat, const QString& id) { QEventLoop eventLoop; QTimer timer; timer.setSingleShot(true); connect(&timer, SIGNAL(timeout()), &eventLoop, SLOT(quit())); connect(this, SIGNAL(trackDataUpdated()), &eventLoop, SLOT(quit())); m_importer->getTrackList(m_importer->config(), cat, id); timer.start(5000); eventLoop.exec(); QVERIFY(timer.isActive()); } kid3-3.0.2/src/test/testserverimporterbase.h000066400000000000000000000034631224603511300211130ustar00rootroot00000000000000/** * \file testserverimporterbase.h * Base class for server importer tests. * * \b Project: Kid3 * \author Urs Fleisch * \date 07 Oct 2012 * * Copyright (C) 2012 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef TESTSERVERIMPORTERBASE_H #define TESTSERVERIMPORTERBASE_H #include class QNetworkAccessManager; class TrackDataModel; class ServerImporter; class ISettings; class ConfigStore; /** * Base class for server importer tests. */ class TestServerImporterBase : public QObject { Q_OBJECT public: explicit TestServerImporterBase(QObject* parent = 0); virtual ~TestServerImporterBase(); public slots: void onFindFinished(const QByteArray& searchStr); void onAlbumFinished(const QByteArray& albumStr); signals: void albumsUpdated(); void trackDataUpdated(); protected: void setServerImporter(ServerImporter* importer); void setServerImporter(const QString& key); void queryAlbums(const QString& artist, const QString& album); void queryTracks(const QString& cat, const QString& id); QNetworkAccessManager* m_netMgr; TrackDataModel* m_trackDataModel; ServerImporter* m_importer; ISettings* m_settings; ConfigStore* m_configStore; }; #endif kid3-3.0.2/src/test/testutils.cpp000066400000000000000000000053131224603511300166570ustar00rootroot00000000000000/** * \file testutils.cpp * Utility functions for tests. * * \b Project: Kid3 * \author Urs Fleisch * \date 07 Oct 2012 * * Copyright (C) 2012-2013 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "testutils.h" #include #include #include /** * Run the tests of a test suite. * * Besides the standard QtTest options, this test runner also allows to select * the testcase with "-testcase" and list the testcases with "-testcases". * This function will exit the application for certain \a args parsed by QtTest. * * @param testSuite an object containing QtTest test cases as children * @param args command line arguments from QCoreApplication::arguments() * * @return 0 if OK, the number of failed tests if failed. */ int TestUtils::runTestSuite(const QObject& testSuite, QStringList& args) { bool listTestCases = false; QRegExp testCaseRe; for (int i = 1; i < args.size(); ++i) { if (args.at(i) == QLatin1String("-help")) { std::printf(" -testcases : Returns a list of current testcases\n" " -testcase re : Run only testcases matching regular " "expression\n"); } else if (args.at(i) == QLatin1String("-testcases")) { listTestCases = true; args.removeAt(i); break; } else if (args.at(i) == QLatin1String("-testcase") && i + 1 < args.size()) { testCaseRe.setPattern(args.at(i + 1)); args.removeAt(i + 1); args.removeAt(i); } } int testsFailed = 0; int testCasesPassed = 0; int testCasesFailed = 0; foreach (QObject* tc, testSuite.children()) { QString tcName(QString::fromLatin1(tc->metaObject()->className())); if (listTestCases) { std::printf("%s\n", qPrintable(tcName)); } else if (testCaseRe.isEmpty() || testCaseRe.exactMatch(tcName)) { int rc = QTest::qExec(tc, args); testsFailed += rc; if (rc == 0) { ++testCasesPassed; } else { ++testCasesFailed; } } } std::printf("Test cases: %d passed, %d failed\n", testCasesPassed, testCasesFailed); return testsFailed; } kid3-3.0.2/src/test/testutils.h000066400000000000000000000027261224603511300163310ustar00rootroot00000000000000/** * \file testutils.h * Utility functions for tests. * * \b Project: Kid3 * \author Urs Fleisch * \date 07 Oct 2012 * * Copyright (C) 2012 Urs Fleisch * * This file is part of Kid3. * * Kid3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Kid3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef TESTUTILS_H #define TESTUTILS_H class QObject; class QStringList; namespace TestUtils { /** * Run the tests of a test suite. * * Besides the standard QtTest options, this test runner also allows to select * the testcase with "-testcase" and list the testcases with "-testcases". * This function will exit the application for certain \a args parsed by QtTest. * * @param testSuite an object containing QtTest test cases as children * @param args command line arguments from QCoreApplication::arguments() * * @return 0 if OK, the number of failed tests if failed. */ int runTestSuite(const QObject& testSuite, QStringList& args); } #endif kid3-3.0.2/win32/000077500000000000000000000000001224603511300133055ustar00rootroot00000000000000kid3-3.0.2/win32/buildkid3.bat000077500000000000000000000013141224603511300156510ustar00rootroot00000000000000set QTDIR=C:\Qt\4.8.1 set MSYSDIR=C:\msys\1.0 set MINGWDIR=C:\msys\1.0\mingw set PERLDIR=%HOME%\prg\Perl set DUMPBINDIR=%HOME%\prg\dumpbin set CMAKEDIR=%HOME%\prg\cmake-2.8.6-win32-x86 set XSLTPROCDIR=%HOME%\prg\xsltproc set DOCBOOKDIR=%HOME%\prg\docbook-xsl-1.72.0 @rem Make sure the following environment variables are set: @rem QTDIR, MINGWDIR, PERLDIR, DUMPBINDIR, CMAKEDIR, XSLTPROCDIR, DOCBOOKDIR set PATH=%QTDIR%\bin;%MINGWDIR%\bin;C:\WINNT\System32;C:\Windows\System32;%PERLDIR%\bin;%DUMPBINDIR%;%CMAKEDIR%\bin set INCLUDE=%MSYSDIR%\local\include set LIB=%MSYSDIR%\local\lib cmake -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX= -DWITH_FFMPEG=ON .. mingw32-make cpack

Product Details

  • Audio CD (November 2, 1999)
  • Original Release Date: November 2, 1999
  • Number of Discs: 1
  • Label: Metal Blade
  • tracks:
listen  1. Bleed For Ancient Gods 4:31$0.99 alternatively (empty lines removed):
1. Before the Devil Knows You're Dead Song TitleArtist"), start); if (start >= 0) { end = str.indexOf(QLatin1String("