From 6193331c5e97e45830a93a41d97a1be465f43952 Mon Sep 17 00:00:00 2001 From: Hendrik Rittich Date: Sun, 16 Dec 2012 11:52:31 +0100 Subject: [PATCH] Import gogglesmm_0.12.7.orig.tar.gz [dgit import orig gogglesmm_0.12.7.orig.tar.gz] --- AUTHORS | 40 + COPYING | 674 ++++++++ ChangeLog | 595 +++++++ INSTALL | 37 + Makefile | 216 +++ README | 145 ++ build/byteorderdetect | 51 + build/functions | 161 ++ build/makemo | 8 + build/makepot | 36 + build/version | 5 + configure | 322 ++++ extra/gogglesmm.1 | 134 ++ extra/gogglesmm.desktop | 24 + extra/gogglesmm.svg | 248 +++ extra/gogglesmm_22.png | Bin 0 -> 1302 bytes extra/gogglesmm_24.png | Bin 0 -> 1474 bytes extra/gogglesmm_48.png | Bin 0 -> 3914 bytes icons/about.png | Bin 0 -> 9096 bytes icons/cursor_hand.gif | Bin 0 -> 112 bytes icons/gogglesmm_16.png | Bin 0 -> 845 bytes icons/gogglesmm_32.png | Bin 0 -> 2141 bytes po/cs.mo | Bin 0 -> 39480 bytes po/cs.po | 2703 ++++++++++++++++++++++++++++++ po/de.mo | Bin 0 -> 37930 bytes po/de.po | 2958 +++++++++++++++++++++++++++++++++ po/es.mo | Bin 0 -> 39617 bytes po/es.po | 3018 +++++++++++++++++++++++++++++++++ po/fi.po | 2315 ++++++++++++++++++++++++++ po/fr.mo | Bin 0 -> 37349 bytes po/fr.po | 3048 ++++++++++++++++++++++++++++++++++ po/gogglesmm.pot | 2282 +++++++++++++++++++++++++ po/hu.mo | Bin 0 -> 39312 bytes po/hu.po | 2610 +++++++++++++++++++++++++++++ po/pt.mo | Bin 0 -> 38809 bytes po/pt.po | 2493 +++++++++++++++++++++++++++ po/ru.mo | Bin 0 -> 45976 bytes po/ru.po | 3043 +++++++++++++++++++++++++++++++++ src/GMAbout.cpp | 137 ++ src/GMAbout.h | 44 + src/GMAnimImage.cpp | 120 ++ src/GMAnimImage.h | 65 + src/GMApp.cpp | 453 +++++ src/GMApp.h | 105 ++ src/GMAudioScrobbler.cpp | 1392 ++++++++++++++++ src/GMAudioScrobbler.h | 159 ++ src/GMAutoPtr.h | 69 + src/GMClipboard.cpp | 123 ++ src/GMClipboard.h | 79 + src/GMColumnDialog.cpp | 283 ++++ src/GMColumnDialog.h | 50 + src/GMCoverCache.cpp | 425 +++++ src/GMCoverCache.h | 82 + src/GMDBus.cpp | 934 +++++++++++ src/GMDBus.h | 225 +++ src/GMDatabase.cpp | 241 +++ src/GMDatabase.h | 106 ++ src/GMDatabaseSource.cpp | 2256 +++++++++++++++++++++++++ src/GMDatabaseSource.h | 170 ++ src/GMEQDialog.cpp | 440 +++++ src/GMEQDialog.h | 80 + src/GMFetch.cpp | 95 ++ src/GMFetch.h | 55 + src/GMFilename.cpp | 575 +++++++ src/GMFilename.h | 97 ++ src/GMFontDialog.cpp | 404 +++++ src/GMFontDialog.h | 78 + src/GMIconTheme.cpp | 700 ++++++++ src/GMIconTheme.h | 142 ++ src/GMImageView.cpp | 251 +++ src/GMImageView.h | 52 + src/GMImportDialog.cpp | 708 ++++++++ src/GMImportDialog.h | 111 ++ src/GMList.cpp | 430 +++++ src/GMList.h | 131 ++ src/GMMediaPlayerService.cpp | 339 ++++ src/GMMediaPlayerService.h | 55 + src/GMMessageChannel.cpp | 153 ++ src/GMMessageChannel.h | 94 ++ src/GMNotifyDaemon.cpp | 392 +++++ src/GMNotifyDaemon.h | 66 + src/GMPlayListSource.cpp | 472 ++++++ src/GMPlayListSource.h | 97 ++ src/GMPlayer.cpp | 1075 ++++++++++++ src/GMPlayer.h | 278 ++++ src/GMPlayerManager.cpp | 1781 ++++++++++++++++++++ src/GMPlayerManager.h | 272 +++ src/GMPreferences.cpp | 359 ++++ src/GMPreferences.h | 171 ++ src/GMPreferencesDialog.cpp | 1293 ++++++++++++++ src/GMPreferencesDialog.h | 131 ++ src/GMQuery.cpp | 335 ++++ src/GMQuery.h | 153 ++ src/GMRemote.cpp | 262 +++ src/GMRemote.h | 77 + src/GMSearch.cpp | 674 ++++++++ src/GMSearch.h | 98 ++ src/GMSettingsDaemon.cpp | 74 + src/GMSettingsDaemon.h | 46 + src/GMSource.cpp | 115 ++ src/GMSource.h | 172 ++ src/GMSourceView.cpp | 324 ++++ src/GMSourceView.h | 92 + src/GMStreamSource.cpp | 296 ++++ src/GMStreamSource.h | 80 + src/GMTag.cpp | 1357 +++++++++++++++ src/GMTag.h | 148 ++ src/GMTaskManager.cpp | 121 ++ src/GMTaskManager.h | 83 + src/GMThread.cpp | 35 + src/GMThread.h | 44 + src/GMTrackDatabase.cpp | 2834 +++++++++++++++++++++++++++++++ src/GMTrackDatabase.h | 399 +++++ src/GMTrackItem.cpp | 553 ++++++ src/GMTrackItem.h | 118 ++ src/GMTrackList.cpp | 1934 +++++++++++++++++++++ src/GMTrackList.h | 476 ++++++ src/GMTrackView.cpp | 2249 +++++++++++++++++++++++++ src/GMTrackView.h | 294 ++++ src/GMTrayIcon.cpp | 355 ++++ src/GMTrayIcon.h | 69 + src/GMURL.cpp | 308 ++++ src/GMURL.h | 71 + src/GMWindow.cpp | 1541 +++++++++++++++++ src/GMWindow.h | 285 ++++ src/ap_buffer.cpp | 168 ++ src/ap_buffer.h | 96 ++ src/ap_http.cpp | 1016 ++++++++++++ src/ap_http.h | 285 ++++ src/ap_xml_parser.cpp | 67 + src/ap_xml_parser.h | 29 + src/fxext.cpp | 2265 +++++++++++++++++++++++++ src/fxext.h | 488 ++++++ src/gmdefs.h | 165 ++ src/gmutils.cpp | 609 +++++++ src/gmutils.h | 71 + src/gogglesmm.xml | 26 + src/main.cpp | 102 ++ src/md5.cpp | 381 +++++ src/md5.h | 91 + src/mpris_player.xml | 49 + src/mpris_root.xml | 17 + src/mpris_tracklist.xml | 32 + 143 files changed, 68090 insertions(+) create mode 100644 AUTHORS create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 INSTALL create mode 100644 Makefile create mode 100644 README create mode 100644 build/byteorderdetect create mode 100644 build/functions create mode 100644 build/makemo create mode 100644 build/makepot create mode 100644 build/version create mode 100755 configure create mode 100644 extra/gogglesmm.1 create mode 100644 extra/gogglesmm.desktop create mode 100644 extra/gogglesmm.svg create mode 100644 extra/gogglesmm_22.png create mode 100644 extra/gogglesmm_24.png create mode 100644 extra/gogglesmm_48.png create mode 100644 icons/about.png create mode 100644 icons/cursor_hand.gif create mode 100644 icons/gogglesmm_16.png create mode 100644 icons/gogglesmm_32.png create mode 100644 po/cs.mo create mode 100644 po/cs.po create mode 100644 po/de.mo create mode 100644 po/de.po create mode 100644 po/es.mo create mode 100644 po/es.po create mode 100644 po/fi.po create mode 100644 po/fr.mo create mode 100644 po/fr.po create mode 100644 po/gogglesmm.pot create mode 100644 po/hu.mo create mode 100644 po/hu.po create mode 100644 po/pt.mo create mode 100644 po/pt.po create mode 100644 po/ru.mo create mode 100644 po/ru.po create mode 100644 src/GMAbout.cpp create mode 100644 src/GMAbout.h create mode 100644 src/GMAnimImage.cpp create mode 100644 src/GMAnimImage.h create mode 100644 src/GMApp.cpp create mode 100644 src/GMApp.h create mode 100644 src/GMAudioScrobbler.cpp create mode 100644 src/GMAudioScrobbler.h create mode 100644 src/GMAutoPtr.h create mode 100644 src/GMClipboard.cpp create mode 100644 src/GMClipboard.h create mode 100644 src/GMColumnDialog.cpp create mode 100644 src/GMColumnDialog.h create mode 100644 src/GMCoverCache.cpp create mode 100644 src/GMCoverCache.h create mode 100644 src/GMDBus.cpp create mode 100644 src/GMDBus.h create mode 100644 src/GMDatabase.cpp create mode 100644 src/GMDatabase.h create mode 100644 src/GMDatabaseSource.cpp create mode 100644 src/GMDatabaseSource.h create mode 100644 src/GMEQDialog.cpp create mode 100644 src/GMEQDialog.h create mode 100644 src/GMFetch.cpp create mode 100644 src/GMFetch.h create mode 100644 src/GMFilename.cpp create mode 100644 src/GMFilename.h create mode 100644 src/GMFontDialog.cpp create mode 100644 src/GMFontDialog.h create mode 100644 src/GMIconTheme.cpp create mode 100644 src/GMIconTheme.h create mode 100644 src/GMImageView.cpp create mode 100644 src/GMImageView.h create mode 100644 src/GMImportDialog.cpp create mode 100644 src/GMImportDialog.h create mode 100644 src/GMList.cpp create mode 100644 src/GMList.h create mode 100644 src/GMMediaPlayerService.cpp create mode 100644 src/GMMediaPlayerService.h create mode 100644 src/GMMessageChannel.cpp create mode 100644 src/GMMessageChannel.h create mode 100644 src/GMNotifyDaemon.cpp create mode 100644 src/GMNotifyDaemon.h create mode 100644 src/GMPlayListSource.cpp create mode 100644 src/GMPlayListSource.h create mode 100644 src/GMPlayer.cpp create mode 100644 src/GMPlayer.h create mode 100644 src/GMPlayerManager.cpp create mode 100644 src/GMPlayerManager.h create mode 100644 src/GMPreferences.cpp create mode 100644 src/GMPreferences.h create mode 100644 src/GMPreferencesDialog.cpp create mode 100644 src/GMPreferencesDialog.h create mode 100644 src/GMQuery.cpp create mode 100644 src/GMQuery.h create mode 100644 src/GMRemote.cpp create mode 100644 src/GMRemote.h create mode 100644 src/GMSearch.cpp create mode 100644 src/GMSearch.h create mode 100644 src/GMSettingsDaemon.cpp create mode 100644 src/GMSettingsDaemon.h create mode 100644 src/GMSource.cpp create mode 100644 src/GMSource.h create mode 100644 src/GMSourceView.cpp create mode 100644 src/GMSourceView.h create mode 100644 src/GMStreamSource.cpp create mode 100644 src/GMStreamSource.h create mode 100644 src/GMTag.cpp create mode 100644 src/GMTag.h create mode 100644 src/GMTaskManager.cpp create mode 100644 src/GMTaskManager.h create mode 100644 src/GMThread.cpp create mode 100644 src/GMThread.h create mode 100644 src/GMTrackDatabase.cpp create mode 100644 src/GMTrackDatabase.h create mode 100644 src/GMTrackItem.cpp create mode 100644 src/GMTrackItem.h create mode 100644 src/GMTrackList.cpp create mode 100644 src/GMTrackList.h create mode 100644 src/GMTrackView.cpp create mode 100644 src/GMTrackView.h create mode 100644 src/GMTrayIcon.cpp create mode 100644 src/GMTrayIcon.h create mode 100644 src/GMURL.cpp create mode 100644 src/GMURL.h create mode 100644 src/GMWindow.cpp create mode 100644 src/GMWindow.h create mode 100644 src/ap_buffer.cpp create mode 100644 src/ap_buffer.h create mode 100644 src/ap_http.cpp create mode 100644 src/ap_http.h create mode 100644 src/ap_xml_parser.cpp create mode 100644 src/ap_xml_parser.h create mode 100644 src/fxext.cpp create mode 100644 src/fxext.h create mode 100644 src/gmdefs.h create mode 100644 src/gmutils.cpp create mode 100644 src/gmutils.h create mode 100644 src/gogglesmm.xml create mode 100644 src/main.cpp create mode 100644 src/md5.cpp create mode 100644 src/md5.h create mode 100644 src/mpris_player.xml create mode 100644 src/mpris_root.xml create mode 100644 src/mpris_tracklist.xml diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..d12501a --- /dev/null +++ b/AUTHORS @@ -0,0 +1,40 @@ +Goggles Music Manager +Copyright (C) 2002-2011 by Sander Jansen. All Rights Reserved. + +Various code snippets from FOX-Toolkit +Copyright (c) 1998-2011 by Jeroen van der Zijp. All Rights Reserved. + + +Translations +------------ +Hendrik Rittich (German) +Víctor Pérez Masegosa (Spanish) +Erwan Inyzant (French) +Sándor Sipos (Hungarian) +L Lawliet (Russian) +David Vachulka (Czech) +Christian Hellberg (Finnish) +Sérgio Marques (Portugese) + +Other Contributions +------------------- + + - Original "Goggles" logo designed by Gyurka Jansen. + (http://glas.its.tudelft.nl/~gyurka) + + - Contains icons from Tango and Gnome icon themes. + + - MD5 implementation see: src/md5.cpp + + +Thanks for all the Fish! +------------------------ + - Thanks to Charles Warren and Jeroen van der Zijp for testing and many useful suggestions. + - Thanks to Oktay Cetinkaya for feedback and bugreports. + - Thanks to Gyurka Jansen for the Goggles Logo!. + - Thanks to Jason Donenfeld for the many useful suggestions and bug reports. + - Thanks to Oliver Duclos for the first tray icon implementation and the usefull bug reports. + - Thanks to Sascha Klauder for FreeBSD porting. + - Thanks to Chadi El Ahmad for the SliTaz package. + - Thanks to John Tyree for feedback and bugreports. + - Thanks to Andrey Yurchuk for the Gentoo ebuild. diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. 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 +them 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 prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. 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. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey 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; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If 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 convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU 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 that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + 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. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +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. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..16fce9a --- /dev/null +++ b/ChangeLog @@ -0,0 +1,595 @@ +0.12.7 + - Support WAV files. + - Minor optimizatons + - Add disc number support in filename parser + - Support NetworkManager 0.9 notifications. + - Fixes to audioscrobbler. + +0.12.6 + - Fix xine 1.2 detection in configure. + +0.12.5 + - Fix memory leak. + - Fix libre.fm support. + - New authentication method for last.fm. + - Improved compatibility/detection for m3u/pls urls when playing Internet Radio. + - Removed libcurl dependency. + +0.12.4 + - Added Portuguese Translation by Sérgio Marques + - Improved notification daemon compatibility + - Support for latest FOX 1.7.29. + - Added genre and location to mpris information. + +0.12.3 + - Do not pass unsupported GL_LINEAR_MIPMAP_LINEAR to GL_TEXTURE_MAG_FILTER. + - Enable support for non-power-of-two textures now that Mesa 7.11-rc4 fixes the issue. + - Fix incorrect usage of FXAutoPtr. + - Fix parallel build. + +0.12.2 + - Gnome 3 compatibility updates + - Fix infinite last.fm warnings when the system time is out of sync with the last.fm server time. + - Merge title that were split into multiple entries in vorbiscomments. + +0.12.1 + - fix crash when right-clicking tray icon (issue 230) + - timer fix in audioscrobbler for fox-1.7 + +0.12.0 + - Updated Look and Feel. + - Updated Czech Translation. + - Allow relative paths when exporting playlists. + - Support for taglib 1.7 + - Remove support for old miniplayer + - Fix audioscrobbler on FOX 1.7 + +0.11.6 + - Updated Spanish Translation + - Improved opengl compatibility in cover display. + +0.11.5 + - Fix ignored icon theme (issue 222) + - Initial svg icon support through use of rsvg-convert (part of librsvg) utility. + +0.11.4 + - Link explicitly to X11, GL and GLU (issue 212). + - Improved icon theme specification support. Now also load from ~/.icons, $XDG_DATA_DIRS/icons and /usr/share/pixmaps. + - Properly initialize libgcrypt to avoid warnings. + - Added button to get out of miniplayer mode and make the miniplayer behave the same as the regular browser window. (issue 196). + - New additional shortcut to toggle between miniplayer mode and browser mode (Ctrl-M). + - Miniplayer is resizeable and remember its size and position. It will no longer automatically resize itself. + - Fix crash when importing with specific icon themes selected. (issue 218) + - Add big-endian support to flac album art loading. (issue 218) + +0.11.3 + - Icon name in desktop file shouldn't have file extension + - Fix disabled mp4 support. + - Translation Updates + +0.11.2 + - Support for .oga files. + - Allow scrobbling to last.fm or libre.fm. + - Fix some threading issues in the audio scrobbler. + +0.11.1 + - Updated Hungarian Translation + - Fixed MANDIR and LOCALEDIR not being defined by configure, resulting in locale data + and manpages to be installed in root. + +0.11.0 + - Drop support for taglib 0.5.x, taglib-extras and internal asf/mp4 support. + - Remove internal icon theme and depend only on external ones. + - Depend optionally on gcrypt for md5 calculation. + - Partial MPRIS v1 interface. + +0.10.27 + - configure cleanups + - Use proper uri (file:// instead of file:) (issue 203). + - Added man page. + +0.10.26 + - Backport new font dialog. + - Use posix sh instead of bash for configure script. + +0.10.25 + - Fix build issue with older taglib releases (1.5.x). + - Updated Czech translation. + +0.10.24 + - Support mp4 and ogg vorbis embedded album art. + - New opengl based cover viewer which allows for smooth scaling of the album cover. + If opengl is not available or slow, the old x11 cover viewer can be used instead, which now has an option to choose the displayed cover size. + - Import Playlists (xspf, m3u and pls) (issue 123) + - Export playlist title for xspf playlists. + - Changed Open MRL -> Play File or Stream and improved the dialog to make it easier choosing a local file from disk. + - Add conditional support to filename format. Window title may now also be customized. + - Show import options when dragging or copy/pasting files. + - Added Tooltip to trayicon showing the current playing track information. + - Open directory of track with right click (issue 146) + - Fix active track in playlist when reordering (issue 194). + +0.10.23 + - Unify package, application and executable name. + - Don't grab keys we're not going to use (issue 189). + +0.10.22 + - Added Czech translation by David Vachulka. + +0.10.21 + - Fix build script issue. + +0.10.20 + - Tray icon background color may now be customized to improve look of the tray icon. + - Don't try to install mo files we don't have when LINGUAS environment variable is set (issue 165) + - Replace libfetch with libcurl. + - Load album art from tag in the album browser as well. (issue 176) + - Fix main library listing genres from the Radio Streams. + +0.10.19 + - Support media keys through the Gnome Settings Daemon. + - Don't show import dialog when radio streams are in the database. (issue 154) + - Use ewmh icon hint to specify application icon which should result in a nicer looking icon in the taskbar. + - Fix various dbus event handling to prevent crashes (issue 159) + +0.10.18 + - Make some functions static that don't need global scope. + - Be more flexible about the http return code (issue 147). + - Fix crash when pressing delete on "All Arists/Genres/Albums" + +0.10.17 + - Russian Translation. + +0.10.16 + - Hungarian Translation. + - Compilation fix for older taglib in combination with mp4. + +0.10.15 + - Add additional signal handler(s) to correctly save state on exit. + - Fix focus getting stuck in Edit Track dialog. + - Year field in edit track dialog should be empty if multiple tracks with different years are selected. + - Support bmp and gif embedded album art. + - Fix memory leak in embedded album art loader. + +0.10.14 + - Support for .m4b files. + - Minor fixes. + +0.10.13 + - Take disc number into account when sorting. + +0.10.12 + - Updated German Translation + +0.10.11 + - Prevent crash when settting input focus on widgets when a FOX version with xim support is being used. + - Fix timeout type mixup with FOX-1.6 vs FOX-1.7 + +0.10.10 + - Fix crash when removing tracks from the library via a playlist. + +0.10.9 + - Fix segfault in certain cases when no track information is yet available. + - Removed support for libdownload. + - Fixed segfault when using libfetch. + - Improved error checking when downloading m3u and pls files. + +0.10.8 + - New command line option (--tray) to start gmm minimized in tray. + - Album Artist tag support for MP4 files. + - Fix shortcut to toggle playback (issue 121) + - Use buildin mp4/asf support from taglib 1.6 if available. + - Rely on default genre list from taglib. + - Build fixes. + +0.10.7 + - Fix compilation --without-new-remote (issue 113) + - Fix duplicate hotkeys in preferences dialog (issue 112) + +0.10.6 + - Fix time/duration display to account for hours. + - Fix critical bug when sending album art to notification daemon. + - Support for fox-1.7.20. + - Optionally use taglib-extras for MP4 and ASF support. + - Allow other scrobbler compatible servers to be used. + - Added French translation. + +0.10.5 + - Override all compiler flags if CFLAGS has been set. + +0.10.4 + - Minor translation fixes + - Fix duplicate hotkey in new radio station dialog. + - Fix exporting playlist. + +0.10.3 + - Translation fixes and updates. + - Warn user when a incompatible (future) version of the database is encountered. + - Fix compilation with taglib-1.4. + +0.10.2 + - Added Spanish translation. + - Fix translations not working with FOX 1.7 (issue 100) + +0.10.1 + - Fix tray icon issues. + - Added German translation. + - Properly encode special characters in audio scrobbler now playing submitter. + - Fix sorting of album list by year in playlists. + - Do not grab "AnyKey" by checking return value of XKeysymToKeycode. + +0.10.0 + - Database Improvements: + + o Improved performance when modifying the database (importing & removing tracks) + o Added fields for per track artist (issue 72) + + - Import Improvements: + + o Added "Sync Folder" capability, which can also update or remove tracks in the database if needed. + o Added import exclusion filter for file and folders. (issue 84) + o Redesigned import dialog. It now contains the import options that were previously in the preferences dialog. + o Added a filename template input to grab track information from a filename. This replaces + the less flexible "title from filename","album from directory" and "artist from directory". + o Added option to choose where to read the track information from: filename, tag or both. + o Replaced "Default Title", "Default Album", "Default Artist" and "Default Genre" with a single "Default Field" + since most of time you would likely use the same text anyway. + + o Removed the Import File(s) option. I don't think it's needed that much, and it reduces the number of choices a user has to make. + Importing single files can still be accomplished through drag-and-drop. + + o Remove wizard. Just show import dialog first time. + + - Other Improvements: + + o Added Localization Capability. (issue 3) + o Added disc column to track view. + o Allow multiple genre selection in browser. + o Double click in artist and album list will start playing. + o Added button to enable or disable last.fm scrobbler. + o Equalizer with configurable presets. + o Take into account the disc number when sorting albums. + o Send cover art through dbus to notify daemon directly instead of saving it to disk. + o allow to write tags when editing even if tracks have not been modified. + o more flexible input to select which fields need to be searched (issue 51). + o allow quoted strings in search field. + o Support for Multimedia Keys. + o Optionally display playing track in title bar. + o The album list may now also be sorted by album year. + + - Audio Improvements: + + o The audio driver can now be changed. (issue 87) + o Replay Gain (Only Ogg Vorbis, native FLAC and mp3 with APE tags). + + - Filename Renamer + + o The excluded character set may now be changed for the filename renamer. + o Added year and disc parameters. + + - Tray Icon Improvements: + + o tray icon is now integrated within gmm. gmm-tray has been removed. + o tray icon show/hide miniplayer as well. + o volume may be controlled using the mousewheel. + o middle mouse click will toggle between playing and pausing. + + - Fixes: + + o crash when sorting in Internet Radio view. + o remember "Update Filename" setting in edit dialogs. + o actually try alternative urls in PLS or m3u before failing completely. + o correctly display playing track in playlists. + o do not run event loop in dbus message callback. + +0.9.18 + - Fix: Add support for fox-1.7.18. + - Fix: more url handling fixes. + - Misc: code cleanups. + +0.9.17 + - Fix: fix crash showing dialogbox when audio device open fails on startup. + +0.9.16 + - Fix: Track-less songs should not be 0 (#62) + - Fix: disable default key press processing in tracklist. + - Fix: Update GUI when done playing. + - Fix: incorrect tracklist state when using search filter. + - Fix: workaround for older notification daemons that don't show '&'. + - Fix: improved url handling. + - New: add additional shortcut '/' for search filter. + - New: When pressing the search hotkeys, existing search text will be selected. + - New: New shortcuts for Repeat Off, Track and All. Added shortcut for Shuffle as well. + - New: Made repeat radio buttons instead of checkbuttons. + - New: Play file from the commandline. + +0.9.15 + - Fix: Prevent crash by checking if session and system bus are available. + - Fix: don't show tray icon option if gmm-tray is not in $PATH. + - Fix: compile issue when building with old remote. + +0.9.14 + - New: Threaded scrobbler submitter which should prevent locking the GUI during DNS lookups. + - New: Tray Icon. (contributed by Olivier Duclos). + - New: More complete dbus interface (required by the tray icon) + - New: NetworkManager support to wakeup scrobbler if network connection becomes available. + - New: Font and colors may now be updated from within GMM and require no application restart. + - Misc: The filename template settings has been moved from the preferences panel to the tag editing dialogs. + - Misc: Support for mugshot was removed. + - Misc: Nicer application and tray icon by Olivier Duclos. + - Fix: Incorrect track number was used when renaming files + + - Misc: build/installation improvements: + + xdg-icon-resource/xdg-desktop-menu are not used anymore. + + * All icons are installed in (prefix)/share/icons/hicolor/* + * gmm.desktop is installed in (prefix)/share/applications + * `make install` will run gtk-update-icon-cache (prefix)/share/icons/hicolor + + Support DESTDIR variable when running make instal: + + make DESTDIR=/some/package install + + This will install gmm in /some/package/(prefix)/bin/gmm + +0.9.13 + - Fix: Crash in miniplayer. + +0.9.12 + - Fix: Cover wasn't shown when switching to mini player. + - Fix: Update highlighted track if tracks are dragged around. + +0.9.11 + - New: Support embedded cover art in flac files (jpg and png only). + - New: Support last-fm 'now playing' notification. + - New: Support album art in notification to notify-daemon. + - New: DBus enabled version now also support the ability to control a running gmm from the command line. + - New: Use "ALBUMARTIST" tag in Ogg / FLAC files and TPE2 for id3v2 tags for artist. + +0.9.10 + - New: Experimental support for libfetch on FreeBSD (untested). + - New: Embedded cover art (id3v2 tags) now also supported in remote. + - New: Make covers in remote slightly larger. + - Fix: Compilation issue on FreeBSD. + - Fix: Scrobbler only submitted one track at a time. + - Fix: Scrobbler connection timeout was too large. + +0.9.9 + - Fix: Track play count and display were not always properly updated (in combination with gapless playback), + This affected last-fm submission as well... + - Fix: Crash in Edit Track dialog if mp4 file doesn't exist. + - Fix: Current item in tracklist need to be updated when stop is pressed to be able to continue playing from the last played track. + - Fix: Restore maximized/fullscreen state on start up. + - Fix: Using the mouse wheel in the remote now also changes the volume. + - Fix: Remote now also has a window icon. + - New: Ctrl-Q now also works in the remote. + - New: Experimental support for embedded cover art support in mp3 / flac with ID3v2 tags. + +0.9.8 + - Fix: files with '#' couldn't be opened anymore due to change in xine-1.1.9. + - Fix: Improved Album Art search. Now looks for jpg,gif,png and bmp files in + the directory of the music file. Gives higher priority to files found with the + following names: cover,album, albumart,.folder,folder. + - New: Added some webbrowser links to last-fm sign-up and Goggles Music Manager user group. + +0.9.7 + - Fix: Really fix Compilation issue on 32 bit systems. + +0.9.6 + - Fix: Compilation issue on 32 bit systems. + +0.9.5 + - Fix: fix url encoding to handle more unsafe characters + +0.9.4 + - New: Last-FM audio scrobbler support. + - Fix: include instead of + +0.9.3 + - Fix: Fix configure columns dialog for FOX-1.7. Draw function wasn't properly overloaded. + - Fix: Support new FXFontDesc api in FOX-1.7.17 + +0.9.2 + - New: Configure Columns dialog can now also show/hide columns. (backport) + - New: Turn on audio playback engine on startup is now a option. + - New: Added Volume Control to Mini Player. + - New: Show year column in track list. + - New: Disc Number Support. + - New: Improved Edit Track Dialog. + - New: Ctrl-W now also closes the window (either quits the application, or toggles the remote). + - Fix: Reflect column order in right click menu. + - Fix: Use new dbus-1.2 api if available. Also show DBus version if available in about panel. + - Fix: Remove Compiler Warnings (gcc 4.3). + - Fix: Improved check if files are local or not which affects updating the GUI. + +0.9.1 + - Fix: Compilation issue with older xine. + - Fix: Compilation issue with debug build. + - Fix: Check for proper SQLite version during runtime. + - Fix: Prevent the application from disappearing when using the old remote. + - Fix: Added extra hint for new remote to show window title for certain window managers. + - Fix: Some fixes to configure. + +0.9.0 + - New: Search Filter. + - New: Change order of columns in track list. + - New: Alternative Mini Player (old one still available through configure option). + - New: Context menu for track list header. + - New: Selection state in browser for each source. + - New: Try raising existing music manager when starting without commandline arguments. + - New: Support for Internet Radio Streams. + - New: Support for ASF files. + - New: Support for MP4 through TagLib. Removed old libmp4v2 support. + - New: now uses the c++ taglib library instead of the c-bindings. + - New: use bold italic font for playing track in tracklist. + - New: Shuffle mode: play tracks in random order. + - New: Repeat A-B mode: repeat section of a track. + - New: Editing improvements. + - New: When clearing the database, playlist entries may be optionally kept. + - New: When removing tracks from database may be optionally deleted from disk as well. + - New: Keyboard shortcuts for editing and deleting tracks. + - New: Show audio/file properties in track edit dialog. + - Fix: better path caching when inserting tracks (should increase speed). + - Fix: Do not allocate icons before they're actually used (about and remote dialogs). + - Fix: fix memory leak in browse sort. + - Fix: Support modified fox-config on Gentoo. + - Fix: Use [[ in build scripts instead of [. + - Fix: Paste from gtk applications failed if it contained line feeds. + - Fix: Only enable next/previous if more than 1 track is available. + - Misc: Changed Full Screen shortcut to F12. Ctrl-F is now used for the search filter. + - Misc: Build system is now more like autoconf/automake. + - Misc: Disable default title/album/artist if options above are selected. + +0.8.0 + - New: Support creating custom playlists. + - New: Don't show duplicate albums names when multiple artists are selected. + - New: Added "All" entry to artist and album lists. + - New: Export database and playlists to XSPF, PLS, Extended M3U, M3U and CSV. + - New: Clipboard and drag-and-drop support for moving tracks to playlists and importing and exporting files. + - New: Configurable toolbar docked on top or bottom, showing big or small icons with optional text. + - New: Customizable Icon Theme. Either use buildin icons, or load from disk. + - New: Animated icon in scan progress dialog. + - New: Support year tag. + - New: Support volume normalization plugin. + - New: Database now also stores for each track: year, rating, import date, play date and play count. + - New: Option to close audio device when playback is stopped. + - New: Upgraded to more advanced build system. + - New: Include Desktop Application Entry and Icon in distribution + - New: New About Dialog. + - New: Report An Issue link in Help Menu. + - New: Changed License to GPL-3. + - New: Warning dialog when FOX without PNG support is used. + - New: Fullscreen mode (requires FOX-1.7.11). + + - Fix: Ctrl-A selection now works in all lists. + - Fix: Do not import MP4 files that contain video. + - Fix: Track titles are also now sorted using the leading keyword filter. + - Fix: Editing Dialogs retain proper sorting order of genres,artists and albums. + - Fix: Allow up to 4 digits in track number input in edit dialogs. + - Fix: issue preventing filenames with '#' characters from playing. + - Fix: compilation fix for older xine libraries. + - Fix: status line now shows correct song in repeat mode. + - Fix: sleeptimer now works properly in FOX 1.6. + - Fix: Write xine configuration file on exit. + - Fix: Don't recurse into directories that are symbolic links. + +0.7.5 + - Fix: Possible crash in empty track list due uninitialized variables. + +0.7.4 + - New: Added Sleep Timer. Stops playback after specified amount of time. + - New: Implement command line options to control an already running music manager. + - Fix: Reset Display after end playlist. + - Fix: Remember import files directory as well. + - Fix: Even more error handling in xine. + +0.7.3 + - Better playback error handling. + - Only use path title (filename without extension) for track title in case of files without tags. + - Track list now has multiple selection capability for easier tag editing. + - Assign track numbers automatically based on the selection order in the track list. + - Added Sorting Arrows on Genre,Artist and Album lists. + - Added Genre Column in tracklist. + - Better alignment for track numbers and times. + - Playing track has now different background color. + - Improved sorting. + +0.7.2 + - File naming options were not taking into account. + +0.7.1 + - Now supports renaming audio files during tag editing. + - Add Statistics Information Dialog. + - Speed up multiple selection in artist list. + - Cleanup dead and unused code. + - Minor Fixes. + +0.7.0 + - gapless playback support. + - mp4 support is now optional. + - Tag Editing Support. + - Repeat Track Support. + - Speed improvements in SQL database. + - Minimize jumping of slider control when seeking. + - Background color for every other row. + - Import of single files now also possible. + - Columns in tracklist are also now configurable in browse mode. + - Fix Crash when clearing database and reimporting in List Mode. + - Set default value for saving tag changes to file to false. + - Manage own taglib string memory because taglib is leaking. + - Also search for .m4p files. + +0.6.2 + - Ability to show directory contents in filemanager of selected song. + - Added "repeat all" option which determines whether to restart playing from the beginning of the play list or not + - FOX 1.7.x support. + - Fixed Relative Path display in scan/search dialog. + - Fixed Typos in welcome dialog. + - Statusbar by default off now. + - MP4 Tag Support [Read Only] + +0.6.1 + - New Progress Dialog during file scanning, which also includes the ability to cancel the scanning at any time. + - Added support for flac and musepack. + - Now depend on taglib for reading and writing tags. + - Ability to hide status bar. + - Support playing file from the command line arguments + - Only allow one gmm to be started and send play request to already running gmm. + +0.6.0 + - Added some more icons. + - Added userfriendly dialog at startup when database is empty. + - Use more advanced sorting routine that can interpret numbers in strings. + - Allow genre,artist and album list to be sorted in reverse. + - Fixed bug with setting current category on startup. + - Removed unused font which caused a crash. + - Tracklist was not properly sorted at startup + - FOX 1.5 -> FOX 1.6 + +0.5.3 + - Ability to remove certain artist from the database. + - FOX 1.4 -> FOX 1.5 + +0.5.2 + - Add Multiple Selection in Artist and Album List. + - Keep better track of last played track. + - Columns in songlist can be configured by the user. + +0.5.1 + - More error checking in database handling. + - Removed fastforward/fastbackward. + - Fixed bug that prevented sorting on time in normal list mode when clicking header. + - Columns in song list now remember their size. + - Songlist remembers its sorting column and order. + - Smart Sorting for lists. Skips common words like "the","a","an". This is a configurable option... + - Change remote mouse click functionality: one click skips to next song, double click opens manager. + - Remote Volume control changes volume immediately on first wheel mouse move. + - Check for old database on startup and remove it if users wants it. We aks only once. + - Remembers which displaymode it is in. + - Added random "sort" mode. + +0.5.0 + - New Database backend. We're now using SQLite. Database file has changed from goggles.database to goggles.db. + - While playing, if the volume is turned down to zero, playback will automatically pause. + - Add Category/Genre List to GUI. + - Fixed bug that prevented using Goggles Music Manager if FOX version was updated. + - Xine is really slow at reading in tags. I put the old ogg vorbis tag reading back. MP3 is still done by Xine. + - Added remote control window. + - Added preferences panel. + +0.2.6 + - FOX 1.4 support. + +0.2.5 + - Added MP3 file support. + - Removed editing capability of tag names for now. Hopefully we can add this back soon. + - We now use xine to retrieve all tags from ogg and mp3 files. It might be slower than before. We might change that in the future again. + +0.2.4 + - You can now either import files into a existing database or create a new database. + - While importing files, we now skip files that are already in the database. + - Now has four GUI modes: Full, Compact, Slim and List. + +0.2.3 + - Initial Release. diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..d40b195 --- /dev/null +++ b/INSTALL @@ -0,0 +1,37 @@ +Installation Instructions +------------------------- + + 1. Install all required dependencies: + + * FOX 1.6.x (recommended) + + or + + * FOX 1.7.x (latest development release, see README file for details) + + * xine-lib >= 1.1.16 + * sqlite >= 3.6.3 + * taglib >= 1.6.3 + * expat + * dbus 1.0.x (optional) + * libgcrypt (optional) + + 2. Run the configure script: + + > ./configure + + 3. Compile + + > make + + 4. If everything compiled fine, you are ready to install Goggles Music Manager in its final location. + By default this will be in /usr. Using the --prefix option on the configure command line you may + change this to something else (eg. ./configure --prefix=/usr/local ) + + > su + > make install + + 5. Run the software + + > gogglesmm + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6b0bc65 --- /dev/null +++ b/Makefile @@ -0,0 +1,216 @@ +#----------------------------------------------------------- +# GOGGLES BUILD SYSTEM +#----------------------------------------------------------- +# +# The actual make file +# +#----------------------------------------------------------- + +include config.make +include build/version + +# Set suffixes +.SUFFIXES: +.SUFFIXES: .cpp .h .gif .png $(OBJEXT) $(BINEXT) $(LIBEXT) + +.PHONY : all clean realclean cleanicons install install-desktop + +INSTALL=install + + +# Convert to Platform specific names +#---------------------------------------------------------- +BINNAME=src/gogglesmm$(BINEXT) # XXX on Linux, X.exe on Windows + +# Installation Directory +ifdef DESTDIR +INSTALL_DIR=$(DESTDIR)$(PREFIX) +INSTALL_LOCALEDIR=$(DESTDIR)$(LOCALEDIR) +INSTALL_MANDIR=$(DESTDIR)$(MANDIR) +else +INSTALL_DIR=$(PREFIX) +INSTALL_LOCALEDIR=$(LOCALEDIR) +INSTALL_MANDIR=$(MANDIR) +endif + +all: $(BINNAME) +TARNAME=gogglesmm-$(MAJOR).$(MINOR).$(LEVEL) + +ICONS := icons/cursor_hand.gif \ +icons/about.png \ +icons/gogglesmm_16.png \ +icons/gogglesmm_32.png + +# Objects to Compile +#---------------------------------------------------------- +SRCFILES := src/fxext.cpp \ +src/GMAbout.cpp \ +src/GMAnimImage.cpp \ +src/GMApp.cpp \ +src/GMAudioScrobbler.cpp \ +src/GMClipboard.cpp \ +src/GMColumnDialog.cpp \ +src/GMDatabase.cpp \ +src/GMDatabaseSource.cpp \ +src/GMFetch.cpp \ +src/GMFilename.cpp \ +src/GMFontDialog.cpp \ +src/GMImageView.cpp \ +src/GMEQDialog.cpp \ +src/GMIconTheme.cpp \ +src/GMImportDialog.cpp \ +src/GMList.cpp \ +src/GMPlayer.cpp \ +src/GMPlayerManager.cpp \ +src/GMPlayListSource.cpp \ +src/GMPreferences.cpp \ +src/GMPreferencesDialog.cpp \ +src/GMQuery.cpp \ +src/GMRemote.cpp \ +src/GMSearch.cpp \ +src/GMSource.cpp \ +src/GMSourceView.cpp \ +src/GMTag.cpp \ +src/GMThread.cpp \ +src/GMTrackDatabase.cpp \ +src/GMTrackList.cpp \ +src/GMTrackItem.cpp \ +src/GMTrackView.cpp \ +src/GMTrayIcon.cpp \ +src/GMStreamSource.cpp \ +src/GMURL.cpp \ +src/GMWindow.cpp \ +src/main.cpp \ +src/gmutils.cpp \ +src/ap_buffer.cpp \ +src/ap_http.cpp \ +src/ap_xml_parser.cpp \ +src/icons.cpp + +ifneq (,$(findstring md5,$(OPTIONS))) + SRCFILES += src/md5.cpp +endif + +ifneq (,$(findstring fox16,$(OPTIONS))) + SRCFILES += src/GMMessageChannel.cpp +endif + +ifneq (,$(findstring dbus,$(OPTIONS))) + SRCFILES += src/GMDBus.cpp \ + src/GMNotifyDaemon.cpp \ + src/GMMediaPlayerService.cpp \ + src/GMSettingsDaemon.cpp +endif + +OBJECTS := $(patsubst %.cpp,%$(OBJEXT),$(SRCFILES)) +DEPENDENCIES = $(patsubst %.cpp,%.d,$(SRCFILES)) + +$(BINNAME): $(OBJECTS) + @echo " Linking $@ ..." +# @echo "$(LINK) $(LDFLAGS) $(OUTPUTBIN)$(BINNAME) $(OBJECTS) $(LIBS)" + @$(LINK) $(LDFLAGS) $(OUTPUTBIN)$(BINNAME) $(OBJECTS) $(LIBS) + +%$(OBJEXT): %.cpp + @echo " Compiling $< ..." +# @echo "$(CXX) $(CFLAGS) $(DEFS) $(CPPFLAGS) -MM -o $*.d -MT $@ $<" + @$(CXX) $(CFLAGS) $(DEFS) $(CPPFLAGS) -MM -o $*.d -MT $@ $< +# @echo "$(CXX) $(CFLAGS) $(DEFS) $(CPPFLAGS) $(OUTPUTOBJ)$@ -c $<" + @$(CXX) $(CFLAGS) $(DEFS) $(CPPFLAGS) $(OUTPUTOBJ)$@ -c $< + +ifneq (,$(findstring dbus,$(OPTIONS))) + +src/gogglesmm_xml.h: src/gogglesmm.xml + @echo " Creating src/gogglesmm_xml.h..." + @$(RESWRAP_TEXT) -o $@ src/gogglesmm.xml + +src/mpris_xml.h: src/mpris_root.xml src/mpris_player.xml src/mpris_tracklist.xml + @echo " Creating src/mpris_xml.h..." + @$(RESWRAP_TEXT) -o $@ src/mpris_root.xml src/mpris_player.xml src/mpris_tracklist.xml + +endif + +$(OBJECTS): src/icons.h src/icons.cpp + +src/icons.h: $(ICONS) + @echo " Creating Icon Resource Header" + @$(RESWRAP_H) -o $@ $(ICONS) + +src/icons.cpp: $(ICONS) + @echo " Creating Icon Resources" + @$(RESWRAP_CPP) -o $@ $(ICONS) + +ifneq (,$(findstring dbus,$(OPTIONS))) +src/GMPlayerManager.cpp: src/gogglesmm_xml.h +src/GMMediaPlayerService.cpp: src/mpris_xml.h +endif + + +TRANSLATIONS:=$(basename $(notdir $(wildcard po/*.mo))) +LINGUAS?=$(TRANSLATIONS) + + +# Install +#---------------------------------------------------------- +install: $(BINNAME) + @echo " Installing $(INSTALL_DIR)/bin/gogglesmm ..." + @$(INSTALL) -m 755 -D src/gogglesmm $(INSTALL_DIR)/bin/gogglesmm + @echo " Installing $(INSTALL_DIR)/share/applications/gogglesmm.desktop" + @$(INSTALL) -m 644 -D extra/gogglesmm.desktop $(INSTALL_DIR)/share/applications/gogglesmm.desktop + @echo " Installing Icons" + $(INSTALL) -m 644 -D icons/gogglesmm_16.png $(INSTALL_DIR)/share/icons/hicolor/16x16/apps/gogglesmm.png + $(INSTALL) -m 644 -D extra/gogglesmm_22.png $(INSTALL_DIR)/share/icons/hicolor/22x22/apps/gogglesmm.png + $(INSTALL) -m 644 -D extra/gogglesmm_24.png $(INSTALL_DIR)/share/icons/hicolor/24x24/apps/gogglesmm.png + $(INSTALL) -m 644 -D icons/gogglesmm_32.png $(INSTALL_DIR)/share/icons/hicolor/32x32/apps/gogglesmm.png + $(INSTALL) -m 644 -D extra/gogglesmm_48.png $(INSTALL_DIR)/share/icons/hicolor/48x48/apps/gogglesmm.png + $(INSTALL) -m 644 -D extra/gogglesmm.svg $(INSTALL_DIR)/share/icons/hicolor/scalable/apps/gogglesmm.svg + @echo " Installing $(INSTALL_MANDIR)/man1/gogglesmm.1" + @$(INSTALL) -m 644 -D extra/gogglesmm.1 $(INSTALL_MANDIR)/man1/gogglesmm.1 +ifneq (,$(findstring nls,$(OPTIONS))) + @echo " Installing Translations" + @linguas='$(filter $(TRANSLATIONS),$(LINGUAS))'; \ + for tr in $$linguas ; do \ + echo " Installing $(INSTALL_LOCALEDIR)/$$tr/LC_MESSAGES/gogglesmm.mo" ;\ + $(INSTALL) -m 644 -D po/$$tr.mo -T $(INSTALL_LOCALEDIR)/$$tr/LC_MESSAGES/gogglesmm.mo ; \ + done; +endif + +# Clean +#---------------------------------------------------------- +clean : + @echo " Remove Executables ..." + @rm -f $(BINNAME) + + @echo " Remove Objects ..." + @rm -f src/*$(OBJEXT) + @rm -f src/*.d + @echo " Remove Generated Files ..." + @rm -f src/icons.cpp + @rm -f src/icons.h + @rm -f src/gogglesmm_xml.h + @rm -f src/mpris_xml.h +#---------------------------------------------------------- + +realclean : + @echo " Remove Configuration ..." + @rm -f config.make + @rm -f src/gmconfig.h + +dist: clean realclean + sh build/makemo + rm po/fi.mo + rsvg-convert -w 22 extra/gogglesmm.svg -o extra/gogglesmm_22.png + rsvg-convert -w 24 extra/gogglesmm.svg -o extra/gogglesmm_24.png + rsvg-convert -w 48 extra/gogglesmm.svg -o extra/gogglesmm_48.png + @echo " Creating Tarbals .." + tar --create --xz --file='../../$(TARNAME).tar.xz' --verbose --exclude-vcs --exclude='*.tar.xz' --transform='s/^./$(TARNAME)/' --show-transformed-names . + tar --create --bzip2 --file='../../$(TARNAME).tar.bz2' --verbose --exclude-vcs --exclude='*.tar.bz2' --transform='s/^./$(TARNAME)/' --show-transformed-names . + +# Clean Icons +#---------------------------------------------------------- +cleanicons : + @rm -f src/icons.* + @rm -f src/icons.* +#---------------------------------------------------------- + +# How to make everything else +-include $(DEPENDENCIES) diff --git a/README b/README new file mode 100644 index 0000000..2550186 --- /dev/null +++ b/README @@ -0,0 +1,145 @@ +Support +------- + + Found a bug, want a feature or tell how great Goggles Music Manager is/sucks. + Please mail me at s.jansen@gmail.com or file an issue at + http://code.google.com/p/gogglesmm + + +Installation +------------ + + Read the INSTALL file on how to build and install Goggles Music Manager on + your machine. + +Name Unification +---------------- + + Before 0.10.23 Goggles Music Manager used various abbreviations to refer to itself (musicmanager,gmm,gogglesm). + To avoid confusion, this naming has been unified in 0.10.23. Since a lot of distributions already used gogglesmm, + it was decided to use 'gogglesmm' as the standard base name. The following things are affected by this change: + + - The source package has been renamed to 'gogglesmm-x.y.z.tar.*' + - The executable has been renamed from 'gmm' to 'gogglesmm' + - The extra desktop files (gmm.desktop and gmm.png) have been renamed to (gogglesmm.desktop and gogglesmm.png) + + If you provide binary packages for Goggles Music Manager please distribute with the gogglesmm name. + +Upgrading +--------- + + If you are using Goggles Music Manager 0.7.x, please note that you will have to + reimport your music due to changes in the database. The database for 0.8.x / 0.9.x / 0.10.x / 0.11.x users + are automatically upgraded. + +FOX 1.6 / FOX 1.7 +------------------ + + Any FOX-1.6.x should work. I recommend to get latest version available (1.6.44 at the time of writing). + + Use FOX-1.7.x at your own risk. In general it should work with the latest release + and development snapshot. If it doesn't, you could try to fix it yourself or wait + for me to catch up with the latest FOX changes. + +Last.fm +------- + + Join the Goggles Music Manager group: + http://www.last.fm/group/Goggles+Music+Manager + + To start scrobble tracks: + + Go to and fill in your lastfm username and password. + If Goggles Music Manager can successfully authenticate with the lastfm service tracks, + will automatically get 'scrobbled'. If no internet connection is present (or a invalid + user/password has been given), tracks to be submitted will be queued until a internet + connection can be established again. + + The queue is kept in ~/.goggles/scrobbler.cache. + + To stop scrobble tracks: + + Uncheck last.fm or remove the lastfm username and password from preferences dialog. + Note that tracks won't be queued either. + + Limitations / Implementation Details + + * Uses audioscrobbler realtime submission protocol v1.2 + * No proxy support. + * Standard 1 minute delay before submitting played tracks. + * 80% of track need to be played to be counted as played. + Only tracks longer than 30sec will be submitted to last-fm. + +Audio Configuration +------------------- + + Specific configuration settings for XINE may be set in + + ~/.goggles/xineconf + + Run and play a track in Goggles Music Manager once to automatically create a default configuration. + + +Keyboard Shortcuts +------------------ + + Ctrl-N Reset default sorting order in current view. + Ctrl-R Shuffle track list. + Ctrl-B Toggles browse mode on or off. + Ctrl-G Toggles Genre list in browse mode. + Ctrl-O Import files from given directory. + Ctrl-J Jump to playing song in track list. + Ctrl-F Find + / Find + Ctrl-S Show source browser + Ctrl-P Start / Pause playback. + Ctrl-\ Stop playback. + Ctrl-[ Play previous track. + Ctrl-] Play next track. + Ctrl-T Toggle Repeat A-B. + Ctrl-A Select All in lists. + Ctrl-Q Quit Goggles Music Manager. + Ctrl-W Close Window + Ctrl-M / F11 + Toggle between Mini Player and full browser. + F12 Toggle Full Screen. + F2 Edit selected track, album, artist or genre. + Del Delete selected track, album, artist or genre. + + +Sorting +------- + + The following sort options are available for the track list: + + Browse: [only available in browse mode] + Sort on album, sort on artist and sort on track number. + Note that artist and album can be either ascending or descending based + on the sorting order of the album and artist list. + + Shuffle: + Try to randomize the order of the tracks. + + By Genre: + Sort on genre, sort on artist, sort on album and sort on track number. + + By Artist: + Sort on artist, sort on album, sort on track number + + By Album: + Sort on album, sort on track number + + By Time: + Sort on time + + By Track Number: + Sort on track number + + By Title: + Sort on title + + + For artists and albums special keywords may be set in the preferences + panel to exclude certain common words from sorting on. + + diff --git a/build/byteorderdetect b/build/byteorderdetect new file mode 100644 index 0000000..f0b0efd --- /dev/null +++ b/build/byteorderdetect @@ -0,0 +1,51 @@ +#----------------------------------------------------------- +# GOGGLES BUILD SYSTEM +#----------------------------------------------------------- +# +# This will determine the byteorder +# +#----------------------------------------------------------- +cd build + +makebyteorder() { +cat > checkbyteorder.cpp <<__EOF +int main() +{ + if (sizeof(long int) == 4) { + long int testInt = 0x12345678; + char * pMem; + pMem = (char *)&testInt; + if (pMem[0] == 0x78) + return 0; + else + return 1; + } + else if (sizeof(int) == 4) { + int testInt = 0x12345678; + char * pMem; + pMem = (char *) &testInt; + if (pMem[0] == 0x78) + return 0; + else + return 1; + } + return 0; +} +__EOF +} + + +makebyteorder +$CXX ${OUTPUTBIN} checkbyteorder checkbyteorder.cpp + +BYTEORDER=${BYTEORDER:-0} +if [ -x checkbyteorder ] ; then + ./checkbyteorder + if [ $? -eq 1 ] ; then + BYTEORDER=1 + fi + rm -rf checkbyteorder +fi +rm -rf checkbyteorder.cpp +DEFINES="$DEFINES -DFOX_BIGENDIAN=${BYTEORDER}" +cd .. diff --git a/build/functions b/build/functions new file mode 100644 index 0000000..faa75ac --- /dev/null +++ b/build/functions @@ -0,0 +1,161 @@ +#!/bin/sh +#----------------------------- + +CFG="src/gmconfig.h" + +add_package_path() +{ + if [ -n "$PKG_CONFIG_PATH" ] ; then + PKG_CONFIG_PATH="$1/lib/pkgconfig:$1/lib64/pkgconfig:$1/share/pkgconfig:$PKG_CONFIG_PATH" + else + PKG_CONFIG_PATH="$1/lib/pkgconfig:$1/lib64/pkgconfig:$1/share/pkgconfig" + fi + export PKG_CONFIG_PATH +} + +# Check for generic config in prefix +#----------------------------------- +check_in_prefix() +{ + echo " Search for $1 >= $4.$5.$6 in $2 ... " + + if [ ! -x $2/bin/$3 ] ; then + return 0 + fi + + echo " Check $1 Config => FOUND" + + CONFIG_LIB=$($2/bin/$3 --libs) + CONFIG_INCLUDE=$($2/bin/$3 --cflags) + CONFIG_VERSION=$($2/bin/$3 --version) + CONFIG_MAJOR=$(echo "${CONFIG_VERSION}" | cut -d. -f1) + CONFIG_MINOR=$(echo "${CONFIG_VERSION}" | cut -d. -f2) + CONFIG_LEVEL=$(echo "${CONFIG_VERSION}" | cut -d. -f3) + CONFIG_PREFIX=$2 + export CONFIG_PREFIX + + INCFLAGS="${INCFLAGS} ${CONFIG_INCLUDE} " + LIBS="${LIBS} ${CONFIG_LIB} " + + + # Make sure it is a compatible version + #-------------------------------------------- + if [ $CONFIG_MAJOR -lt $4 ] ; then + echo " Check $1 Version => Unsupported ($CONFIG_MAJOR.$CONFIG_MINOR.$CONFIG_LEVEL)" + return 0 + fi + + if [ $CONFIG_MAJOR -eq $4 ] && [ $CONFIG_MINOR -lt $5 ] ; then + echo " Check $1 Version => Unsupported ($CONFIG_MAJOR.$CONFIG_MINOR.$CONFIG_LEVEL)" + return 0 + fi + + if [ $CONFIG_MAJOR -eq $4 ] && [ $CONFIG_MINOR -eq $5 ] && [ $CONFIG_LEVEL -lt $6 ] ; then + echo " Check $1 Version => Unsupported ($CONFIG_MAJOR.$CONFIG_MINOR.$CONFIG_LEVEL)" + return 0 + fi + + echo " Check $1 Version => ${CONFIG_VERSION}" + echo "" + return 1 +} + + + +pkgconfig_query_package() +{ + echo " Search for $1" + pkg-config --exists $1 + if [ "$?" -ne "0" ] ; then + echo " Unable to find a compatible $2 installation. Please make" + echo " sure the correct version is installed including the header files." + echo " You can use the \"--$2-prefix\" option to search in an" + echo " alternative installation directory." + return 0 + fi + + PKG_VERSION=$(pkg-config --modversion $1 --print-errors --errors-to-stdout) + PKG_PREFIX=$(pkg-config --variable=prefix $1) + PKG_PREFIX=$(echo $PKG_PREFIX | tr -d '"') + PKG_LDFLAGS=$(pkg-config --libs-only-L $1) + PKG_LIBS=$(pkg-config --libs-only-l --libs-only-other $1) + PKG_CFLAGS=$(pkg-config --cflags-only-other $1) + PKG_CPPFLAGS=$(pkg-config --cflags-only-I $1) + + echo " Found $2 $PKG_VERSION in $PKG_PREFIX" + echo "" + return 1 +} + +add_config_string() +{ + echo "#define $1 \"${2}\"" >> $CFG +} + + +add_config() +{ + DEF=$(echo $1 | tr '[:lower:]' '[:upper:]') + echo "#define HAVE_${DEF}" >> $CFG + OPTIONS="$OPTIONS $1 " +} + + +add_required_package() +{ + pkgconfig_query_package "$1" "$2" + if [ "$?" -eq "0" ] ; then + exit -1 + fi + PACKAGES="$PACKAGES $1 " + add_config "$2" + return 1 +} + +add_package() +{ + pkgconfig_query_package "$1" "$2" + if [ "$?" -eq "0" ] ; then + return 0 + fi + PACKAGES="$PACKAGES $1 " + add_config "$2" + return 1 +} + +# Check for the FOX library +#-------------------------- +check_reswrap() +{ + # Configure Reswrap + #------------------ + echo " Checking Reswrap ... " + COMMAND=${RESWRAP:-${PKG_PREFIX}/bin/reswrap} + if [ ! -x $COMMAND ] ; then + echo "Missing reswrap" + exit 1 + fi + RESCMD=$(${COMMAND} -v 2>&1) + RESVERSION=$(echo ${RESCMD} | cut -d" " -f2) + RESWRAP_MAJOR=$(echo ${RESVERSION} | cut -d. -f1) + + if [ $RESWRAP_MAJOR = "5" ] ; then + RESWRAP_H="${COMMAND} --keep-ext --header" + RESWRAP_CPP="${COMMAND} --keep-ext --source --extern" + RESWRAP_TEXT="${COMMAND} -t --keep-ext" + else + RESWRAP_H="${COMMAND} -i -k" + RESWRAP_CPP="${COMMAND} -e -k" + RESWRAP_TEXT="${COMMAND} -t -k" + fi +} + +check_foxversion() +{ + PKG_MAJOR=$(echo "${PKG_VERSION}" | cut -d. -f1) + PKG_MINOR=$(echo "${PKG_VERSION}" | cut -d. -f2) + PKG_LEVEL=$(echo "${PKG_VERSION}" | cut -d. -f3) + add_config "fox${PKG_MAJOR}${PKG_MINOR}" +} + + diff --git a/build/makemo b/build/makemo new file mode 100644 index 0000000..8ed7503 --- /dev/null +++ b/build/makemo @@ -0,0 +1,8 @@ +#------------------------------------------------------------------------------- +# Make Mo's +#------------------------------------------------------------------------------- +for i in po/*.po +do + echo "Generating ${i%.po}.mo ..." + msgfmt $i -o ${i%.po}.mo +done diff --git a/build/makepot b/build/makepot new file mode 100644 index 0000000..2413bfb --- /dev/null +++ b/build/makepot @@ -0,0 +1,36 @@ +#------------------------------------------------------------------------------- +# Make template +#------------------------------------------------------------------------------- +FOXDIR=../../../fox-1.6.37 + + +INPUT="src/*.cpp src/*.h" +xgettext -C --from-code=UTF-8 --msgid-bugs-address=s.jansen@gmail.com --package-name=gogglesmm --package-version=0.10.0 --copyright-holder="Sander Jansen" --keyword=tr:1 --keyword=fxtr:1 --keyword=notr:1 --keyword=fxtrformat:1 --flag=fxtrformat:1:c-format -o po/gogglesmm.pot $INPUT + +#INPUT="include/GMGenres.h" +#xgettext -C -j -a --from-code=UTF-8 --msgid-bugs-address=s.jansen@gmail.com --package-name=gogglesmm --package-version=0.10.0 --copyright-holder="Sander Jansen" -o po/gogglesmm.pot $INPUT + + +# FOX files +INPUT="$FOXDIR/src/FXMessageBox.cpp \ +$FOXDIR/src/FXColorSelector.cpp \ +$FOXDIR/src/FXDirSelector.cpp \ +$FOXDIR/src/FXFileList.cpp \ +$FOXDIR/src/FXFileSelector.cpp \ +$FOXDIR/src/FXReplaceDialog.cpp \ +$FOXDIR/src/FXSearchDialog.cpp \ +$FOXDIR/src/FXStatusLine.cpp" + +xgettext -C -j --from-code=UTF-8 --msgid-bugs-address=s.jansen@gmail.com --package-name=gogglesmm --package-version=0.10.0 --copyright-holder="Sander Jansen" --keyword=tr:1 -o po/gogglesmm.pot $INPUT + + +# Additional non tr ones +#INPUT="$FOXDIR/src/FXColorNames.cpp" +#xgettext -C -j -a --from-code=UTF-8 --msgid-bugs-address=s.jansen@gmail.com --package-name=gogglesmm --package-version=0.10.0 --copyright-holder="Sander Jansen" -o po/gogglesmm.pot $INPUT + + +for i in po/*.po +do + echo "Updating $i ..." + msgmerge -U $i po/gogglesmm.pot +done diff --git a/build/version b/build/version new file mode 100644 index 0000000..20a7152 --- /dev/null +++ b/build/version @@ -0,0 +1,5 @@ +MAJOR=0 +MINOR=12 +LEVEL=7 +BETA_DB=0 +BETA_APP=0 diff --git a/configure b/configure new file mode 100755 index 0000000..e58d1d7 --- /dev/null +++ b/configure @@ -0,0 +1,322 @@ +#!/bin/sh +#----------------------------- + +. build/version +. build/functions + +# Settings +#--------------------------- +DEBUG=0 +DBUS=1 +FOX17=0 +HELP=0 +NLS=1 +MD5=gcrypt +PREFIX=${PREFIX:-/usr} + +# Default Compiler Settings +# Use environment variables to override +#-------------------------- +CXX=${CXX:-g++} +LINK=${LINK:-g++} +INCFLAGS=${INCFLAGS:-} +LIBS=${LIBS:-} +LIBPREFIX=${LIBPREFIX:-lib} +LIBEXT=${LIBEXT:-.so} +OUTPUTOBJ=${OUTPUTOBJ:--o } +OUTPUTBIN=${OUTPUTBIN:--o } +OBJEXT=${OBJEXT:-.o} + +# Overriding Compiler Flags +# Either override GEN_CFLAGS, OPT_CFLAGS and DEBUG_CFLAGS individually +# or set CFLAGS which overrides all them +#------------------------------------------------------- +GEN_CFLAGS=${GEN_CFLAGS:--Wall -Wextra -Wformat=2 -pipe} +OPT_CFLAGS=${OPT_CFLAGS:--O3 -march=native -fstack-protector} +DEBUG_CFLAGS=${DEBUG_CFLAGS:--g -fstack-protector-all} + +# Overriding Linker Flags +# Either override GEN_LDFLAGS, OPT_LDFLAGS and DEBUG_LDFLAGS individually +# or set LDFLAGS which overrides all them +#------------------------------------------------------- +GEN_LDFLAGS=${GEN_LDFLAGS:-} +OPT_LDFLAGS=${OPT_LDFLAGS:--s} +DEBUG_LDFLAGS=${DEBUG_LDFLAGS:-} + +# Parse Command Line Arguments +#----------------------------- +for arg in $@; do + case $arg in + --enable-debug) + DEBUG=1 + ;; + --with-md5=*) + MD5=${arg#*=} + ;; + --disable-nls) + NLS=0 + ;; + --without-dbus) + DBUS=0 + ;; + --prefix=*) + PREFIX=${arg#*=} + ;; + --fox-prefix=*) + FOX_PREFIX=${arg#*=} + add_package_path $FOX_PREFIX + ;; + --xine-prefix=*) + XINE_PREFIX=${arg#*=} + add_package_path $XINE_PREFIX + ;; + --sqlite-prefix=*) + SQLITE_PREFIX=${arg#*=} + add_package_path $SQLITE_PREFIX + ;; + --taglib-prefix=*) + TAGLIB_PREFIX=${arg#*=} + add_package_path $TAGLIB_PREFIX + ;; + --dbus-prefix=*) + DBUS_PREFIX=${arg#*=} + add_package_path $DBUS_PREFIX + ;; + --localedir=*) + NLSDIR=${arg#*=} + ;; + --mandir=*) + MANDIR=${arg#*=} + ;; + --with-fox17) + FOX17=1 + ;; + --help) + HELP=1 + ;; + -h) + HELP=1 + ;; + esac +done + + +# Print Command Line Options +#--------------------------- +if [ $HELP -eq 1 ] ; then + echo "Available options:" + echo " --enable-debug Compile with debug information" + echo " --with-md5=gcrypt,internal Select which md5 implementation to use. (gcrypt)" + echo " --without-dbus Turn off DBus Support" + echo " --without-new-remote Disable new remote and use old mini player." + echo " --with-fox17 Compile with FOX 1.7.x." + echo " --prefix=

Installation prefix (/usr)" + echo " --fox-prefix=

fox prefix path" + echo " --xine-prefix=

xinelib prefix path" + echo " --taglib-prefix=

tagLib prefix path" + echo " --sqlite-prefix=

sqlite prefix path" + echo " --dbus-prefix=

dbus prefix path" + echo " --localedir=

locale-dependent data (/share/locale)" + echo " --mandir= man pages (/share/man)" + echo " -h,--help Show Help" + exit 0 +fi + +# Debug / Release Mode +#---------------------- +if [ $DEBUG -eq 1 ] ; then + CFLAGS=${CFLAGS:-$GEN_CFLAGS $DEBUG_CFLAGS} + LDFLAGS=${LDFLAGS:-$GEN_LDFLAGS $DEBUG_LDFLAGS} + DEFINES="$DEFINES -DDEBUG" +else + CFLAGS=${CFLAGS:-$GEN_CFLAGS $OPT_CFLAGS} + LDFLAGS=${LDFLAGS:-$GEN_LDFLAGS $OPT_LDFLAGS} + DEFINES="$DEFINES -DNDEBUG" +fi + +# Init Config File +#------------------------------ +echo "/* Generated by configure utility */" > $CFG +echo "#ifndef GMCONFIG_H" >> $CFG +echo "#define GMCONFIG_H" >> $CFG +echo "" >> $CFG +echo "#define APPLICATION_MAJOR $MAJOR" >> $CFG +echo "#define APPLICATION_MINOR $MINOR" >> $CFG +echo "#define APPLICATION_LEVEL $LEVEL" >> $CFG +echo "#define APPLICATION_BETA $BETA_APP" >> $CFG +echo "#define APPLICATION_BETA_DB $BETA_DB" >> $CFG +echo "#define BUILD_DATE __DATE__" >> $CFG +echo "#define BUILD_TIME __TIME__" >> $CFG +echo "" >> $CFG + +if [ $BETA_APP -eq 0 ] ; then +echo "#define APPLICATION_VERSION_STRING \"$MAJOR.$MINOR.$LEVEL\"" >> $CFG +else +echo "#define APPLICATION_VERSION_STRING \"$MAJOR.$MINOR.$LEVEL-rc$BETA_APP\"" >> $CFG +fi +echo "" >> $CFG + +echo "" +echo "Goggles Music Manager:" + + +# Libraries +#------------------------ +if [ $FOX17 -eq 1 ] ; then + add_required_package "fox17 >= 1.7" "fox" +else + add_required_package "fox >= 1.6" "fox" +fi +check_reswrap +check_foxversion + +add_required_package "sqlite3 >= 3.6.3" "sqlite" +add_required_package "taglib >= ${TAGLIB_VERSION:-1.6.3}" "taglib" + +# DBus is optional +if [ $DBUS -eq 1 ] ; then + add_package "dbus-1 >= 1.0" "dbus" + DBUS=$? +fi + +# We need both X11 and GLU +# For systems without pkg-config for x11 and glu we'll just hardwire it. +add_package "x11" "x11" +if [ $? -eq 0 ] ; then + EXTRALIBS="$EXTRALIBS -lX11" +fi + +add_package "glu" "glu" +if [ $? -eq 0 ] ; then + EXTRALIBS="$EXTRALIBS -lGL -lGLU" +fi + +# xine >= 1.2.0 use pkg-config +add_package "libxine" "libxine" +if [ $? -eq 0 ] ; then + + # Old xine is still supported... + XINE_MAJOR=${XINE_MAJOR:-1} + XINE_MINOR=${XINE_MINOR:-1} + XINE_LEVEL=${XINE_LEVEL:-16} + if [ -n "$XINE_PREFIX" ] ; then + check_in_prefix "XINE" $XINE_PREFIX "xine-config" $XINE_MAJOR $XINE_MINOR $XINE_LEVEL + if [ $? -eq 0 ] ; then + echo " Unable to find a compatible XINE library installation. Please make" + echo " sure the correct version is installed including the header files." + echo " You can use the \"--xine-prefix\" option to search in an" + echo " alternative installation directory." + exit -1 + fi + else + check_in_prefix "XINE" "/usr" "xine-config" $XINE_MAJOR $XINE_MINOR $XINE_LEVEL + if [ $? -eq 0 ] ; then + check_in_prefix "XINE" "/usr/local" "xine-config" $XINE_MAJOR $XINE_MINOR $XINE_LEVEL + if [ $? -eq 0 ] ; then + echo " Unable to find a compatible XINE library installation. Please make" + echo " sure the correct version is installed including the header files." + echo " You can use the \"--xine-prefix\" option to search in an" + echo " alternative installation directory." + exit -1 + fi + fi + fi + add_config "libxine" +fi + + +PKG_LDFLAGS=$(pkg-config --libs-only-L $PACKAGES) +PKG_LIBS=$(pkg-config --libs-only-l --libs-only-other $PACKAGES) +PKG_CFLAGS=$(pkg-config --cflags-only-other $PACKAGES) +PKG_CPPFLAGS=$(pkg-config --cflags-only-I $PACKAGES) + +if [ -n "$PKG_LDFLAGS" ] ; then LIBS="$LIBS $PKG_LDFLAGS"; fi +if [ -n "$PKG_LIBS" ] ; then LIBS="$LIBS $PKG_LIBS"; fi +if [ -n "$PKG_CFLAGS" ] ; then GEN_CFLAGS="$GEN_CFLAGS $PKG_CFLAGS"; fi +if [ -n "$PKG_CPPFLAGS" ] ; then INCFLAGS="$INCFLAGS $PKG_CPPFLAGS"; fi + + + +. build/byteorderdetect + +if [ $NLS -eq 1 ] ; then + NLSDIR=${NLSDIR:-$PREFIX/share/locale} + add_config "nls" + add_config_string "LOCALEDIR" ${NLSDIR} +fi + +if [ "$MD5" != "internal" ] ; then + add_config "gcrypt" + GCRYPT_LIBS=$(libgcrypt-config --libs) + LIBS="$LIBS $GCRYPT_LIBS" +else + add_config "md5" +fi + +#Set MANDIR +MANDIR=${MANDIR:-$PREFIX/share/man} + +# expat (for now hardwired) +#------------------------ +LIBS="$LIBS $EXTRALIBS -lexpat" + + +# End of configuration +#------------------------- +echo "" >> $CFG +echo "#endif" >> $CFG + + +# Write out Makefile configuration +#-------------------------------------- +echo "#Generated by configure utility" > config.make +echo "CXX=$CXX" >> config.make +echo "LINK=$LINK" >> config.make +echo "CFLAGS=$CFLAGS" >> config.make +echo "CPPFLAGS=$DEFINES $INCFLAGS" >> config.make +echo "LDFLAGS=$LDFLAGS" >> config.make +echo "LIBS=$LIBS" >> config.make +echo "OUTPUTOBJ=$OUTPUTOBJ" >> config.make +echo "OUTPUTBIN=$OUTPUTBIN" >> config.make +echo "OBJEXT=$OBJEXT" >> config.make +echo "RESWRAP_CPP=$RESWRAP_CPP" >> config.make +echo "RESWRAP_H=$RESWRAP_H" >> config.make +echo "RESWRAP_TEXT=$RESWRAP_TEXT" >> config.make +echo "PREFIX=$PREFIX" >> config.make +echo "OPTIONS=$OPTIONS" >> config.make +echo "LOCALEDIR=$NLSDIR" >> config.make +echo "MANDIR=$MANDIR" >> config.make + + +# Summary +#------------------------- +echo "Features:" +if [ "$MD5" != "internal" ] ; then + echo " MD5 => libgcrypt" +else + echo " MD5 => internal" +fi + +if [ $DBUS -eq 0 ] ; then + echo " DBUS Support => no" +else + echo " DBUS Support => yes" +fi + +if [ $NLS -eq 0 ] ; then + echo " NLS Support => no" +else + echo " NLS Support => yes" + echo " Locale Path => $NLSDIR" +fi + echo " Man Path => $MANDIR" + echo " Install Path => $PREFIX" + + +# Success +#-------------------------------------- +echo "" +echo "Done." +echo "Please run \"make\" and \"make install\" to compile and install GMM." +exit 0 + diff --git a/extra/gogglesmm.1 b/extra/gogglesmm.1 new file mode 100644 index 0000000..d30446e --- /dev/null +++ b/extra/gogglesmm.1 @@ -0,0 +1,134 @@ +.TH gogglesmm 1 "03 August 2010" +.SH NAME +gogglesmm \- Goggles Music Manager +.SH SYNOPSIS +.B gogglesmm +[options] [file] +.SH INTRODUCTION +Goggles Music Manager is a music collection manager and player. It supports gapless playback and features easy tag editing. +.SH OPTIONS +.I General Options +.TP +.B \-h, \-\-help +Display list of options +.TP +.B \-v, \-\-version +Display version information +.LP +.I Startup Options +.TP +.B \-\-tray +Start minimized to tray +.TP +.B \-\-disable-opengl +Disables opengl based features +.TP +.B \-\-xine-debug +Output xine debug info to standard output +.LP +.I Playback Control +.TP +.B \-\-play +Start playback +.TP +.B \-\-pause +Pause playback +.TP +.B \-\-play\-pause +Toggle pause / playback. +.TP +.B \-\-previous +Play previous track +.TP +.B \-\-next +Play next track +.TP +.B \-\-stop +Stop playback +.LP +.I Misc Options +.TP +.B \-\-toggle-shown +Show or Hide the main window +.TP +.B \-\-raise +Try to raise the main window +.TP +.B \-\-now\-playing +Show now playing notification +.SH KEYBOARD SHORTCUTS +.TP +.B "Ctrl-N" +Reset default sorting order in current view. +.TP +.B "Ctrl-R" +Shuffle track list. +.TP +.B "Ctrl-B" +Toggles browse mode on or off. +.TP +.B "Ctrl-G" +Toggles Genre list in browse mode. +.TP +.B "Ctrl-Q" +Quit Goggles Music Manager. +.TP +.B "Ctrl-O" +Import files from given directory. +.TP +.B "Ctrl-J" +Jump to playing song in track list. +.TP +.B "Ctrl-F" or "/" +Find +.TP +.B "Ctrl-S" +Show source browser +.TP +.B "Ctrl-P" +Start / Pause playback. +.TP +.B "Ctrl-\" +Stop playback. +.TP +.B "Ctrl-[" +Play previous track. +.TP +.B "Ctrl-]" +Play next track. +.TP +.B "Ctrl-T" +Toggle Repeat A-B. +.TP +.B "Ctrl-A" +Select All in lists. +.TP +.B "Ctrl-W" +Close Window +.TP +.B "F11" or "Ctrl-M" +Toggle between mini player and browser. +.TP +.B "F12" +Show Full Screen. +.TP +.B "F2" +Edit selected track, album, artist or genre. +.TP +.B "Del" +Delete selected track, album, artist or genre. +.SH FILES +.TP +.I ~/.goggles/goggles.db +Music Database +.TP +.I ~/.goggles/xineconf +xine settings +.TP +.I ~/.foxrc/musicmanager +settings +.TP +.I $(XDG_CACHE_HOME)/gogglesmm/icontheme.cache +Icon theme cache +.SH AUTHOR +Sander Jansen diff --git a/extra/gogglesmm.desktop b/extra/gogglesmm.desktop new file mode 100644 index 0000000..dcfbffd --- /dev/null +++ b/extra/gogglesmm.desktop @@ -0,0 +1,24 @@ +[Desktop Entry] +Name=Goggles Music Manager +Name[en_CA]=Goggles Music Manager +Name[en_GB]=Goggles Music Manager +Name[nl]=Goggles muziekspeler +Name[pt]= Goggles - Reprodutor de músicas +GenericName=Music Player +GenericName[en_CA]=Music Player +GenericName[en_GB]=Music Player +GenericName[nl]=Muziekspeler +GenericName[pt]=Reprodutor de músicas +Comment=Play and organize your music collection +Comment[en_CA]=Play and organize your music collection +Comment[en_GB]=Play and organise your music collection +Comment[nl]=Luister and beheer uw muziekcollectie +Comment[pt]=Reprodução e organização de músicas +Exec=gogglesmm %u +Terminal=false +Hidden=false +Type=Application +Icon=gogglesmm +Categories=Application;AudioVideo;Audio;Player; +MimeType=audio/flac;audio/ogg;audio/x-vorbis;audio/x-vorbis+ogg;audio/mpeg;audio/mp4a-latm;audio/x-musepack; + diff --git a/extra/gogglesmm.svg b/extra/gogglesmm.svg new file mode 100644 index 0000000..6786b30 --- /dev/null +++ b/extra/gogglesmm.svg @@ -0,0 +1,248 @@ + + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/extra/gogglesmm_22.png b/extra/gogglesmm_22.png new file mode 100644 index 0000000000000000000000000000000000000000..88429093ef7aca7b2495fe65fd0cb96fd0042b81 GIT binary patch literal 1302 zcmV+x1?l>UP)5x3KJDD*!+RpiKgMz06i$t0Ec~`wo0mxVmYnw!-P8Ab0W?uVk}tyz0DAR>~Lv z_W3>&QwHfvOOUK?RV_2`DO{fB*u>mSFe|Hi zYQM91_68$42-W5Us00WBMRQ>&+-7;E40fMC-cfZI*6C+_*9Q&Jiyh@!hCA%Kql8c@ z!?Fy-2QKZtapmA&UEE8p?-SU zb8F9mM+%tYDx^$?L^(1>bhP(Y7KGVn0{|)UE&npVsA1Jm_!^2TmO#t%fHD;zfMMK* z5%ojX@MbhLzmfItWi2oB3Kd*cHwOC{A&T0{s%X=M`<(QZ&l zf-SEYmaKr3_`qo#MxZA_sg#zrDIQ(-ARcZcQZYXmvwZXbwGDUmTe=RWA_& z7)cHk{MwB)=a#J5ivYoqF=L3UF+`jxC_SOfP`GWEkqmru-%5ME3mP^}#-<>-)vGyOq6T-D((Zi3G6}zXZGLx_T?mz#_<@TRP|J>yG6M7j0tZIcBumAu6 M07*qoM6N<$g2u*JPXGV_ literal 0 HcmV?d00001 diff --git a/extra/gogglesmm_24.png b/extra/gogglesmm_24.png new file mode 100644 index 0000000000000000000000000000000000000000..368fa4275040885ce4be0999c885573bc48c841b GIT binary patch literal 1474 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 G25SHjN-P%u literal 0 HcmV?d00001 diff --git a/icons/gogglesmm_16.png b/icons/gogglesmm_16.png new file mode 100644 index 0000000000000000000000000000000000000000..131be5deea693228aef7385da86927133a6b2b51 GIT binary patch literal 845 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 literal 0 HcmV?d00001 diff --git a/po/cs.po b/po/cs.po new file mode 100644 index 0000000..12e1c5d --- /dev/null +++ b/po/cs.po @@ -0,0 +1,2703 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Sander Jansen +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: gogglesmm 0.10.0\n" +"Report-Msgid-Bugs-To: s.jansen@gmail.com\n" +"POT-Creation-Date: 2011-02-09 23:26-0600\n" +"PO-Revision-Date: 2011-02-10 19:05+0100\n" +"Last-Translator: David Vachulka \n" +"Language-Team: Czech \n" +"Language: cs\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%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" + +#: src/GMAbout.cpp:136 +#: src/GMDatabaseSource.cpp:218 +#: src/GMDatabaseSource.cpp:2215 +#: src/GMPreferencesDialog.cpp:551 +msgid "&Close" +msgstr "&Zavřít" + +#: src/GMColumnDialog.cpp:204 +msgid "Configure Columns" +msgstr "Konfigurace sloupců" + +#: src/GMColumnDialog.cpp:207 +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:168 +msgid "&Accept" +msgstr "&Přijmout" + +#: src/GMColumnDialog.cpp:208 +#: src/GMDatabaseSource.cpp:98 +#: src/GMDatabaseSource.cpp:1274 +#: src/GMDatabaseSource.cpp:1704 +#: src/GMDatabaseSource.cpp:1804 +#: src/GMDatabaseSource.cpp:1878 +#: src/GMDatabaseSource.cpp:2121 +#: src/GMDatabaseSource.cpp:2182 +#: src/GMImportDialog.cpp:175 +#: src/GMImportDialog.cpp:563 +#: src/GMPlayListSource.cpp:281 +#: src/GMPlayListSource.cpp:410 +#: src/GMSearch.cpp:572 +#: src/GMStreamSource.cpp:169 +#: src/GMStreamSource.cpp:206 +#: src/GMStreamSource.cpp:271 +#: src/GMWindow.cpp:1198 +#: src/GMWindow.cpp:1247 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:107 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:118 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:123 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:129 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:135 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:142 +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:169 +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:135 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:205 +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:113 +msgid "&Cancel" +msgstr "Z&rušit" + +#: src/GMColumnDialog.cpp:212 +msgid "" +"Choose the order of information to appear\n" +"in the track list." +msgstr "" +"Vyberte pořadí informací pro zobrazení\n" +"v seznamu skladeb." + +#: src/GMColumnDialog.cpp:218 +msgid "Move Up" +msgstr "Posunout nahoru" + +#: src/GMColumnDialog.cpp:219 +msgid "Move Down" +msgstr "Posunout dolů" + +#: src/GMDatabaseSource.cpp:52 +msgid "Invalid Template" +msgstr "Neplatná šablona" + +#: src/GMDatabaseSource.cpp:52 +#, c-format +msgid "" +"The provided template is invalid. The track title %%T needs to be specified.\n" +"Please fix the filename template in the preference panel." +msgstr "" +"Daná šablona není platná. Jméno skladby %%T musí být definováno.\n" +"Upravte šablonu v panelu nastavení." + +#: src/GMDatabaseSource.cpp:72 +#: src/GMTrackDatabase.cpp:193 +msgid "Database Error" +msgstr "Chyba databáze" + +#: src/GMDatabaseSource.cpp:72 +msgid "Oops. Database Error" +msgstr "Ajaj. Chyba databáze" + +#: src/GMDatabaseSource.cpp:87 +msgid "No changes" +msgstr "Beze změn" + +#: src/GMDatabaseSource.cpp:87 +msgid "Filenames did not require any changes" +msgstr "Jména souborů nevyžadují žádnou změnu" + +#: src/GMDatabaseSource.cpp:94 +msgid "Rename Audio Files?" +msgstr "Přejmenovat audio soubory?" + +#: src/GMDatabaseSource.cpp:95 +msgid "Renaming Audio Files…" +msgstr "Přejmenování audio souborů..." + +#: src/GMDatabaseSource.cpp:95 +msgid "The following audio files are going to be renamed" +msgstr "Následující audio soubory budou přejmenovány" + +#: src/GMDatabaseSource.cpp:97 +msgid "&Rename" +msgstr "&Přejmenovat" + +#: src/GMDatabaseSource.cpp:121 +#: src/GMDatabaseSource.cpp:125 +msgid "Unable to rename file" +msgstr "Nemohu přejmenovat soubor" + +#: src/GMDatabaseSource.cpp:121 +#, c-format +msgid "" +"Unable to rename:\n" +"%s\n" +"\n" +"to:%s\n" +"Continue renaming files?" +msgstr "" +"Nemohu přejmenovat:\n" +"%s\n" +"\n" +"na:%s\n" +"Pokračovat v přejmenování?" + +#: src/GMDatabaseSource.cpp:125 +#, c-format +msgid "" +"Unable to rename:\n" +"%s\n" +"\n" +"to:%s" +msgstr "" +"Nemohu přejmenovat:\n" +"%s\n" +"\n" +"na:%s" + +#: src/GMDatabaseSource.cpp:160 +#: src/GMImportDialog.cpp:534 +msgid "Filename Template" +msgstr "Šablona jména souboru" + +#: src/GMDatabaseSource.cpp:177 +msgid "" +"Template may contain absolute or relative path, environment variables\n" +"and ~. Relative paths are based on the location of the original file. The\n" +"file extension gets automatically added. The following macros\n" +"may be used:" +msgstr "" +"Šablona může obsahovat absolutní nebo relativní cestu, proměnné prostředí\n" +"a ~. Relativní cesty jsou založeny na umístění souboru.\n" +"Přípony souboru budou automaticky přidány. Následující makra\n" +"můžou být použity:" + +#: src/GMDatabaseSource.cpp:178 +msgid "" +"%T - title %A - album name\n" +"%P - album artist name %p - track artist name\n" +"%y - year %d - disc number\n" +"%N - track number (2 digits) %n - track number \n" +"%G - genre" +msgstr "" +"%T - titul %A - jméno alba\n" +"%P - jméno interpreta alba\n" +"%p - jméno interpreta skladby\n" +"%N - číslo skladby (2 místa)\n" +"%n - číslo skladby\n" +"%G - žánr" + +#: src/GMDatabaseSource.cpp:185 +msgid "Conditions may be used as well:" +msgstr "Podmínky můžou být použity následujícím způsobem:" + +#: src/GMDatabaseSource.cpp:186 +msgid "" +"?c - display a if c is not empty else display b.\n" +"?c - display c if not empty\n" +msgstr "" +"?c - zobrazí a, jestliže není c prázdné, jinak zobrazí b.\n" +"?c - zobrazí c, jestliže není prázdné\n" + +#: src/GMDatabaseSource.cpp:195 +#: src/GMDatabaseSource.cpp:1815 +#: src/GMImportDialog.cpp:546 +msgid "Template:" +msgstr "Šablona:" + +#: src/GMDatabaseSource.cpp:199 +#: src/GMDatabaseSource.cpp:1819 +msgid "Encoding:" +msgstr "Kódování:" + +#: src/GMDatabaseSource.cpp:205 +msgid "Exclude:" +msgstr "Vynechat:" + +#: src/GMDatabaseSource.cpp:209 +#: src/GMDatabaseSource.cpp:1825 +msgid "Options:" +msgstr "Volby:" + +#: src/GMDatabaseSource.cpp:210 +#: src/GMDatabaseSource.cpp:1826 +msgid "Replace spaces with underscores" +msgstr "Nahradit mezery podtržením" + +#: src/GMDatabaseSource.cpp:212 +#: src/GMDatabaseSource.cpp:1828 +msgid "Lower case" +msgstr "Malá písmena" + +#: src/GMDatabaseSource.cpp:214 +#: src/GMDatabaseSource.cpp:1830 +msgid "Lower case extension" +msgstr "Malá písmena přípony" + +#: src/GMDatabaseSource.cpp:341 +#: src/GMStreamSource.cpp:63 +msgid "No." +msgstr "Č." + +#: src/GMDatabaseSource.cpp:342 +msgid "Queue" +msgstr "Fronta" + +#: src/GMDatabaseSource.cpp:343 +#: src/GMDatabaseSource.cpp:1391 +#: src/GMTrackView.cpp:238 +msgid "Title" +msgstr "Název" + +#: src/GMDatabaseSource.cpp:344 +#: src/GMDatabaseSource.cpp:1396 +#: src/GMTrackView.cpp:239 +msgid "Artist" +msgstr "Interpret" + +#: src/GMDatabaseSource.cpp:345 +#: src/GMDatabaseSource.cpp:1397 +msgid "Album Artist" +msgstr "Interpret alba" + +#: src/GMDatabaseSource.cpp:346 +#: src/GMDatabaseSource.cpp:1405 +#: src/GMPreferencesDialog.cpp:540 +#: src/GMTrackView.cpp:240 +msgid "Album" +msgstr "Album" + +#: src/GMDatabaseSource.cpp:347 +#: src/GMDatabaseSource.cpp:1364 +#: src/GMDatabaseSource.cpp:1375 +#: src/GMDatabaseSource.cpp:1381 +msgid "Disc" +msgstr "Disk" + +#: src/GMDatabaseSource.cpp:348 +#: src/GMDatabaseSource.cpp:1408 +#: src/GMStreamSource.cpp:66 +#: src/GMStreamSource.cpp:177 +#: src/GMStreamSource.cpp:216 +#: src/GMTrackView.cpp:241 +msgid "Genre" +msgstr "Žánr" + +#: src/GMDatabaseSource.cpp:349 +#: src/GMDatabaseSource.cpp:1369 +#: src/GMDatabaseSource.cpp:1387 +msgid "Year" +msgstr "Rok" + +#: src/GMDatabaseSource.cpp:350 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:730 +msgid "Time" +msgstr "Délka" + +#: src/GMDatabaseSource.cpp:490 +msgid "Remove…\tDel\tRemove Genre from Library." +msgstr "Odstranit...\tDel\tOdstraní žánr z knihovny." + +#: src/GMDatabaseSource.cpp:496 +#: src/GMDatabaseSource.cpp:505 +#: src/GMPlayListSource.cpp:104 +#: src/GMPlayListSource.cpp:110 +msgid "Copy\tCtrl-C\tCopy associated tracks to the clipboard." +msgstr "Kopírovat\tCtrl-C\tKopíruje asociované skladby do schránky." + +#: src/GMDatabaseSource.cpp:499 +#: src/GMDatabaseSource.cpp:508 +msgid "Remove…\tDel\tRemove associated tracks from library." +msgstr "Odstranit...\tDel\tOdstraní asociované skladby z knihovny." + +#: src/GMDatabaseSource.cpp:513 +#: src/GMPlayListSource.cpp:116 +msgid "Edit…\tF2\tEdit Track Information." +msgstr "Upravit...\tF2\tUpraví informace o skladbě." + +#: src/GMDatabaseSource.cpp:514 +#: src/GMPlayListSource.cpp:117 +msgid "Copy\tCtrl-C\tCopy track(s) to clipboard." +msgstr "Kopírovat\tCtrl-C\tKopíruje skladbu(y) to schránky." + +#: src/GMDatabaseSource.cpp:518 +#: src/GMPlayListSource.cpp:120 +msgid "Open Folder Location\t\tOpen Folder Location." +msgstr "Otevřít adresář\t\tOtevře adresář." + +#: src/GMDatabaseSource.cpp:523 +msgid "Remove…\tDel\tRemove track(s) from library." +msgstr "Odstranit...\tDel\tOdstraní skladbu(y) z knihovny." + +#: src/GMDatabaseSource.cpp:537 +msgid "New Play List…\t\tCreate a new play list." +msgstr "Nový seznam skladeb...\t\tVytvoří nový seznam skladeb." + +#: src/GMDatabaseSource.cpp:539 +#: src/GMPlayListSource.cpp:163 +msgid "Export…" +msgstr "Export…" + +#: src/GMDatabaseSource.cpp:540 +msgid "Information…\t\tLibrary Statistics" +msgstr "Informace...\t\tStatistiky o knihovně" + +#: src/GMDatabaseSource.cpp:542 +msgid "Remove All Tracks\t\tRemove all tracks from the library" +msgstr "Odstranit všechny skladby\t\tOdstraní všechny skladby z knihovny" + +#: src/GMDatabaseSource.cpp:1272 +msgid "Edit Track Information" +msgstr "Upravit informaci o skladbě" + +#: src/GMDatabaseSource.cpp:1275 +#: src/GMPlayListSource.cpp:409 +#: src/GMStreamSource.cpp:205 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:128 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:143 +msgid "&Save" +msgstr "&Uložit" + +#: src/GMDatabaseSource.cpp:1315 +msgid "&Tag" +msgstr "&Tag" + +#: src/GMDatabaseSource.cpp:1320 +msgid "&Properties" +msgstr "&Vlastnosti" + +#: src/GMDatabaseSource.cpp:1325 +#: src/GMImportDialog.cpp:525 +msgid "Filename" +msgstr "Jméno souboru" + +#: src/GMDatabaseSource.cpp:1329 +#: ../../../fox-1.6.37/src/FXFileList.cpp:193 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:728 +msgid "Type" +msgstr "Typ" + +#: src/GMDatabaseSource.cpp:1333 +#: ../../../fox-1.6.37/src/FXFileList.cpp:194 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:729 +msgid "Size" +msgstr "Velikost" + +#: src/GMDatabaseSource.cpp:1341 +#: src/GMStreamSource.cpp:65 +msgid "Bitrate" +msgstr "Bitrate" + +#: src/GMDatabaseSource.cpp:1345 +msgid "Samplerate" +msgstr "Samplerate" + +#: src/GMDatabaseSource.cpp:1349 +msgid "Channels" +msgstr "Kanály" + +#: src/GMDatabaseSource.cpp:1358 +#: src/GMPreferencesDialog.cpp:539 +msgid "Track" +msgstr "Skladba" + +#: src/GMDatabaseSource.cpp:1403 +msgid "\tSeparate Artists" +msgstr "\tOddělení interpreti" + +#: src/GMDatabaseSource.cpp:1403 +msgid "\tShared Artists" +msgstr "\tSdílení interpreti" + +#: src/GMDatabaseSource.cpp:1416 +msgid "Auto track number. Offset:" +msgstr "Automatické číslování skladeb. Krok:" + +#: src/GMDatabaseSource.cpp:1425 +msgid "Update Tag in File" +msgstr "Aktualizovat značku v souboru" + +#: src/GMDatabaseSource.cpp:1430 +msgid "Update Filename" +msgstr "Aktualizovat jméno souboru" + +#: src/GMDatabaseSource.cpp:1431 +msgid "Set export template…" +msgstr "Nastavit šablonu pro export..." + +#: src/GMDatabaseSource.cpp:1645 +msgid "Update Tags?" +msgstr "Aktualizovat značky?" + +#: src/GMDatabaseSource.cpp:1645 +msgid "" +"No tracks were updated.\n" +"Would you still like to write the tags for the selected tracks?" +msgstr "" +"Skladby nebyly aktualizovány.\n" +"Chcete stále zapsat značky pro vybrané skladby?" + +#: src/GMDatabaseSource.cpp:1700 +msgid "Remove Audio Files?" +msgstr "Odstranit audio soubory?" + +#: src/GMDatabaseSource.cpp:1701 +msgid "Remove Audio Files..." +msgstr "Odstranit audio soubory..." + +#: src/GMDatabaseSource.cpp:1701 +msgid "The following audio files are going to be removed" +msgstr "Následující audio soubory budou odstraněny" + +#: src/GMDatabaseSource.cpp:1703 +#: src/GMDatabaseSource.cpp:1877 +#: src/GMPlayListSource.cpp:280 +#: src/GMStreamSource.cpp:270 +msgid "&Remove" +msgstr "&Odstranit" + +#: src/GMDatabaseSource.cpp:1729 +msgid "Export Main Library" +msgstr "Exportovat hlavní knihovnu" + +#: src/GMDatabaseSource.cpp:1731 +msgid "Export Play List" +msgstr "Exportovat seznam skladeb" + +#: src/GMDatabaseSource.cpp:1744 +msgid "Overwrite File?" +msgstr "Přepsat soubor" + +#: src/GMDatabaseSource.cpp:1744 +msgid "File already exists. Would you like to overwrite it?" +msgstr "Soubor už existuje. Chcete ho přepsat?" + +#: src/GMDatabaseSource.cpp:1784 +msgid "Export Genre" +msgstr "Exportovat žánr" + +#: src/GMDatabaseSource.cpp:1785 +msgid "Export tracks with genre to destination directory." +msgstr "Exportovat skladby žánru do daného adresáře." + +#: src/GMDatabaseSource.cpp:1787 +msgid "Export Artists" +msgstr "Exportovat interprety" + +#: src/GMDatabaseSource.cpp:1788 +msgid "Export tracks from artist to destination directory." +msgstr "Exportovat skladby interpreta do daného adresáře." + +#: src/GMDatabaseSource.cpp:1790 +msgid "Export Albums" +msgstr "Exportovat alba" + +#: src/GMDatabaseSource.cpp:1791 +msgid "Export tracks from album to destination directory." +msgstr "Exportovat skladby z alba do daného adresáře." + +#: src/GMDatabaseSource.cpp:1793 +msgid "Export Tracks" +msgstr "Exportovat skladby" + +#: src/GMDatabaseSource.cpp:1794 +msgid "Export tracks to destination directory." +msgstr "Exportovat skladby do daného adresáře." + +#: src/GMDatabaseSource.cpp:1803 +msgid "&Export" +msgstr "&Export" + +#: src/GMDatabaseSource.cpp:1853 +#: src/GMPlayListSource.cpp:261 +msgid "Remove Genre?" +msgstr "Odstranit žánr?" + +#: src/GMDatabaseSource.cpp:1854 +msgid "Remove tracks with genre from library?" +msgstr "Odstranit skladby žánru z knihovny?" + +#: src/GMDatabaseSource.cpp:1858 +#: src/GMPlayListSource.cpp:264 +msgid "Remove Artist?" +msgstr "Odstranit interpreta?" + +#: src/GMDatabaseSource.cpp:1859 +msgid "Remove tracks from artist from library?" +msgstr "Odstranit skladby interpreta z knihovny?" + +#: src/GMDatabaseSource.cpp:1863 +#: src/GMPlayListSource.cpp:267 +msgid "Remove Album?" +msgstr "Odstranit album?" + +#: src/GMDatabaseSource.cpp:1864 +msgid "Remove tracks from album from library?" +msgstr "Odstranit skladby z alba z knihovny?" + +#: src/GMDatabaseSource.cpp:1868 +#: src/GMPlayListSource.cpp:270 +msgid "Remove Track(s)?" +msgstr "Odstranit skladbu(y)?" + +#: src/GMDatabaseSource.cpp:1869 +msgid "Remove track(s) from library?" +msgstr "Odstranit skladbu(y) z knihovny?" + +#: src/GMDatabaseSource.cpp:1881 +#: src/GMPlayListSource.cpp:285 +msgid "Remove tracks from disk" +msgstr "Odstranit skladby z disku" + +#: src/GMDatabaseSource.cpp:1895 +#: src/GMDatabaseSource.cpp:1899 +#: src/GMDatabaseSource.cpp:1903 +#: src/GMDatabaseSource.cpp:1907 +#: src/GMPlayListSource.cpp:308 +#: src/GMStreamSource.cpp:280 +msgid "Library Error" +msgstr "Chyba knihovny" + +#: src/GMDatabaseSource.cpp:1895 +msgid "Unable to remove genre from the library" +msgstr "Nemohu odstranit žánr z knihovny" + +#: src/GMDatabaseSource.cpp:1899 +msgid "Unable to remove artist from the library" +msgstr "Nemohu odstranit interpreta z knihovny" + +#: src/GMDatabaseSource.cpp:1903 +msgid "Unable to remove album from the library" +msgstr "Nemohu odstranit album z knihovny" + +#: src/GMDatabaseSource.cpp:1907 +#: src/GMPlayListSource.cpp:308 +msgid "Unable to remove track from the library." +msgstr "Nemohu odstranit skladbu z knihovny." + +#: src/GMDatabaseSource.cpp:2117 +#: src/GMDatabaseSource.cpp:2118 +msgid "Create Playlist" +msgstr "Vytvořit seznam skladeb" + +#: src/GMDatabaseSource.cpp:2118 +msgid "Specify name of the new playlist" +msgstr "Zadejte jméno nového seznamu skladeb" + +#: src/GMDatabaseSource.cpp:2120 +msgid "&Create" +msgstr "&Vytvořit" + +#: src/GMDatabaseSource.cpp:2125 +#: src/GMPlayListSource.cpp:415 +#: ../../../fox-1.6.37/src/FXFileList.cpp:192 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:727 +msgid "Name" +msgstr "Jméno" + +#: src/GMDatabaseSource.cpp:2127 +msgid "New Playlist" +msgstr "Nový seznam skladeb" + +#: src/GMDatabaseSource.cpp:2178 +#: src/GMDatabaseSource.cpp:2179 +msgid "Clear Music Library?" +msgstr "Vyčistit knihovnu?" + +#: src/GMDatabaseSource.cpp:2179 +msgid "Remove all tracks from the music library?" +msgstr "Odstranit všechny skladby z knihovny?" + +#: src/GMDatabaseSource.cpp:2181 +msgid "&Remove All" +msgstr "Odstranit &vše" + +#: src/GMDatabaseSource.cpp:2185 +msgid "Keep play lists" +msgstr "Ponechat seznamy skladeb" + +#: src/GMDatabaseSource.cpp:2212 +#: src/GMDatabaseSource.cpp:2213 +msgid "Music Library Information" +msgstr "Informace o knihovně" + +#: src/GMDatabaseSource.cpp:2213 +msgid "You music collection consists of…" +msgstr "Kolekce hudby se skládá z..." + +#: src/GMDatabaseSource.cpp:2221 +msgid "Tracks:" +msgstr "Skladby:" + +#: src/GMDatabaseSource.cpp:2226 +msgid "Artists:" +msgstr "Interpreti:" + +#: src/GMDatabaseSource.cpp:2230 +msgid "Albums:" +msgstr "Alba:" + +#: src/GMDatabaseSource.cpp:2234 +msgid "Total Time:" +msgstr "Celková délka:" + +#: src/GMEQDialog.cpp:96 +msgid "Equalizer" +msgstr "Ekvalizér" + +#: src/GMEQDialog.cpp:135 +msgid "Equalizer:" +msgstr "Ekvalizér:" + +#: src/GMEQDialog.cpp:137 +msgid "\tSave" +msgstr "\tUložit" + +#: src/GMEQDialog.cpp:138 +msgid "\tReset" +msgstr "\tResetovat" + +#: src/GMEQDialog.cpp:139 +msgid "\tRemove" +msgstr "\tOdstranit" + +#: src/GMEQDialog.cpp:172 +msgid "Pre-amp" +msgstr "Pre-amp" + +#: src/GMEQDialog.cpp:244 +#: src/GMEQDialog.cpp:254 +msgid "Disabled" +msgstr "Blokovaný" + +#: src/GMEQDialog.cpp:247 +#: src/GMEQDialog.cpp:299 +msgid "Manual" +msgstr "Ruční" + +#: src/GMEQDialog.cpp:314 +msgid "Delete Preset" +msgstr "Smazat nastavení" + +#: src/GMEQDialog.cpp:314 +#, c-format +msgid "Are you sure you want to delete %s preset?" +msgstr "Jste si jistý smazáním nastavení %s?" + +#: src/GMEQDialog.cpp:334 +msgid "Preset Name" +msgstr "Jméno nastavení" + +#: src/GMEQDialog.cpp:334 +msgid "Please enter preset name:" +msgstr "Vložte jméno nastavení:" + +#: src/GMEQDialog.cpp:338 +msgid "Overwrite Preset" +msgstr "Přepsat nastavení" + +#: src/GMEQDialog.cpp:338 +#, c-format +msgid "Preset %s already exists. Would you like to overwrite it?" +msgstr "Nastavení %s už existuje. Chcete ho přepsat?" + +#: src/GMFontDialog.cpp:209 +msgid "Ultra Condensed" +msgstr "Ultra zhuštěné" + +#: src/GMFontDialog.cpp:210 +msgid "Extra Condensed" +msgstr "Extra zhuštěné" + +#: src/GMFontDialog.cpp:211 +msgid "Condensed" +msgstr "Úzké" + +#: src/GMFontDialog.cpp:212 +msgid "Semi Condensed" +msgstr "Polo zhuštěné" + +#: src/GMFontDialog.cpp:214 +msgid "Semi Expanded" +msgstr "Polo roztažené" + +#: src/GMFontDialog.cpp:215 +msgid "Expanded" +msgstr "Roztažené" + +#: src/GMFontDialog.cpp:216 +msgid "Extra Expanded" +msgstr "Extra roztažené" + +#: src/GMFontDialog.cpp:217 +msgid "Ultra Expanded" +msgstr "Ultra roztažené" + +#: src/GMFontDialog.cpp:224 +#: src/GMPreferencesDialog.cpp:1223 +msgid "Thin" +msgstr "Tenké" + +#: src/GMFontDialog.cpp:225 +#: src/GMPreferencesDialog.cpp:1224 +msgid "Extra Light" +msgstr "Extra nepatrné" + +#: src/GMFontDialog.cpp:226 +#: src/GMPreferencesDialog.cpp:1225 +msgid "Light" +msgstr "Nepatrné" + +#: src/GMFontDialog.cpp:228 +#: src/GMPreferencesDialog.cpp:1227 +msgid "Medium" +msgstr "Střední" + +#: src/GMFontDialog.cpp:229 +#: src/GMPreferencesDialog.cpp:1228 +msgid "Demibold" +msgstr "Polotučné" + +#: src/GMFontDialog.cpp:230 +#: src/GMPreferencesDialog.cpp:1229 +msgid "Bold" +msgstr "Tučné" + +#: src/GMFontDialog.cpp:231 +#: src/GMPreferencesDialog.cpp:1230 +msgid "Extra Bold" +msgstr "Extra tučné" + +#: src/GMFontDialog.cpp:232 +#: src/GMPreferencesDialog.cpp:1231 +msgid "Heavy" +msgstr "Husté" + +#: src/GMFontDialog.cpp:239 +msgid "Reverse Oblique" +msgstr "Obráceně šikmé" + +#: src/GMFontDialog.cpp:240 +msgid "Reverse Italic" +msgstr "Obrácená kurzíva" + +#: src/GMFontDialog.cpp:242 +msgid "Italic" +msgstr "Kurzíva" + +#: src/GMFontDialog.cpp:243 +msgid "Oblique" +msgstr "Šikmé" + +#: src/GMFontDialog.cpp:265 +msgid "Normal" +msgstr "Normální" + +#: src/GMImportDialog.cpp:91 +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:137 +msgid "&Directory:" +msgstr "&Adresář" + +#: src/GMImportDialog.cpp:166 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:196 +msgid "&File Name:" +msgstr "&Jméno souboru:" + +#: src/GMImportDialog.cpp:168 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:102 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:106 +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:134 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:198 +msgid "&OK" +msgstr "&OK" + +#: src/GMImportDialog.cpp:170 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:200 +msgid "File F&ilter:" +msgstr "F&iltr souborů:" + +#: src/GMImportDialog.cpp:174 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:204 +msgid "Read Only" +msgstr "Jen pro čtení" + +#: src/GMImportDialog.cpp:179 +#: src/GMSearch.cpp:565 +#: src/GMSearch.cpp:647 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:208 +msgid "Directory:" +msgstr "Adresář:" + +#: src/GMImportDialog.cpp:186 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:228 +msgid "&Set bookmark\t\tBookmark current directory." +msgstr "&Nastavit záložky\t\tZazáložkuje aktuální adresář." + +#: src/GMImportDialog.cpp:187 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:229 +msgid "&Clear bookmarks\t\tClear bookmarks." +msgstr "&Vyčistit záložky\t\tVyčistí záložky." + +#: src/GMImportDialog.cpp:203 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:244 +msgid "\tGo up one directory\tMove up to higher directory." +msgstr "\tJít o adresář výše\tPřesune do adresáře výše." + +#: src/GMImportDialog.cpp:204 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:245 +msgid "\tGo to home directory\tBack to home directory." +msgstr "\tJít do domovského adresáře\tZpátky do domovského adresáře." + +#: src/GMImportDialog.cpp:205 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:246 +msgid "\tGo to work directory\tBack to working directory." +msgstr "\tJít do pracovního adresáře\tZpátky do pracovního adresáře." + +#: src/GMImportDialog.cpp:206 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:247 +msgid "\tBookmarks\tVisit bookmarked directories." +msgstr "\tZáložky\tNavštíví zazáložkované adresáře." + +#: src/GMImportDialog.cpp:209 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:250 +msgid "\tCreate new directory\tCreate new directory." +msgstr "\tVytvořit nový adresář\tVytvoří nový adresář." + +#: src/GMImportDialog.cpp:210 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:251 +msgid "\tShow list\tDisplay directory with small icons." +msgstr "\tZobrazit seznam\tZobrazí adresář s malými ikonami." + +#: src/GMImportDialog.cpp:211 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:252 +msgid "\tShow icons\tDisplay directory with big icons." +msgstr "\tZobrazit ikony\tZobrazí adresář s velkými ikonami." + +#: src/GMImportDialog.cpp:212 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:253 +msgid "\tShow details\tDisplay detailed directory listing." +msgstr "\tZobrazit detaily\tZobrazí detailní seznam adresářů." + +#: src/GMImportDialog.cpp:213 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:254 +msgid "\tShow hidden files\tShow hidden files and directories." +msgstr "\tZobrazit skryté soubory\tZobrazí skryté soubory a adresáře." + +#: src/GMImportDialog.cpp:213 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:254 +msgid "\tHide Hidden Files\tHide hidden files and directories." +msgstr "\tSkrýt skryté soubory\tSkryje skryté soubory a adresáře." + +#: src/GMImportDialog.cpp:412 +msgid "Synchronize Folder" +msgstr "Synchronizovat složku" + +#: src/GMImportDialog.cpp:414 +msgid "Import Playlist" +msgstr "Načíst seznam skladeb" + +#: src/GMImportDialog.cpp:416 +msgid "Import Music" +msgstr "Importovat hudbu" + +#: src/GMImportDialog.cpp:448 +msgid "&File(s)" +msgstr "&Soubor(y)" + +#: src/GMImportDialog.cpp:475 +msgid "&Directory" +msgstr "&Adresář" + +#: src/GMImportDialog.cpp:478 +msgid "Exclude Filter\tFilter out directories and/or files based on pattern" +msgstr "Filtr pro vynechání\tVynechá adresáře a/nebo soubory podle šablony" + +#: src/GMImportDialog.cpp:481 +msgid "Folders:" +msgstr "Adresáře:" + +#: src/GMImportDialog.cpp:483 +msgid "Files:" +msgstr "Soubory:" + +#: src/GMImportDialog.cpp:499 +#: src/GMImportDialog.cpp:560 +msgid "&Sync" +msgstr "&Synchronizovat" + +#: src/GMImportDialog.cpp:501 +msgid "Sync Operation" +msgstr "Synchronizace" + +#: src/GMImportDialog.cpp:504 +msgid "Import new tracks\tImports files not yet in the database." +msgstr "Importovat nové skladby\tImportuje soubory, které nejsou v databázi." + +#: src/GMImportDialog.cpp:505 +msgid "Remove tracks that have been deleted from disk" +msgstr "Odstranit skladby, které byly smazány z disku" + +#: src/GMImportDialog.cpp:507 +msgid "Update existing tracks:" +msgstr "Aktualizovat existující skladby:" + +#: src/GMImportDialog.cpp:508 +msgid "Modified since last import\tOnly reread the tag when the file has been modified." +msgstr "Upravené od posledního importu\tJenom znovu načte značky, jestliže byl soubor změněn." + +#: src/GMImportDialog.cpp:510 +msgid "All\tAlways read the tags" +msgstr "Vše\tVždy číst všechny značky" + +#: src/GMImportDialog.cpp:511 +msgid "Remove tracks found in folder from database" +msgstr "Odstranit skladby nalezené ve složce z databáze" + +#: src/GMImportDialog.cpp:514 +msgid "&Track" +msgstr "&Skladba" + +#: src/GMImportDialog.cpp:517 +msgid "Parse Settings" +msgstr "Nastavení analýzy" + +#: src/GMImportDialog.cpp:521 +msgid "Parse info from:" +msgstr "Analyzovat z:" + +#: src/GMImportDialog.cpp:524 +msgid "Tag" +msgstr "Značka" + +#: src/GMImportDialog.cpp:526 +msgid "Both" +msgstr "Oba" + +#: src/GMImportDialog.cpp:528 +msgid "Default value:" +msgstr "Výchozí hodnota:" + +#: src/GMImportDialog.cpp:532 +msgid "Set track number based on scan order." +msgstr "Nastavit číslo skladby založené na pořadí skenování." + +#: src/GMImportDialog.cpp:537 +msgid "" +"%T - title %A - album name\n" +"%P - album artist name %p - track artist name \n" +"%N - track number %G - genre" +msgstr "" +"%T - název %A - jméno alba\n" +"%P - jméno interpreta alba\n" +"%p - jméno interpreta skladby\n" +"%N - číslo skladby %G - žánr" + +#: src/GMImportDialog.cpp:549 +msgid "Replace underscores with spaces" +msgstr "Nahradit podtržení mezerami" + +#: src/GMImportDialog.cpp:562 +msgid "&Import" +msgstr "&Import" + +#: src/GMPlayer.cpp:212 +msgid "Unable to initialize audio driver." +msgstr "Nemohu inicializovat audio ovladač." + +#: src/GMPlayer.cpp:714 +msgid "Unknown host." +msgstr "Neznámý hostitel." + +#: src/GMPlayer.cpp:715 +msgid "Unknown device" +msgstr "Neznámé zařízení" + +#: src/GMPlayer.cpp:716 +msgid "Network not reachable." +msgstr "Síť není dostupná." + +#: src/GMPlayer.cpp:717 +msgid "Audio output unavailable." +msgstr "Audio výstup nedostupný." + +#: src/GMPlayer.cpp:718 +msgid "Connection Refused." +msgstr "Připojení odmítnuto." + +#: src/GMPlayer.cpp:719 +msgid "File not found." +msgstr "Soubor nenalezen." + +#: src/GMPlayer.cpp:720 +msgid "Resource not accessible. Check permissions" +msgstr "Zdroje nejsou dostupné. Zkontrolujte práva" + +#: src/GMPlayer.cpp:721 +msgid "Read Error" +msgstr "Chyba čtení" + +#: src/GMPlayer.cpp:722 +msgid "Error while loading library/plugin" +msgstr "Chyba při načtení knihovny/zásuvného modulu" + +#: src/GMPlayer.cpp:723 +msgid "Warning" +msgstr "Varování" + +#: src/GMPlayer.cpp:724 +msgid "Security Warning" +msgstr "Bezpečnostní varování" + +#: src/GMPlayer.cpp:725 +msgid "Unknown Error" +msgstr "Neznámá chyba" + +#: src/GMPlayer.cpp:761 +msgid "Error" +msgstr "Chyba" + +#: src/GMPlayerManager.cpp:439 +#, c-format +msgid "Unable to create directory %s\n" +msgstr "Nemohu vytvořit adresář %s\n" + +#: src/GMPlayerManager.cpp:612 +msgid "" +"For some reason the FOX library was compiled without PNG support.\n" +"In order to show all icons, Goggles Music Manager requires PNG\n" +"support in the FOX library. If you've compiled FOX yourself, most\n" +"likely the libpng header files were not installed on your system." +msgstr "" +"Z nějakého důvodu je FOX knihovna byla zkompilována bez podpory PNG.\n" +"Pro zobrazení ikon, Goggles Music Manager potřebuje podporu\n" +"PNG ve FOX knihovně. Při vlastní kompilaci FOX, toto\n" +"nastane, když nejsou nainstalovány hlavičkové soubory libpng v systému." + +#: src/GMPlayerManager.cpp:623 +msgid "Session bus not available. All features requiring dbus are disabled." +msgstr "Session bus není dostupný. Všechny funkce vyžadující dbus jsou zakázané." + +#: src/GMPlayerManager.cpp:633 +msgid "A DBus error occurred. All features requiring sessionbus are disabled." +msgstr "Nastala chyba DBus. Všechny funkce vyžadující sessionbus jsou zakázané." + +#: src/GMPlayerManager.cpp:644 +msgid "Session Bus not available. All features requiring sessionbus are disabled." +msgstr "Session bus není dostupný. Všechny funkce vyžadující sessionbus jsou zakázané." + +#: src/GMPlayerManager.cpp:719 +#: src/GMPreferencesDialog.cpp:594 +msgid "Audio Device Error" +msgstr "Chyba audio zařízení" + +#: src/GMPlayerManager.cpp:1551 +msgid "Last.FM Error" +msgstr "Chyba Last.FM" + +#: src/GMPlayerManager.cpp:1558 +msgid "Playback Error" +msgstr "Chyba přehrávání" + +#: src/GMPlayerManager.cpp:1708 +#, c-format +msgid "" +"%s\n" +"%s (%d)" +msgstr "" +"%s\n" +"%s (%d)" + +#: src/GMPlayListSource.cpp:99 +#: src/GMPlayListSource.cpp:105 +#: src/GMPlayListSource.cpp:111 +#: src/GMPlayListSource.cpp:121 +msgid "Remove…\tDel\tRemove track(s) from play list." +msgstr "Odstranit...\tDel\tOdstraní skladbu(y) ze seznamu skladeb." + +#: src/GMPlayListSource.cpp:161 +msgid "Edit…" +msgstr "Upravit..." + +#: src/GMPlayListSource.cpp:162 +msgid "Import…" +msgstr "Import…" + +#: src/GMPlayListSource.cpp:164 +msgid "Remove Playlist" +msgstr "Odstranit seznam skladeb" + +#: src/GMPlayListSource.cpp:262 +msgid "Remove tracks with genre from play list?" +msgstr "Odstranit skladby žánru ze seznamu skladeb?" + +#: src/GMPlayListSource.cpp:265 +msgid "Remove tracks from artist from play list?" +msgstr "Odstranit skladby interpreta ze seznamu skladeb?" + +#: src/GMPlayListSource.cpp:268 +msgid "Remove tracks from album from play list?" +msgstr "Odstranit skladby z alba ze seznamu skladeb?" + +#: src/GMPlayListSource.cpp:271 +msgid "Remove track(s) from play list?" +msgstr "Odstranit skladbu(y) ze seznamu skladeb?" + +#: src/GMPlayListSource.cpp:284 +msgid "Remove tracks from music library" +msgstr "Odstranit skladby z knihovny" + +#: src/GMPlayListSource.cpp:406 +#: src/GMPlayListSource.cpp:407 +msgid "Edit Playlist" +msgstr "Upravit seznam skladeb" + +#: src/GMPlayListSource.cpp:407 +msgid "Change playlist name" +msgstr "Změnit jméno seznamu skladeb" + +#: src/GMPlayListSource.cpp:432 +msgid "Delete Play List?" +msgstr "Smazat seznam skladeb?" + +#: src/GMPlayListSource.cpp:432 +msgid "Are you sure you want to delete the playlist?" +msgstr "Jste si jistý, že chcete smazat seznam skladeb?" + +#: src/GMPlayListSource.cpp:432 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:111 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:116 +msgid "&Yes" +msgstr "&Ano" + +#: src/GMPlayListSource.cpp:432 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:112 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:117 +msgid "&No" +msgstr "&Ne" + +#: src/GMPreferencesDialog.cpp:252 +msgid "Preferences" +msgstr "Volby" + +#: src/GMPreferencesDialog.cpp:286 +msgid "&General" +msgstr "&Obecné" + +#: src/GMPreferencesDialog.cpp:289 +msgid "Sort Options" +msgstr "Volby seřazení" + +#: src/GMPreferencesDialog.cpp:293 +msgid "Ignore leading words" +msgstr "Ignorovat první slova" + +#: src/GMPreferencesDialog.cpp:296 +msgid "Album Covers" +msgstr "Obaly alb" + +#: src/GMPreferencesDialog.cpp:299 +msgid "Show album cover of playing track\tShow album cover of playing track" +msgstr "Zobrazit obal přehrávané skladby\tZobrazí obal přehrávané skladby" + +#: src/GMPreferencesDialog.cpp:300 +msgid "Show album covers in album browser\tShow album covers in album browser" +msgstr "Zobrazit obaly v prohlížeči alb\tZobrazí obaly v prohlížeči alb" + +#: src/GMPreferencesDialog.cpp:302 +msgid "System Tray" +msgstr "System Tray" + +#: src/GMPreferencesDialog.cpp:304 +msgid "Show Tray Icon\tShow tray icon in the system tray." +msgstr "Zobrazit tray ikonu\tZobrazí tray ikonu v system tray." + +#: src/GMPreferencesDialog.cpp:307 +msgid "Show Track Change Notifications\tInform notification daemon of track changes." +msgstr "Zobrazit upozornění na změnu skladby\tInformuje upozorňovacího démona na změnu skaldby." + +#: src/GMPreferencesDialog.cpp:311 +msgid "Last.fm" +msgstr "Last.fm" + +#: src/GMPreferencesDialog.cpp:315 +msgid "" +"This version of Goggles Music Manager is\n" +"not supported by Last-FM. Please upgrade\n" +"to a newer version of GMM." +msgstr "" +"Tato verze Goggles Music Manager\n" +"nepodporuje Last-FM. Prosím upgradujte\n" +"na novější verzi GMM." + +#: src/GMPreferencesDialog.cpp:321 +msgid "Service:" +msgstr "Služba:" + +#: src/GMPreferencesDialog.cpp:338 +msgid "&Sign up…" +msgstr "&Přihlásit se …" + +#: src/GMPreferencesDialog.cpp:344 +msgid "Username:" +msgstr "Uživatelské jméno:" + +#: src/GMPreferencesDialog.cpp:346 +msgid "Password:" +msgstr "Heslo:" + +#: src/GMPreferencesDialog.cpp:349 +msgid "Scrobble" +msgstr "Scrobble" + +#: src/GMPreferencesDialog.cpp:359 +msgid "&Window" +msgstr "&Okno" + +#: src/GMPreferencesDialog.cpp:362 +msgid "Window" +msgstr "Okno" + +#: src/GMPreferencesDialog.cpp:365 +msgid "Close button minimizes to tray" +msgstr "Tlačítko zavřít minimalizuje do tray" + +#: src/GMPreferencesDialog.cpp:366 +msgid "Show Status Bar" +msgstr "Zobrazit stavový řádek" + +#: src/GMPreferencesDialog.cpp:368 +msgid "Show Icons in Track Browser" +msgstr "Zobrazit ikony v prohlížeči skladeb" + +#: src/GMPreferencesDialog.cpp:370 +msgid "Display playing track in title bar" +msgstr "Zobrazit přehrávanou skladbu v titulkovém pruhu" + +#: src/GMPreferencesDialog.cpp:372 +msgid "Player Controls" +msgstr "Ovládání přehrávače" + +#: src/GMPreferencesDialog.cpp:376 +msgid "Location:" +msgstr "Umístění:" + +#: src/GMPreferencesDialog.cpp:378 +msgid "Top" +msgstr "Nahoře" + +#: src/GMPreferencesDialog.cpp:379 +msgid "Bottom" +msgstr "Dole" + +#: src/GMPreferencesDialog.cpp:387 +msgid "Title Format:" +msgstr "Formát názvu:" + +#: src/GMPreferencesDialog.cpp:391 +msgid "Style:" +msgstr "Styl:" + +#: src/GMPreferencesDialog.cpp:392 +msgid "Show Labels" +msgstr "Zobrazit popisy" + +#: src/GMPreferencesDialog.cpp:395 +msgid "Large Icons" +msgstr "Velké ikony" + +#: src/GMPreferencesDialog.cpp:399 +msgid "A&ppearance" +msgstr "V&zhled" + +#: src/GMPreferencesDialog.cpp:402 +msgid "Colors" +msgstr "Barvy" + +#: src/GMPreferencesDialog.cpp:409 +msgid "fg\tForeground Color" +msgstr "fg\tBarva textu" + +#: src/GMPreferencesDialog.cpp:410 +msgid "bg\tBackground Color" +msgstr "bg\tBarva pozadí " + +#: src/GMPreferencesDialog.cpp:411 +msgid "alt bg\tAlternative Background Color" +msgstr "alt bg\tAlternativní barva pozadí" + +#: src/GMPreferencesDialog.cpp:422 +msgid "Normal\tNormal Text Color" +msgstr "Normální\tBarva normálního textu" + +#: src/GMPreferencesDialog.cpp:426 +msgid "Base\tBase Color" +msgstr "Základní\tZákladní barva" + +#: src/GMPreferencesDialog.cpp:429 +msgid "Selected\tSelected Text Color" +msgstr "Vybraná\tBarva vybraného textu" + +#: src/GMPreferencesDialog.cpp:433 +msgid "Menu\tMenu Base Color" +msgstr "Menu\tZákladní barva menu" + +#: src/GMPreferencesDialog.cpp:437 +msgid "Menu\tMenu Text Color" +msgstr "Menu\tBarva menu" + +#: src/GMPreferencesDialog.cpp:441 +msgid "Border\tBorder Color" +msgstr "Okraj\tBarva okraje" + +#: src/GMPreferencesDialog.cpp:445 +msgid "Tooltip\tTooltip Color" +msgstr "Tooltip\tBarva tooltip" + +#: src/GMPreferencesDialog.cpp:449 +msgid "Hilite\tHilite Color" +msgstr "Zvýrazněné\tBarva zvýraznění" + +#: src/GMPreferencesDialog.cpp:456 +msgid "Shadow\tShadow Color" +msgstr "Stín\tBarva stínu" + +#: src/GMPreferencesDialog.cpp:459 +msgid "Playing\tPlaying Track Color" +msgstr "Přehrávané\tBarva přehrávané skladby" + +#: src/GMPreferencesDialog.cpp:463 +msgid "Tray\tTray Background Color" +msgstr "Tray\tBarva pozadí tray" + +#: src/GMPreferencesDialog.cpp:473 +msgid "Presets:" +msgstr "Přednastaveno:" + +#: src/GMPreferencesDialog.cpp:481 +msgid "Font & Icons" +msgstr "Font & Ikony" + +#: src/GMPreferencesDialog.cpp:487 +msgid "Default Font" +msgstr "Výchozí font" + +#: src/GMPreferencesDialog.cpp:489 +msgid "Change…" +msgstr "Změnit..." + +#: src/GMPreferencesDialog.cpp:490 +msgid "Icons" +msgstr "Ikony" + +#: src/GMPreferencesDialog.cpp:509 +msgid "&Audio" +msgstr "&Audio" + +#: src/GMPreferencesDialog.cpp:512 +msgid "Engine" +msgstr "Engine" + +#: src/GMPreferencesDialog.cpp:516 +msgid "Audio Driver:" +msgstr "Audio ovladač:" + +#: src/GMPreferencesDialog.cpp:527 +msgid "Close audio device on pause." +msgstr "Zavřít audio zařízení při pozastavení." + +#: src/GMPreferencesDialog.cpp:528 +msgid "Turn off playback engine on stop." +msgstr "Vypnout engine přehrávání při zastavení." + +#: src/GMPreferencesDialog.cpp:529 +msgid "Turn on playback engine on startup.\tFor faster startup, playback engine is normally started when first track is played.\tFor faster startup, playback engine is normally started when first track is played." +msgstr "Zapnout engine přehrávání při startu.\tPro rychlejší spuštění, engine přehrávání se normálně při přehrávání první skaldby.\tPro rychlejší spuštění, engine přehrávání se normálně při přehrávání první skaldby." + +#: src/GMPreferencesDialog.cpp:532 +msgid "Playback" +msgstr "Přehrávání" + +#: src/GMPreferencesDialog.cpp:536 +msgid "Replay Gain:" +msgstr "Zisk přehrávání:" + +#: src/GMPreferencesDialog.cpp:538 +msgid "Off" +msgstr "Vypnout" + +#: src/GMPreferencesDialog.cpp:543 +msgid "Gapless playback" +msgstr "Přehrávání bez mezer" + +#: src/GMPreferencesDialog.cpp:544 +msgid "Volume Normalization" +msgstr "Normalizace hlasitosti" + +#: src/GMPreferencesDialog.cpp:594 +#, c-format +msgid "Failed to open requested audio driver: %s" +msgstr "Nemohu otevřít požadovaný audio ovladač: %s" + +#: src/GMPreferencesDialog.cpp:678 +msgid "Current" +msgstr "Aktuální" + +#: src/GMPreferencesDialog.cpp:696 +msgid "Custom" +msgstr "Uživatelský" + +#: src/GMPreferencesDialog.cpp:769 +#: src/GMPreferencesDialog.cpp:774 +#: src/GMWindow.cpp:1133 +#: src/GMWindow.cpp:1140 +#: src/GMWindow.cpp:1147 +#: src/GMWindow.cpp:1154 +msgid "Unable to launch webbrowser" +msgstr "Nemohu spustit webový prohlížeč" + +#: src/GMPreferencesDialog.cpp:1172 +msgid "Select Normal Font" +msgstr "Vybrat font" + +#: src/GMRemote.cpp:295 +msgid "&Next" +msgstr "&Následující" + +#: src/GMRemote.cpp:296 +msgid "P&revious" +msgstr "Pře&dchozí" + +#: src/GMRemote.cpp:297 +#: src/GMWindow.cpp:1197 +msgid "&Play" +msgstr "&Přehrát" + +#: src/GMRemote.cpp:298 +msgid "&Stop" +msgstr "&Zastavit" + +#: src/GMRemote.cpp:300 +msgid "Show Browser" +msgstr "Zobrazit prohlížeč" + +#: src/GMRemote.cpp:301 +#: src/GMTrayIcon.cpp:307 +msgid "Quit" +msgstr "Ukončit" + +#: src/GMRemote.cpp:385 +msgid "\tShow Browser\tShow Browser" +msgstr "\tZobrazit prohlížeč\tZobrazí prohlížeč" + +#: src/GMRemote.cpp:387 +msgid "\tStart Playback\tStart Playback" +msgstr "\tSpustit přehrávání\tSpustí přehrávání" + +#: src/GMRemote.cpp:388 +msgid "\tStop Playback\tStop Playback" +msgstr "\tZastavit přehrávání\tZastaví přehrávání" + +#: src/GMRemote.cpp:390 +msgid "\tPlay Previous Track\tPlay previous track." +msgstr "\tPřehrát předchozí skladbu\tPřehraje předchozí skladbu." + +#: src/GMRemote.cpp:391 +msgid "\tPlay Next Track\tPlay next track." +msgstr "\tPřehrát následující skladbu\tPřehraje následující skladbu." + +#: src/GMRemote.cpp:397 +#: src/GMWindow.cpp:222 +msgid "\tAdjust Volume\tAdjust Volume" +msgstr "\tNastavit hlasitost\tNastaví hlasitost" + +#: src/GMSearch.cpp:128 +msgid "Unable to open the database" +msgstr "Nemohu otevřít databázi" + +#: src/GMSearch.cpp:252 +msgid "Database Error: Unable to retrieve all filenames." +msgstr "Chyba databáze: Nemohu získat všechna jména souborů." + +#: src/GMSearch.cpp:280 +msgid "Unable to update track" +msgstr "Nemohu aktualizovat skladbu" + +#: src/GMSearch.cpp:439 +msgid "Unable to insert track into the database" +msgstr "Nemohu vložit skladbu do databáze" + +#: src/GMSearch.cpp:505 +#: src/GMTrackDatabase.cpp:205 +msgid "Fatal Error" +msgstr "Fatální chyba" + +#: src/GMSearch.cpp:505 +#, c-format +msgid "" +"%s\n" +"Please contact support if this error keeps occuring." +msgstr "" +"%s\n" +"Kontaktujte podporu, jestliže chyba přetrvává." + +#: src/GMSearch.cpp:534 +#: src/GMSearch.cpp:584 +msgid "Updating Database..." +msgstr "Aktualizace databáze..." + +#: src/GMSearch.cpp:537 +#: src/GMSearch.cpp:626 +msgid "Please wait. This may take a while." +msgstr "Prosím vyčkejte. Může to zabrat nějaký čas." + +#: src/GMSearch.cpp:555 +#: src/GMSearch.cpp:637 +msgid "New Tracks:" +msgstr "Nové skladby:" + +#: src/GMSearch.cpp:561 +#: src/GMSearch.cpp:643 +msgid "File:" +msgstr "Soubor:" + +#: src/GMSearch.cpp:594 +#: src/GMSearch.cpp:623 +msgid "Importing Files..." +msgstr "Import souborů..." + +#: src/GMSearch.cpp:652 +msgid "&Stop Import" +msgstr "&Zastavit import" + +#: src/GMSourceView.cpp:54 +msgid "Sources\tPress to change sorting order\tPress to change sorting order" +msgstr "Zdroje\tStisknout pro změnu řazení\tStisknutí změní řazení" + +#: src/GMSourceView.cpp:245 +#: src/GMWindow.cpp:261 +msgid "New Playlist…\t\tCreate a new playlist" +msgstr "Nový seznam skladeb...\t\tVytvoří nový seznam skladeb" + +#: src/GMSourceView.cpp:246 +#: src/GMWindow.cpp:262 +msgid "Import Playlist…\t\tImport existing playlist" +msgstr "Načíst seznam skladeb...\t\tNačte existující seznam skladeb" + +#: src/GMSourceView.cpp:247 +#: src/GMWindow.cpp:263 +msgid "New Radio Station…\t\tCreate a new playlist" +msgstr "Nová rádiová stanice...\t\tVytvoří novou stanici" + +#: src/GMStreamSource.cpp:64 +msgid "Station" +msgstr "Stanice" + +#: src/GMStreamSource.cpp:112 +#: src/GMStreamSource.cpp:118 +msgid "New Station…\t\t" +msgstr "Nová stanice…\t\t" + +#: src/GMStreamSource.cpp:117 +msgid "Edit…\t\t" +msgstr "Upravit...\t\t" + +#: src/GMStreamSource.cpp:119 +msgid "Remove\t\tRemove." +msgstr "Odstranit\t\tOdstraní." + +#: src/GMStreamSource.cpp:165 +#: src/GMStreamSource.cpp:166 +msgid "New Internet Radio Station" +msgstr "Nová internetová stanice" + +#: src/GMStreamSource.cpp:166 +msgid "Specify url and description of new station" +msgstr "Zadejte url a popis nové stanice" + +#: src/GMStreamSource.cpp:168 +msgid "C&reate" +msgstr "Vy&tvořit" + +#: src/GMStreamSource.cpp:173 +#: src/GMStreamSource.cpp:211 +msgid "Location" +msgstr "Adresa" + +#: src/GMStreamSource.cpp:175 +#: src/GMStreamSource.cpp:214 +msgid "Description" +msgstr "Popis" + +#: src/GMStreamSource.cpp:189 +msgid "Untitled" +msgstr "Bez názvu" + +#: src/GMStreamSource.cpp:202 +#: src/GMStreamSource.cpp:203 +msgid "Edit Internet Radio Station" +msgstr "Upravit internetovou stanici" + +#: src/GMStreamSource.cpp:203 +msgid "Update url and description of station" +msgstr "Aktualizace url a popisu stanice" + +#: src/GMStreamSource.cpp:265 +msgid "Remove Internet Radio Station(s)?" +msgstr "Odstranit internetovou rádiovou stanici(e)?" + +#: src/GMStreamSource.cpp:266 +msgid "Remove Internet Radio Station(s) from library?" +msgstr "Odstranit internetovou rádiovou stanici(e) z knihovny?" + +#: src/GMStreamSource.cpp:280 +#, c-format +msgid "Unable to remove station from the library." +msgstr "Nemohu odstranit stanici z knihovny." + +#: src/GMTrackDatabase.cpp:193 +msgid "" +"An incompatible (future) version of the database was found.\n" +"This usually happens when you try to downgrade to a older version of GMM\n" +"Press OK to continue and reset the database (all information will be lost!).\n" +"Press Cancel to quit now and leave the database as is." +msgstr "" +"Nekompatibilní (budoucí) verze databáze.\n" +"Obvykle nastane při snížení na starší verzi GMM\n" +"Stiskněte OK pro pokračování a vynulování databáze (všechny informace budou ztraceny!).\n" +"Stiskněte Zrušit pro ukončení a ponechání databáze jak je." + +#: src/GMTrackDatabase.cpp:205 +msgid "" +"Goggles Music Manager was unable to open the database.\n" +"The database may have been corrupted. Please remove ~/.goggles/goggles.db to try again.\n" +"if the error keeps occuring, please file an issue at http://code.google.com/p/gogglesmm" +msgstr "" +"Goggles Music Manager nemohl otevřít databázi.\n" +"Databáze může být poškozená. Odstraňte ~/.goggles/goggles.db a zkuste znovu.\n" +"Jestliže chyba přetrvává, reportujte na http://code.google.com/p/gogglesmm" + +#: src/GMTrackView.cpp:245 +msgid "\tClose Filter\tClose Filter" +msgstr "\tZavřít filtr\tZavře filtr" + +#: src/GMTrackView.cpp:246 +msgid "&Find" +msgstr "&Najít" + +#: src/GMTrackView.cpp:252 +msgid "Tags\tPress to change sorting order\tPress to change sorting order" +msgstr "Tagy\tStisknout pro změnu řazení\tStisknutí změní řazení" + +#: src/GMTrackView.cpp:256 +msgid "Artists\tPress to change sorting order\tPress to change sorting order" +msgstr "Interpreti\tStisknout pro změnu řazení\tStisknutí změní řazení" + +#: src/GMTrackView.cpp:260 +msgid "Albums\tPress to change sorting order\tPress to change sorting order" +msgstr "Alba\tStisknout pro změnu řazení\tStisknutí změní řazení" + +#: src/GMTrackView.cpp:282 +#: src/GMWindow.cpp:299 +msgid "&Configure Columns…" +msgstr "&Konfigurovat sloupce…" + +#: src/GMTrackView.cpp:286 +msgid "Browse" +msgstr "Prohlížeč" + +#: src/GMTrackView.cpp:287 +msgid "Shuffle\tCtrl-R" +msgstr "Náhodně\tCtrl-R" + +#: src/GMTrackView.cpp:294 +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:388 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:734 +msgid "Reverse" +msgstr "Obráceně" + +#: src/GMTrackView.cpp:804 +#, c-format +msgid "All %d Genres" +msgstr "Všechny %d žánry" + +#: src/GMTrackView.cpp:806 +msgid "All Genres" +msgstr "Všechny žánry" + +#: src/GMTrackView.cpp:832 +#, c-format +msgid "All %d Artists" +msgstr "Všichni %d interpreti" + +#: src/GMTrackView.cpp:872 +#, c-format +msgid "All %d Albums" +msgstr "Všechna %d alba" + +#: src/GMTrackView.cpp:1258 +#, c-format +msgid "By %s" +msgstr "Podle %s" + +#: src/GMTrackView.cpp:1586 +#: src/GMTrackView.cpp:1609 +msgid "Sort by Album Year" +msgstr "Seřadit podle roku vydání alba" + +#: src/GMTrackView.cpp:1637 +msgid "&Columns\t\tChange Visible Columns." +msgstr "&Sloupce\t\tZmění viditelné sloupce." + +#: src/GMTrackView.cpp:1638 +msgid "&Sort\t\tChange Sorting." +msgstr "&Seřazení\t\tZmění seřazení." + +#: src/GMTrayIcon.cpp:302 +#: src/GMWindow.cpp:841 +#: src/GMWindow.cpp:925 +#: src/GMWindow.cpp:948 +#: src/GMWindow.cpp:954 +msgid "Play" +msgstr "Přehrát" + +#: src/GMTrayIcon.cpp:303 +#: src/GMWindow.cpp:843 +msgid "Stop" +msgstr "Zastavit" + +#: src/GMTrayIcon.cpp:304 +msgid "Previous Track" +msgstr "Předchozí skladba" + +#: src/GMTrayIcon.cpp:305 +msgid "Next Track" +msgstr "Následující skladba" + +#: src/GMWindow.cpp:205 +msgid "Play\tStart Playback\tStart Playback" +msgstr "Přehrát\tSpustit přehrávání\tSpustí přehrávání" + +#: src/GMWindow.cpp:205 +msgid "Pause\tPause\tPause Playback" +msgstr "Pozastavit\tPozastavit\tPozastaví přehrávání" + +#: src/GMWindow.cpp:206 +msgid "Stop\tStop Playback\tStop Playback" +msgstr "Zastavit\tZastavit přehrávání\tZastaví přehrávání" + +#: src/GMWindow.cpp:208 +msgid "Previous\tPlay Previous Track\tPlay previous track." +msgstr "Předchozí\tPřehrát předchozí skladbu\tPřehraje předchozí skladbu." + +#: src/GMWindow.cpp:209 +msgid "Next\tPlay Next Track\tPlay next track." +msgstr "Následující\tPřehrát následující skladbu\tPřehraje následující skladbu." + +#: src/GMWindow.cpp:255 +msgid "&Music" +msgstr "&Hudba" + +#: src/GMWindow.cpp:256 +msgid "Import Folder…\tCtrl-O\tImport Music from folder into Library" +msgstr "Importovat složku...\tCtrl-O\tImportuje hudbu ze složky do knihovny" + +#: src/GMWindow.cpp:257 +msgid "Sync Folder…\t\tSynchronize Folder with Music in Library" +msgstr "Synchronizovat složku...\t\tSynchronizuje složku s hudbou v knihovně" + +#: src/GMWindow.cpp:259 +msgid "Play File or Stream…\t\tPlay File or Stream" +msgstr "Přehrát soubor nebo přenos...\t\tPřehraje soubor nebo přenos" + +#: src/GMWindow.cpp:266 +msgid "&Quit\tCtrl-Q\tQuit the application." +msgstr "&Ukončit\tCtrl-Q\tUkončí aplikaci." + +#: src/GMWindow.cpp:271 +msgid "&Edit" +msgstr "&Editovat" + +#: src/GMWindow.cpp:272 +msgid "&Copy\tCtrl-C\tCopy Selected Tracks" +msgstr "&Kopírovat\tCtrl-C\tKopíruje vybrané skladby" + +#: src/GMWindow.cpp:273 +msgid "&Cut\tCtrl-X\tCut Selected Tracks" +msgstr "&Vyjmout\tCtrl-X\tVyjme vybrané skladby" + +#: src/GMWindow.cpp:274 +msgid "&Paste\tCtrl-V\tPaste Clipboard Selection" +msgstr "V&ložit\tCtrl-V\tVloží výběr ze schránky" + +#: src/GMWindow.cpp:276 +msgid "Find…\tCtrl-F\tShow search filter." +msgstr "Najít\tCtrl-F\tZobrazí filtr vyhledávání." + +#: src/GMWindow.cpp:278 +msgid "Preferences…" +msgstr "Nastavení..." + +#: src/GMWindow.cpp:282 +msgid "&View" +msgstr "&Pohled" + +#: src/GMWindow.cpp:283 +msgid "&Browse\tCtrl-B\tShow genre artist and album browser." +msgstr "&Prohlížeč\tCtrl-B\tZobrazí prohlížeč žánrů interpretů a alb." + +#: src/GMWindow.cpp:284 +msgid "Show &Genres\tCtrl-G\tShow genre browser." +msgstr "Zobrazit žán&ry\tCtrl-G\tZobrazí prohlížeč žánrů." + +#: src/GMWindow.cpp:287 +#: src/GMWindow.cpp:289 +msgid "Show &Sources\tCtrl-S\tShow source browser " +msgstr "Zobrazit &zdroje\tCtrl-S\tZobrazí prohlížeč zdrojů" + +#: src/GMWindow.cpp:294 +msgid "Show Full Screen\tF12\tToggle fullscreen mode." +msgstr "Zobrazit přes celou obrazovku\tF12\tPřepne mód celé obrazovky." + +#: src/GMWindow.cpp:296 +msgid "Show Mini Player\tCtrl-M\tToggle Mini Player." +msgstr "Zobrazit mini přehrávač\tCtrl-M\tPřepne mini přehrávač." + +#: src/GMWindow.cpp:300 +msgid "&Sort" +msgstr "&Seřadit" + +#: src/GMWindow.cpp:302 +msgid "&Jump to Current Track\tCtrl-J\tShow current playing track." +msgstr "&Přeskočit na aktuální skladbu\tCtrl-J\tZobrazí aktuálně přehrávanou skladbu." + +#: src/GMWindow.cpp:306 +msgid "&Control" +msgstr "&Ovládání" + +#: src/GMWindow.cpp:307 +msgid "Play\tCtrl-P\tStart playback." +msgstr "Přehrát\tCtrl-P\tSpustí přehrávání." + +#: src/GMWindow.cpp:308 +msgid "Stop\tCtrl-\\\tStop playback." +msgstr "Zastavit\tCtrl-\\\tZastaví přehrávání." + +#: src/GMWindow.cpp:309 +msgid "Previous Track\tCtrl-[\tPlay next track." +msgstr "Předchozí skladba\tCtrl-[\tPřehraje předchozí skladbu." + +#: src/GMWindow.cpp:310 +msgid "Next Track\tCtrl-]\tPlay previous track." +msgstr "Následující skladba\tCtrl-]\tPřehraje následující skladbu." + +#: src/GMWindow.cpp:312 +msgid "Repeat Off\tCtrl-,\tRepeat current track." +msgstr "Opakovat\tCtrl-,\tOpakuje aktuální sladbu." + +#: src/GMWindow.cpp:313 +msgid "Repeat Track\tCtrl-.\tRepeat current track." +msgstr "Opakovat skladbu\tCtrl-.\tOpakuje aktuální skladbu." + +#: src/GMWindow.cpp:314 +msgid "Repeat All Tracks\tCtrl-/\tRepeat all tracks." +msgstr "Opakovat všechny skladby\tCtrl-/\tOpakuje všechny skladby." + +#: src/GMWindow.cpp:315 +msgid "Repeat A-B\tCtrl-T\tRepeat section of track." +msgstr "Opakovat A-B\tCtrl-T\tOpakuje část skladeb." + +#: src/GMWindow.cpp:316 +msgid "Shuffle Mode\tAlt-R\tPlay tracks in random order." +msgstr "Náhodně\tAlt-R\tPřehrává skladby v náhodném pořadí." + +#: src/GMWindow.cpp:318 +msgid "Equalizer\t\t" +msgstr "Ekvalizér\t\t" + +#: src/GMWindow.cpp:319 +msgid "Sleep Timer\t\tSetup sleeptimer." +msgstr "Čas uspání\t\tNastaví čas uspání." + +#: src/GMWindow.cpp:323 +msgid "&Help" +msgstr "&Nápověda" + +#: src/GMWindow.cpp:324 +msgid "&Homepage" +msgstr "&Domovská stránka" + +#: src/GMWindow.cpp:325 +msgid "&Report Issue…" +msgstr "&Reportovat problém..." + +#: src/GMWindow.cpp:327 +msgid "&Sign up for last.fm…" +msgstr "&Přihlásit se do last.fm…" + +#: src/GMWindow.cpp:328 +msgid "&Join GMM on last.fm…\t\tJoin the Goggles Music Manager group on last.fm…" +msgstr "&Připojit GMM na last.fm...\t\tPřipojí skupinu Goggles Music Manager na last.fm..." + +#: src/GMWindow.cpp:330 +msgid "&About…" +msgstr "&O programu..." + +#: src/GMWindow.cpp:842 +#: src/GMWindow.cpp:940 +msgid "Pause" +msgstr "Pozastavit" + +#: src/GMWindow.cpp:844 +msgid "Previous" +msgstr "Předchozí" + +#: src/GMWindow.cpp:845 +msgid "Next" +msgstr "Následující" + +#: src/GMWindow.cpp:920 +#: src/GMWindow.cpp:949 +#: src/GMWindow.cpp:955 +msgid "Start playback." +msgstr "Spustí přehrávání." + +#: src/GMWindow.cpp:926 +#: src/GMWindow.cpp:927 +msgid "Start playback" +msgstr "Spustí přehrávání" + +#: src/GMWindow.cpp:934 +#: src/GMWindow.cpp:941 +msgid "Pause playback." +msgstr "Pozastaví přehrávání." + +#: src/GMWindow.cpp:935 +msgid "Pause playback" +msgstr "Pozastaví přehrávání" + +#: src/GMWindow.cpp:1052 +#: src/GMWindow.cpp:1054 +msgid "Repeat A-B" +msgstr "Opakovat A-B" + +#: src/GMWindow.cpp:1053 +msgid "Repeat A" +msgstr "Opakovat A" + +#: src/GMWindow.cpp:1195 +msgid "Play File or Stream" +msgstr "Přehrát soubor nebo přenos" + +#: src/GMWindow.cpp:1202 +msgid "Please specify a file or url to play:" +msgstr "Zadejte soubor nebo url k přehrání:" + +#: src/GMWindow.cpp:1206 +msgid "…" +msgstr "…" + +#: src/GMWindow.cpp:1212 +msgid "Select File" +msgstr "Vybrat soubor" + +#: src/GMWindow.cpp:1243 +msgid "Sleep Timer" +msgstr "Čas uspání" + +#: src/GMWindow.cpp:1244 +msgid "Setup sleep timer" +msgstr "Nastavit čas uspání" + +#: src/GMWindow.cpp:1244 +msgid "Stop playback within a certain time" +msgstr "Zastavit přehrávání za daný čas" + +#: src/GMWindow.cpp:1246 +msgid "&Start Timer" +msgstr "&Spustit časovač" + +#: src/GMWindow.cpp:1251 +msgid "Sleep in" +msgstr "Uspat za" + +#: src/GMWindow.cpp:1253 +msgid "hours and" +msgstr "hodin a" + +#: src/GMWindow.cpp:1255 +msgid "minutes." +msgstr "minut." + +#: src/GMDatabaseSource.h:131 +msgid "Music Library" +msgstr "Hudební knihovna" + +#: src/GMStreamSource.h:57 +msgid "Internet Radio" +msgstr "Internetové rádio" + +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:122 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:127 +msgid "&Quit" +msgstr "&Ukončit" + +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:133 +msgid "&Skip" +msgstr "&Přeskočit" + +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:134 +msgid "Skip &All" +msgstr "Přeskočit &vše" + +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:140 +msgid "&Don't Save" +msgstr "&Neukládat" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:220 +msgid "\tPick color" +msgstr "\tVybrat barvu" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:229 +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:274 +msgid "\tHue, Saturation, Value" +msgstr "\tOdstín, sytost, hodnota" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:239 +msgid "\tRed, Green, Blue" +msgstr "\tČervená, zelená, modrá" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:245 +msgid "&Red:" +msgstr "Če&rvená:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:250 +msgid "&Green:" +msgstr "&Zelená:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:255 +msgid "&Blue:" +msgstr "&Modrá:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:260 +msgid "&Alpha:" +msgstr "&Alfa:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:280 +msgid "Hue:" +msgstr "Odstín:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:285 +msgid "Saturation:" +msgstr "Sytost:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:290 +msgid "Value:" +msgstr "Hodnota:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:295 +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:330 +msgid "Alpha:" +msgstr "Alfa:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:309 +msgid "\tCyan, Magenta, Yellow" +msgstr "\tAzurová, fialová, žlutá" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:315 +msgid "Cyan:" +msgstr "Azurová:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:320 +msgid "Magenta:" +msgstr "Fialová:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:325 +msgid "Yellow:" +msgstr "Žlutá:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:344 +msgid "\tBy Name" +msgstr "\tPodle jména" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:281 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:521 +msgid "Create New Directory" +msgstr "Vytvořit nový adresář" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:284 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:524 +msgid "Already Exists" +msgstr "Již existuje" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:288 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:528 +msgid "Cannot Create" +msgstr "Nemohu vytvořit" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:309 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:595 +msgid "Copy File" +msgstr "Kopírovat soubor" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:315 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:601 +msgid "Error Copying File" +msgstr "Chyba kopírování souboru" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:326 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:618 +msgid "Move File" +msgstr "Přesunutí souboru" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:332 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:624 +msgid "Error Moving File" +msgstr "Chyba přesunu souboru" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:343 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:641 +msgid "Link File" +msgstr "Odkázat soubor" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:349 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:647 +msgid "Error Linking File" +msgstr "Chyba odkazu soubor" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:359 +msgid "Deleting file" +msgstr "Mazání souboru" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:361 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:667 +msgid "Error Deleting File" +msgstr "Chyba mazání souboru" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:381 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:719 +msgid "Up one level" +msgstr "O úroveň výš" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:382 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:720 +msgid "Home directory" +msgstr "Domovský adresář" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:383 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:721 +msgid "Work directory" +msgstr "Pracovní adresář" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:387 +msgid "Sorting" +msgstr "Seřazení" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:389 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:735 +msgid "Ignore case" +msgstr "Ignorovat velikost" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:390 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:746 +msgid "Hidden files" +msgstr "Skryté soubory" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:393 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:754 +msgid "Bookmarks" +msgstr "Záložky" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:394 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:757 +msgid "Set bookmark" +msgstr "Nastavit záložky" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:395 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:758 +msgid "Clear bookmarks" +msgstr "Vyčistit záložky" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:411 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:774 +msgid "New directory..." +msgstr "Nový adresář..." + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:412 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:775 +msgid "Copy..." +msgstr "Kopírovat..." + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:413 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:776 +msgid "Move..." +msgstr "Přesunout..." + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:414 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:777 +msgid "Link..." +msgstr "Odkazovat..." + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:415 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:778 +msgid "Delete..." +msgstr "Smazat..." + +#: ../../../fox-1.6.37/src/FXFileList.cpp:195 +msgid "Modified Date" +msgstr "Datum úpravy" + +#: ../../../fox-1.6.37/src/FXFileList.cpp:196 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:731 +msgid "User" +msgstr "Uživatel" + +#: ../../../fox-1.6.37/src/FXFileList.cpp:197 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:732 +msgid "Group" +msgstr "Skupina" + +#: ../../../fox-1.6.37/src/FXFileList.cpp:198 +msgid "Attributes" +msgstr "Atributy" + +#: ../../../fox-1.6.37/src/FXFileList.cpp:200 +msgid "Link" +msgstr "Odkaz" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:521 +msgid "Create new directory with name: " +msgstr "Vytvořit nový adresář:" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:524 +#, c-format +msgid "File or directory %s already exists.\n" +msgstr "Soubor nebo adresář %s již existuje.\n" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:528 +#, c-format +msgid "Cannot create directory %s.\n" +msgstr "Nemohu vytvořit adresář %s.\n" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:594 +#, c-format +msgid "" +"Copy file from location:\n" +"\n" +"%s\n" +"\n" +"to location: " +msgstr "" +"Kopírovat soubor z:\n" +"\n" +"%s\n" +"\n" +"do: " + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:601 +#, c-format +msgid "" +"Unable to copy file:\n" +"\n" +"%s to: %s\n" +"\n" +"Continue with operation?" +msgstr "" +"Nemohu kopírovat soubor:\n" +"\n" +"%s do: %s\n" +"\n" +"Pokračovat?" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:617 +#, c-format +msgid "" +"Move file from location:\n" +"\n" +"%s\n" +"\n" +"to location: " +msgstr "" +"Přesunout soubor z:\n" +"\n" +"%s\n" +"\n" +"do: " + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:624 +#, c-format +msgid "" +"Unable to move file:\n" +"\n" +"%s to: %s\n" +"\n" +"Continue with operation?" +msgstr "" +"Nemohu přesunout soubor:\n" +"\n" +"%s do: %s\n" +"\n" +"Pokračovat?" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:640 +#, c-format +msgid "" +"Link file from location:\n" +"\n" +"%s\n" +"\n" +"to location: " +msgstr "" +"Linkovat soubor z:\n" +"\n" +"%s\n" +"\n" +"do: " + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:647 +#, c-format +msgid "" +"Unable to link file:\n" +"\n" +"%s to: %s\n" +"\n" +"Continue with operation?" +msgstr "" +"Nemohu linkovat soubor:\n" +"\n" +"%s do: %s\n" +"\n" +"Pokračovat?" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:663 +msgid "Deleting files" +msgstr "Mazání souborů" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:663 +#, c-format +msgid "" +"Are you sure you want to delete the file:\n" +"\n" +"%s" +msgstr "" +"Jste si jistý smazáním souboru:\n" +"\n" +"%s" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:667 +#, c-format +msgid "" +"Unable to delete file:\n" +"\n" +"%s\n" +"\n" +"Continue with operation?" +msgstr "" +"Nemohu smazat soubor:\n" +"\n" +"%s\n" +"\n" +"Pokračovat?" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:722 +msgid "Select all" +msgstr "Vybrat vše" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:726 +msgid "Sort by" +msgstr "Seřadit podle" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:738 +msgid "View" +msgstr "Náhled" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:739 +msgid "Small icons" +msgstr "Malé ikony" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:740 +msgid "Big icons" +msgstr "Velké ikony" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:741 +msgid "Details" +msgstr "Detaily" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:743 +msgid "Rows" +msgstr "Řádky" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:744 +msgid "Columns" +msgstr "Sloupce" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:747 +msgid "Preview images" +msgstr "Náhledové obrázky" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:749 +msgid "Normal images" +msgstr "Normální obrázky" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:750 +msgid "Medium images" +msgstr "Střední obrázky" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:751 +msgid "Giant images" +msgstr "Obří obrázky" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:111 +msgid "&Replace" +msgstr "&Nahradit" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:112 +msgid "Re&place All" +msgstr "Na&hradit vše" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:121 +msgid "S&earch for:" +msgstr "H&ledat:" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:129 +msgid "Replace &with:" +msgstr "Nahradit &s:" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:138 +msgid "Ex&act" +msgstr "Pře&sně" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:139 +msgid "&Ignore Case" +msgstr "&Ignorovat velikost" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:140 +msgid "E&xpression" +msgstr "Vý&raz" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:141 +msgid "&Backward" +msgstr "&Zpětně" + +#: ../../../fox-1.6.37/src/FXSearchDialog.cpp:71 +msgid "&Search" +msgstr "&Hledat" + +#: ../../../fox-1.6.37/src/FXStatusLine.cpp:82 +msgid "Ready." +msgstr "Připraven." + +#~ msgid "Old Name" +#~ msgstr "Staré jméno" + +#~ msgid "New Name" +#~ msgstr "Nové jméno" + +#~ msgid "Pitch:" +#~ msgstr "Rozteč:" + +#~ msgid "Any" +#~ msgstr "Vše" + +#~ msgid "Fixed" +#~ msgstr "Fixní" + +#~ msgid "Variable" +#~ msgstr "Variabilní" + +#, fuzzy +#~ msgid " Type:" +#~ msgstr "Typ" + +#, fuzzy +#~ msgid "Scalable" +#~ msgstr "S měřítkem:" + +#, fuzzy +#~ msgid "Size:" +#~ msgstr "Velikost" + +#, fuzzy +#~ msgid "Family:" +#~ msgstr "&Rodina:" + +#~ msgid "Always Show Remote" +#~ msgstr "Vždy zobrazit vzdálené" + +#~ msgid "Source\tActive Source Color" +#~ msgstr "Zdroj\tBarva aktivního zdroje" + +#~ msgid "About" +#~ msgstr "O programu" + +#~ msgid "" +#~ "An incompatible version of SQLite (%s) is being used.\n" +#~ "Goggles Music Manager requires at least SQLite 3.3.8.\n" +#~ "Please upgrade your SQLite installation." +#~ msgstr "" +#~ "Používána nekompatabilní verze SQLite (%s).\n" +#~ "Goggles Music Manager vyžaduje nejméně SQLite 3.3.8.\n" +#~ "Prosím aktualizujte SQLite." + +#~ msgid "" +#~ "This version of SQLite (%s) is broken.\n" +#~ "Please upgrade your SQLite installation to at least 3.6.3." +#~ msgstr "" +#~ "Verze SQLite (%s) je poškozená.\n" +#~ "Prosím aktualizujte SQLite na nejméně 3.6.3." + +#~ msgid "" +#~ "&Join GMM on last.fm…\tJoin the Goggles Music Manager group on last.fm…" +#~ "\tJoin the Goggles Music Manager group on last.fm…" +#~ msgstr "" +#~ "&Připojit GMM na last.fm…\tPřipojit skupinu Goggles Music Manager na last." +#~ "fm…\tPřipojí skupinu Goggles Music Manager na last.fm…" + +#~ msgid "Font" +#~ msgstr "Font" + +#~ msgid "Theme Directory:" +#~ msgstr "Adresář s tématem:" + +#~ msgid "Select Theme Directory" +#~ msgstr "Vybrat adresář z tématem" + +#~ msgid "thin" +#~ msgstr "tenké" + +#~ msgid "normal" +#~ msgstr "normální" + +#~ msgid "bold" +#~ msgstr "tučné" + +#~ msgid "regular" +#~ msgstr "normální" + +#~ msgid "&Weight:" +#~ msgstr "&Tloušťka:" + +#~ msgid "&Style:" +#~ msgstr "&Styl:" + +#~ msgid "Si&ze:" +#~ msgstr "Ve&likost:" + +#~ msgid "Character Set:" +#~ msgstr "Znaková sada:" + +#~ msgid "West European" +#~ msgstr "Západoevropské" + +#~ msgid "East European" +#~ msgstr "Východoevropské" + +#~ msgid "South European" +#~ msgstr "Jihoevropské" + +#~ msgid "North European" +#~ msgstr "Severoevropské" + +#~ msgid "Cyrillic" +#~ msgstr "Cyrilice" + +#~ msgid "Arabic" +#~ msgstr "Arabské" + +#~ msgid "Greek" +#~ msgstr "Řecké" + +#~ msgid "Hebrew" +#~ msgstr "Hebrejské" + +#~ msgid "Turkish" +#~ msgstr "Turecké" + +#~ msgid "Nordic" +#~ msgstr "Skandinávský" + +#~ msgid "Thai" +#~ msgstr "Thajské" + +#~ msgid "Baltic" +#~ msgstr "Baltské" + +#~ msgid "Celtic" +#~ msgstr "Keltské" + +#~ msgid "Russian" +#~ msgstr "Ruské" + +#~ msgid "Central European (cp1250)" +#~ msgstr "Středoevropské (cp1250)" + +#~ msgid "Russian (cp1251)" +#~ msgstr "Ruské (cp1251)" + +#~ msgid "Latin1 (cp1252)" +#~ msgstr "Latin1 (cp1252)" + +#~ msgid "Greek (cp1253)" +#~ msgstr "Řecké (cp1253)" + +#~ msgid "Turkish (cp1254)" +#~ msgstr "Turecké (cp1254)" + +#~ msgid "Hebrew (cp1255)" +#~ msgstr "Hebrejské (cp1255)" + +#~ msgid "Arabic (cp1256)" +#~ msgstr "Arabské (cp1256)" + +#~ msgid "Baltic (cp1257)" +#~ msgstr "Baltské (cp1257)" + +#~ msgid "Vietnam (cp1258)" +#~ msgstr "Vietnamské (cp1258)" + +#~ msgid "Thai (cp874)" +#~ msgstr "Thajské (cp874)" + +#~ msgid "UNICODE" +#~ msgstr "UNICODE" + +#~ msgid "Set Width:" +#~ msgstr "Nastavit šířku:" + +#~ msgid "All Fonts:" +#~ msgstr "Všechny fonty:" + +#~ msgid "Preview:" +#~ msgstr "Náhled:" + +#~ msgid "Import Playlist…\t\tImport a existing playlist" +#~ msgstr "Načíst seznam skladeb...\t\tNačte existující seznam skladeb" + +#~ msgid "Import Files?" +#~ msgstr "Importovat soubory?" + +#~ msgid "" +#~ "Would you like import the pasted files and/or directories into the Music " +#~ "Library?" +#~ msgstr "Chcete importovat vložené soubory a/nebo adresáře do knihovny?" + +#~ msgid "" +#~ "%s\n" +#~ "by: %s\n" +#~ "from: %s" +#~ msgstr "" +#~ "%s\n" +#~ "od: %s\n" +#~ "z: %s" + +#~ msgid "Now Playing" +#~ msgstr "Nyní hraje" + +#~ msgid "Open URL…\t\tOpen Stream or File" +#~ msgstr "Otevřít URL...\t\tOtevře proud nebo soubor" + +#~ msgid "Open MRL" +#~ msgstr "Otevřít MRL" diff --git a/po/de.mo b/po/de.mo new file mode 100644 index 0000000000000000000000000000000000000000..a7b97d62a27509973f4acc985209b37d35052511 GIT binary patch literal 37930 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 AssI20 literal 0 HcmV?d00001 diff --git a/po/de.po b/po/de.po new file mode 100644 index 0000000..5af532c --- /dev/null +++ b/po/de.po @@ -0,0 +1,2958 @@ +# German translations for musicmanager package. +# Copyright (C) 2009 Sander Jansen +# This file is distributed under the same license as the musicmanager package. +# Hendrik Rittich , 2009. +# +msgid "" +msgstr "" +"Project-Id-Version: gogglesmm 0.10.0\n" +"Report-Msgid-Bugs-To: s.jansen@gmail.com\n" +"POT-Creation-Date: 2011-02-09 23:26-0600\n" +"PO-Revision-Date: 2009-07-06 09:41-0600\n" +"Last-Translator: Sander Jansen \n" +"Language-Team: German\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: src/GMAbout.cpp:136 src/GMDatabaseSource.cpp:218 +#: src/GMDatabaseSource.cpp:2215 src/GMPreferencesDialog.cpp:551 +msgid "&Close" +msgstr "S&chließen" + +#: src/GMColumnDialog.cpp:204 +msgid "Configure Columns" +msgstr "Spalten konfigurieren" + +#: src/GMColumnDialog.cpp:207 ../../../fox-1.6.37/src/FXColorSelector.cpp:168 +msgid "&Accept" +msgstr "Über&nehmen" + +#: src/GMColumnDialog.cpp:208 src/GMDatabaseSource.cpp:98 +#: src/GMDatabaseSource.cpp:1274 src/GMDatabaseSource.cpp:1704 +#: src/GMDatabaseSource.cpp:1804 src/GMDatabaseSource.cpp:1878 +#: src/GMDatabaseSource.cpp:2121 src/GMDatabaseSource.cpp:2182 +#: src/GMImportDialog.cpp:175 src/GMImportDialog.cpp:563 +#: src/GMPlayListSource.cpp:281 src/GMPlayListSource.cpp:410 +#: src/GMSearch.cpp:572 src/GMStreamSource.cpp:169 src/GMStreamSource.cpp:206 +#: src/GMStreamSource.cpp:271 src/GMWindow.cpp:1198 src/GMWindow.cpp:1247 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:107 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:118 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:123 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:129 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:135 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:142 +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:169 +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:135 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:205 +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:113 +msgid "&Cancel" +msgstr "A&bbrechen" + +#: src/GMColumnDialog.cpp:212 +msgid "" +"Choose the order of information to appear\n" +"in the track list." +msgstr "" +"Bitte wählen Sie die Reihenfolge, in der die\n" +"Informationen in der Titelliste erscheinen sollen." + +#: src/GMColumnDialog.cpp:218 +msgid "Move Up" +msgstr "Nach oben verschieben" + +#: src/GMColumnDialog.cpp:219 +msgid "Move Down" +msgstr "Nach unten verschieben" + +#: src/GMDatabaseSource.cpp:52 +msgid "Invalid Template" +msgstr "Ungültige Vorlage" + +#: src/GMDatabaseSource.cpp:52 +#, c-format +msgid "" +"The provided template is invalid. The track title %%T needs to be " +"specified.\n" +"Please fix the filename template in the preference panel." +msgstr "" +"Die eingegebene Vorlage ist leider ungültig. Der Name des Titels %%T muss " +"angegeben werden.\n" +"Bitte korrigieren Sie die Vorlage für Dateinamen im Einstellungs-Dialog." + +#: src/GMDatabaseSource.cpp:72 src/GMTrackDatabase.cpp:193 +msgid "Database Error" +msgstr "Datenbank-Fehler" + +#: src/GMDatabaseSource.cpp:72 +msgid "Oops. Database Error" +msgstr "Oh je. Ein Datenbank-Fehler" + +#: src/GMDatabaseSource.cpp:87 +msgid "No changes" +msgstr "Keine Änderungen" + +#: src/GMDatabaseSource.cpp:87 +msgid "Filenames did not require any changes" +msgstr "Für die Dateinamen war keine Änderung nötig" + +#: src/GMDatabaseSource.cpp:94 +msgid "Rename Audio Files?" +msgstr "Audio-Dateien umbenennen?" + +#: src/GMDatabaseSource.cpp:95 +msgid "Renaming Audio Files…" +msgstr "Die Audio-Dateien werden umbenannt…" + +#: src/GMDatabaseSource.cpp:95 +msgid "The following audio files are going to be renamed" +msgstr "Die folgenden Audio-Dateien werden umbenannt" + +#: src/GMDatabaseSource.cpp:97 +msgid "&Rename" +msgstr "Um&benennen" + +#: src/GMDatabaseSource.cpp:121 src/GMDatabaseSource.cpp:125 +msgid "Unable to rename file" +msgstr "Umbenennen der Datei nicht möglich" + +#: src/GMDatabaseSource.cpp:121 +#, c-format +msgid "" +"Unable to rename:\n" +"%s\n" +"\n" +"to:%s\n" +"Continue renaming files?" +msgstr "" +"Konnte die Datei nicht umbenennen:\n" +"%s\n" +"\n" +"in:%s\n" +"\n" +"Trotzdem fortfahren?" + +#: src/GMDatabaseSource.cpp:125 +#, c-format +msgid "" +"Unable to rename:\n" +"%s\n" +"\n" +"to:%s" +msgstr "" +"Umbenennen der Datei nicht möglich:\n" +"%s\n" +"\n" +"in:%s" + +#: src/GMDatabaseSource.cpp:160 src/GMImportDialog.cpp:534 +msgid "Filename Template" +msgstr "Vorlage für Dateinamen" + +#: src/GMDatabaseSource.cpp:177 +msgid "" +"Template may contain absolute or relative path, environment variables\n" +"and ~. Relative paths are based on the location of the original file. The\n" +"file extension gets automatically added. The following macros\n" +"may be used:" +msgstr "" +"Die Vorlage darf einen absoluten oder relativen Pfad enthalten, \n" +"Umgebungsvariablen und ~. Relative Pfadangaben beziehen sich auf den\n" +"ursprünglichen Ort der Datei. Die Datei-Erweiterung wird automatisch\n" +"ergänzt. Die folgenden Macros dürfen benutzt werden:" + +#: src/GMDatabaseSource.cpp:178 +msgid "" +"%T - title %A - album name\n" +"%P - album artist name %p - track artist name\n" +"%y - year %d - disc number\n" +"%N - track number (2 digits) %n - track number \n" +"%G - genre" +msgstr "" +"%T - Titelname %A - Albumname\n" +"%P - Künstlername des Albums %p - Künstlername des Titels\n" +"%y - Jahr %d - Disc-Nummer\n" +"%N - Titelnummer (2 stellig) %n - Titelnummer\n" +"%G - Genre" + +#: src/GMDatabaseSource.cpp:185 +msgid "Conditions may be used as well:" +msgstr "" + +#: src/GMDatabaseSource.cpp:186 +msgid "" +"?c - display a if c is not empty else display b.\n" +"?c - display c if not empty\n" +msgstr "" + +#: src/GMDatabaseSource.cpp:195 src/GMDatabaseSource.cpp:1815 +#: src/GMImportDialog.cpp:546 +msgid "Template:" +msgstr "Vorlage:" + +#: src/GMDatabaseSource.cpp:199 src/GMDatabaseSource.cpp:1819 +msgid "Encoding:" +msgstr "Kodierung:" + +#: src/GMDatabaseSource.cpp:205 +msgid "Exclude:" +msgstr "Ausnahmen:" + +#: src/GMDatabaseSource.cpp:209 src/GMDatabaseSource.cpp:1825 +msgid "Options:" +msgstr "Optionen:" + +#: src/GMDatabaseSource.cpp:210 src/GMDatabaseSource.cpp:1826 +msgid "Replace spaces with underscores" +msgstr "Ersetze Leerzeichen durch Unterstriche" + +#: src/GMDatabaseSource.cpp:212 src/GMDatabaseSource.cpp:1828 +msgid "Lower case" +msgstr "Kleinschreibung" + +#: src/GMDatabaseSource.cpp:214 src/GMDatabaseSource.cpp:1830 +msgid "Lower case extension" +msgstr "Klein geschriebene Datei-Erweiterung" + +#: src/GMDatabaseSource.cpp:341 src/GMStreamSource.cpp:63 +msgid "No." +msgstr "Nr." + +#: src/GMDatabaseSource.cpp:342 +msgid "Queue" +msgstr "Warteschlange" + +#: src/GMDatabaseSource.cpp:343 src/GMDatabaseSource.cpp:1391 +#: src/GMTrackView.cpp:238 +msgid "Title" +msgstr "Titelname" + +#: src/GMDatabaseSource.cpp:344 src/GMDatabaseSource.cpp:1396 +#: src/GMTrackView.cpp:239 +msgid "Artist" +msgstr "Künstler" + +#: src/GMDatabaseSource.cpp:345 src/GMDatabaseSource.cpp:1397 +msgid "Album Artist" +msgstr "Künstlername des Albums" + +#: src/GMDatabaseSource.cpp:346 src/GMDatabaseSource.cpp:1405 +#: src/GMPreferencesDialog.cpp:540 src/GMTrackView.cpp:240 +msgid "Album" +msgstr "Album" + +#: src/GMDatabaseSource.cpp:347 src/GMDatabaseSource.cpp:1364 +#: src/GMDatabaseSource.cpp:1375 src/GMDatabaseSource.cpp:1381 +msgid "Disc" +msgstr "Disc" + +#: src/GMDatabaseSource.cpp:348 src/GMDatabaseSource.cpp:1408 +#: src/GMStreamSource.cpp:66 src/GMStreamSource.cpp:177 +#: src/GMStreamSource.cpp:216 src/GMTrackView.cpp:241 +msgid "Genre" +msgstr "Genre" + +#: src/GMDatabaseSource.cpp:349 src/GMDatabaseSource.cpp:1369 +#: src/GMDatabaseSource.cpp:1387 +msgid "Year" +msgstr "Jahr" + +#: src/GMDatabaseSource.cpp:350 ../../../fox-1.6.37/src/FXFileSelector.cpp:730 +msgid "Time" +msgstr "Zeit" + +#: src/GMDatabaseSource.cpp:490 +msgid "Remove…\tDel\tRemove Genre from Library." +msgstr "Löschen…\tDel\tDas Genre aus der Bibliothek löschen." + +#: src/GMDatabaseSource.cpp:496 src/GMDatabaseSource.cpp:505 +#: src/GMPlayListSource.cpp:104 src/GMPlayListSource.cpp:110 +msgid "Copy\tCtrl-C\tCopy associated tracks to the clipboard." +msgstr "Kopieren\tCtrl-C\tKopiere die zugehörigen Titel in die Zwischenablage" + +#: src/GMDatabaseSource.cpp:499 src/GMDatabaseSource.cpp:508 +msgid "Remove…\tDel\tRemove associated tracks from library." +msgstr "Löschen…\tDel\tLösche die zugehörigen Titel aus der Bibliothek" + +#: src/GMDatabaseSource.cpp:513 src/GMPlayListSource.cpp:116 +msgid "Edit…\tF2\tEdit Track Information." +msgstr "Bearbeiten…\tF2\tBearbeite die Titel-Informationen." + +#: src/GMDatabaseSource.cpp:514 src/GMPlayListSource.cpp:117 +msgid "Copy\tCtrl-C\tCopy track(s) to clipboard." +msgstr "Kopieren\tCtrl-C\tKopiere die/den Titel in die Zwischenablage." + +#: src/GMDatabaseSource.cpp:518 src/GMPlayListSource.cpp:120 +msgid "Open Folder Location\t\tOpen Folder Location." +msgstr "" + +#: src/GMDatabaseSource.cpp:523 +msgid "Remove…\tDel\tRemove track(s) from library." +msgstr "Löschen…\tDel\tLösche die/den Titel aus der Bibliothek." + +#: src/GMDatabaseSource.cpp:537 +msgid "New Play List…\t\tCreate a new play list." +msgstr "Neue Wiedergabeliste…\t\tErstelle eine neue Wiedergabeliste" + +#: src/GMDatabaseSource.cpp:539 src/GMPlayListSource.cpp:163 +msgid "Export…" +msgstr "Exportieren…" + +#: src/GMDatabaseSource.cpp:540 +msgid "Information…\t\tLibrary Statistics" +msgstr "Informationen…\t\tBibliotheksstatistik" + +#: src/GMDatabaseSource.cpp:542 +msgid "Remove All Tracks\t\tRemove all tracks from the library" +msgstr "Alle Titel entfernen\t\tEntferne alle Titel aus der Bibliothek" + +#: src/GMDatabaseSource.cpp:1272 +msgid "Edit Track Information" +msgstr "Bearbeite die Titel-Informationen" + +#: src/GMDatabaseSource.cpp:1275 src/GMPlayListSource.cpp:409 +#: src/GMStreamSource.cpp:205 ../../../fox-1.6.37/src/FXMessageBox.cpp:128 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:143 +msgid "&Save" +msgstr "&Speichern" + +#: src/GMDatabaseSource.cpp:1315 +msgid "&Tag" +msgstr "" + +#: src/GMDatabaseSource.cpp:1320 +#, fuzzy +msgid "&Properties" +msgstr "Einstellungen" + +#: src/GMDatabaseSource.cpp:1325 src/GMImportDialog.cpp:525 +msgid "Filename" +msgstr "Dateiname" + +#: src/GMDatabaseSource.cpp:1329 ../../../fox-1.6.37/src/FXFileList.cpp:193 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:728 +msgid "Type" +msgstr "Typ" + +#: src/GMDatabaseSource.cpp:1333 ../../../fox-1.6.37/src/FXFileList.cpp:194 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:729 +msgid "Size" +msgstr "Größe" + +#: src/GMDatabaseSource.cpp:1341 src/GMStreamSource.cpp:65 +msgid "Bitrate" +msgstr "Bitrate" + +#: src/GMDatabaseSource.cpp:1345 +msgid "Samplerate" +msgstr "Sampelrate" + +#: src/GMDatabaseSource.cpp:1349 +msgid "Channels" +msgstr "Kanäle" + +#: src/GMDatabaseSource.cpp:1358 src/GMPreferencesDialog.cpp:539 +msgid "Track" +msgstr "Titel" + +#: src/GMDatabaseSource.cpp:1403 +msgid "\tSeparate Artists" +msgstr "\tKünstler aufteilen" + +#: src/GMDatabaseSource.cpp:1403 +msgid "\tShared Artists" +msgstr "\tKünstler teilen" + +#: src/GMDatabaseSource.cpp:1416 +msgid "Auto track number. Offset:" +msgstr "Automatische Titelnummer. Erste Nummer:" + +#: src/GMDatabaseSource.cpp:1425 +msgid "Update Tag in File" +msgstr "Aktualisiere den Tag in der Datei" + +#: src/GMDatabaseSource.cpp:1430 +msgid "Update Filename" +msgstr "Dateiname aktualisieren" + +#: src/GMDatabaseSource.cpp:1431 +msgid "Set export template…" +msgstr "Wähle Export-Vorlage…" + +#: src/GMDatabaseSource.cpp:1645 +msgid "Update Tags?" +msgstr "Tags aktualisieren?" + +#: src/GMDatabaseSource.cpp:1645 +msgid "" +"No tracks were updated.\n" +"Would you still like to write the tags for the selected tracks?" +msgstr "" +"Keine Titel wurden aktualisiert.\n" +"Möchten Sie immer noch die Tags für die ausgewählten Titel in die Datei " +"schreiben?" + +#: src/GMDatabaseSource.cpp:1700 +msgid "Remove Audio Files?" +msgstr "Audio-Dateien entfernen?" + +#: src/GMDatabaseSource.cpp:1701 +msgid "Remove Audio Files..." +msgstr "Audio-Dateien werden entfernt..." + +#: src/GMDatabaseSource.cpp:1701 +msgid "The following audio files are going to be removed" +msgstr "Die folgenden Audio-Dateien werden entfernt" + +#: src/GMDatabaseSource.cpp:1703 src/GMDatabaseSource.cpp:1877 +#: src/GMPlayListSource.cpp:280 src/GMStreamSource.cpp:270 +msgid "&Remove" +msgstr "&Entfernen" + +#: src/GMDatabaseSource.cpp:1729 +msgid "Export Main Library" +msgstr "Exportiere die Hauptbibliothek" + +#: src/GMDatabaseSource.cpp:1731 +msgid "Export Play List" +msgstr "Exportiere Wiedergabeliste" + +#: src/GMDatabaseSource.cpp:1744 +msgid "Overwrite File?" +msgstr "Datei überschreiben?" + +#: src/GMDatabaseSource.cpp:1744 +msgid "File already exists. Would you like to overwrite it?" +msgstr "Die Datei existiert bereits. Möchten Sie sie überschreiben?" + +#: src/GMDatabaseSource.cpp:1784 +msgid "Export Genre" +msgstr "Genre exportieren" + +#: src/GMDatabaseSource.cpp:1785 +msgid "Export tracks with genre to destination directory." +msgstr "Exportiere die Titel diesen Genres ins Zielverzeichnis." + +#: src/GMDatabaseSource.cpp:1787 +msgid "Export Artists" +msgstr "Künstler exportieren" + +#: src/GMDatabaseSource.cpp:1788 +msgid "Export tracks from artist to destination directory." +msgstr "Exportiere die Titel dieses Künstlers ins Zielverzeichnis." + +#: src/GMDatabaseSource.cpp:1790 +msgid "Export Albums" +msgstr "Album exportieren" + +#: src/GMDatabaseSource.cpp:1791 +msgid "Export tracks from album to destination directory." +msgstr "Exportiere die Titel dieses Albums ins Zielverzeichnis." + +#: src/GMDatabaseSource.cpp:1793 +msgid "Export Tracks" +msgstr "Titel exportieren" + +#: src/GMDatabaseSource.cpp:1794 +msgid "Export tracks to destination directory." +msgstr "Exportiere die Titel ins Zielverzeichnis" + +#: src/GMDatabaseSource.cpp:1803 +msgid "&Export" +msgstr "&Exportieren" + +#: src/GMDatabaseSource.cpp:1853 src/GMPlayListSource.cpp:261 +msgid "Remove Genre?" +msgstr "Genre entfernen?" + +#: src/GMDatabaseSource.cpp:1854 +msgid "Remove tracks with genre from library?" +msgstr "Entferne die Titel dieses Genres aus der Bibliothek." + +#: src/GMDatabaseSource.cpp:1858 src/GMPlayListSource.cpp:264 +msgid "Remove Artist?" +msgstr "Künstler entfernen?" + +#: src/GMDatabaseSource.cpp:1859 +msgid "Remove tracks from artist from library?" +msgstr "Entferne die Titel dieses Künstlers aus der Bibliothek." + +#: src/GMDatabaseSource.cpp:1863 src/GMPlayListSource.cpp:267 +msgid "Remove Album?" +msgstr "Album entfernen?" + +#: src/GMDatabaseSource.cpp:1864 +msgid "Remove tracks from album from library?" +msgstr "Entferne die Titel dieses Albums aus der Bibliothek." + +#: src/GMDatabaseSource.cpp:1868 src/GMPlayListSource.cpp:270 +msgid "Remove Track(s)?" +msgstr "Titel entfernen?" + +#: src/GMDatabaseSource.cpp:1869 +msgid "Remove track(s) from library?" +msgstr "Die/Den Titel aus der Bibliothek entfernen?" + +#: src/GMDatabaseSource.cpp:1881 src/GMPlayListSource.cpp:285 +msgid "Remove tracks from disk" +msgstr "Entferne die Titel von der Festplatte" + +#: src/GMDatabaseSource.cpp:1895 src/GMDatabaseSource.cpp:1899 +#: src/GMDatabaseSource.cpp:1903 src/GMDatabaseSource.cpp:1907 +#: src/GMPlayListSource.cpp:308 src/GMStreamSource.cpp:280 +msgid "Library Error" +msgstr "Fehler in der Bibliothek" + +#: src/GMDatabaseSource.cpp:1895 +msgid "Unable to remove genre from the library" +msgstr "Das Genre konnte nicht aus der Bibliothek gelöscht werden." + +#: src/GMDatabaseSource.cpp:1899 +msgid "Unable to remove artist from the library" +msgstr "Der Künstler konnte nicht aus der Bibliothek gelöscht werden." + +#: src/GMDatabaseSource.cpp:1903 +msgid "Unable to remove album from the library" +msgstr "Das Album konnte nicht aus der Bibliothek gelöscht werden." + +#: src/GMDatabaseSource.cpp:1907 src/GMPlayListSource.cpp:308 +msgid "Unable to remove track from the library." +msgstr "Der Titel konnte nicht aus der Bibliothek gelöscht werden." + +#: src/GMDatabaseSource.cpp:2117 src/GMDatabaseSource.cpp:2118 +msgid "Create Playlist" +msgstr "Erstelle Wiedergabeliste" + +#: src/GMDatabaseSource.cpp:2118 +msgid "Specify name of the new playlist" +msgstr "Geben Sie den Name der Wiedergabeliste ein" + +#: src/GMDatabaseSource.cpp:2120 +msgid "&Create" +msgstr "&Erstellen" + +#: src/GMDatabaseSource.cpp:2125 src/GMPlayListSource.cpp:415 +#: ../../../fox-1.6.37/src/FXFileList.cpp:192 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:727 +msgid "Name" +msgstr "Name" + +#: src/GMDatabaseSource.cpp:2127 +msgid "New Playlist" +msgstr "Neue Wiedergabeliste" + +#: src/GMDatabaseSource.cpp:2178 src/GMDatabaseSource.cpp:2179 +msgid "Clear Music Library?" +msgstr "Musikbibliothek leeren?" + +#: src/GMDatabaseSource.cpp:2179 +msgid "Remove all tracks from the music library?" +msgstr "Alle Titel aus der Musikbibliothek entfernen?" + +#: src/GMDatabaseSource.cpp:2181 +msgid "&Remove All" +msgstr "&Entferne Alle" + +#: src/GMDatabaseSource.cpp:2185 +msgid "Keep play lists" +msgstr "Wiedergabelisten behalten" + +#: src/GMDatabaseSource.cpp:2212 src/GMDatabaseSource.cpp:2213 +msgid "Music Library Information" +msgstr "Musikbibliothek-Informationen" + +#: src/GMDatabaseSource.cpp:2213 +msgid "You music collection consists of…" +msgstr "Ihre Musiksammlung besteht aus…" + +#: src/GMDatabaseSource.cpp:2221 +msgid "Tracks:" +msgstr "Titel:" + +#: src/GMDatabaseSource.cpp:2226 +msgid "Artists:" +msgstr "Künstler:" + +#: src/GMDatabaseSource.cpp:2230 +msgid "Albums:" +msgstr "Alben:" + +#: src/GMDatabaseSource.cpp:2234 +msgid "Total Time:" +msgstr "Zeit insgesamt:" + +#: src/GMEQDialog.cpp:96 +msgid "Equalizer" +msgstr "Equalizer" + +#: src/GMEQDialog.cpp:135 +msgid "Equalizer:" +msgstr "Equalizer:" + +#: src/GMEQDialog.cpp:137 +msgid "\tSave" +msgstr "\tSpeichern" + +#: src/GMEQDialog.cpp:138 +msgid "\tReset" +msgstr "\tZurücksetzen" + +#: src/GMEQDialog.cpp:139 +msgid "\tRemove" +msgstr "\tEntfernen" + +#: src/GMEQDialog.cpp:172 +msgid "Pre-amp" +msgstr "Vorverstärker" + +#: src/GMEQDialog.cpp:244 src/GMEQDialog.cpp:254 +msgid "Disabled" +msgstr "Deaktiviert" + +#: src/GMEQDialog.cpp:247 src/GMEQDialog.cpp:299 +msgid "Manual" +msgstr "Manuell" + +#: src/GMEQDialog.cpp:314 +msgid "Delete Preset" +msgstr "Voreinstellung löschen" + +#: src/GMEQDialog.cpp:314 +#, c-format +msgid "Are you sure you want to delete %s preset?" +msgstr "Sind Sie sicher, dass Sie die Einstellung %s löschen wollen?" + +#: src/GMEQDialog.cpp:334 +msgid "Preset Name" +msgstr "Name der Voreinstellung" + +#: src/GMEQDialog.cpp:334 +msgid "Please enter preset name:" +msgstr "Bitte geben Sie den Namen der Voreinstellung ein:" + +#: src/GMEQDialog.cpp:338 +msgid "Overwrite Preset" +msgstr "Voreinstellung überschreiben" + +#: src/GMEQDialog.cpp:338 +#, c-format +msgid "Preset %s already exists. Would you like to overwrite it?" +msgstr "Die Einstellung %s existiert bereits. Möchten Sie sie überschreiben?" + +#: src/GMFontDialog.cpp:209 +#, fuzzy +msgid "Ultra Condensed" +msgstr "Wahnsinnig dünn" + +#: src/GMFontDialog.cpp:210 +#, fuzzy +msgid "Extra Condensed" +msgstr "Besonders dünn" + +#: src/GMFontDialog.cpp:211 +msgid "Condensed" +msgstr "dünn" + +#: src/GMFontDialog.cpp:212 +#, fuzzy +msgid "Semi Condensed" +msgstr "Halbdünn" + +#: src/GMFontDialog.cpp:214 +#, fuzzy +msgid "Semi Expanded" +msgstr "Halbbreit" + +#: src/GMFontDialog.cpp:215 +msgid "Expanded" +msgstr "Breit" + +#: src/GMFontDialog.cpp:216 +#, fuzzy +msgid "Extra Expanded" +msgstr "Besonders Breit" + +#: src/GMFontDialog.cpp:217 +#, fuzzy +msgid "Ultra Expanded" +msgstr "Wahnsinnig Breit" + +#: src/GMFontDialog.cpp:224 src/GMPreferencesDialog.cpp:1223 +msgid "Thin" +msgstr "" + +#: src/GMFontDialog.cpp:225 src/GMPreferencesDialog.cpp:1224 +#, fuzzy +msgid "Extra Light" +msgstr "besonders dünn" + +#: src/GMFontDialog.cpp:226 src/GMPreferencesDialog.cpp:1225 +#, fuzzy +msgid "Light" +msgstr "leicht" + +#: src/GMFontDialog.cpp:228 src/GMPreferencesDialog.cpp:1227 +#, fuzzy +msgid "Medium" +msgstr "mittel" + +#: src/GMFontDialog.cpp:229 src/GMPreferencesDialog.cpp:1228 +#, fuzzy +msgid "Demibold" +msgstr "halbfett" + +#: src/GMFontDialog.cpp:230 src/GMPreferencesDialog.cpp:1229 +msgid "Bold" +msgstr "" + +#: src/GMFontDialog.cpp:231 src/GMPreferencesDialog.cpp:1230 +#, fuzzy +msgid "Extra Bold" +msgstr "besonders fett" + +#: src/GMFontDialog.cpp:232 src/GMPreferencesDialog.cpp:1231 +#, fuzzy +msgid "Heavy" +msgstr "sehr fett" + +#: src/GMFontDialog.cpp:239 +#, fuzzy +msgid "Reverse Oblique" +msgstr "entgegengesetzt schief" + +#: src/GMFontDialog.cpp:240 +#, fuzzy +msgid "Reverse Italic" +msgstr "entgegengesetzt kursiv" + +#: src/GMFontDialog.cpp:242 +#, fuzzy +msgid "Italic" +msgstr "kursiv" + +#: src/GMFontDialog.cpp:243 +#, fuzzy +msgid "Oblique" +msgstr "schief" + +#: src/GMFontDialog.cpp:265 +msgid "Normal" +msgstr "Normal" + +#: src/GMImportDialog.cpp:91 ../../../fox-1.6.37/src/FXDirSelector.cpp:137 +msgid "&Directory:" +msgstr "Ver&zeichnis" + +#: src/GMImportDialog.cpp:166 ../../../fox-1.6.37/src/FXFileSelector.cpp:196 +msgid "&File Name:" +msgstr "&Dateiname:" + +#: src/GMImportDialog.cpp:168 ../../../fox-1.6.37/src/FXMessageBox.cpp:102 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:106 +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:134 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:198 +msgid "&OK" +msgstr "&OK" + +#: src/GMImportDialog.cpp:170 ../../../fox-1.6.37/src/FXFileSelector.cpp:200 +msgid "File F&ilter:" +msgstr "Dateif&ilter:" + +#: src/GMImportDialog.cpp:174 ../../../fox-1.6.37/src/FXFileSelector.cpp:204 +msgid "Read Only" +msgstr "Schreibgeschützt" + +#: src/GMImportDialog.cpp:179 src/GMSearch.cpp:565 src/GMSearch.cpp:647 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:208 +msgid "Directory:" +msgstr "Verzeichnis:" + +#: src/GMImportDialog.cpp:186 ../../../fox-1.6.37/src/FXFileSelector.cpp:228 +msgid "&Set bookmark\t\tBookmark current directory." +msgstr "" +"Le&sezeichen erstellen\t\tLege ein Lesezeichen ins aktuelle Verzeichnis." + +#: src/GMImportDialog.cpp:187 ../../../fox-1.6.37/src/FXFileSelector.cpp:229 +msgid "&Clear bookmarks\t\tClear bookmarks." +msgstr "Lesezeichen leeren\t\tEntferne alle Lesezeichen." + +#: src/GMImportDialog.cpp:203 ../../../fox-1.6.37/src/FXFileSelector.cpp:244 +msgid "\tGo up one directory\tMove up to higher directory." +msgstr "\tEine Ebene nach oben\tWechsele in ein höheres Verzeichnis." + +#: src/GMImportDialog.cpp:204 ../../../fox-1.6.37/src/FXFileSelector.cpp:245 +msgid "\tGo to home directory\tBack to home directory." +msgstr "\tZum Heimverzeichnis\tZurück ins Heimverzeichnis." + +#: src/GMImportDialog.cpp:205 ../../../fox-1.6.37/src/FXFileSelector.cpp:246 +msgid "\tGo to work directory\tBack to working directory." +msgstr "\tZum Arbeitsverzeichnis\tZurück zum Arbeitsverzeichnis." + +#: src/GMImportDialog.cpp:206 ../../../fox-1.6.37/src/FXFileSelector.cpp:247 +msgid "\tBookmarks\tVisit bookmarked directories." +msgstr "\tLesezeichen\tZu einem Lesezeichen wechseln." + +#: src/GMImportDialog.cpp:209 ../../../fox-1.6.37/src/FXFileSelector.cpp:250 +msgid "\tCreate new directory\tCreate new directory." +msgstr "\tNeues Verzeichnis erstellen\tErstelle ein neues Verzeichnis." + +#: src/GMImportDialog.cpp:210 ../../../fox-1.6.37/src/FXFileSelector.cpp:251 +msgid "\tShow list\tDisplay directory with small icons." +msgstr "\tListenansicht\tDas Verzeichnis mit kleinen Symbolen anzeigen." + +#: src/GMImportDialog.cpp:211 ../../../fox-1.6.37/src/FXFileSelector.cpp:252 +msgid "\tShow icons\tDisplay directory with big icons." +msgstr "\tSymbolansicht\tDas Verzeichnis mit großen Symbolen anzeigen." + +#: src/GMImportDialog.cpp:212 ../../../fox-1.6.37/src/FXFileSelector.cpp:253 +msgid "\tShow details\tDisplay detailed directory listing." +msgstr "\tDetailansicht\tDas Verzeichnis detailiert anzeigen." + +#: src/GMImportDialog.cpp:213 ../../../fox-1.6.37/src/FXFileSelector.cpp:254 +msgid "\tShow hidden files\tShow hidden files and directories." +msgstr "" +"\tVersteckte Dateien anzeigen\tZeige versteckte Dateien und Verzeichnisse an." + +#: src/GMImportDialog.cpp:213 ../../../fox-1.6.37/src/FXFileSelector.cpp:254 +msgid "\tHide Hidden Files\tHide hidden files and directories." +msgstr "" +"\tVerstecke versteckte Dateien\tZeige keine versteckten Dateien und " +"Verzeichnisse an." + +#: src/GMImportDialog.cpp:412 +msgid "Synchronize Folder" +msgstr "Ordner synchronisieren" + +#: src/GMImportDialog.cpp:414 +#, fuzzy +msgid "Import Playlist" +msgstr "Exportiere Wiedergabeliste" + +#: src/GMImportDialog.cpp:416 +msgid "Import Music" +msgstr "Musik importieren" + +#: src/GMImportDialog.cpp:448 +msgid "&File(s)" +msgstr "&Dateien" + +#: src/GMImportDialog.cpp:475 +msgid "&Directory" +msgstr "&Verzeichnis" + +#: src/GMImportDialog.cpp:478 +msgid "Exclude Filter\tFilter out directories and/or files based on pattern" +msgstr "" +"Ausfiltern\tFiltere Verzeichnisse und/oder Dateien nach einem Muster aus" + +#: src/GMImportDialog.cpp:481 +msgid "Folders:" +msgstr "Ordner:" + +#: src/GMImportDialog.cpp:483 +msgid "Files:" +msgstr "Dateien:" + +#: src/GMImportDialog.cpp:499 src/GMImportDialog.cpp:560 +msgid "&Sync" +msgstr "&Sync" + +#: src/GMImportDialog.cpp:501 +msgid "Sync Operation" +msgstr "Sync Operation" + +#: src/GMImportDialog.cpp:504 +msgid "Import new tracks\tImports files not yet in the database." +msgstr "" +"Neue Titel imporieren\tTitel, die sich noch nicht in der Datenbank befinden, " +"werden importiert." + +#: src/GMImportDialog.cpp:505 +msgid "Remove tracks that have been deleted from disk" +msgstr "Entferne von der Festplatte gelösche Titel" + +#: src/GMImportDialog.cpp:507 +msgid "Update existing tracks:" +msgstr "Aktualisiere die bestehenden Titel:" + +#: src/GMImportDialog.cpp:508 +msgid "" +"Modified since last import\tOnly reread the tag when the file has been " +"modified." +msgstr "Zuletzt geänderte Dateien\tLies nur tag aus geänderten Dateien" + +#: src/GMImportDialog.cpp:510 +msgid "All\tAlways read the tags" +msgstr "Alle\tLies alle Tags" + +#: src/GMImportDialog.cpp:511 +msgid "Remove tracks found in folder from database" +msgstr "Entferne alle Titel in diesem Ordner aus der Datenbank" + +#: src/GMImportDialog.cpp:514 +msgid "&Track" +msgstr "&Titel" + +#: src/GMImportDialog.cpp:517 +msgid "Parse Settings" +msgstr "Einstellungen lesen" + +#: src/GMImportDialog.cpp:521 +msgid "Parse info from:" +msgstr "Lies die Informationen aus:" + +#: src/GMImportDialog.cpp:524 +msgid "Tag" +msgstr "Tag" + +#: src/GMImportDialog.cpp:526 +msgid "Both" +msgstr "Beides" + +#: src/GMImportDialog.cpp:528 +msgid "Default value:" +msgstr "Standardwert:" + +#: src/GMImportDialog.cpp:532 +msgid "Set track number based on scan order." +msgstr "Wähle die Titelnummer auf Grund der Scannreihenfolge" + +#: src/GMImportDialog.cpp:537 +msgid "" +"%T - title %A - album name\n" +"%P - album artist name %p - track artist name \n" +"%N - track number %G - genre" +msgstr "" +"%T - Titelname %A - Albumname\n" +"%P - Künstlername des Albums %p - Künstlername des Titels \n" +"%N - Titelnummer %G - Genre" + +#: src/GMImportDialog.cpp:549 +msgid "Replace underscores with spaces" +msgstr "Ersetze Unterstriche durch Leerzeichen" + +#: src/GMImportDialog.cpp:562 +msgid "&Import" +msgstr "&Importieren" + +#: src/GMPlayer.cpp:212 +msgid "Unable to initialize audio driver." +msgstr "Der Audiotreiber konnte nicht geladen werden." + +#: src/GMPlayer.cpp:714 +msgid "Unknown host." +msgstr "Unbekannter host." + +#: src/GMPlayer.cpp:715 +msgid "Unknown device" +msgstr "Unbekanntes Gerät." + +#: src/GMPlayer.cpp:716 +msgid "Network not reachable." +msgstr "Keine Verbindung zum Netzwerk." + +#: src/GMPlayer.cpp:717 +msgid "Audio output unavailable." +msgstr "Die Audioausgabe ist nicht verfügbar." + +#: src/GMPlayer.cpp:718 +msgid "Connection Refused." +msgstr "Verbindung abgewiesen." + +#: src/GMPlayer.cpp:719 +msgid "File not found." +msgstr "Datei wurde nicht gefunden." + +#: src/GMPlayer.cpp:720 +msgid "Resource not accessible. Check permissions" +msgstr "Kein Zugriff auf die Ressource. Überprüfen Sie die Berechtigungen." + +#: src/GMPlayer.cpp:721 +msgid "Read Error" +msgstr "Lesefehler" + +#: src/GMPlayer.cpp:722 +msgid "Error while loading library/plugin" +msgstr "Fehler beim Laden einer Bibliothek/Plugins" + +#: src/GMPlayer.cpp:723 +msgid "Warning" +msgstr "Warnung" + +#: src/GMPlayer.cpp:724 +msgid "Security Warning" +msgstr "Sicherheitswarnung" + +#: src/GMPlayer.cpp:725 +msgid "Unknown Error" +msgstr "Unbekannter Fehler" + +#: src/GMPlayer.cpp:761 +msgid "Error" +msgstr "Fehler" + +#: src/GMPlayerManager.cpp:439 +#, c-format +msgid "Unable to create directory %s\n" +msgstr "Das Verzeichnis %s kann nicht erstellt werden.\n" + +#: src/GMPlayerManager.cpp:612 +msgid "" +"For some reason the FOX library was compiled without PNG support.\n" +"In order to show all icons, Goggles Music Manager requires PNG\n" +"support in the FOX library. If you've compiled FOX yourself, most\n" +"likely the libpng header files were not installed on your system." +msgstr "" +"Das FOX-Toolkit wurde ohne PNG-Unterstützung kompiliert.\n" +"Diese wird allerdings benötigt, damit Goggles Music Manager alle\n" +"Symbole anzeigen kann. Wenn Sie das FOX-Toolkit selber kompiliert\n" +"haben, so waren die libpng Header-Dateien wahrscheinlich nicht auf\n" +"Ihrem System vorhanden." + +#: src/GMPlayerManager.cpp:623 +msgid "Session bus not available. All features requiring dbus are disabled." +msgstr "" +"Der Session-Bus wurde nicht gefunden. Alle Funktionen, die DBus benötigen " +"stehen nicht zur Verfügung." + +#: src/GMPlayerManager.cpp:633 +msgid "A DBus error occurred. All features requiring sessionbus are disabled." +msgstr "" +"DBus hat einen Fehler verursacht. Alle Funktionen, die DBus benötigen stehen " +"nicht zur Verfügung." + +#: src/GMPlayerManager.cpp:644 +msgid "" +"Session Bus not available. All features requiring sessionbus are disabled." +msgstr "" +"Der Session-Bus wurde nicht gefunden. Alle Funktionen, die DBus benötigen " +"stehen nicht zur Verfügung." + +#: src/GMPlayerManager.cpp:719 src/GMPreferencesDialog.cpp:594 +msgid "Audio Device Error" +msgstr "Audio-Gerät-Fehler" + +#: src/GMPlayerManager.cpp:1551 +msgid "Last.FM Error" +msgstr "Last.FM-Fehler" + +#: src/GMPlayerManager.cpp:1558 +msgid "Playback Error" +msgstr "Wiedergabe-Fehler" + +#: src/GMPlayerManager.cpp:1708 +#, c-format +msgid "" +"%s\n" +"%s (%d)" +msgstr "" + +#: src/GMPlayListSource.cpp:99 src/GMPlayListSource.cpp:105 +#: src/GMPlayListSource.cpp:111 src/GMPlayListSource.cpp:121 +msgid "Remove…\tDel\tRemove track(s) from play list." +msgstr "Entfernen…\tDel\tEntferne die/den Titel aus der Wiedergabeliste." + +#: src/GMPlayListSource.cpp:161 +msgid "Edit…" +msgstr "Bearbeiten…" + +#: src/GMPlayListSource.cpp:162 +#, fuzzy +msgid "Import…" +msgstr "Exportieren…" + +#: src/GMPlayListSource.cpp:164 +msgid "Remove Playlist" +msgstr "Entferne Wiedergabeliste" + +#: src/GMPlayListSource.cpp:262 +msgid "Remove tracks with genre from play list?" +msgstr "Entferne die Titel dieses Genres aus der Wiedergabeliste?" + +#: src/GMPlayListSource.cpp:265 +msgid "Remove tracks from artist from play list?" +msgstr "Entferne die Titel dieses Künstlers aus der Wiedergabeliste?" + +#: src/GMPlayListSource.cpp:268 +msgid "Remove tracks from album from play list?" +msgstr "Entferne die Titel dieses Albums aus der Wiedergabeliste?" + +#: src/GMPlayListSource.cpp:271 +msgid "Remove track(s) from play list?" +msgstr "Entferne die/den Titel aus der Wiedergabeliste?" + +#: src/GMPlayListSource.cpp:284 +msgid "Remove tracks from music library" +msgstr "Entferne Titel aus der Musikbibliothek" + +#: src/GMPlayListSource.cpp:406 src/GMPlayListSource.cpp:407 +msgid "Edit Playlist" +msgstr "Wiedergabeliste bearbeiten" + +#: src/GMPlayListSource.cpp:407 +msgid "Change playlist name" +msgstr "Wiedergabeliste umbenennen" + +#: src/GMPlayListSource.cpp:432 +msgid "Delete Play List?" +msgstr "Wiedergabeliste löschen?" + +#: src/GMPlayListSource.cpp:432 +msgid "Are you sure you want to delete the playlist?" +msgstr "Sind Sie sicher, dass Sie diese Wiedergabeliste löschen wollen?" + +#: src/GMPlayListSource.cpp:432 ../../../fox-1.6.37/src/FXMessageBox.cpp:111 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:116 +msgid "&Yes" +msgstr "&Ja" + +#: src/GMPlayListSource.cpp:432 ../../../fox-1.6.37/src/FXMessageBox.cpp:112 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:117 +msgid "&No" +msgstr "&Nein" + +#: src/GMPreferencesDialog.cpp:252 +msgid "Preferences" +msgstr "Einstellungen" + +#: src/GMPreferencesDialog.cpp:286 +msgid "&General" +msgstr "&Allgemein" + +#: src/GMPreferencesDialog.cpp:289 +msgid "Sort Options" +msgstr "Sortierungsoptionen" + +#: src/GMPreferencesDialog.cpp:293 +msgid "Ignore leading words" +msgstr "Führende Worte ignorieren" + +#: src/GMPreferencesDialog.cpp:296 +msgid "Album Covers" +msgstr "Albencover" + +#: src/GMPreferencesDialog.cpp:299 +msgid "Show album cover of playing track\tShow album cover of playing track" +msgstr "" +"Albumcover des abzuspielenden Titels anzeigen\tAlbumcover des abzuspielenden " +"Titels anzeigen" + +#: src/GMPreferencesDialog.cpp:300 +msgid "Show album covers in album browser\tShow album covers in album browser" +msgstr "" +"Albumcover im Albenbrowser anzeigen\tAlbumcover im Albenbrowser anzeigen" + +#: src/GMPreferencesDialog.cpp:302 +msgid "System Tray" +msgstr "Systemleiste" + +#: src/GMPreferencesDialog.cpp:304 +msgid "Show Tray Icon\tShow tray icon in the system tray." +msgstr "Systemleistensymbol anzeigen\tZeige ein Symbol in der Systemleiste an." + +#: src/GMPreferencesDialog.cpp:307 +msgid "" +"Show Track Change Notifications\tInform notification daemon of track changes." +msgstr "" +"Bei Titelwechsel benachrichtigen\tInformiere in der Systemleiste über " +"Titelwechsel." + +#: src/GMPreferencesDialog.cpp:311 +msgid "Last.fm" +msgstr "Last.fm" + +#: src/GMPreferencesDialog.cpp:315 +msgid "" +"This version of Goggles Music Manager is\n" +"not supported by Last-FM. Please upgrade\n" +"to a newer version of GMM." +msgstr "" +"Diese Version von Goggles Music Manager wird\n" +"nicht von Last-FM unterstützt. Bitte aktualisieren\n" +"Sie GMM." + +#: src/GMPreferencesDialog.cpp:321 +msgid "Service:" +msgstr "" + +#: src/GMPreferencesDialog.cpp:338 +#, fuzzy +msgid "&Sign up…" +msgstr "Bei last.fm &anmelden…" + +#: src/GMPreferencesDialog.cpp:344 +msgid "Username:" +msgstr "Benutzername:" + +#: src/GMPreferencesDialog.cpp:346 +msgid "Password:" +msgstr "Passwort:" + +#: src/GMPreferencesDialog.cpp:349 +msgid "Scrobble" +msgstr "" + +#: src/GMPreferencesDialog.cpp:359 +msgid "&Window" +msgstr "&Fenster" + +#: src/GMPreferencesDialog.cpp:362 +msgid "Window" +msgstr "Fenster" + +#: src/GMPreferencesDialog.cpp:365 +#, fuzzy +msgid "Close button minimizes to tray" +msgstr "Schließen Knopf minimiert das Hauptfenster" + +#: src/GMPreferencesDialog.cpp:366 +msgid "Show Status Bar" +msgstr "Zeige die Statusleiste" + +#: src/GMPreferencesDialog.cpp:368 +msgid "Show Icons in Track Browser" +msgstr "Zeige Symbole im Titel-Browser" + +#: src/GMPreferencesDialog.cpp:370 +msgid "Display playing track in title bar" +msgstr "Aktuellen Titel in der Titelleiste anzeigen" + +#: src/GMPreferencesDialog.cpp:372 +msgid "Player Controls" +msgstr "Player-Steuerung" + +#: src/GMPreferencesDialog.cpp:376 +msgid "Location:" +msgstr "Ort:" + +#: src/GMPreferencesDialog.cpp:378 +msgid "Top" +msgstr "Oben" + +#: src/GMPreferencesDialog.cpp:379 +msgid "Bottom" +msgstr "Unten" + +#: src/GMPreferencesDialog.cpp:387 +msgid "Title Format:" +msgstr "" + +#: src/GMPreferencesDialog.cpp:391 +msgid "Style:" +msgstr "Stil:" + +#: src/GMPreferencesDialog.cpp:392 +msgid "Show Labels" +msgstr "Beschriftungen anzeigen" + +#: src/GMPreferencesDialog.cpp:395 +msgid "Large Icons" +msgstr "Große Symbole" + +#: src/GMPreferencesDialog.cpp:399 +msgid "A&ppearance" +msgstr "&Aussehen" + +#: src/GMPreferencesDialog.cpp:402 +msgid "Colors" +msgstr "Farben" + +#: src/GMPreferencesDialog.cpp:409 +msgid "fg\tForeground Color" +msgstr "fg\tVordergrundfarbe" + +#: src/GMPreferencesDialog.cpp:410 +msgid "bg\tBackground Color" +msgstr "bg\tHintergrundfarbe" + +#: src/GMPreferencesDialog.cpp:411 +msgid "alt bg\tAlternative Background Color" +msgstr "alt bg\tAlternative Hintergrundfarbe" + +#: src/GMPreferencesDialog.cpp:422 +msgid "Normal\tNormal Text Color" +msgstr "Normal\tNormale Textfarbe" + +#: src/GMPreferencesDialog.cpp:426 +msgid "Base\tBase Color" +msgstr "Basis\tBasisfarbe" + +#: src/GMPreferencesDialog.cpp:429 +msgid "Selected\tSelected Text Color" +msgstr "Ausgewählt\tFarbe von ausgewähltem Text" + +#: src/GMPreferencesDialog.cpp:433 +#, fuzzy +msgid "Menu\tMenu Base Color" +msgstr "Menü\tFarbe des Menüs" + +#: src/GMPreferencesDialog.cpp:437 +msgid "Menu\tMenu Text Color" +msgstr "Menü\tFarbe des Menüs" + +#: src/GMPreferencesDialog.cpp:441 +msgid "Border\tBorder Color" +msgstr "Rahmen\tRahmenfarbe" + +#: src/GMPreferencesDialog.cpp:445 +msgid "Tooltip\tTooltip Color" +msgstr "Kurzinfo\tFarbe von Kurzinfos" + +#: src/GMPreferencesDialog.cpp:449 +msgid "Hilite\tHilite Color" +msgstr "Hilite\tFarbe von Markierungen" + +#: src/GMPreferencesDialog.cpp:456 +msgid "Shadow\tShadow Color" +msgstr "Schatten\tFarbe von Schatten" + +#: src/GMPreferencesDialog.cpp:459 +msgid "Playing\tPlaying Track Color" +msgstr "Spielend\tFarbe des aktuellen Titels" + +#: src/GMPreferencesDialog.cpp:463 +#, fuzzy +msgid "Tray\tTray Background Color" +msgstr "bg\tHintergrundfarbe" + +#: src/GMPreferencesDialog.cpp:473 +msgid "Presets:" +msgstr "Voreinstellungen:" + +#: src/GMPreferencesDialog.cpp:481 +msgid "Font & Icons" +msgstr "" + +#: src/GMPreferencesDialog.cpp:487 +#, fuzzy +msgid "Default Font" +msgstr "Standardwert:" + +#: src/GMPreferencesDialog.cpp:489 +msgid "Change…" +msgstr "Ändern…" + +#: src/GMPreferencesDialog.cpp:490 +msgid "Icons" +msgstr "Symbole" + +#: src/GMPreferencesDialog.cpp:509 +msgid "&Audio" +msgstr "&Audio" + +#: src/GMPreferencesDialog.cpp:512 +msgid "Engine" +msgstr "Engine" + +#: src/GMPreferencesDialog.cpp:516 +msgid "Audio Driver:" +msgstr "Audio-Treiber:" + +#: src/GMPreferencesDialog.cpp:527 +msgid "Close audio device on pause." +msgstr "Bei Pausen Audio-Gerät freigeben." + +#: src/GMPreferencesDialog.cpp:528 +msgid "Turn off playback engine on stop." +msgstr "Beim Stoppen die Wiedergabe-Engine abschalten." + +#: src/GMPreferencesDialog.cpp:529 +msgid "" +"Turn on playback engine on startup.\tFor faster startup, playback engine is " +"normally started when first track is played.\tFor faster startup, playback " +"engine is normally started when first track is played." +msgstr "" +"Starte die Wiedergabe-Engine beim Programmstart.\tUm einen schnelleren " +"Programmstart zu ermöglichen wird die Wiedergabe-Engine erst gestartet, wenn " +"der erste Titel wiedergegeben wird\tUm einen schnelleren Programmstart zu " +"ermöglichen wird die Wiedergabe-Engine erst gestartet, wenn der erste Titel " +"wiedergegeben wird" + +#: src/GMPreferencesDialog.cpp:532 +msgid "Playback" +msgstr "Wiedergabe" + +#: src/GMPreferencesDialog.cpp:536 +msgid "Replay Gain:" +msgstr "Verstärkung:" + +#: src/GMPreferencesDialog.cpp:538 +msgid "Off" +msgstr "Aus" + +#: src/GMPreferencesDialog.cpp:543 +msgid "Gapless playback" +msgstr "Lückenlose Wiedergabe" + +#: src/GMPreferencesDialog.cpp:544 +msgid "Volume Normalization" +msgstr "Lautstärke angleichen" + +#: src/GMPreferencesDialog.cpp:594 +#, c-format +msgid "Failed to open requested audio driver: %s" +msgstr "Der angeforderte Audiotreiber konnte nicht geladen werden: %s" + +#: src/GMPreferencesDialog.cpp:678 +msgid "Current" +msgstr "Aktuell" + +#: src/GMPreferencesDialog.cpp:696 +msgid "Custom" +msgstr "Benutzerdefiniert" + +#: src/GMPreferencesDialog.cpp:769 src/GMPreferencesDialog.cpp:774 +#: src/GMWindow.cpp:1133 src/GMWindow.cpp:1140 src/GMWindow.cpp:1147 +#: src/GMWindow.cpp:1154 +msgid "Unable to launch webbrowser" +msgstr "Es konnte kein Webbrowser gestartet werden" + +#: src/GMPreferencesDialog.cpp:1172 +msgid "Select Normal Font" +msgstr "Wähle normale Schriftart" + +#: src/GMRemote.cpp:295 +msgid "&Next" +msgstr "&Nächster" + +#: src/GMRemote.cpp:296 +msgid "P&revious" +msgstr "&Vorheriger" + +#: src/GMRemote.cpp:297 src/GMWindow.cpp:1197 +msgid "&Play" +msgstr "&Play" + +#: src/GMRemote.cpp:298 +msgid "&Stop" +msgstr "&Stopp" + +#: src/GMRemote.cpp:300 +msgid "Show Browser" +msgstr "Browser anzeigen" + +#: src/GMRemote.cpp:301 src/GMTrayIcon.cpp:307 +msgid "Quit" +msgstr "Beenden" + +#: src/GMRemote.cpp:385 +#, fuzzy +msgid "\tShow Browser\tShow Browser" +msgstr "Browser anzeigen" + +#: src/GMRemote.cpp:387 +msgid "\tStart Playback\tStart Playback" +msgstr "\tWiedergabe starten\tWiedergabe starten" + +#: src/GMRemote.cpp:388 +msgid "\tStop Playback\tStop Playback" +msgstr "\tWiedergabe anhalten\tWiedergabe anhalten" + +#: src/GMRemote.cpp:390 +msgid "\tPlay Previous Track\tPlay previous track." +msgstr "\tVorherigen Titel wiedergeben\tVorherigen Titel wiedergeben." + +#: src/GMRemote.cpp:391 +msgid "\tPlay Next Track\tPlay next track." +msgstr "\tNächsten Titel wiedergeben\tNächsten Titel wiedergeben." + +#: src/GMRemote.cpp:397 src/GMWindow.cpp:222 +msgid "\tAdjust Volume\tAdjust Volume" +msgstr "\tLautstärke einstellen\tLautstärke einstellen" + +#: src/GMSearch.cpp:128 +msgid "Unable to open the database" +msgstr "Die Datenbank konnte leider nicht geöffnet werden" + +#: src/GMSearch.cpp:252 +msgid "Database Error: Unable to retrieve all filenames." +msgstr "Datenbank-Fehler: Die Dateinamen konnten leider nicht gelesen werden." + +#: src/GMSearch.cpp:280 +msgid "Unable to update track" +msgstr "Der Titel konnte nicht aktualisiert werden" + +#: src/GMSearch.cpp:439 +msgid "Unable to insert track into the database" +msgstr "Der Titel konnte nicht in die Datenbank eingetragen werden" + +#: src/GMSearch.cpp:505 src/GMTrackDatabase.cpp:205 +msgid "Fatal Error" +msgstr "Schwerwiegender Fehler" + +#: src/GMSearch.cpp:505 +#, c-format +msgid "" +"%s\n" +"Please contact support if this error keeps occuring." +msgstr "" +"%s\n" +"Bitte wenden Sie sich an den Support, falls dieser Fehler öfter auftritt." + +#: src/GMSearch.cpp:534 src/GMSearch.cpp:584 +msgid "Updating Database..." +msgstr "Die Datenbank wird aktualisiert..." + +#: src/GMSearch.cpp:537 src/GMSearch.cpp:626 +msgid "Please wait. This may take a while." +msgstr "Bitte warten Sie einen Moment." + +#: src/GMSearch.cpp:555 src/GMSearch.cpp:637 +msgid "New Tracks:" +msgstr "Neue Titel:" + +#: src/GMSearch.cpp:561 src/GMSearch.cpp:643 +msgid "File:" +msgstr "Datei:" + +#: src/GMSearch.cpp:594 src/GMSearch.cpp:623 +msgid "Importing Files..." +msgstr "Dateien werden importiert..." + +#: src/GMSearch.cpp:652 +msgid "&Stop Import" +msgstr "Import &stoppen" + +#: src/GMSourceView.cpp:54 +msgid "Sources\tPress to change sorting order\tPress to change sorting order" +msgstr "" +"Quellen\tKlicken, um die Reihenfolge zu ändern\tKlicken, um die Reihenfolge " +"zu ändern" + +#: src/GMSourceView.cpp:245 src/GMWindow.cpp:261 +msgid "New Playlist…\t\tCreate a new playlist" +msgstr "Neue &Wiedergabeliste…\t\tErstelle eine neue Wiedergabeliste" + +#: src/GMSourceView.cpp:246 src/GMWindow.cpp:262 +#, fuzzy +msgid "Import Playlist…\t\tImport existing playlist" +msgstr "Neue &Wiedergabeliste…\t\tErstelle eine neue Wiedergabeliste" + +#: src/GMSourceView.cpp:247 src/GMWindow.cpp:263 +msgid "New Radio Station…\t\tCreate a new playlist" +msgstr "Neuer &Radiosender…\t\tTrage einen neuen Radiosender ein" + +#: src/GMStreamSource.cpp:64 +msgid "Station" +msgstr "Sender" + +#: src/GMStreamSource.cpp:112 src/GMStreamSource.cpp:118 +msgid "New Station…\t\t" +msgstr "Neuer Radiosender\t\t" + +#: src/GMStreamSource.cpp:117 +msgid "Edit…\t\t" +msgstr "Bearbeiten…\t\t" + +#: src/GMStreamSource.cpp:119 +msgid "Remove\t\tRemove." +msgstr "Entfernen\t\tEntfernen" + +#: src/GMStreamSource.cpp:165 src/GMStreamSource.cpp:166 +msgid "New Internet Radio Station" +msgstr "Neuer Internetradiosender" + +#: src/GMStreamSource.cpp:166 +msgid "Specify url and description of new station" +msgstr "Geben Sie die URL und eine Beschreibung des Senders ein" + +#: src/GMStreamSource.cpp:168 +#, fuzzy +msgid "C&reate" +msgstr "&Erstellen" + +#: src/GMStreamSource.cpp:173 src/GMStreamSource.cpp:211 +msgid "Location" +msgstr "Ort" + +#: src/GMStreamSource.cpp:175 src/GMStreamSource.cpp:214 +msgid "Description" +msgstr "Beschreibung" + +#: src/GMStreamSource.cpp:189 +msgid "Untitled" +msgstr "Unbenannt" + +#: src/GMStreamSource.cpp:202 src/GMStreamSource.cpp:203 +msgid "Edit Internet Radio Station" +msgstr "Bearbeite Radiosender" + +#: src/GMStreamSource.cpp:203 +msgid "Update url and description of station" +msgstr "Aktualisiere die URL und Beschreibung des Senders" + +#: src/GMStreamSource.cpp:265 +msgid "Remove Internet Radio Station(s)?" +msgstr "Radiosender entfernen?" + +#: src/GMStreamSource.cpp:266 +msgid "Remove Internet Radio Station(s) from library?" +msgstr "Radiosender aus der Bibliothek entfernen?" + +#: src/GMStreamSource.cpp:280 +#, c-format +msgid "Unable to remove station from the library." +msgstr "Der Sender konnte nicht aus der Bibliothek gelöscht werden." + +#: src/GMTrackDatabase.cpp:193 +msgid "" +"An incompatible (future) version of the database was found.\n" +"This usually happens when you try to downgrade to a older version of GMM\n" +"Press OK to continue and reset the database (all information will be " +"lost!).\n" +"Press Cancel to quit now and leave the database as is." +msgstr "" +"Es wurde eine inkompatible (neuere) Version der Datenbank gefunden.\n" +"Dieser Fehler tritt beim Verwenden einer älteren GMM Version auf.\n" +"Drücken Sie OK um fortzufahren. Dabei wird die Datenbank zrückgesetzt und " +"alle zuvor gespeicherten Informationen verworfen! \n" +"Drücken Sie Abbrechen um die Datenbank unverändert zu lassen." + +#: src/GMTrackDatabase.cpp:205 +msgid "" +"Goggles Music Manager was unable to open the database.\n" +"The database may have been corrupted. Please remove ~/.goggles/goggles.db to " +"try again.\n" +"if the error keeps occuring, please file an issue at http://code.google.com/" +"p/gogglesmm" +msgstr "" +"Google Music Manager konnte die Datenbank nicht öffnen.\n" +"Die Datenbank wurde möglicherweise beschädigt. Bitte entfernen Sie die\n" +"Datei ~/.goggles/goggles.db und versuchen Sie es erneut.\n" +"Sollte der Fehler weiterhin bestehen, senden Sie bitte einen\n" +"Fehlerbericht auf http://code.google.com/p/gogglesmm ein." + +#: src/GMTrackView.cpp:245 +msgid "\tClose Filter\tClose Filter" +msgstr "\tFilter schließen\tFilter schließen" + +#: src/GMTrackView.cpp:246 +#, fuzzy +msgid "&Find" +msgstr "Suchen" + +#: src/GMTrackView.cpp:252 +#, fuzzy +msgid "Tags\tPress to change sorting order\tPress to change sorting order" +msgstr "" +"Genres\tKlicken, um die Reihenfolge zu ändern\tKlicken, um die Reihenfolge " +"zu ändern" + +#: src/GMTrackView.cpp:256 +msgid "Artists\tPress to change sorting order\tPress to change sorting order" +msgstr "" +"Künstlername\tKlicken, um die Reihenfolge zu ändern\tKlicken, um die " +"Reihenfolge zu ändern" + +#: src/GMTrackView.cpp:260 +msgid "Albums\tPress to change sorting order\tPress to change sorting order" +msgstr "" +"Alben\tKlicken, um die Reihenfolge zu ändern\tKlicken, um die Reihenfolge zu " +"ändern" + +#: src/GMTrackView.cpp:282 src/GMWindow.cpp:299 +msgid "&Configure Columns…" +msgstr "Spalten &konfigurieren" + +#: src/GMTrackView.cpp:286 +msgid "Browse" +msgstr "Browser anzeigen" + +#: src/GMTrackView.cpp:287 +msgid "Shuffle\tCtrl-R" +msgstr "Zufällige Wiedergabe\tCtrl-R" + +#: src/GMTrackView.cpp:294 ../../../fox-1.6.37/src/FXDirSelector.cpp:388 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:734 +msgid "Reverse" +msgstr "Rückwärts" + +#: src/GMTrackView.cpp:804 +#, c-format +msgid "All %d Genres" +msgstr "Alle %d Genre" + +#: src/GMTrackView.cpp:806 +msgid "All Genres" +msgstr "Alle Genre" + +#: src/GMTrackView.cpp:832 +#, c-format +msgid "All %d Artists" +msgstr "Alle %d Künstler" + +#: src/GMTrackView.cpp:872 +#, c-format +msgid "All %d Albums" +msgstr "Alle %d Alben" + +#: src/GMTrackView.cpp:1258 +#, c-format +msgid "By %s" +msgstr "Nach %s" + +#: src/GMTrackView.cpp:1586 src/GMTrackView.cpp:1609 +msgid "Sort by Album Year" +msgstr "Sortiere nach Erscheinungsjahr" + +#: src/GMTrackView.cpp:1637 +msgid "&Columns\t\tChange Visible Columns." +msgstr "&Spalten\t\tÄndere die Spalten." + +#: src/GMTrackView.cpp:1638 +msgid "&Sort\t\tChange Sorting." +msgstr "&Sortierung\t\tSortierung ändern." + +#: src/GMTrayIcon.cpp:302 src/GMWindow.cpp:841 src/GMWindow.cpp:925 +#: src/GMWindow.cpp:948 src/GMWindow.cpp:954 +msgid "Play" +msgstr "Play" + +#: src/GMTrayIcon.cpp:303 src/GMWindow.cpp:843 +msgid "Stop" +msgstr "Stop" + +#: src/GMTrayIcon.cpp:304 +msgid "Previous Track" +msgstr "Vorherigen Titel" + +#: src/GMTrayIcon.cpp:305 +msgid "Next Track" +msgstr "Nächsten Titel" + +#: src/GMWindow.cpp:205 +msgid "Play\tStart Playback\tStart Playback" +msgstr "Play\tStarte die Wiedergabe\tStarte die Wiedergabe" + +#: src/GMWindow.cpp:205 +msgid "Pause\tPause\tPause Playback" +msgstr "Pause\tPause\tDie Wiedergabe pausieren" + +#: src/GMWindow.cpp:206 +msgid "Stop\tStop Playback\tStop Playback" +msgstr "Stopp\tDie Wiedergabe anhalten\tDie Wiedergabe anhalten" + +#: src/GMWindow.cpp:208 +msgid "Previous\tPlay Previous Track\tPlay previous track." +msgstr "Vorherigen\tVorherigen Titel wiedergeben\tVorherigen Titel wiedergeben" + +#: src/GMWindow.cpp:209 +msgid "Next\tPlay Next Track\tPlay next track." +msgstr "Nächsten\tNächsten Titel wiedergeben\tNächsten Titel wiedergeben" + +#: src/GMWindow.cpp:255 +msgid "&Music" +msgstr "&Musik" + +#: src/GMWindow.cpp:256 +msgid "Import Folder…\tCtrl-O\tImport Music from folder into Library" +msgstr "" +"Ordner &importieren…\tCtrl-O\tImportiere die Musik aus einem Ordner in die " +"Bibliothek" + +#: src/GMWindow.cpp:257 +msgid "Sync Folder…\t\tSynchronize Folder with Music in Library" +msgstr "" +"Ordner &synchronisieren…\t\tSynchronisiere die Ordner mit der Bibliothek" + +#: src/GMWindow.cpp:259 +msgid "Play File or Stream…\t\tPlay File or Stream" +msgstr "" + +#: src/GMWindow.cpp:266 +msgid "&Quit\tCtrl-Q\tQuit the application." +msgstr "B&eenden\tCtrl-Q\tBeende die Anwendung." + +#: src/GMWindow.cpp:271 +msgid "&Edit" +msgstr "B&earbeiten" + +#: src/GMWindow.cpp:272 +msgid "&Copy\tCtrl-C\tCopy Selected Tracks" +msgstr "&Kopieren\tCtrl-C\tDie ausgewählten Titel kopieren" + +#: src/GMWindow.cpp:273 +msgid "&Cut\tCtrl-X\tCut Selected Tracks" +msgstr "Auss&chneiden\tCtrl-X\tDie ausgewählten Titel ausschneiden" + +#: src/GMWindow.cpp:274 +msgid "&Paste\tCtrl-V\tPaste Clipboard Selection" +msgstr "E&infügen\tCtrl-V\tAus der Zwischenablage einfügen" + +#: src/GMWindow.cpp:276 +msgid "Find…\tCtrl-F\tShow search filter." +msgstr "Suchen…\tCtrl-F\tZeige Suchfilter." + +#: src/GMWindow.cpp:278 +msgid "Preferences…" +msgstr "Einstellungen…" + +#: src/GMWindow.cpp:282 +msgid "&View" +msgstr "&Ansicht" + +#: src/GMWindow.cpp:283 +msgid "&Browse\tCtrl-B\tShow genre artist and album browser." +msgstr "&Browser anzeigen\tCtrl-B\tZeige den Künstler- und Alben-Browser." + +#: src/GMWindow.cpp:284 +msgid "Show &Genres\tCtrl-G\tShow genre browser." +msgstr "&Genre anzeigen\tCtrl-G\tZeige den Genre-Browser." + +#: src/GMWindow.cpp:287 src/GMWindow.cpp:289 +msgid "Show &Sources\tCtrl-S\tShow source browser " +msgstr "&Quellen anzeigen\tCtrl-S\tQuellen-Browser anzeigen " + +#: src/GMWindow.cpp:294 +msgid "Show Full Screen\tF12\tToggle fullscreen mode." +msgstr "Vollbild\tF12\tVollbildmodus umschalten" + +#: src/GMWindow.cpp:296 +#, fuzzy +msgid "Show Mini Player\tCtrl-M\tToggle Mini Player." +msgstr "Fernbedienung anzeigen\tF11\tZeige ein kleines Wiedergabefenster an." + +#: src/GMWindow.cpp:300 +msgid "&Sort" +msgstr "&Sortierung" + +#: src/GMWindow.cpp:302 +msgid "&Jump to Current Track\tCtrl-J\tShow current playing track." +msgstr "Zum aktuellen Titel &springen\tCtrl-J\tZeige den aktuellen Titel an." + +#: src/GMWindow.cpp:306 +msgid "&Control" +msgstr "&Steuerung" + +#: src/GMWindow.cpp:307 +msgid "Play\tCtrl-P\tStart playback." +msgstr "Play\tCtrl-P\tStarte die Wiedergabe." + +#: src/GMWindow.cpp:308 +msgid "Stop\tCtrl-\\\tStop playback." +msgstr "Stopp\tCtrl-\\\tDie Wiedergabe anhalten." + +#: src/GMWindow.cpp:309 +msgid "Previous Track\tCtrl-[\tPlay next track." +msgstr "Vorherigen Titel\tCtrl-[\tGib den vorherigen Titel wieder." + +#: src/GMWindow.cpp:310 +msgid "Next Track\tCtrl-]\tPlay previous track." +msgstr "Nächsten Titel\tCtrl-]\tGib den nächsten Titel wieder." + +#: src/GMWindow.cpp:312 +msgid "Repeat Off\tCtrl-,\tRepeat current track." +msgstr "Wiederholung aus\tCtrl-,\tWiederhole den aktuellen Titel." + +#: src/GMWindow.cpp:313 +msgid "Repeat Track\tCtrl-.\tRepeat current track." +msgstr "Wiederholung\tCtrl-.\tWiederhole den aktuellen Titel." + +#: src/GMWindow.cpp:314 +msgid "Repeat All Tracks\tCtrl-/\tRepeat all tracks." +msgstr "Wiederhole alle Titel\tCtrl-/\tWiederhole alle Titel." + +#: src/GMWindow.cpp:315 +msgid "Repeat A-B\tCtrl-T\tRepeat section of track." +msgstr "" +"Wiederholung A-B\tCtrl-T\tWiederhole die Titel, die sich zwischen zwei " +"ausgewählten Titeln befinden" + +#: src/GMWindow.cpp:316 +msgid "Shuffle Mode\tAlt-R\tPlay tracks in random order." +msgstr "" +"Zufällige Wiedergabe\tAlt-R\tGib die Titel in eine zufälligen Reihenfolge " +"wieder" + +#: src/GMWindow.cpp:318 +msgid "Equalizer\t\t" +msgstr "Equalizer\t\t" + +#: src/GMWindow.cpp:319 +msgid "Sleep Timer\t\tSetup sleeptimer." +msgstr "Zeitschaltuhr\t\tDie Zeitschaltuhr einstellen." + +#: src/GMWindow.cpp:323 +msgid "&Help" +msgstr "&Hilfe" + +#: src/GMWindow.cpp:324 +msgid "&Homepage" +msgstr "&Homepage" + +#: src/GMWindow.cpp:325 +msgid "&Report Issue…" +msgstr "Einen Fehle&r melden…" + +#: src/GMWindow.cpp:327 +msgid "&Sign up for last.fm…" +msgstr "Bei last.fm &anmelden…" + +#: src/GMWindow.cpp:328 +msgid "" +"&Join GMM on last.fm…\t\tJoin the Goggles Music Manager group on last.fm…" +msgstr "" +"GMM auf last.fm b&eitreten…\t\tTreten Sie der Goggles Music Manager-Gruppe " +"auf last.fm bei…" + +#: src/GMWindow.cpp:330 +msgid "&About…" +msgstr "Ü&ber" + +#: src/GMWindow.cpp:842 src/GMWindow.cpp:940 +msgid "Pause" +msgstr "Pause" + +#: src/GMWindow.cpp:844 +msgid "Previous" +msgstr "Vorheriges" + +#: src/GMWindow.cpp:845 +msgid "Next" +msgstr "Nächstes" + +#: src/GMWindow.cpp:920 src/GMWindow.cpp:949 src/GMWindow.cpp:955 +msgid "Start playback." +msgstr "Starte die Wiedergabe." + +#: src/GMWindow.cpp:926 src/GMWindow.cpp:927 +#, fuzzy +msgid "Start playback" +msgstr "Starte die Wiedergabe." + +#: src/GMWindow.cpp:934 src/GMWindow.cpp:941 +msgid "Pause playback." +msgstr "Die Wiedergabe pausieren." + +#: src/GMWindow.cpp:935 +#, fuzzy +msgid "Pause playback" +msgstr "Die Wiedergabe pausieren." + +#: src/GMWindow.cpp:1052 src/GMWindow.cpp:1054 +msgid "Repeat A-B" +msgstr "Wiederhole A-B" + +#: src/GMWindow.cpp:1053 +msgid "Repeat A" +msgstr "Wiederhole A" + +#: src/GMWindow.cpp:1195 +msgid "Play File or Stream" +msgstr "" + +#: src/GMWindow.cpp:1202 +#, fuzzy +msgid "Please specify a file or url to play:" +msgstr "Bitte eine Adresse zum Abspielen eingeben" + +#: src/GMWindow.cpp:1206 +#, fuzzy +msgid "…" +msgstr "Bearbeiten…" + +#: src/GMWindow.cpp:1212 +#, fuzzy +msgid "Select File" +msgstr "Alles Auswählen" + +#: src/GMWindow.cpp:1243 +msgid "Sleep Timer" +msgstr "Zeitschaltuhr" + +#: src/GMWindow.cpp:1244 +msgid "Setup sleep timer" +msgstr "Einstellung der Zeitschaltuhr" + +#: src/GMWindow.cpp:1244 +msgid "Stop playback within a certain time" +msgstr "Die Wiedergabe nach einen bestimmten Zeit anhalten" + +#: src/GMWindow.cpp:1246 +msgid "&Start Timer" +msgstr "Die Uhr &starten" + +#: src/GMWindow.cpp:1251 +msgid "Sleep in" +msgstr "Stoppe in" + +#: src/GMWindow.cpp:1253 +msgid "hours and" +msgstr "Stunden und" + +#: src/GMWindow.cpp:1255 +msgid "minutes." +msgstr "Minuten." + +#: src/GMDatabaseSource.h:131 +msgid "Music Library" +msgstr "Musikbibliothek" + +#: src/GMStreamSource.h:57 +msgid "Internet Radio" +msgstr "Internet-Radio" + +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:122 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:127 +msgid "&Quit" +msgstr "B&eenden" + +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:133 +msgid "&Skip" +msgstr "Über&springen" + +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:134 +msgid "Skip &All" +msgstr "&Alle Überspringen" + +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:140 +msgid "&Don't Save" +msgstr "&Nicht Speichern" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:220 +msgid "\tPick color" +msgstr "\tFarbe wählen" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:229 +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:274 +msgid "\tHue, Saturation, Value" +msgstr "\tFarbton, Sättigung, Wert" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:239 +msgid "\tRed, Green, Blue" +msgstr "\tRot, Grün, Blau" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:245 +msgid "&Red:" +msgstr "&Rot:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:250 +msgid "&Green:" +msgstr "&Grün:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:255 +msgid "&Blue:" +msgstr "&Blau:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:260 +msgid "&Alpha:" +msgstr "&Alpha:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:280 +msgid "Hue:" +msgstr "Farbton:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:285 +msgid "Saturation:" +msgstr "Sättigung:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:290 +msgid "Value:" +msgstr "Wert:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:295 +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:330 +msgid "Alpha:" +msgstr "Alpha:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:309 +msgid "\tCyan, Magenta, Yellow" +msgstr "\tZyan, Magenta, Gelb" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:315 +msgid "Cyan:" +msgstr "Zyan:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:320 +msgid "Magenta:" +msgstr "Magenta:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:325 +msgid "Yellow:" +msgstr "Gelb:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:344 +msgid "\tBy Name" +msgstr "\tNach Name" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:281 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:521 +msgid "Create New Directory" +msgstr "Neues Verzeichnis anlegen" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:284 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:524 +msgid "Already Exists" +msgstr "Existiert bereits" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:288 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:528 +msgid "Cannot Create" +msgstr "Kann nicht erstellt werden" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:309 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:595 +msgid "Copy File" +msgstr "Kopiere Datei" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:315 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:601 +msgid "Error Copying File" +msgstr "Fehler während des Kopierens" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:326 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:618 +msgid "Move File" +msgstr "Verschiebe Datei" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:332 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:624 +msgid "Error Moving File" +msgstr "Fehler während des Verschiebens" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:343 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:641 +msgid "Link File" +msgstr "Verknüpfung erstellen" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:349 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:647 +msgid "Error Linking File" +msgstr "Fehler während der Erstellung der Verknüpfung" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:359 +msgid "Deleting file" +msgstr "Datei löschen" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:361 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:667 +msgid "Error Deleting File" +msgstr "Fehler während dem Löschen" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:381 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:719 +msgid "Up one level" +msgstr "Eine Ebene nach oben" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:382 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:720 +msgid "Home directory" +msgstr "Heimverzeichnis" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:383 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:721 +msgid "Work directory" +msgstr "Arbeitsverzeichnis" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:387 +msgid "Sorting" +msgstr "Sortierung" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:389 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:735 +msgid "Ignore case" +msgstr "Groß-/Kleinschreibung ignorieren" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:390 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:746 +msgid "Hidden files" +msgstr "Versteckte Dateien" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:393 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:754 +msgid "Bookmarks" +msgstr "Lesezeichen" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:394 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:757 +msgid "Set bookmark" +msgstr "Lesezeichen erstellen" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:395 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:758 +msgid "Clear bookmarks" +msgstr "Lesezeichen leeren" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:411 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:774 +msgid "New directory..." +msgstr "Neues Verzeichnis..." + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:412 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:775 +msgid "Copy..." +msgstr "Kopieren..." + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:413 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:776 +msgid "Move..." +msgstr "Verschieben..." + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:414 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:777 +msgid "Link..." +msgstr "Verknüpfung erstellen..." + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:415 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:778 +msgid "Delete..." +msgstr "Löschen..." + +#: ../../../fox-1.6.37/src/FXFileList.cpp:195 +msgid "Modified Date" +msgstr "Änderungsdatum" + +#: ../../../fox-1.6.37/src/FXFileList.cpp:196 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:731 +msgid "User" +msgstr "Benutzer" + +#: ../../../fox-1.6.37/src/FXFileList.cpp:197 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:732 +msgid "Group" +msgstr "Gruppe" + +#: ../../../fox-1.6.37/src/FXFileList.cpp:198 +msgid "Attributes" +msgstr "Attribute" + +#: ../../../fox-1.6.37/src/FXFileList.cpp:200 +msgid "Link" +msgstr "Verknüpfung" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:521 +msgid "Create new directory with name: " +msgstr "Erstelle neues Verzeichnis mit dem Namen: " + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:524 +#, c-format +msgid "File or directory %s already exists.\n" +msgstr "Datei oder Verzeichnis %s existiert bereits.\n" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:528 +#, c-format +msgid "Cannot create directory %s.\n" +msgstr "Das Verzeichnis %s kann nicht erstellt werden.\n" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:594 +#, c-format +msgid "" +"Copy file from location:\n" +"\n" +"%s\n" +"\n" +"to location: " +msgstr "" +"Kopiere Datei von:\n" +"\n" +"%s\n" +"\n" +"nach: " + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:601 +#, c-format +msgid "" +"Unable to copy file:\n" +"\n" +"%s to: %s\n" +"\n" +"Continue with operation?" +msgstr "" +"Konnte die Datei nicht von:\n" +"\n" +"%s nach: %s kopieren.\n" +"\n" +"Trotzdem fortfahren?" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:617 +#, c-format +msgid "" +"Move file from location:\n" +"\n" +"%s\n" +"\n" +"to location: " +msgstr "" +"Verschiebe Datei von:\n" +"\n" +"%s\n" +"\n" +"nach: " + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:624 +#, c-format +msgid "" +"Unable to move file:\n" +"\n" +"%s to: %s\n" +"\n" +"Continue with operation?" +msgstr "" +"Konnte die Datei nicht von:\n" +"\n" +"%s nach: %s verschieben.\n" +"\n" +"Trotzdem fortfahren?" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:640 +#, c-format +msgid "" +"Link file from location:\n" +"\n" +"%s\n" +"\n" +"to location: " +msgstr "" +"Erstelle Verknüpfung\n" +"\n" +"%s\n" +"\n" +"mit: " + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:647 +#, c-format +msgid "" +"Unable to link file:\n" +"\n" +"%s to: %s\n" +"\n" +"Continue with operation?" +msgstr "" +"Konnte keine Verknüpfung\n" +"\n" +"%s mit: %s erstellen.\n" +"\n" +"Trotzdem fortfahren?" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:663 +msgid "Deleting files" +msgstr "Dateien werden gelöscht" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:663 +#, c-format +msgid "" +"Are you sure you want to delete the file:\n" +"\n" +"%s" +msgstr "" +"Sind Sie sicher, dass Sie die folgende Datei löschen wollen:\n" +"\n" +"%s" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:667 +#, c-format +msgid "" +"Unable to delete file:\n" +"\n" +"%s\n" +"\n" +"Continue with operation?" +msgstr "" +"Die Datei:\n" +"\n" +"%s konnte nicht gelöscht werden.\n" +"\n" +"Trotzdem fortfahren?" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:722 +msgid "Select all" +msgstr "Alles Auswählen" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:726 +msgid "Sort by" +msgstr "Sortieren nach" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:738 +msgid "View" +msgstr "Ansicht" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:739 +msgid "Small icons" +msgstr "Kleine Symbole" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:740 +msgid "Big icons" +msgstr "Große Symbole" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:741 +msgid "Details" +msgstr "Details" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:743 +msgid "Rows" +msgstr "Zeilen" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:744 +msgid "Columns" +msgstr "Spalten" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:747 +msgid "Preview images" +msgstr "Vorschaubild" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:749 +msgid "Normal images" +msgstr "Normalgroße Bilder" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:750 +msgid "Medium images" +msgstr "Mittelgroße Bilder" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:751 +msgid "Giant images" +msgstr "Große Bilder" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:111 +msgid "&Replace" +msgstr "E&rsetzen" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:112 +msgid "Re&place All" +msgstr "Alle Erset&zen" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:121 +msgid "S&earch for:" +msgstr "Such&en nach:" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:129 +msgid "Replace &with:" +msgstr "Ersetzen &mit:" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:138 +msgid "Ex&act" +msgstr "Extr&ahieren" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:139 +msgid "&Ignore Case" +msgstr "Groß-/Kleinschreibung ignorieren" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:140 +msgid "E&xpression" +msgstr "Ausdrücke" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:141 +msgid "&Backward" +msgstr "&Rückwärts" + +#: ../../../fox-1.6.37/src/FXSearchDialog.cpp:71 +msgid "&Search" +msgstr "&Suchen" + +#: ../../../fox-1.6.37/src/FXStatusLine.cpp:82 +msgid "Ready." +msgstr "Bereit." + +#~ msgid "Old Name" +#~ msgstr "Alter Name" + +#~ msgid "New Name" +#~ msgstr "Neuer Name" + +#~ msgid "Pitch:" +#~ msgstr "Abstand:" + +#~ msgid "Any" +#~ msgstr "Beliebig" + +#~ msgid "Fixed" +#~ msgstr "Fest" + +#~ msgid "Variable" +#~ msgstr "Variabel" + +#, fuzzy +#~ msgid " Type:" +#~ msgstr "Typ" + +#, fuzzy +#~ msgid "Scalable" +#~ msgstr "Skalierbar:" + +#, fuzzy +#~ msgid "Size:" +#~ msgstr "Größe" + +#, fuzzy +#~ msgid "Family:" +#~ msgstr "&Familie:" + +#~ msgid "Always Show Remote" +#~ msgstr "Die Fernbedienung immer anzeigen" + +#~ msgid "Source\tActive Source Color" +#~ msgstr "Quelle\tFarbe der aktiven Quelle" + +#~ msgid "About" +#~ msgstr "Über" + +#~ msgid "" +#~ "An incompatible version of SQLite (%s) is being used.\n" +#~ "Goggles Music Manager requires at least SQLite 3.3.8.\n" +#~ "Please upgrade your SQLite installation." +#~ msgstr "" +#~ "Eine inkompatible SQLite-Version (%s) wird benutzt.\n" +#~ "Goggles Music Manager benötigt mindestens Version 3.3.8.\n" +#~ "Bitte aktualisieren Sie ihre Version von SQLite." + +#~ msgid "" +#~ "This version of SQLite (%s) is broken.\n" +#~ "Please upgrade your SQLite installation to at least 3.6.3." +#~ msgstr "" +#~ "Diese SQLite-Version (%s) ist defekt.\n" +#~ "Bitte aktualisieren Sie SQLite auf mindestens Version 3.6.3." + +#~ msgid "" +#~ "&Join GMM on last.fm…\tJoin the Goggles Music Manager group on last.fm…" +#~ "\tJoin the Goggles Music Manager group on last.fm…" +#~ msgstr "" +#~ "GMM auf last.fm &beitreten…\tTreten Sie der Goggles Music Manager-Gruppe " +#~ "auf last.fm bei…\tTreten Sie der Goggles Music Manager-Gruppe auf last.fm " +#~ "bei…" + +#~ msgid "Font" +#~ msgstr "Schrift" + +#~ msgid "Theme Directory:" +#~ msgstr "Themenverzeichnis:" + +#~ msgid "Select Theme Directory" +#~ msgstr "Themenverzeichnis wählen" + +#~ msgid "thin" +#~ msgstr "dünn" + +#~ msgid "normal" +#~ msgstr "normal" + +#~ msgid "bold" +#~ msgstr "fett" + +#~ msgid "regular" +#~ msgstr "regulär" + +#~ msgid "&Weight:" +#~ msgstr "&Breite:" + +#~ msgid "&Style:" +#~ msgstr "&Stil" + +#~ msgid "Si&ze:" +#~ msgstr "&Größe" + +#~ msgid "Character Set:" +#~ msgstr "Zeichensatz:" + +#~ msgid "West European" +#~ msgstr "West-Europäisch" + +#~ msgid "East European" +#~ msgstr "Ost-Europäisch" + +#~ msgid "South European" +#~ msgstr "Süd-Europäisch" + +#~ msgid "North European" +#~ msgstr "Nord-Europäisch" + +#~ msgid "Cyrillic" +#~ msgstr "Kyrillisch" + +#~ msgid "Arabic" +#~ msgstr "Arabisch" + +#~ msgid "Greek" +#~ msgstr "Griechisch" + +#~ msgid "Hebrew" +#~ msgstr "Hebräisch" + +#~ msgid "Turkish" +#~ msgstr "Türkisch" + +#~ msgid "Thai" +#~ msgstr "Thai" + +#~ msgid "Baltic" +#~ msgstr "Baltisch" + +#~ msgid "Russian" +#~ msgstr "Russisch" + +#~ msgid "Central European (cp1250)" +#~ msgstr "Zentral-Europäisch (cp1250)" + +#~ msgid "Russian (cp1251)" +#~ msgstr "Russisch (cp1251)" + +#~ msgid "Latin1 (cp1252)" +#~ msgstr "Latin1 (cp1252)" + +#~ msgid "Greek (cp1253)" +#~ msgstr "Griechisch (cp1253)" + +#~ msgid "Turkish (cp1254)" +#~ msgstr "Türkisch (cp1254)" + +#~ msgid "Hebrew (cp1255)" +#~ msgstr "Hebräisch (cp1255)" + +#~ msgid "Arabic (cp1256)" +#~ msgstr "Arabisch (cp1256)" + +#~ msgid "Baltic (cp1257)" +#~ msgstr "Baltisch (cp1257)" + +#~ msgid "Vietnam (cp1258)" +#~ msgstr "Vietnamesisch (cp1258)" + +#~ msgid "Thai (cp874)" +#~ msgstr "Thai (cp874)" + +#~ msgid "UNICODE" +#~ msgstr "UNICODE" + +#~ msgid "Set Width:" +#~ msgstr "Wähle Breite:" + +#~ msgid "All Fonts:" +#~ msgstr "Alle Schriftarten:" + +#~ msgid "Preview:" +#~ msgstr "Vorschau:" + +#, fuzzy +#~ msgid "Import Playlist…\t\tImport a existing playlist" +#~ msgstr "Neue &Wiedergabeliste…\t\tErstelle eine neue Wiedergabeliste" + +#~ msgid "Import Files?" +#~ msgstr "Dateien importieren?" + +#~ msgid "" +#~ "Would you like import the pasted files and/or directories into the Music " +#~ "Library?" +#~ msgstr "" +#~ "Möchten Sie die eingefügten Dateien und/oder Verzeichnisse in die " +#~ "Musikbibliothek importieren?" + +#~ msgid "" +#~ "%s\n" +#~ "by: %s\n" +#~ "from: %s" +#~ msgstr "" +#~ "%s\n" +#~ "von: %s\n" +#~ "aus: %s" + +#~ msgid "Now Playing" +#~ msgstr "Aktueller Titel" + +#~ msgid "Open URL…\t\tOpen Stream or File" +#~ msgstr "URL ö&ffnen…\t\tInternetstream oder Datei öffnen" + +#~ msgid "Open MRL" +#~ msgstr "MRL öffnen" + +#~ msgid "P&ause" +#~ msgstr "P&ause" + +#~ msgid "\tPause\tPause Playback" +#~ msgstr "\tPause\tWiedergabe pausieren" + +#~ msgid "A capella" +#~ msgstr "A capella" + +#~ msgid "Acid" +#~ msgstr "Acid" + +#~ msgid "Acid Jazz" +#~ msgstr "Acid Jazz" + +#~ msgid "Acid Punk" +#~ msgstr "Acid Punk" + +#~ msgid "Acoustic" +#~ msgstr "Akustisch" + +#~ msgid "Alternative" +#~ msgstr "Alternative" + +#~ msgid "Avantgarde" +#~ msgstr "Avantgarde" + +#~ msgid "Ballad" +#~ msgstr "Ballade" + +#~ msgid "Bass" +#~ msgstr "Bass" + +#~ msgid "Bebob" +#~ msgstr "Bebob" + +#~ msgid "Big Band" +#~ msgstr "Big Band" + +#~ msgid "Blues" +#~ msgstr "Blues" + +#~ msgid "Bluegrass" +#~ msgstr "Bluegrass" + +#~ msgid "Booty Bass" +#~ msgstr "Booty Bass" + +#~ msgid "Cabaret" +#~ msgstr "Kabarett" + +#~ msgid "Chamber Music" +#~ msgstr "Kammermusik" + +#~ msgid "Chanson" +#~ msgstr "Chanson" + +#~ msgid "Chorus" +#~ msgstr "Chor" + +#~ msgid "Classical" +#~ msgstr "Klassik" + +#~ msgid "Classic Rock" +#~ msgstr "Classic Rock" + +#~ msgid "Club" +#~ msgstr "Club" + +#~ msgid "Comedy" +#~ msgstr "Comedy" + +#~ msgid "Country" +#~ msgstr "Country" + +#~ msgid "Dance" +#~ msgstr "Dance" + +#~ msgid "Dance Hall" +#~ msgstr "Dance Hall" + +#~ msgid "Darkwave" +#~ msgstr "Darkwave" + +#~ msgid "Death Metal" +#~ msgstr "Death Metal" + +#~ msgid "Disco" +#~ msgstr "Disco" + +#~ msgid "Drum Solo" +#~ msgstr "Schlagzeugsolo" + +#~ msgid "Duet" +#~ msgstr "Duet" + +#~ msgid "Easy Listening" +#~ msgstr "Easy Listening" + +#~ msgid "Electronic" +#~ msgstr "Elektronische Musik" + +#~ msgid "Folk" +#~ msgstr "Folk" + +#~ msgid "Folk-Rock" +#~ msgstr "Folk-Rock" + +#~ msgid "Folklore" +#~ msgstr "Folklore" + +#~ msgid "Funk" +#~ msgstr "Funk" + +#~ msgid "Gospel" +#~ msgstr "Gospel" + +#~ msgid "Gothic" +#~ msgstr "Gothic" + +#~ msgid "Gothic Rock" +#~ msgstr "Gothic Rock" + +#~ msgid "Grunge" +#~ msgstr "Grunge" + +#~ msgid "Hard Rock" +#~ msgstr "Hard Rock" + +#~ msgid "Hip-Hop" +#~ msgstr "Hip-Hop" + +#~ msgid "House" +#~ msgstr "House" + +#~ msgid "Industrial" +#~ msgstr "Industrial" + +#~ msgid "Instrumental" +#~ msgstr "Instrumental" + +#~ msgid "Jazz" +#~ msgstr "Jazz" + +#~ msgid "Jazz+Funk" +#~ msgstr "Jazz+Funk" + +#~ msgid "Meditative" +#~ msgstr "Meditativ" + +#~ msgid "Metal" +#~ msgstr "Metal" + +#~ msgid "Musical" +#~ msgstr "Musical" + +#~ msgid "New Age" +#~ msgstr "New Age" + +#~ msgid "New Wave" +#~ msgstr "New Wave" + +#~ msgid "Noise" +#~ msgstr "Noise" + +#~ msgid "Oldies" +#~ msgstr "Oldies" + +#~ msgid "Opera" +#~ msgstr "Oper" + +#~ msgid "Other" +#~ msgstr "Andere" + +#~ msgid "Polka" +#~ msgstr "Polka" + +#~ msgid "Pop" +#~ msgstr "Pop" + +#~ msgid "Pop-Folk" +#~ msgstr "Pop-Folk" + +#~ msgid "Pop/Funk" +#~ msgstr "Pop/Funk" + +#~ msgid "Progressive Rock" +#~ msgstr "Progressive Rock" + +#~ msgid "Psychadelic" +#~ msgstr "Psychadelic" + +#~ msgid "Psychedelic Rock" +#~ msgstr "Psychedelic Rock" + +#~ msgid "Punk" +#~ msgstr "Punk" + +#~ msgid "Punk Rock" +#~ msgstr "Punk Rock" + +#~ msgid "R&B" +#~ msgstr "R&B" + +#~ msgid "Rap" +#~ msgstr "Rap" + +#~ msgid "Rave" +#~ msgstr "Rave" + +#~ msgid "Reggae" +#~ msgstr "Reggae" + +#~ msgid "Retro" +#~ msgstr "Retro" + +#~ msgid "Revival" +#~ msgstr "Revival" + +#~ msgid "Rock" +#~ msgstr "Rock" + +#~ msgid "Rock & Roll" +#~ msgstr "Rock & Roll" + +#~ msgid "Samba" +#~ msgstr "Samba" + +#~ msgid "Satire" +#~ msgstr "Satire" + +#~ msgid "Ska" +#~ msgstr "Ska" + +#~ msgid "Sonata" +#~ msgstr "Sonate" + +#~ msgid "Soul" +#~ msgstr "Soul" + +#~ msgid "Soundtrack" +#~ msgstr "Soundtrack" + +#~ msgid "Speech" +#~ msgstr "Sprache" + +#~ msgid "Swing" +#~ msgstr "Swing" + +#~ msgid "Symphony" +#~ msgstr "Symphonie" + +#~ msgid "Tango" +#~ msgstr "Tango" + +#~ msgid "Techno" +#~ msgstr "Techno" + +#~ msgid "Top 40" +#~ msgstr "Top 40" + +#~ msgid "Trailer" +#~ msgstr "Trailer" + +#~ msgid "Trance" +#~ msgstr "Trance" + +#~ msgid "Tribal" +#~ msgstr "Tribal" + +#~ msgid "Trip-Hop" +#~ msgstr "Trip-Hop" + +#~ msgid "Vocal" +#~ msgstr "Gesang" + +#~ msgid "Start Up:" +#~ msgstr "Einschalten:" + +#~ msgid "Show Main Window" +#~ msgstr "Zeige Hauptfenster" + +#~ msgid "Show Mini Remote" +#~ msgstr "Zeige Fernbedienung" + +#~ msgid "Previous View" +#~ msgstr "Vorherige Ansicht" + +#~ msgid "Configure Columns…" +#~ msgstr "Spalten &konfigurieren…" + +#~ msgid "Yes" +#~ msgstr "Ja" + +#~ msgid "Edit…\tF2\tEdit Genre." +#~ msgstr "Bearbeiten…\tF2\tGenre bearbeiten." + +#~ msgid "Edit…\tF2\tEdit Artist." +#~ msgstr "Bearbeiten…\tF2\tKünstlername bearbeiten." + +#~ msgid "Edit…\tF2\tEdit Album." +#~ msgstr "Bearbeiten…\tF2\tAlbumname bearbeiten." diff --git a/po/es.mo b/po/es.mo new file mode 100644 index 0000000000000000000000000000000000000000..5f84ac16d6b03d3bafb075dc57b3322ed91a722e GIT binary patch literal 39617 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 VTA*hSTvZD=I^eJEAo5kk{{{V@8pZ$s literal 0 HcmV?d00001 diff --git a/po/es.po b/po/es.po new file mode 100644 index 0000000..3715ded --- /dev/null +++ b/po/es.po @@ -0,0 +1,3018 @@ +# Copyright (C) YEAR Sander Jansen +# This file is distributed under the same license as the PACKAGE package. +# +# Víctor Pérez Masegosa 2009. +# Víctor Pérez Masegosa , 2011. +msgid "" +msgstr "" +"Project-Id-Version: gogglesmm 0.10.x\n" +"Report-Msgid-Bugs-To: s.jansen@gmail.com\n" +"POT-Creation-Date: 2011-02-09 23:26-0600\n" +"PO-Revision-Date: 2011-01-23 13:33+0100\n" +"Last-Translator: Víctor Pérez Masegosa \n" +"Language-Team: Spanish\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Lokalize 1.1\n" + +#: src/GMAbout.cpp:136 src/GMDatabaseSource.cpp:218 +#: src/GMDatabaseSource.cpp:2215 src/GMPreferencesDialog.cpp:551 +msgid "&Close" +msgstr "&Cerrar" + +#: src/GMColumnDialog.cpp:204 +msgid "Configure Columns" +msgstr "Configurar columnas" + +#: src/GMColumnDialog.cpp:207 ../../../fox-1.6.37/src/FXColorSelector.cpp:168 +msgid "&Accept" +msgstr "&Aceptar" + +#: src/GMColumnDialog.cpp:208 src/GMDatabaseSource.cpp:98 +#: src/GMDatabaseSource.cpp:1274 src/GMDatabaseSource.cpp:1704 +#: src/GMDatabaseSource.cpp:1804 src/GMDatabaseSource.cpp:1878 +#: src/GMDatabaseSource.cpp:2121 src/GMDatabaseSource.cpp:2182 +#: src/GMImportDialog.cpp:175 src/GMImportDialog.cpp:563 +#: src/GMPlayListSource.cpp:281 src/GMPlayListSource.cpp:410 +#: src/GMSearch.cpp:572 src/GMStreamSource.cpp:169 src/GMStreamSource.cpp:206 +#: src/GMStreamSource.cpp:271 src/GMWindow.cpp:1198 src/GMWindow.cpp:1247 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:107 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:118 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:123 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:129 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:135 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:142 +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:169 +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:135 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:205 +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:113 +msgid "&Cancel" +msgstr "&Cancelar" + +#: src/GMColumnDialog.cpp:212 +msgid "" +"Choose the order of information to appear\n" +"in the track list." +msgstr "" +"Elije en qué orden aparecerá la información\n" +"en la lista de reproducción." + +#: src/GMColumnDialog.cpp:218 +msgid "Move Up" +msgstr "Mover arriba" + +#: src/GMColumnDialog.cpp:219 +msgid "Move Down" +msgstr "Mover abajo" + +#: src/GMDatabaseSource.cpp:52 +msgid "Invalid Template" +msgstr "Plantilla inválida" + +#: src/GMDatabaseSource.cpp:52 +#, c-format +msgid "" +"The provided template is invalid. The track title %%T needs to be " +"specified.\n" +"Please fix the filename template in the preference panel." +msgstr "" +"La plantilla dada es inválida. El título de la pista %%T necesita ser " +"especificado.\n" +"Por favor, arregle el nombre de la plantilla en el panel de preferencias." + +#: src/GMDatabaseSource.cpp:72 src/GMTrackDatabase.cpp:193 +msgid "Database Error" +msgstr "Error de base de datos" + +#: src/GMDatabaseSource.cpp:72 +msgid "Oops. Database Error" +msgstr "Oops. Error de base de datos" + +#: src/GMDatabaseSource.cpp:87 +msgid "No changes" +msgstr "Sin cambios" + +#: src/GMDatabaseSource.cpp:87 +msgid "Filenames did not require any changes" +msgstr "Los nombres de los archivos no requirieron cambios" + +#: src/GMDatabaseSource.cpp:94 +msgid "Rename Audio Files?" +msgstr "¿Renombrar archivos de audio?" + +#: src/GMDatabaseSource.cpp:95 +msgid "Renaming Audio Files…" +msgstr "Renombrando archivos de audio..." + +#: src/GMDatabaseSource.cpp:95 +msgid "The following audio files are going to be renamed" +msgstr "Los siguientes archivos de audio van a ser renombrados." + +#: src/GMDatabaseSource.cpp:97 +msgid "&Rename" +msgstr "&Renombrar" + +#: src/GMDatabaseSource.cpp:121 src/GMDatabaseSource.cpp:125 +msgid "Unable to rename file" +msgstr "No se pudo renombrar el archivo" + +#: src/GMDatabaseSource.cpp:121 +#, c-format +msgid "" +"Unable to rename:\n" +"%s\n" +"\n" +"to:%s\n" +"Continue renaming files?" +msgstr "" +"No se pudo renombrar el archivo:\n" +"%s\n" +"\n" +"a:%s\n" +"¿Continuar renombrando archivos?" + +#: src/GMDatabaseSource.cpp:125 +#, c-format +msgid "" +"Unable to rename:\n" +"%s\n" +"\n" +"to:%s" +msgstr "" +"No se pudo renombrar el archivo:\n" +"%s\n" +"\n" +"a:%s" + +#: src/GMDatabaseSource.cpp:160 src/GMImportDialog.cpp:534 +msgid "Filename Template" +msgstr "Plantilla de nombres de archivo" + +#: src/GMDatabaseSource.cpp:177 +msgid "" +"Template may contain absolute or relative path, environment variables\n" +"and ~. Relative paths are based on the location of the original file. The\n" +"file extension gets automatically added. The following macros\n" +"may be used:" +msgstr "" +"La plantilla puede contener rutas relativas o absolutas, variables de " +"entorno\n" +"y ~. Las rutas relativas están basadas en la localización del archivo " +"original. La\n" +"extensión del archivo queda automáticamente añadida. Las siguientes macros\n" +"pueden ser usadas:" + +#: src/GMDatabaseSource.cpp:178 +msgid "" +"%T - title %A - album name\n" +"%P - album artist name %p - track artist name\n" +"%y - year %d - disc number\n" +"%N - track number (2 digits) %n - track number \n" +"%G - genre" +msgstr "" +"%T - título %A - nombre del album\n" +"%P - nombre del artista del album %p - nombre del artista de la pista\n" +"%y - año %d - número del disco\n" +"%N - número de pista (2 dígitos) %n - número de pista \n" +"%G - género" + +#: src/GMDatabaseSource.cpp:185 +msgid "Conditions may be used as well:" +msgstr "También pueden ser usadas condiciones:" + +#: src/GMDatabaseSource.cpp:186 +msgid "" +"?c - display a if c is not empty else display b.\n" +"?c - display c if not empty\n" +msgstr "" +"?c - mostrar a si c no está vacío. Si no, mostrar b.\n" +"?c - mostrar c si no está vacío\n" + +#: src/GMDatabaseSource.cpp:195 src/GMDatabaseSource.cpp:1815 +#: src/GMImportDialog.cpp:546 +msgid "Template:" +msgstr "Plantilla:" + +#: src/GMDatabaseSource.cpp:199 src/GMDatabaseSource.cpp:1819 +msgid "Encoding:" +msgstr "Encodificado:" + +#: src/GMDatabaseSource.cpp:205 +msgid "Exclude:" +msgstr "Excluir:" + +#: src/GMDatabaseSource.cpp:209 src/GMDatabaseSource.cpp:1825 +msgid "Options:" +msgstr "Opciones:" + +#: src/GMDatabaseSource.cpp:210 src/GMDatabaseSource.cpp:1826 +msgid "Replace spaces with underscores" +msgstr "Reemplazar espacios por guiones bajos" + +#: src/GMDatabaseSource.cpp:212 src/GMDatabaseSource.cpp:1828 +msgid "Lower case" +msgstr "Minúscula" + +#: src/GMDatabaseSource.cpp:214 src/GMDatabaseSource.cpp:1830 +msgid "Lower case extension" +msgstr "Extensión en minúscula" + +#: src/GMDatabaseSource.cpp:341 src/GMStreamSource.cpp:63 +msgid "No." +msgstr "No." + +#: src/GMDatabaseSource.cpp:342 +msgid "Queue" +msgstr "Cola" + +#: src/GMDatabaseSource.cpp:343 src/GMDatabaseSource.cpp:1391 +#: src/GMTrackView.cpp:238 +msgid "Title" +msgstr "Título" + +#: src/GMDatabaseSource.cpp:344 src/GMDatabaseSource.cpp:1396 +#: src/GMTrackView.cpp:239 +msgid "Artist" +msgstr "Artista" + +#: src/GMDatabaseSource.cpp:345 src/GMDatabaseSource.cpp:1397 +msgid "Album Artist" +msgstr "Artista del álbum" + +#: src/GMDatabaseSource.cpp:346 src/GMDatabaseSource.cpp:1405 +#: src/GMPreferencesDialog.cpp:540 src/GMTrackView.cpp:240 +msgid "Album" +msgstr "Álbum" + +#: src/GMDatabaseSource.cpp:347 src/GMDatabaseSource.cpp:1364 +#: src/GMDatabaseSource.cpp:1375 src/GMDatabaseSource.cpp:1381 +msgid "Disc" +msgstr "Disco" + +#: src/GMDatabaseSource.cpp:348 src/GMDatabaseSource.cpp:1408 +#: src/GMStreamSource.cpp:66 src/GMStreamSource.cpp:177 +#: src/GMStreamSource.cpp:216 src/GMTrackView.cpp:241 +msgid "Genre" +msgstr "Género" + +#: src/GMDatabaseSource.cpp:349 src/GMDatabaseSource.cpp:1369 +#: src/GMDatabaseSource.cpp:1387 +msgid "Year" +msgstr "Año" + +#: src/GMDatabaseSource.cpp:350 ../../../fox-1.6.37/src/FXFileSelector.cpp:730 +msgid "Time" +msgstr "Duración" + +#: src/GMDatabaseSource.cpp:490 +msgid "Remove…\tDel\tRemove Genre from Library." +msgstr "Eliminar...\tSupr\tEliminar género de la biblioteca." + +#: src/GMDatabaseSource.cpp:496 src/GMDatabaseSource.cpp:505 +#: src/GMPlayListSource.cpp:104 src/GMPlayListSource.cpp:110 +msgid "Copy\tCtrl-C\tCopy associated tracks to the clipboard." +msgstr "Copiar\tCtrl-C\tCopiar pistas asociadas al portapapeles." + +#: src/GMDatabaseSource.cpp:499 src/GMDatabaseSource.cpp:508 +msgid "Remove…\tDel\tRemove associated tracks from library." +msgstr "Eliminar...\tDel\tEliminar pistas asociadas de la biblioteca." + +#: src/GMDatabaseSource.cpp:513 src/GMPlayListSource.cpp:116 +msgid "Edit…\tF2\tEdit Track Information." +msgstr "Editar...\tF2\tEditar información de la pista." + +#: src/GMDatabaseSource.cpp:514 src/GMPlayListSource.cpp:117 +msgid "Copy\tCtrl-C\tCopy track(s) to clipboard." +msgstr "Copiar\tCtrl-C\tCopiar pista(s) al portapapeles." + +#: src/GMDatabaseSource.cpp:518 src/GMPlayListSource.cpp:120 +msgid "Open Folder Location\t\tOpen Folder Location." +msgstr "Abrir ubicación de la carpeta\t\tAbrir ubicación de la carpeta." + +#: src/GMDatabaseSource.cpp:523 +msgid "Remove…\tDel\tRemove track(s) from library." +msgstr "Eliminar…\tSupr\tEliminar pista(s) de la biblioteca." + +#: src/GMDatabaseSource.cpp:537 +msgid "New Play List…\t\tCreate a new play list." +msgstr "Nueva lista de reproducción...\t\tCrear nueva lista de reproducción." + +#: src/GMDatabaseSource.cpp:539 src/GMPlayListSource.cpp:163 +msgid "Export…" +msgstr "Exportar..." + +#: src/GMDatabaseSource.cpp:540 +msgid "Information…\t\tLibrary Statistics" +msgstr "Información...\t\tEstadísticas de la biblioteca" + +#: src/GMDatabaseSource.cpp:542 +msgid "Remove All Tracks\t\tRemove all tracks from the library" +msgstr "" +"Eliminar todas las pistas\t\tEliminar todas las pistas de la biblioteca" + +#: src/GMDatabaseSource.cpp:1272 +msgid "Edit Track Information" +msgstr "Editar la información de la pista" + +#: src/GMDatabaseSource.cpp:1275 src/GMPlayListSource.cpp:409 +#: src/GMStreamSource.cpp:205 ../../../fox-1.6.37/src/FXMessageBox.cpp:128 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:143 +msgid "&Save" +msgstr "&Guardar" + +#: src/GMDatabaseSource.cpp:1315 +msgid "&Tag" +msgstr "" + +#: src/GMDatabaseSource.cpp:1320 +#, fuzzy +msgid "&Properties" +msgstr "Propiedades" + +#: src/GMDatabaseSource.cpp:1325 src/GMImportDialog.cpp:525 +msgid "Filename" +msgstr "Nombre de archivo" + +#: src/GMDatabaseSource.cpp:1329 ../../../fox-1.6.37/src/FXFileList.cpp:193 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:728 +msgid "Type" +msgstr "Tipo" + +#: src/GMDatabaseSource.cpp:1333 ../../../fox-1.6.37/src/FXFileList.cpp:194 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:729 +msgid "Size" +msgstr "Tamaño" + +#: src/GMDatabaseSource.cpp:1341 src/GMStreamSource.cpp:65 +msgid "Bitrate" +msgstr "Tasa de bits" + +#: src/GMDatabaseSource.cpp:1345 +msgid "Samplerate" +msgstr "Tasa de muestreo" + +#: src/GMDatabaseSource.cpp:1349 +msgid "Channels" +msgstr "Canales" + +#: src/GMDatabaseSource.cpp:1358 src/GMPreferencesDialog.cpp:539 +msgid "Track" +msgstr "Pista" + +#: src/GMDatabaseSource.cpp:1403 +msgid "\tSeparate Artists" +msgstr "\tSeparar artistas" + +#: src/GMDatabaseSource.cpp:1403 +msgid "\tShared Artists" +msgstr "\tArtistas compartidos" + +#: src/GMDatabaseSource.cpp:1416 +msgid "Auto track number. Offset:" +msgstr "Número de pista automático. Compensación:" + +#: src/GMDatabaseSource.cpp:1425 +msgid "Update Tag in File" +msgstr "Actualizar etiquetas en el archivo" + +#: src/GMDatabaseSource.cpp:1430 +msgid "Update Filename" +msgstr "Actualizar el nombre del archivo" + +#: src/GMDatabaseSource.cpp:1431 +msgid "Set export template…" +msgstr "Ajustar plantilla de exportación..." + +#: src/GMDatabaseSource.cpp:1645 +msgid "Update Tags?" +msgstr "¿Actualizar etiquetas?" + +#: src/GMDatabaseSource.cpp:1645 +msgid "" +"No tracks were updated.\n" +"Would you still like to write the tags for the selected tracks?" +msgstr "" +"Ninguna pista fue actualizada.\n" +"¿Sigues queriendo escribir las etiquetas para las pistas seleccionadas?" + +#: src/GMDatabaseSource.cpp:1700 +msgid "Remove Audio Files?" +msgstr "¿Suprimir archivos de audio?" + +#: src/GMDatabaseSource.cpp:1701 +msgid "Remove Audio Files..." +msgstr "Eliminar archivos de audio..." + +#: src/GMDatabaseSource.cpp:1701 +msgid "The following audio files are going to be removed" +msgstr "Los siguientes archivos de audio serán eliminados" + +#: src/GMDatabaseSource.cpp:1703 src/GMDatabaseSource.cpp:1877 +#: src/GMPlayListSource.cpp:280 src/GMStreamSource.cpp:270 +msgid "&Remove" +msgstr "&Eliminar" + +#: src/GMDatabaseSource.cpp:1729 +msgid "Export Main Library" +msgstr "Exportar biblioteca principal" + +#: src/GMDatabaseSource.cpp:1731 +msgid "Export Play List" +msgstr "Exportar lista de reproducción" + +#: src/GMDatabaseSource.cpp:1744 +msgid "Overwrite File?" +msgstr "¿Sobrescribir archivo?" + +#: src/GMDatabaseSource.cpp:1744 +msgid "File already exists. Would you like to overwrite it?" +msgstr "El archivo ya existe. ¿Quieres sobrescribirlo?" + +#: src/GMDatabaseSource.cpp:1784 +msgid "Export Genre" +msgstr "Exportar género" + +#: src/GMDatabaseSource.cpp:1785 +msgid "Export tracks with genre to destination directory." +msgstr "Exportar pistas con género al directorio de destino." + +#: src/GMDatabaseSource.cpp:1787 +msgid "Export Artists" +msgstr "Exportar artistas" + +#: src/GMDatabaseSource.cpp:1788 +msgid "Export tracks from artist to destination directory." +msgstr "Exportar pistas del artista al directorio de destino." + +#: src/GMDatabaseSource.cpp:1790 +msgid "Export Albums" +msgstr "Exportar álbumes" + +#: src/GMDatabaseSource.cpp:1791 +msgid "Export tracks from album to destination directory." +msgstr "Exportar pistas del álbum al directorio de destino." + +#: src/GMDatabaseSource.cpp:1793 +msgid "Export Tracks" +msgstr "Exportar pistas" + +#: src/GMDatabaseSource.cpp:1794 +msgid "Export tracks to destination directory." +msgstr "Exportar pistas al directorio de destino." + +#: src/GMDatabaseSource.cpp:1803 +msgid "&Export" +msgstr "&Exportar" + +#: src/GMDatabaseSource.cpp:1853 src/GMPlayListSource.cpp:261 +msgid "Remove Genre?" +msgstr "¿Eliminar género?" + +#: src/GMDatabaseSource.cpp:1854 +msgid "Remove tracks with genre from library?" +msgstr "¿Eliminar pistas con género de la biblioteca?" + +#: src/GMDatabaseSource.cpp:1858 src/GMPlayListSource.cpp:264 +msgid "Remove Artist?" +msgstr "¿Eliminar artista?" + +#: src/GMDatabaseSource.cpp:1859 +msgid "Remove tracks from artist from library?" +msgstr "¿Eliminar pistas del artista de la biblioteca?" + +#: src/GMDatabaseSource.cpp:1863 src/GMPlayListSource.cpp:267 +msgid "Remove Album?" +msgstr "¿Eliminar álbum?" + +#: src/GMDatabaseSource.cpp:1864 +msgid "Remove tracks from album from library?" +msgstr "¿Eliminar pistas del álbum de la biblioteca?" + +#: src/GMDatabaseSource.cpp:1868 src/GMPlayListSource.cpp:270 +msgid "Remove Track(s)?" +msgstr "¿Eliminar pista(s)?" + +#: src/GMDatabaseSource.cpp:1869 +msgid "Remove track(s) from library?" +msgstr "¿Eliminar pista(s) de la biblioteca?" + +#: src/GMDatabaseSource.cpp:1881 src/GMPlayListSource.cpp:285 +msgid "Remove tracks from disk" +msgstr "Eliminar pistas del disco" + +#: src/GMDatabaseSource.cpp:1895 src/GMDatabaseSource.cpp:1899 +#: src/GMDatabaseSource.cpp:1903 src/GMDatabaseSource.cpp:1907 +#: src/GMPlayListSource.cpp:308 src/GMStreamSource.cpp:280 +msgid "Library Error" +msgstr "Error de biblioteca" + +#: src/GMDatabaseSource.cpp:1895 +msgid "Unable to remove genre from the library" +msgstr "No se pudo eliminar género de la biblioteca" + +#: src/GMDatabaseSource.cpp:1899 +msgid "Unable to remove artist from the library" +msgstr "No se pudo eliminar artista de la biblioteca" + +#: src/GMDatabaseSource.cpp:1903 +msgid "Unable to remove album from the library" +msgstr "No se pudo eliminar álbum de la biblioteca" + +#: src/GMDatabaseSource.cpp:1907 src/GMPlayListSource.cpp:308 +msgid "Unable to remove track from the library." +msgstr "No se pudo eliminar la pista de la biblioteca" + +#: src/GMDatabaseSource.cpp:2117 src/GMDatabaseSource.cpp:2118 +msgid "Create Playlist" +msgstr "Crear lista de reproducción" + +#: src/GMDatabaseSource.cpp:2118 +msgid "Specify name of the new playlist" +msgstr "Especificar nombre de la nueva lista de reproducción" + +#: src/GMDatabaseSource.cpp:2120 +msgid "&Create" +msgstr "&Crear" + +#: src/GMDatabaseSource.cpp:2125 src/GMPlayListSource.cpp:415 +#: ../../../fox-1.6.37/src/FXFileList.cpp:192 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:727 +msgid "Name" +msgstr "Nombre" + +#: src/GMDatabaseSource.cpp:2127 +msgid "New Playlist" +msgstr "Nueva lista de reproducción" + +#: src/GMDatabaseSource.cpp:2178 src/GMDatabaseSource.cpp:2179 +msgid "Clear Music Library?" +msgstr "¿Limpiar biblioteca de música?" + +#: src/GMDatabaseSource.cpp:2179 +msgid "Remove all tracks from the music library?" +msgstr "¿Eliminar todas las pistas de la biblioteca de música?" + +#: src/GMDatabaseSource.cpp:2181 +msgid "&Remove All" +msgstr "&Eliminar todo" + +#: src/GMDatabaseSource.cpp:2185 +msgid "Keep play lists" +msgstr "Conservar listas de reproducción" + +#: src/GMDatabaseSource.cpp:2212 src/GMDatabaseSource.cpp:2213 +msgid "Music Library Information" +msgstr "Información de la biblioteca de música" + +#: src/GMDatabaseSource.cpp:2213 +msgid "You music collection consists of…" +msgstr "Tu colección de música consiste en..." + +#: src/GMDatabaseSource.cpp:2221 +msgid "Tracks:" +msgstr "Pistas:" + +#: src/GMDatabaseSource.cpp:2226 +msgid "Artists:" +msgstr "Artistas:" + +#: src/GMDatabaseSource.cpp:2230 +msgid "Albums:" +msgstr "Álbumes:" + +#: src/GMDatabaseSource.cpp:2234 +msgid "Total Time:" +msgstr "Tiempo total:" + +#: src/GMEQDialog.cpp:96 +msgid "Equalizer" +msgstr "Ecualizador" + +#: src/GMEQDialog.cpp:135 +msgid "Equalizer:" +msgstr "Ecualizador:" + +#: src/GMEQDialog.cpp:137 +msgid "\tSave" +msgstr "\tGuardar" + +#: src/GMEQDialog.cpp:138 +msgid "\tReset" +msgstr "\tReiniciar" + +#: src/GMEQDialog.cpp:139 +msgid "\tRemove" +msgstr "\tEliminar" + +#: src/GMEQDialog.cpp:172 +msgid "Pre-amp" +msgstr "Preamplificador" + +#: src/GMEQDialog.cpp:244 src/GMEQDialog.cpp:254 +msgid "Disabled" +msgstr "Desactivado" + +#: src/GMEQDialog.cpp:247 src/GMEQDialog.cpp:299 +msgid "Manual" +msgstr "Manual" + +#: src/GMEQDialog.cpp:314 +msgid "Delete Preset" +msgstr "Eliminar predefinición" + +#: src/GMEQDialog.cpp:314 +#, c-format +msgid "Are you sure you want to delete %s preset?" +msgstr "¿Estás seguro de que quieres eliminar la predefinición %s?" + +#: src/GMEQDialog.cpp:334 +msgid "Preset Name" +msgstr "Nombre de predefinición" + +#: src/GMEQDialog.cpp:334 +msgid "Please enter preset name:" +msgstr "Por favor, introduce el nombre de la predefinición:" + +#: src/GMEQDialog.cpp:338 +msgid "Overwrite Preset" +msgstr "Sobreescribir predefiniciónd" + +#: src/GMEQDialog.cpp:338 +#, c-format +msgid "Preset %s already exists. Would you like to overwrite it?" +msgstr "La predefinición %s ya existe. ¿Quieres sobrescribirla?" + +#: src/GMFontDialog.cpp:209 +#, fuzzy +msgid "Ultra Condensed" +msgstr "Ultra colapsado" + +#: src/GMFontDialog.cpp:210 +#, fuzzy +msgid "Extra Condensed" +msgstr "Extra colapsado" + +#: src/GMFontDialog.cpp:211 +msgid "Condensed" +msgstr "Colapsado" + +#: src/GMFontDialog.cpp:212 +#, fuzzy +msgid "Semi Condensed" +msgstr "Medio colapsado" + +#: src/GMFontDialog.cpp:214 +#, fuzzy +msgid "Semi Expanded" +msgstr "Medio expandido" + +#: src/GMFontDialog.cpp:215 +msgid "Expanded" +msgstr "Expandido" + +#: src/GMFontDialog.cpp:216 +#, fuzzy +msgid "Extra Expanded" +msgstr "Extra expandido" + +#: src/GMFontDialog.cpp:217 +#, fuzzy +msgid "Ultra Expanded" +msgstr "Ultra expandido" + +#: src/GMFontDialog.cpp:224 src/GMPreferencesDialog.cpp:1223 +msgid "Thin" +msgstr "Delgada" + +#: src/GMFontDialog.cpp:225 src/GMPreferencesDialog.cpp:1224 +msgid "Extra Light" +msgstr "Extra ligera" + +#: src/GMFontDialog.cpp:226 src/GMPreferencesDialog.cpp:1225 +msgid "Light" +msgstr "Ligera" + +#: src/GMFontDialog.cpp:228 src/GMPreferencesDialog.cpp:1227 +msgid "Medium" +msgstr "Media" + +#: src/GMFontDialog.cpp:229 src/GMPreferencesDialog.cpp:1228 +msgid "Demibold" +msgstr "Cuasi negrita" + +#: src/GMFontDialog.cpp:230 src/GMPreferencesDialog.cpp:1229 +msgid "Bold" +msgstr "Negrita" + +#: src/GMFontDialog.cpp:231 src/GMPreferencesDialog.cpp:1230 +msgid "Extra Bold" +msgstr "Extra negrita" + +#: src/GMFontDialog.cpp:232 src/GMPreferencesDialog.cpp:1231 +msgid "Heavy" +msgstr "Pesada" + +#: src/GMFontDialog.cpp:239 +#, fuzzy +msgid "Reverse Oblique" +msgstr "Oblicua revertida" + +#: src/GMFontDialog.cpp:240 +#, fuzzy +msgid "Reverse Italic" +msgstr "Italizada revertida" + +#: src/GMFontDialog.cpp:242 +#, fuzzy +msgid "Italic" +msgstr "italizada" + +#: src/GMFontDialog.cpp:243 +#, fuzzy +msgid "Oblique" +msgstr "oblicua" + +#: src/GMFontDialog.cpp:265 +msgid "Normal" +msgstr "Normal" + +#: src/GMImportDialog.cpp:91 ../../../fox-1.6.37/src/FXDirSelector.cpp:137 +msgid "&Directory:" +msgstr "&Directorio:" + +#: src/GMImportDialog.cpp:166 ../../../fox-1.6.37/src/FXFileSelector.cpp:196 +msgid "&File Name:" +msgstr "&Nombre de Archivo:" + +#: src/GMImportDialog.cpp:168 ../../../fox-1.6.37/src/FXMessageBox.cpp:102 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:106 +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:134 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:198 +msgid "&OK" +msgstr "&OK" + +#: src/GMImportDialog.cpp:170 ../../../fox-1.6.37/src/FXFileSelector.cpp:200 +msgid "File F&ilter:" +msgstr "F&iltro de Archivos:" + +#: src/GMImportDialog.cpp:174 ../../../fox-1.6.37/src/FXFileSelector.cpp:204 +msgid "Read Only" +msgstr "Solo Lectura" + +#: src/GMImportDialog.cpp:179 src/GMSearch.cpp:565 src/GMSearch.cpp:647 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:208 +msgid "Directory:" +msgstr "Directorio:" + +#: src/GMImportDialog.cpp:186 ../../../fox-1.6.37/src/FXFileSelector.cpp:228 +msgid "&Set bookmark\t\tBookmark current directory." +msgstr "&Crear marcador\t\tAñadir el directorio actual a marcadores." + +#: src/GMImportDialog.cpp:187 ../../../fox-1.6.37/src/FXFileSelector.cpp:229 +msgid "&Clear bookmarks\t\tClear bookmarks." +msgstr "&Limpiar marcadores\t\tLimpiar Marcadores." + +#: src/GMImportDialog.cpp:203 ../../../fox-1.6.37/src/FXFileSelector.cpp:244 +msgid "\tGo up one directory\tMove up to higher directory." +msgstr "\tSubir un directorio\tSubir al directorio superior." + +#: src/GMImportDialog.cpp:204 ../../../fox-1.6.37/src/FXFileSelector.cpp:245 +msgid "\tGo to home directory\tBack to home directory." +msgstr "\tIr al directorio de usuario\tVolver al directorio de usuario." + +#: src/GMImportDialog.cpp:205 ../../../fox-1.6.37/src/FXFileSelector.cpp:246 +msgid "\tGo to work directory\tBack to working directory." +msgstr "\tIr al directorio de trabajo\tVolver al directorio de trabajo." + +#: src/GMImportDialog.cpp:206 ../../../fox-1.6.37/src/FXFileSelector.cpp:247 +msgid "\tBookmarks\tVisit bookmarked directories." +msgstr "\tMarcadores\tVisitar directorios con marcadores." + +#: src/GMImportDialog.cpp:209 ../../../fox-1.6.37/src/FXFileSelector.cpp:250 +msgid "\tCreate new directory\tCreate new directory." +msgstr "\tCrear nuevo directorio\tCrear nuevo directorio." + +#: src/GMImportDialog.cpp:210 ../../../fox-1.6.37/src/FXFileSelector.cpp:251 +msgid "\tShow list\tDisplay directory with small icons." +msgstr "\tMostrar lista\tMostrar directorio con iconos pequeños." + +#: src/GMImportDialog.cpp:211 ../../../fox-1.6.37/src/FXFileSelector.cpp:252 +msgid "\tShow icons\tDisplay directory with big icons." +msgstr "\tMostrar iconos\tMostrar directorio con iconos grandes." + +#: src/GMImportDialog.cpp:212 ../../../fox-1.6.37/src/FXFileSelector.cpp:253 +msgid "\tShow details\tDisplay detailed directory listing." +msgstr "\tMostrar detalles\tMostrar listado de directorio detallado." + +#: src/GMImportDialog.cpp:213 ../../../fox-1.6.37/src/FXFileSelector.cpp:254 +msgid "\tShow hidden files\tShow hidden files and directories." +msgstr "\tMostrar archivos ocultos\tMostrar archivos y directorios ocultos." + +#: src/GMImportDialog.cpp:213 ../../../fox-1.6.37/src/FXFileSelector.cpp:254 +msgid "\tHide Hidden Files\tHide hidden files and directories." +msgstr "\tOcultar Archivos Ocultos\tOcultar archivos y directorios ocultos." + +#: src/GMImportDialog.cpp:412 +msgid "Synchronize Folder" +msgstr "Sincronizar carpeta" + +#: src/GMImportDialog.cpp:414 +msgid "Import Playlist" +msgstr "Importar lista de reproducción" + +#: src/GMImportDialog.cpp:416 +msgid "Import Music" +msgstr "Importar música" + +#: src/GMImportDialog.cpp:448 +msgid "&File(s)" +msgstr "&Archivo(s)" + +#: src/GMImportDialog.cpp:475 +msgid "&Directory" +msgstr "&Directorio" + +#: src/GMImportDialog.cpp:478 +msgid "Exclude Filter\tFilter out directories and/or files based on pattern" +msgstr "Excluir filtro\tFiltrar directorios y/o archivos basados en el patrón" + +#: src/GMImportDialog.cpp:481 +msgid "Folders:" +msgstr "Carpetas:" + +#: src/GMImportDialog.cpp:483 +msgid "Files:" +msgstr "Archivos:" + +#: src/GMImportDialog.cpp:499 src/GMImportDialog.cpp:560 +msgid "&Sync" +msgstr "&Sincronizar" + +#: src/GMImportDialog.cpp:501 +msgid "Sync Operation" +msgstr "Sincronizar operación" + +#: src/GMImportDialog.cpp:504 +msgid "Import new tracks\tImports files not yet in the database." +msgstr "" +"Importar nuevas pistas\tImporta archivos que no están en la base de datos " +"todavía." + +#: src/GMImportDialog.cpp:505 +msgid "Remove tracks that have been deleted from disk" +msgstr "Eliminar pistas que hayan sido eliminadas del disco" + +#: src/GMImportDialog.cpp:507 +msgid "Update existing tracks:" +msgstr "Actualizar pistas existentes:" + +#: src/GMImportDialog.cpp:508 +msgid "" +"Modified since last import\tOnly reread the tag when the file has been " +"modified." +msgstr "" +"Modificado desde la última importación\t Solamente releer la etiqueta cuando " +"el archivo ha sido modificado." + +#: src/GMImportDialog.cpp:510 +msgid "All\tAlways read the tags" +msgstr "Todas\tSiempre leer las etiquetas" + +#: src/GMImportDialog.cpp:511 +msgid "Remove tracks found in folder from database" +msgstr "Eliminar pistas encontradas en la carpeta de la base de datos" + +#: src/GMImportDialog.cpp:514 +msgid "&Track" +msgstr "&Pista" + +#: src/GMImportDialog.cpp:517 +msgid "Parse Settings" +msgstr "Analizar configuración" + +#: src/GMImportDialog.cpp:521 +msgid "Parse info from:" +msgstr "Analizar información de:" + +#: src/GMImportDialog.cpp:524 +msgid "Tag" +msgstr "Etiquetar" + +#: src/GMImportDialog.cpp:526 +msgid "Both" +msgstr "Ambos:" + +#: src/GMImportDialog.cpp:528 +msgid "Default value:" +msgstr "Valor por defecto:" + +#: src/GMImportDialog.cpp:532 +msgid "Set track number based on scan order." +msgstr "Establecer número de pista en base a el orden de exploración." + +#: src/GMImportDialog.cpp:537 +msgid "" +"%T - title %A - album name\n" +"%P - album artist name %p - track artist name \n" +"%N - track number %G - genre" +msgstr "" +"%T - título %A - nombre del album\n" +"%P - nombre del artista del album %p - nombre del artista de la pista \n" +"%N - nombre de la pista %G - género" + +#: src/GMImportDialog.cpp:549 +msgid "Replace underscores with spaces" +msgstr "Reemplazar guiones bajos con espacios" + +#: src/GMImportDialog.cpp:562 +msgid "&Import" +msgstr "&Importar" + +#: src/GMPlayer.cpp:212 +msgid "Unable to initialize audio driver." +msgstr "No se pudo inicializar el controlador de audio" + +#: src/GMPlayer.cpp:714 +msgid "Unknown host." +msgstr "Servidor desconocido." + +#: src/GMPlayer.cpp:715 +msgid "Unknown device" +msgstr "Dispositivo desconocido" + +#: src/GMPlayer.cpp:716 +msgid "Network not reachable." +msgstr "Red no alcanzable." + +#: src/GMPlayer.cpp:717 +msgid "Audio output unavailable." +msgstr "Salida de audio no disponible." + +#: src/GMPlayer.cpp:718 +msgid "Connection Refused." +msgstr "Conexión rechazada." + +#: src/GMPlayer.cpp:719 +msgid "File not found." +msgstr "Archivo no encontrado." + +#: src/GMPlayer.cpp:720 +msgid "Resource not accessible. Check permissions" +msgstr "Recurso no disponible. Comprobar permisos" + +#: src/GMPlayer.cpp:721 +msgid "Read Error" +msgstr "Error de lectura" + +#: src/GMPlayer.cpp:722 +msgid "Error while loading library/plugin" +msgstr "Error al cargar la librería/complemento" + +#: src/GMPlayer.cpp:723 +msgid "Warning" +msgstr "Advertencia" + +#: src/GMPlayer.cpp:724 +msgid "Security Warning" +msgstr "Aviso de seguridad" + +#: src/GMPlayer.cpp:725 +msgid "Unknown Error" +msgstr "Error desconocido" + +#: src/GMPlayer.cpp:761 +msgid "Error" +msgstr "Error" + +#: src/GMPlayerManager.cpp:439 +#, c-format +msgid "Unable to create directory %s\n" +msgstr "No se pudo crear el directorio %s.\n" + +#: src/GMPlayerManager.cpp:612 +msgid "" +"For some reason the FOX library was compiled without PNG support.\n" +"In order to show all icons, Goggles Music Manager requires PNG\n" +"support in the FOX library. If you've compiled FOX yourself, most\n" +"likely the libpng header files were not installed on your system." +msgstr "" +"Por alguna razón, la librería FOX fue compilada sin soporte para PNG.\n" +"Para poder ver todos los iconos, Goggles Music Manager necesita soporte\n" +"para PNG en la librería FOX. Si has compilado FOX por ti mismo, lo más\n" +"probable es que encabezados de libpng no fueran instalados en tu sistema." + +#: src/GMPlayerManager.cpp:623 +msgid "Session bus not available. All features requiring dbus are disabled." +msgstr "" +"Sesión bus no disponible. Todas las características que requieren dbus están " +"desactivadas." + +#: src/GMPlayerManager.cpp:633 +msgid "A DBus error occurred. All features requiring sessionbus are disabled." +msgstr "" +"Ha ocurrido un error en DBus. Todas las características que requieren dbus " +"están desactivadas." + +#: src/GMPlayerManager.cpp:644 +msgid "" +"Session Bus not available. All features requiring sessionbus are disabled." +msgstr "" +"Sesión DBus no disponible. Todas las características que requieren " +"sessionbus están desactivadas." + +#: src/GMPlayerManager.cpp:719 src/GMPreferencesDialog.cpp:594 +msgid "Audio Device Error" +msgstr "Error del dispositivo de audio" + +#: src/GMPlayerManager.cpp:1551 +msgid "Last.FM Error" +msgstr "Error de Last.FM" + +#: src/GMPlayerManager.cpp:1558 +msgid "Playback Error" +msgstr "Error de reproducción" + +#: src/GMPlayerManager.cpp:1708 +#, c-format +msgid "" +"%s\n" +"%s (%d)" +msgstr "" +"%s\n" +"%s (%d)" + +#: src/GMPlayListSource.cpp:99 src/GMPlayListSource.cpp:105 +#: src/GMPlayListSource.cpp:111 src/GMPlayListSource.cpp:121 +msgid "Remove…\tDel\tRemove track(s) from play list." +msgstr "Eliminar…\tSupr\tEliminar pistas(s) de la lista de reproducción." + +#: src/GMPlayListSource.cpp:161 +msgid "Edit…" +msgstr "Editar..." + +#: src/GMPlayListSource.cpp:162 +msgid "Import…" +msgstr "Importar..." + +#: src/GMPlayListSource.cpp:164 +msgid "Remove Playlist" +msgstr "Eliminar lista de reproducción" + +#: src/GMPlayListSource.cpp:262 +msgid "Remove tracks with genre from play list?" +msgstr "¿Eliminar pistas con género de la lista de reproducción?" + +#: src/GMPlayListSource.cpp:265 +msgid "Remove tracks from artist from play list?" +msgstr "¿Eliminar pistas del artista de la lista de reproducción?" + +#: src/GMPlayListSource.cpp:268 +msgid "Remove tracks from album from play list?" +msgstr "¿Eliminar pistas del álbum de la lista de reproducción?" + +#: src/GMPlayListSource.cpp:271 +msgid "Remove track(s) from play list?" +msgstr "¿Eliminar pista(s) de la lista de reproducción?" + +#: src/GMPlayListSource.cpp:284 +msgid "Remove tracks from music library" +msgstr "Eliminar pistas de la biblioteca de música" + +#: src/GMPlayListSource.cpp:406 src/GMPlayListSource.cpp:407 +msgid "Edit Playlist" +msgstr "Editar lista de reprodución" + +#: src/GMPlayListSource.cpp:407 +msgid "Change playlist name" +msgstr "Cambiar el nombre de la lista de reproducción" + +#: src/GMPlayListSource.cpp:432 +msgid "Delete Play List?" +msgstr "¿Eliminar lista de reproducción?" + +#: src/GMPlayListSource.cpp:432 +msgid "Are you sure you want to delete the playlist?" +msgstr "¿Estás seguro de que quieres eliminar la lista de reproducción?" + +#: src/GMPlayListSource.cpp:432 ../../../fox-1.6.37/src/FXMessageBox.cpp:111 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:116 +msgid "&Yes" +msgstr "&Sí" + +#: src/GMPlayListSource.cpp:432 ../../../fox-1.6.37/src/FXMessageBox.cpp:112 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:117 +msgid "&No" +msgstr "&No" + +#: src/GMPreferencesDialog.cpp:252 +msgid "Preferences" +msgstr "Preferencias" + +#: src/GMPreferencesDialog.cpp:286 +msgid "&General" +msgstr "&General" + +#: src/GMPreferencesDialog.cpp:289 +msgid "Sort Options" +msgstr "Opciones de clasificación" + +#: src/GMPreferencesDialog.cpp:293 +msgid "Ignore leading words" +msgstr "Ignorar las primeras palabras" + +#: src/GMPreferencesDialog.cpp:296 +msgid "Album Covers" +msgstr "Carátulas" + +#: src/GMPreferencesDialog.cpp:299 +msgid "Show album cover of playing track\tShow album cover of playing track" +msgstr "" +"Mostrar carátula de la pista en reproducción\tMostrar carátula de la pista " +"en reproducción" + +#: src/GMPreferencesDialog.cpp:300 +msgid "Show album covers in album browser\tShow album covers in album browser" +msgstr "" +"Mostrar las carátulas en el navegador de carátulas\tMostrar las carátulas en " +"el navegador de carátulas" + +#: src/GMPreferencesDialog.cpp:302 +msgid "System Tray" +msgstr "Bandeja del sistema" + +#: src/GMPreferencesDialog.cpp:304 +msgid "Show Tray Icon\tShow tray icon in the system tray." +msgstr "Mostrar icono de bandeja\tMostrar icono en la bandeja de sistema." + +#: src/GMPreferencesDialog.cpp:307 +msgid "" +"Show Track Change Notifications\tInform notification daemon of track changes." +msgstr "" +"Informar sobre los cambios de pista\tInformar al demonio de notificaciones " +"sobre cambios de pista." + +#: src/GMPreferencesDialog.cpp:311 +msgid "Last.fm" +msgstr "Last.fm" + +#: src/GMPreferencesDialog.cpp:315 +msgid "" +"This version of Goggles Music Manager is\n" +"not supported by Last-FM. Please upgrade\n" +"to a newer version of GMM." +msgstr "" +"Esta versión de Goggles Music Manager no\n" +"está soportada por Last-FM. Por favor,\n" +"actualiza tu versión de GMM." + +#: src/GMPreferencesDialog.cpp:321 +msgid "Service:" +msgstr "Servicio:" + +#: src/GMPreferencesDialog.cpp:338 +msgid "&Sign up…" +msgstr "&Regístrate..." + +#: src/GMPreferencesDialog.cpp:344 +msgid "Username:" +msgstr "Nombre de usuario:" + +#: src/GMPreferencesDialog.cpp:346 +msgid "Password:" +msgstr "Contraseña:" + +#: src/GMPreferencesDialog.cpp:349 +msgid "Scrobble" +msgstr "Scrobble" + +#: src/GMPreferencesDialog.cpp:359 +msgid "&Window" +msgstr "&Ventana" + +#: src/GMPreferencesDialog.cpp:362 +msgid "Window" +msgstr "Ventana" + +#: src/GMPreferencesDialog.cpp:365 +#, fuzzy +msgid "Close button minimizes to tray" +msgstr "Botón de cerrar esconde la ventana principal" + +#: src/GMPreferencesDialog.cpp:366 +msgid "Show Status Bar" +msgstr "Mostrar barra de estado" + +#: src/GMPreferencesDialog.cpp:368 +msgid "Show Icons in Track Browser" +msgstr "Mostrar iconos en el navegador de pistas" + +#: src/GMPreferencesDialog.cpp:370 +msgid "Display playing track in title bar" +msgstr "Mostrar la pista en reproducción en la barra de título" + +#: src/GMPreferencesDialog.cpp:372 +msgid "Player Controls" +msgstr "Controles del reproductor" + +#: src/GMPreferencesDialog.cpp:376 +msgid "Location:" +msgstr "Ubicación:" + +#: src/GMPreferencesDialog.cpp:378 +msgid "Top" +msgstr "Arriba" + +#: src/GMPreferencesDialog.cpp:379 +msgid "Bottom" +msgstr "Abajo" + +#: src/GMPreferencesDialog.cpp:387 +msgid "Title Format:" +msgstr "Formato de título:" + +#: src/GMPreferencesDialog.cpp:391 +msgid "Style:" +msgstr "Estilo:" + +#: src/GMPreferencesDialog.cpp:392 +msgid "Show Labels" +msgstr "Mostrar etiquetas" + +#: src/GMPreferencesDialog.cpp:395 +msgid "Large Icons" +msgstr "Iconos grandes" + +#: src/GMPreferencesDialog.cpp:399 +msgid "A&ppearance" +msgstr "A&pariencia" + +#: src/GMPreferencesDialog.cpp:402 +msgid "Colors" +msgstr "Colores" + +#: src/GMPreferencesDialog.cpp:409 +msgid "fg\tForeground Color" +msgstr "fg\tColor del primer plano" + +#: src/GMPreferencesDialog.cpp:410 +msgid "bg\tBackground Color" +msgstr "bg\tColor de fondo" + +#: src/GMPreferencesDialog.cpp:411 +msgid "alt bg\tAlternative Background Color" +msgstr "alt bg\tColor de fondo alternativo" + +#: src/GMPreferencesDialog.cpp:422 +msgid "Normal\tNormal Text Color" +msgstr "Normal\tColor de texto normal" + +#: src/GMPreferencesDialog.cpp:426 +msgid "Base\tBase Color" +msgstr "Base\tColor base" + +#: src/GMPreferencesDialog.cpp:429 +msgid "Selected\tSelected Text Color" +msgstr "Seleccionado\tColor de texto seleccionado" + +#: src/GMPreferencesDialog.cpp:433 +#, fuzzy +msgid "Menu\tMenu Base Color" +msgstr "Menú\tColor de texto del menú" + +#: src/GMPreferencesDialog.cpp:437 +msgid "Menu\tMenu Text Color" +msgstr "Menú\tColor de texto del menú" + +#: src/GMPreferencesDialog.cpp:441 +msgid "Border\tBorder Color" +msgstr "Borde\tColor de borde" + +#: src/GMPreferencesDialog.cpp:445 +msgid "Tooltip\tTooltip Color" +msgstr "Consejo\tColor de consejos" + +#: src/GMPreferencesDialog.cpp:449 +msgid "Hilite\tHilite Color" +msgstr "Destacado\tColor del destacado" + +#: src/GMPreferencesDialog.cpp:456 +msgid "Shadow\tShadow Color" +msgstr "Sombra\tColor de la sombra" + +#: src/GMPreferencesDialog.cpp:459 +msgid "Playing\tPlaying Track Color" +msgstr "Reproduciendo\tColor de la pista en reproducción" + +#: src/GMPreferencesDialog.cpp:463 +msgid "Tray\tTray Background Color" +msgstr "Tray\tColor del fondo de la bandeja" + +#: src/GMPreferencesDialog.cpp:473 +msgid "Presets:" +msgstr "Predefinidos:" + +#: src/GMPreferencesDialog.cpp:481 +#, fuzzy +msgid "Font & Icons" +msgstr "Fuentes & Iconos" + +#: src/GMPreferencesDialog.cpp:487 +msgid "Default Font" +msgstr "Fuente por defecto:" + +#: src/GMPreferencesDialog.cpp:489 +msgid "Change…" +msgstr "Cambiar..." + +#: src/GMPreferencesDialog.cpp:490 +msgid "Icons" +msgstr "Iconos" + +#: src/GMPreferencesDialog.cpp:509 +msgid "&Audio" +msgstr "&Audio" + +#: src/GMPreferencesDialog.cpp:512 +msgid "Engine" +msgstr "Motor" + +#: src/GMPreferencesDialog.cpp:516 +msgid "Audio Driver:" +msgstr "Controlador de audio:" + +#: src/GMPreferencesDialog.cpp:527 +msgid "Close audio device on pause." +msgstr "Cerrar el dispositivo de audio en pausa." + +#: src/GMPreferencesDialog.cpp:528 +msgid "Turn off playback engine on stop." +msgstr "Apagar el motor de reproducción en stop." + +#: src/GMPreferencesDialog.cpp:529 +msgid "" +"Turn on playback engine on startup.\tFor faster startup, playback engine is " +"normally started when first track is played.\tFor faster startup, playback " +"engine is normally started when first track is played." +msgstr "" +"Encender el motor de reproducción al inicio.\tPara una inicialización más " +"rápida, el motor de reproducción normalmente se enciende cuando la primera " +"pista es reproducida.\tPara una inicialización más rápida, el motor de " +"reproducción normalmente se enciende cuando la primera pista es reproducida." + +#: src/GMPreferencesDialog.cpp:532 +msgid "Playback" +msgstr "Reproducción" + +#: src/GMPreferencesDialog.cpp:536 +msgid "Replay Gain:" +msgstr "Ganancia de repetición:" + +#: src/GMPreferencesDialog.cpp:538 +msgid "Off" +msgstr "Apagado" + +#: src/GMPreferencesDialog.cpp:543 +msgid "Gapless playback" +msgstr "Reproducción sin espacios" + +#: src/GMPreferencesDialog.cpp:544 +msgid "Volume Normalization" +msgstr "Normalización del volumen" + +#: src/GMPreferencesDialog.cpp:594 +#, c-format +msgid "Failed to open requested audio driver: %s" +msgstr "No se pudo inicializar el controlador de audio: %s" + +#: src/GMPreferencesDialog.cpp:678 +msgid "Current" +msgstr "Actual" + +#: src/GMPreferencesDialog.cpp:696 +msgid "Custom" +msgstr "Personalizado" + +#: src/GMPreferencesDialog.cpp:769 src/GMPreferencesDialog.cpp:774 +#: src/GMWindow.cpp:1133 src/GMWindow.cpp:1140 src/GMWindow.cpp:1147 +#: src/GMWindow.cpp:1154 +msgid "Unable to launch webbrowser" +msgstr "No se pudo iniciar el navegador web" + +#: src/GMPreferencesDialog.cpp:1172 +msgid "Select Normal Font" +msgstr "Seleccionar fuente normal" + +#: src/GMRemote.cpp:295 +msgid "&Next" +msgstr "&Siguiente" + +#: src/GMRemote.cpp:296 +msgid "P&revious" +msgstr "A&nterior" + +#: src/GMRemote.cpp:297 src/GMWindow.cpp:1197 +msgid "&Play" +msgstr "&Reproducir" + +#: src/GMRemote.cpp:298 +msgid "&Stop" +msgstr "&Parar" + +#: src/GMRemote.cpp:300 +msgid "Show Browser" +msgstr "Mostrar navegador" + +#: src/GMRemote.cpp:301 src/GMTrayIcon.cpp:307 +msgid "Quit" +msgstr "Salir" + +#: src/GMRemote.cpp:385 +#, fuzzy +msgid "\tShow Browser\tShow Browser" +msgstr "Mostrar navegador" + +#: src/GMRemote.cpp:387 +msgid "\tStart Playback\tStart Playback" +msgstr "\tEmpezar reproducción\tEmpezar reproducción" + +#: src/GMRemote.cpp:388 +msgid "\tStop Playback\tStop Playback" +msgstr "\tParar reproducción\tParar reproducción" + +#: src/GMRemote.cpp:390 +msgid "\tPlay Previous Track\tPlay previous track." +msgstr "\tReproducir pista anterior\tReproducir pista anterior." + +#: src/GMRemote.cpp:391 +msgid "\tPlay Next Track\tPlay next track." +msgstr "\tReproducir pista siguiente\tReproducir pista siguiente." + +#: src/GMRemote.cpp:397 src/GMWindow.cpp:222 +msgid "\tAdjust Volume\tAdjust Volume" +msgstr "\tAjustar volumen\tAjustar volumen" + +#: src/GMSearch.cpp:128 +msgid "Unable to open the database" +msgstr "No se pudo abrir la base de datos" + +#: src/GMSearch.cpp:252 +msgid "Database Error: Unable to retrieve all filenames." +msgstr "" +"Error de base de datos: No se pudieron recuperar todos los nombres de " +"archivos." + +#: src/GMSearch.cpp:280 +msgid "Unable to update track" +msgstr "No se pudo actualizar pista" + +#: src/GMSearch.cpp:439 +msgid "Unable to insert track into the database" +msgstr "No se pudo insertar la pista en la base de datos" + +#: src/GMSearch.cpp:505 src/GMTrackDatabase.cpp:205 +msgid "Fatal Error" +msgstr "Error Fatal" + +#: src/GMSearch.cpp:505 +#, c-format +msgid "" +"%s\n" +"Please contact support if this error keeps occuring." +msgstr "" +"%s\n" +"Por favor, contacta con soporte si este error continúa ocurriendo." + +#: src/GMSearch.cpp:534 src/GMSearch.cpp:584 +msgid "Updating Database..." +msgstr "Actualizando base de datos..." + +#: src/GMSearch.cpp:537 src/GMSearch.cpp:626 +msgid "Please wait. This may take a while." +msgstr "Por favor, espera. Esto puede tomar un tiempo." + +#: src/GMSearch.cpp:555 src/GMSearch.cpp:637 +msgid "New Tracks:" +msgstr "Nuevas Pistas:" + +#: src/GMSearch.cpp:561 src/GMSearch.cpp:643 +msgid "File:" +msgstr "Archivo:" + +#: src/GMSearch.cpp:594 src/GMSearch.cpp:623 +msgid "Importing Files..." +msgstr "Importando archivos..." + +#: src/GMSearch.cpp:652 +msgid "&Stop Import" +msgstr "&Parar importación" + +#: src/GMSourceView.cpp:54 +msgid "Sources\tPress to change sorting order\tPress to change sorting order" +msgstr "Orígenes\tPulsa para cambiar el orden\tPulsa para cambiar el orden" + +#: src/GMSourceView.cpp:245 src/GMWindow.cpp:261 +msgid "New Playlist…\t\tCreate a new playlist" +msgstr "Nueva lista de reproducción...\t\tCrear nueva lista de reproducción" + +#: src/GMSourceView.cpp:246 src/GMWindow.cpp:262 +msgid "Import Playlist…\t\tImport existing playlist" +msgstr "" +"Importar lista de reproducción...\t\tImportar lista de reproducción existente" + +#: src/GMSourceView.cpp:247 src/GMWindow.cpp:263 +msgid "New Radio Station…\t\tCreate a new playlist" +msgstr "Nueva estación de radio...\t\tCrear nueva lista de reproducción" + +#: src/GMStreamSource.cpp:64 +msgid "Station" +msgstr "Estación" + +#: src/GMStreamSource.cpp:112 src/GMStreamSource.cpp:118 +msgid "New Station…\t\t" +msgstr "Nueva estación...\t\t" + +#: src/GMStreamSource.cpp:117 +msgid "Edit…\t\t" +msgstr "Editar...\t\t" + +#: src/GMStreamSource.cpp:119 +msgid "Remove\t\tRemove." +msgstr "Eliminar\t\tEliminar." + +#: src/GMStreamSource.cpp:165 src/GMStreamSource.cpp:166 +msgid "New Internet Radio Station" +msgstr "Nueva estación de radio de Internet" + +#: src/GMStreamSource.cpp:166 +msgid "Specify url and description of new station" +msgstr "Especifica url y descripción de la nueva estación" + +#: src/GMStreamSource.cpp:168 +msgid "C&reate" +msgstr "C&rear" + +#: src/GMStreamSource.cpp:173 src/GMStreamSource.cpp:211 +msgid "Location" +msgstr "Ubicación" + +#: src/GMStreamSource.cpp:175 src/GMStreamSource.cpp:214 +msgid "Description" +msgstr "Descripción" + +#: src/GMStreamSource.cpp:189 +msgid "Untitled" +msgstr "Sin título" + +#: src/GMStreamSource.cpp:202 src/GMStreamSource.cpp:203 +msgid "Edit Internet Radio Station" +msgstr "Editar estación de radio de Internet" + +#: src/GMStreamSource.cpp:203 +msgid "Update url and description of station" +msgstr "Actualizar la url y descripción de la estación" + +#: src/GMStreamSource.cpp:265 +msgid "Remove Internet Radio Station(s)?" +msgstr "¿Eliminar estación(es) de radio de Internet?" + +#: src/GMStreamSource.cpp:266 +msgid "Remove Internet Radio Station(s) from library?" +msgstr "¿Eliminar estación(es) de radio de Internet de la biblioteca?" + +#: src/GMStreamSource.cpp:280 +#, c-format +msgid "Unable to remove station from the library." +msgstr "No se pudo eliminar la estación de la biblioteca" + +#: src/GMTrackDatabase.cpp:193 +msgid "" +"An incompatible (future) version of the database was found.\n" +"This usually happens when you try to downgrade to a older version of GMM\n" +"Press OK to continue and reset the database (all information will be " +"lost!).\n" +"Press Cancel to quit now and leave the database as is." +msgstr "" +"Una incompatible (futura) versión de la base de datos ha sido encontrada.\n" +"Suele pasar cuando intentas hacer un downgrade a una versión más antigua de " +"GMM\n" +"Presiona OK para continuar y resetear la base de datos (¡Se perderá toda la " +"información!).\n" +"Presiona Cancelar para salir ahora mismo y dejar la base de datos como está." + +#: src/GMTrackDatabase.cpp:205 +msgid "" +"Goggles Music Manager was unable to open the database.\n" +"The database may have been corrupted. Please remove ~/.goggles/goggles.db to " +"try again.\n" +"if the error keeps occuring, please file an issue at http://code.google.com/" +"p/gogglesmm" +msgstr "" +"Goggles Music Manager no pudo abrir la base de datos.\n" +"La base de datos puede haberse corrompido. Por favor, elimina ~/.goggles/" +"goggles.db y prueba de nuevo.\n" +"Si el error sigue ocurriendo, por favor, avísanos del problema en http://" +"code.google.com/p/gogglesmm" + +#: src/GMTrackView.cpp:245 +msgid "\tClose Filter\tClose Filter" +msgstr "\tCerrar Filtro\tCerrar Filtro" + +#: src/GMTrackView.cpp:246 +#, fuzzy +msgid "&Find" +msgstr "Encontrar" + +#: src/GMTrackView.cpp:252 +#, fuzzy +msgid "Tags\tPress to change sorting order\tPress to change sorting order" +msgstr "Géneros\tPulsar para cambiar el orden\tPulsar para cambiar el orden" + +#: src/GMTrackView.cpp:256 +msgid "Artists\tPress to change sorting order\tPress to change sorting order" +msgstr "Artistas\tPulsar para cambiar el orden\tPulsar para cambiar el orden" + +#: src/GMTrackView.cpp:260 +msgid "Albums\tPress to change sorting order\tPress to change sorting order" +msgstr "Álbumes\tPulsar para cambiar el orden\tPulsar para cambiar el orden" + +#: src/GMTrackView.cpp:282 src/GMWindow.cpp:299 +msgid "&Configure Columns…" +msgstr "&Configurar Columnas..." + +#: src/GMTrackView.cpp:286 +msgid "Browse" +msgstr "Navegar" + +#: src/GMTrackView.cpp:287 +msgid "Shuffle\tCtrl-R" +msgstr "Aleatorio\tCtrl-R" + +#: src/GMTrackView.cpp:294 ../../../fox-1.6.37/src/FXDirSelector.cpp:388 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:734 +msgid "Reverse" +msgstr "Invertido" + +#: src/GMTrackView.cpp:804 +#, c-format +msgid "All %d Genres" +msgstr "Todos %d los géneros" + +#: src/GMTrackView.cpp:806 +msgid "All Genres" +msgstr "Todos los géneros" + +#: src/GMTrackView.cpp:832 +#, c-format +msgid "All %d Artists" +msgstr "Todos %d los artistas" + +#: src/GMTrackView.cpp:872 +#, c-format +msgid "All %d Albums" +msgstr "Todos %d los álbumes" + +#: src/GMTrackView.cpp:1258 +#, c-format +msgid "By %s" +msgstr "Por %s" + +#: src/GMTrackView.cpp:1586 src/GMTrackView.cpp:1609 +msgid "Sort by Album Year" +msgstr "Ordenar por año del álbum" + +#: src/GMTrackView.cpp:1637 +msgid "&Columns\t\tChange Visible Columns." +msgstr "&Columnas\t\tCambiar columnas visibles." + +#: src/GMTrackView.cpp:1638 +msgid "&Sort\t\tChange Sorting." +msgstr "&Orden\t\tCambiar orden." + +#: src/GMTrayIcon.cpp:302 src/GMWindow.cpp:841 src/GMWindow.cpp:925 +#: src/GMWindow.cpp:948 src/GMWindow.cpp:954 +msgid "Play" +msgstr "Reproducir" + +#: src/GMTrayIcon.cpp:303 src/GMWindow.cpp:843 +msgid "Stop" +msgstr "Parar" + +#: src/GMTrayIcon.cpp:304 +msgid "Previous Track" +msgstr "Pista anterior" + +#: src/GMTrayIcon.cpp:305 +msgid "Next Track" +msgstr "Pista posterior" + +#: src/GMWindow.cpp:205 +msgid "Play\tStart Playback\tStart Playback" +msgstr "Reproducir\tEmpezar reproducción\tEmpezar reproducción" + +#: src/GMWindow.cpp:205 +msgid "Pause\tPause\tPause Playback" +msgstr "Pausar\tPausar\tPausar reproducción" + +#: src/GMWindow.cpp:206 +msgid "Stop\tStop Playback\tStop Playback" +msgstr "Parar\tParar reproducción\tParar reproducción" + +#: src/GMWindow.cpp:208 +msgid "Previous\tPlay Previous Track\tPlay previous track." +msgstr "Previa\tReproducir pista previa\tReproducir pista previa." + +#: src/GMWindow.cpp:209 +msgid "Next\tPlay Next Track\tPlay next track." +msgstr "Siguiente\tReproducir siguiente pista\tReproducir siguiente pista." + +#: src/GMWindow.cpp:255 +msgid "&Music" +msgstr "&Música" + +#: src/GMWindow.cpp:256 +msgid "Import Folder…\tCtrl-O\tImport Music from folder into Library" +msgstr "" +"Importar carpeta...\tCtrl-O\tImportar música de una carpeta a la biblioteca" + +#: src/GMWindow.cpp:257 +msgid "Sync Folder…\t\tSynchronize Folder with Music in Library" +msgstr "" +"Sincronizar carpeta...\t\tSincronizar carpeta con música en la biblioteca" + +#: src/GMWindow.cpp:259 +msgid "Play File or Stream…\t\tPlay File or Stream" +msgstr "Reproducir medio…\t\tReproducir medio" + +#: src/GMWindow.cpp:266 +msgid "&Quit\tCtrl-Q\tQuit the application." +msgstr "&Salir\tCtrl-Q\tSalir de la aplicación." + +#: src/GMWindow.cpp:271 +msgid "&Edit" +msgstr "&Editar" + +#: src/GMWindow.cpp:272 +msgid "&Copy\tCtrl-C\tCopy Selected Tracks" +msgstr "&Copiar\tCtrl-C\tCopiar las Pistas Seleccionadas" + +#: src/GMWindow.cpp:273 +msgid "&Cut\tCtrl-X\tCut Selected Tracks" +msgstr "&Cortar\tCtrl-X\tCortar las Pistas Seleccionadas" + +#: src/GMWindow.cpp:274 +msgid "&Paste\tCtrl-V\tPaste Clipboard Selection" +msgstr "&Pegar\tCtrl-V\tPegar la selección del Portapapeles" + +#: src/GMWindow.cpp:276 +msgid "Find…\tCtrl-F\tShow search filter." +msgstr "Buscar...\tCtrl-F\tMostrar filtro de búsqueda." + +#: src/GMWindow.cpp:278 +msgid "Preferences…" +msgstr "Preferencias" + +#: src/GMWindow.cpp:282 +msgid "&View" +msgstr "&Ver" + +#: src/GMWindow.cpp:283 +msgid "&Browse\tCtrl-B\tShow genre artist and album browser." +msgstr "&Navegar\tCtrl-B\tMostrar navegador de género, artista y álbum." + +#: src/GMWindow.cpp:284 +msgid "Show &Genres\tCtrl-G\tShow genre browser." +msgstr "Mostrar &Géneros\tCtrl-G\tMostrar navegador de géneros." + +#: src/GMWindow.cpp:287 src/GMWindow.cpp:289 +msgid "Show &Sources\tCtrl-S\tShow source browser " +msgstr "Mostrar &Fuentes\tCtrl-S\tMostrar navegador de orígenes" + +#: src/GMWindow.cpp:294 +msgid "Show Full Screen\tF12\tToggle fullscreen mode." +msgstr "Mostrar Pantalla Completa\tF12\tCambiar a modo de pantalla completa." + +#: src/GMWindow.cpp:296 +#, fuzzy +msgid "Show Mini Player\tCtrl-M\tToggle Mini Player." +msgstr "Mostrar Mini Reproductor\tF11\tCambiar a Mini Reproductor." + +#: src/GMWindow.cpp:300 +msgid "&Sort" +msgstr "&Ordenar" + +#: src/GMWindow.cpp:302 +msgid "&Jump to Current Track\tCtrl-J\tShow current playing track." +msgstr "" +"&Saltar a la Pista Actual\tCtrl-J\tMostrar la pista que se está " +"reproduciendo." + +#: src/GMWindow.cpp:306 +msgid "&Control" +msgstr "&Control" + +#: src/GMWindow.cpp:307 +msgid "Play\tCtrl-P\tStart playback." +msgstr "Reproducir\tCtrl-P\tIniciar reproducción." + +#: src/GMWindow.cpp:308 +msgid "Stop\tCtrl-\\\tStop playback." +msgstr "Parar\tCtrl-\\\tParar reproducción." + +#: src/GMWindow.cpp:309 +msgid "Previous Track\tCtrl-[\tPlay next track." +msgstr "Pista anterior\tCtrl-[\tReproducir pista siguiente." + +#: src/GMWindow.cpp:310 +msgid "Next Track\tCtrl-]\tPlay previous track." +msgstr "Siguiente Pista\tCtrl-]\tReproducir pista anterior." + +#: src/GMWindow.cpp:312 +msgid "Repeat Off\tCtrl-,\tRepeat current track." +msgstr "Repetir Apagado\tCtrl-,\tRepetir pista actual." + +#: src/GMWindow.cpp:313 +msgid "Repeat Track\tCtrl-.\tRepeat current track." +msgstr "Repetir Pista\tCtrl-.\tRepetir pista actual." + +#: src/GMWindow.cpp:314 +msgid "Repeat All Tracks\tCtrl-/\tRepeat all tracks." +msgstr "Repetir Todas las Pistas\tCtrl-/\tRepetir todas las pistas." + +#: src/GMWindow.cpp:315 +msgid "Repeat A-B\tCtrl-T\tRepeat section of track." +msgstr "Repetir A-B\tCtrl-T\tRepetir sección de pista." + +#: src/GMWindow.cpp:316 +msgid "Shuffle Mode\tAlt-R\tPlay tracks in random order." +msgstr "Modo Aleatorio\tAlt-R\tReproducir pistas en orden aleatorio." + +#: src/GMWindow.cpp:318 +msgid "Equalizer\t\t" +msgstr "Ecualizador\t\t" + +#: src/GMWindow.cpp:319 +msgid "Sleep Timer\t\tSetup sleeptimer." +msgstr "Temporizador\t\tConfigurar temporizador." + +#: src/GMWindow.cpp:323 +msgid "&Help" +msgstr "&Ayuda" + +#: src/GMWindow.cpp:324 +msgid "&Homepage" +msgstr "&Página principal" + +#: src/GMWindow.cpp:325 +msgid "&Report Issue…" +msgstr "&Reportar Problema..." + +#: src/GMWindow.cpp:327 +msgid "&Sign up for last.fm…" +msgstr "&Regístrate en Last.fm..." + +#: src/GMWindow.cpp:328 +msgid "" +"&Join GMM on last.fm…\t\tJoin the Goggles Music Manager group on last.fm…" +msgstr "" +"&Únete a GMM en last.fm...\t\tÚnete al grupo de Goggles Music Manager en " +"last.fm..." + +#: src/GMWindow.cpp:330 +msgid "&About…" +msgstr "&Acerca de..." + +#: src/GMWindow.cpp:842 src/GMWindow.cpp:940 +msgid "Pause" +msgstr "Pausar" + +#: src/GMWindow.cpp:844 +msgid "Previous" +msgstr "Anterior" + +#: src/GMWindow.cpp:845 +msgid "Next" +msgstr "Siguiente" + +#: src/GMWindow.cpp:920 src/GMWindow.cpp:949 src/GMWindow.cpp:955 +msgid "Start playback." +msgstr "Iniciar reproducción." + +#: src/GMWindow.cpp:926 src/GMWindow.cpp:927 +msgid "Start playback" +msgstr "Iniciar reproducción" + +#: src/GMWindow.cpp:934 src/GMWindow.cpp:941 +msgid "Pause playback." +msgstr "Pausar reproducción." + +#: src/GMWindow.cpp:935 +msgid "Pause playback" +msgstr "Pausar reproducción" + +#: src/GMWindow.cpp:1052 src/GMWindow.cpp:1054 +msgid "Repeat A-B" +msgstr "Repetir A-B" + +#: src/GMWindow.cpp:1053 +msgid "Repeat A" +msgstr "Repetir A" + +#: src/GMWindow.cpp:1195 +msgid "Play File or Stream" +msgstr "Reproducir medio" + +#: src/GMWindow.cpp:1202 +msgid "Please specify a file or url to play:" +msgstr "Por favor, especifique un archivo o url a reproducir:" + +#: src/GMWindow.cpp:1206 +msgid "…" +msgstr "..." + +#: src/GMWindow.cpp:1212 +msgid "Select File" +msgstr "Seleccionar Archivo" + +#: src/GMWindow.cpp:1243 +msgid "Sleep Timer" +msgstr "Temporizador" + +#: src/GMWindow.cpp:1244 +msgid "Setup sleep timer" +msgstr "Configurar temporizador" + +#: src/GMWindow.cpp:1244 +msgid "Stop playback within a certain time" +msgstr "Para la reproducción en un cierto tiempo" + +#: src/GMWindow.cpp:1246 +msgid "&Start Timer" +msgstr "&Iniciar Temporizador" + +#: src/GMWindow.cpp:1251 +msgid "Sleep in" +msgstr "Dormir en" + +#: src/GMWindow.cpp:1253 +msgid "hours and" +msgstr "horas y" + +#: src/GMWindow.cpp:1255 +msgid "minutes." +msgstr "minutos." + +#: src/GMDatabaseSource.h:131 +msgid "Music Library" +msgstr "Biblioteca Musical" + +#: src/GMStreamSource.h:57 +msgid "Internet Radio" +msgstr "Radio de Internet" + +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:122 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:127 +msgid "&Quit" +msgstr "&Salir" + +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:133 +msgid "&Skip" +msgstr "&Saltar" + +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:134 +msgid "Skip &All" +msgstr "Saltar &Todos" + +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:140 +msgid "&Don't Save" +msgstr "&No Guardar" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:220 +msgid "\tPick color" +msgstr "\tElige color" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:229 +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:274 +msgid "\tHue, Saturation, Value" +msgstr "\tTono, saturación, valor" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:239 +msgid "\tRed, Green, Blue" +msgstr "\tRojo, Verde, Azul" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:245 +msgid "&Red:" +msgstr "&Rojo:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:250 +msgid "&Green:" +msgstr "&Verde:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:255 +msgid "&Blue:" +msgstr "&Azul:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:260 +msgid "&Alpha:" +msgstr "&Alfa:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:280 +msgid "Hue:" +msgstr "Tono:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:285 +msgid "Saturation:" +msgstr "Saturación:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:290 +msgid "Value:" +msgstr "Valor:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:295 +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:330 +msgid "Alpha:" +msgstr "Alfa:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:309 +msgid "\tCyan, Magenta, Yellow" +msgstr "\tCian, Magenta, Amarillo" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:315 +msgid "Cyan:" +msgstr "Cian:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:320 +msgid "Magenta:" +msgstr "Magenta:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:325 +msgid "Yellow:" +msgstr "Amarillo:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:344 +msgid "\tBy Name" +msgstr "\tPor Nombre" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:281 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:521 +msgid "Create New Directory" +msgstr "Crear Nuevo Directorio" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:284 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:524 +msgid "Already Exists" +msgstr "Ya Existe" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:288 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:528 +msgid "Cannot Create" +msgstr "No se pudo crear" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:309 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:595 +msgid "Copy File" +msgstr "Copiar Archivo" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:315 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:601 +msgid "Error Copying File" +msgstr "Error Copiando Archivo" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:326 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:618 +msgid "Move File" +msgstr "Mover Archivo" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:332 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:624 +msgid "Error Moving File" +msgstr "Error Moviendo Archivo" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:343 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:641 +msgid "Link File" +msgstr "Enlazar Archivo" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:349 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:647 +msgid "Error Linking File" +msgstr "Error Enlazando Archivo" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:359 +msgid "Deleting file" +msgstr "Borrando archivo" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:361 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:667 +msgid "Error Deleting File" +msgstr "Error Borrando Archivo" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:381 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:719 +msgid "Up one level" +msgstr "Un nivel arriba" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:382 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:720 +msgid "Home directory" +msgstr "Directorio personal" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:383 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:721 +msgid "Work directory" +msgstr "Directorio de trabajo" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:387 +msgid "Sorting" +msgstr "Orden" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:389 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:735 +msgid "Ignore case" +msgstr "Ignorar mayúsculas o minúsculas" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:390 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:746 +msgid "Hidden files" +msgstr "Archivos ocultos" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:393 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:754 +msgid "Bookmarks" +msgstr "Marcadores" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:394 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:757 +msgid "Set bookmark" +msgstr "Crear marcador" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:395 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:758 +msgid "Clear bookmarks" +msgstr "Limpiar marcadores" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:411 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:774 +msgid "New directory..." +msgstr "Nuevo directorio..." + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:412 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:775 +msgid "Copy..." +msgstr "Copiar..." + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:413 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:776 +msgid "Move..." +msgstr "Mover..." + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:414 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:777 +msgid "Link..." +msgstr "Enlazar..." + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:415 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:778 +msgid "Delete..." +msgstr "Eliminar..." + +#: ../../../fox-1.6.37/src/FXFileList.cpp:195 +msgid "Modified Date" +msgstr "Fecha Modificada" + +#: ../../../fox-1.6.37/src/FXFileList.cpp:196 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:731 +msgid "User" +msgstr "Usuario" + +#: ../../../fox-1.6.37/src/FXFileList.cpp:197 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:732 +msgid "Group" +msgstr "Grupo" + +#: ../../../fox-1.6.37/src/FXFileList.cpp:198 +msgid "Attributes" +msgstr "Atributos" + +#: ../../../fox-1.6.37/src/FXFileList.cpp:200 +msgid "Link" +msgstr "Enlace" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:521 +msgid "Create new directory with name: " +msgstr "Crear nuevo directorio con nombre:" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:524 +#, c-format +msgid "File or directory %s already exists.\n" +msgstr "El archivo o directorio %s ya existe.\n" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:528 +#, c-format +msgid "Cannot create directory %s.\n" +msgstr "No se puede crear el directorio %s.\n" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:594 +#, c-format +msgid "" +"Copy file from location:\n" +"\n" +"%s\n" +"\n" +"to location: " +msgstr "" +"Copiar archivo de:\n" +"\n" +"%s\n" +"\n" +"a:" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:601 +#, c-format +msgid "" +"Unable to copy file:\n" +"\n" +"%s to: %s\n" +"\n" +"Continue with operation?" +msgstr "" +"No se pudo copiar archivo:\n" +"\n" +"%s a: %s\n" +"\n" +"¿Continuar con la operación?" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:617 +#, c-format +msgid "" +"Move file from location:\n" +"\n" +"%s\n" +"\n" +"to location: " +msgstr "" +"Mover archivo de:\n" +"\n" +"%s\n" +"\n" +"a:" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:624 +#, c-format +msgid "" +"Unable to move file:\n" +"\n" +"%s to: %s\n" +"\n" +"Continue with operation?" +msgstr "" +"No se pudo mover archivo:\n" +"\n" +"%s a: %s\n" +"\n" +"¿Continuar con la operación?" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:640 +#, c-format +msgid "" +"Link file from location:\n" +"\n" +"%s\n" +"\n" +"to location: " +msgstr "" +"Enlazar archivo de:\n" +"\n" +"%s\n" +"\n" +"a:" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:647 +#, c-format +msgid "" +"Unable to link file:\n" +"\n" +"%s to: %s\n" +"\n" +"Continue with operation?" +msgstr "" +"No se pudo enlazar archivo:\n" +"\n" +"%s a: %s\n" +"\n" +"¿Continuar con la operación?" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:663 +msgid "Deleting files" +msgstr "Borrando archivos" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:663 +#, c-format +msgid "" +"Are you sure you want to delete the file:\n" +"\n" +"%s" +msgstr "" +"Estás seguro de que quieres borrar el archivo:\n" +"\n" +"%s" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:667 +#, c-format +msgid "" +"Unable to delete file:\n" +"\n" +"%s\n" +"\n" +"Continue with operation?" +msgstr "" +"No se pudo borrar archivo:\n" +"\n" +"%s\n" +"\n" +"¿Continuar con la operación?" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:722 +msgid "Select all" +msgstr "Seleccionar todo" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:726 +msgid "Sort by" +msgstr "Ordenar por" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:738 +msgid "View" +msgstr "Ver" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:739 +msgid "Small icons" +msgstr "Iconos pequeños" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:740 +msgid "Big icons" +msgstr "Iconos grandes" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:741 +msgid "Details" +msgstr "Detalles" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:743 +msgid "Rows" +msgstr "Filas" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:744 +msgid "Columns" +msgstr "Columnas" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:747 +msgid "Preview images" +msgstr "Previsualizar imágenes" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:749 +msgid "Normal images" +msgstr "Imágenes normales" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:750 +msgid "Medium images" +msgstr "Imágenes medianas" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:751 +msgid "Giant images" +msgstr "Imágenes gigantes" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:111 +msgid "&Replace" +msgstr "&Reemplazar" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:112 +msgid "Re&place All" +msgstr "Re&emplazar Todos" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:121 +msgid "S&earch for:" +msgstr "B&uscar:" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:129 +msgid "Replace &with:" +msgstr "Reemplazar &con:" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:138 +msgid "Ex&act" +msgstr "Ex&acto" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:139 +msgid "&Ignore Case" +msgstr "&Ignorar mayúsculas y minúsculas" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:140 +msgid "E&xpression" +msgstr "E&xpresión" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:141 +msgid "&Backward" +msgstr "&Hacia Atrás" + +#: ../../../fox-1.6.37/src/FXSearchDialog.cpp:71 +msgid "&Search" +msgstr "&Búsqueda" + +#: ../../../fox-1.6.37/src/FXStatusLine.cpp:82 +msgid "Ready." +msgstr "Listo." + +#~ msgid "Old Name" +#~ msgstr "Nombre anteriorro" + +#~ msgid "New Name" +#~ msgstr "Nombre nuevo" + +#~ msgid "Pitch:" +#~ msgstr "Tono:" + +#~ msgid "Any" +#~ msgstr "Cualquiera" + +#~ msgid "Fixed" +#~ msgstr "Fijo" + +#~ msgid "Variable" +#~ msgstr "Variable" + +#~ msgid " Type:" +#~ msgstr " Tipo:" + +#~ msgid "Scalable" +#~ msgstr "Escalable" + +#~ msgid "Size:" +#~ msgstr "Tamaño:" + +#~ msgid "Family:" +#~ msgstr "Familia:" + +#~ msgid "Always Show Remote" +#~ msgstr "Siempre mostrar remoto" + +#~ msgid "Source\tActive Source Color" +#~ msgstr "Origen\tColor del origen activo" + +#~ msgid "About" +#~ msgstr "Acerca de" + +#~ msgid "" +#~ "&Join GMM on last.fm…\tJoin the Goggles Music Manager group on last.fm…" +#~ "\tJoin the Goggles Music Manager group on last.fm…" +#~ msgstr "" +#~ "&Únete a GMM en Last.fm...\tÚnete al grupo de Goggles Music Manager en " +#~ "Last.fm... \tÚnete al grupo de Goggles Music Manager en Last.fm..." + +#~ msgid "Font" +#~ msgstr "Fuente" + +#~ msgid "Theme Directory:" +#~ msgstr "Directorio del tema:" + +#~ msgid "Select Theme Directory" +#~ msgstr "Seleccionar directorio del tema" + +#~ msgid "thin" +#~ msgstr "fino" + +#~ msgid "normal" +#~ msgstr "normal" + +#~ msgid "bold" +#~ msgstr "negrita" + +#~ msgid "regular" +#~ msgstr "regular" + +#~ msgid "&Weight:" +#~ msgstr "&Peso:" + +#~ msgid "&Style:" +#~ msgstr "&Estilo:" + +#~ msgid "Si&ze:" +#~ msgstr "Ta&maño:" + +#~ msgid "Character Set:" +#~ msgstr "Juego de Caracteres:" + +#~ msgid "West European" +#~ msgstr "Europeo del Oeste" + +#~ msgid "East European" +#~ msgstr "Europeo del Este" + +#~ msgid "South European" +#~ msgstr "Sur europeo" + +#~ msgid "North European" +#~ msgstr "Noreuropeo" + +#~ msgid "Cyrillic" +#~ msgstr "Cirílico" + +#~ msgid "Arabic" +#~ msgstr "Arábigo" + +#~ msgid "Greek" +#~ msgstr "Griego" + +#~ msgid "Hebrew" +#~ msgstr "Hebreo" + +#~ msgid "Turkish" +#~ msgstr "Turco" + +#~ msgid "Nordic" +#~ msgstr "Nórdico" + +#~ msgid "Thai" +#~ msgstr "Tailandés" + +#~ msgid "Baltic" +#~ msgstr "Báltico" + +#~ msgid "Celtic" +#~ msgstr "Celta" + +#~ msgid "Russian" +#~ msgstr "Ruso" + +#~ msgid "Central European (cp1250)" +#~ msgstr "Centro Europeo (cp1250)" + +#~ msgid "Russian (cp1251)" +#~ msgstr "Ruso (cp1251)" + +#~ msgid "Latin1 (cp1252)" +#~ msgstr "Latino1 (cp1252)" + +#~ msgid "Greek (cp1253)" +#~ msgstr "Griego (cp1253)" + +#~ msgid "Turkish (cp1254)" +#~ msgstr "Turco (cp1254)" + +#~ msgid "Hebrew (cp1255)" +#~ msgstr "Hebreo (cp1255)" + +#~ msgid "Arabic (cp1256)" +#~ msgstr "Arábigo (cp1256)" + +#~ msgid "Baltic (cp1257)" +#~ msgstr "Báltico (cp1257)" + +#~ msgid "Vietnam (cp1258)" +#~ msgstr "Vietnamita (cp1258)" + +#~ msgid "Thai (cp874)" +#~ msgstr "Tailandés (cp874)" + +#~ msgid "UNICODE" +#~ msgstr "UNICODE" + +#~ msgid "Set Width:" +#~ msgstr "Elegir Anchura:" + +#~ msgid "All Fonts:" +#~ msgstr "Todas las Fuentes:" + +#~ msgid "Preview:" +#~ msgstr "Previsualización:" + +#, fuzzy +#~ msgid "Import Playlist…\t\tImport a existing playlist" +#~ msgstr "Nueva lista de reproducción...\t\tCrear nueva lista de reproducción" + +#~ msgid "Import Files?" +#~ msgstr "¿Importar archivos?" + +#~ msgid "" +#~ "Would you like import the pasted files and/or directories into the Music " +#~ "Library?" +#~ msgstr "" +#~ "¿Tegustaría importar los archivos pegados y/o los directorios en la " +#~ "Biblioteca de Música?" + +#, fuzzy +#~ msgid "Now Playing" +#~ msgstr "Nueva lista de reproducción" + +#~ msgid "Open URL…\t\tOpen Stream or File" +#~ msgstr "Abrir URL...\t\tAbrir flujo o archivo" + +#~ msgid "Open MRL" +#~ msgstr "Abrir MRL" + +#~ msgid "P&ause" +#~ msgstr "P&ausar" + +#~ msgid "\tPause\tPause Playback" +#~ msgstr "\tPausar\tPausar reproducción" + +#~ msgid "A capella" +#~ msgstr "A capella" + +#~ msgid "Acid" +#~ msgstr "Acid" + +#~ msgid "Acid Jazz" +#~ msgstr "Acid Jazz" + +#~ msgid "Acid Punk" +#~ msgstr "Acid Punk" + +#~ msgid "Acoustic" +#~ msgstr "Acústica" + +#~ msgid "Alternative" +#~ msgstr "Alternativa" + +#~ msgid "AlternRockAmbient" +#~ msgstr "AlternRockAmbient" + +#~ msgid "Avantgarde" +#~ msgstr "Avantgarde" + +#~ msgid "Ballad" +#~ msgstr "Balada" + +#~ msgid "Bass" +#~ msgstr "Bajo" + +#~ msgid "Bebob" +#~ msgstr "Bebob" + +#~ msgid "Big Band" +#~ msgstr "Big Band" + +#~ msgid "Blues" +#~ msgstr "Blues" + +#~ msgid "Bluegrass" +#~ msgstr "Bluegrass" + +#~ msgid "Booty Bass" +#~ msgstr "Booty Bass" + +#~ msgid "Cabaret" +#~ msgstr "Cabaret" + +#~ msgid "Chamber Music" +#~ msgstr "Música de Cámara" + +#~ msgid "Chanson" +#~ msgstr "Chanson" + +#~ msgid "Chorus" +#~ msgstr "Coro" + +#~ msgid "Christian Rap" +#~ msgstr "Rap Cristiano" + +#~ msgid "Classical" +#~ msgstr "Clásica" + +#~ msgid "Classic Rock" +#~ msgstr "Rock Clásico" + +#~ msgid "Club" +#~ msgstr "Club" + +#~ msgid "Comedy" +#~ msgstr "Comedy" + +#~ msgid "Country" +#~ msgstr "Country" + +#~ msgid "Cult" +#~ msgstr "Cult" + +#~ msgid "Dance" +#~ msgstr "Dance" + +#~ msgid "Dance Hall" +#~ msgstr "Dance Hall" + +#~ msgid "Darkwave" +#~ msgstr "Darkwave" + +#~ msgid "Death Metal" +#~ msgstr "Death Metal" + +#~ msgid "Disco" +#~ msgstr "Disco" + +#~ msgid "Dream" +#~ msgstr "Dream" + +#~ msgid "Drum Solo" +#~ msgstr "Solo de Batería" + +#~ msgid "Duet" +#~ msgstr "Dúo" + +#~ msgid "Easy Listening" +#~ msgstr "Easy Listening" + +#~ msgid "Electronic" +#~ msgstr "Electrónica" + +#~ msgid "Ethnic" +#~ msgstr "Étnica" + +#~ msgid "Euro-Dance" +#~ msgstr "Euro-Dance" + +#~ msgid "Euro-House" +#~ msgstr "Euro-House" + +#~ msgid "Euro-Techno" +#~ msgstr "Euro-Techno" + +#~ msgid "Fast Fusion" +#~ msgstr "Fusión Rápida" + +#~ msgid "Folk" +#~ msgstr "Folk" + +#~ msgid "Folk-Rock" +#~ msgstr "Folk-Rock" + +#~ msgid "Folklore" +#~ msgstr "Folclore" + +#~ msgid "Freestyle" +#~ msgstr "Freestyle" + +#~ msgid "Funk" +#~ msgstr "Funk" + +#~ msgid "Fusion" +#~ msgstr "Fusión" + +#~ msgid "Game" +#~ msgstr "Juego" + +#~ msgid "Gangsta" +#~ msgstr "Gangsta" + +#~ msgid "Gospel" +#~ msgstr "Gospel" + +#~ msgid "Gothic" +#~ msgstr "Gótico" + +#~ msgid "Gothic Rock" +#~ msgstr "Rock Gótico" + +#~ msgid "Grunge" +#~ msgstr "Grunge" + +#~ msgid "Hard Rock" +#~ msgstr "Hard Rock" + +#~ msgid "Hip-Hop" +#~ msgstr "Hip-Hop" + +#, fuzzy +#~ msgid "House" +#~ msgstr "Euro-House" + +#, fuzzy +#~ msgid "Humour" +#~ msgstr "HouseHumour" + +#~ msgid "Industrial" +#~ msgstr "Industrial" + +#~ msgid "Instrumental" +#~ msgstr "Instrumental" + +#~ msgid "Instrumental Pop" +#~ msgstr "Pop Instrumental" + +#~ msgid "Instrumental Rock" +#~ msgstr "Rock Instrumental" + +#~ msgid "Jazz" +#~ msgstr "Jazz" + +#~ msgid "Jazz+Funk" +#~ msgstr "Jazz+Funk" + +#~ msgid "Jungle" +#~ msgstr "Jungla" + +#~ msgid "Latin" +#~ msgstr "Latino" + +#~ msgid "Lo-Fi" +#~ msgstr "Lo-Fi" + +#~ msgid "Meditative" +#~ msgstr "Meditativo" + +#~ msgid "Metal" +#~ msgstr "Metal" + +#~ msgid "Musical" +#~ msgstr "Musical" + +#~ msgid "National Folk" +#~ msgstr "Folk Nacional" + +#~ msgid "Native American" +#~ msgstr "Native American" + +#~ msgid "New Age" +#~ msgstr "New Age" + +#~ msgid "New Wave" +#~ msgstr "New Wave" + +#~ msgid "Noise" +#~ msgstr "Ruido" + +#~ msgid "Oldies" +#~ msgstr "Oldies" + +#~ msgid "Opera" +#~ msgstr "Opera" + +#~ msgid "Other" +#~ msgstr "Otro" + +#~ msgid "Polka" +#~ msgstr "Polka" + +#~ msgid "Pop" +#~ msgstr "Pop" + +#~ msgid "Pop-Folk" +#~ msgstr "Pop-Folk" + +#~ msgid "Pop/Funk" +#~ msgstr "Pop/Funk" + +#~ msgid "Porn Groove" +#~ msgstr "Groove Porno" + +#~ msgid "Power Ballad" +#~ msgstr "Power Balada" + +#~ msgid "Pranks" +#~ msgstr "Bromas" + +#~ msgid "Primus" +#~ msgstr "Primus" + +#~ msgid "Progressive Rock" +#~ msgstr "Rock Progresivo" + +#~ msgid "Psychadelic" +#~ msgstr "Psicodélico" + +#~ msgid "Psychedelic Rock" +#~ msgstr "Rock Psicodélico" + +#~ msgid "Punk" +#~ msgstr "Punk" + +#~ msgid "Punk Rock" +#~ msgstr "Punk Rock" + +#~ msgid "R&B" +#~ msgstr "R&B" + +#~ msgid "Rap" +#~ msgstr "Rap" + +#~ msgid "Rave" +#~ msgstr "Rave" + +#~ msgid "Reggae" +#~ msgstr "Reggae" + +#~ msgid "Retro" +#~ msgstr "Retro" + +#~ msgid "Revival" +#~ msgstr "Revival" + +#~ msgid "Rhythmic Soul" +#~ msgstr "Soul Rítmico" + +#~ msgid "Rock" +#~ msgstr "Rock" + +#~ msgid "Rock & Roll" +#~ msgstr "Rock & Roll" + +#~ msgid "Samba" +#~ msgstr "Samba" + +#~ msgid "Satire" +#~ msgstr "Sátira" + +#~ msgid "Showtunes" +#~ msgstr "Showtunes" + +#~ msgid "Ska" +#~ msgstr "Ska" + +#~ msgid "Slow Jam" +#~ msgstr "Jam Lenta" + +#~ msgid "Slow Rock" +#~ msgstr "Rock Lento" + +#~ msgid "Sonata" +#~ msgstr "Sonata" + +#~ msgid "Soul" +#~ msgstr "Soul" + +#~ msgid "Soundtrack" +#~ msgstr "Banda Sonora" + +#~ msgid "Sound Clip" +#~ msgstr "Clip de Sonido" + +#~ msgid "Southern Rock" +#~ msgstr "Rock Sureño" + +#~ msgid "Space" +#~ msgstr "Espacio" + +#~ msgid "Speech" +#~ msgstr "Discurso" + +#~ msgid "Swing" +#~ msgstr "Swing" + +#~ msgid "Symphonic Rock" +#~ msgstr "Rock Sinfónico" + +#~ msgid "Symphony" +#~ msgstr "Sinfonía" + +#~ msgid "Tango" +#~ msgstr "Tango" + +#~ msgid "Techno" +#~ msgstr "Techno" + +#~ msgid "Techno-Industrial" +#~ msgstr "Techno-Industrial" + +#~ msgid "Top 40" +#~ msgstr "40 Principales" + +#~ msgid "Trailer" +#~ msgstr "Trailer" + +#~ msgid "Trance" +#~ msgstr "Trance" + +#~ msgid "Tribal" +#~ msgstr "Tribal" + +#~ msgid "Trip-Hop" +#~ msgstr "Trip-Hop" + +#~ msgid "Vocal" +#~ msgstr "Vocal" + +#~ msgid "Start Up:" +#~ msgstr "Inicio:" + +#~ msgid "Show Main Window" +#~ msgstr "Mostrar ventana principal" + +#~ msgid "Show Mini Remote" +#~ msgstr "Mostrar mini-reproductor" + +#~ msgid "Previous View" +#~ msgstr "Modo anterior" + +#, fuzzy +#~ msgid "Configure Columns…" +#~ msgstr "&Configurar Columnas..." + +#~ msgid "Yes" +#~ msgstr "Sí" + +#~ msgid "Edit…\tF2\tEdit Genre." +#~ msgstr "Editar…\tF2\tEditar Género." + +#~ msgid "Edit…\tF2\tEdit Artist." +#~ msgstr "Editar...\tF2\tEditar Artista." + +#~ msgid "Edit…\tF2\tEdit Album." +#~ msgstr "Editar…\tF2\tEditar Álbum." diff --git a/po/fi.po b/po/fi.po new file mode 100644 index 0000000..2d3cc7e --- /dev/null +++ b/po/fi.po @@ -0,0 +1,2315 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Sander Jansen +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: gogglesmm 0.10.0\n" +"Report-Msgid-Bugs-To: s.jansen@gmail.com\n" +"POT-Creation-Date: 2011-02-09 23:26-0600\n" +"PO-Revision-Date: 2010-07-27 21:17+0200\n" +"Last-Translator: Christian Hellberg \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: src/GMAbout.cpp:136 src/GMDatabaseSource.cpp:218 +#: src/GMDatabaseSource.cpp:2215 src/GMPreferencesDialog.cpp:551 +msgid "&Close" +msgstr "&Sulje" + +#: src/GMColumnDialog.cpp:204 +msgid "Configure Columns" +msgstr "Säädä sarakkeet" + +#: src/GMColumnDialog.cpp:207 ../../../fox-1.6.37/src/FXColorSelector.cpp:168 +msgid "&Accept" +msgstr "&Hyväksy" + +#: src/GMColumnDialog.cpp:208 src/GMDatabaseSource.cpp:98 +#: src/GMDatabaseSource.cpp:1274 src/GMDatabaseSource.cpp:1704 +#: src/GMDatabaseSource.cpp:1804 src/GMDatabaseSource.cpp:1878 +#: src/GMDatabaseSource.cpp:2121 src/GMDatabaseSource.cpp:2182 +#: src/GMImportDialog.cpp:175 src/GMImportDialog.cpp:563 +#: src/GMPlayListSource.cpp:281 src/GMPlayListSource.cpp:410 +#: src/GMSearch.cpp:572 src/GMStreamSource.cpp:169 src/GMStreamSource.cpp:206 +#: src/GMStreamSource.cpp:271 src/GMWindow.cpp:1198 src/GMWindow.cpp:1247 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:107 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:118 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:123 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:129 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:135 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:142 +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:169 +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:135 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:205 +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:113 +msgid "&Cancel" +msgstr "&Peruuta" + +#: src/GMColumnDialog.cpp:212 +msgid "" +"Choose the order of information to appear\n" +"in the track list." +msgstr "" + +#: src/GMColumnDialog.cpp:218 +msgid "Move Up" +msgstr "Siirrä ylös" + +#: src/GMColumnDialog.cpp:219 +msgid "Move Down" +msgstr "Siirrä alas" + +#: src/GMDatabaseSource.cpp:52 +msgid "Invalid Template" +msgstr "Virheellinen malli" + +#: src/GMDatabaseSource.cpp:52 +#, c-format +msgid "" +"The provided template is invalid. The track title %%T needs to be " +"specified.\n" +"Please fix the filename template in the preference panel." +msgstr "" + +#: src/GMDatabaseSource.cpp:72 src/GMTrackDatabase.cpp:193 +msgid "Database Error" +msgstr "Tietokantavirhe" + +#: src/GMDatabaseSource.cpp:72 +msgid "Oops. Database Error" +msgstr "Oho. Ilmeni tietokantavirhe." + +#: src/GMDatabaseSource.cpp:87 +msgid "No changes" +msgstr "Ei muutoksia" + +#: src/GMDatabaseSource.cpp:87 +msgid "Filenames did not require any changes" +msgstr "" + +#: src/GMDatabaseSource.cpp:94 +msgid "Rename Audio Files?" +msgstr "Nimetäänkö äänitiedostot uudelleen?" + +#: src/GMDatabaseSource.cpp:95 +msgid "Renaming Audio Files…" +msgstr "Nimetään uudelleen äänitiedostoja..." + +#: src/GMDatabaseSource.cpp:95 +msgid "The following audio files are going to be renamed" +msgstr "" + +#: src/GMDatabaseSource.cpp:97 +msgid "&Rename" +msgstr "&Nimeä uudelleen" + +#: src/GMDatabaseSource.cpp:121 src/GMDatabaseSource.cpp:125 +msgid "Unable to rename file" +msgstr "Ei voida nimetä uudelleen." + +#: src/GMDatabaseSource.cpp:121 +#, c-format +msgid "" +"Unable to rename:\n" +"%s\n" +"\n" +"to:%s\n" +"Continue renaming files?" +msgstr "" + +#: src/GMDatabaseSource.cpp:125 +#, c-format +msgid "" +"Unable to rename:\n" +"%s\n" +"\n" +"to:%s" +msgstr "" + +#: src/GMDatabaseSource.cpp:160 src/GMImportDialog.cpp:534 +msgid "Filename Template" +msgstr "Tiedoston nimi -malli" + +#: src/GMDatabaseSource.cpp:177 +msgid "" +"Template may contain absolute or relative path, environment variables\n" +"and ~. Relative paths are based on the location of the original file. The\n" +"file extension gets automatically added. The following macros\n" +"may be used:" +msgstr "" + +#: src/GMDatabaseSource.cpp:178 +msgid "" +"%T - title %A - album name\n" +"%P - album artist name %p - track artist name\n" +"%y - year %d - disc number\n" +"%N - track number (2 digits) %n - track number \n" +"%G - genre" +msgstr "" + +#: src/GMDatabaseSource.cpp:185 +msgid "Conditions may be used as well:" +msgstr "" + +#: src/GMDatabaseSource.cpp:186 +msgid "" +"?c - display a if c is not empty else display b.\n" +"?c - display c if not empty\n" +msgstr "" + +#: src/GMDatabaseSource.cpp:195 src/GMDatabaseSource.cpp:1815 +#: src/GMImportDialog.cpp:546 +msgid "Template:" +msgstr "Malli:" + +#: src/GMDatabaseSource.cpp:199 src/GMDatabaseSource.cpp:1819 +msgid "Encoding:" +msgstr "Koodaus:" + +#: src/GMDatabaseSource.cpp:205 +msgid "Exclude:" +msgstr "" + +#: src/GMDatabaseSource.cpp:209 src/GMDatabaseSource.cpp:1825 +msgid "Options:" +msgstr "Vaihtoehdot:" + +#: src/GMDatabaseSource.cpp:210 src/GMDatabaseSource.cpp:1826 +msgid "Replace spaces with underscores" +msgstr "" + +#: src/GMDatabaseSource.cpp:212 src/GMDatabaseSource.cpp:1828 +msgid "Lower case" +msgstr "" + +#: src/GMDatabaseSource.cpp:214 src/GMDatabaseSource.cpp:1830 +msgid "Lower case extension" +msgstr "" + +#: src/GMDatabaseSource.cpp:341 src/GMStreamSource.cpp:63 +msgid "No." +msgstr "Ei." + +#: src/GMDatabaseSource.cpp:342 +msgid "Queue" +msgstr "" + +#: src/GMDatabaseSource.cpp:343 src/GMDatabaseSource.cpp:1391 +#: src/GMTrackView.cpp:238 +msgid "Title" +msgstr "Otsikko" + +#: src/GMDatabaseSource.cpp:344 src/GMDatabaseSource.cpp:1396 +#: src/GMTrackView.cpp:239 +msgid "Artist" +msgstr "Artisti" + +#: src/GMDatabaseSource.cpp:345 src/GMDatabaseSource.cpp:1397 +msgid "Album Artist" +msgstr "Artistin albumi" + +#: src/GMDatabaseSource.cpp:346 src/GMDatabaseSource.cpp:1405 +#: src/GMPreferencesDialog.cpp:540 src/GMTrackView.cpp:240 +msgid "Album" +msgstr "Albumi" + +#: src/GMDatabaseSource.cpp:347 src/GMDatabaseSource.cpp:1364 +#: src/GMDatabaseSource.cpp:1375 src/GMDatabaseSource.cpp:1381 +msgid "Disc" +msgstr "Levy" + +#: src/GMDatabaseSource.cpp:348 src/GMDatabaseSource.cpp:1408 +#: src/GMStreamSource.cpp:66 src/GMStreamSource.cpp:177 +#: src/GMStreamSource.cpp:216 src/GMTrackView.cpp:241 +msgid "Genre" +msgstr "Laji" + +#: src/GMDatabaseSource.cpp:349 src/GMDatabaseSource.cpp:1369 +#: src/GMDatabaseSource.cpp:1387 +msgid "Year" +msgstr "Vuosi" + +#: src/GMDatabaseSource.cpp:350 ../../../fox-1.6.37/src/FXFileSelector.cpp:730 +msgid "Time" +msgstr "Aika" + +#: src/GMDatabaseSource.cpp:490 +msgid "Remove…\tDel\tRemove Genre from Library." +msgstr "" + +#: src/GMDatabaseSource.cpp:496 src/GMDatabaseSource.cpp:505 +#: src/GMPlayListSource.cpp:104 src/GMPlayListSource.cpp:110 +msgid "Copy\tCtrl-C\tCopy associated tracks to the clipboard." +msgstr "" + +#: src/GMDatabaseSource.cpp:499 src/GMDatabaseSource.cpp:508 +msgid "Remove…\tDel\tRemove associated tracks from library." +msgstr "" + +#: src/GMDatabaseSource.cpp:513 src/GMPlayListSource.cpp:116 +msgid "Edit…\tF2\tEdit Track Information." +msgstr "" + +#: src/GMDatabaseSource.cpp:514 src/GMPlayListSource.cpp:117 +msgid "Copy\tCtrl-C\tCopy track(s) to clipboard." +msgstr "" + +#: src/GMDatabaseSource.cpp:518 src/GMPlayListSource.cpp:120 +msgid "Open Folder Location\t\tOpen Folder Location." +msgstr "" + +#: src/GMDatabaseSource.cpp:523 +msgid "Remove…\tDel\tRemove track(s) from library." +msgstr "" + +#: src/GMDatabaseSource.cpp:537 +msgid "New Play List…\t\tCreate a new play list." +msgstr "" + +#: src/GMDatabaseSource.cpp:539 src/GMPlayListSource.cpp:163 +msgid "Export…" +msgstr "Vie..." + +#: src/GMDatabaseSource.cpp:540 +msgid "Information…\t\tLibrary Statistics" +msgstr "" + +#: src/GMDatabaseSource.cpp:542 +msgid "Remove All Tracks\t\tRemove all tracks from the library" +msgstr "" + +#: src/GMDatabaseSource.cpp:1272 +msgid "Edit Track Information" +msgstr "Muokkaa raidan tietoja" + +#: src/GMDatabaseSource.cpp:1275 src/GMPlayListSource.cpp:409 +#: src/GMStreamSource.cpp:205 ../../../fox-1.6.37/src/FXMessageBox.cpp:128 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:143 +msgid "&Save" +msgstr "&Tallenna" + +#: src/GMDatabaseSource.cpp:1315 +msgid "&Tag" +msgstr "" + +#: src/GMDatabaseSource.cpp:1320 +#, fuzzy +msgid "&Properties" +msgstr "Ominaisuudet" + +#: src/GMDatabaseSource.cpp:1325 src/GMImportDialog.cpp:525 +msgid "Filename" +msgstr "Tiedoston nimi" + +#: src/GMDatabaseSource.cpp:1329 ../../../fox-1.6.37/src/FXFileList.cpp:193 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:728 +msgid "Type" +msgstr "Tyyppi" + +#: src/GMDatabaseSource.cpp:1333 ../../../fox-1.6.37/src/FXFileList.cpp:194 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:729 +msgid "Size" +msgstr "Koko" + +#: src/GMDatabaseSource.cpp:1341 src/GMStreamSource.cpp:65 +msgid "Bitrate" +msgstr "Bittinopeus" + +#: src/GMDatabaseSource.cpp:1345 +msgid "Samplerate" +msgstr "" + +#: src/GMDatabaseSource.cpp:1349 +msgid "Channels" +msgstr "Kanavat" + +#: src/GMDatabaseSource.cpp:1358 src/GMPreferencesDialog.cpp:539 +msgid "Track" +msgstr "Raita" + +#: src/GMDatabaseSource.cpp:1403 +msgid "\tSeparate Artists" +msgstr "\tErottele artistit" + +#: src/GMDatabaseSource.cpp:1403 +msgid "\tShared Artists" +msgstr "\tJaetut artistit" + +#: src/GMDatabaseSource.cpp:1416 +msgid "Auto track number. Offset:" +msgstr "" + +#: src/GMDatabaseSource.cpp:1425 +msgid "Update Tag in File" +msgstr "Päivitä tiedoston avainsana" + +#: src/GMDatabaseSource.cpp:1430 +msgid "Update Filename" +msgstr "Päivitä tiedoston nimi" + +#: src/GMDatabaseSource.cpp:1431 +msgid "Set export template…" +msgstr "Aseta tuontimalli..." + +#: src/GMDatabaseSource.cpp:1645 +msgid "Update Tags?" +msgstr "Päivitetäänkö avainsanat?" + +#: src/GMDatabaseSource.cpp:1645 +msgid "" +"No tracks were updated.\n" +"Would you still like to write the tags for the selected tracks?" +msgstr "" + +#: src/GMDatabaseSource.cpp:1700 +msgid "Remove Audio Files?" +msgstr "Poistetaanko äänitiedosto?" + +#: src/GMDatabaseSource.cpp:1701 +msgid "Remove Audio Files..." +msgstr "Poista äänitiedostot..." + +#: src/GMDatabaseSource.cpp:1701 +msgid "The following audio files are going to be removed" +msgstr "" + +#: src/GMDatabaseSource.cpp:1703 src/GMDatabaseSource.cpp:1877 +#: src/GMPlayListSource.cpp:280 src/GMStreamSource.cpp:270 +msgid "&Remove" +msgstr "&Poista" + +#: src/GMDatabaseSource.cpp:1729 +msgid "Export Main Library" +msgstr "Tuo pääkirjasto" + +#: src/GMDatabaseSource.cpp:1731 +msgid "Export Play List" +msgstr "Tuo soittolista" + +#: src/GMDatabaseSource.cpp:1744 +msgid "Overwrite File?" +msgstr "Ylikirjoitetaanko tiedosto?" + +#: src/GMDatabaseSource.cpp:1744 +msgid "File already exists. Would you like to overwrite it?" +msgstr "" + +#: src/GMDatabaseSource.cpp:1784 +msgid "Export Genre" +msgstr "Vie laji" + +#: src/GMDatabaseSource.cpp:1785 +msgid "Export tracks with genre to destination directory." +msgstr "" + +#: src/GMDatabaseSource.cpp:1787 +msgid "Export Artists" +msgstr "Vie artistit" + +#: src/GMDatabaseSource.cpp:1788 +msgid "Export tracks from artist to destination directory." +msgstr "" + +#: src/GMDatabaseSource.cpp:1790 +msgid "Export Albums" +msgstr "Vie albumit" + +#: src/GMDatabaseSource.cpp:1791 +msgid "Export tracks from album to destination directory." +msgstr "" + +#: src/GMDatabaseSource.cpp:1793 +msgid "Export Tracks" +msgstr "Vie raidat" + +#: src/GMDatabaseSource.cpp:1794 +msgid "Export tracks to destination directory." +msgstr "" + +#: src/GMDatabaseSource.cpp:1803 +msgid "&Export" +msgstr "&Vie" + +#: src/GMDatabaseSource.cpp:1853 src/GMPlayListSource.cpp:261 +msgid "Remove Genre?" +msgstr "Poistetaanko laji?" + +#: src/GMDatabaseSource.cpp:1854 +msgid "Remove tracks with genre from library?" +msgstr "" + +#: src/GMDatabaseSource.cpp:1858 src/GMPlayListSource.cpp:264 +msgid "Remove Artist?" +msgstr "Poistetaanko artisti?" + +#: src/GMDatabaseSource.cpp:1859 +msgid "Remove tracks from artist from library?" +msgstr "" + +#: src/GMDatabaseSource.cpp:1863 src/GMPlayListSource.cpp:267 +msgid "Remove Album?" +msgstr "" + +#: src/GMDatabaseSource.cpp:1864 +msgid "Remove tracks from album from library?" +msgstr "" + +#: src/GMDatabaseSource.cpp:1868 src/GMPlayListSource.cpp:270 +msgid "Remove Track(s)?" +msgstr "" + +#: src/GMDatabaseSource.cpp:1869 +msgid "Remove track(s) from library?" +msgstr "" + +#: src/GMDatabaseSource.cpp:1881 src/GMPlayListSource.cpp:285 +msgid "Remove tracks from disk" +msgstr "" + +#: src/GMDatabaseSource.cpp:1895 src/GMDatabaseSource.cpp:1899 +#: src/GMDatabaseSource.cpp:1903 src/GMDatabaseSource.cpp:1907 +#: src/GMPlayListSource.cpp:308 src/GMStreamSource.cpp:280 +msgid "Library Error" +msgstr "Kirjastovirhe" + +#: src/GMDatabaseSource.cpp:1895 +msgid "Unable to remove genre from the library" +msgstr "" + +#: src/GMDatabaseSource.cpp:1899 +msgid "Unable to remove artist from the library" +msgstr "" + +#: src/GMDatabaseSource.cpp:1903 +msgid "Unable to remove album from the library" +msgstr "" + +#: src/GMDatabaseSource.cpp:1907 src/GMPlayListSource.cpp:308 +msgid "Unable to remove track from the library." +msgstr "" + +#: src/GMDatabaseSource.cpp:2117 src/GMDatabaseSource.cpp:2118 +msgid "Create Playlist" +msgstr "" + +#: src/GMDatabaseSource.cpp:2118 +msgid "Specify name of the new playlist" +msgstr "" + +#: src/GMDatabaseSource.cpp:2120 +msgid "&Create" +msgstr "&Luo" + +#: src/GMDatabaseSource.cpp:2125 src/GMPlayListSource.cpp:415 +#: ../../../fox-1.6.37/src/FXFileList.cpp:192 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:727 +msgid "Name" +msgstr "" + +#: src/GMDatabaseSource.cpp:2127 +msgid "New Playlist" +msgstr "" + +#: src/GMDatabaseSource.cpp:2178 src/GMDatabaseSource.cpp:2179 +msgid "Clear Music Library?" +msgstr "" + +#: src/GMDatabaseSource.cpp:2179 +msgid "Remove all tracks from the music library?" +msgstr "" + +#: src/GMDatabaseSource.cpp:2181 +msgid "&Remove All" +msgstr "&Poista kaikki" + +#: src/GMDatabaseSource.cpp:2185 +msgid "Keep play lists" +msgstr "" + +#: src/GMDatabaseSource.cpp:2212 src/GMDatabaseSource.cpp:2213 +msgid "Music Library Information" +msgstr "" + +#: src/GMDatabaseSource.cpp:2213 +msgid "You music collection consists of…" +msgstr "" + +#: src/GMDatabaseSource.cpp:2221 +msgid "Tracks:" +msgstr "" + +#: src/GMDatabaseSource.cpp:2226 +msgid "Artists:" +msgstr "Artistit:" + +#: src/GMDatabaseSource.cpp:2230 +msgid "Albums:" +msgstr "" + +#: src/GMDatabaseSource.cpp:2234 +msgid "Total Time:" +msgstr "" + +#: src/GMEQDialog.cpp:96 +msgid "Equalizer" +msgstr "" + +#: src/GMEQDialog.cpp:135 +msgid "Equalizer:" +msgstr "" + +#: src/GMEQDialog.cpp:137 +msgid "\tSave" +msgstr "" + +#: src/GMEQDialog.cpp:138 +msgid "\tReset" +msgstr "" + +#: src/GMEQDialog.cpp:139 +msgid "\tRemove" +msgstr "\tPoista" + +#: src/GMEQDialog.cpp:172 +msgid "Pre-amp" +msgstr "" + +#: src/GMEQDialog.cpp:244 src/GMEQDialog.cpp:254 +msgid "Disabled" +msgstr "" + +#: src/GMEQDialog.cpp:247 src/GMEQDialog.cpp:299 +msgid "Manual" +msgstr "" + +#: src/GMEQDialog.cpp:314 +msgid "Delete Preset" +msgstr "" + +#: src/GMEQDialog.cpp:314 +#, c-format +msgid "Are you sure you want to delete %s preset?" +msgstr "" + +#: src/GMEQDialog.cpp:334 +msgid "Preset Name" +msgstr "" + +#: src/GMEQDialog.cpp:334 +msgid "Please enter preset name:" +msgstr "" + +#: src/GMEQDialog.cpp:338 +msgid "Overwrite Preset" +msgstr "" + +#: src/GMEQDialog.cpp:338 +#, c-format +msgid "Preset %s already exists. Would you like to overwrite it?" +msgstr "" + +#: src/GMFontDialog.cpp:209 +msgid "Ultra Condensed" +msgstr "" + +#: src/GMFontDialog.cpp:210 +msgid "Extra Condensed" +msgstr "" + +#: src/GMFontDialog.cpp:211 +msgid "Condensed" +msgstr "" + +#: src/GMFontDialog.cpp:212 +msgid "Semi Condensed" +msgstr "" + +#: src/GMFontDialog.cpp:214 +msgid "Semi Expanded" +msgstr "" + +#: src/GMFontDialog.cpp:215 +msgid "Expanded" +msgstr "" + +#: src/GMFontDialog.cpp:216 +msgid "Extra Expanded" +msgstr "" + +#: src/GMFontDialog.cpp:217 +msgid "Ultra Expanded" +msgstr "" + +#: src/GMFontDialog.cpp:224 src/GMPreferencesDialog.cpp:1223 +msgid "Thin" +msgstr "" + +#: src/GMFontDialog.cpp:225 src/GMPreferencesDialog.cpp:1224 +msgid "Extra Light" +msgstr "" + +#: src/GMFontDialog.cpp:226 src/GMPreferencesDialog.cpp:1225 +msgid "Light" +msgstr "" + +#: src/GMFontDialog.cpp:228 src/GMPreferencesDialog.cpp:1227 +#, fuzzy +msgid "Medium" +msgstr "Keskikokoiset kuvat" + +#: src/GMFontDialog.cpp:229 src/GMPreferencesDialog.cpp:1228 +msgid "Demibold" +msgstr "" + +#: src/GMFontDialog.cpp:230 src/GMPreferencesDialog.cpp:1229 +msgid "Bold" +msgstr "" + +#: src/GMFontDialog.cpp:231 src/GMPreferencesDialog.cpp:1230 +msgid "Extra Bold" +msgstr "" + +#: src/GMFontDialog.cpp:232 src/GMPreferencesDialog.cpp:1231 +msgid "Heavy" +msgstr "" + +#: src/GMFontDialog.cpp:239 +msgid "Reverse Oblique" +msgstr "" + +#: src/GMFontDialog.cpp:240 +msgid "Reverse Italic" +msgstr "" + +#: src/GMFontDialog.cpp:242 +msgid "Italic" +msgstr "" + +#: src/GMFontDialog.cpp:243 +msgid "Oblique" +msgstr "" + +#: src/GMFontDialog.cpp:265 +msgid "Normal" +msgstr "" + +#: src/GMImportDialog.cpp:91 ../../../fox-1.6.37/src/FXDirSelector.cpp:137 +msgid "&Directory:" +msgstr "&Hakemisto:" + +#: src/GMImportDialog.cpp:166 ../../../fox-1.6.37/src/FXFileSelector.cpp:196 +msgid "&File Name:" +msgstr "&Tiedoston nimi:" + +#: src/GMImportDialog.cpp:168 ../../../fox-1.6.37/src/FXMessageBox.cpp:102 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:106 +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:134 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:198 +msgid "&OK" +msgstr "&OK" + +#: src/GMImportDialog.cpp:170 ../../../fox-1.6.37/src/FXFileSelector.cpp:200 +msgid "File F&ilter:" +msgstr "" + +#: src/GMImportDialog.cpp:174 ../../../fox-1.6.37/src/FXFileSelector.cpp:204 +msgid "Read Only" +msgstr "Vain luku" + +#: src/GMImportDialog.cpp:179 src/GMSearch.cpp:565 src/GMSearch.cpp:647 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:208 +msgid "Directory:" +msgstr "" + +#: src/GMImportDialog.cpp:186 ../../../fox-1.6.37/src/FXFileSelector.cpp:228 +msgid "&Set bookmark\t\tBookmark current directory." +msgstr "" + +#: src/GMImportDialog.cpp:187 ../../../fox-1.6.37/src/FXFileSelector.cpp:229 +msgid "&Clear bookmarks\t\tClear bookmarks." +msgstr "" + +#: src/GMImportDialog.cpp:203 ../../../fox-1.6.37/src/FXFileSelector.cpp:244 +msgid "\tGo up one directory\tMove up to higher directory." +msgstr "" + +#: src/GMImportDialog.cpp:204 ../../../fox-1.6.37/src/FXFileSelector.cpp:245 +msgid "\tGo to home directory\tBack to home directory." +msgstr "" + +#: src/GMImportDialog.cpp:205 ../../../fox-1.6.37/src/FXFileSelector.cpp:246 +msgid "\tGo to work directory\tBack to working directory." +msgstr "" + +#: src/GMImportDialog.cpp:206 ../../../fox-1.6.37/src/FXFileSelector.cpp:247 +msgid "\tBookmarks\tVisit bookmarked directories." +msgstr "" + +#: src/GMImportDialog.cpp:209 ../../../fox-1.6.37/src/FXFileSelector.cpp:250 +msgid "\tCreate new directory\tCreate new directory." +msgstr "" + +#: src/GMImportDialog.cpp:210 ../../../fox-1.6.37/src/FXFileSelector.cpp:251 +msgid "\tShow list\tDisplay directory with small icons." +msgstr "" + +#: src/GMImportDialog.cpp:211 ../../../fox-1.6.37/src/FXFileSelector.cpp:252 +msgid "\tShow icons\tDisplay directory with big icons." +msgstr "" + +#: src/GMImportDialog.cpp:212 ../../../fox-1.6.37/src/FXFileSelector.cpp:253 +msgid "\tShow details\tDisplay detailed directory listing." +msgstr "" + +#: src/GMImportDialog.cpp:213 ../../../fox-1.6.37/src/FXFileSelector.cpp:254 +msgid "\tShow hidden files\tShow hidden files and directories." +msgstr "" + +#: src/GMImportDialog.cpp:213 ../../../fox-1.6.37/src/FXFileSelector.cpp:254 +msgid "\tHide Hidden Files\tHide hidden files and directories." +msgstr "" + +#: src/GMImportDialog.cpp:412 +msgid "Synchronize Folder" +msgstr "" + +#: src/GMImportDialog.cpp:414 +msgid "Import Playlist" +msgstr "" + +#: src/GMImportDialog.cpp:416 +msgid "Import Music" +msgstr "" + +#: src/GMImportDialog.cpp:448 +msgid "&File(s)" +msgstr "&Tiedosto(t)" + +#: src/GMImportDialog.cpp:475 +msgid "&Directory" +msgstr "&Hakemisto" + +#: src/GMImportDialog.cpp:478 +msgid "Exclude Filter\tFilter out directories and/or files based on pattern" +msgstr "" + +#: src/GMImportDialog.cpp:481 +msgid "Folders:" +msgstr "" + +#: src/GMImportDialog.cpp:483 +msgid "Files:" +msgstr "" + +#: src/GMImportDialog.cpp:499 src/GMImportDialog.cpp:560 +msgid "&Sync" +msgstr "" + +#: src/GMImportDialog.cpp:501 +msgid "Sync Operation" +msgstr "" + +#: src/GMImportDialog.cpp:504 +msgid "Import new tracks\tImports files not yet in the database." +msgstr "" + +#: src/GMImportDialog.cpp:505 +msgid "Remove tracks that have been deleted from disk" +msgstr "" + +#: src/GMImportDialog.cpp:507 +msgid "Update existing tracks:" +msgstr "" + +#: src/GMImportDialog.cpp:508 +msgid "" +"Modified since last import\tOnly reread the tag when the file has been " +"modified." +msgstr "" + +#: src/GMImportDialog.cpp:510 +msgid "All\tAlways read the tags" +msgstr "" + +#: src/GMImportDialog.cpp:511 +msgid "Remove tracks found in folder from database" +msgstr "" + +#: src/GMImportDialog.cpp:514 +msgid "&Track" +msgstr "&Raita" + +#: src/GMImportDialog.cpp:517 +msgid "Parse Settings" +msgstr "" + +#: src/GMImportDialog.cpp:521 +msgid "Parse info from:" +msgstr "" + +#: src/GMImportDialog.cpp:524 +msgid "Tag" +msgstr "Aivainsana" + +#: src/GMImportDialog.cpp:526 +msgid "Both" +msgstr "" + +#: src/GMImportDialog.cpp:528 +msgid "Default value:" +msgstr "" + +#: src/GMImportDialog.cpp:532 +msgid "Set track number based on scan order." +msgstr "" + +#: src/GMImportDialog.cpp:537 +msgid "" +"%T - title %A - album name\n" +"%P - album artist name %p - track artist name \n" +"%N - track number %G - genre" +msgstr "" + +#: src/GMImportDialog.cpp:549 +msgid "Replace underscores with spaces" +msgstr "" + +#: src/GMImportDialog.cpp:562 +msgid "&Import" +msgstr "&Tuo" + +#: src/GMPlayer.cpp:212 +msgid "Unable to initialize audio driver." +msgstr "" + +#: src/GMPlayer.cpp:714 +msgid "Unknown host." +msgstr "" + +#: src/GMPlayer.cpp:715 +msgid "Unknown device" +msgstr "" + +#: src/GMPlayer.cpp:716 +msgid "Network not reachable." +msgstr "" + +#: src/GMPlayer.cpp:717 +msgid "Audio output unavailable." +msgstr "" + +#: src/GMPlayer.cpp:718 +msgid "Connection Refused." +msgstr "" + +#: src/GMPlayer.cpp:719 +msgid "File not found." +msgstr "" + +#: src/GMPlayer.cpp:720 +msgid "Resource not accessible. Check permissions" +msgstr "" + +#: src/GMPlayer.cpp:721 +msgid "Read Error" +msgstr "" + +#: src/GMPlayer.cpp:722 +msgid "Error while loading library/plugin" +msgstr "" + +#: src/GMPlayer.cpp:723 +msgid "Warning" +msgstr "" + +#: src/GMPlayer.cpp:724 +msgid "Security Warning" +msgstr "" + +#: src/GMPlayer.cpp:725 +msgid "Unknown Error" +msgstr "" + +#: src/GMPlayer.cpp:761 +msgid "Error" +msgstr "" + +#: src/GMPlayerManager.cpp:439 +#, c-format +msgid "Unable to create directory %s\n" +msgstr "" + +#: src/GMPlayerManager.cpp:612 +msgid "" +"For some reason the FOX library was compiled without PNG support.\n" +"In order to show all icons, Goggles Music Manager requires PNG\n" +"support in the FOX library. If you've compiled FOX yourself, most\n" +"likely the libpng header files were not installed on your system." +msgstr "" + +#: src/GMPlayerManager.cpp:623 +msgid "Session bus not available. All features requiring dbus are disabled." +msgstr "" + +#: src/GMPlayerManager.cpp:633 +msgid "A DBus error occurred. All features requiring sessionbus are disabled." +msgstr "" + +#: src/GMPlayerManager.cpp:644 +msgid "" +"Session Bus not available. All features requiring sessionbus are disabled." +msgstr "" + +#: src/GMPlayerManager.cpp:719 src/GMPreferencesDialog.cpp:594 +msgid "Audio Device Error" +msgstr "" + +#: src/GMPlayerManager.cpp:1551 +msgid "Last.FM Error" +msgstr "" + +#: src/GMPlayerManager.cpp:1558 +msgid "Playback Error" +msgstr "" + +#: src/GMPlayerManager.cpp:1708 +#, c-format +msgid "" +"%s\n" +"%s (%d)" +msgstr "" + +#: src/GMPlayListSource.cpp:99 src/GMPlayListSource.cpp:105 +#: src/GMPlayListSource.cpp:111 src/GMPlayListSource.cpp:121 +msgid "Remove…\tDel\tRemove track(s) from play list." +msgstr "" + +#: src/GMPlayListSource.cpp:161 +msgid "Edit…" +msgstr "" + +#: src/GMPlayListSource.cpp:162 +msgid "Import…" +msgstr "" + +#: src/GMPlayListSource.cpp:164 +msgid "Remove Playlist" +msgstr "" + +#: src/GMPlayListSource.cpp:262 +msgid "Remove tracks with genre from play list?" +msgstr "" + +#: src/GMPlayListSource.cpp:265 +msgid "Remove tracks from artist from play list?" +msgstr "" + +#: src/GMPlayListSource.cpp:268 +msgid "Remove tracks from album from play list?" +msgstr "" + +#: src/GMPlayListSource.cpp:271 +msgid "Remove track(s) from play list?" +msgstr "" + +#: src/GMPlayListSource.cpp:284 +msgid "Remove tracks from music library" +msgstr "" + +#: src/GMPlayListSource.cpp:406 src/GMPlayListSource.cpp:407 +msgid "Edit Playlist" +msgstr "" + +#: src/GMPlayListSource.cpp:407 +msgid "Change playlist name" +msgstr "" + +#: src/GMPlayListSource.cpp:432 +msgid "Delete Play List?" +msgstr "" + +#: src/GMPlayListSource.cpp:432 +msgid "Are you sure you want to delete the playlist?" +msgstr "" + +#: src/GMPlayListSource.cpp:432 ../../../fox-1.6.37/src/FXMessageBox.cpp:111 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:116 +msgid "&Yes" +msgstr "&Kyllä" + +#: src/GMPlayListSource.cpp:432 ../../../fox-1.6.37/src/FXMessageBox.cpp:112 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:117 +msgid "&No" +msgstr "&Ei" + +#: src/GMPreferencesDialog.cpp:252 +msgid "Preferences" +msgstr "" + +#: src/GMPreferencesDialog.cpp:286 +msgid "&General" +msgstr "&Yleinen" + +#: src/GMPreferencesDialog.cpp:289 +msgid "Sort Options" +msgstr "" + +#: src/GMPreferencesDialog.cpp:293 +msgid "Ignore leading words" +msgstr "" + +#: src/GMPreferencesDialog.cpp:296 +msgid "Album Covers" +msgstr "" + +#: src/GMPreferencesDialog.cpp:299 +msgid "Show album cover of playing track\tShow album cover of playing track" +msgstr "" + +#: src/GMPreferencesDialog.cpp:300 +msgid "Show album covers in album browser\tShow album covers in album browser" +msgstr "" + +#: src/GMPreferencesDialog.cpp:302 +msgid "System Tray" +msgstr "" + +#: src/GMPreferencesDialog.cpp:304 +msgid "Show Tray Icon\tShow tray icon in the system tray." +msgstr "" + +#: src/GMPreferencesDialog.cpp:307 +msgid "" +"Show Track Change Notifications\tInform notification daemon of track changes." +msgstr "" + +#: src/GMPreferencesDialog.cpp:311 +msgid "Last.fm" +msgstr "" + +#: src/GMPreferencesDialog.cpp:315 +msgid "" +"This version of Goggles Music Manager is\n" +"not supported by Last-FM. Please upgrade\n" +"to a newer version of GMM." +msgstr "" + +#: src/GMPreferencesDialog.cpp:321 +msgid "Service:" +msgstr "" + +#: src/GMPreferencesDialog.cpp:338 +#, fuzzy +msgid "&Sign up…" +msgstr "&Rekisteröidy last.fm-palveluun…" + +#: src/GMPreferencesDialog.cpp:344 +msgid "Username:" +msgstr "" + +#: src/GMPreferencesDialog.cpp:346 +msgid "Password:" +msgstr "" + +#: src/GMPreferencesDialog.cpp:349 +msgid "Scrobble" +msgstr "" + +#: src/GMPreferencesDialog.cpp:359 +msgid "&Window" +msgstr "&Ikkuna" + +#: src/GMPreferencesDialog.cpp:362 +msgid "Window" +msgstr "" + +#: src/GMPreferencesDialog.cpp:365 +msgid "Close button minimizes to tray" +msgstr "" + +#: src/GMPreferencesDialog.cpp:366 +msgid "Show Status Bar" +msgstr "" + +#: src/GMPreferencesDialog.cpp:368 +msgid "Show Icons in Track Browser" +msgstr "" + +#: src/GMPreferencesDialog.cpp:370 +msgid "Display playing track in title bar" +msgstr "" + +#: src/GMPreferencesDialog.cpp:372 +msgid "Player Controls" +msgstr "" + +#: src/GMPreferencesDialog.cpp:376 +msgid "Location:" +msgstr "" + +#: src/GMPreferencesDialog.cpp:378 +msgid "Top" +msgstr "" + +#: src/GMPreferencesDialog.cpp:379 +msgid "Bottom" +msgstr "" + +#: src/GMPreferencesDialog.cpp:387 +msgid "Title Format:" +msgstr "" + +#: src/GMPreferencesDialog.cpp:391 +msgid "Style:" +msgstr "" + +#: src/GMPreferencesDialog.cpp:392 +msgid "Show Labels" +msgstr "" + +#: src/GMPreferencesDialog.cpp:395 +msgid "Large Icons" +msgstr "" + +#: src/GMPreferencesDialog.cpp:399 +msgid "A&ppearance" +msgstr "U&lkoasu" + +#: src/GMPreferencesDialog.cpp:402 +msgid "Colors" +msgstr "" + +#: src/GMPreferencesDialog.cpp:409 +msgid "fg\tForeground Color" +msgstr "" + +#: src/GMPreferencesDialog.cpp:410 +msgid "bg\tBackground Color" +msgstr "" + +#: src/GMPreferencesDialog.cpp:411 +msgid "alt bg\tAlternative Background Color" +msgstr "" + +#: src/GMPreferencesDialog.cpp:422 +msgid "Normal\tNormal Text Color" +msgstr "" + +#: src/GMPreferencesDialog.cpp:426 +msgid "Base\tBase Color" +msgstr "" + +#: src/GMPreferencesDialog.cpp:429 +msgid "Selected\tSelected Text Color" +msgstr "" + +#: src/GMPreferencesDialog.cpp:433 +msgid "Menu\tMenu Base Color" +msgstr "" + +#: src/GMPreferencesDialog.cpp:437 +msgid "Menu\tMenu Text Color" +msgstr "" + +#: src/GMPreferencesDialog.cpp:441 +msgid "Border\tBorder Color" +msgstr "" + +#: src/GMPreferencesDialog.cpp:445 +msgid "Tooltip\tTooltip Color" +msgstr "" + +#: src/GMPreferencesDialog.cpp:449 +msgid "Hilite\tHilite Color" +msgstr "" + +#: src/GMPreferencesDialog.cpp:456 +msgid "Shadow\tShadow Color" +msgstr "" + +#: src/GMPreferencesDialog.cpp:459 +msgid "Playing\tPlaying Track Color" +msgstr "" + +#: src/GMPreferencesDialog.cpp:463 +msgid "Tray\tTray Background Color" +msgstr "" + +#: src/GMPreferencesDialog.cpp:473 +msgid "Presets:" +msgstr "" + +#: src/GMPreferencesDialog.cpp:481 +msgid "Font & Icons" +msgstr "" + +#: src/GMPreferencesDialog.cpp:487 +msgid "Default Font" +msgstr "" + +#: src/GMPreferencesDialog.cpp:489 +msgid "Change…" +msgstr "" + +#: src/GMPreferencesDialog.cpp:490 +msgid "Icons" +msgstr "" + +#: src/GMPreferencesDialog.cpp:509 +msgid "&Audio" +msgstr "&Ääni" + +#: src/GMPreferencesDialog.cpp:512 +msgid "Engine" +msgstr "" + +#: src/GMPreferencesDialog.cpp:516 +msgid "Audio Driver:" +msgstr "" + +#: src/GMPreferencesDialog.cpp:527 +msgid "Close audio device on pause." +msgstr "" + +#: src/GMPreferencesDialog.cpp:528 +msgid "Turn off playback engine on stop." +msgstr "" + +#: src/GMPreferencesDialog.cpp:529 +msgid "" +"Turn on playback engine on startup.\tFor faster startup, playback engine is " +"normally started when first track is played.\tFor faster startup, playback " +"engine is normally started when first track is played." +msgstr "" + +#: src/GMPreferencesDialog.cpp:532 +msgid "Playback" +msgstr "" + +#: src/GMPreferencesDialog.cpp:536 +msgid "Replay Gain:" +msgstr "" + +#: src/GMPreferencesDialog.cpp:538 +msgid "Off" +msgstr "" + +#: src/GMPreferencesDialog.cpp:543 +msgid "Gapless playback" +msgstr "" + +#: src/GMPreferencesDialog.cpp:544 +msgid "Volume Normalization" +msgstr "" + +#: src/GMPreferencesDialog.cpp:594 +#, c-format +msgid "Failed to open requested audio driver: %s" +msgstr "" + +#: src/GMPreferencesDialog.cpp:678 +msgid "Current" +msgstr "" + +#: src/GMPreferencesDialog.cpp:696 +msgid "Custom" +msgstr "" + +#: src/GMPreferencesDialog.cpp:769 src/GMPreferencesDialog.cpp:774 +#: src/GMWindow.cpp:1133 src/GMWindow.cpp:1140 src/GMWindow.cpp:1147 +#: src/GMWindow.cpp:1154 +msgid "Unable to launch webbrowser" +msgstr "" + +#: src/GMPreferencesDialog.cpp:1172 +msgid "Select Normal Font" +msgstr "" + +#: src/GMRemote.cpp:295 +msgid "&Next" +msgstr "&Seuraava" + +#: src/GMRemote.cpp:296 +msgid "P&revious" +msgstr "E&dellinen" + +#: src/GMRemote.cpp:297 src/GMWindow.cpp:1197 +msgid "&Play" +msgstr "&Toista" + +#: src/GMRemote.cpp:298 +msgid "&Stop" +msgstr "&Pysäytä" + +#: src/GMRemote.cpp:300 +msgid "Show Browser" +msgstr "" + +#: src/GMRemote.cpp:301 src/GMTrayIcon.cpp:307 +msgid "Quit" +msgstr "" + +#: src/GMRemote.cpp:385 +msgid "\tShow Browser\tShow Browser" +msgstr "" + +#: src/GMRemote.cpp:387 +msgid "\tStart Playback\tStart Playback" +msgstr "" + +#: src/GMRemote.cpp:388 +msgid "\tStop Playback\tStop Playback" +msgstr "" + +#: src/GMRemote.cpp:390 +msgid "\tPlay Previous Track\tPlay previous track." +msgstr "" + +#: src/GMRemote.cpp:391 +msgid "\tPlay Next Track\tPlay next track." +msgstr "" + +#: src/GMRemote.cpp:397 src/GMWindow.cpp:222 +msgid "\tAdjust Volume\tAdjust Volume" +msgstr "" + +#: src/GMSearch.cpp:128 +msgid "Unable to open the database" +msgstr "" + +#: src/GMSearch.cpp:252 +msgid "Database Error: Unable to retrieve all filenames." +msgstr "" + +#: src/GMSearch.cpp:280 +msgid "Unable to update track" +msgstr "" + +#: src/GMSearch.cpp:439 +msgid "Unable to insert track into the database" +msgstr "" + +#: src/GMSearch.cpp:505 src/GMTrackDatabase.cpp:205 +msgid "Fatal Error" +msgstr "" + +#: src/GMSearch.cpp:505 +#, c-format +msgid "" +"%s\n" +"Please contact support if this error keeps occuring." +msgstr "" + +#: src/GMSearch.cpp:534 src/GMSearch.cpp:584 +msgid "Updating Database..." +msgstr "" + +#: src/GMSearch.cpp:537 src/GMSearch.cpp:626 +msgid "Please wait. This may take a while." +msgstr "" + +#: src/GMSearch.cpp:555 src/GMSearch.cpp:637 +msgid "New Tracks:" +msgstr "" + +#: src/GMSearch.cpp:561 src/GMSearch.cpp:643 +msgid "File:" +msgstr "" + +#: src/GMSearch.cpp:594 src/GMSearch.cpp:623 +msgid "Importing Files..." +msgstr "" + +#: src/GMSearch.cpp:652 +msgid "&Stop Import" +msgstr "&Pysäytä tuonti" + +#: src/GMSourceView.cpp:54 +msgid "Sources\tPress to change sorting order\tPress to change sorting order" +msgstr "" + +#: src/GMSourceView.cpp:245 src/GMWindow.cpp:261 +msgid "New Playlist…\t\tCreate a new playlist" +msgstr "" + +#: src/GMSourceView.cpp:246 src/GMWindow.cpp:262 +msgid "Import Playlist…\t\tImport existing playlist" +msgstr "" + +#: src/GMSourceView.cpp:247 src/GMWindow.cpp:263 +msgid "New Radio Station…\t\tCreate a new playlist" +msgstr "" + +#: src/GMStreamSource.cpp:64 +msgid "Station" +msgstr "" + +#: src/GMStreamSource.cpp:112 src/GMStreamSource.cpp:118 +msgid "New Station…\t\t" +msgstr "" + +#: src/GMStreamSource.cpp:117 +msgid "Edit…\t\t" +msgstr "" + +#: src/GMStreamSource.cpp:119 +msgid "Remove\t\tRemove." +msgstr "" + +#: src/GMStreamSource.cpp:165 src/GMStreamSource.cpp:166 +msgid "New Internet Radio Station" +msgstr "" + +#: src/GMStreamSource.cpp:166 +msgid "Specify url and description of new station" +msgstr "" + +#: src/GMStreamSource.cpp:168 +msgid "C&reate" +msgstr "L&uo" + +#: src/GMStreamSource.cpp:173 src/GMStreamSource.cpp:211 +msgid "Location" +msgstr "" + +#: src/GMStreamSource.cpp:175 src/GMStreamSource.cpp:214 +msgid "Description" +msgstr "" + +#: src/GMStreamSource.cpp:189 +msgid "Untitled" +msgstr "" + +#: src/GMStreamSource.cpp:202 src/GMStreamSource.cpp:203 +msgid "Edit Internet Radio Station" +msgstr "" + +#: src/GMStreamSource.cpp:203 +msgid "Update url and description of station" +msgstr "" + +#: src/GMStreamSource.cpp:265 +msgid "Remove Internet Radio Station(s)?" +msgstr "" + +#: src/GMStreamSource.cpp:266 +msgid "Remove Internet Radio Station(s) from library?" +msgstr "" + +#: src/GMStreamSource.cpp:280 +#, c-format +msgid "Unable to remove station from the library." +msgstr "" + +#: src/GMTrackDatabase.cpp:193 +msgid "" +"An incompatible (future) version of the database was found.\n" +"This usually happens when you try to downgrade to a older version of GMM\n" +"Press OK to continue and reset the database (all information will be " +"lost!).\n" +"Press Cancel to quit now and leave the database as is." +msgstr "" + +#: src/GMTrackDatabase.cpp:205 +msgid "" +"Goggles Music Manager was unable to open the database.\n" +"The database may have been corrupted. Please remove ~/.goggles/goggles.db to " +"try again.\n" +"if the error keeps occuring, please file an issue at http://code.google.com/" +"p/gogglesmm" +msgstr "" + +#: src/GMTrackView.cpp:245 +msgid "\tClose Filter\tClose Filter" +msgstr "" + +#: src/GMTrackView.cpp:246 +msgid "&Find" +msgstr "" + +#: src/GMTrackView.cpp:252 +msgid "Tags\tPress to change sorting order\tPress to change sorting order" +msgstr "" + +#: src/GMTrackView.cpp:256 +msgid "Artists\tPress to change sorting order\tPress to change sorting order" +msgstr "" + +#: src/GMTrackView.cpp:260 +msgid "Albums\tPress to change sorting order\tPress to change sorting order" +msgstr "" + +#: src/GMTrackView.cpp:282 src/GMWindow.cpp:299 +msgid "&Configure Columns…" +msgstr "&Säädä sarakkeita…" + +#: src/GMTrackView.cpp:286 +msgid "Browse" +msgstr "" + +#: src/GMTrackView.cpp:287 +msgid "Shuffle\tCtrl-R" +msgstr "" + +#: src/GMTrackView.cpp:294 ../../../fox-1.6.37/src/FXDirSelector.cpp:388 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:734 +msgid "Reverse" +msgstr "" + +#: src/GMTrackView.cpp:804 +#, c-format +msgid "All %d Genres" +msgstr "Kaikki %d lajit" + +#: src/GMTrackView.cpp:806 +msgid "All Genres" +msgstr "Kaikki lajit" + +#: src/GMTrackView.cpp:832 +#, c-format +msgid "All %d Artists" +msgstr "Kaikki %d artistit" + +#: src/GMTrackView.cpp:872 +#, c-format +msgid "All %d Albums" +msgstr "" + +#: src/GMTrackView.cpp:1258 +#, c-format +msgid "By %s" +msgstr "" + +#: src/GMTrackView.cpp:1586 src/GMTrackView.cpp:1609 +msgid "Sort by Album Year" +msgstr "" + +#: src/GMTrackView.cpp:1637 +msgid "&Columns\t\tChange Visible Columns." +msgstr "" + +#: src/GMTrackView.cpp:1638 +msgid "&Sort\t\tChange Sorting." +msgstr "" + +#: src/GMTrayIcon.cpp:302 src/GMWindow.cpp:841 src/GMWindow.cpp:925 +#: src/GMWindow.cpp:948 src/GMWindow.cpp:954 +msgid "Play" +msgstr "" + +#: src/GMTrayIcon.cpp:303 src/GMWindow.cpp:843 +msgid "Stop" +msgstr "" + +#: src/GMTrayIcon.cpp:304 +msgid "Previous Track" +msgstr "Edellinen raita" + +#: src/GMTrayIcon.cpp:305 +msgid "Next Track" +msgstr "Seuraava raita" + +#: src/GMWindow.cpp:205 +msgid "Play\tStart Playback\tStart Playback" +msgstr "" + +#: src/GMWindow.cpp:205 +msgid "Pause\tPause\tPause Playback" +msgstr "" + +#: src/GMWindow.cpp:206 +msgid "Stop\tStop Playback\tStop Playback" +msgstr "" + +#: src/GMWindow.cpp:208 +msgid "Previous\tPlay Previous Track\tPlay previous track." +msgstr "" + +#: src/GMWindow.cpp:209 +msgid "Next\tPlay Next Track\tPlay next track." +msgstr "" + +#: src/GMWindow.cpp:255 +msgid "&Music" +msgstr "&Musiikki" + +#: src/GMWindow.cpp:256 +msgid "Import Folder…\tCtrl-O\tImport Music from folder into Library" +msgstr "" + +#: src/GMWindow.cpp:257 +msgid "Sync Folder…\t\tSynchronize Folder with Music in Library" +msgstr "" + +#: src/GMWindow.cpp:259 +msgid "Play File or Stream…\t\tPlay File or Stream" +msgstr "" + +#: src/GMWindow.cpp:266 +msgid "&Quit\tCtrl-Q\tQuit the application." +msgstr "" + +#: src/GMWindow.cpp:271 +msgid "&Edit" +msgstr "&Muokkaa" + +#: src/GMWindow.cpp:272 +msgid "&Copy\tCtrl-C\tCopy Selected Tracks" +msgstr "" + +#: src/GMWindow.cpp:273 +msgid "&Cut\tCtrl-X\tCut Selected Tracks" +msgstr "" + +#: src/GMWindow.cpp:274 +msgid "&Paste\tCtrl-V\tPaste Clipboard Selection" +msgstr "" + +#: src/GMWindow.cpp:276 +msgid "Find…\tCtrl-F\tShow search filter." +msgstr "" + +#: src/GMWindow.cpp:278 +msgid "Preferences…" +msgstr "" + +#: src/GMWindow.cpp:282 +msgid "&View" +msgstr "&Näytä" + +#: src/GMWindow.cpp:283 +msgid "&Browse\tCtrl-B\tShow genre artist and album browser." +msgstr "" + +#: src/GMWindow.cpp:284 +msgid "Show &Genres\tCtrl-G\tShow genre browser." +msgstr "" + +#: src/GMWindow.cpp:287 src/GMWindow.cpp:289 +msgid "Show &Sources\tCtrl-S\tShow source browser " +msgstr "" + +#: src/GMWindow.cpp:294 +msgid "Show Full Screen\tF12\tToggle fullscreen mode." +msgstr "" + +#: src/GMWindow.cpp:296 +msgid "Show Mini Player\tCtrl-M\tToggle Mini Player." +msgstr "" + +#: src/GMWindow.cpp:300 +msgid "&Sort" +msgstr "&Järjestä" + +#: src/GMWindow.cpp:302 +msgid "&Jump to Current Track\tCtrl-J\tShow current playing track." +msgstr "" + +#: src/GMWindow.cpp:306 +msgid "&Control" +msgstr "&Hallitse" + +#: src/GMWindow.cpp:307 +msgid "Play\tCtrl-P\tStart playback." +msgstr "" + +#: src/GMWindow.cpp:308 +msgid "Stop\tCtrl-\\\tStop playback." +msgstr "" + +#: src/GMWindow.cpp:309 +msgid "Previous Track\tCtrl-[\tPlay next track." +msgstr "" + +#: src/GMWindow.cpp:310 +msgid "Next Track\tCtrl-]\tPlay previous track." +msgstr "" + +#: src/GMWindow.cpp:312 +msgid "Repeat Off\tCtrl-,\tRepeat current track." +msgstr "" + +#: src/GMWindow.cpp:313 +msgid "Repeat Track\tCtrl-.\tRepeat current track." +msgstr "" + +#: src/GMWindow.cpp:314 +msgid "Repeat All Tracks\tCtrl-/\tRepeat all tracks." +msgstr "" + +#: src/GMWindow.cpp:315 +msgid "Repeat A-B\tCtrl-T\tRepeat section of track." +msgstr "" + +#: src/GMWindow.cpp:316 +msgid "Shuffle Mode\tAlt-R\tPlay tracks in random order." +msgstr "" + +#: src/GMWindow.cpp:318 +msgid "Equalizer\t\t" +msgstr "" + +#: src/GMWindow.cpp:319 +msgid "Sleep Timer\t\tSetup sleeptimer." +msgstr "" + +#: src/GMWindow.cpp:323 +msgid "&Help" +msgstr "&Ohje" + +#: src/GMWindow.cpp:324 +msgid "&Homepage" +msgstr "&Kotisivu" + +#: src/GMWindow.cpp:325 +msgid "&Report Issue…" +msgstr "&Ilmoita ongelmasta…" + +#: src/GMWindow.cpp:327 +msgid "&Sign up for last.fm…" +msgstr "&Rekisteröidy last.fm-palveluun…" + +#: src/GMWindow.cpp:328 +msgid "" +"&Join GMM on last.fm…\t\tJoin the Goggles Music Manager group on last.fm…" +msgstr "" + +#: src/GMWindow.cpp:330 +msgid "&About…" +msgstr "&Tietoja…" + +#: src/GMWindow.cpp:842 src/GMWindow.cpp:940 +msgid "Pause" +msgstr "" + +#: src/GMWindow.cpp:844 +msgid "Previous" +msgstr "Edellinen" + +#: src/GMWindow.cpp:845 +msgid "Next" +msgstr "Seuraava" + +#: src/GMWindow.cpp:920 src/GMWindow.cpp:949 src/GMWindow.cpp:955 +msgid "Start playback." +msgstr "" + +#: src/GMWindow.cpp:926 src/GMWindow.cpp:927 +msgid "Start playback" +msgstr "" + +#: src/GMWindow.cpp:934 src/GMWindow.cpp:941 +msgid "Pause playback." +msgstr "" + +#: src/GMWindow.cpp:935 +msgid "Pause playback" +msgstr "" + +#: src/GMWindow.cpp:1052 src/GMWindow.cpp:1054 +msgid "Repeat A-B" +msgstr "" + +#: src/GMWindow.cpp:1053 +msgid "Repeat A" +msgstr "" + +#: src/GMWindow.cpp:1195 +msgid "Play File or Stream" +msgstr "" + +#: src/GMWindow.cpp:1202 +msgid "Please specify a file or url to play:" +msgstr "" + +#: src/GMWindow.cpp:1206 +msgid "…" +msgstr "" + +#: src/GMWindow.cpp:1212 +msgid "Select File" +msgstr "" + +#: src/GMWindow.cpp:1243 +msgid "Sleep Timer" +msgstr "" + +#: src/GMWindow.cpp:1244 +msgid "Setup sleep timer" +msgstr "" + +#: src/GMWindow.cpp:1244 +msgid "Stop playback within a certain time" +msgstr "" + +#: src/GMWindow.cpp:1246 +msgid "&Start Timer" +msgstr "&Aloita ajastin" + +#: src/GMWindow.cpp:1251 +msgid "Sleep in" +msgstr "" + +#: src/GMWindow.cpp:1253 +msgid "hours and" +msgstr "" + +#: src/GMWindow.cpp:1255 +msgid "minutes." +msgstr "" + +#: src/GMDatabaseSource.h:131 +msgid "Music Library" +msgstr "" + +#: src/GMStreamSource.h:57 +msgid "Internet Radio" +msgstr "" + +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:122 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:127 +msgid "&Quit" +msgstr "&Poistu" + +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:133 +msgid "&Skip" +msgstr "&Ohita" + +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:134 +msgid "Skip &All" +msgstr "Ohita &kaikki" + +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:140 +msgid "&Don't Save" +msgstr "&Älä tallenna" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:220 +msgid "\tPick color" +msgstr "" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:229 +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:274 +msgid "\tHue, Saturation, Value" +msgstr "" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:239 +msgid "\tRed, Green, Blue" +msgstr "" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:245 +msgid "&Red:" +msgstr "&Punainen:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:250 +msgid "&Green:" +msgstr "&Vihreä:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:255 +msgid "&Blue:" +msgstr "&Sininen:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:260 +msgid "&Alpha:" +msgstr "" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:280 +msgid "Hue:" +msgstr "" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:285 +msgid "Saturation:" +msgstr "" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:290 +msgid "Value:" +msgstr "" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:295 +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:330 +msgid "Alpha:" +msgstr "" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:309 +msgid "\tCyan, Magenta, Yellow" +msgstr "" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:315 +msgid "Cyan:" +msgstr "" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:320 +msgid "Magenta:" +msgstr "" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:325 +msgid "Yellow:" +msgstr "" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:344 +msgid "\tBy Name" +msgstr "" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:281 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:521 +msgid "Create New Directory" +msgstr "" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:284 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:524 +msgid "Already Exists" +msgstr "On jo olemassa" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:288 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:528 +msgid "Cannot Create" +msgstr "Ei voida luoda" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:309 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:595 +msgid "Copy File" +msgstr "Kopioi tiedosto" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:315 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:601 +msgid "Error Copying File" +msgstr "" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:326 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:618 +msgid "Move File" +msgstr "" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:332 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:624 +msgid "Error Moving File" +msgstr "" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:343 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:641 +msgid "Link File" +msgstr "" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:349 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:647 +msgid "Error Linking File" +msgstr "" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:359 +msgid "Deleting file" +msgstr "" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:361 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:667 +msgid "Error Deleting File" +msgstr "" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:381 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:719 +msgid "Up one level" +msgstr "" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:382 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:720 +msgid "Home directory" +msgstr "" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:383 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:721 +msgid "Work directory" +msgstr "" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:387 +msgid "Sorting" +msgstr "" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:389 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:735 +msgid "Ignore case" +msgstr "" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:390 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:746 +msgid "Hidden files" +msgstr "" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:393 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:754 +msgid "Bookmarks" +msgstr "" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:394 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:757 +msgid "Set bookmark" +msgstr "" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:395 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:758 +msgid "Clear bookmarks" +msgstr "" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:411 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:774 +msgid "New directory..." +msgstr "" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:412 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:775 +msgid "Copy..." +msgstr "Kopioi..." + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:413 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:776 +msgid "Move..." +msgstr "Siirrä..." + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:414 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:777 +msgid "Link..." +msgstr "" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:415 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:778 +msgid "Delete..." +msgstr "Poista..." + +#: ../../../fox-1.6.37/src/FXFileList.cpp:195 +msgid "Modified Date" +msgstr "Muokkauspäivä" + +#: ../../../fox-1.6.37/src/FXFileList.cpp:196 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:731 +msgid "User" +msgstr "Käyttäjä" + +#: ../../../fox-1.6.37/src/FXFileList.cpp:197 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:732 +msgid "Group" +msgstr "Ryhmä" + +#: ../../../fox-1.6.37/src/FXFileList.cpp:198 +msgid "Attributes" +msgstr "" + +#: ../../../fox-1.6.37/src/FXFileList.cpp:200 +msgid "Link" +msgstr "" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:521 +msgid "Create new directory with name: " +msgstr "" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:524 +#, c-format +msgid "File or directory %s already exists.\n" +msgstr "" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:528 +#, c-format +msgid "Cannot create directory %s.\n" +msgstr "" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:594 +#, c-format +msgid "" +"Copy file from location:\n" +"\n" +"%s\n" +"\n" +"to location: " +msgstr "" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:601 +#, c-format +msgid "" +"Unable to copy file:\n" +"\n" +"%s to: %s\n" +"\n" +"Continue with operation?" +msgstr "" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:617 +#, c-format +msgid "" +"Move file from location:\n" +"\n" +"%s\n" +"\n" +"to location: " +msgstr "" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:624 +#, c-format +msgid "" +"Unable to move file:\n" +"\n" +"%s to: %s\n" +"\n" +"Continue with operation?" +msgstr "" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:640 +#, c-format +msgid "" +"Link file from location:\n" +"\n" +"%s\n" +"\n" +"to location: " +msgstr "" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:647 +#, c-format +msgid "" +"Unable to link file:\n" +"\n" +"%s to: %s\n" +"\n" +"Continue with operation?" +msgstr "" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:663 +msgid "Deleting files" +msgstr "" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:663 +#, c-format +msgid "" +"Are you sure you want to delete the file:\n" +"\n" +"%s" +msgstr "" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:667 +#, c-format +msgid "" +"Unable to delete file:\n" +"\n" +"%s\n" +"\n" +"Continue with operation?" +msgstr "" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:722 +msgid "Select all" +msgstr "Valitse kaikki" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:726 +msgid "Sort by" +msgstr "järjestä:" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:738 +msgid "View" +msgstr "näytä" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:739 +msgid "Small icons" +msgstr "Pienet kuvakkeet" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:740 +msgid "Big icons" +msgstr "Isot kuvakkeet" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:741 +msgid "Details" +msgstr "Tiedot" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:743 +msgid "Rows" +msgstr "Rivit" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:744 +msgid "Columns" +msgstr "Sarakkeet" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:747 +#, fuzzy +msgid "Preview images" +msgstr "Esikatselukuvat" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:749 +msgid "Normal images" +msgstr "Tavalliset kuvat" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:750 +msgid "Medium images" +msgstr "Keskikokoiset kuvat" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:751 +msgid "Giant images" +msgstr "Suure kuvat" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:111 +msgid "&Replace" +msgstr "&Korvaa" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:112 +msgid "Re&place All" +msgstr "Ko&rvaa kaikki" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:121 +msgid "S&earch for:" +msgstr "E&tsi:" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:129 +msgid "Replace &with:" +msgstr "&Korvaa:" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:138 +msgid "Ex&act" +msgstr "" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:139 +msgid "&Ignore Case" +msgstr "" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:140 +msgid "E&xpression" +msgstr "" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:141 +msgid "&Backward" +msgstr "" + +#: ../../../fox-1.6.37/src/FXSearchDialog.cpp:71 +msgid "&Search" +msgstr "&Etsi" + +#: ../../../fox-1.6.37/src/FXStatusLine.cpp:82 +msgid "Ready." +msgstr "Valmis." + +#~ msgid "Old Name" +#~ msgstr "Vanha nimi" + +#~ msgid "New Name" +#~ msgstr "Uusi nimi" + +#, fuzzy +#~ msgid " Type:" +#~ msgstr "Tyyppi" + +#, fuzzy +#~ msgid "Size:" +#~ msgstr "Koko" + +#, fuzzy +#~ msgid "Family:" +#~ msgstr "&Perhe:" + +#~ msgid "About" +#~ msgstr "Tietoja" + +#~ msgid "&Style:" +#~ msgstr "&Tyyli:" + +#~ msgid "Si&ze:" +#~ msgstr "Ko&ko:" + +#~ msgid "UNICODE" +#~ msgstr "UNICODE" diff --git a/po/fr.mo b/po/fr.mo new file mode 100644 index 0000000000000000000000000000000000000000..782a1cc0accd19325b31c645731b2618432b2c6a GIT binary patch literal 37349 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 J#|oXn{{?zUhm`;T literal 0 HcmV?d00001 diff --git a/po/fr.po b/po/fr.po new file mode 100644 index 0000000..f4c67c4 --- /dev/null +++ b/po/fr.po @@ -0,0 +1,3048 @@ +msgid "" +msgstr "" +"Project-Id-Version: Goggles Music Manager\n" +"Report-Msgid-Bugs-To: s.jansen@gmail.com\n" +"POT-Creation-Date: 2011-02-09 23:26-0600\n" +"PO-Revision-Date: \n" +"Last-Translator: Erwan Inyzant \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Poedit-Language: French\n" +"X-Poedit-Country: FRANCE\n" +"X-Language: fr_FR\n" + +#: src/GMAbout.cpp:136 src/GMDatabaseSource.cpp:218 +#: src/GMDatabaseSource.cpp:2215 src/GMPreferencesDialog.cpp:551 +msgid "&Close" +msgstr "&Fermer" + +#: src/GMColumnDialog.cpp:204 +msgid "Configure Columns" +msgstr "Configurer les Colonnes" + +#: src/GMColumnDialog.cpp:207 ../../../fox-1.6.37/src/FXColorSelector.cpp:168 +msgid "&Accept" +msgstr "&Accepter" + +#: src/GMColumnDialog.cpp:208 src/GMDatabaseSource.cpp:98 +#: src/GMDatabaseSource.cpp:1274 src/GMDatabaseSource.cpp:1704 +#: src/GMDatabaseSource.cpp:1804 src/GMDatabaseSource.cpp:1878 +#: src/GMDatabaseSource.cpp:2121 src/GMDatabaseSource.cpp:2182 +#: src/GMImportDialog.cpp:175 src/GMImportDialog.cpp:563 +#: src/GMPlayListSource.cpp:281 src/GMPlayListSource.cpp:410 +#: src/GMSearch.cpp:572 src/GMStreamSource.cpp:169 src/GMStreamSource.cpp:206 +#: src/GMStreamSource.cpp:271 src/GMWindow.cpp:1198 src/GMWindow.cpp:1247 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:107 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:118 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:123 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:129 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:135 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:142 +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:169 +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:135 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:205 +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:113 +msgid "&Cancel" +msgstr "&Annuler" + +#: src/GMColumnDialog.cpp:212 +msgid "" +"Choose the order of information to appear\n" +"in the track list." +msgstr "Choisissez l'ordre d'apparition des informations dans le track-list." + +#: src/GMColumnDialog.cpp:218 +msgid "Move Up" +msgstr "Vers le Haut" + +#: src/GMColumnDialog.cpp:219 +msgid "Move Down" +msgstr "Vers le Bas" + +#: src/GMDatabaseSource.cpp:52 +msgid "Invalid Template" +msgstr "Modèle Invalide" + +#: src/GMDatabaseSource.cpp:52 +#, c-format +msgid "" +"The provided template is invalid. The track title %%T needs to be " +"specified.\n" +"Please fix the filename template in the preference panel." +msgstr "" +"Le Modèle utilisé est invalide. Le titre du morceau %%T doit être spécifié.\n" +"Modifiez le modèle du nom de fichier dans les préférences." + +#: src/GMDatabaseSource.cpp:72 src/GMTrackDatabase.cpp:193 +msgid "Database Error" +msgstr "Erreur de base de donnée" + +#: src/GMDatabaseSource.cpp:72 +msgid "Oops. Database Error" +msgstr "Oups. Erreur de Base de donnée" + +#: src/GMDatabaseSource.cpp:87 +msgid "No changes" +msgstr "Aucuns changements" + +#: src/GMDatabaseSource.cpp:87 +msgid "Filenames did not require any changes" +msgstr "Le nom des fichiers n'ont eu besoin d'aucuns changements" + +#: src/GMDatabaseSource.cpp:94 +msgid "Rename Audio Files?" +msgstr "Renommer les Fichiers Audio ?" + +#: src/GMDatabaseSource.cpp:95 +msgid "Renaming Audio Files…" +msgstr "Renommage des Fichiers Audio..." + +#: src/GMDatabaseSource.cpp:95 +msgid "The following audio files are going to be renamed" +msgstr "Les fichier audio suivant vont être renommés" + +#: src/GMDatabaseSource.cpp:97 +msgid "&Rename" +msgstr "&Renommer" + +#: src/GMDatabaseSource.cpp:121 src/GMDatabaseSource.cpp:125 +msgid "Unable to rename file" +msgstr "Impossible de renommer le fichier" + +#: src/GMDatabaseSource.cpp:121 +#, c-format +msgid "" +"Unable to rename:\n" +"%s\n" +"\n" +"to:%s\n" +"Continue renaming files?" +msgstr "" +"Impossible de renommer:\n" +"%s\n" +"\n" +"en:%s\n" +"Continuer à renommer les fichiers ?" + +#: src/GMDatabaseSource.cpp:125 +#, c-format +msgid "" +"Unable to rename:\n" +"%s\n" +"\n" +"to:%s" +msgstr "" +"Impossible de renommer:\n" +"%s\n" +"\n" +"en:%s" + +#: src/GMDatabaseSource.cpp:160 src/GMImportDialog.cpp:534 +msgid "Filename Template" +msgstr "Modèle du Nom de Fichier" + +#: src/GMDatabaseSource.cpp:177 +msgid "" +"Template may contain absolute or relative path, environment variables\n" +"and ~. Relative paths are based on the location of the original file. The\n" +"file extension gets automatically added. The following macros\n" +"may be used:" +msgstr "" +"Pour les Modèles vous pouvez utiliser des chemins relatifs ou absolus, \n" +"des variables d'environnements et le caractère ~. Les chemins relatifs\n" +"sont calculés par rapport à l'emplacement du fichier original. L'extension\n" +"est récupérée automatiquement. Les macros suivantes peuvent être\n" +"utilisées:" + +#: src/GMDatabaseSource.cpp:178 +msgid "" +"%T - title %A - album name\n" +"%P - album artist name %p - track artist name\n" +"%y - year %d - disc number\n" +"%N - track number (2 digits) %n - track number \n" +"%G - genre" +msgstr "" +"%T - titre %A - nom de l'album\n" +"%P - nom de l'artiste pour l'album %p - nom de l'artiste pour la " +"piste\n" +"%y - annee %d - numéro du disque\n" +"%N - numéro de la piste (2 chiffres) %n - numéro de la piste \n" +"%G - genre" + +#: src/GMDatabaseSource.cpp:185 +msgid "Conditions may be used as well:" +msgstr "" + +#: src/GMDatabaseSource.cpp:186 +msgid "" +"?c - display a if c is not empty else display b.\n" +"?c - display c if not empty\n" +msgstr "" + +#: src/GMDatabaseSource.cpp:195 src/GMDatabaseSource.cpp:1815 +#: src/GMImportDialog.cpp:546 +msgid "Template:" +msgstr "Modèle:" + +#: src/GMDatabaseSource.cpp:199 src/GMDatabaseSource.cpp:1819 +msgid "Encoding:" +msgstr "Encodage:" + +#: src/GMDatabaseSource.cpp:205 +msgid "Exclude:" +msgstr "Exclure:" + +#: src/GMDatabaseSource.cpp:209 src/GMDatabaseSource.cpp:1825 +msgid "Options:" +msgstr "Options:" + +#: src/GMDatabaseSource.cpp:210 src/GMDatabaseSource.cpp:1826 +msgid "Replace spaces with underscores" +msgstr "Remplacer les espaces par des underscores" + +#: src/GMDatabaseSource.cpp:212 src/GMDatabaseSource.cpp:1828 +msgid "Lower case" +msgstr "Minuscule" + +#: src/GMDatabaseSource.cpp:214 src/GMDatabaseSource.cpp:1830 +msgid "Lower case extension" +msgstr "Extension minuscule" + +#: src/GMDatabaseSource.cpp:341 src/GMStreamSource.cpp:63 +msgid "No." +msgstr "No." + +#: src/GMDatabaseSource.cpp:342 +msgid "Queue" +msgstr "Queue" + +#: src/GMDatabaseSource.cpp:343 src/GMDatabaseSource.cpp:1391 +#: src/GMTrackView.cpp:238 +msgid "Title" +msgstr "Titre" + +#: src/GMDatabaseSource.cpp:344 src/GMDatabaseSource.cpp:1396 +#: src/GMTrackView.cpp:239 +msgid "Artist" +msgstr "Artiste" + +#: src/GMDatabaseSource.cpp:345 src/GMDatabaseSource.cpp:1397 +msgid "Album Artist" +msgstr "Artiste de l'Album" + +#: src/GMDatabaseSource.cpp:346 src/GMDatabaseSource.cpp:1405 +#: src/GMPreferencesDialog.cpp:540 src/GMTrackView.cpp:240 +msgid "Album" +msgstr "Album" + +#: src/GMDatabaseSource.cpp:347 src/GMDatabaseSource.cpp:1364 +#: src/GMDatabaseSource.cpp:1375 src/GMDatabaseSource.cpp:1381 +msgid "Disc" +msgstr "Disque" + +#: src/GMDatabaseSource.cpp:348 src/GMDatabaseSource.cpp:1408 +#: src/GMStreamSource.cpp:66 src/GMStreamSource.cpp:177 +#: src/GMStreamSource.cpp:216 src/GMTrackView.cpp:241 +msgid "Genre" +msgstr "Genre" + +#: src/GMDatabaseSource.cpp:349 src/GMDatabaseSource.cpp:1369 +#: src/GMDatabaseSource.cpp:1387 +msgid "Year" +msgstr "Année" + +#: src/GMDatabaseSource.cpp:350 ../../../fox-1.6.37/src/FXFileSelector.cpp:730 +msgid "Time" +msgstr "Durée" + +#: src/GMDatabaseSource.cpp:490 +msgid "Remove…\tDel\tRemove Genre from Library." +msgstr "Supprimer...\tSuppr\tSupprimer le Genre de la Bibliothèque." + +#: src/GMDatabaseSource.cpp:496 src/GMDatabaseSource.cpp:505 +#: src/GMPlayListSource.cpp:104 src/GMPlayListSource.cpp:110 +msgid "Copy\tCtrl-C\tCopy associated tracks to the clipboard." +msgstr "Copier\tCtrl-C\tCopier les morceaux associés dans le presse papier." + +#: src/GMDatabaseSource.cpp:499 src/GMDatabaseSource.cpp:508 +msgid "Remove…\tDel\tRemove associated tracks from library." +msgstr "" +"Supprimer...\tSuppr\tSupprimer les morceaux associés de la bibliothèque." + +#: src/GMDatabaseSource.cpp:513 src/GMPlayListSource.cpp:116 +msgid "Edit…\tF2\tEdit Track Information." +msgstr "Editer...\tF2\tEditer les Informations sur le morceau." + +#: src/GMDatabaseSource.cpp:514 src/GMPlayListSource.cpp:117 +msgid "Copy\tCtrl-C\tCopy track(s) to clipboard." +msgstr "Copier\tCtrl-C\tCopier le(s) morceau(x) dans le presse papier." + +#: src/GMDatabaseSource.cpp:518 src/GMPlayListSource.cpp:120 +msgid "Open Folder Location\t\tOpen Folder Location." +msgstr "" + +#: src/GMDatabaseSource.cpp:523 +msgid "Remove…\tDel\tRemove track(s) from library." +msgstr "Supprimer...\tSuppr\tSupprimer le(s) morceau(x) de la bibliothèque." + +#: src/GMDatabaseSource.cpp:537 +msgid "New Play List…\t\tCreate a new play list." +msgstr "Nouvelle Playliste...\t\tCréer une nouvelle playliste." + +#: src/GMDatabaseSource.cpp:539 src/GMPlayListSource.cpp:163 +msgid "Export…" +msgstr "Exporter..." + +#: src/GMDatabaseSource.cpp:540 +msgid "Information…\t\tLibrary Statistics" +msgstr "Information...\t\tStatistiques sur la Bibliothèque" + +#: src/GMDatabaseSource.cpp:542 +msgid "Remove All Tracks\t\tRemove all tracks from the library" +msgstr "" +"Supprimer Toutes les Pistes\t\tSupprimer toutes les pistes de la bibliothèque" + +#: src/GMDatabaseSource.cpp:1272 +msgid "Edit Track Information" +msgstr "Modifier les Informations de la Piste" + +#: src/GMDatabaseSource.cpp:1275 src/GMPlayListSource.cpp:409 +#: src/GMStreamSource.cpp:205 ../../../fox-1.6.37/src/FXMessageBox.cpp:128 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:143 +msgid "&Save" +msgstr "&Sauver" + +#: src/GMDatabaseSource.cpp:1315 +msgid "&Tag" +msgstr "" + +#: src/GMDatabaseSource.cpp:1320 +#, fuzzy +msgid "&Properties" +msgstr "Propriétés" + +#: src/GMDatabaseSource.cpp:1325 src/GMImportDialog.cpp:525 +msgid "Filename" +msgstr "Nom du Fichier" + +#: src/GMDatabaseSource.cpp:1329 ../../../fox-1.6.37/src/FXFileList.cpp:193 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:728 +msgid "Type" +msgstr "Type" + +#: src/GMDatabaseSource.cpp:1333 ../../../fox-1.6.37/src/FXFileList.cpp:194 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:729 +msgid "Size" +msgstr "Taille" + +#: src/GMDatabaseSource.cpp:1341 src/GMStreamSource.cpp:65 +msgid "Bitrate" +msgstr "Bitrate" + +#: src/GMDatabaseSource.cpp:1345 +msgid "Samplerate" +msgstr "Echantillonnage" + +#: src/GMDatabaseSource.cpp:1349 +msgid "Channels" +msgstr "Cannaux" + +#: src/GMDatabaseSource.cpp:1358 src/GMPreferencesDialog.cpp:539 +msgid "Track" +msgstr "Piste" + +#: src/GMDatabaseSource.cpp:1403 +msgid "\tSeparate Artists" +msgstr "\tArtistes Différents" + +#: src/GMDatabaseSource.cpp:1403 +msgid "\tShared Artists" +msgstr "\tMêmes Artistes" + +#: src/GMDatabaseSource.cpp:1416 +msgid "Auto track number. Offset:" +msgstr "Récupération auto des N° de pistes. Offset:" + +#: src/GMDatabaseSource.cpp:1425 +msgid "Update Tag in File" +msgstr "Mettre à Jour les Tags dans le Fichier" + +#: src/GMDatabaseSource.cpp:1430 +msgid "Update Filename" +msgstr "Mettre à jour le Nom du Fichier" + +#: src/GMDatabaseSource.cpp:1431 +msgid "Set export template…" +msgstr "Gérer le modèle d'export..." + +#: src/GMDatabaseSource.cpp:1645 +msgid "Update Tags?" +msgstr "Mise à Jour des Tags? " + +#: src/GMDatabaseSource.cpp:1645 +msgid "" +"No tracks were updated.\n" +"Would you still like to write the tags for the selected tracks?" +msgstr "" +"Aucun morceau n'a été mis à jour.\n" +"Voulez vous toujours écrire les tags pour les morceaux sélectionnés?" + +#: src/GMDatabaseSource.cpp:1700 +msgid "Remove Audio Files?" +msgstr "Supprimer les Fichiers Audio ?" + +#: src/GMDatabaseSource.cpp:1701 +msgid "Remove Audio Files..." +msgstr "Supprimer les Fichiers Audio..." + +#: src/GMDatabaseSource.cpp:1701 +msgid "The following audio files are going to be removed" +msgstr "Les fichiers audio suivant vont être supprimer" + +#: src/GMDatabaseSource.cpp:1703 src/GMDatabaseSource.cpp:1877 +#: src/GMPlayListSource.cpp:280 src/GMStreamSource.cpp:270 +msgid "&Remove" +msgstr "&Supprimer" + +#: src/GMDatabaseSource.cpp:1729 +msgid "Export Main Library" +msgstr "Exporter la Bibliothèque Principale" + +#: src/GMDatabaseSource.cpp:1731 +msgid "Export Play List" +msgstr "Exporter la Play Liste" + +#: src/GMDatabaseSource.cpp:1744 +msgid "Overwrite File?" +msgstr "Ecraser ce fichier ?" + +#: src/GMDatabaseSource.cpp:1744 +msgid "File already exists. Would you like to overwrite it?" +msgstr "Le fichier existe déjà. Voulez vous l'écraser ?" + +#: src/GMDatabaseSource.cpp:1784 +msgid "Export Genre" +msgstr "Exporter les Genres" + +#: src/GMDatabaseSource.cpp:1785 +msgid "Export tracks with genre to destination directory." +msgstr "Exporter les morceaux de ce genre dans le répertoire de destination." + +#: src/GMDatabaseSource.cpp:1787 +msgid "Export Artists" +msgstr "Exporter les Artistes" + +#: src/GMDatabaseSource.cpp:1788 +msgid "Export tracks from artist to destination directory." +msgstr "" +"Exporter les morceaux de cet artiste vers le répertoire de destination." + +#: src/GMDatabaseSource.cpp:1790 +msgid "Export Albums" +msgstr "Exporter les Albums" + +#: src/GMDatabaseSource.cpp:1791 +msgid "Export tracks from album to destination directory." +msgstr "Exporter les morceaux de cet album vers le répertoire de destination." + +#: src/GMDatabaseSource.cpp:1793 +msgid "Export Tracks" +msgstr "Exporter les morceaux" + +#: src/GMDatabaseSource.cpp:1794 +msgid "Export tracks to destination directory." +msgstr "Exporter les morceaux vers le répertoire de destination." + +#: src/GMDatabaseSource.cpp:1803 +msgid "&Export" +msgstr "&Exporter" + +#: src/GMDatabaseSource.cpp:1853 src/GMPlayListSource.cpp:261 +msgid "Remove Genre?" +msgstr "Supprimer le Genre ?" + +#: src/GMDatabaseSource.cpp:1854 +msgid "Remove tracks with genre from library?" +msgstr "Supprimer tous les morceaux de ce genre de la bibliothèque?" + +#: src/GMDatabaseSource.cpp:1858 src/GMPlayListSource.cpp:264 +msgid "Remove Artist?" +msgstr "Supprimer l'Artiste?" + +#: src/GMDatabaseSource.cpp:1859 +msgid "Remove tracks from artist from library?" +msgstr "Supprimer les morceaux de cet artiste de la bibliothèque?" + +#: src/GMDatabaseSource.cpp:1863 src/GMPlayListSource.cpp:267 +msgid "Remove Album?" +msgstr "Supprimer l'Album?" + +#: src/GMDatabaseSource.cpp:1864 +msgid "Remove tracks from album from library?" +msgstr "Supprimer les morceaux de l'album de la bibliothèque?" + +#: src/GMDatabaseSource.cpp:1868 src/GMPlayListSource.cpp:270 +msgid "Remove Track(s)?" +msgstr "Supprimer le(s) les Morceaux ?" + +#: src/GMDatabaseSource.cpp:1869 +msgid "Remove track(s) from library?" +msgstr "Supprimer le(s) morceau(x) de la bibliothèque?" + +#: src/GMDatabaseSource.cpp:1881 src/GMPlayListSource.cpp:285 +msgid "Remove tracks from disk" +msgstr "Supprimer les morceaux du disque" + +#: src/GMDatabaseSource.cpp:1895 src/GMDatabaseSource.cpp:1899 +#: src/GMDatabaseSource.cpp:1903 src/GMDatabaseSource.cpp:1907 +#: src/GMPlayListSource.cpp:308 src/GMStreamSource.cpp:280 +msgid "Library Error" +msgstr "Erreur sur la Bibliothèque" + +#: src/GMDatabaseSource.cpp:1895 +msgid "Unable to remove genre from the library" +msgstr "Impossible de supprimer le genre de la bibliothèque" + +#: src/GMDatabaseSource.cpp:1899 +msgid "Unable to remove artist from the library" +msgstr "Impossible de supprimer l'artiste de la bibliothèque" + +#: src/GMDatabaseSource.cpp:1903 +msgid "Unable to remove album from the library" +msgstr "Impossible de supprimer l'album de la bibliothèque" + +#: src/GMDatabaseSource.cpp:1907 src/GMPlayListSource.cpp:308 +msgid "Unable to remove track from the library." +msgstr "Impossible de supprimer le morceau de la bibliothèque." + +#: src/GMDatabaseSource.cpp:2117 src/GMDatabaseSource.cpp:2118 +msgid "Create Playlist" +msgstr "Créer une Playliste" + +#: src/GMDatabaseSource.cpp:2118 +msgid "Specify name of the new playlist" +msgstr "Spécifiez le nom de la nouvelle playliste" + +#: src/GMDatabaseSource.cpp:2120 +msgid "&Create" +msgstr "&Créer" + +#: src/GMDatabaseSource.cpp:2125 src/GMPlayListSource.cpp:415 +#: ../../../fox-1.6.37/src/FXFileList.cpp:192 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:727 +msgid "Name" +msgstr "Nom" + +#: src/GMDatabaseSource.cpp:2127 +msgid "New Playlist" +msgstr "Nouvelle Playliste" + +#: src/GMDatabaseSource.cpp:2178 src/GMDatabaseSource.cpp:2179 +msgid "Clear Music Library?" +msgstr "Effacer la Bibliothèque ?" + +#: src/GMDatabaseSource.cpp:2179 +msgid "Remove all tracks from the music library?" +msgstr "Supprimer tous les morceaux de la bibliothèque?" + +#: src/GMDatabaseSource.cpp:2181 +msgid "&Remove All" +msgstr "&Tout Supprimer" + +#: src/GMDatabaseSource.cpp:2185 +msgid "Keep play lists" +msgstr "Garder les playlistes" + +#: src/GMDatabaseSource.cpp:2212 src/GMDatabaseSource.cpp:2213 +msgid "Music Library Information" +msgstr "Information sur la Bibliothèque" + +#: src/GMDatabaseSource.cpp:2213 +msgid "You music collection consists of…" +msgstr "Votre collection de musique s'élève à..." + +#: src/GMDatabaseSource.cpp:2221 +msgid "Tracks:" +msgstr "Pistes:" + +#: src/GMDatabaseSource.cpp:2226 +msgid "Artists:" +msgstr "Artistes:" + +#: src/GMDatabaseSource.cpp:2230 +msgid "Albums:" +msgstr "Albums:" + +#: src/GMDatabaseSource.cpp:2234 +msgid "Total Time:" +msgstr "Durée Totale:" + +#: src/GMEQDialog.cpp:96 +msgid "Equalizer" +msgstr "Equaliser" + +#: src/GMEQDialog.cpp:135 +msgid "Equalizer:" +msgstr "Equaliser:" + +#: src/GMEQDialog.cpp:137 +msgid "\tSave" +msgstr "\tSauver" + +#: src/GMEQDialog.cpp:138 +msgid "\tReset" +msgstr "\tReset" + +#: src/GMEQDialog.cpp:139 +msgid "\tRemove" +msgstr "\tSupprimer" + +#: src/GMEQDialog.cpp:172 +msgid "Pre-amp" +msgstr "Pré-amp" + +#: src/GMEQDialog.cpp:244 src/GMEQDialog.cpp:254 +msgid "Disabled" +msgstr "Désactiver" + +#: src/GMEQDialog.cpp:247 src/GMEQDialog.cpp:299 +msgid "Manual" +msgstr "Manuel" + +#: src/GMEQDialog.cpp:314 +msgid "Delete Preset" +msgstr "Supprimer le Preset" + +#: src/GMEQDialog.cpp:314 +#, c-format +msgid "Are you sure you want to delete %s preset?" +msgstr "Etes vous sûr de vouloir supprimer le preset %s?" + +#: src/GMEQDialog.cpp:334 +msgid "Preset Name" +msgstr "Nom du Preset" + +#: src/GMEQDialog.cpp:334 +msgid "Please enter preset name:" +msgstr "Entrez le nom du preset:" + +#: src/GMEQDialog.cpp:338 +msgid "Overwrite Preset" +msgstr "Ecraser le preset" + +#: src/GMEQDialog.cpp:338 +#, c-format +msgid "Preset %s already exists. Would you like to overwrite it?" +msgstr "Le preset %s existe déjà. Voules vous l'écraser ?" + +#: src/GMFontDialog.cpp:209 +#, fuzzy +msgid "Ultra Condensed" +msgstr "Ultra condensé" + +#: src/GMFontDialog.cpp:210 +#, fuzzy +msgid "Extra Condensed" +msgstr "Très condensé" + +#: src/GMFontDialog.cpp:211 +msgid "Condensed" +msgstr "Condensé" + +#: src/GMFontDialog.cpp:212 +#, fuzzy +msgid "Semi Condensed" +msgstr "Un Peu Condensé" + +#: src/GMFontDialog.cpp:214 +#, fuzzy +msgid "Semi Expanded" +msgstr "Peu Etendu" + +#: src/GMFontDialog.cpp:215 +msgid "Expanded" +msgstr "Etendu" + +#: src/GMFontDialog.cpp:216 +#, fuzzy +msgid "Extra Expanded" +msgstr "Très Etendu" + +#: src/GMFontDialog.cpp:217 +#, fuzzy +msgid "Ultra Expanded" +msgstr "Ultra Etendu" + +#: src/GMFontDialog.cpp:224 src/GMPreferencesDialog.cpp:1223 +msgid "Thin" +msgstr "" + +#: src/GMFontDialog.cpp:225 src/GMPreferencesDialog.cpp:1224 +#, fuzzy +msgid "Extra Light" +msgstr "Très léger" + +#: src/GMFontDialog.cpp:226 src/GMPreferencesDialog.cpp:1225 +#, fuzzy +msgid "Light" +msgstr "léger" + +#: src/GMFontDialog.cpp:228 src/GMPreferencesDialog.cpp:1227 +#, fuzzy +msgid "Medium" +msgstr "moyen" + +#: src/GMFontDialog.cpp:229 src/GMPreferencesDialog.cpp:1228 +#, fuzzy +msgid "Demibold" +msgstr "bien gras" + +#: src/GMFontDialog.cpp:230 src/GMPreferencesDialog.cpp:1229 +msgid "Bold" +msgstr "" + +#: src/GMFontDialog.cpp:231 src/GMPreferencesDialog.cpp:1230 +#, fuzzy +msgid "Extra Bold" +msgstr "très gras" + +#: src/GMFontDialog.cpp:232 src/GMPreferencesDialog.cpp:1231 +#, fuzzy +msgid "Heavy" +msgstr "très gras" + +#: src/GMFontDialog.cpp:239 +#, fuzzy +msgid "Reverse Oblique" +msgstr "oblique inversé" + +#: src/GMFontDialog.cpp:240 +#, fuzzy +msgid "Reverse Italic" +msgstr "italique inversé" + +#: src/GMFontDialog.cpp:242 +#, fuzzy +msgid "Italic" +msgstr "italique" + +#: src/GMFontDialog.cpp:243 +#, fuzzy +msgid "Oblique" +msgstr "oblique" + +#: src/GMFontDialog.cpp:265 +msgid "Normal" +msgstr "Normal" + +#: src/GMImportDialog.cpp:91 ../../../fox-1.6.37/src/FXDirSelector.cpp:137 +msgid "&Directory:" +msgstr "&Répertoire:" + +#: src/GMImportDialog.cpp:166 ../../../fox-1.6.37/src/FXFileSelector.cpp:196 +msgid "&File Name:" +msgstr "&Nom du Fichier:" + +#: src/GMImportDialog.cpp:168 ../../../fox-1.6.37/src/FXMessageBox.cpp:102 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:106 +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:134 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:198 +msgid "&OK" +msgstr "&OK" + +#: src/GMImportDialog.cpp:170 ../../../fox-1.6.37/src/FXFileSelector.cpp:200 +msgid "File F&ilter:" +msgstr "F&iltre de fichier:" + +#: src/GMImportDialog.cpp:174 ../../../fox-1.6.37/src/FXFileSelector.cpp:204 +msgid "Read Only" +msgstr "Lecture Seule" + +#: src/GMImportDialog.cpp:179 src/GMSearch.cpp:565 src/GMSearch.cpp:647 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:208 +msgid "Directory:" +msgstr "Répertoire:" + +#: src/GMImportDialog.cpp:186 ../../../fox-1.6.37/src/FXFileSelector.cpp:228 +msgid "&Set bookmark\t\tBookmark current directory." +msgstr "&Créer un signet\t\tCréer un signet pour le répertoire courant." + +#: src/GMImportDialog.cpp:187 ../../../fox-1.6.37/src/FXFileSelector.cpp:229 +msgid "&Clear bookmarks\t\tClear bookmarks." +msgstr "&Effacer les bookmarks\t\tEffacer les bookmarks." + +#: src/GMImportDialog.cpp:203 ../../../fox-1.6.37/src/FXFileSelector.cpp:244 +msgid "\tGo up one directory\tMove up to higher directory." +msgstr "" +"\tRemonter d'un répertoire\tRemonter d'un répertoire dans l'arborescence." + +#: src/GMImportDialog.cpp:204 ../../../fox-1.6.37/src/FXFileSelector.cpp:245 +msgid "\tGo to home directory\tBack to home directory." +msgstr "\tAller au répertoire utilisateur\tRevenir au répertoire utilisateur." + +#: src/GMImportDialog.cpp:205 ../../../fox-1.6.37/src/FXFileSelector.cpp:246 +msgid "\tGo to work directory\tBack to working directory." +msgstr "\tAller au répertoire de travail\tRevenir au répertoire de travail." + +#: src/GMImportDialog.cpp:206 ../../../fox-1.6.37/src/FXFileSelector.cpp:247 +msgid "\tBookmarks\tVisit bookmarked directories." +msgstr "\tSignets\tParcourir les répertoires signés." + +#: src/GMImportDialog.cpp:209 ../../../fox-1.6.37/src/FXFileSelector.cpp:250 +msgid "\tCreate new directory\tCreate new directory." +msgstr "\tCréer un nouveau répertoire\tCréer un nouveau répertoire." + +#: src/GMImportDialog.cpp:210 ../../../fox-1.6.37/src/FXFileSelector.cpp:251 +msgid "\tShow list\tDisplay directory with small icons." +msgstr "\tAfficher en liste\tAfficher les répertoires avec de petites icônes." + +#: src/GMImportDialog.cpp:211 ../../../fox-1.6.37/src/FXFileSelector.cpp:252 +msgid "\tShow icons\tDisplay directory with big icons." +msgstr "\tAfficher en icônes\tAfficher les répertoires avec de grosses icônes." + +#: src/GMImportDialog.cpp:212 ../../../fox-1.6.37/src/FXFileSelector.cpp:253 +msgid "\tShow details\tDisplay detailed directory listing." +msgstr "\tAfficher en détails\tAfficher une liste détaillée des répertoires." + +#: src/GMImportDialog.cpp:213 ../../../fox-1.6.37/src/FXFileSelector.cpp:254 +msgid "\tShow hidden files\tShow hidden files and directories." +msgstr "" +"\tAfficher les fichiers cachés\tAfficher les fichiers et répertoires cachés." + +#: src/GMImportDialog.cpp:213 ../../../fox-1.6.37/src/FXFileSelector.cpp:254 +msgid "\tHide Hidden Files\tHide hidden files and directories." +msgstr "" +"\tMasquer les Fichiers Cachés\tMasquer les fichiers et les répertoires " +"cachés." + +#: src/GMImportDialog.cpp:412 +msgid "Synchronize Folder" +msgstr "Synchroniser les répertoires" + +#: src/GMImportDialog.cpp:414 +#, fuzzy +msgid "Import Playlist" +msgstr "Exporter la Play Liste" + +#: src/GMImportDialog.cpp:416 +msgid "Import Music" +msgstr "Importer la Musique" + +#: src/GMImportDialog.cpp:448 +msgid "&File(s)" +msgstr "&Fichier(s)" + +#: src/GMImportDialog.cpp:475 +msgid "&Directory" +msgstr "&Répertoire" + +#: src/GMImportDialog.cpp:478 +msgid "Exclude Filter\tFilter out directories and/or files based on pattern" +msgstr "" +"Filtre d'exclusion\tPermet de filtrer les répertoires et/ou les fichiers à " +"partir d'un modèle" + +#: src/GMImportDialog.cpp:481 +msgid "Folders:" +msgstr "Dossiers:" + +#: src/GMImportDialog.cpp:483 +msgid "Files:" +msgstr "Fichiers:" + +#: src/GMImportDialog.cpp:499 src/GMImportDialog.cpp:560 +msgid "&Sync" +msgstr "&Sync" + +#: src/GMImportDialog.cpp:501 +msgid "Sync Operation" +msgstr "Opération de Synchro" + +#: src/GMImportDialog.cpp:504 +msgid "Import new tracks\tImports files not yet in the database." +msgstr "" +"Importer de nouveaux morceaux\tImporte les fichiers qui ne sont pas encore " +"dans la base de données." + +#: src/GMImportDialog.cpp:505 +msgid "Remove tracks that have been deleted from disk" +msgstr "Enlever les morceaux qui ont été supprimé du disque" + +#: src/GMImportDialog.cpp:507 +msgid "Update existing tracks:" +msgstr "Mettre à jour les morceaux existant :" + +#: src/GMImportDialog.cpp:508 +msgid "" +"Modified since last import\tOnly reread the tag when the file has been " +"modified." +msgstr "" +"Modifiés depuis l'import\tRelit les tags seulement si le fichier a été " +"modifié." + +#: src/GMImportDialog.cpp:510 +msgid "All\tAlways read the tags" +msgstr "Tout\tToujours lire les tags" + +#: src/GMImportDialog.cpp:511 +msgid "Remove tracks found in folder from database" +msgstr "Supprimer de la base de données les morceaux trouvés dans les dossiers" + +#: src/GMImportDialog.cpp:514 +msgid "&Track" +msgstr "&Piste" + +#: src/GMImportDialog.cpp:517 +msgid "Parse Settings" +msgstr "Paramètres d'analyse" + +#: src/GMImportDialog.cpp:521 +msgid "Parse info from:" +msgstr "Analyser les infos depuis:" + +#: src/GMImportDialog.cpp:524 +msgid "Tag" +msgstr "Tag" + +#: src/GMImportDialog.cpp:526 +msgid "Both" +msgstr "Les deux" + +#: src/GMImportDialog.cpp:528 +msgid "Default value:" +msgstr "Valeur par défaut:" + +#: src/GMImportDialog.cpp:532 +msgid "Set track number based on scan order." +msgstr "Numéro des pistes selon l'ordre d'analyse." + +#: src/GMImportDialog.cpp:537 +msgid "" +"%T - title %A - album name\n" +"%P - album artist name %p - track artist name \n" +"%N - track number %G - genre" +msgstr "" +"%T - Titre %P - nom de l'artiste pour l'album\n" +"%p - nom de l'artiste du morceau %A - nom de l'album\n" +"%N - numéro du morceau %G - genre" + +#: src/GMImportDialog.cpp:549 +msgid "Replace underscores with spaces" +msgstr "Remplacer les underscores par des espaces" + +#: src/GMImportDialog.cpp:562 +msgid "&Import" +msgstr "&Importer" + +#: src/GMPlayer.cpp:212 +msgid "Unable to initialize audio driver." +msgstr "Impossible d'initialiser le driver audio." + +#: src/GMPlayer.cpp:714 +msgid "Unknown host." +msgstr "Hôte inconnu." + +#: src/GMPlayer.cpp:715 +msgid "Unknown device" +msgstr "Périphérique inconnu" + +#: src/GMPlayer.cpp:716 +msgid "Network not reachable." +msgstr "Réseau introuvable." + +#: src/GMPlayer.cpp:717 +msgid "Audio output unavailable." +msgstr "Sorties Audio indisponibles." + +#: src/GMPlayer.cpp:718 +msgid "Connection Refused." +msgstr "Connection Refusée." + +#: src/GMPlayer.cpp:719 +msgid "File not found." +msgstr "Fichier introuvable." + +#: src/GMPlayer.cpp:720 +msgid "Resource not accessible. Check permissions" +msgstr "Ressource inaccessible. Vérifiez les permissions" + +#: src/GMPlayer.cpp:721 +msgid "Read Error" +msgstr "Erreur de Lecture" + +#: src/GMPlayer.cpp:722 +msgid "Error while loading library/plugin" +msgstr "Erreur pendant le chargement de la librairie/plugin" + +#: src/GMPlayer.cpp:723 +msgid "Warning" +msgstr "Attention" + +#: src/GMPlayer.cpp:724 +msgid "Security Warning" +msgstr "Attention Problème de Sécurité" + +#: src/GMPlayer.cpp:725 +msgid "Unknown Error" +msgstr "Erreur Inconnue" + +#: src/GMPlayer.cpp:761 +msgid "Error" +msgstr "Erreur" + +#: src/GMPlayerManager.cpp:439 +#, c-format +msgid "Unable to create directory %s\n" +msgstr "Impossible de créer le répertoire %s\n" + +#: src/GMPlayerManager.cpp:612 +msgid "" +"For some reason the FOX library was compiled without PNG support.\n" +"In order to show all icons, Goggles Music Manager requires PNG\n" +"support in the FOX library. If you've compiled FOX yourself, most\n" +"likely the libpng header files were not installed on your system." +msgstr "" +"Pour une raison inconnue la librairie FOX n'a pas été compilé avec le " +"support de PNG.\n" +"Afin de permettre à Goggles Music Manager d'afficher de petites icônes, la " +"librairie FOX\n" +"doit supporter PNG. Si vous avez compilé FOX par vous-même, le header libpng " +"ne\n" +"devait pas être installé sur votre système." + +#: src/GMPlayerManager.cpp:623 +msgid "Session bus not available. All features requiring dbus are disabled." +msgstr "" +"Le bus de session est indisponible. Toutes les fonctions qui utilisent dbus " +"sont désactivées." + +#: src/GMPlayerManager.cpp:633 +msgid "A DBus error occurred. All features requiring sessionbus are disabled." +msgstr "" +"Erreur DBus. Toutes les fonctions qui utilisent le bus de session sont " +"désactivées." + +#: src/GMPlayerManager.cpp:644 +msgid "" +"Session Bus not available. All features requiring sessionbus are disabled." +msgstr "" +"Le bus de session est indisponible. Toutes les fonctions qui utilisent le " +"bus de session sont désactivées." + +#: src/GMPlayerManager.cpp:719 src/GMPreferencesDialog.cpp:594 +msgid "Audio Device Error" +msgstr "Erreur sur le Périphérique Audio" + +#: src/GMPlayerManager.cpp:1551 +msgid "Last.FM Error" +msgstr "Erreur Last.FM" + +#: src/GMPlayerManager.cpp:1558 +msgid "Playback Error" +msgstr "Erreur de Lecture" + +#: src/GMPlayerManager.cpp:1708 +#, c-format +msgid "" +"%s\n" +"%s (%d)" +msgstr "" + +#: src/GMPlayListSource.cpp:99 src/GMPlayListSource.cpp:105 +#: src/GMPlayListSource.cpp:111 src/GMPlayListSource.cpp:121 +msgid "Remove…\tDel\tRemove track(s) from play list." +msgstr "Supprimer...\tSuppr\tSupprimer le(s) morceau(x) de la playliste." + +#: src/GMPlayListSource.cpp:161 +msgid "Edit…" +msgstr "Modifier..." + +#: src/GMPlayListSource.cpp:162 +#, fuzzy +msgid "Import…" +msgstr "Exporter..." + +#: src/GMPlayListSource.cpp:164 +msgid "Remove Playlist" +msgstr "Supprimer la Playliste" + +#: src/GMPlayListSource.cpp:262 +msgid "Remove tracks with genre from play list?" +msgstr "Supprimer tous les morceaux de ce genre de la bibliothèque ?" + +#: src/GMPlayListSource.cpp:265 +msgid "Remove tracks from artist from play list?" +msgstr "Supprimer les morceaux de l'artiste de la playliste?" + +#: src/GMPlayListSource.cpp:268 +msgid "Remove tracks from album from play list?" +msgstr "Supprimer les morceaux de l'album de la playliste ?" + +#: src/GMPlayListSource.cpp:271 +msgid "Remove track(s) from play list?" +msgstr "Supprimer le(s) morceau(x) de la playliste?" + +#: src/GMPlayListSource.cpp:284 +msgid "Remove tracks from music library" +msgstr "Supprimer les morceaux de la bibliothèque" + +#: src/GMPlayListSource.cpp:406 src/GMPlayListSource.cpp:407 +msgid "Edit Playlist" +msgstr "Modifier la Playliste" + +#: src/GMPlayListSource.cpp:407 +msgid "Change playlist name" +msgstr "Modifier le nom de la playliste" + +#: src/GMPlayListSource.cpp:432 +msgid "Delete Play List?" +msgstr "Supprimer la Playliste ?" + +#: src/GMPlayListSource.cpp:432 +msgid "Are you sure you want to delete the playlist?" +msgstr "Etes vous sûr de vouloir supprimer la playlist ?" + +#: src/GMPlayListSource.cpp:432 ../../../fox-1.6.37/src/FXMessageBox.cpp:111 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:116 +msgid "&Yes" +msgstr "&Oui" + +#: src/GMPlayListSource.cpp:432 ../../../fox-1.6.37/src/FXMessageBox.cpp:112 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:117 +msgid "&No" +msgstr "&Non" + +#: src/GMPreferencesDialog.cpp:252 +msgid "Preferences" +msgstr "Préférences" + +#: src/GMPreferencesDialog.cpp:286 +msgid "&General" +msgstr "&Général" + +#: src/GMPreferencesDialog.cpp:289 +msgid "Sort Options" +msgstr "Options de Tri" + +#: src/GMPreferencesDialog.cpp:293 +msgid "Ignore leading words" +msgstr "Ignorer les mots" + +#: src/GMPreferencesDialog.cpp:296 +msgid "Album Covers" +msgstr "Pochettes d'Albums" + +#: src/GMPreferencesDialog.cpp:299 +msgid "Show album cover of playing track\tShow album cover of playing track" +msgstr "" +"Afficher la pochette d'album pour le morceau courant\tAfficher la pochette " +"d'album pour le morceau courant" + +#: src/GMPreferencesDialog.cpp:300 +msgid "Show album covers in album browser\tShow album covers in album browser" +msgstr "" +"Afficher les pochettes d'ablums dans l'explorateur\tAfficher les pochettes " +"dl'ablums dans l'explorateur" + +#: src/GMPreferencesDialog.cpp:302 +msgid "System Tray" +msgstr "System Tray" + +#: src/GMPreferencesDialog.cpp:304 +msgid "Show Tray Icon\tShow tray icon in the system tray." +msgstr "Afficher le Tray Icône\tAfficher le tray icône dans le système tray." + +#: src/GMPreferencesDialog.cpp:307 +msgid "" +"Show Track Change Notifications\tInform notification daemon of track changes." +msgstr "" +"Afficher les notifications de changement de morceau\tInformer le démon de " +"notification des changements de morceaux." + +#: src/GMPreferencesDialog.cpp:311 +msgid "Last.fm" +msgstr "Last.fm" + +#: src/GMPreferencesDialog.cpp:315 +msgid "" +"This version of Goggles Music Manager is\n" +"not supported by Last-FM. Please upgrade\n" +"to a newer version of GMM." +msgstr "" +"Cette version de Goggles Music Manager\n" +"ne gère pas Last-FM. Vous devez installer\n" +"un version plus récente de GMM." + +#: src/GMPreferencesDialog.cpp:321 +msgid "Service:" +msgstr "" + +#: src/GMPreferencesDialog.cpp:338 +#, fuzzy +msgid "&Sign up…" +msgstr "&Inscrivez vous sur last.fm..." + +#: src/GMPreferencesDialog.cpp:344 +msgid "Username:" +msgstr "Nom d'Utilisateur:" + +#: src/GMPreferencesDialog.cpp:346 +msgid "Password:" +msgstr "Mot de passe:" + +#: src/GMPreferencesDialog.cpp:349 +msgid "Scrobble" +msgstr "" + +#: src/GMPreferencesDialog.cpp:359 +msgid "&Window" +msgstr "&Fenêtre" + +#: src/GMPreferencesDialog.cpp:362 +msgid "Window" +msgstr "Fenêtre" + +#: src/GMPreferencesDialog.cpp:365 +#, fuzzy +msgid "Close button minimizes to tray" +msgstr "Le bouton de fermeture cache la fenêtre principale" + +#: src/GMPreferencesDialog.cpp:366 +msgid "Show Status Bar" +msgstr "Afficher la Barre de Statut" + +#: src/GMPreferencesDialog.cpp:368 +msgid "Show Icons in Track Browser" +msgstr "Afficher les Icônes dans l'Explorateur de Pistes" + +#: src/GMPreferencesDialog.cpp:370 +msgid "Display playing track in title bar" +msgstr "Affiche le morceau courant dans la barre de titre" + +#: src/GMPreferencesDialog.cpp:372 +msgid "Player Controls" +msgstr "Contrôles du Lecteur" + +#: src/GMPreferencesDialog.cpp:376 +msgid "Location:" +msgstr "Position:" + +#: src/GMPreferencesDialog.cpp:378 +msgid "Top" +msgstr "Haut" + +#: src/GMPreferencesDialog.cpp:379 +msgid "Bottom" +msgstr "Bas" + +#: src/GMPreferencesDialog.cpp:387 +msgid "Title Format:" +msgstr "" + +#: src/GMPreferencesDialog.cpp:391 +msgid "Style:" +msgstr "Style:" + +#: src/GMPreferencesDialog.cpp:392 +msgid "Show Labels" +msgstr "Afficher les Libellés" + +#: src/GMPreferencesDialog.cpp:395 +msgid "Large Icons" +msgstr "Grande Icônes" + +#: src/GMPreferencesDialog.cpp:399 +msgid "A&ppearance" +msgstr "A&pparence" + +#: src/GMPreferencesDialog.cpp:402 +msgid "Colors" +msgstr "Couleurs" + +#: src/GMPreferencesDialog.cpp:409 +msgid "fg\tForeground Color" +msgstr "fg\tCouleur de premier plan" + +#: src/GMPreferencesDialog.cpp:410 +msgid "bg\tBackground Color" +msgstr "bg\tCouleur d'arrière plan" + +#: src/GMPreferencesDialog.cpp:411 +msgid "alt bg\tAlternative Background Color" +msgstr "alt bg\tCouleur Alternative d'Arrière Plan" + +#: src/GMPreferencesDialog.cpp:422 +msgid "Normal\tNormal Text Color" +msgstr "Normal\tCouleur Normale du Texte" + +#: src/GMPreferencesDialog.cpp:426 +msgid "Base\tBase Color" +msgstr "Base\tCouleur de Base" + +#: src/GMPreferencesDialog.cpp:429 +msgid "Selected\tSelected Text Color" +msgstr "Sélection\tCouleur du Texte Sélectionné" + +#: src/GMPreferencesDialog.cpp:433 +#, fuzzy +msgid "Menu\tMenu Base Color" +msgstr "Menu\tCouleur de Texte du Menu" + +#: src/GMPreferencesDialog.cpp:437 +msgid "Menu\tMenu Text Color" +msgstr "Menu\tCouleur de Texte du Menu" + +#: src/GMPreferencesDialog.cpp:441 +msgid "Border\tBorder Color" +msgstr "Bordure\tCouleur de la Bordure" + +#: src/GMPreferencesDialog.cpp:445 +msgid "Tooltip\tTooltip Color" +msgstr "Info Bulle\tCouleur de l'Info Bulle" + +#: src/GMPreferencesDialog.cpp:449 +msgid "Hilite\tHilite Color" +msgstr "Surbillance\t Couleur de Surbrillance" + +#: src/GMPreferencesDialog.cpp:456 +msgid "Shadow\tShadow Color" +msgstr "Ombre\tCouleur de l'Ombre" + +#: src/GMPreferencesDialog.cpp:459 +msgid "Playing\tPlaying Track Color" +msgstr "Lecture\tCouleur de la piste actuellement en lecture" + +#: src/GMPreferencesDialog.cpp:463 +#, fuzzy +msgid "Tray\tTray Background Color" +msgstr "bg\tCouleur d'arrière plan" + +#: src/GMPreferencesDialog.cpp:473 +msgid "Presets:" +msgstr "Presets:" + +#: src/GMPreferencesDialog.cpp:481 +msgid "Font & Icons" +msgstr "" + +#: src/GMPreferencesDialog.cpp:487 +#, fuzzy +msgid "Default Font" +msgstr "Valeur par défaut:" + +#: src/GMPreferencesDialog.cpp:489 +msgid "Change…" +msgstr "Modifier..." + +#: src/GMPreferencesDialog.cpp:490 +msgid "Icons" +msgstr "Icônes" + +#: src/GMPreferencesDialog.cpp:509 +msgid "&Audio" +msgstr "&Audio" + +#: src/GMPreferencesDialog.cpp:512 +msgid "Engine" +msgstr "Moteur" + +#: src/GMPreferencesDialog.cpp:516 +msgid "Audio Driver:" +msgstr "Driver Audio:" + +#: src/GMPreferencesDialog.cpp:527 +msgid "Close audio device on pause." +msgstr "Libérer l'audio à la pause." + +#: src/GMPreferencesDialog.cpp:528 +msgid "Turn off playback engine on stop." +msgstr "Eteindre le moteur audio à stop." + +#: src/GMPreferencesDialog.cpp:529 +msgid "" +"Turn on playback engine on startup.\tFor faster startup, playback engine is " +"normally started when first track is played.\tFor faster startup, playback " +"engine is normally started when first track is played." +msgstr "" +"Lancer le moteur Audio au démarrage.\tAfin que le démarrage soit plus " +"rapide, le moteur audio est généralement lancé à la lecture de la première " +"piste.\tAfin que le démarrage soit plus rapide, le moteur audio est " +"généralement lancé à la lecture de la première piste." + +#: src/GMPreferencesDialog.cpp:532 +msgid "Playback" +msgstr "Playback" + +#: src/GMPreferencesDialog.cpp:536 +msgid "Replay Gain:" +msgstr "Replay Gain:" + +#: src/GMPreferencesDialog.cpp:538 +msgid "Off" +msgstr "" + +#: src/GMPreferencesDialog.cpp:543 +msgid "Gapless playback" +msgstr "Lecture Gapless" + +#: src/GMPreferencesDialog.cpp:544 +msgid "Volume Normalization" +msgstr "Normalisation du Volume" + +#: src/GMPreferencesDialog.cpp:594 +#, c-format +msgid "Failed to open requested audio driver: %s" +msgstr "Impossible d'utiliser le driver audio:%s" + +#: src/GMPreferencesDialog.cpp:678 +msgid "Current" +msgstr "Courant" + +#: src/GMPreferencesDialog.cpp:696 +msgid "Custom" +msgstr "Personnalisé" + +#: src/GMPreferencesDialog.cpp:769 src/GMPreferencesDialog.cpp:774 +#: src/GMWindow.cpp:1133 src/GMWindow.cpp:1140 src/GMWindow.cpp:1147 +#: src/GMWindow.cpp:1154 +msgid "Unable to launch webbrowser" +msgstr "Impossible de lancer le navigateur internet" + +#: src/GMPreferencesDialog.cpp:1172 +msgid "Select Normal Font" +msgstr "Selection de la Police" + +#: src/GMRemote.cpp:295 +msgid "&Next" +msgstr "&Next" + +#: src/GMRemote.cpp:296 +msgid "P&revious" +msgstr "P&revious" + +#: src/GMRemote.cpp:297 src/GMWindow.cpp:1197 +msgid "&Play" +msgstr "&Play" + +#: src/GMRemote.cpp:298 +msgid "&Stop" +msgstr "&Stop" + +#: src/GMRemote.cpp:300 +msgid "Show Browser" +msgstr "Afficher l'Exploreur" + +#: src/GMRemote.cpp:301 src/GMTrayIcon.cpp:307 +msgid "Quit" +msgstr "Quitter" + +#: src/GMRemote.cpp:385 +#, fuzzy +msgid "\tShow Browser\tShow Browser" +msgstr "Afficher l'Exploreur" + +#: src/GMRemote.cpp:387 +msgid "\tStart Playback\tStart Playback" +msgstr "\tDémarrer la lecture\tDémarrer la lecture" + +#: src/GMRemote.cpp:388 +msgid "\tStop Playback\tStop Playback" +msgstr "\tArrêter la Lecture\tArrêter la Lecture" + +#: src/GMRemote.cpp:390 +msgid "\tPlay Previous Track\tPlay previous track." +msgstr "\tJouer le morceau Précédent\tJouer le morceau précédent." + +#: src/GMRemote.cpp:391 +msgid "\tPlay Next Track\tPlay next track." +msgstr "\tJouer le Morceau Suivant\tJouer le Morceau Suivant." + +#: src/GMRemote.cpp:397 src/GMWindow.cpp:222 +msgid "\tAdjust Volume\tAdjust Volume" +msgstr "\tAjuster le Volume\tAjuste le Volume" + +#: src/GMSearch.cpp:128 +msgid "Unable to open the database" +msgstr "Impossible d'ouvrir la base de données" + +#: src/GMSearch.cpp:252 +msgid "Database Error: Unable to retrieve all filenames." +msgstr "" +"Erreur de la Base de Données : Impossible de récupérer tous les noms de " +"fichiers." + +#: src/GMSearch.cpp:280 +msgid "Unable to update track" +msgstr "Impossible de mettre à jour la piste" + +#: src/GMSearch.cpp:439 +msgid "Unable to insert track into the database" +msgstr "Impossible d'ajouter la piste à la base de données" + +#: src/GMSearch.cpp:505 src/GMTrackDatabase.cpp:205 +msgid "Fatal Error" +msgstr "Erreur Fatale" + +#: src/GMSearch.cpp:505 +#, c-format +msgid "" +"%s\n" +"Please contact support if this error keeps occuring." +msgstr "" +"%s\n" +"Contactez le support si l'erreur se reproduit." + +#: src/GMSearch.cpp:534 src/GMSearch.cpp:584 +msgid "Updating Database..." +msgstr "Mise à Jour de la Base de Données..." + +#: src/GMSearch.cpp:537 src/GMSearch.cpp:626 +msgid "Please wait. This may take a while." +msgstr "Veuillez attendre. Cela peut prendre un peu de temps." + +#: src/GMSearch.cpp:555 src/GMSearch.cpp:637 +msgid "New Tracks:" +msgstr "Nouvelles Pistes:" + +#: src/GMSearch.cpp:561 src/GMSearch.cpp:643 +msgid "File:" +msgstr "Fichier:" + +#: src/GMSearch.cpp:594 src/GMSearch.cpp:623 +msgid "Importing Files..." +msgstr "Importation des Fichiers..." + +#: src/GMSearch.cpp:652 +msgid "&Stop Import" +msgstr "&Arreter l'importation" + +#: src/GMSourceView.cpp:54 +msgid "Sources\tPress to change sorting order\tPress to change sorting order" +msgstr "" +"Sources\tCliquez pour changer l'ordre de tri\tCliquez pour changer l'ordre " +"de tri" + +#: src/GMSourceView.cpp:245 src/GMWindow.cpp:261 +msgid "New Playlist…\t\tCreate a new playlist" +msgstr "Nouvelle Playliste...\t\tCréer une nouvelle playliste" + +#: src/GMSourceView.cpp:246 src/GMWindow.cpp:262 +#, fuzzy +msgid "Import Playlist…\t\tImport existing playlist" +msgstr "Nouvelle Playliste...\t\tCréer une nouvelle playliste" + +#: src/GMSourceView.cpp:247 src/GMWindow.cpp:263 +msgid "New Radio Station…\t\tCreate a new playlist" +msgstr "Nouvelle Station Radio...\t\tCréer une nouvelle playliste" + +#: src/GMStreamSource.cpp:64 +msgid "Station" +msgstr "Station" + +#: src/GMStreamSource.cpp:112 src/GMStreamSource.cpp:118 +msgid "New Station…\t\t" +msgstr "Nouvelle Station...\t\t" + +#: src/GMStreamSource.cpp:117 +msgid "Edit…\t\t" +msgstr "Modifier...\t\t" + +#: src/GMStreamSource.cpp:119 +msgid "Remove\t\tRemove." +msgstr "Supprimer\t\tSupprimer." + +#: src/GMStreamSource.cpp:165 src/GMStreamSource.cpp:166 +msgid "New Internet Radio Station" +msgstr "Nouvelle Radio Internet" + +#: src/GMStreamSource.cpp:166 +msgid "Specify url and description of new station" +msgstr "Spécifiez l'url et la description de la nouvelle station" + +#: src/GMStreamSource.cpp:168 +#, fuzzy +msgid "C&reate" +msgstr "&Créer" + +#: src/GMStreamSource.cpp:173 src/GMStreamSource.cpp:211 +msgid "Location" +msgstr "Chemin" + +#: src/GMStreamSource.cpp:175 src/GMStreamSource.cpp:214 +msgid "Description" +msgstr "Description" + +#: src/GMStreamSource.cpp:189 +msgid "Untitled" +msgstr "Sans-Titre" + +#: src/GMStreamSource.cpp:202 src/GMStreamSource.cpp:203 +msgid "Edit Internet Radio Station" +msgstr "Modifier la Radio Internet" + +#: src/GMStreamSource.cpp:203 +msgid "Update url and description of station" +msgstr "Mettre à jour l'url et la description de la radio" + +#: src/GMStreamSource.cpp:265 +msgid "Remove Internet Radio Station(s)?" +msgstr "Supprimer la/les Radio(s) Internet ?" + +#: src/GMStreamSource.cpp:266 +msgid "Remove Internet Radio Station(s) from library?" +msgstr "Supprimer la/les Radio(s) Internet de la Bibliothèque ?" + +#: src/GMStreamSource.cpp:280 +#, c-format +msgid "Unable to remove station from the library." +msgstr "Impossible de Supprimer la Radio de la Bibliothèque." + +#: src/GMTrackDatabase.cpp:193 +msgid "" +"An incompatible (future) version of the database was found.\n" +"This usually happens when you try to downgrade to a older version of GMM\n" +"Press OK to continue and reset the database (all information will be " +"lost!).\n" +"Press Cancel to quit now and leave the database as is." +msgstr "" + +#: src/GMTrackDatabase.cpp:205 +msgid "" +"Goggles Music Manager was unable to open the database.\n" +"The database may have been corrupted. Please remove ~/.goggles/goggles.db to " +"try again.\n" +"if the error keeps occuring, please file an issue at http://code.google.com/" +"p/gogglesmm" +msgstr "" +"Goggles Music Manager n'a pas réussi à ouvrir la base de donnée.\n" +"La base de donnée est peut être corrompue. Supprimez ~/.goggles/goggles.db " +"puis essayez à nouveau.\n" +"Si l'erreur subsiste, vous pouvez poster un bug sur le site : http://code." +"google.com/p/gogglesmm" + +#: src/GMTrackView.cpp:245 +msgid "\tClose Filter\tClose Filter" +msgstr "\tFermer les Filtres\tFermer les Filtres" + +#: src/GMTrackView.cpp:246 +#, fuzzy +msgid "&Find" +msgstr "Chercher" + +#: src/GMTrackView.cpp:252 +#, fuzzy +msgid "Tags\tPress to change sorting order\tPress to change sorting order" +msgstr "" +"Genres\tCliquez pour changer l'ordre de tri\tCliquez pour changer l'ordre de " +"tri" + +#: src/GMTrackView.cpp:256 +msgid "Artists\tPress to change sorting order\tPress to change sorting order" +msgstr "" +"Artistes\tCliquez pour changer l'ordre de tri\tCliquez pour changer l'ordre " +"de tri" + +#: src/GMTrackView.cpp:260 +msgid "Albums\tPress to change sorting order\tPress to change sorting order" +msgstr "" +"Albums\tCliquez pour changer l'ordre de tri\tCliquez pour changer l'ordre de " +"tri" + +#: src/GMTrackView.cpp:282 src/GMWindow.cpp:299 +msgid "&Configure Columns…" +msgstr "&Configurer les Colonnes..." + +#: src/GMTrackView.cpp:286 +msgid "Browse" +msgstr "Explorer" + +#: src/GMTrackView.cpp:287 +msgid "Shuffle\tCtrl-R" +msgstr "Mélanger\tCtrl-R" + +#: src/GMTrackView.cpp:294 ../../../fox-1.6.37/src/FXDirSelector.cpp:388 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:734 +msgid "Reverse" +msgstr "Inverser" + +#: src/GMTrackView.cpp:804 +#, c-format +msgid "All %d Genres" +msgstr "Tous les %d Genres" + +#: src/GMTrackView.cpp:806 +msgid "All Genres" +msgstr "Tous les Genres" + +#: src/GMTrackView.cpp:832 +#, c-format +msgid "All %d Artists" +msgstr "Tous les %d Artistes" + +#: src/GMTrackView.cpp:872 +#, c-format +msgid "All %d Albums" +msgstr "Tous les %d Albums" + +#: src/GMTrackView.cpp:1258 +#, c-format +msgid "By %s" +msgstr "Par %s" + +#: src/GMTrackView.cpp:1586 src/GMTrackView.cpp:1609 +msgid "Sort by Album Year" +msgstr "Trier par année d'album" + +#: src/GMTrackView.cpp:1637 +msgid "&Columns\t\tChange Visible Columns." +msgstr "&Colonnes\t\tChanger les Colonnes Visibles." + +#: src/GMTrackView.cpp:1638 +msgid "&Sort\t\tChange Sorting." +msgstr "&Trier\t\tChange le Tri." + +#: src/GMTrayIcon.cpp:302 src/GMWindow.cpp:841 src/GMWindow.cpp:925 +#: src/GMWindow.cpp:948 src/GMWindow.cpp:954 +msgid "Play" +msgstr "Play" + +#: src/GMTrayIcon.cpp:303 src/GMWindow.cpp:843 +msgid "Stop" +msgstr "Stop" + +#: src/GMTrayIcon.cpp:304 +msgid "Previous Track" +msgstr "Previous Track" + +#: src/GMTrayIcon.cpp:305 +msgid "Next Track" +msgstr "Next Track" + +#: src/GMWindow.cpp:205 +msgid "Play\tStart Playback\tStart Playback" +msgstr "Play\t Démarrer la Lecture\tDémarre la Lecture" + +#: src/GMWindow.cpp:205 +msgid "Pause\tPause\tPause Playback" +msgstr "Pause\tPause\tPause dans la lecture" + +#: src/GMWindow.cpp:206 +msgid "Stop\tStop Playback\tStop Playback" +msgstr "Stop\tArrêter la Lecture\tArrêter la lecture" + +#: src/GMWindow.cpp:208 +msgid "Previous\tPlay Previous Track\tPlay previous track." +msgstr "Previous\tJouer la Piste Précédente\tJouer la piste précédente." + +#: src/GMWindow.cpp:209 +msgid "Next\tPlay Next Track\tPlay next track." +msgstr "Next\tJouer la Piste Suivante\tJouer la piste suivante." + +#: src/GMWindow.cpp:255 +msgid "&Music" +msgstr "&Musique" + +#: src/GMWindow.cpp:256 +msgid "Import Folder…\tCtrl-O\tImport Music from folder into Library" +msgstr "Importer...\tCtrl-O\tImporter de la musique dans la Bibliothèque" + +#: src/GMWindow.cpp:257 +msgid "Sync Folder…\t\tSynchronize Folder with Music in Library" +msgstr "Synchroniser...\t\tSynchroniser les répertoires avec la Bibliothèque" + +#: src/GMWindow.cpp:259 +msgid "Play File or Stream…\t\tPlay File or Stream" +msgstr "" + +#: src/GMWindow.cpp:266 +msgid "&Quit\tCtrl-Q\tQuit the application." +msgstr "&Quitter\tCtrl-Q\t Quitter l'application." + +#: src/GMWindow.cpp:271 +msgid "&Edit" +msgstr "&Modifier" + +#: src/GMWindow.cpp:272 +msgid "&Copy\tCtrl-C\tCopy Selected Tracks" +msgstr "&Copier\tCtrl-C\tCopie les Morceaux Sélectionnés" + +#: src/GMWindow.cpp:273 +msgid "&Cut\tCtrl-X\tCut Selected Tracks" +msgstr "&Couper\tCtrl-X\tCoupe les Morceaux Sélectionnés" + +#: src/GMWindow.cpp:274 +msgid "&Paste\tCtrl-V\tPaste Clipboard Selection" +msgstr "&Coller\tCtrl-V\tColle le Contenu du Presse Papier" + +#: src/GMWindow.cpp:276 +msgid "Find…\tCtrl-F\tShow search filter." +msgstr "Chercher...\tCtrl-F\tAfficher les filtres de recherche." + +#: src/GMWindow.cpp:278 +msgid "Preferences…" +msgstr "Préférences..." + +#: src/GMWindow.cpp:282 +msgid "&View" +msgstr "&Affichage" + +#: src/GMWindow.cpp:283 +msgid "&Browse\tCtrl-B\tShow genre artist and album browser." +msgstr "&Parcourir\tCtrl-B\tAffiche l'explorateur d'artistes et albums." + +#: src/GMWindow.cpp:284 +msgid "Show &Genres\tCtrl-G\tShow genre browser." +msgstr "Afficher les &Genres\tCtrl-G\tAfficher l'explorateur de genre." + +#: src/GMWindow.cpp:287 src/GMWindow.cpp:289 +msgid "Show &Sources\tCtrl-S\tShow source browser " +msgstr "Afficher les &Sources\tCtrl-S\tAfficher l'explorateur de sources" + +#: src/GMWindow.cpp:294 +msgid "Show Full Screen\tF12\tToggle fullscreen mode." +msgstr "Passer en Plein Ecran\tF12\tPasser en mode plein écran." + +#: src/GMWindow.cpp:296 +#, fuzzy +msgid "Show Mini Player\tCtrl-M\tToggle Mini Player." +msgstr "Afficher le Mini Lecteur\tF11\tAfficher le Mini Lecteur." + +#: src/GMWindow.cpp:300 +msgid "&Sort" +msgstr "&Trier" + +#: src/GMWindow.cpp:302 +msgid "&Jump to Current Track\tCtrl-J\tShow current playing track." +msgstr "" +"&Revenir au Morceau Courant\tCtrl-J\tAffiche le morceau - album - artiste " +"courant." + +#: src/GMWindow.cpp:306 +msgid "&Control" +msgstr "&Contrôles" + +#: src/GMWindow.cpp:307 +msgid "Play\tCtrl-P\tStart playback." +msgstr "Play\tCtrl-P\tDémarre la lecture." + +#: src/GMWindow.cpp:308 +msgid "Stop\tCtrl-\\\tStop playback." +msgstr "Stop\tCtrl\\\tArrêter la lecture." + +#: src/GMWindow.cpp:309 +msgid "Previous Track\tCtrl-[\tPlay next track." +msgstr "Morceau Précédent\tCtrl-[\tJouer le morceau précédent." + +#: src/GMWindow.cpp:310 +msgid "Next Track\tCtrl-]\tPlay previous track." +msgstr "Morceau Suivant\tCtrl-]\tJouer le morceau suivant." + +#: src/GMWindow.cpp:312 +msgid "Repeat Off\tCtrl-,\tRepeat current track." +msgstr "Ne pas Répéter\tCtrl-,\tNe pas répéter le morceau courant." + +#: src/GMWindow.cpp:313 +msgid "Repeat Track\tCtrl-.\tRepeat current track." +msgstr "Répéter le morceau\tCtrl-.\tRépète le morceau courant." + +#: src/GMWindow.cpp:314 +msgid "Repeat All Tracks\tCtrl-/\tRepeat all tracks." +msgstr "Répéter Tous les Morceaux\tCtrl-/\tRépète tous les morceaux." + +#: src/GMWindow.cpp:315 +msgid "Repeat A-B\tCtrl-T\tRepeat section of track." +msgstr "Répeter A-B\tCtrl-T\tRépete la section du morceau." + +#: src/GMWindow.cpp:316 +msgid "Shuffle Mode\tAlt-R\tPlay tracks in random order." +msgstr "Mode Shuffle\tAlt-R\tJoue les morceaux dans un ordre aléatoire." + +#: src/GMWindow.cpp:318 +msgid "Equalizer\t\t" +msgstr "Equaliser\t\t" + +#: src/GMWindow.cpp:319 +msgid "Sleep Timer\t\tSetup sleeptimer." +msgstr "Minuteur\t\tParamétrer le minuteur." + +#: src/GMWindow.cpp:323 +msgid "&Help" +msgstr "&Aide" + +#: src/GMWindow.cpp:324 +msgid "&Homepage" +msgstr "&Page officielle" + +#: src/GMWindow.cpp:325 +msgid "&Report Issue…" +msgstr "&Rapporter une erreur..." + +#: src/GMWindow.cpp:327 +msgid "&Sign up for last.fm…" +msgstr "&Inscrivez vous sur last.fm..." + +#: src/GMWindow.cpp:328 +msgid "" +"&Join GMM on last.fm…\t\tJoin the Goggles Music Manager group on last.fm…" +msgstr "" +"&Rejoignez GMM sur last.fm...\t\tRejoignez le groupe Goggles Music Manager " +"sur last.fm..." + +#: src/GMWindow.cpp:330 +msgid "&About…" +msgstr "&A Propos..." + +#: src/GMWindow.cpp:842 src/GMWindow.cpp:940 +msgid "Pause" +msgstr "Pause" + +#: src/GMWindow.cpp:844 +msgid "Previous" +msgstr "Précédent" + +#: src/GMWindow.cpp:845 +msgid "Next" +msgstr "Suivant" + +#: src/GMWindow.cpp:920 src/GMWindow.cpp:949 src/GMWindow.cpp:955 +msgid "Start playback." +msgstr "Démarre la lecture." + +#: src/GMWindow.cpp:926 src/GMWindow.cpp:927 +#, fuzzy +msgid "Start playback" +msgstr "Démarre la lecture." + +#: src/GMWindow.cpp:934 src/GMWindow.cpp:941 +msgid "Pause playback." +msgstr "Mettre en Pause." + +#: src/GMWindow.cpp:935 +#, fuzzy +msgid "Pause playback" +msgstr "Mettre en Pause." + +#: src/GMWindow.cpp:1052 src/GMWindow.cpp:1054 +msgid "Repeat A-B" +msgstr "Répeter A-B" + +#: src/GMWindow.cpp:1053 +msgid "Repeat A" +msgstr "Répeter A" + +#: src/GMWindow.cpp:1195 +msgid "Play File or Stream" +msgstr "" + +#: src/GMWindow.cpp:1202 +#, fuzzy +msgid "Please specify a file or url to play:" +msgstr "Veuillez spécifier la MRL à jouer" + +#: src/GMWindow.cpp:1206 +#, fuzzy +msgid "…" +msgstr "Modifier..." + +#: src/GMWindow.cpp:1212 +#, fuzzy +msgid "Select File" +msgstr "Tout sélectionner" + +#: src/GMWindow.cpp:1243 +msgid "Sleep Timer" +msgstr "Minuteur" + +#: src/GMWindow.cpp:1244 +msgid "Setup sleep timer" +msgstr "Régler le minuteur" + +#: src/GMWindow.cpp:1244 +msgid "Stop playback within a certain time" +msgstr "Arrête la lecture après un laps de temps" + +#: src/GMWindow.cpp:1246 +msgid "&Start Timer" +msgstr "&Démarrer le minuteur" + +#: src/GMWindow.cpp:1251 +msgid "Sleep in" +msgstr "Eteindre dans" + +#: src/GMWindow.cpp:1253 +msgid "hours and" +msgstr "Heures et" + +#: src/GMWindow.cpp:1255 +msgid "minutes." +msgstr "minutes." + +#: src/GMDatabaseSource.h:131 +msgid "Music Library" +msgstr "Bibliothèque" + +#: src/GMStreamSource.h:57 +msgid "Internet Radio" +msgstr "Radio Internet" + +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:122 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:127 +msgid "&Quit" +msgstr "&Quitter" + +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:133 +msgid "&Skip" +msgstr "&Passer" + +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:134 +msgid "Skip &All" +msgstr "&Tout passer" + +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:140 +msgid "&Don't Save" +msgstr "&Ne pas Sauver" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:220 +msgid "\tPick color" +msgstr "\tChoisir une couleur" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:229 +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:274 +msgid "\tHue, Saturation, Value" +msgstr "\tTeinte, Saturation, Valeur" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:239 +msgid "\tRed, Green, Blue" +msgstr "\tRouge, Vert, Bleu" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:245 +msgid "&Red:" +msgstr "&Rouge:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:250 +msgid "&Green:" +msgstr "&Vert:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:255 +msgid "&Blue:" +msgstr "&Bleu:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:260 +msgid "&Alpha:" +msgstr "&Alpha:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:280 +msgid "Hue:" +msgstr "Teinte:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:285 +msgid "Saturation:" +msgstr "Saturation:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:290 +msgid "Value:" +msgstr "Valeur:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:295 +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:330 +msgid "Alpha:" +msgstr "Alpha:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:309 +msgid "\tCyan, Magenta, Yellow" +msgstr "\tCyan, Magenta, Jaune" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:315 +msgid "Cyan:" +msgstr "Cyan:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:320 +msgid "Magenta:" +msgstr "Magenta:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:325 +msgid "Yellow:" +msgstr "Jaune:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:344 +msgid "\tBy Name" +msgstr "\tPar Nom" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:281 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:521 +msgid "Create New Directory" +msgstr "Créer un Nouveau Répertoire" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:284 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:524 +msgid "Already Exists" +msgstr "Existe Déjà" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:288 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:528 +msgid "Cannot Create" +msgstr "Impossible à Créer" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:309 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:595 +msgid "Copy File" +msgstr "Copie un Fichier" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:315 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:601 +msgid "Error Copying File" +msgstr "Erreur en Copiant le Fichier" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:326 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:618 +msgid "Move File" +msgstr "Déplacer le Fichier" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:332 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:624 +msgid "Error Moving File" +msgstr "Erreur en Déplaçant le Fichier" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:343 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:641 +msgid "Link File" +msgstr "Lier le Fichier" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:349 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:647 +msgid "Error Linking File" +msgstr "Erreur en Liant le Fichier" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:359 +msgid "Deleting file" +msgstr "Suppression du fichier" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:361 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:667 +msgid "Error Deleting File" +msgstr "Erreur à la suppression du fichier" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:381 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:719 +msgid "Up one level" +msgstr "Remonter" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:382 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:720 +msgid "Home directory" +msgstr "Répertoire Home" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:383 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:721 +msgid "Work directory" +msgstr "Répertoire de travail" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:387 +msgid "Sorting" +msgstr "Trier" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:389 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:735 +msgid "Ignore case" +msgstr "Ignorer la casse" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:390 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:746 +msgid "Hidden files" +msgstr "Fichiers cachés" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:393 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:754 +msgid "Bookmarks" +msgstr "Bookmark" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:394 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:757 +msgid "Set bookmark" +msgstr "Créer un bookmark" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:395 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:758 +msgid "Clear bookmarks" +msgstr "Effacer les bookmarks" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:411 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:774 +msgid "New directory..." +msgstr "Nouveau Répertoire..." + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:412 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:775 +msgid "Copy..." +msgstr "Copier..." + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:413 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:776 +msgid "Move..." +msgstr "Déplacer..." + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:414 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:777 +msgid "Link..." +msgstr "Lier..." + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:415 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:778 +msgid "Delete..." +msgstr "Supprimer..." + +#: ../../../fox-1.6.37/src/FXFileList.cpp:195 +msgid "Modified Date" +msgstr "Date modifiée" + +#: ../../../fox-1.6.37/src/FXFileList.cpp:196 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:731 +msgid "User" +msgstr "Utilisateur" + +#: ../../../fox-1.6.37/src/FXFileList.cpp:197 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:732 +msgid "Group" +msgstr "Groupe" + +#: ../../../fox-1.6.37/src/FXFileList.cpp:198 +msgid "Attributes" +msgstr "Attributs" + +#: ../../../fox-1.6.37/src/FXFileList.cpp:200 +msgid "Link" +msgstr "Lien" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:521 +msgid "Create new directory with name: " +msgstr "Créer un nouveau répertoire avec le nom :" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:524 +#, c-format +msgid "File or directory %s already exists.\n" +msgstr "Le fichier ou le répertoire %s existe déjà.\n" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:528 +#, c-format +msgid "Cannot create directory %s.\n" +msgstr "Impossible de créer le répertoire %s.\n" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:594 +#, c-format +msgid "" +"Copy file from location:\n" +"\n" +"%s\n" +"\n" +"to location: " +msgstr "" +"Copie du fichier depuis:\n" +"\n" +"%s\n" +"\n" +"vers :" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:601 +#, c-format +msgid "" +"Unable to copy file:\n" +"\n" +"%s to: %s\n" +"\n" +"Continue with operation?" +msgstr "" +"Impossible de copier le fichier:\n" +"\n" +"%s vers %s\n" +"\n" +"Continuer l'opération?" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:617 +#, c-format +msgid "" +"Move file from location:\n" +"\n" +"%s\n" +"\n" +"to location: " +msgstr "" +"Déplacer le fichier depuis:\n" +"\n" +"%s\n" +"\n" +"vers:" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:624 +#, c-format +msgid "" +"Unable to move file:\n" +"\n" +"%s to: %s\n" +"\n" +"Continue with operation?" +msgstr "" +"Impossible de déplacer le fichier:\n" +"\n" +"%s vers: %s\n" +"\n" +"Continuer l'opération ?" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:640 +#, c-format +msgid "" +"Link file from location:\n" +"\n" +"%s\n" +"\n" +"to location: " +msgstr "" +"Créer un lien depuis:\n" +"\n" +"%s\n" +"vers :" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:647 +#, c-format +msgid "" +"Unable to link file:\n" +"\n" +"%s to: %s\n" +"\n" +"Continue with operation?" +msgstr "" +"Impossible de lier le fichier:\n" +"\n" +"%s à: %s\n" +"\n" +"Continuer l'opération ?" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:663 +msgid "Deleting files" +msgstr "Suppression des fichiers" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:663 +#, c-format +msgid "" +"Are you sure you want to delete the file:\n" +"\n" +"%s" +msgstr "" +"Etes vous sûr de vouloir supprimer ce fichier ? : \n" +"\n" +"%s" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:667 +#, c-format +msgid "" +"Unable to delete file:\n" +"\n" +"%s\n" +"\n" +"Continue with operation?" +msgstr "" +"Impossible de supprimer le ficher :\n" +"\n" +"%s\n" +"\n" +"Continuer l'opération ?" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:722 +msgid "Select all" +msgstr "Tout sélectionner" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:726 +msgid "Sort by" +msgstr "Trier par" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:738 +msgid "View" +msgstr "Vue" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:739 +msgid "Small icons" +msgstr "Petites icônes" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:740 +msgid "Big icons" +msgstr "Grandes icônes" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:741 +msgid "Details" +msgstr "Détails" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:743 +msgid "Rows" +msgstr "Lignes" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:744 +msgid "Columns" +msgstr "Colonnes" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:747 +msgid "Preview images" +msgstr "Aperçu d'images" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:749 +msgid "Normal images" +msgstr "Images Normales" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:750 +msgid "Medium images" +msgstr "Images Moyennes" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:751 +msgid "Giant images" +msgstr "Grandes Images" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:111 +msgid "&Replace" +msgstr "&Remplacer" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:112 +msgid "Re&place All" +msgstr "Tout Rem&placer" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:121 +msgid "S&earch for:" +msgstr "Re&cherche sur:" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:129 +msgid "Replace &with:" +msgstr "Remplacer &par:" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:138 +msgid "Ex&act" +msgstr "Ex&act" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:139 +msgid "&Ignore Case" +msgstr "&Ignorer la casse" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:140 +msgid "E&xpression" +msgstr "E&xpression" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:141 +msgid "&Backward" +msgstr "&Retour en arrière" + +#: ../../../fox-1.6.37/src/FXSearchDialog.cpp:71 +msgid "&Search" +msgstr "&Rechercher" + +#: ../../../fox-1.6.37/src/FXStatusLine.cpp:82 +msgid "Ready." +msgstr "Prêts." + +#~ msgid "Old Name" +#~ msgstr "Ancien Nom" + +#~ msgid "New Name" +#~ msgstr "Nouveau Nom" + +#~ msgid "Pitch:" +#~ msgstr "Hauteur:" + +#~ msgid "Any" +#~ msgstr "Tout" + +#~ msgid "Fixed" +#~ msgstr "Fixée" + +#~ msgid "Variable" +#~ msgstr "Variable" + +#, fuzzy +#~ msgid " Type:" +#~ msgstr "Type" + +#, fuzzy +#~ msgid "Scalable" +#~ msgstr "Ajustable:" + +#, fuzzy +#~ msgid "Size:" +#~ msgstr "Taille" + +#, fuzzy +#~ msgid "Family:" +#~ msgstr "&Famille:" + +#~ msgid "Always Show Remote" +#~ msgstr "Toujours Afficher le Mini Lecteur" + +#~ msgid "Source\tActive Source Color" +#~ msgstr "Source\tCouleur de la Source Active" + +#~ msgid "About" +#~ msgstr "A propos" + +#~ msgid "" +#~ "An incompatible version of SQLite (%s) is being used.\n" +#~ "Goggles Music Manager requires at least SQLite 3.3.8.\n" +#~ "Please upgrade your SQLite installation." +#~ msgstr "" +#~ "La version de SQLite (%s) utilisée est incompatible.\n" +#~ "Goggles Music Manager utilise une version au moins\n" +#~ "équivalente à SQLite 3.3.8. Vous devez mettre à jour\n" +#~ "votre installation de SQLite." + +#~ msgid "" +#~ "This version of SQLite (%s) is broken.\n" +#~ "Please upgrade your SQLite installation to at least 3.6.3." +#~ msgstr "" +#~ "Cette version de SQLite (%s) ne fonctionner pas.\n" +#~ "Vous devez mettre à jour votre installation de SQLite avec une version au " +#~ "moins égale à 3.6.3." + +#~ msgid "" +#~ "&Join GMM on last.fm…\tJoin the Goggles Music Manager group on last.fm…" +#~ "\tJoin the Goggles Music Manager group on last.fm…" +#~ msgstr "" +#~ "&Rejoignez GMM sur last.fm...\tRejoignez le groupe Goggles Music Manager " +#~ "sur last.fm...\tRejoignez le groupe Goggles Music Manager sur last.fm..." + +#~ msgid "Font" +#~ msgstr "Police" + +#~ msgid "Theme Directory:" +#~ msgstr "Répertoire du thème:" + +#~ msgid "Select Theme Directory" +#~ msgstr "Selection du Répertoire du Thème" + +#~ msgid "thin" +#~ msgstr "fin" + +#~ msgid "normal" +#~ msgstr "normal" + +#~ msgid "bold" +#~ msgstr "gras" + +#~ msgid "regular" +#~ msgstr "normal" + +#~ msgid "&Weight:" +#~ msgstr "&Epaisseur:" + +#~ msgid "&Style:" +#~ msgstr "&Style:" + +#~ msgid "Si&ze:" +#~ msgstr "Tai&lle:" + +#~ msgid "Character Set:" +#~ msgstr "Encodage:" + +#~ msgid "West European" +#~ msgstr "Europe de l'Ouest" + +#~ msgid "East European" +#~ msgstr "Europe de Est" + +#~ msgid "South European" +#~ msgstr "Europe du Sud" + +#~ msgid "North European" +#~ msgstr "Europe du Nord" + +#~ msgid "Cyrillic" +#~ msgstr "Cyrillique" + +#~ msgid "Arabic" +#~ msgstr "Arabe" + +#~ msgid "Greek" +#~ msgstr "Grèc" + +#~ msgid "Hebrew" +#~ msgstr "Hébreu" + +#~ msgid "Turkish" +#~ msgstr "Turque" + +#~ msgid "Nordic" +#~ msgstr "Nordique" + +#~ msgid "Thai" +#~ msgstr "Thai" + +#~ msgid "Baltic" +#~ msgstr "Baltique" + +#~ msgid "Celtic" +#~ msgstr "Celtic" + +#~ msgid "Russian" +#~ msgstr "Russe" + +#~ msgid "Central European (cp1250)" +#~ msgstr "Europe Centrale (cp1250)" + +#~ msgid "Russian (cp1251)" +#~ msgstr "Russe (cp1251)" + +#~ msgid "Latin1 (cp1252)" +#~ msgstr "Latin1 (cp1252)" + +#~ msgid "Greek (cp1253)" +#~ msgstr "Grèc (cp1253)" + +#~ msgid "Turkish (cp1254)" +#~ msgstr "Turque (cp1254)" + +#~ msgid "Hebrew (cp1255)" +#~ msgstr "Hébreu (cp1255)" + +#~ msgid "Arabic (cp1256)" +#~ msgstr "Arabe (cp1256)" + +#~ msgid "Baltic (cp1257)" +#~ msgstr "Baltique (cp1257)" + +#~ msgid "Vietnam (cp1258)" +#~ msgstr "Vietnam (cp1258)" + +#~ msgid "Thai (cp874)" +#~ msgstr "Thai (cp874)" + +#~ msgid "UNICODE" +#~ msgstr "UNICODE" + +#~ msgid "Set Width:" +#~ msgstr "Espace:" + +#~ msgid "All Fonts:" +#~ msgstr "Toutes les Polices:" + +#~ msgid "Preview:" +#~ msgstr "Aperçu:" + +#, fuzzy +#~ msgid "Import Playlist…\t\tImport a existing playlist" +#~ msgstr "Nouvelle Playliste...\t\tCréer une nouvelle playliste" + +#~ msgid "Import Files?" +#~ msgstr "Importer les Fichiers?" + +#~ msgid "" +#~ "Would you like import the pasted files and/or directories into the Music " +#~ "Library?" +#~ msgstr "" +#~ "Voulez vous importer les fichiers et/ou répertoires insérés, dans la " +#~ "bibliothèque?" + +#~ msgid "" +#~ "%s\n" +#~ "by: %s\n" +#~ "from: %s" +#~ msgstr "" +#~ "%s\n" +#~ "par: %s\n" +#~ "depuis: %s" + +#~ msgid "Now Playing" +#~ msgstr "En Lecture" + +#~ msgid "Open URL…\t\tOpen Stream or File" +#~ msgstr "Ouvrir l'URL...\t\tOuvrir un Stream ou un Fichier" + +#~ msgid "Open MRL" +#~ msgstr "Ouvrir une MRL" + +#~ msgid "P&ause" +#~ msgstr "P&ause" + +#~ msgid "\tPause\tPause Playback" +#~ msgstr "\tPause\tMettre la Lecture en Pause" + +#~ msgid "A capella" +#~ msgstr "A capella" + +#~ msgid "Acid" +#~ msgstr "Acid" + +#~ msgid "Acid Jazz" +#~ msgstr "Acid Jazz" + +#~ msgid "Acid Punk" +#~ msgstr "Acid Punk" + +#~ msgid "Acoustic" +#~ msgstr "Acoustic" + +#~ msgid "Alternative" +#~ msgstr "Alternative" + +#~ msgid "AlternRockAmbient" +#~ msgstr "AlternRockAmbient" + +#~ msgid "Avantgarde" +#~ msgstr "Avantgarde" + +#~ msgid "Ballad" +#~ msgstr "Ballad" + +#~ msgid "Bass" +#~ msgstr "Bass" + +#~ msgid "Bebob" +#~ msgstr "Bebob" + +#~ msgid "Big Band" +#~ msgstr "Big Band" + +#~ msgid "Blues" +#~ msgstr "Blues" + +#~ msgid "Bluegrass" +#~ msgstr "Bluegrass" + +#~ msgid "Booty Bass" +#~ msgstr "Booty Bass" + +#~ msgid "Cabaret" +#~ msgstr "Cabaret" + +#~ msgid "Chamber Music" +#~ msgstr "Chamber Music" + +#~ msgid "Chanson" +#~ msgstr "Chanson" + +#~ msgid "Chorus" +#~ msgstr "Chorus" + +#~ msgid "Christian Rap" +#~ msgstr "Christian Rap" + +#~ msgid "Classical" +#~ msgstr "Classical" + +#~ msgid "Classic Rock" +#~ msgstr "Classic Rock" + +#~ msgid "Club" +#~ msgstr "Club" + +#~ msgid "Comedy" +#~ msgstr "Comedy" + +#~ msgid "Country" +#~ msgstr "Country" + +#~ msgid "Cult" +#~ msgstr "Cult" + +#~ msgid "Dance" +#~ msgstr "Dance" + +#~ msgid "Dance Hall" +#~ msgstr "Dance Hall" + +#~ msgid "Darkwave" +#~ msgstr "Darkwave" + +#~ msgid "Death Metal" +#~ msgstr "Death Metal" + +#~ msgid "Disco" +#~ msgstr "Disco" + +#~ msgid "Dream" +#~ msgstr "Dream" + +#~ msgid "Drum Solo" +#~ msgstr "Drum Solo" + +#~ msgid "Duet" +#~ msgstr "Duet" + +#~ msgid "Easy Listening" +#~ msgstr "Easy Listening" + +#~ msgid "Electronic" +#~ msgstr "Electronic" + +#~ msgid "Ethnic" +#~ msgstr "Ethnic" + +#~ msgid "Euro-Dance" +#~ msgstr "Euro-Dance" + +#~ msgid "Euro-House" +#~ msgstr "Euro-House" + +#~ msgid "Euro-Techno" +#~ msgstr "Euro-Techno" + +#~ msgid "Fast Fusion" +#~ msgstr "Fast Fusion" + +#~ msgid "Folk" +#~ msgstr "Folk" + +#~ msgid "Folk-Rock" +#~ msgstr "Folk-Rock" + +#~ msgid "Folklore" +#~ msgstr "Folklore" + +#~ msgid "Freestyle" +#~ msgstr "Freestyle" + +#~ msgid "Funk" +#~ msgstr "Funk" + +#~ msgid "Fusion" +#~ msgstr "Fusion" + +#~ msgid "Game" +#~ msgstr "Game" + +#~ msgid "Gangsta" +#~ msgstr "Gangsta" + +#~ msgid "Gospel" +#~ msgstr "Gospel" + +#~ msgid "Gothic" +#~ msgstr "Gothic" + +#~ msgid "Gothic Rock" +#~ msgstr "Gothic Rock" + +#~ msgid "Grunge" +#~ msgstr "Grunge" + +#~ msgid "Hard Rock" +#~ msgstr "Hard Rock" + +#~ msgid "Hip-Hop" +#~ msgstr "Hip-Hop" + +#~ msgid "House" +#~ msgstr "House" + +#~ msgid "Humour" +#~ msgstr "Humour" + +#~ msgid "Industrial" +#~ msgstr "Industrial" + +#~ msgid "Instrumental" +#~ msgstr "Instrumental" + +#~ msgid "Instrumental Pop" +#~ msgstr "Instrumental Pop" + +#~ msgid "Instrumental Rock" +#~ msgstr "Instrumental Rock" + +#~ msgid "Jazz" +#~ msgstr "Jazz" + +#~ msgid "Jazz+Funk" +#~ msgstr "Jazz+Funk" + +#~ msgid "Jungle" +#~ msgstr "Jungle" + +#~ msgid "Latin" +#~ msgstr "Latin" + +#~ msgid "Lo-Fi" +#~ msgstr "Lo-Fi" + +#~ msgid "Meditative" +#~ msgstr "Meditative" + +#~ msgid "Metal" +#~ msgstr "Metal" + +#~ msgid "Musical" +#~ msgstr "Musical" + +#~ msgid "National Folk" +#~ msgstr "National Folk" + +#~ msgid "Native American" +#~ msgstr "Native American" + +#~ msgid "New Age" +#~ msgstr "New Age" + +#~ msgid "New Wave" +#~ msgstr "New Wave" + +#~ msgid "Noise" +#~ msgstr "Noise" + +#~ msgid "Oldies" +#~ msgstr "Oldies" + +#~ msgid "Opera" +#~ msgstr "Opera" + +#~ msgid "Other" +#~ msgstr "Other" + +#~ msgid "Polka" +#~ msgstr "Polka" + +#~ msgid "Pop" +#~ msgstr "Pop" + +#~ msgid "Pop-Folk" +#~ msgstr "Pop-Folk" + +#~ msgid "Pop/Funk" +#~ msgstr "Pop/Funk" + +#~ msgid "Porn Groove" +#~ msgstr "Porn Groove" + +#~ msgid "Power Ballad" +#~ msgstr "Power Ballad" + +#~ msgid "Pranks" +#~ msgstr "Pranks" + +#~ msgid "Primus" +#~ msgstr "Primus" + +#~ msgid "Progressive Rock" +#~ msgstr "Progressive Rock" + +#~ msgid "Psychadelic" +#~ msgstr "Psychadelic" + +#~ msgid "Psychedelic Rock" +#~ msgstr "Psychedelic Rock" + +#~ msgid "Punk" +#~ msgstr "Punk" + +#~ msgid "Punk Rock" +#~ msgstr "Punk Rock" + +#~ msgid "R&B" +#~ msgstr "R&B" + +#~ msgid "Rap" +#~ msgstr "Rap" + +#~ msgid "Rave" +#~ msgstr "Rave" + +#~ msgid "Reggae" +#~ msgstr "Reggae" + +#~ msgid "Retro" +#~ msgstr "Retro" + +#~ msgid "Revival" +#~ msgstr "Revival" + +#~ msgid "Rhythmic Soul" +#~ msgstr "Rhythmic Soul" + +#~ msgid "Rock" +#~ msgstr "Rock" + +#~ msgid "Rock & Roll" +#~ msgstr "Rock & Roll" + +#~ msgid "Samba" +#~ msgstr "Samba" + +#~ msgid "Satire" +#~ msgstr "Satire" + +#~ msgid "Showtunes" +#~ msgstr "Showtunes" + +#~ msgid "Ska" +#~ msgstr "Ska" + +#~ msgid "Slow Jam" +#~ msgstr "Slow Jam" + +#~ msgid "Slow Rock" +#~ msgstr "Slow Rock" + +#~ msgid "Sonata" +#~ msgstr "Sonata" + +#~ msgid "Soul" +#~ msgstr "Soul" + +#~ msgid "Soundtrack" +#~ msgstr "Soundtrack" + +#~ msgid "Sound Clip" +#~ msgstr "Sound Clip" + +#~ msgid "Southern Rock" +#~ msgstr "Southern Rock" + +#~ msgid "Space" +#~ msgstr "Space" + +#~ msgid "Speech" +#~ msgstr "Speech" + +#~ msgid "Swing" +#~ msgstr "Swing" + +#~ msgid "Symphonic Rock" +#~ msgstr "Symphonic Rock" + +#~ msgid "Symphony" +#~ msgstr "Symphony" + +#~ msgid "Tango" +#~ msgstr "Tango" + +#~ msgid "Techno" +#~ msgstr "Techno" + +#~ msgid "Techno-Industrial" +#~ msgstr "Techno-Industrial" + +#~ msgid "Top 40" +#~ msgstr "Top 40" + +#~ msgid "Trailer" +#~ msgstr "Trailer" + +#~ msgid "Trance" +#~ msgstr "Trance" + +#~ msgid "Tribal" +#~ msgstr "Tribal" + +#~ msgid "Trip-Hop" +#~ msgstr "Trip-Hop" + +#~ msgid "Vocal" +#~ msgstr "Vocal" + +#~ msgid "Start Up:" +#~ msgstr "Démarrage:" + +#~ msgid "Show Main Window" +#~ msgstr "Afficher la Fenêtre Principale" + +#~ msgid "Show Mini Remote" +#~ msgstr "Afficher le Mini Lecteur" + +#~ msgid "Previous View" +#~ msgstr "Vue Précédente" + +#~ msgid "Configure Columns…" +#~ msgstr "Configurer les Colonnes..." diff --git a/po/gogglesmm.pot b/po/gogglesmm.pot new file mode 100644 index 0000000..97e66aa --- /dev/null +++ b/po/gogglesmm.pot @@ -0,0 +1,2282 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Sander Jansen +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gogglesmm 0.10.0\n" +"Report-Msgid-Bugs-To: s.jansen@gmail.com\n" +"POT-Creation-Date: 2011-02-09 23:26-0600\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: src/GMAbout.cpp:136 src/GMDatabaseSource.cpp:218 +#: src/GMDatabaseSource.cpp:2215 src/GMPreferencesDialog.cpp:551 +msgid "&Close" +msgstr "" + +#: src/GMColumnDialog.cpp:204 +msgid "Configure Columns" +msgstr "" + +#: src/GMColumnDialog.cpp:207 ../../../fox-1.6.37/src/FXColorSelector.cpp:168 +msgid "&Accept" +msgstr "" + +#: src/GMColumnDialog.cpp:208 src/GMDatabaseSource.cpp:98 +#: src/GMDatabaseSource.cpp:1274 src/GMDatabaseSource.cpp:1704 +#: src/GMDatabaseSource.cpp:1804 src/GMDatabaseSource.cpp:1878 +#: src/GMDatabaseSource.cpp:2121 src/GMDatabaseSource.cpp:2182 +#: src/GMImportDialog.cpp:175 src/GMImportDialog.cpp:563 +#: src/GMPlayListSource.cpp:281 src/GMPlayListSource.cpp:410 +#: src/GMSearch.cpp:572 src/GMStreamSource.cpp:169 src/GMStreamSource.cpp:206 +#: src/GMStreamSource.cpp:271 src/GMWindow.cpp:1198 src/GMWindow.cpp:1247 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:107 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:118 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:123 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:129 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:135 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:142 +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:169 +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:135 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:205 +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:113 +msgid "&Cancel" +msgstr "" + +#: src/GMColumnDialog.cpp:212 +msgid "" +"Choose the order of information to appear\n" +"in the track list." +msgstr "" + +#: src/GMColumnDialog.cpp:218 +msgid "Move Up" +msgstr "" + +#: src/GMColumnDialog.cpp:219 +msgid "Move Down" +msgstr "" + +#: src/GMDatabaseSource.cpp:52 +msgid "Invalid Template" +msgstr "" + +#: src/GMDatabaseSource.cpp:52 +#, c-format +msgid "" +"The provided template is invalid. The track title %%T needs to be " +"specified.\n" +"Please fix the filename template in the preference panel." +msgstr "" + +#: src/GMDatabaseSource.cpp:72 src/GMTrackDatabase.cpp:193 +msgid "Database Error" +msgstr "" + +#: src/GMDatabaseSource.cpp:72 +msgid "Oops. Database Error" +msgstr "" + +#: src/GMDatabaseSource.cpp:87 +msgid "No changes" +msgstr "" + +#: src/GMDatabaseSource.cpp:87 +msgid "Filenames did not require any changes" +msgstr "" + +#: src/GMDatabaseSource.cpp:94 +msgid "Rename Audio Files?" +msgstr "" + +#: src/GMDatabaseSource.cpp:95 +msgid "Renaming Audio Files…" +msgstr "" + +#: src/GMDatabaseSource.cpp:95 +msgid "The following audio files are going to be renamed" +msgstr "" + +#: src/GMDatabaseSource.cpp:97 +msgid "&Rename" +msgstr "" + +#: src/GMDatabaseSource.cpp:121 src/GMDatabaseSource.cpp:125 +msgid "Unable to rename file" +msgstr "" + +#: src/GMDatabaseSource.cpp:121 +#, c-format +msgid "" +"Unable to rename:\n" +"%s\n" +"\n" +"to:%s\n" +"Continue renaming files?" +msgstr "" + +#: src/GMDatabaseSource.cpp:125 +#, c-format +msgid "" +"Unable to rename:\n" +"%s\n" +"\n" +"to:%s" +msgstr "" + +#: src/GMDatabaseSource.cpp:160 src/GMImportDialog.cpp:534 +msgid "Filename Template" +msgstr "" + +#: src/GMDatabaseSource.cpp:177 +msgid "" +"Template may contain absolute or relative path, environment variables\n" +"and ~. Relative paths are based on the location of the original file. The\n" +"file extension gets automatically added. The following macros\n" +"may be used:" +msgstr "" + +#: src/GMDatabaseSource.cpp:178 +msgid "" +"%T - title %A - album name\n" +"%P - album artist name %p - track artist name\n" +"%y - year %d - disc number\n" +"%N - track number (2 digits) %n - track number \n" +"%G - genre" +msgstr "" + +#: src/GMDatabaseSource.cpp:185 +msgid "Conditions may be used as well:" +msgstr "" + +#: src/GMDatabaseSource.cpp:186 +msgid "" +"?c - display a if c is not empty else display b.\n" +"?c - display c if not empty\n" +msgstr "" + +#: src/GMDatabaseSource.cpp:195 src/GMDatabaseSource.cpp:1815 +#: src/GMImportDialog.cpp:546 +msgid "Template:" +msgstr "" + +#: src/GMDatabaseSource.cpp:199 src/GMDatabaseSource.cpp:1819 +msgid "Encoding:" +msgstr "" + +#: src/GMDatabaseSource.cpp:205 +msgid "Exclude:" +msgstr "" + +#: src/GMDatabaseSource.cpp:209 src/GMDatabaseSource.cpp:1825 +msgid "Options:" +msgstr "" + +#: src/GMDatabaseSource.cpp:210 src/GMDatabaseSource.cpp:1826 +msgid "Replace spaces with underscores" +msgstr "" + +#: src/GMDatabaseSource.cpp:212 src/GMDatabaseSource.cpp:1828 +msgid "Lower case" +msgstr "" + +#: src/GMDatabaseSource.cpp:214 src/GMDatabaseSource.cpp:1830 +msgid "Lower case extension" +msgstr "" + +#: src/GMDatabaseSource.cpp:341 src/GMStreamSource.cpp:63 +msgid "No." +msgstr "" + +#: src/GMDatabaseSource.cpp:342 +msgid "Queue" +msgstr "" + +#: src/GMDatabaseSource.cpp:343 src/GMDatabaseSource.cpp:1391 +#: src/GMTrackView.cpp:238 +msgid "Title" +msgstr "" + +#: src/GMDatabaseSource.cpp:344 src/GMDatabaseSource.cpp:1396 +#: src/GMTrackView.cpp:239 +msgid "Artist" +msgstr "" + +#: src/GMDatabaseSource.cpp:345 src/GMDatabaseSource.cpp:1397 +msgid "Album Artist" +msgstr "" + +#: src/GMDatabaseSource.cpp:346 src/GMDatabaseSource.cpp:1405 +#: src/GMPreferencesDialog.cpp:540 src/GMTrackView.cpp:240 +msgid "Album" +msgstr "" + +#: src/GMDatabaseSource.cpp:347 src/GMDatabaseSource.cpp:1364 +#: src/GMDatabaseSource.cpp:1375 src/GMDatabaseSource.cpp:1381 +msgid "Disc" +msgstr "" + +#: src/GMDatabaseSource.cpp:348 src/GMDatabaseSource.cpp:1408 +#: src/GMStreamSource.cpp:66 src/GMStreamSource.cpp:177 +#: src/GMStreamSource.cpp:216 src/GMTrackView.cpp:241 +msgid "Genre" +msgstr "" + +#: src/GMDatabaseSource.cpp:349 src/GMDatabaseSource.cpp:1369 +#: src/GMDatabaseSource.cpp:1387 +msgid "Year" +msgstr "" + +#: src/GMDatabaseSource.cpp:350 ../../../fox-1.6.37/src/FXFileSelector.cpp:730 +msgid "Time" +msgstr "" + +#: src/GMDatabaseSource.cpp:490 +msgid "Remove…\tDel\tRemove Genre from Library." +msgstr "" + +#: src/GMDatabaseSource.cpp:496 src/GMDatabaseSource.cpp:505 +#: src/GMPlayListSource.cpp:104 src/GMPlayListSource.cpp:110 +msgid "Copy\tCtrl-C\tCopy associated tracks to the clipboard." +msgstr "" + +#: src/GMDatabaseSource.cpp:499 src/GMDatabaseSource.cpp:508 +msgid "Remove…\tDel\tRemove associated tracks from library." +msgstr "" + +#: src/GMDatabaseSource.cpp:513 src/GMPlayListSource.cpp:116 +msgid "Edit…\tF2\tEdit Track Information." +msgstr "" + +#: src/GMDatabaseSource.cpp:514 src/GMPlayListSource.cpp:117 +msgid "Copy\tCtrl-C\tCopy track(s) to clipboard." +msgstr "" + +#: src/GMDatabaseSource.cpp:518 src/GMPlayListSource.cpp:120 +msgid "Open Folder Location\t\tOpen Folder Location." +msgstr "" + +#: src/GMDatabaseSource.cpp:523 +msgid "Remove…\tDel\tRemove track(s) from library." +msgstr "" + +#: src/GMDatabaseSource.cpp:537 +msgid "New Play List…\t\tCreate a new play list." +msgstr "" + +#: src/GMDatabaseSource.cpp:539 src/GMPlayListSource.cpp:163 +msgid "Export…" +msgstr "" + +#: src/GMDatabaseSource.cpp:540 +msgid "Information…\t\tLibrary Statistics" +msgstr "" + +#: src/GMDatabaseSource.cpp:542 +msgid "Remove All Tracks\t\tRemove all tracks from the library" +msgstr "" + +#: src/GMDatabaseSource.cpp:1272 +msgid "Edit Track Information" +msgstr "" + +#: src/GMDatabaseSource.cpp:1275 src/GMPlayListSource.cpp:409 +#: src/GMStreamSource.cpp:205 ../../../fox-1.6.37/src/FXMessageBox.cpp:128 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:143 +msgid "&Save" +msgstr "" + +#: src/GMDatabaseSource.cpp:1315 +msgid "&Tag" +msgstr "" + +#: src/GMDatabaseSource.cpp:1320 +msgid "&Properties" +msgstr "" + +#: src/GMDatabaseSource.cpp:1325 src/GMImportDialog.cpp:525 +msgid "Filename" +msgstr "" + +#: src/GMDatabaseSource.cpp:1329 ../../../fox-1.6.37/src/FXFileList.cpp:193 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:728 +msgid "Type" +msgstr "" + +#: src/GMDatabaseSource.cpp:1333 ../../../fox-1.6.37/src/FXFileList.cpp:194 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:729 +msgid "Size" +msgstr "" + +#: src/GMDatabaseSource.cpp:1341 src/GMStreamSource.cpp:65 +msgid "Bitrate" +msgstr "" + +#: src/GMDatabaseSource.cpp:1345 +msgid "Samplerate" +msgstr "" + +#: src/GMDatabaseSource.cpp:1349 +msgid "Channels" +msgstr "" + +#: src/GMDatabaseSource.cpp:1358 src/GMPreferencesDialog.cpp:539 +msgid "Track" +msgstr "" + +#: src/GMDatabaseSource.cpp:1403 +msgid "\tSeparate Artists" +msgstr "" + +#: src/GMDatabaseSource.cpp:1403 +msgid "\tShared Artists" +msgstr "" + +#: src/GMDatabaseSource.cpp:1416 +msgid "Auto track number. Offset:" +msgstr "" + +#: src/GMDatabaseSource.cpp:1425 +msgid "Update Tag in File" +msgstr "" + +#: src/GMDatabaseSource.cpp:1430 +msgid "Update Filename" +msgstr "" + +#: src/GMDatabaseSource.cpp:1431 +msgid "Set export template…" +msgstr "" + +#: src/GMDatabaseSource.cpp:1645 +msgid "Update Tags?" +msgstr "" + +#: src/GMDatabaseSource.cpp:1645 +msgid "" +"No tracks were updated.\n" +"Would you still like to write the tags for the selected tracks?" +msgstr "" + +#: src/GMDatabaseSource.cpp:1700 +msgid "Remove Audio Files?" +msgstr "" + +#: src/GMDatabaseSource.cpp:1701 +msgid "Remove Audio Files..." +msgstr "" + +#: src/GMDatabaseSource.cpp:1701 +msgid "The following audio files are going to be removed" +msgstr "" + +#: src/GMDatabaseSource.cpp:1703 src/GMDatabaseSource.cpp:1877 +#: src/GMPlayListSource.cpp:280 src/GMStreamSource.cpp:270 +msgid "&Remove" +msgstr "" + +#: src/GMDatabaseSource.cpp:1729 +msgid "Export Main Library" +msgstr "" + +#: src/GMDatabaseSource.cpp:1731 +msgid "Export Play List" +msgstr "" + +#: src/GMDatabaseSource.cpp:1744 +msgid "Overwrite File?" +msgstr "" + +#: src/GMDatabaseSource.cpp:1744 +msgid "File already exists. Would you like to overwrite it?" +msgstr "" + +#: src/GMDatabaseSource.cpp:1784 +msgid "Export Genre" +msgstr "" + +#: src/GMDatabaseSource.cpp:1785 +msgid "Export tracks with genre to destination directory." +msgstr "" + +#: src/GMDatabaseSource.cpp:1787 +msgid "Export Artists" +msgstr "" + +#: src/GMDatabaseSource.cpp:1788 +msgid "Export tracks from artist to destination directory." +msgstr "" + +#: src/GMDatabaseSource.cpp:1790 +msgid "Export Albums" +msgstr "" + +#: src/GMDatabaseSource.cpp:1791 +msgid "Export tracks from album to destination directory." +msgstr "" + +#: src/GMDatabaseSource.cpp:1793 +msgid "Export Tracks" +msgstr "" + +#: src/GMDatabaseSource.cpp:1794 +msgid "Export tracks to destination directory." +msgstr "" + +#: src/GMDatabaseSource.cpp:1803 +msgid "&Export" +msgstr "" + +#: src/GMDatabaseSource.cpp:1853 src/GMPlayListSource.cpp:261 +msgid "Remove Genre?" +msgstr "" + +#: src/GMDatabaseSource.cpp:1854 +msgid "Remove tracks with genre from library?" +msgstr "" + +#: src/GMDatabaseSource.cpp:1858 src/GMPlayListSource.cpp:264 +msgid "Remove Artist?" +msgstr "" + +#: src/GMDatabaseSource.cpp:1859 +msgid "Remove tracks from artist from library?" +msgstr "" + +#: src/GMDatabaseSource.cpp:1863 src/GMPlayListSource.cpp:267 +msgid "Remove Album?" +msgstr "" + +#: src/GMDatabaseSource.cpp:1864 +msgid "Remove tracks from album from library?" +msgstr "" + +#: src/GMDatabaseSource.cpp:1868 src/GMPlayListSource.cpp:270 +msgid "Remove Track(s)?" +msgstr "" + +#: src/GMDatabaseSource.cpp:1869 +msgid "Remove track(s) from library?" +msgstr "" + +#: src/GMDatabaseSource.cpp:1881 src/GMPlayListSource.cpp:285 +msgid "Remove tracks from disk" +msgstr "" + +#: src/GMDatabaseSource.cpp:1895 src/GMDatabaseSource.cpp:1899 +#: src/GMDatabaseSource.cpp:1903 src/GMDatabaseSource.cpp:1907 +#: src/GMPlayListSource.cpp:308 src/GMStreamSource.cpp:280 +msgid "Library Error" +msgstr "" + +#: src/GMDatabaseSource.cpp:1895 +msgid "Unable to remove genre from the library" +msgstr "" + +#: src/GMDatabaseSource.cpp:1899 +msgid "Unable to remove artist from the library" +msgstr "" + +#: src/GMDatabaseSource.cpp:1903 +msgid "Unable to remove album from the library" +msgstr "" + +#: src/GMDatabaseSource.cpp:1907 src/GMPlayListSource.cpp:308 +msgid "Unable to remove track from the library." +msgstr "" + +#: src/GMDatabaseSource.cpp:2117 src/GMDatabaseSource.cpp:2118 +msgid "Create Playlist" +msgstr "" + +#: src/GMDatabaseSource.cpp:2118 +msgid "Specify name of the new playlist" +msgstr "" + +#: src/GMDatabaseSource.cpp:2120 +msgid "&Create" +msgstr "" + +#: src/GMDatabaseSource.cpp:2125 src/GMPlayListSource.cpp:415 +#: ../../../fox-1.6.37/src/FXFileList.cpp:192 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:727 +msgid "Name" +msgstr "" + +#: src/GMDatabaseSource.cpp:2127 +msgid "New Playlist" +msgstr "" + +#: src/GMDatabaseSource.cpp:2178 src/GMDatabaseSource.cpp:2179 +msgid "Clear Music Library?" +msgstr "" + +#: src/GMDatabaseSource.cpp:2179 +msgid "Remove all tracks from the music library?" +msgstr "" + +#: src/GMDatabaseSource.cpp:2181 +msgid "&Remove All" +msgstr "" + +#: src/GMDatabaseSource.cpp:2185 +msgid "Keep play lists" +msgstr "" + +#: src/GMDatabaseSource.cpp:2212 src/GMDatabaseSource.cpp:2213 +msgid "Music Library Information" +msgstr "" + +#: src/GMDatabaseSource.cpp:2213 +msgid "You music collection consists of…" +msgstr "" + +#: src/GMDatabaseSource.cpp:2221 +msgid "Tracks:" +msgstr "" + +#: src/GMDatabaseSource.cpp:2226 +msgid "Artists:" +msgstr "" + +#: src/GMDatabaseSource.cpp:2230 +msgid "Albums:" +msgstr "" + +#: src/GMDatabaseSource.cpp:2234 +msgid "Total Time:" +msgstr "" + +#: src/GMEQDialog.cpp:96 +msgid "Equalizer" +msgstr "" + +#: src/GMEQDialog.cpp:135 +msgid "Equalizer:" +msgstr "" + +#: src/GMEQDialog.cpp:137 +msgid "\tSave" +msgstr "" + +#: src/GMEQDialog.cpp:138 +msgid "\tReset" +msgstr "" + +#: src/GMEQDialog.cpp:139 +msgid "\tRemove" +msgstr "" + +#: src/GMEQDialog.cpp:172 +msgid "Pre-amp" +msgstr "" + +#: src/GMEQDialog.cpp:244 src/GMEQDialog.cpp:254 +msgid "Disabled" +msgstr "" + +#: src/GMEQDialog.cpp:247 src/GMEQDialog.cpp:299 +msgid "Manual" +msgstr "" + +#: src/GMEQDialog.cpp:314 +msgid "Delete Preset" +msgstr "" + +#: src/GMEQDialog.cpp:314 +#, c-format +msgid "Are you sure you want to delete %s preset?" +msgstr "" + +#: src/GMEQDialog.cpp:334 +msgid "Preset Name" +msgstr "" + +#: src/GMEQDialog.cpp:334 +msgid "Please enter preset name:" +msgstr "" + +#: src/GMEQDialog.cpp:338 +msgid "Overwrite Preset" +msgstr "" + +#: src/GMEQDialog.cpp:338 +#, c-format +msgid "Preset %s already exists. Would you like to overwrite it?" +msgstr "" + +#: src/GMFontDialog.cpp:209 +msgid "Ultra Condensed" +msgstr "" + +#: src/GMFontDialog.cpp:210 +msgid "Extra Condensed" +msgstr "" + +#: src/GMFontDialog.cpp:211 +msgid "Condensed" +msgstr "" + +#: src/GMFontDialog.cpp:212 +msgid "Semi Condensed" +msgstr "" + +#: src/GMFontDialog.cpp:214 +msgid "Semi Expanded" +msgstr "" + +#: src/GMFontDialog.cpp:215 +msgid "Expanded" +msgstr "" + +#: src/GMFontDialog.cpp:216 +msgid "Extra Expanded" +msgstr "" + +#: src/GMFontDialog.cpp:217 +msgid "Ultra Expanded" +msgstr "" + +#: src/GMFontDialog.cpp:224 src/GMPreferencesDialog.cpp:1223 +msgid "Thin" +msgstr "" + +#: src/GMFontDialog.cpp:225 src/GMPreferencesDialog.cpp:1224 +msgid "Extra Light" +msgstr "" + +#: src/GMFontDialog.cpp:226 src/GMPreferencesDialog.cpp:1225 +msgid "Light" +msgstr "" + +#: src/GMFontDialog.cpp:228 src/GMPreferencesDialog.cpp:1227 +msgid "Medium" +msgstr "" + +#: src/GMFontDialog.cpp:229 src/GMPreferencesDialog.cpp:1228 +msgid "Demibold" +msgstr "" + +#: src/GMFontDialog.cpp:230 src/GMPreferencesDialog.cpp:1229 +msgid "Bold" +msgstr "" + +#: src/GMFontDialog.cpp:231 src/GMPreferencesDialog.cpp:1230 +msgid "Extra Bold" +msgstr "" + +#: src/GMFontDialog.cpp:232 src/GMPreferencesDialog.cpp:1231 +msgid "Heavy" +msgstr "" + +#: src/GMFontDialog.cpp:239 +msgid "Reverse Oblique" +msgstr "" + +#: src/GMFontDialog.cpp:240 +msgid "Reverse Italic" +msgstr "" + +#: src/GMFontDialog.cpp:242 +msgid "Italic" +msgstr "" + +#: src/GMFontDialog.cpp:243 +msgid "Oblique" +msgstr "" + +#: src/GMFontDialog.cpp:265 +msgid "Normal" +msgstr "" + +#: src/GMImportDialog.cpp:91 ../../../fox-1.6.37/src/FXDirSelector.cpp:137 +msgid "&Directory:" +msgstr "" + +#: src/GMImportDialog.cpp:166 ../../../fox-1.6.37/src/FXFileSelector.cpp:196 +msgid "&File Name:" +msgstr "" + +#: src/GMImportDialog.cpp:168 ../../../fox-1.6.37/src/FXMessageBox.cpp:102 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:106 +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:134 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:198 +msgid "&OK" +msgstr "" + +#: src/GMImportDialog.cpp:170 ../../../fox-1.6.37/src/FXFileSelector.cpp:200 +msgid "File F&ilter:" +msgstr "" + +#: src/GMImportDialog.cpp:174 ../../../fox-1.6.37/src/FXFileSelector.cpp:204 +msgid "Read Only" +msgstr "" + +#: src/GMImportDialog.cpp:179 src/GMSearch.cpp:565 src/GMSearch.cpp:647 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:208 +msgid "Directory:" +msgstr "" + +#: src/GMImportDialog.cpp:186 ../../../fox-1.6.37/src/FXFileSelector.cpp:228 +msgid "&Set bookmark\t\tBookmark current directory." +msgstr "" + +#: src/GMImportDialog.cpp:187 ../../../fox-1.6.37/src/FXFileSelector.cpp:229 +msgid "&Clear bookmarks\t\tClear bookmarks." +msgstr "" + +#: src/GMImportDialog.cpp:203 ../../../fox-1.6.37/src/FXFileSelector.cpp:244 +msgid "\tGo up one directory\tMove up to higher directory." +msgstr "" + +#: src/GMImportDialog.cpp:204 ../../../fox-1.6.37/src/FXFileSelector.cpp:245 +msgid "\tGo to home directory\tBack to home directory." +msgstr "" + +#: src/GMImportDialog.cpp:205 ../../../fox-1.6.37/src/FXFileSelector.cpp:246 +msgid "\tGo to work directory\tBack to working directory." +msgstr "" + +#: src/GMImportDialog.cpp:206 ../../../fox-1.6.37/src/FXFileSelector.cpp:247 +msgid "\tBookmarks\tVisit bookmarked directories." +msgstr "" + +#: src/GMImportDialog.cpp:209 ../../../fox-1.6.37/src/FXFileSelector.cpp:250 +msgid "\tCreate new directory\tCreate new directory." +msgstr "" + +#: src/GMImportDialog.cpp:210 ../../../fox-1.6.37/src/FXFileSelector.cpp:251 +msgid "\tShow list\tDisplay directory with small icons." +msgstr "" + +#: src/GMImportDialog.cpp:211 ../../../fox-1.6.37/src/FXFileSelector.cpp:252 +msgid "\tShow icons\tDisplay directory with big icons." +msgstr "" + +#: src/GMImportDialog.cpp:212 ../../../fox-1.6.37/src/FXFileSelector.cpp:253 +msgid "\tShow details\tDisplay detailed directory listing." +msgstr "" + +#: src/GMImportDialog.cpp:213 ../../../fox-1.6.37/src/FXFileSelector.cpp:254 +msgid "\tShow hidden files\tShow hidden files and directories." +msgstr "" + +#: src/GMImportDialog.cpp:213 ../../../fox-1.6.37/src/FXFileSelector.cpp:254 +msgid "\tHide Hidden Files\tHide hidden files and directories." +msgstr "" + +#: src/GMImportDialog.cpp:412 +msgid "Synchronize Folder" +msgstr "" + +#: src/GMImportDialog.cpp:414 +msgid "Import Playlist" +msgstr "" + +#: src/GMImportDialog.cpp:416 +msgid "Import Music" +msgstr "" + +#: src/GMImportDialog.cpp:448 +msgid "&File(s)" +msgstr "" + +#: src/GMImportDialog.cpp:475 +msgid "&Directory" +msgstr "" + +#: src/GMImportDialog.cpp:478 +msgid "Exclude Filter\tFilter out directories and/or files based on pattern" +msgstr "" + +#: src/GMImportDialog.cpp:481 +msgid "Folders:" +msgstr "" + +#: src/GMImportDialog.cpp:483 +msgid "Files:" +msgstr "" + +#: src/GMImportDialog.cpp:499 src/GMImportDialog.cpp:560 +msgid "&Sync" +msgstr "" + +#: src/GMImportDialog.cpp:501 +msgid "Sync Operation" +msgstr "" + +#: src/GMImportDialog.cpp:504 +msgid "Import new tracks\tImports files not yet in the database." +msgstr "" + +#: src/GMImportDialog.cpp:505 +msgid "Remove tracks that have been deleted from disk" +msgstr "" + +#: src/GMImportDialog.cpp:507 +msgid "Update existing tracks:" +msgstr "" + +#: src/GMImportDialog.cpp:508 +msgid "" +"Modified since last import\tOnly reread the tag when the file has been " +"modified." +msgstr "" + +#: src/GMImportDialog.cpp:510 +msgid "All\tAlways read the tags" +msgstr "" + +#: src/GMImportDialog.cpp:511 +msgid "Remove tracks found in folder from database" +msgstr "" + +#: src/GMImportDialog.cpp:514 +msgid "&Track" +msgstr "" + +#: src/GMImportDialog.cpp:517 +msgid "Parse Settings" +msgstr "" + +#: src/GMImportDialog.cpp:521 +msgid "Parse info from:" +msgstr "" + +#: src/GMImportDialog.cpp:524 +msgid "Tag" +msgstr "" + +#: src/GMImportDialog.cpp:526 +msgid "Both" +msgstr "" + +#: src/GMImportDialog.cpp:528 +msgid "Default value:" +msgstr "" + +#: src/GMImportDialog.cpp:532 +msgid "Set track number based on scan order." +msgstr "" + +#: src/GMImportDialog.cpp:537 +msgid "" +"%T - title %A - album name\n" +"%P - album artist name %p - track artist name \n" +"%N - track number %G - genre" +msgstr "" + +#: src/GMImportDialog.cpp:549 +msgid "Replace underscores with spaces" +msgstr "" + +#: src/GMImportDialog.cpp:562 +msgid "&Import" +msgstr "" + +#: src/GMPlayer.cpp:212 +msgid "Unable to initialize audio driver." +msgstr "" + +#: src/GMPlayer.cpp:714 +msgid "Unknown host." +msgstr "" + +#: src/GMPlayer.cpp:715 +msgid "Unknown device" +msgstr "" + +#: src/GMPlayer.cpp:716 +msgid "Network not reachable." +msgstr "" + +#: src/GMPlayer.cpp:717 +msgid "Audio output unavailable." +msgstr "" + +#: src/GMPlayer.cpp:718 +msgid "Connection Refused." +msgstr "" + +#: src/GMPlayer.cpp:719 +msgid "File not found." +msgstr "" + +#: src/GMPlayer.cpp:720 +msgid "Resource not accessible. Check permissions" +msgstr "" + +#: src/GMPlayer.cpp:721 +msgid "Read Error" +msgstr "" + +#: src/GMPlayer.cpp:722 +msgid "Error while loading library/plugin" +msgstr "" + +#: src/GMPlayer.cpp:723 +msgid "Warning" +msgstr "" + +#: src/GMPlayer.cpp:724 +msgid "Security Warning" +msgstr "" + +#: src/GMPlayer.cpp:725 +msgid "Unknown Error" +msgstr "" + +#: src/GMPlayer.cpp:761 +msgid "Error" +msgstr "" + +#: src/GMPlayerManager.cpp:439 +#, c-format +msgid "Unable to create directory %s\n" +msgstr "" + +#: src/GMPlayerManager.cpp:612 +msgid "" +"For some reason the FOX library was compiled without PNG support.\n" +"In order to show all icons, Goggles Music Manager requires PNG\n" +"support in the FOX library. If you've compiled FOX yourself, most\n" +"likely the libpng header files were not installed on your system." +msgstr "" + +#: src/GMPlayerManager.cpp:623 +msgid "Session bus not available. All features requiring dbus are disabled." +msgstr "" + +#: src/GMPlayerManager.cpp:633 +msgid "A DBus error occurred. All features requiring sessionbus are disabled." +msgstr "" + +#: src/GMPlayerManager.cpp:644 +msgid "" +"Session Bus not available. All features requiring sessionbus are disabled." +msgstr "" + +#: src/GMPlayerManager.cpp:719 src/GMPreferencesDialog.cpp:594 +msgid "Audio Device Error" +msgstr "" + +#: src/GMPlayerManager.cpp:1551 +msgid "Last.FM Error" +msgstr "" + +#: src/GMPlayerManager.cpp:1558 +msgid "Playback Error" +msgstr "" + +#: src/GMPlayerManager.cpp:1708 +#, c-format +msgid "" +"%s\n" +"%s (%d)" +msgstr "" + +#: src/GMPlayListSource.cpp:99 src/GMPlayListSource.cpp:105 +#: src/GMPlayListSource.cpp:111 src/GMPlayListSource.cpp:121 +msgid "Remove…\tDel\tRemove track(s) from play list." +msgstr "" + +#: src/GMPlayListSource.cpp:161 +msgid "Edit…" +msgstr "" + +#: src/GMPlayListSource.cpp:162 +msgid "Import…" +msgstr "" + +#: src/GMPlayListSource.cpp:164 +msgid "Remove Playlist" +msgstr "" + +#: src/GMPlayListSource.cpp:262 +msgid "Remove tracks with genre from play list?" +msgstr "" + +#: src/GMPlayListSource.cpp:265 +msgid "Remove tracks from artist from play list?" +msgstr "" + +#: src/GMPlayListSource.cpp:268 +msgid "Remove tracks from album from play list?" +msgstr "" + +#: src/GMPlayListSource.cpp:271 +msgid "Remove track(s) from play list?" +msgstr "" + +#: src/GMPlayListSource.cpp:284 +msgid "Remove tracks from music library" +msgstr "" + +#: src/GMPlayListSource.cpp:406 src/GMPlayListSource.cpp:407 +msgid "Edit Playlist" +msgstr "" + +#: src/GMPlayListSource.cpp:407 +msgid "Change playlist name" +msgstr "" + +#: src/GMPlayListSource.cpp:432 +msgid "Delete Play List?" +msgstr "" + +#: src/GMPlayListSource.cpp:432 +msgid "Are you sure you want to delete the playlist?" +msgstr "" + +#: src/GMPlayListSource.cpp:432 ../../../fox-1.6.37/src/FXMessageBox.cpp:111 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:116 +msgid "&Yes" +msgstr "" + +#: src/GMPlayListSource.cpp:432 ../../../fox-1.6.37/src/FXMessageBox.cpp:112 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:117 +msgid "&No" +msgstr "" + +#: src/GMPreferencesDialog.cpp:252 +msgid "Preferences" +msgstr "" + +#: src/GMPreferencesDialog.cpp:286 +msgid "&General" +msgstr "" + +#: src/GMPreferencesDialog.cpp:289 +msgid "Sort Options" +msgstr "" + +#: src/GMPreferencesDialog.cpp:293 +msgid "Ignore leading words" +msgstr "" + +#: src/GMPreferencesDialog.cpp:296 +msgid "Album Covers" +msgstr "" + +#: src/GMPreferencesDialog.cpp:299 +msgid "Show album cover of playing track\tShow album cover of playing track" +msgstr "" + +#: src/GMPreferencesDialog.cpp:300 +msgid "Show album covers in album browser\tShow album covers in album browser" +msgstr "" + +#: src/GMPreferencesDialog.cpp:302 +msgid "System Tray" +msgstr "" + +#: src/GMPreferencesDialog.cpp:304 +msgid "Show Tray Icon\tShow tray icon in the system tray." +msgstr "" + +#: src/GMPreferencesDialog.cpp:307 +msgid "" +"Show Track Change Notifications\tInform notification daemon of track changes." +msgstr "" + +#: src/GMPreferencesDialog.cpp:311 +msgid "Last.fm" +msgstr "" + +#: src/GMPreferencesDialog.cpp:315 +msgid "" +"This version of Goggles Music Manager is\n" +"not supported by Last-FM. Please upgrade\n" +"to a newer version of GMM." +msgstr "" + +#: src/GMPreferencesDialog.cpp:321 +msgid "Service:" +msgstr "" + +#: src/GMPreferencesDialog.cpp:338 +msgid "&Sign up…" +msgstr "" + +#: src/GMPreferencesDialog.cpp:344 +msgid "Username:" +msgstr "" + +#: src/GMPreferencesDialog.cpp:346 +msgid "Password:" +msgstr "" + +#: src/GMPreferencesDialog.cpp:349 +msgid "Scrobble" +msgstr "" + +#: src/GMPreferencesDialog.cpp:359 +msgid "&Window" +msgstr "" + +#: src/GMPreferencesDialog.cpp:362 +msgid "Window" +msgstr "" + +#: src/GMPreferencesDialog.cpp:365 +msgid "Close button minimizes to tray" +msgstr "" + +#: src/GMPreferencesDialog.cpp:366 +msgid "Show Status Bar" +msgstr "" + +#: src/GMPreferencesDialog.cpp:368 +msgid "Show Icons in Track Browser" +msgstr "" + +#: src/GMPreferencesDialog.cpp:370 +msgid "Display playing track in title bar" +msgstr "" + +#: src/GMPreferencesDialog.cpp:372 +msgid "Player Controls" +msgstr "" + +#: src/GMPreferencesDialog.cpp:376 +msgid "Location:" +msgstr "" + +#: src/GMPreferencesDialog.cpp:378 +msgid "Top" +msgstr "" + +#: src/GMPreferencesDialog.cpp:379 +msgid "Bottom" +msgstr "" + +#: src/GMPreferencesDialog.cpp:387 +msgid "Title Format:" +msgstr "" + +#: src/GMPreferencesDialog.cpp:391 +msgid "Style:" +msgstr "" + +#: src/GMPreferencesDialog.cpp:392 +msgid "Show Labels" +msgstr "" + +#: src/GMPreferencesDialog.cpp:395 +msgid "Large Icons" +msgstr "" + +#: src/GMPreferencesDialog.cpp:399 +msgid "A&ppearance" +msgstr "" + +#: src/GMPreferencesDialog.cpp:402 +msgid "Colors" +msgstr "" + +#: src/GMPreferencesDialog.cpp:409 +msgid "fg\tForeground Color" +msgstr "" + +#: src/GMPreferencesDialog.cpp:410 +msgid "bg\tBackground Color" +msgstr "" + +#: src/GMPreferencesDialog.cpp:411 +msgid "alt bg\tAlternative Background Color" +msgstr "" + +#: src/GMPreferencesDialog.cpp:422 +msgid "Normal\tNormal Text Color" +msgstr "" + +#: src/GMPreferencesDialog.cpp:426 +msgid "Base\tBase Color" +msgstr "" + +#: src/GMPreferencesDialog.cpp:429 +msgid "Selected\tSelected Text Color" +msgstr "" + +#: src/GMPreferencesDialog.cpp:433 +msgid "Menu\tMenu Base Color" +msgstr "" + +#: src/GMPreferencesDialog.cpp:437 +msgid "Menu\tMenu Text Color" +msgstr "" + +#: src/GMPreferencesDialog.cpp:441 +msgid "Border\tBorder Color" +msgstr "" + +#: src/GMPreferencesDialog.cpp:445 +msgid "Tooltip\tTooltip Color" +msgstr "" + +#: src/GMPreferencesDialog.cpp:449 +msgid "Hilite\tHilite Color" +msgstr "" + +#: src/GMPreferencesDialog.cpp:456 +msgid "Shadow\tShadow Color" +msgstr "" + +#: src/GMPreferencesDialog.cpp:459 +msgid "Playing\tPlaying Track Color" +msgstr "" + +#: src/GMPreferencesDialog.cpp:463 +msgid "Tray\tTray Background Color" +msgstr "" + +#: src/GMPreferencesDialog.cpp:473 +msgid "Presets:" +msgstr "" + +#: src/GMPreferencesDialog.cpp:481 +msgid "Font & Icons" +msgstr "" + +#: src/GMPreferencesDialog.cpp:487 +msgid "Default Font" +msgstr "" + +#: src/GMPreferencesDialog.cpp:489 +msgid "Change…" +msgstr "" + +#: src/GMPreferencesDialog.cpp:490 +msgid "Icons" +msgstr "" + +#: src/GMPreferencesDialog.cpp:509 +msgid "&Audio" +msgstr "" + +#: src/GMPreferencesDialog.cpp:512 +msgid "Engine" +msgstr "" + +#: src/GMPreferencesDialog.cpp:516 +msgid "Audio Driver:" +msgstr "" + +#: src/GMPreferencesDialog.cpp:527 +msgid "Close audio device on pause." +msgstr "" + +#: src/GMPreferencesDialog.cpp:528 +msgid "Turn off playback engine on stop." +msgstr "" + +#: src/GMPreferencesDialog.cpp:529 +msgid "" +"Turn on playback engine on startup.\tFor faster startup, playback engine is " +"normally started when first track is played.\tFor faster startup, playback " +"engine is normally started when first track is played." +msgstr "" + +#: src/GMPreferencesDialog.cpp:532 +msgid "Playback" +msgstr "" + +#: src/GMPreferencesDialog.cpp:536 +msgid "Replay Gain:" +msgstr "" + +#: src/GMPreferencesDialog.cpp:538 +msgid "Off" +msgstr "" + +#: src/GMPreferencesDialog.cpp:543 +msgid "Gapless playback" +msgstr "" + +#: src/GMPreferencesDialog.cpp:544 +msgid "Volume Normalization" +msgstr "" + +#: src/GMPreferencesDialog.cpp:594 +#, c-format +msgid "Failed to open requested audio driver: %s" +msgstr "" + +#: src/GMPreferencesDialog.cpp:678 +msgid "Current" +msgstr "" + +#: src/GMPreferencesDialog.cpp:696 +msgid "Custom" +msgstr "" + +#: src/GMPreferencesDialog.cpp:769 src/GMPreferencesDialog.cpp:774 +#: src/GMWindow.cpp:1133 src/GMWindow.cpp:1140 src/GMWindow.cpp:1147 +#: src/GMWindow.cpp:1154 +msgid "Unable to launch webbrowser" +msgstr "" + +#: src/GMPreferencesDialog.cpp:1172 +msgid "Select Normal Font" +msgstr "" + +#: src/GMRemote.cpp:295 +msgid "&Next" +msgstr "" + +#: src/GMRemote.cpp:296 +msgid "P&revious" +msgstr "" + +#: src/GMRemote.cpp:297 src/GMWindow.cpp:1197 +msgid "&Play" +msgstr "" + +#: src/GMRemote.cpp:298 +msgid "&Stop" +msgstr "" + +#: src/GMRemote.cpp:300 +msgid "Show Browser" +msgstr "" + +#: src/GMRemote.cpp:301 src/GMTrayIcon.cpp:307 +msgid "Quit" +msgstr "" + +#: src/GMRemote.cpp:385 +msgid "\tShow Browser\tShow Browser" +msgstr "" + +#: src/GMRemote.cpp:387 +msgid "\tStart Playback\tStart Playback" +msgstr "" + +#: src/GMRemote.cpp:388 +msgid "\tStop Playback\tStop Playback" +msgstr "" + +#: src/GMRemote.cpp:390 +msgid "\tPlay Previous Track\tPlay previous track." +msgstr "" + +#: src/GMRemote.cpp:391 +msgid "\tPlay Next Track\tPlay next track." +msgstr "" + +#: src/GMRemote.cpp:397 src/GMWindow.cpp:222 +msgid "\tAdjust Volume\tAdjust Volume" +msgstr "" + +#: src/GMSearch.cpp:128 +msgid "Unable to open the database" +msgstr "" + +#: src/GMSearch.cpp:252 +msgid "Database Error: Unable to retrieve all filenames." +msgstr "" + +#: src/GMSearch.cpp:280 +msgid "Unable to update track" +msgstr "" + +#: src/GMSearch.cpp:439 +msgid "Unable to insert track into the database" +msgstr "" + +#: src/GMSearch.cpp:505 src/GMTrackDatabase.cpp:205 +msgid "Fatal Error" +msgstr "" + +#: src/GMSearch.cpp:505 +#, c-format +msgid "" +"%s\n" +"Please contact support if this error keeps occuring." +msgstr "" + +#: src/GMSearch.cpp:534 src/GMSearch.cpp:584 +msgid "Updating Database..." +msgstr "" + +#: src/GMSearch.cpp:537 src/GMSearch.cpp:626 +msgid "Please wait. This may take a while." +msgstr "" + +#: src/GMSearch.cpp:555 src/GMSearch.cpp:637 +msgid "New Tracks:" +msgstr "" + +#: src/GMSearch.cpp:561 src/GMSearch.cpp:643 +msgid "File:" +msgstr "" + +#: src/GMSearch.cpp:594 src/GMSearch.cpp:623 +msgid "Importing Files..." +msgstr "" + +#: src/GMSearch.cpp:652 +msgid "&Stop Import" +msgstr "" + +#: src/GMSourceView.cpp:54 +msgid "Sources\tPress to change sorting order\tPress to change sorting order" +msgstr "" + +#: src/GMSourceView.cpp:245 src/GMWindow.cpp:261 +msgid "New Playlist…\t\tCreate a new playlist" +msgstr "" + +#: src/GMSourceView.cpp:246 src/GMWindow.cpp:262 +msgid "Import Playlist…\t\tImport existing playlist" +msgstr "" + +#: src/GMSourceView.cpp:247 src/GMWindow.cpp:263 +msgid "New Radio Station…\t\tCreate a new playlist" +msgstr "" + +#: src/GMStreamSource.cpp:64 +msgid "Station" +msgstr "" + +#: src/GMStreamSource.cpp:112 src/GMStreamSource.cpp:118 +msgid "New Station…\t\t" +msgstr "" + +#: src/GMStreamSource.cpp:117 +msgid "Edit…\t\t" +msgstr "" + +#: src/GMStreamSource.cpp:119 +msgid "Remove\t\tRemove." +msgstr "" + +#: src/GMStreamSource.cpp:165 src/GMStreamSource.cpp:166 +msgid "New Internet Radio Station" +msgstr "" + +#: src/GMStreamSource.cpp:166 +msgid "Specify url and description of new station" +msgstr "" + +#: src/GMStreamSource.cpp:168 +msgid "C&reate" +msgstr "" + +#: src/GMStreamSource.cpp:173 src/GMStreamSource.cpp:211 +msgid "Location" +msgstr "" + +#: src/GMStreamSource.cpp:175 src/GMStreamSource.cpp:214 +msgid "Description" +msgstr "" + +#: src/GMStreamSource.cpp:189 +msgid "Untitled" +msgstr "" + +#: src/GMStreamSource.cpp:202 src/GMStreamSource.cpp:203 +msgid "Edit Internet Radio Station" +msgstr "" + +#: src/GMStreamSource.cpp:203 +msgid "Update url and description of station" +msgstr "" + +#: src/GMStreamSource.cpp:265 +msgid "Remove Internet Radio Station(s)?" +msgstr "" + +#: src/GMStreamSource.cpp:266 +msgid "Remove Internet Radio Station(s) from library?" +msgstr "" + +#: src/GMStreamSource.cpp:280 +#, c-format +msgid "Unable to remove station from the library." +msgstr "" + +#: src/GMTrackDatabase.cpp:193 +msgid "" +"An incompatible (future) version of the database was found.\n" +"This usually happens when you try to downgrade to a older version of GMM\n" +"Press OK to continue and reset the database (all information will be " +"lost!).\n" +"Press Cancel to quit now and leave the database as is." +msgstr "" + +#: src/GMTrackDatabase.cpp:205 +msgid "" +"Goggles Music Manager was unable to open the database.\n" +"The database may have been corrupted. Please remove ~/.goggles/goggles.db to " +"try again.\n" +"if the error keeps occuring, please file an issue at http://code.google.com/" +"p/gogglesmm" +msgstr "" + +#: src/GMTrackView.cpp:245 +msgid "\tClose Filter\tClose Filter" +msgstr "" + +#: src/GMTrackView.cpp:246 +msgid "&Find" +msgstr "" + +#: src/GMTrackView.cpp:252 +msgid "Tags\tPress to change sorting order\tPress to change sorting order" +msgstr "" + +#: src/GMTrackView.cpp:256 +msgid "Artists\tPress to change sorting order\tPress to change sorting order" +msgstr "" + +#: src/GMTrackView.cpp:260 +msgid "Albums\tPress to change sorting order\tPress to change sorting order" +msgstr "" + +#: src/GMTrackView.cpp:282 src/GMWindow.cpp:299 +msgid "&Configure Columns…" +msgstr "" + +#: src/GMTrackView.cpp:286 +msgid "Browse" +msgstr "" + +#: src/GMTrackView.cpp:287 +msgid "Shuffle\tCtrl-R" +msgstr "" + +#: src/GMTrackView.cpp:294 ../../../fox-1.6.37/src/FXDirSelector.cpp:388 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:734 +msgid "Reverse" +msgstr "" + +#: src/GMTrackView.cpp:804 +#, c-format +msgid "All %d Genres" +msgstr "" + +#: src/GMTrackView.cpp:806 +msgid "All Genres" +msgstr "" + +#: src/GMTrackView.cpp:832 +#, c-format +msgid "All %d Artists" +msgstr "" + +#: src/GMTrackView.cpp:872 +#, c-format +msgid "All %d Albums" +msgstr "" + +#: src/GMTrackView.cpp:1258 +#, c-format +msgid "By %s" +msgstr "" + +#: src/GMTrackView.cpp:1586 src/GMTrackView.cpp:1609 +msgid "Sort by Album Year" +msgstr "" + +#: src/GMTrackView.cpp:1637 +msgid "&Columns\t\tChange Visible Columns." +msgstr "" + +#: src/GMTrackView.cpp:1638 +msgid "&Sort\t\tChange Sorting." +msgstr "" + +#: src/GMTrayIcon.cpp:302 src/GMWindow.cpp:841 src/GMWindow.cpp:925 +#: src/GMWindow.cpp:948 src/GMWindow.cpp:954 +msgid "Play" +msgstr "" + +#: src/GMTrayIcon.cpp:303 src/GMWindow.cpp:843 +msgid "Stop" +msgstr "" + +#: src/GMTrayIcon.cpp:304 +msgid "Previous Track" +msgstr "" + +#: src/GMTrayIcon.cpp:305 +msgid "Next Track" +msgstr "" + +#: src/GMWindow.cpp:205 +msgid "Play\tStart Playback\tStart Playback" +msgstr "" + +#: src/GMWindow.cpp:205 +msgid "Pause\tPause\tPause Playback" +msgstr "" + +#: src/GMWindow.cpp:206 +msgid "Stop\tStop Playback\tStop Playback" +msgstr "" + +#: src/GMWindow.cpp:208 +msgid "Previous\tPlay Previous Track\tPlay previous track." +msgstr "" + +#: src/GMWindow.cpp:209 +msgid "Next\tPlay Next Track\tPlay next track." +msgstr "" + +#: src/GMWindow.cpp:255 +msgid "&Music" +msgstr "" + +#: src/GMWindow.cpp:256 +msgid "Import Folder…\tCtrl-O\tImport Music from folder into Library" +msgstr "" + +#: src/GMWindow.cpp:257 +msgid "Sync Folder…\t\tSynchronize Folder with Music in Library" +msgstr "" + +#: src/GMWindow.cpp:259 +msgid "Play File or Stream…\t\tPlay File or Stream" +msgstr "" + +#: src/GMWindow.cpp:266 +msgid "&Quit\tCtrl-Q\tQuit the application." +msgstr "" + +#: src/GMWindow.cpp:271 +msgid "&Edit" +msgstr "" + +#: src/GMWindow.cpp:272 +msgid "&Copy\tCtrl-C\tCopy Selected Tracks" +msgstr "" + +#: src/GMWindow.cpp:273 +msgid "&Cut\tCtrl-X\tCut Selected Tracks" +msgstr "" + +#: src/GMWindow.cpp:274 +msgid "&Paste\tCtrl-V\tPaste Clipboard Selection" +msgstr "" + +#: src/GMWindow.cpp:276 +msgid "Find…\tCtrl-F\tShow search filter." +msgstr "" + +#: src/GMWindow.cpp:278 +msgid "Preferences…" +msgstr "" + +#: src/GMWindow.cpp:282 +msgid "&View" +msgstr "" + +#: src/GMWindow.cpp:283 +msgid "&Browse\tCtrl-B\tShow genre artist and album browser." +msgstr "" + +#: src/GMWindow.cpp:284 +msgid "Show &Genres\tCtrl-G\tShow genre browser." +msgstr "" + +#: src/GMWindow.cpp:287 src/GMWindow.cpp:289 +msgid "Show &Sources\tCtrl-S\tShow source browser " +msgstr "" + +#: src/GMWindow.cpp:294 +msgid "Show Full Screen\tF12\tToggle fullscreen mode." +msgstr "" + +#: src/GMWindow.cpp:296 +msgid "Show Mini Player\tCtrl-M\tToggle Mini Player." +msgstr "" + +#: src/GMWindow.cpp:300 +msgid "&Sort" +msgstr "" + +#: src/GMWindow.cpp:302 +msgid "&Jump to Current Track\tCtrl-J\tShow current playing track." +msgstr "" + +#: src/GMWindow.cpp:306 +msgid "&Control" +msgstr "" + +#: src/GMWindow.cpp:307 +msgid "Play\tCtrl-P\tStart playback." +msgstr "" + +#: src/GMWindow.cpp:308 +msgid "Stop\tCtrl-\\\tStop playback." +msgstr "" + +#: src/GMWindow.cpp:309 +msgid "Previous Track\tCtrl-[\tPlay next track." +msgstr "" + +#: src/GMWindow.cpp:310 +msgid "Next Track\tCtrl-]\tPlay previous track." +msgstr "" + +#: src/GMWindow.cpp:312 +msgid "Repeat Off\tCtrl-,\tRepeat current track." +msgstr "" + +#: src/GMWindow.cpp:313 +msgid "Repeat Track\tCtrl-.\tRepeat current track." +msgstr "" + +#: src/GMWindow.cpp:314 +msgid "Repeat All Tracks\tCtrl-/\tRepeat all tracks." +msgstr "" + +#: src/GMWindow.cpp:315 +msgid "Repeat A-B\tCtrl-T\tRepeat section of track." +msgstr "" + +#: src/GMWindow.cpp:316 +msgid "Shuffle Mode\tAlt-R\tPlay tracks in random order." +msgstr "" + +#: src/GMWindow.cpp:318 +msgid "Equalizer\t\t" +msgstr "" + +#: src/GMWindow.cpp:319 +msgid "Sleep Timer\t\tSetup sleeptimer." +msgstr "" + +#: src/GMWindow.cpp:323 +msgid "&Help" +msgstr "" + +#: src/GMWindow.cpp:324 +msgid "&Homepage" +msgstr "" + +#: src/GMWindow.cpp:325 +msgid "&Report Issue…" +msgstr "" + +#: src/GMWindow.cpp:327 +msgid "&Sign up for last.fm…" +msgstr "" + +#: src/GMWindow.cpp:328 +msgid "" +"&Join GMM on last.fm…\t\tJoin the Goggles Music Manager group on last.fm…" +msgstr "" + +#: src/GMWindow.cpp:330 +msgid "&About…" +msgstr "" + +#: src/GMWindow.cpp:842 src/GMWindow.cpp:940 +msgid "Pause" +msgstr "" + +#: src/GMWindow.cpp:844 +msgid "Previous" +msgstr "" + +#: src/GMWindow.cpp:845 +msgid "Next" +msgstr "" + +#: src/GMWindow.cpp:920 src/GMWindow.cpp:949 src/GMWindow.cpp:955 +msgid "Start playback." +msgstr "" + +#: src/GMWindow.cpp:926 src/GMWindow.cpp:927 +msgid "Start playback" +msgstr "" + +#: src/GMWindow.cpp:934 src/GMWindow.cpp:941 +msgid "Pause playback." +msgstr "" + +#: src/GMWindow.cpp:935 +msgid "Pause playback" +msgstr "" + +#: src/GMWindow.cpp:1052 src/GMWindow.cpp:1054 +msgid "Repeat A-B" +msgstr "" + +#: src/GMWindow.cpp:1053 +msgid "Repeat A" +msgstr "" + +#: src/GMWindow.cpp:1195 +msgid "Play File or Stream" +msgstr "" + +#: src/GMWindow.cpp:1202 +msgid "Please specify a file or url to play:" +msgstr "" + +#: src/GMWindow.cpp:1206 +msgid "…" +msgstr "" + +#: src/GMWindow.cpp:1212 +msgid "Select File" +msgstr "" + +#: src/GMWindow.cpp:1243 +msgid "Sleep Timer" +msgstr "" + +#: src/GMWindow.cpp:1244 +msgid "Setup sleep timer" +msgstr "" + +#: src/GMWindow.cpp:1244 +msgid "Stop playback within a certain time" +msgstr "" + +#: src/GMWindow.cpp:1246 +msgid "&Start Timer" +msgstr "" + +#: src/GMWindow.cpp:1251 +msgid "Sleep in" +msgstr "" + +#: src/GMWindow.cpp:1253 +msgid "hours and" +msgstr "" + +#: src/GMWindow.cpp:1255 +msgid "minutes." +msgstr "" + +#: src/GMDatabaseSource.h:131 +msgid "Music Library" +msgstr "" + +#: src/GMStreamSource.h:57 +msgid "Internet Radio" +msgstr "" + +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:122 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:127 +msgid "&Quit" +msgstr "" + +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:133 +msgid "&Skip" +msgstr "" + +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:134 +msgid "Skip &All" +msgstr "" + +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:140 +msgid "&Don't Save" +msgstr "" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:220 +msgid "\tPick color" +msgstr "" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:229 +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:274 +msgid "\tHue, Saturation, Value" +msgstr "" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:239 +msgid "\tRed, Green, Blue" +msgstr "" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:245 +msgid "&Red:" +msgstr "" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:250 +msgid "&Green:" +msgstr "" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:255 +msgid "&Blue:" +msgstr "" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:260 +msgid "&Alpha:" +msgstr "" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:280 +msgid "Hue:" +msgstr "" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:285 +msgid "Saturation:" +msgstr "" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:290 +msgid "Value:" +msgstr "" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:295 +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:330 +msgid "Alpha:" +msgstr "" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:309 +msgid "\tCyan, Magenta, Yellow" +msgstr "" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:315 +msgid "Cyan:" +msgstr "" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:320 +msgid "Magenta:" +msgstr "" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:325 +msgid "Yellow:" +msgstr "" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:344 +msgid "\tBy Name" +msgstr "" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:281 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:521 +msgid "Create New Directory" +msgstr "" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:284 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:524 +msgid "Already Exists" +msgstr "" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:288 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:528 +msgid "Cannot Create" +msgstr "" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:309 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:595 +msgid "Copy File" +msgstr "" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:315 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:601 +msgid "Error Copying File" +msgstr "" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:326 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:618 +msgid "Move File" +msgstr "" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:332 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:624 +msgid "Error Moving File" +msgstr "" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:343 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:641 +msgid "Link File" +msgstr "" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:349 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:647 +msgid "Error Linking File" +msgstr "" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:359 +msgid "Deleting file" +msgstr "" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:361 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:667 +msgid "Error Deleting File" +msgstr "" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:381 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:719 +msgid "Up one level" +msgstr "" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:382 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:720 +msgid "Home directory" +msgstr "" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:383 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:721 +msgid "Work directory" +msgstr "" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:387 +msgid "Sorting" +msgstr "" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:389 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:735 +msgid "Ignore case" +msgstr "" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:390 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:746 +msgid "Hidden files" +msgstr "" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:393 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:754 +msgid "Bookmarks" +msgstr "" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:394 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:757 +msgid "Set bookmark" +msgstr "" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:395 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:758 +msgid "Clear bookmarks" +msgstr "" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:411 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:774 +msgid "New directory..." +msgstr "" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:412 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:775 +msgid "Copy..." +msgstr "" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:413 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:776 +msgid "Move..." +msgstr "" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:414 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:777 +msgid "Link..." +msgstr "" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:415 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:778 +msgid "Delete..." +msgstr "" + +#: ../../../fox-1.6.37/src/FXFileList.cpp:195 +msgid "Modified Date" +msgstr "" + +#: ../../../fox-1.6.37/src/FXFileList.cpp:196 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:731 +msgid "User" +msgstr "" + +#: ../../../fox-1.6.37/src/FXFileList.cpp:197 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:732 +msgid "Group" +msgstr "" + +#: ../../../fox-1.6.37/src/FXFileList.cpp:198 +msgid "Attributes" +msgstr "" + +#: ../../../fox-1.6.37/src/FXFileList.cpp:200 +msgid "Link" +msgstr "" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:521 +msgid "Create new directory with name: " +msgstr "" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:524 +#, c-format +msgid "File or directory %s already exists.\n" +msgstr "" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:528 +#, c-format +msgid "Cannot create directory %s.\n" +msgstr "" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:594 +#, c-format +msgid "" +"Copy file from location:\n" +"\n" +"%s\n" +"\n" +"to location: " +msgstr "" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:601 +#, c-format +msgid "" +"Unable to copy file:\n" +"\n" +"%s to: %s\n" +"\n" +"Continue with operation?" +msgstr "" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:617 +#, c-format +msgid "" +"Move file from location:\n" +"\n" +"%s\n" +"\n" +"to location: " +msgstr "" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:624 +#, c-format +msgid "" +"Unable to move file:\n" +"\n" +"%s to: %s\n" +"\n" +"Continue with operation?" +msgstr "" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:640 +#, c-format +msgid "" +"Link file from location:\n" +"\n" +"%s\n" +"\n" +"to location: " +msgstr "" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:647 +#, c-format +msgid "" +"Unable to link file:\n" +"\n" +"%s to: %s\n" +"\n" +"Continue with operation?" +msgstr "" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:663 +msgid "Deleting files" +msgstr "" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:663 +#, c-format +msgid "" +"Are you sure you want to delete the file:\n" +"\n" +"%s" +msgstr "" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:667 +#, c-format +msgid "" +"Unable to delete file:\n" +"\n" +"%s\n" +"\n" +"Continue with operation?" +msgstr "" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:722 +msgid "Select all" +msgstr "" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:726 +msgid "Sort by" +msgstr "" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:738 +msgid "View" +msgstr "" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:739 +msgid "Small icons" +msgstr "" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:740 +msgid "Big icons" +msgstr "" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:741 +msgid "Details" +msgstr "" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:743 +msgid "Rows" +msgstr "" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:744 +msgid "Columns" +msgstr "" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:747 +msgid "Preview images" +msgstr "" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:749 +msgid "Normal images" +msgstr "" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:750 +msgid "Medium images" +msgstr "" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:751 +msgid "Giant images" +msgstr "" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:111 +msgid "&Replace" +msgstr "" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:112 +msgid "Re&place All" +msgstr "" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:121 +msgid "S&earch for:" +msgstr "" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:129 +msgid "Replace &with:" +msgstr "" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:138 +msgid "Ex&act" +msgstr "" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:139 +msgid "&Ignore Case" +msgstr "" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:140 +msgid "E&xpression" +msgstr "" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:141 +msgid "&Backward" +msgstr "" + +#: ../../../fox-1.6.37/src/FXSearchDialog.cpp:71 +msgid "&Search" +msgstr "" + +#: ../../../fox-1.6.37/src/FXStatusLine.cpp:82 +msgid "Ready." +msgstr "" diff --git a/po/hu.mo b/po/hu.mo new file mode 100644 index 0000000000000000000000000000000000000000..698717f38b29ce73cf24c86e55d18661c504c1e1 GIT binary patch literal 39312 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 GDEwcePk8JA literal 0 HcmV?d00001 diff --git a/po/hu.po b/po/hu.po new file mode 100644 index 0000000..2018b03 --- /dev/null +++ b/po/hu.po @@ -0,0 +1,2610 @@ +# Copyright (C) YEAR Sander Jansen +# This file is distributed under the same license as the gogglesmm package. +# +# Sándor Sipos , 2010. +msgid "" +msgstr "" +"Project-Id-Version: hu\n" +"Report-Msgid-Bugs-To: s.jansen@gmail.com\n" +"POT-Creation-Date: 2011-02-09 23:26-0600\n" +"PO-Revision-Date: 2010-08-30 11:52+0200\n" +"Last-Translator: Sipos Sándor \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: KBabel 1.11.4\n" + +#: src/GMAbout.cpp:136 src/GMDatabaseSource.cpp:218 +#: src/GMDatabaseSource.cpp:2215 src/GMPreferencesDialog.cpp:551 +msgid "&Close" +msgstr "&Bezár" + +#: src/GMColumnDialog.cpp:204 +msgid "Configure Columns" +msgstr "Oszlopok beállítása" + +#: src/GMColumnDialog.cpp:207 ../../../fox-1.6.37/src/FXColorSelector.cpp:168 +msgid "&Accept" +msgstr "&Ok" + +#: src/GMColumnDialog.cpp:208 src/GMDatabaseSource.cpp:98 +#: src/GMDatabaseSource.cpp:1274 src/GMDatabaseSource.cpp:1704 +#: src/GMDatabaseSource.cpp:1804 src/GMDatabaseSource.cpp:1878 +#: src/GMDatabaseSource.cpp:2121 src/GMDatabaseSource.cpp:2182 +#: src/GMImportDialog.cpp:175 src/GMImportDialog.cpp:563 +#: src/GMPlayListSource.cpp:281 src/GMPlayListSource.cpp:410 +#: src/GMSearch.cpp:572 src/GMStreamSource.cpp:169 src/GMStreamSource.cpp:206 +#: src/GMStreamSource.cpp:271 src/GMWindow.cpp:1198 src/GMWindow.cpp:1247 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:107 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:118 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:123 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:129 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:135 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:142 +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:169 +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:135 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:205 +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:113 +msgid "&Cancel" +msgstr "&Mégsem" + +#: src/GMColumnDialog.cpp:212 +msgid "" +"Choose the order of information to appear\n" +"in the track list." +msgstr "" +"Válassza ki a zeneszámok listájának \n" +"rendezési sorrendjét." + +#: src/GMColumnDialog.cpp:218 +msgid "Move Up" +msgstr "Felfelé mozgat" + +#: src/GMColumnDialog.cpp:219 +msgid "Move Down" +msgstr "Lefelé mozgat" + +#: src/GMDatabaseSource.cpp:52 +msgid "Invalid Template" +msgstr "Rossz sablon" + +#: src/GMDatabaseSource.cpp:52 +#, c-format +msgid "" +"The provided template is invalid. The track title %%T needs to be " +"specified.\n" +"Please fix the filename template in the preference panel." +msgstr "" +"A megadott sablon rossz! A szám címét %%T szükséges megadni. \n" +"Kérjük javítsa ki a sablont a Beállítások ablakban." + +#: src/GMDatabaseSource.cpp:72 src/GMTrackDatabase.cpp:193 +msgid "Database Error" +msgstr "Adatbázis hiba" + +#: src/GMDatabaseSource.cpp:72 +msgid "Oops. Database Error" +msgstr "Húha! Adatbázis hiba!" + +#: src/GMDatabaseSource.cpp:87 +msgid "No changes" +msgstr "Nincs változás" + +#: src/GMDatabaseSource.cpp:87 +msgid "Filenames did not require any changes" +msgstr "A fájlnév változatlan" + +#: src/GMDatabaseSource.cpp:94 +msgid "Rename Audio Files?" +msgstr "Átnevezzem a hangfájlokat?" + +#: src/GMDatabaseSource.cpp:95 +msgid "Renaming Audio Files…" +msgstr "Hangfájlok átnevezése…" + +#: src/GMDatabaseSource.cpp:95 +msgid "The following audio files are going to be renamed" +msgstr "A következő hangfájlok kerülnek átnevezésre" + +#: src/GMDatabaseSource.cpp:97 +msgid "&Rename" +msgstr "Át&nevezés" + +#: src/GMDatabaseSource.cpp:121 src/GMDatabaseSource.cpp:125 +msgid "Unable to rename file" +msgstr "A fájl átnevezése sikertelen" + +#: src/GMDatabaseSource.cpp:121 +#, c-format +msgid "" +"Unable to rename:\n" +"%s\n" +"\n" +"to:%s\n" +"Continue renaming files?" +msgstr "" +"A fájl átnevezése sikertelen:\n" +"%s\n" +"\n" +"az új névre:%s\n" +"Folytassuk a fájlok átnevezését?" + +#: src/GMDatabaseSource.cpp:125 +#, c-format +msgid "" +"Unable to rename:\n" +"%s\n" +"\n" +"to:%s" +msgstr "" +"A fájl átnevezése sikertelen:\n" +"%s\n" +"\n" +"az új névre:%s" + +#: src/GMDatabaseSource.cpp:160 src/GMImportDialog.cpp:534 +msgid "Filename Template" +msgstr "Fájlnév sablonok" + +#: src/GMDatabaseSource.cpp:177 +msgid "" +"Template may contain absolute or relative path, environment variables\n" +"and ~. Relative paths are based on the location of the original file. The\n" +"file extension gets automatically added. The following macros\n" +"may be used:" +msgstr "" +"A sablon tartalmazhat relatív, vagy abszolút útvonalat, környezeti változót\n" +"és ~ jelet. A relatív útvonalak alpjául a fájl eredeti helye szolgál.\n" +"A kiterjesztést automatikusan hozzáadja a program. A következő makrók " +"használhatók:" + +#: src/GMDatabaseSource.cpp:178 +msgid "" +"%T - title %A - album name\n" +"%P - album artist name %p - track artist name\n" +"%y - year %d - disc number\n" +"%N - track number (2 digits) %n - track number \n" +"%G - genre" +msgstr "" +"%T - Cím\t\t\t\t\t\t\t%A - Album címe\n" +"%P - Album előadója\t\t\t\t%p - Szám előadója\n" +"%y - Év\t\t\t\t\t\t\t%d - lemez száma\n" +"%N - Zeneszám sorszáma (2 jegy)\t%n - Zeneszám sorszáma\n" +"%G - Műfaj" + +#: src/GMDatabaseSource.cpp:185 +msgid "Conditions may be used as well:" +msgstr "Feltételek is alkalmazhatók:" + +#: src/GMDatabaseSource.cpp:186 +msgid "" +"?c - display a if c is not empty else display b.\n" +"?c - display c if not empty\n" +msgstr "" +"?c \t- Mutassa a-t, ha c nem üres, egyéb esetekben b-t.\n" +"?c\t\t\t- Mutassa c-t, ha az nem üres.\n" + +#: src/GMDatabaseSource.cpp:195 src/GMDatabaseSource.cpp:1815 +#: src/GMImportDialog.cpp:546 +msgid "Template:" +msgstr "Sablon:" + +#: src/GMDatabaseSource.cpp:199 src/GMDatabaseSource.cpp:1819 +msgid "Encoding:" +msgstr "Kódolás:" + +#: src/GMDatabaseSource.cpp:205 +msgid "Exclude:" +msgstr "Kivéve:" + +#: src/GMDatabaseSource.cpp:209 src/GMDatabaseSource.cpp:1825 +msgid "Options:" +msgstr "Beállítások:" + +#: src/GMDatabaseSource.cpp:210 src/GMDatabaseSource.cpp:1826 +msgid "Replace spaces with underscores" +msgstr "Szóközök kicserélése alsóvonalra" + +#: src/GMDatabaseSource.cpp:212 src/GMDatabaseSource.cpp:1828 +msgid "Lower case" +msgstr "Kisbetűs" + +#: src/GMDatabaseSource.cpp:214 src/GMDatabaseSource.cpp:1830 +msgid "Lower case extension" +msgstr "Kisbetűs kiterjesztés" + +#: src/GMDatabaseSource.cpp:341 src/GMStreamSource.cpp:63 +msgid "No." +msgstr "No." + +#: src/GMDatabaseSource.cpp:342 +msgid "Queue" +msgstr "Sor" + +#: src/GMDatabaseSource.cpp:343 src/GMDatabaseSource.cpp:1391 +#: src/GMTrackView.cpp:238 +msgid "Title" +msgstr "Cím" + +#: src/GMDatabaseSource.cpp:344 src/GMDatabaseSource.cpp:1396 +#: src/GMTrackView.cpp:239 +msgid "Artist" +msgstr "Előadó" + +#: src/GMDatabaseSource.cpp:345 src/GMDatabaseSource.cpp:1397 +msgid "Album Artist" +msgstr "Album előadója" + +#: src/GMDatabaseSource.cpp:346 src/GMDatabaseSource.cpp:1405 +#: src/GMPreferencesDialog.cpp:540 src/GMTrackView.cpp:240 +msgid "Album" +msgstr "Album" + +#: src/GMDatabaseSource.cpp:347 src/GMDatabaseSource.cpp:1364 +#: src/GMDatabaseSource.cpp:1375 src/GMDatabaseSource.cpp:1381 +msgid "Disc" +msgstr "Lemez" + +#: src/GMDatabaseSource.cpp:348 src/GMDatabaseSource.cpp:1408 +#: src/GMStreamSource.cpp:66 src/GMStreamSource.cpp:177 +#: src/GMStreamSource.cpp:216 src/GMTrackView.cpp:241 +msgid "Genre" +msgstr "Műfaj" + +#: src/GMDatabaseSource.cpp:349 src/GMDatabaseSource.cpp:1369 +#: src/GMDatabaseSource.cpp:1387 +msgid "Year" +msgstr "Év" + +#: src/GMDatabaseSource.cpp:350 ../../../fox-1.6.37/src/FXFileSelector.cpp:730 +msgid "Time" +msgstr "Idő" + +#: src/GMDatabaseSource.cpp:490 +msgid "Remove…\tDel\tRemove Genre from Library." +msgstr "Törlés…\tDel\tA műfaj eltávolítása a gyűjteményből." + +#: src/GMDatabaseSource.cpp:496 src/GMDatabaseSource.cpp:505 +#: src/GMPlayListSource.cpp:104 src/GMPlayListSource.cpp:110 +msgid "Copy\tCtrl-C\tCopy associated tracks to the clipboard." +msgstr "Másolás\tCtrl-C\tA kijelölt számok másolása a vágólapra." + +#: src/GMDatabaseSource.cpp:499 src/GMDatabaseSource.cpp:508 +msgid "Remove…\tDel\tRemove associated tracks from library." +msgstr "Törlés…\tDel\tA kijelölt számok eltávolítása a gyűjteményből." + +#: src/GMDatabaseSource.cpp:513 src/GMPlayListSource.cpp:116 +msgid "Edit…\tF2\tEdit Track Information." +msgstr "Szerkesztés…\tF2\tZeneszám információinak szerkesztése" + +#: src/GMDatabaseSource.cpp:514 src/GMPlayListSource.cpp:117 +msgid "Copy\tCtrl-C\tCopy track(s) to clipboard." +msgstr "Másolás\tCtrl-C\tSzám(ok) másolása a vágólapra." + +#: src/GMDatabaseSource.cpp:518 src/GMPlayListSource.cpp:120 +msgid "Open Folder Location\t\tOpen Folder Location." +msgstr "" + +#: src/GMDatabaseSource.cpp:523 +msgid "Remove…\tDel\tRemove track(s) from library." +msgstr "Törlés…\tDel\tA szám(ok) eltávolítása a gyűjteményből." + +#: src/GMDatabaseSource.cpp:537 +msgid "New Play List…\t\tCreate a new play list." +msgstr "Új lejátszólista…\t\tÚj lejátszólista létrehozása." + +#: src/GMDatabaseSource.cpp:539 src/GMPlayListSource.cpp:163 +msgid "Export…" +msgstr "Exportálás…" + +#: src/GMDatabaseSource.cpp:540 +msgid "Information…\t\tLibrary Statistics" +msgstr "Információk…\t\tGyűjtemény statisztika" + +#: src/GMDatabaseSource.cpp:542 +msgid "Remove All Tracks\t\tRemove all tracks from the library" +msgstr "Mindet töröl\t\tAz összes zeneszám eltávolítása a gyűjteményből." + +#: src/GMDatabaseSource.cpp:1272 +msgid "Edit Track Information" +msgstr "Szám információk szerkesztése" + +#: src/GMDatabaseSource.cpp:1275 src/GMPlayListSource.cpp:409 +#: src/GMStreamSource.cpp:205 ../../../fox-1.6.37/src/FXMessageBox.cpp:128 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:143 +msgid "&Save" +msgstr "Menté&s" + +#: src/GMDatabaseSource.cpp:1315 +msgid "&Tag" +msgstr "" + +#: src/GMDatabaseSource.cpp:1320 +#, fuzzy +msgid "&Properties" +msgstr "Tulajdonságok" + +#: src/GMDatabaseSource.cpp:1325 src/GMImportDialog.cpp:525 +msgid "Filename" +msgstr "Fájlnév" + +#: src/GMDatabaseSource.cpp:1329 ../../../fox-1.6.37/src/FXFileList.cpp:193 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:728 +msgid "Type" +msgstr "Típus" + +#: src/GMDatabaseSource.cpp:1333 ../../../fox-1.6.37/src/FXFileList.cpp:194 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:729 +msgid "Size" +msgstr "Méret" + +#: src/GMDatabaseSource.cpp:1341 src/GMStreamSource.cpp:65 +msgid "Bitrate" +msgstr "Bitráta" + +#: src/GMDatabaseSource.cpp:1345 +msgid "Samplerate" +msgstr "Mintavételezés" + +#: src/GMDatabaseSource.cpp:1349 +msgid "Channels" +msgstr "Csatornák" + +#: src/GMDatabaseSource.cpp:1358 src/GMPreferencesDialog.cpp:539 +msgid "Track" +msgstr "Szám" + +#: src/GMDatabaseSource.cpp:1403 +msgid "\tSeparate Artists" +msgstr "\tKülönböző előadók" + +#: src/GMDatabaseSource.cpp:1403 +msgid "\tShared Artists" +msgstr "\tKözös előadók" + +#: src/GMDatabaseSource.cpp:1416 +msgid "Auto track number. Offset:" +msgstr "Automatikus sorszámozás. Eltolás:" + +#: src/GMDatabaseSource.cpp:1425 +msgid "Update Tag in File" +msgstr "Címkék (Tag) frissítése a fájlban" + +#: src/GMDatabaseSource.cpp:1430 +msgid "Update Filename" +msgstr "Fájlnév frissítése" + +#: src/GMDatabaseSource.cpp:1431 +msgid "Set export template…" +msgstr "Exportálási sablon…" + +#: src/GMDatabaseSource.cpp:1645 +msgid "Update Tags?" +msgstr "Frissítsem a címkéket (Tag)?" + +#: src/GMDatabaseSource.cpp:1645 +msgid "" +"No tracks were updated.\n" +"Would you still like to write the tags for the selected tracks?" +msgstr "" +"Nem változott egyetlen szám sem.\n" +"Továbbra is szeretné a címkék frissítését a kijelölt számokra?" + +#: src/GMDatabaseSource.cpp:1700 +msgid "Remove Audio Files?" +msgstr "Töröljem a hangfájlokat?" + +#: src/GMDatabaseSource.cpp:1701 +msgid "Remove Audio Files..." +msgstr "A hangfájlok eltávolítása..." + +#: src/GMDatabaseSource.cpp:1701 +msgid "The following audio files are going to be removed" +msgstr "A következő hangfájlokat fogom törölni" + +#: src/GMDatabaseSource.cpp:1703 src/GMDatabaseSource.cpp:1877 +#: src/GMPlayListSource.cpp:280 src/GMStreamSource.cpp:270 +msgid "&Remove" +msgstr "&Törlés" + +#: src/GMDatabaseSource.cpp:1729 +msgid "Export Main Library" +msgstr "Fő gyűjtemény exportálása" + +#: src/GMDatabaseSource.cpp:1731 +msgid "Export Play List" +msgstr "Lejátszólista exportálása" + +#: src/GMDatabaseSource.cpp:1744 +msgid "Overwrite File?" +msgstr "Felülírjam a fájt?" + +#: src/GMDatabaseSource.cpp:1744 +msgid "File already exists. Would you like to overwrite it?" +msgstr "A fájl már létezik. Felülírjam a fájlt?" + +#: src/GMDatabaseSource.cpp:1784 +msgid "Export Genre" +msgstr "Műfaj exportálása" + +#: src/GMDatabaseSource.cpp:1785 +msgid "Export tracks with genre to destination directory." +msgstr "Számok exportálása könyvtárba műfajuk alapján." + +#: src/GMDatabaseSource.cpp:1787 +msgid "Export Artists" +msgstr "Előadók exportálása" + +#: src/GMDatabaseSource.cpp:1788 +msgid "Export tracks from artist to destination directory." +msgstr "Számok exportálása könyvtárba előadójuk alapján." + +#: src/GMDatabaseSource.cpp:1790 +msgid "Export Albums" +msgstr "Albumok exportálása" + +#: src/GMDatabaseSource.cpp:1791 +msgid "Export tracks from album to destination directory." +msgstr "Számok exportálása könyvtárba albumuk alapján." + +#: src/GMDatabaseSource.cpp:1793 +msgid "Export Tracks" +msgstr "Számok exportálása" + +#: src/GMDatabaseSource.cpp:1794 +msgid "Export tracks to destination directory." +msgstr "Számok exportálása könyvtárba." + +#: src/GMDatabaseSource.cpp:1803 +msgid "&Export" +msgstr "&Exportálás" + +#: src/GMDatabaseSource.cpp:1853 src/GMPlayListSource.cpp:261 +msgid "Remove Genre?" +msgstr "Eltávolítsam a műfajt?" + +#: src/GMDatabaseSource.cpp:1854 +msgid "Remove tracks with genre from library?" +msgstr "Eltávolítsam az adott műfajú zenéket a gyűjteményből?" + +#: src/GMDatabaseSource.cpp:1858 src/GMPlayListSource.cpp:264 +msgid "Remove Artist?" +msgstr "Töröljem az előadót?" + +#: src/GMDatabaseSource.cpp:1859 +msgid "Remove tracks from artist from library?" +msgstr "Töröljem az előadó összes számát a gyűjteményből?" + +#: src/GMDatabaseSource.cpp:1863 src/GMPlayListSource.cpp:267 +msgid "Remove Album?" +msgstr "Töröljem az albumot?" + +#: src/GMDatabaseSource.cpp:1864 +msgid "Remove tracks from album from library?" +msgstr "Töröljem az albumhoz tartozó összes számot a gyűjteményből?" + +#: src/GMDatabaseSource.cpp:1868 src/GMPlayListSource.cpp:270 +msgid "Remove Track(s)?" +msgstr "Töröljem a számo(ka)t" + +#: src/GMDatabaseSource.cpp:1869 +msgid "Remove track(s) from library?" +msgstr "Töröljem a számo(ka)t a gyűjteményből?" + +#: src/GMDatabaseSource.cpp:1881 src/GMPlayListSource.cpp:285 +msgid "Remove tracks from disk" +msgstr "Számok törlése a lemezről" + +#: src/GMDatabaseSource.cpp:1895 src/GMDatabaseSource.cpp:1899 +#: src/GMDatabaseSource.cpp:1903 src/GMDatabaseSource.cpp:1907 +#: src/GMPlayListSource.cpp:308 src/GMStreamSource.cpp:280 +msgid "Library Error" +msgstr "Gyűjtemény hiba" + +#: src/GMDatabaseSource.cpp:1895 +msgid "Unable to remove genre from the library" +msgstr "A műfaj eltávolítása az adatbázisból sikertelen." + +#: src/GMDatabaseSource.cpp:1899 +msgid "Unable to remove artist from the library" +msgstr "Az előadó eltávolítása az adatbázisból sikertelen." + +#: src/GMDatabaseSource.cpp:1903 +msgid "Unable to remove album from the library" +msgstr "Az album eltávolítása az adatbázisból sikertelen." + +#: src/GMDatabaseSource.cpp:1907 src/GMPlayListSource.cpp:308 +msgid "Unable to remove track from the library." +msgstr "A zeneszám eltávolítása az adatbázisból sikertelen." + +#: src/GMDatabaseSource.cpp:2117 src/GMDatabaseSource.cpp:2118 +msgid "Create Playlist" +msgstr "Lejátszólista létrehozása" + +#: src/GMDatabaseSource.cpp:2118 +msgid "Specify name of the new playlist" +msgstr "Adja meg az új lejátszólista nevét" + +#: src/GMDatabaseSource.cpp:2120 +msgid "&Create" +msgstr "&Létrehozás" + +#: src/GMDatabaseSource.cpp:2125 src/GMPlayListSource.cpp:415 +#: ../../../fox-1.6.37/src/FXFileList.cpp:192 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:727 +msgid "Name" +msgstr "Név" + +#: src/GMDatabaseSource.cpp:2127 +msgid "New Playlist" +msgstr "Új lejátszólista" + +#: src/GMDatabaseSource.cpp:2178 src/GMDatabaseSource.cpp:2179 +msgid "Clear Music Library?" +msgstr "Töröljük a Zenei gyűjteményt?" + +#: src/GMDatabaseSource.cpp:2179 +msgid "Remove all tracks from the music library?" +msgstr "Eltávolítsuk az összes számot a gyűjteményből?" + +#: src/GMDatabaseSource.cpp:2181 +msgid "&Remove All" +msgstr "&Összes eltávolítása" + +#: src/GMDatabaseSource.cpp:2185 +msgid "Keep play lists" +msgstr "Lejátszólisták megtartása" + +#: src/GMDatabaseSource.cpp:2212 src/GMDatabaseSource.cpp:2213 +msgid "Music Library Information" +msgstr "Zenei Gyűjtemény információk" + +#: src/GMDatabaseSource.cpp:2213 +msgid "You music collection consists of…" +msgstr "Az Ön zenei gyűjteménye a következőkből áll" + +#: src/GMDatabaseSource.cpp:2221 +msgid "Tracks:" +msgstr "Számok:" + +#: src/GMDatabaseSource.cpp:2226 +msgid "Artists:" +msgstr "Előadók:" + +#: src/GMDatabaseSource.cpp:2230 +msgid "Albums:" +msgstr "Albumok:" + +#: src/GMDatabaseSource.cpp:2234 +msgid "Total Time:" +msgstr "Összes idő:" + +#: src/GMEQDialog.cpp:96 +msgid "Equalizer" +msgstr "Hangszínszabályozó" + +#: src/GMEQDialog.cpp:135 +msgid "Equalizer:" +msgstr "Hangszínszabályozó:" + +#: src/GMEQDialog.cpp:137 +msgid "\tSave" +msgstr "\tMentés" + +#: src/GMEQDialog.cpp:138 +msgid "\tReset" +msgstr "\tAlapállapot" + +#: src/GMEQDialog.cpp:139 +msgid "\tRemove" +msgstr "\tTörlés" + +#: src/GMEQDialog.cpp:172 +msgid "Pre-amp" +msgstr "Előerősítés" + +#: src/GMEQDialog.cpp:244 src/GMEQDialog.cpp:254 +msgid "Disabled" +msgstr "Letiltva" + +#: src/GMEQDialog.cpp:247 src/GMEQDialog.cpp:299 +msgid "Manual" +msgstr "Kézi" + +#: src/GMEQDialog.cpp:314 +msgid "Delete Preset" +msgstr "Beállítás törlése" + +#: src/GMEQDialog.cpp:314 +#, c-format +msgid "Are you sure you want to delete %s preset?" +msgstr "Biztos, hogy törölni akarja a(z) \"%s\" nevű hangszín beállítást?" + +#: src/GMEQDialog.cpp:334 +msgid "Preset Name" +msgstr "Beállítás neve" + +#: src/GMEQDialog.cpp:334 +msgid "Please enter preset name:" +msgstr "Adja meg a beállítás nevét:" + +#: src/GMEQDialog.cpp:338 +msgid "Overwrite Preset" +msgstr "Beállítás felülírása" + +#: src/GMEQDialog.cpp:338 +#, c-format +msgid "Preset %s already exists. Would you like to overwrite it?" +msgstr "A(z) \"%s\" beállítás már létezik. Felülírjam?" + +#: src/GMFontDialog.cpp:209 +#, fuzzy +msgid "Ultra Condensed" +msgstr "Nagyon sűrű" + +#: src/GMFontDialog.cpp:210 +#, fuzzy +msgid "Extra Condensed" +msgstr "Extra sűrű" + +#: src/GMFontDialog.cpp:211 +msgid "Condensed" +msgstr "Sűrű" + +#: src/GMFontDialog.cpp:212 +#, fuzzy +msgid "Semi Condensed" +msgstr "Mérsékelten sűrű" + +#: src/GMFontDialog.cpp:214 +#, fuzzy +msgid "Semi Expanded" +msgstr "Mérsékelten széthúzott" + +#: src/GMFontDialog.cpp:215 +msgid "Expanded" +msgstr "Széthúzott" + +#: src/GMFontDialog.cpp:216 +#, fuzzy +msgid "Extra Expanded" +msgstr "Extra széthúzott" + +#: src/GMFontDialog.cpp:217 +#, fuzzy +msgid "Ultra Expanded" +msgstr "Ultra széthúzott" + +#: src/GMFontDialog.cpp:224 src/GMPreferencesDialog.cpp:1223 +msgid "Thin" +msgstr "" + +#: src/GMFontDialog.cpp:225 src/GMPreferencesDialog.cpp:1224 +#, fuzzy +msgid "Extra Light" +msgstr "extravékony" + +#: src/GMFontDialog.cpp:226 src/GMPreferencesDialog.cpp:1225 +#, fuzzy +msgid "Light" +msgstr "vékony" + +#: src/GMFontDialog.cpp:228 src/GMPreferencesDialog.cpp:1227 +#, fuzzy +msgid "Medium" +msgstr "közepes" + +#: src/GMFontDialog.cpp:229 src/GMPreferencesDialog.cpp:1228 +#, fuzzy +msgid "Demibold" +msgstr "demikövér" + +#: src/GMFontDialog.cpp:230 src/GMPreferencesDialog.cpp:1229 +msgid "Bold" +msgstr "" + +#: src/GMFontDialog.cpp:231 src/GMPreferencesDialog.cpp:1230 +#, fuzzy +msgid "Extra Bold" +msgstr "extrabold" + +#: src/GMFontDialog.cpp:232 src/GMPreferencesDialog.cpp:1231 +#, fuzzy +msgid "Heavy" +msgstr "nehéz" + +#: src/GMFontDialog.cpp:239 +#, fuzzy +msgid "Reverse Oblique" +msgstr "visszafelé dőlt" + +#: src/GMFontDialog.cpp:240 +#, fuzzy +msgid "Reverse Italic" +msgstr "visszafelé dőlt" + +#: src/GMFontDialog.cpp:242 +#, fuzzy +msgid "Italic" +msgstr "dőlt" + +#: src/GMFontDialog.cpp:243 +#, fuzzy +msgid "Oblique" +msgstr "dőlt" + +#: src/GMFontDialog.cpp:265 +msgid "Normal" +msgstr "Normál" + +#: src/GMImportDialog.cpp:91 ../../../fox-1.6.37/src/FXDirSelector.cpp:137 +msgid "&Directory:" +msgstr "Könyvtár:" + +#: src/GMImportDialog.cpp:166 ../../../fox-1.6.37/src/FXFileSelector.cpp:196 +msgid "&File Name:" +msgstr "&File név:" + +#: src/GMImportDialog.cpp:168 ../../../fox-1.6.37/src/FXMessageBox.cpp:102 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:106 +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:134 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:198 +msgid "&OK" +msgstr "&OK" + +#: src/GMImportDialog.cpp:170 ../../../fox-1.6.37/src/FXFileSelector.cpp:200 +msgid "File F&ilter:" +msgstr "F&ile szűrő:" + +#: src/GMImportDialog.cpp:174 ../../../fox-1.6.37/src/FXFileSelector.cpp:204 +msgid "Read Only" +msgstr "Csak olvasható" + +#: src/GMImportDialog.cpp:179 src/GMSearch.cpp:565 src/GMSearch.cpp:647 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:208 +msgid "Directory:" +msgstr "Könyvtár:" + +#: src/GMImportDialog.cpp:186 ../../../fox-1.6.37/src/FXFileSelector.cpp:228 +msgid "&Set bookmark\t\tBookmark current directory." +msgstr "&Könyvjelző ide\t\tKönyvjelző elhelyezése az aktuális könyvtárra." + +#: src/GMImportDialog.cpp:187 ../../../fox-1.6.37/src/FXFileSelector.cpp:229 +msgid "&Clear bookmarks\t\tClear bookmarks." +msgstr "&Könyvjelzők törlése\t\tKönyvjelzők törlése" + +#: src/GMImportDialog.cpp:203 ../../../fox-1.6.37/src/FXFileSelector.cpp:244 +msgid "\tGo up one directory\tMove up to higher directory." +msgstr "\tEgy könyvtárral feljebb\tEgy könyvtárral feljebb" + +#: src/GMImportDialog.cpp:204 ../../../fox-1.6.37/src/FXFileSelector.cpp:245 +msgid "\tGo to home directory\tBack to home directory." +msgstr "\tSaját könyvtár\tSaját könyvtár." + +#: src/GMImportDialog.cpp:205 ../../../fox-1.6.37/src/FXFileSelector.cpp:246 +msgid "\tGo to work directory\tBack to working directory." +msgstr "\tMunkakönyvtár\tUgrás a futtatási könyvtárba" + +#: src/GMImportDialog.cpp:206 ../../../fox-1.6.37/src/FXFileSelector.cpp:247 +msgid "\tBookmarks\tVisit bookmarked directories." +msgstr "\tKönyvjelzők\tA könyvjelzők megtekintése." + +#: src/GMImportDialog.cpp:209 ../../../fox-1.6.37/src/FXFileSelector.cpp:250 +msgid "\tCreate new directory\tCreate new directory." +msgstr "\tÚj könyvtár létrehozása\tÚj könyvtár létrehozása" + +#: src/GMImportDialog.cpp:210 ../../../fox-1.6.37/src/FXFileSelector.cpp:251 +msgid "\tShow list\tDisplay directory with small icons." +msgstr "\tLista\tA könyvtár tartalmának megjelenítése listaként." + +#: src/GMImportDialog.cpp:211 ../../../fox-1.6.37/src/FXFileSelector.cpp:252 +msgid "\tShow icons\tDisplay directory with big icons." +msgstr "\tIkonok\tA könyvtár tartalmának megjelenítése nagy ikonok formájában." + +#: src/GMImportDialog.cpp:212 ../../../fox-1.6.37/src/FXFileSelector.cpp:253 +msgid "\tShow details\tDisplay detailed directory listing." +msgstr "\tRészletek megjelenítése\tRészletes könvtárlista megjelenítése." + +#: src/GMImportDialog.cpp:213 ../../../fox-1.6.37/src/FXFileSelector.cpp:254 +msgid "\tShow hidden files\tShow hidden files and directories." +msgstr "" +"\tRejtett fájlok megjelenítése\tRejtett fájlok és könyvtárak megjelenítése." + +#: src/GMImportDialog.cpp:213 ../../../fox-1.6.37/src/FXFileSelector.cpp:254 +msgid "\tHide Hidden Files\tHide hidden files and directories." +msgstr "" +"\tRejtett fájlok rejtése\tNe mutassa a rejtett fájlokat és könyvtárakat." + +#: src/GMImportDialog.cpp:412 +msgid "Synchronize Folder" +msgstr "Könyvtár szinkronizálása" + +#: src/GMImportDialog.cpp:414 +msgid "Import Playlist" +msgstr "Lejátszólista exportálása" + +#: src/GMImportDialog.cpp:416 +msgid "Import Music" +msgstr "Zenék importálása" + +#: src/GMImportDialog.cpp:448 +msgid "&File(s)" +msgstr "&Fájl(ok)" + +#: src/GMImportDialog.cpp:475 +msgid "&Directory" +msgstr "&Könyvtár" + +#: src/GMImportDialog.cpp:478 +msgid "Exclude Filter\tFilter out directories and/or files based on pattern" +msgstr "" +"Kizáró szűrő\tHagyja ki azokat a fájlokat/könyvtárakat amelyre teljesül a " +"szűrő" + +#: src/GMImportDialog.cpp:481 +msgid "Folders:" +msgstr "Könyvtárak:" + +#: src/GMImportDialog.cpp:483 +msgid "Files:" +msgstr "Fájlok:" + +#: src/GMImportDialog.cpp:499 src/GMImportDialog.cpp:560 +msgid "&Sync" +msgstr "&Szinkronizálás" + +#: src/GMImportDialog.cpp:501 +msgid "Sync Operation" +msgstr "Szinkronizálás" + +#: src/GMImportDialog.cpp:504 +msgid "Import new tracks\tImports files not yet in the database." +msgstr "" +"Új számok importálása\tImportálja azon fájlokat, amelyek eddig nem " +"szerepeltek a gyűjteményben." + +#: src/GMImportDialog.cpp:505 +msgid "Remove tracks that have been deleted from disk" +msgstr "" +"Számok eltávolítása a gyűjteményből, melyek már nem találhatók a lemezen" + +#: src/GMImportDialog.cpp:507 +msgid "Update existing tracks:" +msgstr "Meglévő számok frissítése" + +#: src/GMImportDialog.cpp:508 +msgid "" +"Modified since last import\tOnly reread the tag when the file has been " +"modified." +msgstr "" +"Módosult a legutóbbi importálás óta\tCsak a címkék (Tag) újraolvasása " +"azoknál a fájloknál, amelyek a legutóbbi import óta módosultak." + +#: src/GMImportDialog.cpp:510 +msgid "All\tAlways read the tags" +msgstr "Összes\tMindig olvassa be a címkéket (Tag)" + +#: src/GMImportDialog.cpp:511 +msgid "Remove tracks found in folder from database" +msgstr "A könyvtárban található számok eltávolítása a gyűjteményből" + +#: src/GMImportDialog.cpp:514 +msgid "&Track" +msgstr "&Szám" + +#: src/GMImportDialog.cpp:517 +msgid "Parse Settings" +msgstr "Beállítások beolvasása" + +#: src/GMImportDialog.cpp:521 +msgid "Parse info from:" +msgstr "Beállítások beolvasása innen:" + +#: src/GMImportDialog.cpp:524 +msgid "Tag" +msgstr "Címke" + +#: src/GMImportDialog.cpp:526 +msgid "Both" +msgstr "Mindkettő" + +#: src/GMImportDialog.cpp:528 +msgid "Default value:" +msgstr "Alapértelmezett érték:" + +#: src/GMImportDialog.cpp:532 +msgid "Set track number based on scan order." +msgstr "A szám sorszáma a beolvasás sorrendje szerint" + +#: src/GMImportDialog.cpp:537 +msgid "" +"%T - title %A - album name\n" +"%P - album artist name %p - track artist name \n" +"%N - track number %G - genre" +msgstr "" +"%T - cím %A - album cím\n" +"%P - album előadója %p - szám előadója \n" +"%N - szám sorszáma %G - műfaj" + +#: src/GMImportDialog.cpp:549 +msgid "Replace underscores with spaces" +msgstr "Alsóvonalakat cserélje szóközre" + +#: src/GMImportDialog.cpp:562 +msgid "&Import" +msgstr "&Importálás" + +#: src/GMPlayer.cpp:212 +msgid "Unable to initialize audio driver." +msgstr "Nem tudom inicializálni a lejátszót." + +#: src/GMPlayer.cpp:714 +msgid "Unknown host." +msgstr "Ismeretlen host." + +#: src/GMPlayer.cpp:715 +msgid "Unknown device" +msgstr "Ismeretlen eszköz" + +#: src/GMPlayer.cpp:716 +msgid "Network not reachable." +msgstr "A hálózat nem elérhető." + +#: src/GMPlayer.cpp:717 +msgid "Audio output unavailable." +msgstr "A hangkimenet nem elérhető." + +#: src/GMPlayer.cpp:718 +msgid "Connection Refused." +msgstr "Kapcsolódás visszautasítva." + +#: src/GMPlayer.cpp:719 +msgid "File not found." +msgstr "A fájl nem található." + +#: src/GMPlayer.cpp:720 +msgid "Resource not accessible. Check permissions" +msgstr "Az erőforrás nem elérhető. Ellenőrizze a jogosultságokat." + +#: src/GMPlayer.cpp:721 +msgid "Read Error" +msgstr "Olvasási hiba" + +#: src/GMPlayer.cpp:722 +msgid "Error while loading library/plugin" +msgstr "Hiba a könyvtár/kiegészítő betöltése során." + +#: src/GMPlayer.cpp:723 +msgid "Warning" +msgstr "Figyelmeztetés" + +#: src/GMPlayer.cpp:724 +msgid "Security Warning" +msgstr "Biztonsági figyelmeztetés" + +#: src/GMPlayer.cpp:725 +msgid "Unknown Error" +msgstr "Ismeretlen hiba" + +#: src/GMPlayer.cpp:761 +msgid "Error" +msgstr "Hiba" + +#: src/GMPlayerManager.cpp:439 +#, c-format +msgid "Unable to create directory %s\n" +msgstr "Nem tudom létrehozni a(z) \"%s\" könyvtárat\n" + +#: src/GMPlayerManager.cpp:612 +msgid "" +"For some reason the FOX library was compiled without PNG support.\n" +"In order to show all icons, Goggles Music Manager requires PNG\n" +"support in the FOX library. If you've compiled FOX yourself, most\n" +"likely the libpng header files were not installed on your system." +msgstr "" +"Valamilyen okból kifolyólag a FOX PNG támogatás nélkül került lefordításra.\n" +"Ahhoz, hogy az összes ikon megjeleníthető legyen, szükség van a PNG " +"támogatásra. \n" +"Ha a FOX könyvtárat Ön fordította, ellenőrizze, hogy a libpng fejlesztői " +"fájlok telepítve legyenek a FOX lefordításakor." + +#: src/GMPlayerManager.cpp:623 +msgid "Session bus not available. All features requiring dbus are disabled." +msgstr "A session busz nem elérhető. A dbus-t igénylő funkciókat kikapcsoltam." + +#: src/GMPlayerManager.cpp:633 +msgid "A DBus error occurred. All features requiring sessionbus are disabled." +msgstr "DBus hiba keletkezett. A dbus-t igénylő funkciókat kikapcsoltam." + +#: src/GMPlayerManager.cpp:644 +msgid "" +"Session Bus not available. All features requiring sessionbus are disabled." +msgstr "A session busz nem elérhető. A dbus-t igénylő funkciókat kikapcsoltam." + +#: src/GMPlayerManager.cpp:719 src/GMPreferencesDialog.cpp:594 +msgid "Audio Device Error" +msgstr "Hangeszköz hiba" + +#: src/GMPlayerManager.cpp:1551 +msgid "Last.FM Error" +msgstr "Last.FM hiba." + +#: src/GMPlayerManager.cpp:1558 +msgid "Playback Error" +msgstr "Lejátszási hiba" + +#: src/GMPlayerManager.cpp:1708 +#, c-format +msgid "" +"%s\n" +"%s (%d)" +msgstr "" + +#: src/GMPlayListSource.cpp:99 src/GMPlayListSource.cpp:105 +#: src/GMPlayListSource.cpp:111 src/GMPlayListSource.cpp:121 +msgid "Remove…\tDel\tRemove track(s) from play list." +msgstr "Törlés…\tDel\tA szám eltávolítása a lejátszólistából." + +#: src/GMPlayListSource.cpp:161 +msgid "Edit…" +msgstr "Szerkesztés…" + +#: src/GMPlayListSource.cpp:162 +msgid "Import…" +msgstr "Importálás…" + +#: src/GMPlayListSource.cpp:164 +msgid "Remove Playlist" +msgstr "Lejátszólista eltávolítása" + +#: src/GMPlayListSource.cpp:262 +msgid "Remove tracks with genre from play list?" +msgstr "Eltávolítsuk az ilyen műfajú számokat a lejátszólistából?" + +#: src/GMPlayListSource.cpp:265 +msgid "Remove tracks from artist from play list?" +msgstr "Eltávolítsuk az előadóhoz tartozó számokat a lejátszólistából?" + +#: src/GMPlayListSource.cpp:268 +msgid "Remove tracks from album from play list?" +msgstr "Eltávolítsuk az albumhoz tartozó számokat a lejátszólistából?" + +#: src/GMPlayListSource.cpp:271 +msgid "Remove track(s) from play list?" +msgstr "Eltávolítsuk a számokat a lejátszólistából?" + +#: src/GMPlayListSource.cpp:284 +msgid "Remove tracks from music library" +msgstr "Számok eltávolítása a lejátszólistából" + +#: src/GMPlayListSource.cpp:406 src/GMPlayListSource.cpp:407 +msgid "Edit Playlist" +msgstr "Lejátszólista szerkesztése" + +#: src/GMPlayListSource.cpp:407 +msgid "Change playlist name" +msgstr "Lejátszólista nevének megváltoztatása" + +#: src/GMPlayListSource.cpp:432 +msgid "Delete Play List?" +msgstr "Töröljem a lejátszólistát?" + +#: src/GMPlayListSource.cpp:432 +msgid "Are you sure you want to delete the playlist?" +msgstr "Biztos benne, hogy töröljem a lejátszólistát?" + +#: src/GMPlayListSource.cpp:432 ../../../fox-1.6.37/src/FXMessageBox.cpp:111 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:116 +msgid "&Yes" +msgstr "&Igen" + +#: src/GMPlayListSource.cpp:432 ../../../fox-1.6.37/src/FXMessageBox.cpp:112 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:117 +msgid "&No" +msgstr "&Nem" + +#: src/GMPreferencesDialog.cpp:252 +msgid "Preferences" +msgstr "Beállítások" + +#: src/GMPreferencesDialog.cpp:286 +msgid "&General" +msgstr "&Általános" + +#: src/GMPreferencesDialog.cpp:289 +msgid "Sort Options" +msgstr "Rendezési beállítások" + +#: src/GMPreferencesDialog.cpp:293 +msgid "Ignore leading words" +msgstr "Névelők figylemen kívül hagyása" + +#: src/GMPreferencesDialog.cpp:296 +msgid "Album Covers" +msgstr "Album borítók" + +#: src/GMPreferencesDialog.cpp:299 +msgid "Show album cover of playing track\tShow album cover of playing track" +msgstr "" +"Mutassa az albumborítót a szám lejátszásakor\tMutassa a lejátszott szám " +"ablumborítóját." + +#: src/GMPreferencesDialog.cpp:300 +msgid "Show album covers in album browser\tShow album covers in album browser" +msgstr "" +"Albumborítók megjelenítése az albumböngészőben\tAlbumborítók megjelenítése " +"az albumböngészőben" + +#: src/GMPreferencesDialog.cpp:302 +msgid "System Tray" +msgstr "Tálca" + +#: src/GMPreferencesDialog.cpp:304 +msgid "Show Tray Icon\tShow tray icon in the system tray." +msgstr "Tálca ikon használata\tMutassa a kis tálca ikont." + +#: src/GMPreferencesDialog.cpp:307 +msgid "" +"Show Track Change Notifications\tInform notification daemon of track changes." +msgstr "" +"Számváltás üzenetpanel\tÜzenetet küld a notification daemonnak,hogy más " +"számot játszik már a program." + +#: src/GMPreferencesDialog.cpp:311 +msgid "Last.fm" +msgstr "Last.fm" + +#: src/GMPreferencesDialog.cpp:315 +msgid "" +"This version of Goggles Music Manager is\n" +"not supported by Last-FM. Please upgrade\n" +"to a newer version of GMM." +msgstr "" +"A Goggles Music Manager ezen verziója\n" +"nem támogatott a Last.fm által. Frissítsen\n" +"egy frissebb verziójú programra." + +#: src/GMPreferencesDialog.cpp:321 +msgid "Service:" +msgstr "" + +#: src/GMPreferencesDialog.cpp:338 +#, fuzzy +msgid "&Sign up…" +msgstr "&Feliratkozás last.fm-re…" + +#: src/GMPreferencesDialog.cpp:344 +msgid "Username:" +msgstr "Felhasználónév:" + +#: src/GMPreferencesDialog.cpp:346 +msgid "Password:" +msgstr "Jelszó:" + +#: src/GMPreferencesDialog.cpp:349 +msgid "Scrobble" +msgstr "" + +#: src/GMPreferencesDialog.cpp:359 +msgid "&Window" +msgstr "&Ablak" + +#: src/GMPreferencesDialog.cpp:362 +msgid "Window" +msgstr "Ablak" + +#: src/GMPreferencesDialog.cpp:365 +#, fuzzy +msgid "Close button minimizes to tray" +msgstr "A bezáró gomb elrejti a főablakot" + +#: src/GMPreferencesDialog.cpp:366 +msgid "Show Status Bar" +msgstr "Állapotsor megjelenítése" + +#: src/GMPreferencesDialog.cpp:368 +msgid "Show Icons in Track Browser" +msgstr "Ikonok a zeneszám böngészőben" + +#: src/GMPreferencesDialog.cpp:370 +msgid "Display playing track in title bar" +msgstr "A lejátszott szám információinak megjelenítése a címsorban" + +#: src/GMPreferencesDialog.cpp:372 +msgid "Player Controls" +msgstr "Lejátszó gombok" + +#: src/GMPreferencesDialog.cpp:376 +msgid "Location:" +msgstr "Hely:" + +#: src/GMPreferencesDialog.cpp:378 +msgid "Top" +msgstr "Tetejére" + +#: src/GMPreferencesDialog.cpp:379 +msgid "Bottom" +msgstr "Aljára" + +#: src/GMPreferencesDialog.cpp:387 +msgid "Title Format:" +msgstr "" + +#: src/GMPreferencesDialog.cpp:391 +msgid "Style:" +msgstr "Stílus" + +#: src/GMPreferencesDialog.cpp:392 +msgid "Show Labels" +msgstr "Gombfeliratok megjelenítése" + +#: src/GMPreferencesDialog.cpp:395 +msgid "Large Icons" +msgstr "Nagy ikonok" + +#: src/GMPreferencesDialog.cpp:399 +msgid "A&ppearance" +msgstr "&Megjelenés" + +#: src/GMPreferencesDialog.cpp:402 +msgid "Colors" +msgstr "Színek" + +#: src/GMPreferencesDialog.cpp:409 +msgid "fg\tForeground Color" +msgstr "elő\tElőtér színe" + +#: src/GMPreferencesDialog.cpp:410 +msgid "bg\tBackground Color" +msgstr "hát\tHáttér színe" + +#: src/GMPreferencesDialog.cpp:411 +msgid "alt bg\tAlternative Background Color" +msgstr "alt\tAlternatív szín" + +#: src/GMPreferencesDialog.cpp:422 +msgid "Normal\tNormal Text Color" +msgstr "Normál\tNormál betűszín" + +#: src/GMPreferencesDialog.cpp:426 +msgid "Base\tBase Color" +msgstr "Alap\tAlap szín" + +#: src/GMPreferencesDialog.cpp:429 +msgid "Selected\tSelected Text Color" +msgstr "Kijelölt\tKijelölt szöveg színe" + +#: src/GMPreferencesDialog.cpp:433 +#, fuzzy +msgid "Menu\tMenu Base Color" +msgstr "Menü\tMenü színe" + +#: src/GMPreferencesDialog.cpp:437 +msgid "Menu\tMenu Text Color" +msgstr "Menü\tMenü színe" + +#: src/GMPreferencesDialog.cpp:441 +msgid "Border\tBorder Color" +msgstr "Keret\tKeret színe" + +#: src/GMPreferencesDialog.cpp:445 +msgid "Tooltip\tTooltip Color" +msgstr "Súgó\tSúgó színe" + +#: src/GMPreferencesDialog.cpp:449 +msgid "Hilite\tHilite Color" +msgstr "Aktuális\tAktuális menüpont színe" + +#: src/GMPreferencesDialog.cpp:456 +msgid "Shadow\tShadow Color" +msgstr "Árnyék\tÁrnyék színe" + +#: src/GMPreferencesDialog.cpp:459 +msgid "Playing\tPlaying Track Color" +msgstr "Lejátszott\tAz éppen lejátszott szám színe" + +#: src/GMPreferencesDialog.cpp:463 +msgid "Tray\tTray Background Color" +msgstr "Tálca\tTálca háttér színe" + +#: src/GMPreferencesDialog.cpp:473 +msgid "Presets:" +msgstr "Beállítás:" + +#: src/GMPreferencesDialog.cpp:481 +msgid "Font & Icons" +msgstr "" + +#: src/GMPreferencesDialog.cpp:487 +#, fuzzy +msgid "Default Font" +msgstr "Alapértelmezett érték:" + +#: src/GMPreferencesDialog.cpp:489 +msgid "Change…" +msgstr "Változtatás…" + +#: src/GMPreferencesDialog.cpp:490 +msgid "Icons" +msgstr "Ikonok" + +#: src/GMPreferencesDialog.cpp:509 +msgid "&Audio" +msgstr "&Hang" + +#: src/GMPreferencesDialog.cpp:512 +msgid "Engine" +msgstr "Lejátszó modul" + +#: src/GMPreferencesDialog.cpp:516 +msgid "Audio Driver:" +msgstr "Hangeszköz" + +#: src/GMPreferencesDialog.cpp:527 +msgid "Close audio device on pause." +msgstr "Zárja le a hangeszközt szünetre" + +#: src/GMPreferencesDialog.cpp:528 +msgid "Turn off playback engine on stop." +msgstr "Kapcsolja ki a lejátszó modult leállításkor" + +#: src/GMPreferencesDialog.cpp:529 +msgid "" +"Turn on playback engine on startup.\tFor faster startup, playback engine is " +"normally started when first track is played.\tFor faster startup, playback " +"engine is normally started when first track is played." +msgstr "" +"Kapcsolja be a lejátszó modult induláskor\tA gyorsabb indulás érdekében a " +"lejátszó modul csak az első szám lejátszásakor kerül elindításra.\tA " +"gyorsabb indulás érdekében a lejátszó modul csak az első szám lejátszásakor " +"kerül elindításra." + +#: src/GMPreferencesDialog.cpp:532 +msgid "Playback" +msgstr "Lejátszás" + +#: src/GMPreferencesDialog.cpp:536 +msgid "Replay Gain:" +msgstr "Lejátszás vezérlés" + +#: src/GMPreferencesDialog.cpp:538 +msgid "Off" +msgstr "Kikapcsol" + +#: src/GMPreferencesDialog.cpp:543 +msgid "Gapless playback" +msgstr "Szünetmentes lejátszás" + +#: src/GMPreferencesDialog.cpp:544 +msgid "Volume Normalization" +msgstr "Hangerő normalizálása" + +#: src/GMPreferencesDialog.cpp:594 +#, c-format +msgid "Failed to open requested audio driver: %s" +msgstr "A(z) %s hangeszköz megnyitása sikertelen." + +#: src/GMPreferencesDialog.cpp:678 +msgid "Current" +msgstr "Aktuális" + +#: src/GMPreferencesDialog.cpp:696 +msgid "Custom" +msgstr "Egyedi" + +#: src/GMPreferencesDialog.cpp:769 src/GMPreferencesDialog.cpp:774 +#: src/GMWindow.cpp:1133 src/GMWindow.cpp:1140 src/GMWindow.cpp:1147 +#: src/GMWindow.cpp:1154 +msgid "Unable to launch webbrowser" +msgstr "Nem tudtam elindítani a böngészőt" + +#: src/GMPreferencesDialog.cpp:1172 +msgid "Select Normal Font" +msgstr "Normál betűtípus kiválasztása" + +#: src/GMRemote.cpp:295 +msgid "&Next" +msgstr "&Következő" + +#: src/GMRemote.cpp:296 +msgid "P&revious" +msgstr "&Előző" + +#: src/GMRemote.cpp:297 src/GMWindow.cpp:1197 +msgid "&Play" +msgstr "&Lejátszás" + +#: src/GMRemote.cpp:298 +msgid "&Stop" +msgstr "Áll&j" + +#: src/GMRemote.cpp:300 +msgid "Show Browser" +msgstr "Böngésző megjelenítése" + +#: src/GMRemote.cpp:301 src/GMTrayIcon.cpp:307 +msgid "Quit" +msgstr "Kilépés" + +#: src/GMRemote.cpp:385 +#, fuzzy +msgid "\tShow Browser\tShow Browser" +msgstr "Böngésző megjelenítése" + +#: src/GMRemote.cpp:387 +msgid "\tStart Playback\tStart Playback" +msgstr "\tLejátszás\tLejátszás" + +#: src/GMRemote.cpp:388 +msgid "\tStop Playback\tStop Playback" +msgstr "\tLejátszás megállítása\tLejátszás megállítása" + +#: src/GMRemote.cpp:390 +msgid "\tPlay Previous Track\tPlay previous track." +msgstr "\tElőző szám lejátszása\tElőző szám lejátszása" + +#: src/GMRemote.cpp:391 +msgid "\tPlay Next Track\tPlay next track." +msgstr "\tKövetkező szám lejátszása\tKövetkező szám lejátszása" + +#: src/GMRemote.cpp:397 src/GMWindow.cpp:222 +msgid "\tAdjust Volume\tAdjust Volume" +msgstr "\tHangerő beállítása\tHangerő beállítása" + +#: src/GMSearch.cpp:128 +msgid "Unable to open the database" +msgstr "Nem tudom az adatbázist megnyitni." + +#: src/GMSearch.cpp:252 +msgid "Database Error: Unable to retrieve all filenames." +msgstr "Adatbázis hiba: Nem tudom a fájlneveket kiolvasni." + +#: src/GMSearch.cpp:280 +msgid "Unable to update track" +msgstr "A szám adatainak frissítése sikertelen." + +#: src/GMSearch.cpp:439 +msgid "Unable to insert track into the database" +msgstr "Nem tudom a zeneszámot az adatbázisba írni!" + +#: src/GMSearch.cpp:505 src/GMTrackDatabase.cpp:205 +msgid "Fatal Error" +msgstr "Súlyos hiba" + +#: src/GMSearch.cpp:505 +#, c-format +msgid "" +"%s\n" +"Please contact support if this error keeps occuring." +msgstr "" +"%s\n" +"Kérem vegye fel a fejlesztővel a kapcsolatot, ha ez a hiba többször is " +"előfordul." + +#: src/GMSearch.cpp:534 src/GMSearch.cpp:584 +msgid "Updating Database..." +msgstr "Adatbázis frissítése…" + +#: src/GMSearch.cpp:537 src/GMSearch.cpp:626 +msgid "Please wait. This may take a while." +msgstr "Kérem várjon. Ez eltarthat egy ideig." + +#: src/GMSearch.cpp:555 src/GMSearch.cpp:637 +msgid "New Tracks:" +msgstr "Új számok:" + +#: src/GMSearch.cpp:561 src/GMSearch.cpp:643 +msgid "File:" +msgstr "Fájl:" + +#: src/GMSearch.cpp:594 src/GMSearch.cpp:623 +msgid "Importing Files..." +msgstr "Fájlok importálása…" + +#: src/GMSearch.cpp:652 +msgid "&Stop Import" +msgstr "Importálás megállítá&sa" + +#: src/GMSourceView.cpp:54 +msgid "Sources\tPress to change sorting order\tPress to change sorting order" +msgstr "" +"Források\tNyomja meg a sorrend megváltoztatásához\tNyomja meg a sorrend " +"megváltoztatásához" + +#: src/GMSourceView.cpp:245 src/GMWindow.cpp:261 +msgid "New Playlist…\t\tCreate a new playlist" +msgstr "Új lejátszólista…\t\tÚj lejátszólista létrehozása" + +#: src/GMSourceView.cpp:246 src/GMWindow.cpp:262 +msgid "Import Playlist…\t\tImport existing playlist" +msgstr "Lejátszólista importálása…\t\tLétező lejátszólista importálása" + +#: src/GMSourceView.cpp:247 src/GMWindow.cpp:263 +msgid "New Radio Station…\t\tCreate a new playlist" +msgstr "Új rádióállomás…\t\tÚj rádióállomás hozzáadása" + +#: src/GMStreamSource.cpp:64 +msgid "Station" +msgstr "Rádióállomás" + +#: src/GMStreamSource.cpp:112 src/GMStreamSource.cpp:118 +msgid "New Station…\t\t" +msgstr "Új Rádióállomás…\t\t" + +#: src/GMStreamSource.cpp:117 +msgid "Edit…\t\t" +msgstr "Szerkesztés…\t\t" + +#: src/GMStreamSource.cpp:119 +msgid "Remove\t\tRemove." +msgstr "Törlés\t\tTörlés" + +#: src/GMStreamSource.cpp:165 src/GMStreamSource.cpp:166 +msgid "New Internet Radio Station" +msgstr "Új Internetes Rádióállomás" + +#: src/GMStreamSource.cpp:166 +msgid "Specify url and description of new station" +msgstr "Adja meg az új rádióállomás címét és leírását" + +#: src/GMStreamSource.cpp:168 +msgid "C&reate" +msgstr "&Létrehozás" + +#: src/GMStreamSource.cpp:173 src/GMStreamSource.cpp:211 +msgid "Location" +msgstr "Cím" + +#: src/GMStreamSource.cpp:175 src/GMStreamSource.cpp:214 +msgid "Description" +msgstr "Leírás" + +#: src/GMStreamSource.cpp:189 +msgid "Untitled" +msgstr "Névtelen" + +#: src/GMStreamSource.cpp:202 src/GMStreamSource.cpp:203 +msgid "Edit Internet Radio Station" +msgstr "Internetes Rádióállomás szerkesztése" + +#: src/GMStreamSource.cpp:203 +msgid "Update url and description of station" +msgstr "Adja meg a rádióállomás URL-jét és leírását" + +#: src/GMStreamSource.cpp:265 +msgid "Remove Internet Radio Station(s)?" +msgstr "Eltávolítsam az Internetes Rádióállomás(oka)t?" + +#: src/GMStreamSource.cpp:266 +msgid "Remove Internet Radio Station(s) from library?" +msgstr "Eltávolítsam az Internetes Rádióállomás(oka)t a gyűjteményből?" + +#: src/GMStreamSource.cpp:280 +#, c-format +msgid "Unable to remove station from the library." +msgstr "Nem tudom a rádióállomást eltávolítani a gyűjteményből!" + +#: src/GMTrackDatabase.cpp:193 +msgid "" +"An incompatible (future) version of the database was found.\n" +"This usually happens when you try to downgrade to a older version of GMM\n" +"Press OK to continue and reset the database (all information will be " +"lost!).\n" +"Press Cancel to quit now and leave the database as is." +msgstr "" +"Inkompatibilis (jövőbeli verziószámmal rendelkező) adatbázist találtam! \n" +"Ez általában akkor fordulhat elő, ha a korábban használtnál régebbi GMM " +"változatot telepített.\n" +"Nyomjon OK-t az adatbázis eldobásához (minden ott tárolt információ elvész)\n" +"Nyomjon \"Mégsem\"-et a kilépéshez, az adatbázis meghagyásához." + +#: src/GMTrackDatabase.cpp:205 +msgid "" +"Goggles Music Manager was unable to open the database.\n" +"The database may have been corrupted. Please remove ~/.goggles/goggles.db to " +"try again.\n" +"if the error keeps occuring, please file an issue at http://code.google.com/" +"p/gogglesmm" +msgstr "" +"Goggles Music Manager nem tudta megnyitni az adatbázist.\n" +"Az adatbázis fájl megsérülhetett. Kérem, törölje a \"~/.goggles/goggles.db\" " +"fájlt és próbálja újból.\n" +"Ha a hiba továbbra is elfordul, kérem, tegyen hibabejelentést a http://code." +"google.com/p/gogglesmm címen." + +#: src/GMTrackView.cpp:245 +msgid "\tClose Filter\tClose Filter" +msgstr "\tSzűrő bezárása\tSzűrő bezárása" + +#: src/GMTrackView.cpp:246 +#, fuzzy +msgid "&Find" +msgstr "Keresés" + +#: src/GMTrackView.cpp:252 +#, fuzzy +msgid "Tags\tPress to change sorting order\tPress to change sorting order" +msgstr "" +"Műfajok\tNyomja meg a sorrend megváltoztatásához\tNyomja meg a sorrend " +"megváltoztatásához" + +#: src/GMTrackView.cpp:256 +msgid "Artists\tPress to change sorting order\tPress to change sorting order" +msgstr "" +"Előadók\tNyomja meg a rendezés megváltoztatásához\tNyomja meg a rendezés " +"megváltoztatásához" + +#: src/GMTrackView.cpp:260 +msgid "Albums\tPress to change sorting order\tPress to change sorting order" +msgstr "" +"Albumok\tNyomja meg a sorrend megváltoztatásához\tNyomja meg a sorrend " +"megváltoztatásához" + +#: src/GMTrackView.cpp:282 src/GMWindow.cpp:299 +msgid "&Configure Columns…" +msgstr "Oszlopok &beállítása" + +#: src/GMTrackView.cpp:286 +msgid "Browse" +msgstr "Böngészés" + +#: src/GMTrackView.cpp:287 +msgid "Shuffle\tCtrl-R" +msgstr "Keverés\tCtrl-R" + +#: src/GMTrackView.cpp:294 ../../../fox-1.6.37/src/FXDirSelector.cpp:388 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:734 +msgid "Reverse" +msgstr "Visszafelé" + +#: src/GMTrackView.cpp:804 +#, c-format +msgid "All %d Genres" +msgstr "Az összes (%d db) műfaj" + +#: src/GMTrackView.cpp:806 +msgid "All Genres" +msgstr "Összes műfaj" + +#: src/GMTrackView.cpp:832 +#, c-format +msgid "All %d Artists" +msgstr "Az összes (%d db) előadó" + +#: src/GMTrackView.cpp:872 +#, c-format +msgid "All %d Albums" +msgstr "Az összes (%d db) album" + +#: src/GMTrackView.cpp:1258 +#, c-format +msgid "By %s" +msgstr "%s alapján" + +#: src/GMTrackView.cpp:1586 src/GMTrackView.cpp:1609 +msgid "Sort by Album Year" +msgstr "Rendezés az album (kiadási) éve alapján" + +#: src/GMTrackView.cpp:1637 +msgid "&Columns\t\tChange Visible Columns." +msgstr "&Oszlopok\t\tLátható oszlopok beállítása." + +#: src/GMTrackView.cpp:1638 +msgid "&Sort\t\tChange Sorting." +msgstr "Rendezé&s\t\tRendezés beállítása." + +#: src/GMTrayIcon.cpp:302 src/GMWindow.cpp:841 src/GMWindow.cpp:925 +#: src/GMWindow.cpp:948 src/GMWindow.cpp:954 +msgid "Play" +msgstr "Lejátszás" + +#: src/GMTrayIcon.cpp:303 src/GMWindow.cpp:843 +msgid "Stop" +msgstr "Állj" + +#: src/GMTrayIcon.cpp:304 +msgid "Previous Track" +msgstr "Előző szám" + +#: src/GMTrayIcon.cpp:305 +msgid "Next Track" +msgstr "Következő szám" + +#: src/GMWindow.cpp:205 +msgid "Play\tStart Playback\tStart Playback" +msgstr "Lejátszás\tA lejátszás elindítása\tA lejátszás elindítása" + +#: src/GMWindow.cpp:205 +msgid "Pause\tPause\tPause Playback" +msgstr "Szünet\tA lejátszás megakasztása\tA lejátszás megakasztása" + +#: src/GMWindow.cpp:206 +msgid "Stop\tStop Playback\tStop Playback" +msgstr "Állj\tA lejátszás leállítása\tA lejátszás leállítása" + +#: src/GMWindow.cpp:208 +msgid "Previous\tPlay Previous Track\tPlay previous track." +msgstr "Előző\tAz előző szám lejátszása\tAz előző szám lejátszása" + +#: src/GMWindow.cpp:209 +msgid "Next\tPlay Next Track\tPlay next track." +msgstr "Következő\tA következő szám lejátszása\tA következő szám lejátszása" + +#: src/GMWindow.cpp:255 +msgid "&Music" +msgstr "&Fájl" + +#: src/GMWindow.cpp:256 +msgid "Import Folder…\tCtrl-O\tImport Music from folder into Library" +msgstr "" +"Könyvtár importálása…\tCtrl-O\tZenék importálása a megadott könyvtárból a " +"gyűjteménybe" + +#: src/GMWindow.cpp:257 +msgid "Sync Folder…\t\tSynchronize Folder with Music in Library" +msgstr "Szinkronizálás…\t\tA könyvtár szinkronizálása a gyűjteménnyel" + +#: src/GMWindow.cpp:259 +msgid "Play File or Stream…\t\tPlay File or Stream" +msgstr "" + +#: src/GMWindow.cpp:266 +msgid "&Quit\tCtrl-Q\tQuit the application." +msgstr "&Kilépés\tCtrl-Q\tKilépés a programból" + +#: src/GMWindow.cpp:271 +msgid "&Edit" +msgstr "Sz&erkesztés" + +#: src/GMWindow.cpp:272 +msgid "&Copy\tCtrl-C\tCopy Selected Tracks" +msgstr "&Másolás\tCtrl-C\tKijelölt fájlok másolása" + +#: src/GMWindow.cpp:273 +msgid "&Cut\tCtrl-X\tCut Selected Tracks" +msgstr "&Kivágás\tCtrl-X\tKijelölt fájlok kivágása" + +#: src/GMWindow.cpp:274 +msgid "&Paste\tCtrl-V\tPaste Clipboard Selection" +msgstr "&Beillesztés\tCtrl-V\tBeillesztés a vágólapról." + +#: src/GMWindow.cpp:276 +msgid "Find…\tCtrl-F\tShow search filter." +msgstr "Keresés…\tCtrl-F\tKeresőszűrő megjelenítése." + +#: src/GMWindow.cpp:278 +msgid "Preferences…" +msgstr "Beállítások…" + +#: src/GMWindow.cpp:282 +msgid "&View" +msgstr "&Nézet" + +#: src/GMWindow.cpp:283 +msgid "&Browse\tCtrl-B\tShow genre artist and album browser." +msgstr "&Böngészés\tCtrl-B\tMűfaj, előadó, album böngésző megjelenítése." + +#: src/GMWindow.cpp:284 +msgid "Show &Genres\tCtrl-G\tShow genre browser." +msgstr "&Műfajok megjelenítése\tCtrl-G\tMűfajböngésző megjelenítése." + +#: src/GMWindow.cpp:287 src/GMWindow.cpp:289 +msgid "Show &Sources\tCtrl-S\tShow source browser " +msgstr "Források megjelenítése\tCtrl-S\tForrás böngésző megjelenítése." + +#: src/GMWindow.cpp:294 +msgid "Show Full Screen\tF12\tToggle fullscreen mode." +msgstr "Teljes képernyő\tF12\tTeljes képernyő ki/bekapcsolása." + +#: src/GMWindow.cpp:296 +#, fuzzy +msgid "Show Mini Player\tCtrl-M\tToggle Mini Player." +msgstr "Mini lejátszó\tF11\tMini lejátszó üzemódba kapcsolás" + +#: src/GMWindow.cpp:300 +msgid "&Sort" +msgstr "&Rendezés" + +#: src/GMWindow.cpp:302 +msgid "&Jump to Current Track\tCtrl-J\tShow current playing track." +msgstr "" +"&Ugrás az aktuális számra\tCtrl-J\tAz éppen lejátszott számra ugrik a " +"listában." + +#: src/GMWindow.cpp:306 +msgid "&Control" +msgstr "&Lejátszás" + +#: src/GMWindow.cpp:307 +msgid "Play\tCtrl-P\tStart playback." +msgstr "Lejátszás\tCtrl-P\tLejátszás megkezdése" + +#: src/GMWindow.cpp:308 +msgid "Stop\tCtrl-\\\tStop playback." +msgstr "Állj\tCtrl-\\\tA lejátszás megállítása" + +#: src/GMWindow.cpp:309 +msgid "Previous Track\tCtrl-[\tPlay next track." +msgstr "Előző szám\tCtrl-[\tElőző szám lejátszása" + +#: src/GMWindow.cpp:310 +msgid "Next Track\tCtrl-]\tPlay previous track." +msgstr "Következő szám\tCtrl-]\tA következő szám lejátszása." + +#: src/GMWindow.cpp:312 +msgid "Repeat Off\tCtrl-,\tRepeat current track." +msgstr "Ismétlés kikapcsolása\tCtrl-,\tNem ismétli a számokat." + +#: src/GMWindow.cpp:313 +msgid "Repeat Track\tCtrl-.\tRepeat current track." +msgstr "Szám ismétlése\tCtrl-.\tAz aktuális szám ismétlése." + +#: src/GMWindow.cpp:314 +msgid "Repeat All Tracks\tCtrl-/\tRepeat all tracks." +msgstr "Összes ismétlése\tCtrl-/\tAz összes számot ismétli." + +#: src/GMWindow.cpp:315 +msgid "Repeat A-B\tCtrl-T\tRepeat section of track." +msgstr "Szám egy részének ismétlése\tCtrl-T\tA szám egy részének ismétlése" + +#: src/GMWindow.cpp:316 +msgid "Shuffle Mode\tAlt-R\tPlay tracks in random order." +msgstr "Véletlenszerűen\tAlt-R\tA számok véletlenszerű lejátszása." + +#: src/GMWindow.cpp:318 +msgid "Equalizer\t\t" +msgstr "Hangszínszabályozó\t\t" + +#: src/GMWindow.cpp:319 +msgid "Sleep Timer\t\tSetup sleeptimer." +msgstr "Sleep funkció\t\tSleep funkció beállítása" + +#: src/GMWindow.cpp:323 +msgid "&Help" +msgstr "&Segítség" + +#: src/GMWindow.cpp:324 +msgid "&Homepage" +msgstr "&Honlap" + +#: src/GMWindow.cpp:325 +msgid "&Report Issue…" +msgstr "Hiba &bejelentése" + +#: src/GMWindow.cpp:327 +msgid "&Sign up for last.fm…" +msgstr "&Feliratkozás last.fm-re…" + +#: src/GMWindow.cpp:328 +msgid "" +"&Join GMM on last.fm…\t\tJoin the Goggles Music Manager group on last.fm…" +msgstr "" +"&Csatlakozás a GMM-hez a last.fm-en…\t\tCsatlakozás a last.fm GMM " +"csoportjához." + +#: src/GMWindow.cpp:330 +msgid "&About…" +msgstr "&A programról…" + +#: src/GMWindow.cpp:842 src/GMWindow.cpp:940 +msgid "Pause" +msgstr "Szünet" + +#: src/GMWindow.cpp:844 +msgid "Previous" +msgstr "Előző" + +#: src/GMWindow.cpp:845 +msgid "Next" +msgstr "Következő" + +#: src/GMWindow.cpp:920 src/GMWindow.cpp:949 src/GMWindow.cpp:955 +msgid "Start playback." +msgstr "Lejátszás." + +#: src/GMWindow.cpp:926 src/GMWindow.cpp:927 +msgid "Start playback" +msgstr "Lejátszás" + +#: src/GMWindow.cpp:934 src/GMWindow.cpp:941 +msgid "Pause playback." +msgstr "Szünet" + +#: src/GMWindow.cpp:935 +msgid "Pause playback" +msgstr "Szünet" + +#: src/GMWindow.cpp:1052 src/GMWindow.cpp:1054 +msgid "Repeat A-B" +msgstr "Szám egy részének ismétlése" + +#: src/GMWindow.cpp:1053 +msgid "Repeat A" +msgstr "Szám egy részének megjelölése" + +#: src/GMWindow.cpp:1195 +msgid "Play File or Stream" +msgstr "" + +#: src/GMWindow.cpp:1202 +msgid "Please specify a file or url to play:" +msgstr "Kérem adja meg a lejátszandó file-t, vagy címet:" + +#: src/GMWindow.cpp:1206 +msgid "…" +msgstr "…" + +#: src/GMWindow.cpp:1212 +msgid "Select File" +msgstr "Fájl kiválasztása" + +#: src/GMWindow.cpp:1243 +msgid "Sleep Timer" +msgstr "Sleep időzítő" + +#: src/GMWindow.cpp:1244 +msgid "Setup sleep timer" +msgstr "Sleep időzítő beállítása" + +#: src/GMWindow.cpp:1244 +msgid "Stop playback within a certain time" +msgstr "Lejátszás megállítása ennyi idő után" + +#: src/GMWindow.cpp:1246 +msgid "&Start Timer" +msgstr "Időzítő indítá&sa" + +#: src/GMWindow.cpp:1251 +msgid "Sleep in" +msgstr "Kapcsoljon ki " + +#: src/GMWindow.cpp:1253 +msgid "hours and" +msgstr "óra és" + +#: src/GMWindow.cpp:1255 +msgid "minutes." +msgstr "perc múlva." + +#: src/GMDatabaseSource.h:131 +msgid "Music Library" +msgstr "Zenei gyűjtemény" + +#: src/GMStreamSource.h:57 +msgid "Internet Radio" +msgstr "Internet Rádió" + +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:122 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:127 +msgid "&Quit" +msgstr "&Kilépés" + +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:133 +msgid "&Skip" +msgstr "&Kihagyás" + +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:134 +msgid "Skip &All" +msgstr "Min&dent kihagy" + +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:140 +msgid "&Don't Save" +msgstr "&Nincs mentés" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:220 +msgid "\tPick color" +msgstr "\tSzín kiválasztása" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:229 +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:274 +msgid "\tHue, Saturation, Value" +msgstr "\tSzín, telítettség, fényerő" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:239 +msgid "\tRed, Green, Blue" +msgstr "\tPiros, Zöld, Kék" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:245 +msgid "&Red:" +msgstr "&Piros:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:250 +msgid "&Green:" +msgstr "&Zöld:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:255 +msgid "&Blue:" +msgstr "&Kék:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:260 +msgid "&Alpha:" +msgstr "&Alfa:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:280 +msgid "Hue:" +msgstr "Szín:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:285 +msgid "Saturation:" +msgstr "Telítettség:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:290 +msgid "Value:" +msgstr "Fényerő:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:295 +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:330 +msgid "Alpha:" +msgstr "Alfa:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:309 +msgid "\tCyan, Magenta, Yellow" +msgstr "\tCián, Magenta, Sárga" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:315 +msgid "Cyan:" +msgstr "Cián:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:320 +msgid "Magenta:" +msgstr "Magenta:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:325 +msgid "Yellow:" +msgstr "Sárga:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:344 +msgid "\tBy Name" +msgstr "\tNév szerint" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:281 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:521 +msgid "Create New Directory" +msgstr "Új könyvtár létrehozása" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:284 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:524 +msgid "Already Exists" +msgstr "Már létezik" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:288 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:528 +msgid "Cannot Create" +msgstr "Nem tudtam létrehozni" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:309 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:595 +msgid "Copy File" +msgstr "Fájl másolása" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:315 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:601 +msgid "Error Copying File" +msgstr "Hiba a másolás során" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:326 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:618 +msgid "Move File" +msgstr "Fájl mozgatása" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:332 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:624 +msgid "Error Moving File" +msgstr "Hiba a mozgatás során" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:343 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:641 +msgid "Link File" +msgstr "Hivatkozás" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:349 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:647 +msgid "Error Linking File" +msgstr "Hiba a hivatkozás létrehozásakor" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:359 +msgid "Deleting file" +msgstr "Fájl törlése" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:361 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:667 +msgid "Error Deleting File" +msgstr "Hiba a törlés során" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:381 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:719 +msgid "Up one level" +msgstr "Egy szinttel feljebb" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:382 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:720 +msgid "Home directory" +msgstr "Saját könyvtár" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:383 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:721 +msgid "Work directory" +msgstr "Futtatási könyvtár" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:387 +msgid "Sorting" +msgstr "Rendezés" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:389 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:735 +msgid "Ignore case" +msgstr "kisbetű/Nagybetű" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:390 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:746 +msgid "Hidden files" +msgstr "Rejtett fájlok" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:393 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:754 +msgid "Bookmarks" +msgstr "Könyvjelzők" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:394 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:757 +msgid "Set bookmark" +msgstr "Könyvjelző létrehozása" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:395 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:758 +msgid "Clear bookmarks" +msgstr "Könyvjelzők törlése" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:411 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:774 +msgid "New directory..." +msgstr "Új könyvtár..." + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:412 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:775 +msgid "Copy..." +msgstr "Másolás..." + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:413 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:776 +msgid "Move..." +msgstr "Mozgatás..." + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:414 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:777 +msgid "Link..." +msgstr "Hivatkozás..." + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:415 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:778 +msgid "Delete..." +msgstr "Törlés..." + +#: ../../../fox-1.6.37/src/FXFileList.cpp:195 +msgid "Modified Date" +msgstr "Módosítás dátuma" + +#: ../../../fox-1.6.37/src/FXFileList.cpp:196 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:731 +msgid "User" +msgstr "Felhasználó" + +#: ../../../fox-1.6.37/src/FXFileList.cpp:197 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:732 +msgid "Group" +msgstr "Csoport" + +#: ../../../fox-1.6.37/src/FXFileList.cpp:198 +msgid "Attributes" +msgstr "Tulajdonságok" + +#: ../../../fox-1.6.37/src/FXFileList.cpp:200 +msgid "Link" +msgstr "Hivatkozás" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:521 +msgid "Create new directory with name: " +msgstr "Új könyvtár létrehozása a következő névvel:" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:524 +#, c-format +msgid "File or directory %s already exists.\n" +msgstr "A %s fájl vagy könyvtár már létezik.\n" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:528 +#, c-format +msgid "Cannot create directory %s.\n" +msgstr "Nem tudtam a %s könyvtárat létrehozni.\n" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:594 +#, c-format +msgid "" +"Copy file from location:\n" +"\n" +"%s\n" +"\n" +"to location: " +msgstr "" +"Fájl másolása innen:\n" +"\n" +"%s\n" +"\n" +"ide: " + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:601 +#, c-format +msgid "" +"Unable to copy file:\n" +"\n" +"%s to: %s\n" +"\n" +"Continue with operation?" +msgstr "" +"A fájl másolása sikertelen:\n" +"\n" +"%s ide: %s\n" +"\n" +"Folytassam a műveletet?" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:617 +#, c-format +msgid "" +"Move file from location:\n" +"\n" +"%s\n" +"\n" +"to location: " +msgstr "" +"A fájl mozgatása innen:\n" +"\n" +"%s\n" +"\n" +"ide:" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:624 +#, c-format +msgid "" +"Unable to move file:\n" +"\n" +"%s to: %s\n" +"\n" +"Continue with operation?" +msgstr "" +"A fájl mozgatása sikertelen:\n" +"\n" +"%s ide: %s\n" +"\n" +"Folytassam a műveletet?" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:640 +#, c-format +msgid "" +"Link file from location:\n" +"\n" +"%s\n" +"\n" +"to location: " +msgstr "" +"A fájl hivatkozása innen:\n" +"\n" +"%s\n" +"\n" +"ide:" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:647 +#, c-format +msgid "" +"Unable to link file:\n" +"\n" +"%s to: %s\n" +"\n" +"Continue with operation?" +msgstr "" +"A fájl hivatkozása sikertelen:\n" +"\n" +"%s fájlt ide: %s \n" +"\n" +"Folytassam a műveletet?" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:663 +msgid "Deleting files" +msgstr "Fájlok törlése" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:663 +#, c-format +msgid "" +"Are you sure you want to delete the file:\n" +"\n" +"%s" +msgstr "" +"Biztos, hogy törölni akarja a fájlt:\n" +"\n" +"%s" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:667 +#, c-format +msgid "" +"Unable to delete file:\n" +"\n" +"%s\n" +"\n" +"Continue with operation?" +msgstr "" +"A fájl törlése sikertelen:\n" +"\n" +"%s\n" +"\n" +"Folytassam a műveletet?" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:722 +msgid "Select all" +msgstr "Mindet kijelöl" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:726 +msgid "Sort by" +msgstr "Rendezés" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:738 +msgid "View" +msgstr "Nézet" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:739 +msgid "Small icons" +msgstr "Kis ikonok" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:740 +msgid "Big icons" +msgstr "Nagy ikonok" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:741 +msgid "Details" +msgstr "Részletek" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:743 +msgid "Rows" +msgstr "Sorok" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:744 +msgid "Columns" +msgstr "Oszlopo" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:747 +msgid "Preview images" +msgstr "Előnézeti képek" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:749 +msgid "Normal images" +msgstr "Normál méretű képek" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:750 +msgid "Medium images" +msgstr "Közepes képek" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:751 +msgid "Giant images" +msgstr "Nagy képek" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:111 +msgid "&Replace" +msgstr "&Csere" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:112 +msgid "Re&place All" +msgstr "Cserélje &mindet" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:121 +msgid "S&earch for:" +msgstr "Keresse e&zt" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:129 +msgid "Replace &with:" +msgstr "Csere e&rre:" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:138 +msgid "Ex&act" +msgstr "&Teljes" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:139 +msgid "&Ignore Case" +msgstr "&Nagybetűérzékeny" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:140 +msgid "E&xpression" +msgstr "Ki&fejezés" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:141 +msgid "&Backward" +msgstr "&Visszafelé" + +#: ../../../fox-1.6.37/src/FXSearchDialog.cpp:71 +msgid "&Search" +msgstr "&Keresés" + +#: ../../../fox-1.6.37/src/FXStatusLine.cpp:82 +msgid "Ready." +msgstr "Ok." + +#~ msgid "Old Name" +#~ msgstr "Régi név" + +#~ msgid "New Name" +#~ msgstr "Új név" + +#~ msgid "Pitch:" +#~ msgstr "Szélesség:" + +#~ msgid "Any" +#~ msgstr "Bármilyen" + +#~ msgid "Fixed" +#~ msgstr "Fix" + +#~ msgid "Variable" +#~ msgstr "Változó" + +#, fuzzy +#~ msgid " Type:" +#~ msgstr "Típus" + +#, fuzzy +#~ msgid "Scalable" +#~ msgstr "Átméretezhető:" + +#, fuzzy +#~ msgid "Size:" +#~ msgstr "Méret" + +#, fuzzy +#~ msgid "Family:" +#~ msgstr "&Család" + +#~ msgid "Always Show Remote" +#~ msgstr "Mindig látszódjon a mini lejátszó" + +#~ msgid "Source\tActive Source Color" +#~ msgstr "Forrás\tAktív forrás színe" + +#~ msgid "About" +#~ msgstr "A programról" + +#~ msgid "" +#~ "An incompatible version of SQLite (%s) is being used.\n" +#~ "Goggles Music Manager requires at least SQLite 3.3.8.\n" +#~ "Please upgrade your SQLite installation." +#~ msgstr "" +#~ "Nem támogatott SQLite verziót (%s) találtam.\n" +#~ "A Goggle Music Manager legalább 3.3.8-as SQLite-t igényel.\n" +#~ "Kérem frissítse az SQLite-ot!" + +#~ msgid "" +#~ "This version of SQLite (%s) is broken.\n" +#~ "Please upgrade your SQLite installation to at least 3.6.3." +#~ msgstr "" +#~ "Az SQLite ezen verziója(%s) rossz.\n" +#~ "Kérem frissítsen legalább SQLite 3.6.3-ra." + +#~ msgid "" +#~ "&Join GMM on last.fm…\tJoin the Goggles Music Manager group on last.fm…" +#~ "\tJoin the Goggles Music Manager group on last.fm…" +#~ msgstr "" +#~ "&Csatlakozás GMM csoporthoz a last.fm-en…\tCsatlakozás GMM csoporthoz a " +#~ "last.fm-en…\tCsatlakozás GMM csoporthoz a last.fm-en…" + +#~ msgid "Font" +#~ msgstr "Betűtípus" + +#~ msgid "Theme Directory:" +#~ msgstr "Téma könyvtár:" + +#~ msgid "Select Theme Directory" +#~ msgstr "Válassza ki a téma könyvtárát" + +#~ msgid "thin" +#~ msgstr "vékony" + +#~ msgid "normal" +#~ msgstr "normál" + +#~ msgid "bold" +#~ msgstr "félkövér" + +#~ msgid "regular" +#~ msgstr "általános" + +#~ msgid "&Weight:" +#~ msgstr "&Vastagság" + +#~ msgid "&Style:" +#~ msgstr "&Stílus" + +#~ msgid "Si&ze:" +#~ msgstr "&Méret:" + +#~ msgid "Character Set:" +#~ msgstr "Karakter kód:" + +#~ msgid "West European" +#~ msgstr "Nyugat Európai" + +#~ msgid "East European" +#~ msgstr "Kelet-Európai" + +#~ msgid "South European" +#~ msgstr "Dél-Európai" + +#~ msgid "North European" +#~ msgstr "Észak-Európai" + +#~ msgid "Cyrillic" +#~ msgstr "Cirill" + +#~ msgid "Arabic" +#~ msgstr "Arab" + +#~ msgid "Greek" +#~ msgstr "Görög" + +#~ msgid "Hebrew" +#~ msgstr "Héber" + +#~ msgid "Turkish" +#~ msgstr "Török" + +#~ msgid "Nordic" +#~ msgstr "Északi" + +#~ msgid "Thai" +#~ msgstr "Thai" + +#~ msgid "Baltic" +#~ msgstr "Balti" + +#~ msgid "Celtic" +#~ msgstr "Kelta" + +#~ msgid "Russian" +#~ msgstr "Orosz" + +#~ msgid "Central European (cp1250)" +#~ msgstr "Közép-Európai (cp1250)" + +#~ msgid "Russian (cp1251)" +#~ msgstr "Orosz (cp1251)" + +#~ msgid "Latin1 (cp1252)" +#~ msgstr "Latin1 (cp1252)" + +#~ msgid "Greek (cp1253)" +#~ msgstr "Görög (cp1253)" + +#~ msgid "Turkish (cp1254)" +#~ msgstr "Török (cp1254)" + +#~ msgid "Hebrew (cp1255)" +#~ msgstr "Héber (cp1255)" + +#~ msgid "Arabic (cp1256)" +#~ msgstr "Arab (cp1256)" + +#~ msgid "Baltic (cp1257)" +#~ msgstr "Balti (cp1257)" + +#~ msgid "Vietnam (cp1258)" +#~ msgstr "Vietnámi (cp1258)" + +#~ msgid "Thai (cp874)" +#~ msgstr "Thai (cp874)" + +#~ msgid "UNICODE" +#~ msgstr "UNICODE" + +#~ msgid "Set Width:" +#~ msgstr "Betűköz/Szélesség:" + +#~ msgid "All Fonts:" +#~ msgstr "Minden betű:" + +#~ msgid "Preview:" +#~ msgstr "Előnézeti képek:" diff --git a/po/pt.mo b/po/pt.mo new file mode 100644 index 0000000000000000000000000000000000000000..80363c34837e78e0ced5a5c0084e1033d65ac13c GIT binary patch literal 38809 zcmcJY37lL-wg0cmk{K4+WEHqX(d>Qg9613w$rQ19%sB5O_B@4}2Kh72JtRwg(Rcw*wCb zw*~W{zI!C7dRK$0_w^wE1)tzg9()8m0NjIs`t=g7#o%o46j1pqp!(GUUjSYOif*^~_>Y4s_haxt z@Ig@RQKv+g9X##@s^2dHm4BX(KLS*J$NTg_A6@|}-)TPnY*76wfIET{KK*>~6vA%+ zj{)xk)$Umf-1nXjK9BGVJnjP?On85vz7$kDPx0aPp!$Ed55EpnJ1zp1?`=MO4XAcs z5AFiq461*h_32*%48}RY6Mj872>t}rcf%vycjtrp&H|4M!9|2ufU4*1;NIX>;7;Ie;LhOZ zefV3T#`k_u?f9in{|z|nIYIC@Q2pNaD96W#dprkJ{qtw z{{ij;?g&$Bp5(yig9D)W;8alUJrfjN3O;@u+?{aU$6o~MyKnJ$IVk#H4XXV6Kz;89 zQ1k09a1ZdipuY1csCNDyJP!PePe1x)u01P2l{*X6Je>sfy?23DKd5?c05v~u2X_U( z1FD@5gODtE3OpE`4-+f@TJTWt98mRK0;*k?gQCL?U~^f<_YhP)M}zwALQv(F`*0ale>VE?Ye3a^F{tle0ji(Zg5sMG zf}-aqeE42a{d)jZy-$FuZ+8Y!{I@Ttex2iS3{?ASp!$6QD0*EAs@`ir>D9<@*B2 zln%ZHz61=8cjI>?sQ9x$_3Q1R+Iuyqar+>s`acG$KU3g5@Y_E8XHfNTdxGPO-9eQ* z37i9-4yxZ5fNKB0fa>R^KK*^5`uP=5{r)DXe7^(LpFe@yFi*E#==#%pqN{H|Q0WJO zzXbE3zVm_spAK3&D0<{U^Yg6iM3pz8S$sPEkcif(s<;;V1_@Q*;X>k&}xd>mB$ zPk@Jle*u+02UCbnc~I#`gBrI1Q2kip(<4ydKiA`VpuW2qRKANmzT3y&2CgRl^PuY8 zd5OclL49{NsCFFU!|OoRcMiA@SO&KNF9P=j-vaInUJuR#KL?7gkAgYyNl^9ex75*V z87TUk?!!fJ8^SGc4tPGOe!d@6`I~+CyWo1lKLei!zI4#_`xtOWLl0@aT- z;C|qGa5mTg#h>p4_5GVbegB7`%Kscx`QL&n_YY8X-uEO&k0Zes5q>$iJ$L~q`dtL7 z{N*5`G`P;k-w)0r{3xjOCw%y6P;z(Hau?qZ<_Iqbw*_mU+P@Lh_`M3$IJ^n$0j~hJ z1K$g(y&nWc?~j4f6TbxNdw&9@mu-Kt>-YYk@*M%@!G+*KU?ou;d{VC z!M}rj;JlTt-RnWsw*j05z7gyN-wGZAUInV$mq4}Sx1idw=PE~s6G63qwZ|&BBjLA$ zD*ql(q?gU;8s{EDU$>6o%67W}`zLy_z^ZHnD4&gPReQKct40J3U*o(1WfJVR8Z}`3Oo$_FerNb5bOc( z2StZRzyrYFgX+&N!!CXwQ0iQvQFzk@G6&GqYP zQ1s|o=kQqYBEl1(`u`+Y2LJ5CqXEdO z6VyDs54;zA0@Qatd%ElY-JtsM9S~I|cn}o5UwDR_H^+jKZ#8f(cm=3&|17BdKlZrC z%U%2sD84C!%Kr`@{v0U!KjPzeIn(vy7*O@C29E?cfO~@P2i4zCfNI}YeE9pI_~0Q> z34!^ z$EQJ+|GH1V4-|j=(Bogg-3cFXw!;OWzB34_-9wnp-5&o4ioS0!a>n} z=VhSiu+WE30`=Y19@qQ$SAd!)qoC;bE>PomJ*fQmfTH`qf$Gnbpz`eyJ9+g2Q2koy z!-JsaNevX=yxQZNLDlz8Q01=$#YZ3U>7ViO_kgPJyP)#_#E1U_6#qR2if{e|s-B%j zo&4JiJf3h7RKKqWm3}k0EqE6we)tTiary$Nc0LJe{DLu;Z)Y$hxCht^z7$k_CxXhi z98|gW9t)u4L>1f~ybx5mOF_-UcY)%&DNy|S5UBe82yO>H4Ql>uH}1+G04hEQDt{j+ zIu3y9&nlmOI=BttSAg4tMISDMuxfA~xCH!;Pv5iT>OBD5mH0WJ=y^0Kx-A1|ffs@r z@3(>a{vDv`{Z)_mgDU?7D7rrPT-V=O;118B445PSzzuHw9|Vsj+yvFm+rSgRPl7$* zQy!mFcHi3pRDTZy4+Q6fv%wXh=wAiZ?$>}Se<`SXuL66)_ksI?H-T#3S3&jbn;w4v zioOqcd<5Ku@PC5(&fh`xKb&y&EdiB(HK=y41J(a4J>CFHetZf%82llq@BRZ+ySAyg zdUgco65b0G-=FAl7+gkp6g&>R6;%Ho2i5<-g6ijfRmW#@K=tof@E~wKsP9dJJA$tT z)!w&);>#;Q$*~(jjpr{wn?IoH->K%pec%p+7lF#R%Eym@I}zUG(=P-?&&z!H8c_ZG zFsOFi3LXZ2%%}eX+?VhZpy;&2c}^dl3#wm(puRf<9s-^P?hIZGs@`|{^lL%2<0er3 z{3Iy;{|c!2^9^tx@F95#GJp!uTzXw(S)8O;LU79XE1l8~PpxUz@ z6n#g)L%|K8)|sn7@y~6b+WQkw^*s!#|4)MRz#Utzo}<9|ga<&;u>vapM(}CyVsH=P zMwk8`j~@j8O8iGbjps);x$oTyiY}i7Rqh+$Zs5;Bjl+L|`p#3}?%>lNci+s~O88(< zc9d>GsdJeWmIbXy3j{ilHX&Sp^Ue+wx3ya(JL{17O5PJ!Z!?}2K^ zgP^|m7^r^y1r*(6R2P5sf-1icsB%YxN?+pBSAlBB+2Gz_3DkGr0E&;^;_-4&-@6)A z`>q4`0q+1`0DcoZ1AGY7_#XEv_x)v{zPAokJ6;K@{tJBk#o)e#F9Sv2kAiB)?H<1Y zE+qUgsC+MawVUTBfG;CF32Gef0L6b_1x3g2gZj>+;Bnv+py<)}8pp@Sf$B#YJR5us zsPEqos^1TS`rf0U`u*GsTz$Qu`m;N@AGkj#zCOms7eVn&1>6&SqmRD^6#YNw@pe$( z`8=p``wA#IbuXy?Jnr#nQ1smWwQjvR06dcLQc(HN2gO&{fU5riP~U$XG(LEp;;OxF9Jp1`Jm`q0wpIl zgDUqCkGFuL_Z^_|F{paJ2Z|0q_vyd%=}&>j6Tkf%T)Cy7=)DdU-_?EmTfsiU?*-Mr zdp-UN)V$dKjZV+o2RwlALQvy%4tO;9X7C{JZcz0+>~YtBarTjg;DN+Xg8Kf|;C%2d zQ28GMj{$eN(8Zq&_7mRZ!*_t<)1QNC_udz|b}j%lK0~1B@N!V~j(WTT%n|+~I2U{r z6#aI&*!AZikIO)f-vp@fIv>=yd=wNP-34ksd>z~l{3)pKJ?!y!pvwOh6ras{lPkA7 zC_XwERJ)G@HE!#`7lEUo+H;AIe>bT9+zhS&?*dnX&waDYHw0=tB2e^ifU5smk9UFx z6TSzW13m$&-Fsf*=r<2k`UtoacnNp{_%2ZL^he<7;O~6=NpErb;l&;w1=ap#Z*}~+ z9^9Vr#i021?O+dhEvWJS2sjJ82UI`51FHQGfui4&py~@Qb>Uq=&4U+$F9gp34*@rV z>gRPHKMv|U_k!DkKLGW;`@uuOzk+%2;J3N)S_dv6d^V`>T@R`qcYvbLmqE4jN1*8O zAov(~;M-mQX8o&MFLwbCC%zv<6b2hWm3s`Vg1-mVuIMsXZUZR(X?VQY$G;ogk@ydQ zYX5CM{^Owf@p*6__yG7*aMtBc4m<+xLipF9`tes#{oUaTH~)?XUrhK6Q2ccPsPyZ> z+2DPk=Iif4(RJQC-2P=PsCn4}Mfb}*-U_PxyXM%IV%|3h`*h~2H;11x|LCwP- zfX@Se3hH|gf}-c2efrbjo`ko%%H`W1R67m=H6ITFMeik`=voBTz7{CHzR=^PpuTe@ z_#E(h+;?%k1VqOta3SuOgpUM8>+|q$117&+3Ezc#5U1Z?9Rw=~KZ1V}ybe@*lV2Ym z@zvKzdoympCoBXXA^drqe&^xN#{X*EnYb_D{zTmIAR;d~1=MdRkMh3`|3APpz#rkh zj(-vEPx#*t{tTxdqB;04;s!0`{`{Hn?{VV@{`a`(;3UEq;%+A# z<9?32nDEIs{r($#EAD+RX!7Ufgs;V&h)aG?;kkqGe}T{O@yFqR7w(g|ZxZ)za1{3# z{@p*EJO#6obH4}TWi7N_~&z@3asejiLc9$)WqCHTDOy1*MKTf`0fw7-A{ z5!P=GI0^pNLhLUI--7=D+$A{iuzv3){y%WqgYSq-ekOB#7!hc);)OmeJ>t)}(|qO$ z;#=PTe4ppT9{&?u>iy55-8bPsmb~A=U5KmWBI4cv{y*?@xC&0cPvKUW&#hVV?&s6w z{}JwC!e0ZQmx))}Bn{_yMp7H-&#s zoaBOj58;0=_&E3q+?R0rz0&?-67u1F!3uGo1aHB;6u)HKn9uhi@Z-c^0ZPW4fd6{j zy*^#=Q^NYq0vF>X6O-Rt@JQC(O#DIMrxnKkThbK zzd!!3;J;jv{FXWh))4n|pXaB<-H3mg4~HI=b!Eo?EBt@NEhhfQxPQaH7x)RBeqY6n z5_dK3ZMaY2erGA@!}vdszX5KCdpqty!t21xaXSA`elN!J8p1pK2(7_;5q<$KkNY+5 z#keKJUyHj0zkdIY`y%dhxCJ=Ws;Ncbz@!QeeU?O3pe`@0V--v#(zg4-E)BjJ)ydkTDj@XK*0 z;r<)F8r-@=`an~lri zzKZ*aPp{&C82>6CjtNhAzn~wtEp9v9-8lUYB+otIzk>RG7PkcdOMUnU7VGrAV~9Hn zcN=jZ1+M{b1MdPa#$AQ~Qt)NqZ*jNc|0Zq~{^WNbp8tnCoVcC9V{l)>CBJKkKa9XH zlL+wnxI=M&z49e>7s)x z!QF^E6I_nF7ymE7zvJG6zvc5kM0gMHe+~Y>;q-eGajWI&87Q9HYBa-j)pBbh?hFMz zi>lQP6H$Fbqi0>IQEG-GJ{%XrVyPY%n$>zKZsf^08Lo;Z;-F`7x!Q=s!BV*y*E<3v z)#Iochn09!T6nT+B=s$xj4JcPmC;ySX-4zIGvac&x+&;cRt=lgaJ)JZXR25f6*kO_ zrV^KXQ?C`%@70(Zcqh_lfHA_`Gx-KfWAl^`ko)oI(Y8`($f~N82X1KOa2VJj4HF0r%xU3$>d}@(>X-zyqzk;4MaU*U9J;RX#!{gOW z;i7tVQ-iU~27FR6ZbqeYqi0E}QB&(&IKvB*VY$?3LRDI4<+F5{8PkO_R;pB}Rx)4l zj5Zl=DmBN$kh-*!-TpN%2!B(+U4RR6*o1%IVAM8%Nf<8L^1}NqizI89RKIJ0vJ^aaT%nI^;GmVdJtqsmwuN@$M2 zGCp1ztCi8xSgW4KSucof);X#qxrRtoBNz9`9}dT5q%2d+gg}EFCPcwj(`7up2k*>$ zOOlBYMVOm0p0+|ptR$}Qb2iU&DKFO%Wql7DVvo;($3xGb*3^@!$~2qFEX zxLo5eVz`Ec4sy%KDphrfp(HTjDm=Mbs)Wl{u0)=O<*3ojk516po*s*Dj>qA$>e!g% zKz>8b;IxB(~c1Ea^-DtuamLkC;-~h= z53|N$RI8Os1rz8DG$I#5c`@rT94JG?HL-+*khT__M?Am0(P+iS4sLjcVY0$_kQuyiFB8N$blXZBKj}Di{Dq5>XnKW&Zr}3`x4JF2I*v4FcQ)X4bO)n=H*On&YIysSw z?r)fk?C@lz5aiZIWBidI3v%m9@uncR9&TZk&Yc0J78Z_+UOjSxlUo{;Fw(3pgv{zn zwHd|}wdQ0Pmzn$sHp?)Q?_F4M^1`H@MN(k2U7$A@2$w8kwR3{OL_$3-=0(|X6!n6c z)d=hHd99K}Mc9ZN4MuW=5)ll_mm=n4F&_-%YBlJq86FHsa=X9bfXQngSj-YxZ@56C zhn2R`kQz{Mle^*Q6;|s-mdj^M^;PtP<(`4^rf5=)jEcrL&1g*DC`-y)Q`#YGOd4bw z(ge$zdQIvLlvUYexO6jX4H+t7sZyv;)Zj%)pShzg(Pdt!-aw78D(MO}qGmM0lnpmU z4OpgCDdv0EO6s;6t*Bg{498*VxY7tWjicR6R$EM}NfCfSu8h?qG#>&{SS>?36$DT0)x3ovxY_vi98nhe)Y*z{qcCt@THk*dvu=H-25P9Cd5hL<%J zWknStjM30KDx^Opw9)~FT+MDs-paEnLUyPFNL4gZ)*)0SnzQg(v#NS&bN#)nUR#x0 z+$+^YbxQ~E@W13E#}P16vso{Vw3^}vEqUP*)WAXRQbTI>~29?Q5va7^~r@kGTHj@1>538M%AJt zJCmvwwHlDrB{DKi;wDO!(nRTeM3STPWMIUk3a2Q56?P7dxM;qTHUo`t0$~aBQyn@9 zvyZ(`ztTGu2gOv-4K$(G#G}g1*Qe_bQKL~Ulo&2#jYPF;3B4{P>s^;ZmXr10=5A*` z+vM11Y&wLa_38xE)Gd29IeL5PYZ?<;-h7^~xKUlj?k`nHlE@)HSCVkY7SXBwS~L5p z%`Nj_LrkS@Vt=qC5oC`0JA?h#F*&t>FS}yYt>QBouLN7iPDH>%#yfKsF!LQH~hAt3Q~gbIln~7 zQ_vqVe5gOERs%cxt;k4J50>UOONdFZ2h!-maI+`eULDWNU2)^;Jw z+k)3B&}>pN*wd5x1{d^nFOv_Jve*=9T|d6DQY8+Sp2zwD+tawjr8 zu1V)~31LdeOx%i6#kT92D0b%43T_&w5}1#UQ)Agp;KOR=7R?HlZqBjA01D+6OBWT% zMByMrI;H{#MNbRMVJK0f)Yk~LNEW5WvZ9K%NgZ@#bB&x#U#CSU>vt0J>$eZuqMhVi z88M1p#3XSkk0lW{K#e5iC$LR9w^xCsi?KG`ZrMyFCSzh+eKO}=5o?WG{eG{nVS1nRM5C?7 zQL|uZ)v{z8p6^|bvZ!TUlyAs7WSW{P$DY)6`{w)6P(8irc7DdU^WpMQk^NwVaoPm6 zjktOvE|1O+Cs6)+H7aPLYB|%n#;hJk)ulo7oSC^PhV3*wrAmWw7mv_S)e$x(VUUS@ zuq>*P6HN~hDzi`Ers1-ZbivXDd(KACy(?0SQJ4JMVT`ACBx;IRCdBivyg^TiR7SGsjHhE&M+n)M^@YW$c_Kdd_SA7(+% zW!D%+&2YTgto0vu7@|1l3srX3c?f%0E&0sEM6gU->)<5x<;lR?ERBo=$}l{pO92vD z3ZHiOZ8i@uS@s8xt^97kfK=puyY2jGlZjl6s>Md&_KB>f+8CsqySm56JN3$B(x_!& z^dLYY470hCs?v4KCnjNAqkNoAX~}(!wA8tzu(Sy$JADh-?HKonaVA+Py&{+ntS^`> zE)6a2?jw?5nf1~>@7wJbS_EqW0(30F1koj!=jrrc4zHF9!713athHt~Z3HW#Iz}T$ zx$H&|XZH(LA z5GxAms#Fbcn(xO`VYLrd#>Eo5Xa~u>S{YYbJ^B-7b`tH#wN&T1Z6zASXo=~I@)iea zsKG{-nGY?fQf8%|)fL1+ooV0RJ-hv>rlxg!oM~%waKhJ_r#|N85_YZ5XLPc-(`qhe zE9s>&qF2WTp)(X_*dthl&<|F{O*?`Rx6y2@2e$W;BHYO*sj8+Vad;?w(|F7)&ygLT zh`)_v^3?5e8oOr$-Akp}I#k1F$?FwJ%Vi&2Ya5Av^QY^e$YMq|{dxAo3*~ZFJS&wo z=$-9Kqtv>C4V$`Do)}&l^1Au#6pS!D`zSu!yftRS;lJX?QQm=yK1voq+%{!CX}MYF=s z&Qn`4(?GWHU`?EZQi$yVtleuQr`=RAuf(@SVy8cI zM$8 z+4i`7XV>0uA={R}Ez^-d0)6%h-R9NuJ#&rN_&0QGc?T7zWoV8|flih8qM3Y(Zjigw zm)Y_@Q^Tico6&B`IcyyXUmNeak9s{XnG@48&NS*}o3VvLogCr0X{++4?N)Bv$4;9$ z#7W9#VkO}?-{L?f97sKVi!xp(N!HSL%hm9fOdU-~z0jvzaVErCQ#f3S!;%v1nUdyZ z6?U2CCz+B%vd%iKK9@7U<(wwNavH>bHO7LC8iI{&r!FFJUcz|_8$E=3lAeiknOp&v zJ{c~HN|-Cx#0@s62uNv!5eL{9@1zoji^pS@G|n0)*wvwAt6rVi#0iFe*%Li4(Z>QN6<85g}#BZ6Unht2$;B z^-&Pr=ZuRzE)JbNqaF=o=F~dut=bk$Ejq(-U56Aj!`UlCo#Yu2(jy5i8vmn%yre1} z@oimI#hIsp=uheSK8@pa(won`y+-V;Xk#OP2cvBjY!ujso0%l9T%5l)%G%jA?ErOn zJmS0ztBt_xAa=_|$LjEoKqDMtHPPiR zT~k<@)SF51b!y+hpe({hZDn26{upOfwaI?Z9UC-y+@49}mqub{K`n4+m1d@9-^7&) zjP*%nleSZKBOwVj2`zHyDv+~n=>|{t95d?R zgt3Aqq7r@(W>hjW3$?UHN6Y@|!I~r(vft(EUAft}#%Wg8C@anhb&+K<;Vz8o!7vB- zAs1eliNXWXiNwREGt&<;r5NFKqvm8-*vda$Yg>}4dddShR>aJgA!Z+ zyA#Y|uU*?Z;t!%LD6Se0(~Ob~Qo8B94BW|iiN;NNG44s&=wD8L7=c->0Lqgh3-b61v|?TC4;7B=wzU~7r6i3P$cFkGhJfSwa!E%4bCR9 z0Eg-Mi=D6HQBuGwnchuVhXx9F(sD| zxH!t845oT*vb2$kdu_YLp2As}xT)5p9!{urDWJg0!yX%YH=EU4ZMC`5uGbmUYw3QH;o;;vPs5qt zwj-6&fc_v_cQ(y8qL1Jm8rqHRkJ%DI?gq_6LK?K@5+^Ykrj?V z+=_I6VR2pIfa=w1xml{QI2ih6Z!Pn1ZB^$pDo8HLUiX(RtQfmTe^@dK8*~i}&T_Ws zwXM23O0;VE9vz2_(n&gk1W}x<>E4jw z^V{ttS%Ba+%LHxg`pamkjuFz0A(>Q;(fYrrGgv#xzlL*KnQvt@DHl#vsrH$6LDvMu zC3gBA!p!|#7;W_S${J#~ZcN~CeBngP!XQ%?3k;l^8u3&vlkE0E?aH^RPBesODbn$8 z$KhN)Q!j@#h|YwVvi(a!q}$GR`^r%Z@rjjh#ILcLO3Jc$|C9Zez2ToN3nASE?9yH* zLcCgFdop#*@3k^*Px56pwa=Jw%#K%|y+SzF?t@IvnI*pew&Uby&S-K+B<^UsV*9xT zrHdZ@a~jW-*5*NsU~ZGB>Ka)dbk z%*x&1oPZ0`quQrL++!ITlQzjs1-<~M&yc62&xjixlQ4@rG2w!j^e;H7@9?7zKb(wx?0C6L-ISS$4cJ_kK;Xn`D)E}J5HcnWvs

lJf~u`Cx)HZi7qdumaGWGY<=&uYK<|0Du)fj1=;;UsJQTZ7Yw@_5~QtKSTl*g;xi)S+M>Z3JM!o)w3?LpTT zp)AFaiKjWM3tSd6q7=qsRN_X@s;LiD?ZN^^=uAb8!`S1xEIePJP!-y)txn7j8}|5T z>ITZqN13)4KT9ztmG!^~o04AUpi&lF=hL{+sI)oaxN59b;?!cxDP0P2$-x>uvrv-4 zKY5A4DTY`yB<8Zm{4hGdRaRYECS6O^pk;M#j)E%@zv?D75}GYm>Jrt?rbj~bs?o#k zWKlW^bBj1bsdoO9Vri{y*6K>nq&-u&APF}Ao+4(Pp8$2Waf+{2>e%NhYnVN=L1X;uK#iO zQggr?!xp7XHRf8xRPud{8793l@Q;e9d4Q)gd>SE`OXP7+JI+T1gKW)4l1|;JTzhj+Cl+)WaQ*Omsaa>&iT0{Ajz16`jQ>?uazs&bgH! zeYr#-zYK)UR}a+`&FsSLu%gC~B6>DP=a*Pz(SjA@^Nq~TK6~8v6?-qRC9r@)?r+XV^_~41~D~je<9REVh(xR>jQXXRjV$81gZ5 zitg(ie`VvmB<+m!ayAp0#yG)!^3-)AGy*>utd_6}p(8MDVEh&xl7&*ebyF*OG$nCb z&iA@qnyj6uM352<3~qPq;n#~W5_L(^{8WYLOU9}wSEVsqo!wg~QIEG!rsk>z?4~Gz zjnMP>^rp;Z6m*LROH?|nb&Vw|C^v?qhI+<Dh6F227L{m;?NC@W>_Cbw({wiNjZCb;gA&%s zP&*9+@#9n;G?&;G9ELqrg4zm??Kh47Zm=}TYFt>OV>~6(B$(|srL&XwRf%MROwjw> zNO}vao>!SfVI4;^={Yl*7@4|pBCK)X7Phz~DDiHS+sIxlx!Kl!ZbhnBv;uD1`pM6` zG@3(o&!kW3@TB1NDK9yi~9$oOUiM{1z-xX$u?zAS~s5A^)_;*>VM^C6s3A9n| zwteRfmbAij@UrvNr%l`N+y08>8C6d6PPc)Fkb|g_M&7_S{_fF_*rG!ux!E~Awjbe_ zvT$`oqD-~h>`3~_b!lF6G4%DR8~ci;RWKr0+n5{KxGG;xWLnowEo`S-xkv^Ro+9}| zBGf=C>r%3Ii{rXowN-!;ewHt>qf29TR0yv@NIOoH@hUMbEajIG{eUbFD!l*p&4}W%6_R@cAcU zk=^U}kHV;Z3l%K3E7)GYOeJmd6inhb>u*02bAnlt+c5|tpkQpGlj zn3m|u_w8gSpQndmA?=gBdBKEGhs7qX-4*xQlMU~)=YRGbt^vNQP7HQ!fib8(CQD}C zQVX$=CW7BZ#P+t#(Oa^EckcW{7=x(xtZA@VGXCPWR6&3O+WYrw#We;Rg%8Ssy(_CD zT~?<99-fGkSeCrN8GsHA{G>^1$^rxXZ220pmQ{F#3OXQQY1*Au0+^X8-5BuCuHUEejC#X4%~b^06zNG(aE)>xgVwIm-7 z>smnxqflY$CTYXIT?|N7O`YG1dd~$Vr7))xx;I!W?PXfJ-Lee<^vts*w;j0?$^Io; zu*{pQ*d3dCxH|2DyA`t1|4S&g{euzB@Y@Qbkve+SwDmLJJN0I?88*n;v{<93Z8p{Q4Od<5Nrdh~LOl}cP4hq;SPGF~VnzwtFCPALD z0wtCqbXJ68w6Pu~FV)D%wznCjyq-K&BVoc>X>3z0HnxmT7O{a!G+F;VER(+-(_Q!` zCaLaeBk5q-^e%p^)Z zGtc?9;@EJ|3>!&C**LSujkfxqz2%DeHpV2=As8Y|kXSh?wS z_@^h>fi=Wg2q5=T&*aHWiD;{={{51&xYWcw=Ho80yw`L6WnYu3c&YGRn2pTtD+d@Z zPFL9ZI8(T%*+60$)#1i41mx67Pw=E%@z);f`M;efiY|RC;=C@xfpVgj;B~s)Q1|o8 zc8RX(T_ZDfr?lW-*FYWMO@p>S?Ml0*8{w8cUI^&Ua~G#b zB)kdd)-x&^QmAO^owH_00%wP=otj8Q_3X4YE0DXs8c52xp{tBW8>@BB|F~F!nVju_ zEKb}}g3C=IwtJ$yHyz-%s1xFF1bYT9jkx2Sf8u)+g_m0Wm>D}tjN_NnMa&S z0};Zpg?l2%4pWS1e3*D=e_NA2f@3OuPfl(X|BP6u?> zWb@OAhS{A>#_-gg5YYMAonRw{4{Rr&ybKg$yRE(3v?j0J?6?6TZ{rfHCKaX? zWk+U4v%0a|)^%ulNy+8*TgB7>)iEaNB$%Fg3vJAr$dmp|FWQv@w zn!;Jv05>b!XCm1&H@i7FW&7=3Gn&soXY_t2>fC!qMe(4~mM0O&`aqjq6((|AcK4W3 z*{8`NcWr_-MIUu}=etX0K5kl__63*Ey=YXO7U>Vuhg&&+TF+kB4!zncOUxrhb~$W| zS+{0qo-oeN>M$J%mNhA-o^c)Czi!MNKJivk|5Oi_HJzAnB9IT)aXcd~k?!BWCzdx9r zym*`)3~*D%c4<;{F}+OPz?$MUOI;F5c%dVbM+{bme=kx?P@;(XB}!rYkUzV#o1x^F z9m}WqBY})5(wz~sO_BC068dwB$a6Uae{$aeO}Tvv`9D7E*O476tiL7VwJ)gnY;`Wt z^GIZ>wzTb=1JkcS8;pfN|aoehf9n zeZ82(x=9P^p|L>Rks~dbTrA|G5M%0ZM2ha#`Sv$$bCQ-~8tbYK=e$e>1bwCIn#_Aj z_f_$>Ka_6gO^c6UFf*Hglmcc+(y`jznV`1Or9!t{mY4vX@ zf578a9S(RAyrvx=PF?vYEKR?ODXh~fUa*>YBw8@4{( z5zbE?C!=RZxxC5V&9ySBP2I`A`!>;*Oro$}-?1gpzbk~K;(*Ox4Qm^YduGH+nrN+S zn~<}X(sp(?D($ehM#F42)3WoaeTtmis{ZHh$*iVTCb+febBl|niY2j^W5VQp4TlZ( z3=|(bTiqEOHNvm zM;Ud`@7uzSTIF;GR+s9HzQMd|Uo~h~m$ZoMwo%Cnc#D>u|2b0H+Nx_TYIbVv=3j); zKRBy(SBr@rei||SA%3bJ=6O3(ub0q{+O`K@FVd+N@tDz8b4+G!69G1At_X=8F|)^X&N@3qF-l>@sOv!#4tumly#G45J_9br&* zH)po?ozuT&%L##B(%~3|WRNivGI!9>t(tldiUOw%s{6~RdCqqQSxM1JO`k~a_jpqt zT6X*DR(7guDXU3xnnC{=v-ul%CP>_@XF=o15U-T@H`=GJtHBgHWXya?m&9y{CBbNz z>9^@Zpc^T?ZY{AsyVX`==l`VUU@gk2e<+&KFWpPgwtksW$lV6y%iU}2s3y7LIGuvoitIfo8H^V5KX;!;F{vmTN zYv@Wp??bcI*(JD{XFSFc&(vcx9c>+Px}L>m+hFnpxsp83=$_{74c!wH)i%@lq}I$r zDglxjG`dwJvybCMJlPCxyX&OQmf{ie?x|^u%EoGdl>Fs?O zODg<3INJLyY2Vm1MIueGEt5uG>P03N;U}9XrbM`jCEQ=&v^!}g&z#QEKD&3<)hsDJ z7(?79;=W5Vh63Givm(}RHcNKDSUQ!z{$`0z1KOhgGvGGnmbJq$rmfUz?q;H{N433J zZ*{rNjJW>%gVIiEK|^uR{oF1$@Hf-jcg>(w`X~;|Zxy?9#2U@=HK1ECZ5Bcw(8{4! sEFKI8R;=w5!nW+$tu<~x4q00JzR}{rj?BS0#w*C<9*?rLV%rJ+KM8jtZ2$lO literal 0 HcmV?d00001 diff --git a/po/pt.po b/po/pt.po new file mode 100644 index 0000000..8a1390b --- /dev/null +++ b/po/pt.po @@ -0,0 +1,2493 @@ +# Portuguese translation of gogglesmm package +# Copyright (C) YEAR Sander Jansen +# This file is distributed under the same license as the gogglesmm package. +# Sérgio Marques , 2011 +# +msgid "" +msgstr "" +"Project-Id-Version: gogglesmm\n" +"Report-Msgid-Bugs-To: s.jansen@gmail.com\n" +"POT-Creation-Date: 2011-02-09 23:26-0600\n" +"PO-Revision-Date: 2011-11-16 22:22-0000\n" +"Last-Translator: Sérgio Marques \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Poedit-Language: Portuguese\n" +"X-Poedit-Country: Portugal\n" + +#: src/GMAbout.cpp:136 +#: src/GMDatabaseSource.cpp:218 +#: src/GMDatabaseSource.cpp:2215 +#: src/GMPreferencesDialog.cpp:551 +msgid "&Close" +msgstr "Fe&char" + +#: src/GMColumnDialog.cpp:204 +msgid "Configure Columns" +msgstr "Configurar colunas" + +#: src/GMColumnDialog.cpp:207 +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:168 +msgid "&Accept" +msgstr "&Aceitar" + +#: src/GMColumnDialog.cpp:208 +#: src/GMDatabaseSource.cpp:98 +#: src/GMDatabaseSource.cpp:1274 +#: src/GMDatabaseSource.cpp:1704 +#: src/GMDatabaseSource.cpp:1804 +#: src/GMDatabaseSource.cpp:1878 +#: src/GMDatabaseSource.cpp:2121 +#: src/GMDatabaseSource.cpp:2182 +#: src/GMImportDialog.cpp:175 +#: src/GMImportDialog.cpp:563 +#: src/GMPlayListSource.cpp:281 +#: src/GMPlayListSource.cpp:410 +#: src/GMSearch.cpp:572 +#: src/GMStreamSource.cpp:169 +#: src/GMStreamSource.cpp:206 +#: src/GMStreamSource.cpp:271 +#: src/GMWindow.cpp:1198 +#: src/GMWindow.cpp:1247 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:107 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:118 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:123 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:129 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:135 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:142 +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:169 +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:135 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:205 +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:113 +msgid "&Cancel" +msgstr "&Cancelar" + +#: src/GMColumnDialog.cpp:212 +msgid "" +"Choose the order of information to appear\n" +"in the track list." +msgstr "" +"Escolha a ordem das informações\n" +"a exibir na lista de faixas." + +#: src/GMColumnDialog.cpp:218 +msgid "Move Up" +msgstr "Para cima" + +#: src/GMColumnDialog.cpp:219 +msgid "Move Down" +msgstr "Para baixo" + +#: src/GMDatabaseSource.cpp:52 +msgid "Invalid Template" +msgstr "Modelo inválido" + +#: src/GMDatabaseSource.cpp:52 +#, c-format +msgid "" +"The provided template is invalid. The track title %%T needs to be specified.\n" +"Please fix the filename template in the preference panel." +msgstr "" +"O modelo indicado é inválido. Deve indicar %%T como título da faixa.\n" +"Corrija o nome do modelo no painel de preferências." + +#: src/GMDatabaseSource.cpp:72 +#: src/GMTrackDatabase.cpp:193 +msgid "Database Error" +msgstr "Erro na base de dados" + +#: src/GMDatabaseSource.cpp:72 +msgid "Oops. Database Error" +msgstr "Oops. Erro na base de dados" + +#: src/GMDatabaseSource.cpp:87 +msgid "No changes" +msgstr "Sem alterações" + +#: src/GMDatabaseSource.cpp:87 +msgid "Filenames did not require any changes" +msgstr "Os nomes não necessitam de ser alterados" + +#: src/GMDatabaseSource.cpp:94 +msgid "Rename Audio Files?" +msgstr "Mudar nome dos ficheiros?" + +#: src/GMDatabaseSource.cpp:95 +msgid "Renaming Audio Files…" +msgstr "A mudar nome dos ficheiros..." + +#: src/GMDatabaseSource.cpp:95 +msgid "The following audio files are going to be renamed" +msgstr "O nome dos seguintes ficheiros vai ser alterado:" + +#: src/GMDatabaseSource.cpp:97 +msgid "&Rename" +msgstr "Muda&r nome" + +#: src/GMDatabaseSource.cpp:121 +#: src/GMDatabaseSource.cpp:125 +msgid "Unable to rename file" +msgstr "Incapaz de mudar o nome" + +#: src/GMDatabaseSource.cpp:121 +#, c-format +msgid "" +"Unable to rename:\n" +"%s\n" +"\n" +"to:%s\n" +"Continue renaming files?" +msgstr "" +"Incapaz de mudar o nome de:\n" +"%s\n" +"\n" +"para: %s\n" +"Continuar operação?" + +#: src/GMDatabaseSource.cpp:125 +#, c-format +msgid "" +"Unable to rename:\n" +"%s\n" +"\n" +"to:%s" +msgstr "" +"Incapaz de mudar o nome de:\n" +"%s\n" +"\n" +"para: %s" + +#: src/GMDatabaseSource.cpp:160 +#: src/GMImportDialog.cpp:534 +msgid "Filename Template" +msgstr "Nome do ficheiro modelo" + +#: src/GMDatabaseSource.cpp:177 +msgid "" +"Template may contain absolute or relative path, environment variables\n" +"and ~. Relative paths are based on the location of the original file. The\n" +"file extension gets automatically added. The following macros\n" +"may be used:" +msgstr "" +"Os modelos podem conter caminhos absolutos ou relativos, variáveis de\n" +"ambiente e ~. Os caminhos relativos têm como base o local do ficheiro.\n" +"A extensão de ficheiro é adicionada automaticamente. Pode utilizar\n" +"as seguintes macros:" + +#: src/GMDatabaseSource.cpp:178 +msgid "" +"%T - title %A - album name\n" +"%P - album artist name %p - track artist name\n" +"%y - year %d - disc number\n" +"%N - track number (2 digits) %n - track number \n" +"%G - genre" +msgstr "" +"%T - título %A - album name\n" +"%P - artista do álbum %p - artista da faixa\n" +"%y - ano %d - disc number\n" +"%N - n.º da faixa (2 dígitos) %n - n.º da faixa\n" +"%G - género" + +#: src/GMDatabaseSource.cpp:185 +msgid "Conditions may be used as well:" +msgstr "Também pode utilizar estas condições:" + +#: src/GMDatabaseSource.cpp:186 +msgid "" +"?c - display a if c is not empty else display b.\n" +"?c - display c if not empty\n" +msgstr "" +"?c - exibe \"a\" se \"c\" não for vazio, senão exibe \"b\".\n" +"?c - exibe \"c\" se não vazio\n" + +#: src/GMDatabaseSource.cpp:195 +#: src/GMDatabaseSource.cpp:1815 +#: src/GMImportDialog.cpp:546 +msgid "Template:" +msgstr "Modelo:" + +#: src/GMDatabaseSource.cpp:199 +#: src/GMDatabaseSource.cpp:1819 +msgid "Encoding:" +msgstr "Codificação:" + +#: src/GMDatabaseSource.cpp:205 +msgid "Exclude:" +msgstr "Exclusão:" + +#: src/GMDatabaseSource.cpp:209 +#: src/GMDatabaseSource.cpp:1825 +msgid "Options:" +msgstr "Opções:" + +#: src/GMDatabaseSource.cpp:210 +#: src/GMDatabaseSource.cpp:1826 +msgid "Replace spaces with underscores" +msgstr "Substituir espaços por \"underscores\"" + +#: src/GMDatabaseSource.cpp:212 +#: src/GMDatabaseSource.cpp:1828 +msgid "Lower case" +msgstr "Minúsculas" + +#: src/GMDatabaseSource.cpp:214 +#: src/GMDatabaseSource.cpp:1830 +msgid "Lower case extension" +msgstr "Extensão de minúsculas" + +#: src/GMDatabaseSource.cpp:341 +#: src/GMStreamSource.cpp:63 +msgid "No." +msgstr "N.º" + +#: src/GMDatabaseSource.cpp:342 +msgid "Queue" +msgstr "Fila" + +#: src/GMDatabaseSource.cpp:343 +#: src/GMDatabaseSource.cpp:1391 +#: src/GMTrackView.cpp:238 +msgid "Title" +msgstr "Título" + +#: src/GMDatabaseSource.cpp:344 +#: src/GMDatabaseSource.cpp:1396 +#: src/GMTrackView.cpp:239 +msgid "Artist" +msgstr "Artista" + +#: src/GMDatabaseSource.cpp:345 +#: src/GMDatabaseSource.cpp:1397 +msgid "Album Artist" +msgstr "Artista do álbum" + +#: src/GMDatabaseSource.cpp:346 +#: src/GMDatabaseSource.cpp:1405 +#: src/GMPreferencesDialog.cpp:540 +#: src/GMTrackView.cpp:240 +msgid "Album" +msgstr "Álbum" + +#: src/GMDatabaseSource.cpp:347 +#: src/GMDatabaseSource.cpp:1364 +#: src/GMDatabaseSource.cpp:1375 +#: src/GMDatabaseSource.cpp:1381 +msgid "Disc" +msgstr "Disco" + +#: src/GMDatabaseSource.cpp:348 +#: src/GMDatabaseSource.cpp:1408 +#: src/GMStreamSource.cpp:66 +#: src/GMStreamSource.cpp:177 +#: src/GMStreamSource.cpp:216 +#: src/GMTrackView.cpp:241 +msgid "Genre" +msgstr "Género" + +#: src/GMDatabaseSource.cpp:349 +#: src/GMDatabaseSource.cpp:1369 +#: src/GMDatabaseSource.cpp:1387 +msgid "Year" +msgstr "Ano" + +#: src/GMDatabaseSource.cpp:350 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:730 +msgid "Time" +msgstr "Duração" + +#: src/GMDatabaseSource.cpp:490 +msgid "Remove…\tDel\tRemove Genre from Library." +msgstr "Remover…\tDel\tRemover género da coleção." + +#: src/GMDatabaseSource.cpp:496 +#: src/GMDatabaseSource.cpp:505 +#: src/GMPlayListSource.cpp:104 +#: src/GMPlayListSource.cpp:110 +msgid "Copy\tCtrl-C\tCopy associated tracks to the clipboard." +msgstr "Copiar\tCtrl-C\tCopiar faixas relacionadas para a área de transferência" + +#: src/GMDatabaseSource.cpp:499 +#: src/GMDatabaseSource.cpp:508 +msgid "Remove…\tDel\tRemove associated tracks from library." +msgstr "Remover…\tDel\tRemover da coleção as faixas relacionadas" + +#: src/GMDatabaseSource.cpp:513 +#: src/GMPlayListSource.cpp:116 +msgid "Edit…\tF2\tEdit Track Information." +msgstr "Editar…\tF2\tEditar informações da faixa" + +#: src/GMDatabaseSource.cpp:514 +#: src/GMPlayListSource.cpp:117 +msgid "Copy\tCtrl-C\tCopy track(s) to clipboard." +msgstr "Copiar\tCtrl-C\tCopiar faixa(s) para a área de transferência" + +#: src/GMDatabaseSource.cpp:518 +#: src/GMPlayListSource.cpp:120 +msgid "Open Folder Location\t\tOpen Folder Location." +msgstr "Abrir local da pasta\t\tAbrir local da pasta" + +#: src/GMDatabaseSource.cpp:523 +msgid "Remove…\tDel\tRemove track(s) from library." +msgstr "Remover…\tDel\tRemover faixa(s) da coleção" + +#: src/GMDatabaseSource.cpp:537 +msgid "New Play List…\t\tCreate a new play list." +msgstr "Nova lista de reprodução…\t\tCriar nova lista de reprodução" + +#: src/GMDatabaseSource.cpp:539 +#: src/GMPlayListSource.cpp:163 +msgid "Export…" +msgstr "Exportar…" + +#: src/GMDatabaseSource.cpp:540 +msgid "Information…\t\tLibrary Statistics" +msgstr "Informações…\t\tEstatísticas da coleção" + +#: src/GMDatabaseSource.cpp:542 +msgid "Remove All Tracks\t\tRemove all tracks from the library" +msgstr "Remover todas as faixas\t\tRemover todas as faixas da coleção" + +#: src/GMDatabaseSource.cpp:1272 +msgid "Edit Track Information" +msgstr "Editar informações da faixa" + +#: src/GMDatabaseSource.cpp:1275 +#: src/GMPlayListSource.cpp:409 +#: src/GMStreamSource.cpp:205 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:128 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:143 +msgid "&Save" +msgstr "&Gravar" + +#: src/GMDatabaseSource.cpp:1315 +msgid "&Tag" +msgstr "De&talhes" + +#: src/GMDatabaseSource.cpp:1320 +msgid "&Properties" +msgstr "&Propriedades" + +#: src/GMDatabaseSource.cpp:1325 +#: src/GMImportDialog.cpp:525 +msgid "Filename" +msgstr "Nome do ficheiro" + +#: src/GMDatabaseSource.cpp:1329 +#: ../../../fox-1.6.37/src/FXFileList.cpp:193 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:728 +msgid "Type" +msgstr "Tipo" + +#: src/GMDatabaseSource.cpp:1333 +#: ../../../fox-1.6.37/src/FXFileList.cpp:194 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:729 +msgid "Size" +msgstr "Tamanho" + +#: src/GMDatabaseSource.cpp:1341 +#: src/GMStreamSource.cpp:65 +msgid "Bitrate" +msgstr "Qualidade" + +#: src/GMDatabaseSource.cpp:1345 +msgid "Samplerate" +msgstr "Frequência" + +#: src/GMDatabaseSource.cpp:1349 +msgid "Channels" +msgstr "Canais" + +#: src/GMDatabaseSource.cpp:1358 +#: src/GMPreferencesDialog.cpp:539 +msgid "Track" +msgstr "Faixa" + +#: src/GMDatabaseSource.cpp:1403 +msgid "\tSeparate Artists" +msgstr "" + +#: src/GMDatabaseSource.cpp:1403 +msgid "\tShared Artists" +msgstr "" + +#: src/GMDatabaseSource.cpp:1416 +msgid "Auto track number. Offset:" +msgstr "N.º de faixa automático. Inicia:" + +#: src/GMDatabaseSource.cpp:1425 +msgid "Update Tag in File" +msgstr "Atualizar detalhes no ficheiro" + +#: src/GMDatabaseSource.cpp:1430 +msgid "Update Filename" +msgstr "Atualizar nome de ficheiro" + +#: src/GMDatabaseSource.cpp:1431 +msgid "Set export template…" +msgstr "Definir modelo..." + +#: src/GMDatabaseSource.cpp:1645 +msgid "Update Tags?" +msgstr "Atualizar detalhes?" + +#: src/GMDatabaseSource.cpp:1645 +msgid "" +"No tracks were updated.\n" +"Would you still like to write the tags for the selected tracks?" +msgstr "" +"Nenhuma faixa foi alterada.\n" +"Ainda assim, pretende gravar os detalhes das faixas selecionadas?" + +#: src/GMDatabaseSource.cpp:1700 +msgid "Remove Audio Files?" +msgstr "Remover ficheiros áudio?" + +#: src/GMDatabaseSource.cpp:1701 +msgid "Remove Audio Files..." +msgstr "Remover ficheiros áudio..." + +#: src/GMDatabaseSource.cpp:1701 +msgid "The following audio files are going to be removed" +msgstr "Estes ficheiros áudio vão ser removidos" + +#: src/GMDatabaseSource.cpp:1703 +#: src/GMDatabaseSource.cpp:1877 +#: src/GMPlayListSource.cpp:280 +#: src/GMStreamSource.cpp:270 +msgid "&Remove" +msgstr "&Remover" + +#: src/GMDatabaseSource.cpp:1729 +msgid "Export Main Library" +msgstr "Exportar coleção principal" + +#: src/GMDatabaseSource.cpp:1731 +msgid "Export Play List" +msgstr "Exportar lista de reprodução" + +#: src/GMDatabaseSource.cpp:1744 +msgid "Overwrite File?" +msgstr "Substituir ficheiro?" + +#: src/GMDatabaseSource.cpp:1744 +msgid "File already exists. Would you like to overwrite it?" +msgstr "O ficheiro já existe. Pretende substituir?" + +#: src/GMDatabaseSource.cpp:1784 +msgid "Export Genre" +msgstr "Exportar por género" + +#: src/GMDatabaseSource.cpp:1785 +msgid "Export tracks with genre to destination directory." +msgstr "Exportar faixas deste género para o diretório" + +#: src/GMDatabaseSource.cpp:1787 +msgid "Export Artists" +msgstr "Exportar artistas" + +#: src/GMDatabaseSource.cpp:1788 +msgid "Export tracks from artist to destination directory." +msgstr "Exportar faixas deste artista para o diretório" + +#: src/GMDatabaseSource.cpp:1790 +msgid "Export Albums" +msgstr "Exportar álbuns" + +#: src/GMDatabaseSource.cpp:1791 +msgid "Export tracks from album to destination directory." +msgstr "Exportar faixas deste álbum para o diretório" + +#: src/GMDatabaseSource.cpp:1793 +msgid "Export Tracks" +msgstr "Exportar faixas" + +#: src/GMDatabaseSource.cpp:1794 +msgid "Export tracks to destination directory." +msgstr "Exportar faixas para o diretório" + +#: src/GMDatabaseSource.cpp:1803 +msgid "&Export" +msgstr "&Exportar" + +#: src/GMDatabaseSource.cpp:1853 +#: src/GMPlayListSource.cpp:261 +msgid "Remove Genre?" +msgstr "Remover género?" + +#: src/GMDatabaseSource.cpp:1854 +msgid "Remove tracks with genre from library?" +msgstr "Remover da coleção as faixas deste género?" + +#: src/GMDatabaseSource.cpp:1858 +#: src/GMPlayListSource.cpp:264 +msgid "Remove Artist?" +msgstr "Remover artista?" + +#: src/GMDatabaseSource.cpp:1859 +msgid "Remove tracks from artist from library?" +msgstr "Remover da coleção as faixas deste artista?" + +#: src/GMDatabaseSource.cpp:1863 +#: src/GMPlayListSource.cpp:267 +msgid "Remove Album?" +msgstr "Remover álbum?" + +#: src/GMDatabaseSource.cpp:1864 +msgid "Remove tracks from album from library?" +msgstr "Remover este álbum da coleção?" + +#: src/GMDatabaseSource.cpp:1868 +#: src/GMPlayListSource.cpp:270 +msgid "Remove Track(s)?" +msgstr "Remover faixa(s)?" + +#: src/GMDatabaseSource.cpp:1869 +msgid "Remove track(s) from library?" +msgstr "Remover faixa(s) da coleção?" + +#: src/GMDatabaseSource.cpp:1881 +#: src/GMPlayListSource.cpp:285 +msgid "Remove tracks from disk" +msgstr "Remover faixas do disco rígido" + +#: src/GMDatabaseSource.cpp:1895 +#: src/GMDatabaseSource.cpp:1899 +#: src/GMDatabaseSource.cpp:1903 +#: src/GMDatabaseSource.cpp:1907 +#: src/GMPlayListSource.cpp:308 +#: src/GMStreamSource.cpp:280 +msgid "Library Error" +msgstr "Erro na coleção" + +#: src/GMDatabaseSource.cpp:1895 +msgid "Unable to remove genre from the library" +msgstr "Incapaz de remover o género" + +#: src/GMDatabaseSource.cpp:1899 +msgid "Unable to remove artist from the library" +msgstr "Incapaz de remover o artista" + +#: src/GMDatabaseSource.cpp:1903 +msgid "Unable to remove album from the library" +msgstr "Incapaz de remover o álbum" + +#: src/GMDatabaseSource.cpp:1907 +#: src/GMPlayListSource.cpp:308 +msgid "Unable to remove track from the library." +msgstr "Incapaz de remover a(s) faixa(s)" + +#: src/GMDatabaseSource.cpp:2117 +#: src/GMDatabaseSource.cpp:2118 +msgid "Create Playlist" +msgstr "Criar lista de reprodução" + +#: src/GMDatabaseSource.cpp:2118 +msgid "Specify name of the new playlist" +msgstr "Indique o nome da nova lista" + +#: src/GMDatabaseSource.cpp:2120 +msgid "&Create" +msgstr "C&riar" + +#: src/GMDatabaseSource.cpp:2125 +#: src/GMPlayListSource.cpp:415 +#: ../../../fox-1.6.37/src/FXFileList.cpp:192 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:727 +msgid "Name" +msgstr "Nome" + +#: src/GMDatabaseSource.cpp:2127 +msgid "New Playlist" +msgstr "Nova lista de reprodução" + +#: src/GMDatabaseSource.cpp:2178 +#: src/GMDatabaseSource.cpp:2179 +msgid "Clear Music Library?" +msgstr "Apagar coleção?" + +#: src/GMDatabaseSource.cpp:2179 +msgid "Remove all tracks from the music library?" +msgstr "Remover todas as faixas da coleção?" + +#: src/GMDatabaseSource.cpp:2181 +msgid "&Remove All" +msgstr "&Remover tudo" + +#: src/GMDatabaseSource.cpp:2185 +msgid "Keep play lists" +msgstr "Manter listas de reprodução" + +#: src/GMDatabaseSource.cpp:2212 +#: src/GMDatabaseSource.cpp:2213 +msgid "Music Library Information" +msgstr "Informações da coleção" + +#: src/GMDatabaseSource.cpp:2213 +msgid "You music collection consists of…" +msgstr "A sua coleção possui..." + +#: src/GMDatabaseSource.cpp:2221 +msgid "Tracks:" +msgstr "Faixas:" + +#: src/GMDatabaseSource.cpp:2226 +msgid "Artists:" +msgstr "Artistas:" + +#: src/GMDatabaseSource.cpp:2230 +msgid "Albums:" +msgstr "Álbums:" + +#: src/GMDatabaseSource.cpp:2234 +msgid "Total Time:" +msgstr "Tempo total:" + +#: src/GMEQDialog.cpp:96 +msgid "Equalizer" +msgstr "Equalizador" + +#: src/GMEQDialog.cpp:135 +msgid "Equalizer:" +msgstr "Equalizador:" + +#: src/GMEQDialog.cpp:137 +msgid "\tSave" +msgstr "\tGravar" + +#: src/GMEQDialog.cpp:138 +msgid "\tReset" +msgstr "\tRepor" + +#: src/GMEQDialog.cpp:139 +msgid "\tRemove" +msgstr "\tRemover" + +#: src/GMEQDialog.cpp:172 +msgid "Pre-amp" +msgstr "Amplificador" + +#: src/GMEQDialog.cpp:244 +#: src/GMEQDialog.cpp:254 +msgid "Disabled" +msgstr "Inativo" + +#: src/GMEQDialog.cpp:247 +#: src/GMEQDialog.cpp:299 +msgid "Manual" +msgstr "Manual" + +#: src/GMEQDialog.cpp:314 +msgid "Delete Preset" +msgstr "Eliminar pré-ajuste" + +#: src/GMEQDialog.cpp:314 +#, c-format +msgid "Are you sure you want to delete %s preset?" +msgstr "Tem a certeza de que quer eliminar %s?" + +#: src/GMEQDialog.cpp:334 +msgid "Preset Name" +msgstr "Nome" + +#: src/GMEQDialog.cpp:334 +msgid "Please enter preset name:" +msgstr "Indique o nome do pré-ajuste" + +#: src/GMEQDialog.cpp:338 +msgid "Overwrite Preset" +msgstr "Substituir pré-ajuste" + +#: src/GMEQDialog.cpp:338 +#, c-format +msgid "Preset %s already exists. Would you like to overwrite it?" +msgstr "%s já existe. Pretende substituir o pré-ajuste?" + +#: src/GMFontDialog.cpp:209 +msgid "Ultra Condensed" +msgstr "Ultra condensada" + +#: src/GMFontDialog.cpp:210 +msgid "Extra Condensed" +msgstr "Extra condensada" + +#: src/GMFontDialog.cpp:211 +msgid "Condensed" +msgstr "Condensada" + +#: src/GMFontDialog.cpp:212 +msgid "Semi Condensed" +msgstr "Pouco condensada" + +#: src/GMFontDialog.cpp:214 +msgid "Semi Expanded" +msgstr "Pouco expandida" + +#: src/GMFontDialog.cpp:215 +msgid "Expanded" +msgstr "Expandida" + +#: src/GMFontDialog.cpp:216 +msgid "Extra Expanded" +msgstr "Extra expandida" + +#: src/GMFontDialog.cpp:217 +msgid "Ultra Expanded" +msgstr "Ultra expandida" + +#: src/GMFontDialog.cpp:224 +#: src/GMPreferencesDialog.cpp:1223 +msgid "Thin" +msgstr "Estreita" + +#: src/GMFontDialog.cpp:225 +#: src/GMPreferencesDialog.cpp:1224 +msgid "Extra Light" +msgstr "Extra suave" + +#: src/GMFontDialog.cpp:226 +#: src/GMPreferencesDialog.cpp:1225 +msgid "Light" +msgstr "Suave" + +#: src/GMFontDialog.cpp:228 +#: src/GMPreferencesDialog.cpp:1227 +msgid "Medium" +msgstr "Média" + +#: src/GMFontDialog.cpp:229 +#: src/GMPreferencesDialog.cpp:1228 +msgid "Demibold" +msgstr "Pouco negrito" + +#: src/GMFontDialog.cpp:230 +#: src/GMPreferencesDialog.cpp:1229 +msgid "Bold" +msgstr "Negrito" + +#: src/GMFontDialog.cpp:231 +#: src/GMPreferencesDialog.cpp:1230 +msgid "Extra Bold" +msgstr "Extra negrito" + +#: src/GMFontDialog.cpp:232 +#: src/GMPreferencesDialog.cpp:1231 +msgid "Heavy" +msgstr "Carregada" + +#: src/GMFontDialog.cpp:239 +msgid "Reverse Oblique" +msgstr "Oblíqua invertida" + +#: src/GMFontDialog.cpp:240 +msgid "Reverse Italic" +msgstr "Ítálico invertido" + +#: src/GMFontDialog.cpp:242 +msgid "Italic" +msgstr "Ítalico" + +#: src/GMFontDialog.cpp:243 +msgid "Oblique" +msgstr "Oblíqua" + +#: src/GMFontDialog.cpp:265 +msgid "Normal" +msgstr "Normal" + +#: src/GMImportDialog.cpp:91 +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:137 +msgid "&Directory:" +msgstr "Dire&tório:" + +#: src/GMImportDialog.cpp:166 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:196 +msgid "&File Name:" +msgstr "Nome do &ficheiro:" + +#: src/GMImportDialog.cpp:168 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:102 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:106 +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:134 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:198 +msgid "&OK" +msgstr "&OK" + +#: src/GMImportDialog.cpp:170 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:200 +msgid "File F&ilter:" +msgstr "F&iltro:" + +#: src/GMImportDialog.cpp:174 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:204 +msgid "Read Only" +msgstr "Só leitura" + +#: src/GMImportDialog.cpp:179 +#: src/GMSearch.cpp:565 +#: src/GMSearch.cpp:647 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:208 +msgid "Directory:" +msgstr "Diretório:" + +#: src/GMImportDialog.cpp:186 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:228 +msgid "&Set bookmark\t\tBookmark current directory." +msgstr "&Definir marcadort\tMarcar diretório atual" + +#: src/GMImportDialog.cpp:187 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:229 +msgid "&Clear bookmarks\t\tClear bookmarks." +msgstr "Apagar mar&cadores\t\tApagar marcadores" + +#: src/GMImportDialog.cpp:203 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:244 +msgid "\tGo up one directory\tMove up to higher directory." +msgstr "\tSubir um diretório\tIr para o diretório superior" + +#: src/GMImportDialog.cpp:204 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:245 +msgid "\tGo to home directory\tBack to home directory." +msgstr "\tIr para o diretório pessoal\tIr para o diretório pessoal" + +#: src/GMImportDialog.cpp:205 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:246 +msgid "\tGo to work directory\tBack to working directory." +msgstr "\tIr para o diretório de trabalho\tIr para o diretório de trabalho" + +#: src/GMImportDialog.cpp:206 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:247 +msgid "\tBookmarks\tVisit bookmarked directories." +msgstr "\tMarcadores\tVer diretórios marcados" + +#: src/GMImportDialog.cpp:209 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:250 +msgid "\tCreate new directory\tCreate new directory." +msgstr "\tCriar novo diretório\tCriar novo diretório" + +#: src/GMImportDialog.cpp:210 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:251 +msgid "\tShow list\tDisplay directory with small icons." +msgstr "\tMostrar em lista\tExibir diretório com ícones pequenos" + +#: src/GMImportDialog.cpp:211 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:252 +msgid "\tShow icons\tDisplay directory with big icons." +msgstr "\tMostrar ícones\tExibir diretório com ícones grandes" + +#: src/GMImportDialog.cpp:212 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:253 +msgid "\tShow details\tDisplay detailed directory listing." +msgstr "\tMostrar detalhes\tExibir detalhes do diretório" + +#: src/GMImportDialog.cpp:213 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:254 +msgid "\tShow hidden files\tShow hidden files and directories." +msgstr "\tMostrar ficheiros\tMostrar diretórios/ficheiros ocultos" + +#: src/GMImportDialog.cpp:213 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:254 +msgid "\tHide Hidden Files\tHide hidden files and directories." +msgstr "\tOcultar ficheiros\tNão mostrar diretórios/ficheiros ocultos" + +#: src/GMImportDialog.cpp:412 +msgid "Synchronize Folder" +msgstr "Sincronizar pasta" + +#: src/GMImportDialog.cpp:414 +msgid "Import Playlist" +msgstr "Importar lista de reprodução" + +#: src/GMImportDialog.cpp:416 +msgid "Import Music" +msgstr "Importar músicas" + +#: src/GMImportDialog.cpp:448 +msgid "&File(s)" +msgstr "&Ficheiro(s)" + +#: src/GMImportDialog.cpp:475 +msgid "&Directory" +msgstr "&Diretório" + +#: src/GMImportDialog.cpp:478 +msgid "Exclude Filter\tFilter out directories and/or files based on pattern" +msgstr "Exclusão\tFiltrar diretórios e/ou ficheiros com base num padrão" + +#: src/GMImportDialog.cpp:481 +msgid "Folders:" +msgstr "Pastas:" + +#: src/GMImportDialog.cpp:483 +msgid "Files:" +msgstr "Ficheiros:" + +#: src/GMImportDialog.cpp:499 +#: src/GMImportDialog.cpp:560 +msgid "&Sync" +msgstr "&Sincronizar" + +#: src/GMImportDialog.cpp:501 +msgid "Sync Operation" +msgstr "Sincronização" + +#: src/GMImportDialog.cpp:504 +msgid "Import new tracks\tImports files not yet in the database." +msgstr "Importar faixas\tImportar ficheiros não existentes na base de dados" + +#: src/GMImportDialog.cpp:505 +msgid "Remove tracks that have been deleted from disk" +msgstr "Remover faixas que foram eliminadas do disco" + +#: src/GMImportDialog.cpp:507 +msgid "Update existing tracks:" +msgstr "Atualizar faixas existentes:" + +#: src/GMImportDialog.cpp:508 +msgid "Modified since last import\tOnly reread the tag when the file has been modified." +msgstr "Se modificadas desde a última importação\tReanalisar detalhes dos ficheiros modificados" + +#: src/GMImportDialog.cpp:510 +msgid "All\tAlways read the tags" +msgstr "Sempre\tReanalisar todos os detalhes" + +#: src/GMImportDialog.cpp:511 +msgid "Remove tracks found in folder from database" +msgstr "Remover da base de dados as faixas encontradas na pasta" + +#: src/GMImportDialog.cpp:514 +msgid "&Track" +msgstr "Fai&xa" + +#: src/GMImportDialog.cpp:517 +msgid "Parse Settings" +msgstr "Definições" + +#: src/GMImportDialog.cpp:521 +msgid "Parse info from:" +msgstr "Analisar de:" + +#: src/GMImportDialog.cpp:524 +msgid "Tag" +msgstr "Detalhes" + +#: src/GMImportDialog.cpp:526 +msgid "Both" +msgstr "Ambos" + +#: src/GMImportDialog.cpp:528 +msgid "Default value:" +msgstr "Valor pré-definido:" + +#: src/GMImportDialog.cpp:532 +msgid "Set track number based on scan order." +msgstr "Definir número da faixa com base na ordem" + +#: src/GMImportDialog.cpp:537 +msgid "" +"%T - title %A - album name\n" +"%P - album artist name %p - track artist name \n" +"%N - track number %G - genre" +msgstr "" +"%T - titulo %A - álbum\n" +"%P - artista do álbum %p - artista da faixa\n" +"%N - n.º da faixa %G - género" + +#: src/GMImportDialog.cpp:549 +msgid "Replace underscores with spaces" +msgstr "Substituir \"underscores\" por espaços" + +#: src/GMImportDialog.cpp:562 +msgid "&Import" +msgstr "&Importar" + +#: src/GMPlayer.cpp:212 +msgid "Unable to initialize audio driver." +msgstr "Incapaz de iniciar o sistema de som" + +#: src/GMPlayer.cpp:714 +msgid "Unknown host." +msgstr "Servidor desconhecido" + +#: src/GMPlayer.cpp:715 +msgid "Unknown device" +msgstr "Dispositivo desconhecido" + +#: src/GMPlayer.cpp:716 +msgid "Network not reachable." +msgstr "Incapaz de ligar à rede" + +#: src/GMPlayer.cpp:717 +msgid "Audio output unavailable." +msgstr "Sistema de som não disponível" + +#: src/GMPlayer.cpp:718 +msgid "Connection Refused." +msgstr "Ligação recusada" + +#: src/GMPlayer.cpp:719 +msgid "File not found." +msgstr "Ficheiro não encontrado" + +#: src/GMPlayer.cpp:720 +msgid "Resource not accessible. Check permissions" +msgstr "Incapaz de aceder ao recurso. Veja as permissões" + +#: src/GMPlayer.cpp:721 +msgid "Read Error" +msgstr "Erro de leitura" + +#: src/GMPlayer.cpp:722 +msgid "Error while loading library/plugin" +msgstr "Erro ao carregar coleção/\"plug-in\"" + +#: src/GMPlayer.cpp:723 +msgid "Warning" +msgstr "Aviso" + +#: src/GMPlayer.cpp:724 +msgid "Security Warning" +msgstr "Aviso de segurança" + +#: src/GMPlayer.cpp:725 +msgid "Unknown Error" +msgstr "Erro desconhecido" + +#: src/GMPlayer.cpp:761 +msgid "Error" +msgstr "Erro" + +#: src/GMPlayerManager.cpp:439 +#, c-format +msgid "Unable to create directory %s\n" +msgstr "Incapaz de criar o diretório %s\n" + +#: src/GMPlayerManager.cpp:612 +msgid "" +"For some reason the FOX library was compiled without PNG support.\n" +"In order to show all icons, Goggles Music Manager requires PNG\n" +"support in the FOX library. If you've compiled FOX yourself, most\n" +"likely the libpng header files were not installed on your system." +msgstr "" +"Por alguma razão a biblioteca FOX foi compilada sem suporte a PNG.\n" +"Para mostrar todos os ícones, o Goggles necessita do suporte a PNG\n" +"disponibilizado pela biblioteca FOX. Se foi você que compilou o FOX,\n" +"verifique se ficheiros libpng estão instalados." + +#: src/GMPlayerManager.cpp:623 +msgid "Session bus not available. All features requiring dbus are disabled." +msgstr "Canal de transmissão indisponível. Todas as funções Dbus estão inativas." + +#: src/GMPlayerManager.cpp:633 +msgid "A DBus error occurred. All features requiring sessionbus are disabled." +msgstr "Ocorreu um erro DBus. Todas as funções sessionbus estão inativas." + +#: src/GMPlayerManager.cpp:644 +msgid "Session Bus not available. All features requiring sessionbus are disabled." +msgstr "Bus de sessão indisponível. Todas as funções sessionbus estão inativas." + +#: src/GMPlayerManager.cpp:719 +#: src/GMPreferencesDialog.cpp:594 +msgid "Audio Device Error" +msgstr "Erro de dispositivo" + +#: src/GMPlayerManager.cpp:1551 +msgid "Last.FM Error" +msgstr "Erro last.fm" + +#: src/GMPlayerManager.cpp:1558 +msgid "Playback Error" +msgstr "Erro de reprodução" + +#: src/GMPlayerManager.cpp:1708 +#, c-format +msgid "" +"%s\n" +"%s (%d)" +msgstr "" +"%s\n" +"%s (%d)" + +#: src/GMPlayListSource.cpp:99 +#: src/GMPlayListSource.cpp:105 +#: src/GMPlayListSource.cpp:111 +#: src/GMPlayListSource.cpp:121 +msgid "Remove…\tDel\tRemove track(s) from play list." +msgstr "Remover…\tDel\tRemover faixa(s) da lista de reprodução" + +#: src/GMPlayListSource.cpp:161 +msgid "Edit…" +msgstr "Editar…" + +#: src/GMPlayListSource.cpp:162 +msgid "Import…" +msgstr "Importar…" + +#: src/GMPlayListSource.cpp:164 +msgid "Remove Playlist" +msgstr "Remover lista de reprodução" + +#: src/GMPlayListSource.cpp:262 +msgid "Remove tracks with genre from play list?" +msgstr "Remover da lista de reprodução as faixas deste género?" + +#: src/GMPlayListSource.cpp:265 +msgid "Remove tracks from artist from play list?" +msgstr "Remover da lista de reprodução as faixas deste artista?" + +#: src/GMPlayListSource.cpp:268 +msgid "Remove tracks from album from play list?" +msgstr "Remover este álbum da lista de reprodução?" + +#: src/GMPlayListSource.cpp:271 +msgid "Remove track(s) from play list?" +msgstr "Remover faixa(s) da lista de reprodução?" + +#: src/GMPlayListSource.cpp:284 +msgid "Remove tracks from music library" +msgstr "Remover faixas da coleção" + +#: src/GMPlayListSource.cpp:406 +#: src/GMPlayListSource.cpp:407 +msgid "Edit Playlist" +msgstr "Editar lista de reprodução" + +#: src/GMPlayListSource.cpp:407 +msgid "Change playlist name" +msgstr "Mudar nome da lista de reprodução" + +#: src/GMPlayListSource.cpp:432 +msgid "Delete Play List?" +msgstr "Eliminar lista de reprodução?" + +#: src/GMPlayListSource.cpp:432 +msgid "Are you sure you want to delete the playlist?" +msgstr "Tem a certeza de que quer eliminar a lista de reprodução?" + +#: src/GMPlayListSource.cpp:432 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:111 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:116 +msgid "&Yes" +msgstr "&Sim" + +#: src/GMPlayListSource.cpp:432 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:112 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:117 +msgid "&No" +msgstr "&Não" + +#: src/GMPreferencesDialog.cpp:252 +msgid "Preferences" +msgstr "Preferências" + +#: src/GMPreferencesDialog.cpp:286 +msgid "&General" +msgstr "&Geral" + +#: src/GMPreferencesDialog.cpp:289 +msgid "Sort Options" +msgstr "Opções de ordem" + +#: src/GMPreferencesDialog.cpp:293 +msgid "Ignore leading words" +msgstr "Ignorar as palavras" + +#: src/GMPreferencesDialog.cpp:296 +msgid "Album Covers" +msgstr "Capas de álbum" + +#: src/GMPreferencesDialog.cpp:299 +msgid "Show album cover of playing track\tShow album cover of playing track" +msgstr "Mostrar capa de álbum da faixa em reprodução\tMostrar capa de álbum da faixa em reprodução" + +#: src/GMPreferencesDialog.cpp:300 +msgid "Show album covers in album browser\tShow album covers in album browser" +msgstr "Mostrar capa de álbum no explorador de álbuns\tMostrar capa de álbum no explorador de álbuns" + +#: src/GMPreferencesDialog.cpp:302 +msgid "System Tray" +msgstr "Bandeja do sistema" + +#: src/GMPreferencesDialog.cpp:304 +msgid "Show Tray Icon\tShow tray icon in the system tray." +msgstr "Mostrar ícone na bandeja\tMostrar ícone na bandeja do sistema" + +#: src/GMPreferencesDialog.cpp:307 +msgid "Show Track Change Notifications\tInform notification daemon of track changes." +msgstr "Mostrar notificação de alteração de faixa\tMostrar notificações ao alterar de faixa" + +#: src/GMPreferencesDialog.cpp:311 +msgid "Last.fm" +msgstr "Last.fm" + +#: src/GMPreferencesDialog.cpp:315 +msgid "" +"This version of Goggles Music Manager is\n" +"not supported by Last-FM. Please upgrade\n" +"to a newer version of GMM." +msgstr "" +"A last.fm não possui suporte a esta versão\n" +"do Goggles Music Manager.\n" +"Deve atualizar o Goggles Music Manager. " + +#: src/GMPreferencesDialog.cpp:321 +msgid "Service:" +msgstr "Serviço:" + +#: src/GMPreferencesDialog.cpp:338 +msgid "&Sign up…" +msgstr "Iniciar &sessão..." + +#: src/GMPreferencesDialog.cpp:344 +msgid "Username:" +msgstr "Utilizador:" + +#: src/GMPreferencesDialog.cpp:346 +msgid "Password:" +msgstr "Senha:" + +#: src/GMPreferencesDialog.cpp:349 +msgid "Scrobble" +msgstr "\"Scrobble\"" + +#: src/GMPreferencesDialog.cpp:359 +msgid "&Window" +msgstr "&Janela" + +#: src/GMPreferencesDialog.cpp:362 +msgid "Window" +msgstr "Janela" + +#: src/GMPreferencesDialog.cpp:365 +msgid "Close button minimizes to tray" +msgstr "Botão \"Fechar\" minimiza para a bandeja" + +#: src/GMPreferencesDialog.cpp:366 +msgid "Show Status Bar" +msgstr "Mostrar barra de estado" + +#: src/GMPreferencesDialog.cpp:368 +msgid "Show Icons in Track Browser" +msgstr "Mostrar ícones no explorador de faixas" + +#: src/GMPreferencesDialog.cpp:370 +msgid "Display playing track in title bar" +msgstr "Mostrar nome da faixa atual na barra de título" + +#: src/GMPreferencesDialog.cpp:372 +msgid "Player Controls" +msgstr "Controlos" + +#: src/GMPreferencesDialog.cpp:376 +msgid "Location:" +msgstr "Localização:" + +#: src/GMPreferencesDialog.cpp:378 +msgid "Top" +msgstr "Em cima" + +#: src/GMPreferencesDialog.cpp:379 +msgid "Bottom" +msgstr "Em baixo" + +#: src/GMPreferencesDialog.cpp:387 +msgid "Title Format:" +msgstr "Formato do título:" + +#: src/GMPreferencesDialog.cpp:391 +msgid "Style:" +msgstr "Estilo" + +#: src/GMPreferencesDialog.cpp:392 +msgid "Show Labels" +msgstr "Mostrar texto" + +#: src/GMPreferencesDialog.cpp:395 +msgid "Large Icons" +msgstr "Ícones grandes" + +#: src/GMPreferencesDialog.cpp:399 +msgid "A&ppearance" +msgstr "As&peto" + +#: src/GMPreferencesDialog.cpp:402 +msgid "Colors" +msgstr "Cores" + +#: src/GMPreferencesDialog.cpp:409 +msgid "fg\tForeground Color" +msgstr "CF\tCor de fundo" + +#: src/GMPreferencesDialog.cpp:410 +msgid "bg\tBackground Color" +msgstr "CP\tCor principal" + +#: src/GMPreferencesDialog.cpp:411 +msgid "alt bg\tAlternative Background Color" +msgstr "CF ALT\tCor de fundo alternativa" + +#: src/GMPreferencesDialog.cpp:422 +msgid "Normal\tNormal Text Color" +msgstr "Normal\tCor de texto normal" + +#: src/GMPreferencesDialog.cpp:426 +msgid "Base\tBase Color" +msgstr "Base\tCor base" + +#: src/GMPreferencesDialog.cpp:429 +msgid "Selected\tSelected Text Color" +msgstr "Seleção\tCor do texto da seleção" + +#: src/GMPreferencesDialog.cpp:433 +msgid "Menu\tMenu Base Color" +msgstr "Menu\tCor do menu" + +#: src/GMPreferencesDialog.cpp:437 +msgid "Menu\tMenu Text Color" +msgstr "Menu\tCor do texto do menu" + +#: src/GMPreferencesDialog.cpp:441 +msgid "Border\tBorder Color" +msgstr "Contornos\tCor dos contornos" + +#: src/GMPreferencesDialog.cpp:445 +msgid "Tooltip\tTooltip Color" +msgstr "Dicas\tCor das dicas" + +#: src/GMPreferencesDialog.cpp:449 +msgid "Hilite\tHilite Color" +msgstr "Realce\tCor de realce" + +#: src/GMPreferencesDialog.cpp:456 +msgid "Shadow\tShadow Color" +msgstr "Sombra\tCor da sombra" + +#: src/GMPreferencesDialog.cpp:459 +msgid "Playing\tPlaying Track Color" +msgstr "Em reprodução\tCor da faixa em reprodução" + +#: src/GMPreferencesDialog.cpp:463 +msgid "Tray\tTray Background Color" +msgstr "Bandeja\tCor de fundo da bandeja" + +#: src/GMPreferencesDialog.cpp:473 +msgid "Presets:" +msgstr "Pré-ajustes:" + +#: src/GMPreferencesDialog.cpp:481 +msgid "Font & Icons" +msgstr "Letras e ícones" + +#: src/GMPreferencesDialog.cpp:487 +msgid "Default Font" +msgstr "Letra pré-definida" + +#: src/GMPreferencesDialog.cpp:489 +msgid "Change…" +msgstr "Alterar..." + +#: src/GMPreferencesDialog.cpp:490 +msgid "Icons" +msgstr "Ícones" + +#: src/GMPreferencesDialog.cpp:509 +msgid "&Audio" +msgstr "Á&udio" + +#: src/GMPreferencesDialog.cpp:512 +msgid "Engine" +msgstr "Sistema" + +#: src/GMPreferencesDialog.cpp:516 +msgid "Audio Driver:" +msgstr "Controlador:" + +#: src/GMPreferencesDialog.cpp:527 +msgid "Close audio device on pause." +msgstr "Fechar dispositivo ao pausar" + +#: src/GMPreferencesDialog.cpp:528 +msgid "Turn off playback engine on stop." +msgstr "Desligar sistema de reprodução ao parar" + +#: src/GMPreferencesDialog.cpp:529 +msgid "Turn on playback engine on startup.\tFor faster startup, playback engine is normally started when first track is played.\tFor faster startup, playback engine is normally started when first track is played." +msgstr "Ligar sistema de reprodução ao iniciar\tPara um início mais rápido, o sistema de som é iniciado ao reproduzir a primeira faixa\tPara um início mais rápido, o sistema de som é iniciado ao reproduzir a primeira faixa" + +#: src/GMPreferencesDialog.cpp:532 +msgid "Playback" +msgstr "Reprodução" + +#: src/GMPreferencesDialog.cpp:536 +msgid "Replay Gain:" +msgstr "Replay Gain:" + +#: src/GMPreferencesDialog.cpp:538 +msgid "Off" +msgstr "Desligado" + +#: src/GMPreferencesDialog.cpp:543 +msgid "Gapless playback" +msgstr "Reprodução Gapless" + +#: src/GMPreferencesDialog.cpp:544 +msgid "Volume Normalization" +msgstr "Normalização de volume" + +#: src/GMPreferencesDialog.cpp:594 +#, c-format +msgid "Failed to open requested audio driver: %s" +msgstr "Falha ao carregar o controlador áudio: %s" + +#: src/GMPreferencesDialog.cpp:678 +msgid "Current" +msgstr "Atual" + +#: src/GMPreferencesDialog.cpp:696 +msgid "Custom" +msgstr "Personalizar" + +#: src/GMPreferencesDialog.cpp:769 +#: src/GMPreferencesDialog.cpp:774 +#: src/GMWindow.cpp:1133 +#: src/GMWindow.cpp:1140 +#: src/GMWindow.cpp:1147 +#: src/GMWindow.cpp:1154 +msgid "Unable to launch webbrowser" +msgstr "Incapaz de iniciar o navegador web" + +#: src/GMPreferencesDialog.cpp:1172 +msgid "Select Normal Font" +msgstr "Selecione o tipo de letra" + +#: src/GMRemote.cpp:295 +msgid "&Next" +msgstr "Segui&nte" + +#: src/GMRemote.cpp:296 +msgid "P&revious" +msgstr "Ante&rior" + +#: src/GMRemote.cpp:297 +#: src/GMWindow.cpp:1197 +msgid "&Play" +msgstr "Re&produzir" + +#: src/GMRemote.cpp:298 +msgid "&Stop" +msgstr "P&arar" + +#: src/GMRemote.cpp:300 +msgid "Show Browser" +msgstr "Mostrar explorador" + +#: src/GMRemote.cpp:301 +#: src/GMTrayIcon.cpp:307 +msgid "Quit" +msgstr "Sair" + +#: src/GMRemote.cpp:385 +msgid "\tShow Browser\tShow Browser" +msgstr "\tMostrar explorador\tMostrar explorador" + +#: src/GMRemote.cpp:387 +msgid "\tStart Playback\tStart Playback" +msgstr "\tIniciar reprodução\tIniciar reprodução" + +#: src/GMRemote.cpp:388 +msgid "\tStop Playback\tStop Playback" +msgstr "\tParar reprodução\tParar reprodução" + +#: src/GMRemote.cpp:390 +msgid "\tPlay Previous Track\tPlay previous track." +msgstr "\tReproduzir faixa anterior\tReproduzir faixa anterior" + +#: src/GMRemote.cpp:391 +msgid "\tPlay Next Track\tPlay next track." +msgstr "\tReproduzir faixa seguinte\tReproduzir faixa seguinte" + +#: src/GMRemote.cpp:397 +#: src/GMWindow.cpp:222 +msgid "\tAdjust Volume\tAdjust Volume" +msgstr "\tAjustar volume\tAjustar volume" + +#: src/GMSearch.cpp:128 +msgid "Unable to open the database" +msgstr "Incapaz de abrir a base de dados" + +#: src/GMSearch.cpp:252 +msgid "Database Error: Unable to retrieve all filenames." +msgstr "Erro na base de dados: incapaz de obter nome dos ficheiros" + +#: src/GMSearch.cpp:280 +msgid "Unable to update track" +msgstr "Incapaz de atualizar faixa" + +#: src/GMSearch.cpp:439 +msgid "Unable to insert track into the database" +msgstr "Incapaz de inserir a faixa na base de dados" + +#: src/GMSearch.cpp:505 +#: src/GMTrackDatabase.cpp:205 +msgid "Fatal Error" +msgstr "Erro fatal" + +#: src/GMSearch.cpp:505 +#, c-format +msgid "" +"%s\n" +"Please contact support if this error keeps occuring." +msgstr "" +"%s\n" +"Contacte o programador se este erro voltar a ocorrer" + +#: src/GMSearch.cpp:534 +#: src/GMSearch.cpp:584 +msgid "Updating Database..." +msgstr "A atualizar base de dados..." + +#: src/GMSearch.cpp:537 +#: src/GMSearch.cpp:626 +msgid "Please wait. This may take a while." +msgstr "Por favor, aguarde. Pode levar algum tempo." + +#: src/GMSearch.cpp:555 +#: src/GMSearch.cpp:637 +msgid "New Tracks:" +msgstr "Novas faixas:" + +#: src/GMSearch.cpp:561 +#: src/GMSearch.cpp:643 +msgid "File:" +msgstr "Ficheiro:" + +#: src/GMSearch.cpp:594 +#: src/GMSearch.cpp:623 +msgid "Importing Files..." +msgstr "A importar ficheiros..." + +#: src/GMSearch.cpp:652 +msgid "&Stop Import" +msgstr "&Parar importação" + +#: src/GMSourceView.cpp:54 +msgid "Sources\tPress to change sorting order\tPress to change sorting order" +msgstr "Fontes\tPrima para alterar a ordem\tPrima para alterar a ordem" + +#: src/GMSourceView.cpp:245 +#: src/GMWindow.cpp:261 +msgid "New Playlist…\t\tCreate a new playlist" +msgstr "Nova lista de reprodução…\t\tCriar lista de reprodução" + +#: src/GMSourceView.cpp:246 +#: src/GMWindow.cpp:262 +msgid "Import Playlist…\t\tImport existing playlist" +msgstr "Importar lista de reprodução…\t\tImportar lista de reprodução" + +#: src/GMSourceView.cpp:247 +#: src/GMWindow.cpp:263 +msgid "New Radio Station…\t\tCreate a new playlist" +msgstr "Nova estação de rádio…\t\tCriar lista de reprodução" + +#: src/GMStreamSource.cpp:64 +msgid "Station" +msgstr "Estação" + +#: src/GMStreamSource.cpp:112 +#: src/GMStreamSource.cpp:118 +msgid "New Station…\t\t" +msgstr "Nova estação…\t\t" + +#: src/GMStreamSource.cpp:117 +msgid "Edit…\t\t" +msgstr "Editar…\t\t" + +#: src/GMStreamSource.cpp:119 +msgid "Remove\t\tRemove." +msgstr "Remover\t\tRemover" + +#: src/GMStreamSource.cpp:165 +#: src/GMStreamSource.cpp:166 +msgid "New Internet Radio Station" +msgstr "Nova estação de rádio" + +#: src/GMStreamSource.cpp:166 +msgid "Specify url and description of new station" +msgstr "Indique o URL e a descrição da estação" + +#: src/GMStreamSource.cpp:168 +msgid "C&reate" +msgstr "C&riar" + +#: src/GMStreamSource.cpp:173 +#: src/GMStreamSource.cpp:211 +msgid "Location" +msgstr "Endereço" + +#: src/GMStreamSource.cpp:175 +#: src/GMStreamSource.cpp:214 +msgid "Description" +msgstr "Descrição" + +#: src/GMStreamSource.cpp:189 +msgid "Untitled" +msgstr "Sem nome" + +#: src/GMStreamSource.cpp:202 +#: src/GMStreamSource.cpp:203 +msgid "Edit Internet Radio Station" +msgstr "Editar estação de rádio" + +#: src/GMStreamSource.cpp:203 +msgid "Update url and description of station" +msgstr "Atualizar URL e descrição da estação" + +#: src/GMStreamSource.cpp:265 +msgid "Remove Internet Radio Station(s)?" +msgstr "Remover estações de rádio?" + +#: src/GMStreamSource.cpp:266 +msgid "Remove Internet Radio Station(s) from library?" +msgstr "Remover da coleção as estações de rádio?" + +#: src/GMStreamSource.cpp:280 +#, c-format +msgid "Unable to remove station from the library." +msgstr "Incapaz de remover a estação da coleção" + +#: src/GMTrackDatabase.cpp:193 +msgid "" +"An incompatible (future) version of the database was found.\n" +"This usually happens when you try to downgrade to a older version of GMM\n" +"Press OK to continue and reset the database (all information will be lost!).\n" +"Press Cancel to quit now and leave the database as is." +msgstr "" +"Foi encontrada uma base de dados incompatível.\n" +"Normalmente, esta situação ocorre ao instalar uma versão antiga do GMM\n" +"Prima OK para continuar e iniciar a base de dados (as informações serão perdidas!).\n" +"Prima Cancelar para sair e manter a base de dados intacta." + +#: src/GMTrackDatabase.cpp:205 +msgid "" +"Goggles Music Manager was unable to open the database.\n" +"The database may have been corrupted. Please remove ~/.goggles/goggles.db to try again.\n" +"if the error keeps occuring, please file an issue at http://code.google.com/p/gogglesmm" +msgstr "" +"O Goggles Music Manager não conseguiu abrir a base de dados.\n" +"É possível que a base de dados esteja danificada. Remova o ficheiro ~/.goggles/goggles.db e tente novamente.\n" +"Se este erro voltar a ocorrer, reporte-o em http://code.google.com/p/gogglesmm" + +#: src/GMTrackView.cpp:245 +msgid "\tClose Filter\tClose Filter" +msgstr "\tFechar filtro\tFechar filtro" + +#: src/GMTrackView.cpp:246 +msgid "&Find" +msgstr "&Procurar" + +#: src/GMTrackView.cpp:252 +msgid "Tags\tPress to change sorting order\tPress to change sorting order" +msgstr "Detalhes\tPrima para alterar a ordem\tPrima para alterar a ordem" + +#: src/GMTrackView.cpp:256 +msgid "Artists\tPress to change sorting order\tPress to change sorting order" +msgstr "Artistas\tPrima para alterar a ordem\tPrima para alterar a ordem" + +#: src/GMTrackView.cpp:260 +msgid "Albums\tPress to change sorting order\tPress to change sorting order" +msgstr "Álbuns\tPrima para alterar a ordem\tPrima para alterar a ordem" + +#: src/GMTrackView.cpp:282 +#: src/GMWindow.cpp:299 +msgid "&Configure Columns…" +msgstr "&Configurar colunas..." + +#: src/GMTrackView.cpp:286 +msgid "Browse" +msgstr "Explorar" + +#: src/GMTrackView.cpp:287 +msgid "Shuffle\tCtrl-R" +msgstr "Baralhar\tCtrl-R" + +#: src/GMTrackView.cpp:294 +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:388 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:734 +msgid "Reverse" +msgstr "Inverter" + +#: src/GMTrackView.cpp:804 +#, c-format +msgid "All %d Genres" +msgstr "Todos os %d géneros" + +#: src/GMTrackView.cpp:806 +msgid "All Genres" +msgstr "Todos os géneros" + +#: src/GMTrackView.cpp:832 +#, c-format +msgid "All %d Artists" +msgstr "Todos os %d artistas" + +#: src/GMTrackView.cpp:872 +#, c-format +msgid "All %d Albums" +msgstr "Todos os %d álbuns" + +#: src/GMTrackView.cpp:1258 +#, c-format +msgid "By %s" +msgstr "Por %s" + +#: src/GMTrackView.cpp:1586 +#: src/GMTrackView.cpp:1609 +msgid "Sort by Album Year" +msgstr "Ordenar por ano" + +#: src/GMTrackView.cpp:1637 +msgid "&Columns\t\tChange Visible Columns." +msgstr "&Colunas\t\tAlterar colunas visíveis" + +#: src/GMTrackView.cpp:1638 +msgid "&Sort\t\tChange Sorting." +msgstr "Or&denar\t\tAlterar ordem" + +#: src/GMTrayIcon.cpp:302 +#: src/GMWindow.cpp:841 +#: src/GMWindow.cpp:925 +#: src/GMWindow.cpp:948 +#: src/GMWindow.cpp:954 +msgid "Play" +msgstr "Reproduzir" + +#: src/GMTrayIcon.cpp:303 +#: src/GMWindow.cpp:843 +msgid "Stop" +msgstr "Parar" + +#: src/GMTrayIcon.cpp:304 +msgid "Previous Track" +msgstr "Faixa anterior" + +#: src/GMTrayIcon.cpp:305 +msgid "Next Track" +msgstr "Faixa seguinte" + +#: src/GMWindow.cpp:205 +msgid "Play\tStart Playback\tStart Playback" +msgstr "Reproduzir\tIniciar reprodução\tIniciar reprodução" + +#: src/GMWindow.cpp:205 +msgid "Pause\tPause\tPause Playback" +msgstr "Pausa\tPausa\tPausar reprodução" + +#: src/GMWindow.cpp:206 +msgid "Stop\tStop Playback\tStop Playback" +msgstr "Parar\tParar reprodução\tParar reprodução" + +#: src/GMWindow.cpp:208 +msgid "Previous\tPlay Previous Track\tPlay previous track." +msgstr "Anterior\tReproduzir faixa anterior\tReproduzir faixa anterior" + +#: src/GMWindow.cpp:209 +msgid "Next\tPlay Next Track\tPlay next track." +msgstr "Seguinte\tReproduzir faixa seguinte\tReproduzir faixa seguinte" + +#: src/GMWindow.cpp:255 +msgid "&Music" +msgstr "&Música" + +#: src/GMWindow.cpp:256 +msgid "Import Folder…\tCtrl-O\tImport Music from folder into Library" +msgstr "Importar pasta…\tCtrl-O\tImportar músicas de uma pasta para a coleção" + +#: src/GMWindow.cpp:257 +msgid "Sync Folder…\t\tSynchronize Folder with Music in Library" +msgstr "Sincronizar pasta…\t\tSincronizar pasta com as músicas da coleção" + +#: src/GMWindow.cpp:259 +msgid "Play File or Stream…\t\tPlay File or Stream" +msgstr "Reproduzir ficheiro ou emissão…\t\tReproduzir ficheiro ou emissão" + +#: src/GMWindow.cpp:266 +msgid "&Quit\tCtrl-Q\tQuit the application." +msgstr "&Sair\tCtrl-Q\tSair da aplicação" + +#: src/GMWindow.cpp:271 +msgid "&Edit" +msgstr "&Editar" + +#: src/GMWindow.cpp:272 +msgid "&Copy\tCtrl-C\tCopy Selected Tracks" +msgstr "&Copiar\tCtrl-C\tCopiar faixas selecionadas" + +#: src/GMWindow.cpp:273 +msgid "&Cut\tCtrl-X\tCut Selected Tracks" +msgstr "Cor&tar\tCtrl-X\tCortar faixas selecionadas" + +#: src/GMWindow.cpp:274 +msgid "&Paste\tCtrl-V\tPaste Clipboard Selection" +msgstr "C&olar\tCtrl-V\tColar dados da área de transferência" + +#: src/GMWindow.cpp:276 +msgid "Find…\tCtrl-F\tShow search filter." +msgstr "Procurar…\tCtrl-F\tMostrar filtro de procura" + +#: src/GMWindow.cpp:278 +msgid "Preferences…" +msgstr "Preferências" + +#: src/GMWindow.cpp:282 +msgid "&View" +msgstr "&Ver" + +#: src/GMWindow.cpp:283 +msgid "&Browse\tCtrl-B\tShow genre artist and album browser." +msgstr "E&xplorar\tCtrl-B\tMostrar género, artista e ábum" + +#: src/GMWindow.cpp:284 +msgid "Show &Genres\tCtrl-G\tShow genre browser." +msgstr "Mostrar &género\tCtrl-G\tMostrar género" + +#: src/GMWindow.cpp:287 +#: src/GMWindow.cpp:289 +msgid "Show &Sources\tCtrl-S\tShow source browser " +msgstr "Mostrar fonte&s\tCtrl-S\tMostrar fontes" + +#: src/GMWindow.cpp:294 +msgid "Show Full Screen\tF12\tToggle fullscreen mode." +msgstr "Mostrar ecrã completo\tF12\tTrocar modo de ecrã completo" + +#: src/GMWindow.cpp:296 +msgid "Show Mini Player\tCtrl-M\tToggle Mini Player." +msgstr "Mostrar reprodutor pequeno\tCtrl-M\tTrocar esquema de reprodutor" + +#: src/GMWindow.cpp:300 +msgid "&Sort" +msgstr "Or&dem" + +#: src/GMWindow.cpp:302 +msgid "&Jump to Current Track\tCtrl-J\tShow current playing track." +msgstr "&Ir para a faixa atual\tCtrl-J\tMostrar a faixa em reprodução" + +#: src/GMWindow.cpp:306 +msgid "&Control" +msgstr "&Controlo" + +#: src/GMWindow.cpp:307 +msgid "Play\tCtrl-P\tStart playback." +msgstr "Reproduzir\tCtrl-P\tIniciar reprodução" + +#: src/GMWindow.cpp:308 +msgid "Stop\tCtrl-\\\tStop playback." +msgstr "Parar\tCtrl-\\\tParar reprodução" + +#: src/GMWindow.cpp:309 +msgid "Previous Track\tCtrl-[\tPlay next track." +msgstr "Faixa anterior\tCtrl-[\tReproduzir faixa anterior" + +#: src/GMWindow.cpp:310 +msgid "Next Track\tCtrl-]\tPlay previous track." +msgstr "Faixa seguinte\tCtrl-]\tReproduzir faixa seguinte" + +#: src/GMWindow.cpp:312 +msgid "Repeat Off\tCtrl-,\tRepeat current track." +msgstr "Não repetir\tCtrl-,\tNão repetir faixa" + +#: src/GMWindow.cpp:313 +msgid "Repeat Track\tCtrl-.\tRepeat current track." +msgstr "Repetir faixa\tCtrl-.\tRepetir faixa atual" + +#: src/GMWindow.cpp:314 +msgid "Repeat All Tracks\tCtrl-/\tRepeat all tracks." +msgstr "Repetir todas\tCtrl-/\tRepetir todas as faixas" + +#: src/GMWindow.cpp:315 +msgid "Repeat A-B\tCtrl-T\tRepeat section of track." +msgstr "Repetir A-B\tCtrl-T\tRepetir secção da faixa" + +#: src/GMWindow.cpp:316 +msgid "Shuffle Mode\tAlt-R\tPlay tracks in random order." +msgstr "Baralhar\tAlt-R\tReproduzir faixas aleatoriamente" + +#: src/GMWindow.cpp:318 +msgid "Equalizer\t\t" +msgstr "Equalizador\t\t" + +#: src/GMWindow.cpp:319 +msgid "Sleep Timer\t\tSetup sleeptimer." +msgstr "Intervalo\t\tConfigurar intervalo" + +#: src/GMWindow.cpp:323 +msgid "&Help" +msgstr "Aju&da" + +#: src/GMWindow.cpp:324 +msgid "&Homepage" +msgstr "Pa&gina inicial" + +#: src/GMWindow.cpp:325 +msgid "&Report Issue…" +msgstr "&Reportar problema..." + +#: src/GMWindow.cpp:327 +msgid "&Sign up for last.fm…" +msgstr "Iniciar &sessão na last.fm..." + +#: src/GMWindow.cpp:328 +msgid "&Join GMM on last.fm…\t\tJoin the Goggles Music Manager group on last.fm…" +msgstr "Integrar o gr&upo GMM da last.fm...\t\tIntegração no grupo GMM da last.fm..." + +#: src/GMWindow.cpp:330 +msgid "&About…" +msgstr "So&bre" + +#: src/GMWindow.cpp:842 +#: src/GMWindow.cpp:940 +msgid "Pause" +msgstr "Pausa" + +#: src/GMWindow.cpp:844 +msgid "Previous" +msgstr "Anterior" + +#: src/GMWindow.cpp:845 +msgid "Next" +msgstr "Seguinte" + +#: src/GMWindow.cpp:920 +#: src/GMWindow.cpp:949 +#: src/GMWindow.cpp:955 +msgid "Start playback." +msgstr "Iniciar reprodução" + +#: src/GMWindow.cpp:926 +#: src/GMWindow.cpp:927 +msgid "Start playback" +msgstr "Iniciar reprodução" + +#: src/GMWindow.cpp:934 +#: src/GMWindow.cpp:941 +msgid "Pause playback." +msgstr "Pausar reprodução" + +#: src/GMWindow.cpp:935 +msgid "Pause playback" +msgstr "Pausar reprodução" + +#: src/GMWindow.cpp:1052 +#: src/GMWindow.cpp:1054 +msgid "Repeat A-B" +msgstr "Repetir A-B" + +#: src/GMWindow.cpp:1053 +msgid "Repeat A" +msgstr "Repetir A" + +#: src/GMWindow.cpp:1195 +msgid "Play File or Stream" +msgstr "Reproduzir ficheiro ou emissão" + +#: src/GMWindow.cpp:1202 +msgid "Please specify a file or url to play:" +msgstr "Indique o ficheiro ou URL a reproduzir:" + +#: src/GMWindow.cpp:1206 +msgid "…" +msgstr "…" + +#: src/GMWindow.cpp:1212 +msgid "Select File" +msgstr "Selecione o ficheiro" + +#: src/GMWindow.cpp:1243 +msgid "Sleep Timer" +msgstr "Intervalo" + +#: src/GMWindow.cpp:1244 +msgid "Setup sleep timer" +msgstr "Configurar intervalo" + +#: src/GMWindow.cpp:1244 +msgid "Stop playback within a certain time" +msgstr "Parar reprodução após um intervalo de tempo" + +#: src/GMWindow.cpp:1246 +msgid "&Start Timer" +msgstr "&Ativar" + +#: src/GMWindow.cpp:1251 +msgid "Sleep in" +msgstr "Parar em" + +#: src/GMWindow.cpp:1253 +msgid "hours and" +msgstr "horas e" + +#: src/GMWindow.cpp:1255 +msgid "minutes." +msgstr "minutos" + +#: src/GMDatabaseSource.h:131 +msgid "Music Library" +msgstr "Coleção" + +#: src/GMStreamSource.h:57 +msgid "Internet Radio" +msgstr "Rádio na internet" + +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:122 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:127 +msgid "&Quit" +msgstr "&Sair" + +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:133 +msgid "&Skip" +msgstr "I&gnorar" + +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:134 +msgid "Skip &All" +msgstr "Ignorar tod&as" + +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:140 +msgid "&Don't Save" +msgstr "Não &gravar" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:220 +msgid "\tPick color" +msgstr "\tEscolher cor" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:229 +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:274 +msgid "\tHue, Saturation, Value" +msgstr "\tTom, saturação, valor" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:239 +msgid "\tRed, Green, Blue" +msgstr "\tVermelho, verde, azul" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:245 +msgid "&Red:" +msgstr "Ve&rmelho:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:250 +msgid "&Green:" +msgstr "&Verde:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:255 +msgid "&Blue:" +msgstr "A&zul:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:260 +msgid "&Alpha:" +msgstr "&Alfa:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:280 +msgid "Hue:" +msgstr "Tom:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:285 +msgid "Saturation:" +msgstr "Saturação:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:290 +msgid "Value:" +msgstr "Valor:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:295 +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:330 +msgid "Alpha:" +msgstr "Alfa:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:309 +msgid "\tCyan, Magenta, Yellow" +msgstr "\tCiano, magenta, amarelo" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:315 +msgid "Cyan:" +msgstr "Ciano:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:320 +msgid "Magenta:" +msgstr "Magenta:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:325 +msgid "Yellow:" +msgstr "Amarelo:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:344 +msgid "\tBy Name" +msgstr "\tPor nome" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:281 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:521 +msgid "Create New Directory" +msgstr "Criar novo diretório" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:284 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:524 +msgid "Already Exists" +msgstr "Já existe" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:288 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:528 +msgid "Cannot Create" +msgstr "Incapaz de criar" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:309 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:595 +msgid "Copy File" +msgstr "Copiar ficheiro" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:315 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:601 +msgid "Error Copying File" +msgstr "Erro ao copiar ficheiro" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:326 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:618 +msgid "Move File" +msgstr "Mover ficheiro" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:332 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:624 +msgid "Error Moving File" +msgstr "Erro ao mover ficheiro" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:343 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:641 +msgid "Link File" +msgstr "Ligar ficheiro" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:349 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:647 +msgid "Error Linking File" +msgstr "Erro ao ligar ao ficheiro" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:359 +msgid "Deleting file" +msgstr "A eliminar ficheiro" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:361 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:667 +msgid "Error Deleting File" +msgstr "Erro ao eliminar ficheiro" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:381 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:719 +msgid "Up one level" +msgstr "Subir um nível" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:382 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:720 +msgid "Home directory" +msgstr "Diretório pessoal" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:383 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:721 +msgid "Work directory" +msgstr "Diretório de trabalho" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:387 +msgid "Sorting" +msgstr "Ordenação" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:389 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:735 +msgid "Ignore case" +msgstr "Ignorar capitulares" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:390 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:746 +msgid "Hidden files" +msgstr "Ficheiros ocultos" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:393 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:754 +msgid "Bookmarks" +msgstr "Marcadores" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:394 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:757 +msgid "Set bookmark" +msgstr "Definir marcador" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:395 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:758 +msgid "Clear bookmarks" +msgstr "Apagar marcadores" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:411 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:774 +msgid "New directory..." +msgstr "Novo diretório..." + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:412 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:775 +msgid "Copy..." +msgstr "Copiar..." + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:413 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:776 +msgid "Move..." +msgstr "Mover..." + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:414 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:777 +msgid "Link..." +msgstr "Ligação..." + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:415 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:778 +msgid "Delete..." +msgstr "Eliminar..." + +#: ../../../fox-1.6.37/src/FXFileList.cpp:195 +msgid "Modified Date" +msgstr "Data de modificação" + +#: ../../../fox-1.6.37/src/FXFileList.cpp:196 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:731 +msgid "User" +msgstr "Utilizador" + +#: ../../../fox-1.6.37/src/FXFileList.cpp:197 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:732 +msgid "Group" +msgstr "Grupo" + +#: ../../../fox-1.6.37/src/FXFileList.cpp:198 +msgid "Attributes" +msgstr "Atributos" + +#: ../../../fox-1.6.37/src/FXFileList.cpp:200 +msgid "Link" +msgstr "Ligação" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:521 +msgid "Create new directory with name: " +msgstr "Criar novo diretório com o nome:" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:524 +#, c-format +msgid "File or directory %s already exists.\n" +msgstr "O ficheiro ou diretório %s já existe.\n" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:528 +#, c-format +msgid "Cannot create directory %s.\n" +msgstr "Incapaz de criar o diretório %s.\n" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:594 +#, c-format +msgid "" +"Copy file from location:\n" +"\n" +"%s\n" +"\n" +"to location: " +msgstr "" +"Copiar ficheiro de:\n" +"\n" +"%s\n" +"\n" +"para: " + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:601 +#, c-format +msgid "" +"Unable to copy file:\n" +"\n" +"%s to: %s\n" +"\n" +"Continue with operation?" +msgstr "" +"Incapaz de copiar:\n" +"\n" +"%s para: %s\n" +"\n" +"Continuar?" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:617 +#, c-format +msgid "" +"Move file from location:\n" +"\n" +"%s\n" +"\n" +"to location: " +msgstr "" +"Mover ficheiro de:\n" +"\n" +"%s\n" +"\n" +"para:" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:624 +#, c-format +msgid "" +"Unable to move file:\n" +"\n" +"%s to: %s\n" +"\n" +"Continue with operation?" +msgstr "" +"Incapaz de mover:\n" +"\n" +"%s para: %s\n" +"\n" +"Continuar?" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:640 +#, c-format +msgid "" +"Link file from location:\n" +"\n" +"%s\n" +"\n" +"to location: " +msgstr "" +"Cria ligação de:\n" +"\n" +"%s\n" +"\n" +"para:" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:647 +#, c-format +msgid "" +"Unable to link file:\n" +"\n" +"%s to: %s\n" +"\n" +"Continue with operation?" +msgstr "" +"Incapaz de criar ligação de:\n" +"\n" +"%s para: %s\n" +"\n" +"Continuar?" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:663 +msgid "Deleting files" +msgstr "A eliminar ficheiros" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:663 +#, c-format +msgid "" +"Are you sure you want to delete the file:\n" +"\n" +"%s" +msgstr "" +"Tem a certeza de que pretende eliminar:\n" +"\n" +"%s ?" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:667 +#, c-format +msgid "" +"Unable to delete file:\n" +"\n" +"%s\n" +"\n" +"Continue with operation?" +msgstr "" +"Incapaz de eliminar:\n" +"\n" +"%s\n" +"\n" +"Continuar?" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:722 +msgid "Select all" +msgstr "Selecionar tudo" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:726 +msgid "Sort by" +msgstr "Ordenar por" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:738 +msgid "View" +msgstr "Ver" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:739 +msgid "Small icons" +msgstr "Ícones pequenos" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:740 +msgid "Big icons" +msgstr "Ícones grandes" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:741 +msgid "Details" +msgstr "Detalhes" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:743 +msgid "Rows" +msgstr "Linhas" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:744 +msgid "Columns" +msgstr "Colunas" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:747 +msgid "Preview images" +msgstr "Ver imagens" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:749 +msgid "Normal images" +msgstr "Normais" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:750 +msgid "Medium images" +msgstr "Médias" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:751 +msgid "Giant images" +msgstr "Grandes" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:111 +msgid "&Replace" +msgstr "Substitui&r" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:112 +msgid "Re&place All" +msgstr "Subs&tituir tudo" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:121 +msgid "S&earch for:" +msgstr "P&esquisar:" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:129 +msgid "Replace &with:" +msgstr "Su&bstituir por:" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:138 +msgid "Ex&act" +msgstr "Ex&ata" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:139 +msgid "&Ignore Case" +msgstr "&Ignorar capitulares" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:140 +msgid "E&xpression" +msgstr "E&xpressão" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:141 +msgid "&Backward" +msgstr "&Voltar" + +#: ../../../fox-1.6.37/src/FXSearchDialog.cpp:71 +msgid "&Search" +msgstr "Pe&squisar" + +#: ../../../fox-1.6.37/src/FXStatusLine.cpp:82 +msgid "Ready." +msgstr "Pronto" + +#~ msgid "New Name" +#~ msgstr "Novo nome" diff --git a/po/ru.mo b/po/ru.mo new file mode 100644 index 0000000000000000000000000000000000000000..1c5cb6336ce4765193e5223c3153b4347bad68be GIT binary patch literal 45976 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, 2010. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: gogglesmm 0.10.0\n" +"Report-Msgid-Bugs-To: s.jansen@gmail.com\n" +"POT-Creation-Date: 2011-02-09 23:26-0600\n" +"PO-Revision-Date: 2010-01-24 20:10+0300\n" +"Language-Team: Russian\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"L Lawliet \n" + +#: src/GMAbout.cpp:136 src/GMDatabaseSource.cpp:218 +#: src/GMDatabaseSource.cpp:2215 src/GMPreferencesDialog.cpp:551 +msgid "&Close" +msgstr "&Закрыть" + +#: src/GMColumnDialog.cpp:204 +msgid "Configure Columns" +msgstr "Упорядочить столбцы" + +#: src/GMColumnDialog.cpp:207 ../../../fox-1.6.37/src/FXColorSelector.cpp:168 +msgid "&Accept" +msgstr "&Принять" + +#: src/GMColumnDialog.cpp:208 src/GMDatabaseSource.cpp:98 +#: src/GMDatabaseSource.cpp:1274 src/GMDatabaseSource.cpp:1704 +#: src/GMDatabaseSource.cpp:1804 src/GMDatabaseSource.cpp:1878 +#: src/GMDatabaseSource.cpp:2121 src/GMDatabaseSource.cpp:2182 +#: src/GMImportDialog.cpp:175 src/GMImportDialog.cpp:563 +#: src/GMPlayListSource.cpp:281 src/GMPlayListSource.cpp:410 +#: src/GMSearch.cpp:572 src/GMStreamSource.cpp:169 src/GMStreamSource.cpp:206 +#: src/GMStreamSource.cpp:271 src/GMWindow.cpp:1198 src/GMWindow.cpp:1247 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:107 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:118 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:123 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:129 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:135 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:142 +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:169 +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:135 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:205 +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:113 +msgid "&Cancel" +msgstr "&Отменить" + +#: src/GMColumnDialog.cpp:212 +msgid "" +"Choose the order of information to appear\n" +"in the track list." +msgstr "" +"Выбрать порядок отображения информации\n" +"в трек-листе" + +#: src/GMColumnDialog.cpp:218 +msgid "Move Up" +msgstr "Переместить вверх" + +#: src/GMColumnDialog.cpp:219 +msgid "Move Down" +msgstr "Переместить вниз" + +#: src/GMDatabaseSource.cpp:52 +msgid "Invalid Template" +msgstr "Неверный шаблон" + +#: src/GMDatabaseSource.cpp:52 +#, c-format +msgid "" +"The provided template is invalid. The track title %%T needs to be " +"specified.\n" +"Please fix the filename template in the preference panel." +msgstr "" +"Введенный шаблон неверен. Название трека %%T должно быть " +"определеноПожалуйста исправьте шаблон имени файла в панели настроек." + +#: src/GMDatabaseSource.cpp:72 src/GMTrackDatabase.cpp:193 +msgid "Database Error" +msgstr "Ошибка базы данных" + +#: src/GMDatabaseSource.cpp:72 +msgid "Oops. Database Error" +msgstr "Упс. Ошибка базы данных" + +#: src/GMDatabaseSource.cpp:87 +msgid "No changes" +msgstr "Изменений нет" + +#: src/GMDatabaseSource.cpp:87 +msgid "Filenames did not require any changes" +msgstr "Имена файлов не требуют изменений" + +#: src/GMDatabaseSource.cpp:94 +msgid "Rename Audio Files?" +msgstr "Переименовать аудио-файлы?" + +#: src/GMDatabaseSource.cpp:95 +msgid "Renaming Audio Files…" +msgstr "Переименовывание аудио-файлов..." + +#: src/GMDatabaseSource.cpp:95 +msgid "The following audio files are going to be renamed" +msgstr "Следующие аудио-файлы будут переименованы" + +#: src/GMDatabaseSource.cpp:97 +msgid "&Rename" +msgstr "&Переименовать" + +#: src/GMDatabaseSource.cpp:121 src/GMDatabaseSource.cpp:125 +msgid "Unable to rename file" +msgstr "Невозможно переименовать файл" + +#: src/GMDatabaseSource.cpp:121 +#, c-format +msgid "" +"Unable to rename:\n" +"%s\n" +"\n" +"to:%s\n" +"Continue renaming files?" +msgstr "" +"Невозможно переименовать:\n" +"%s\n" +"\n" +"в:%s\n" +"Продолжить переименование файлов?" + +#: src/GMDatabaseSource.cpp:125 +#, c-format +msgid "" +"Unable to rename:\n" +"%s\n" +"\n" +"to:%s" +msgstr "" +"Невозможно переименовать:\n" +"%s\n" +"\n" +"в:%s" + +#: src/GMDatabaseSource.cpp:160 src/GMImportDialog.cpp:534 +msgid "Filename Template" +msgstr "Шаблоны имен файлов" + +#: src/GMDatabaseSource.cpp:177 +msgid "" +"Template may contain absolute or relative path, environment variables\n" +"and ~. Relative paths are based on the location of the original file. The\n" +"file extension gets automatically added. The following macros\n" +"may be used:" +msgstr "" +"Шаблон может содержать абсолютный или относительный путь, переменные\n" +"окружения и ~. Относительный путь базируется на расположении оригинального\n" +"файла. Расширение файла добавляется автоматически. Могут быть использованы\n" +"следующие макросы:" + +#: src/GMDatabaseSource.cpp:178 +msgid "" +"%T - title %A - album name\n" +"%P - album artist name %p - track artist name\n" +"%y - year %d - disc number\n" +"%N - track number (2 digits) %n - track number \n" +"%G - genre" +msgstr "" +"%T - название %A - название альбома\n" +"%P - имя исполнителя альбома %p - имя исполнителя трека\n" +"%y - год %d - номер диска\n" +"%N - номер трека (2 цифры) %n - номер трека \n" +"%G - жанр" + +#: src/GMDatabaseSource.cpp:185 +msgid "Conditions may be used as well:" +msgstr "" + +#: src/GMDatabaseSource.cpp:186 +msgid "" +"?c - display a if c is not empty else display b.\n" +"?c - display c if not empty\n" +msgstr "" + +#: src/GMDatabaseSource.cpp:195 src/GMDatabaseSource.cpp:1815 +#: src/GMImportDialog.cpp:546 +msgid "Template:" +msgstr "Шаблон:" + +#: src/GMDatabaseSource.cpp:199 src/GMDatabaseSource.cpp:1819 +msgid "Encoding:" +msgstr "Кодировка:" + +#: src/GMDatabaseSource.cpp:205 +msgid "Exclude:" +msgstr "Исключить:" + +#: src/GMDatabaseSource.cpp:209 src/GMDatabaseSource.cpp:1825 +msgid "Options:" +msgstr "Опции:" + +#: src/GMDatabaseSource.cpp:210 src/GMDatabaseSource.cpp:1826 +msgid "Replace spaces with underscores" +msgstr "Заменить пробелы подчеркиваниями" + +#: src/GMDatabaseSource.cpp:212 src/GMDatabaseSource.cpp:1828 +msgid "Lower case" +msgstr "Нижний регистр" + +#: src/GMDatabaseSource.cpp:214 src/GMDatabaseSource.cpp:1830 +msgid "Lower case extension" +msgstr "Расширение в нижнем регистре" + +#: src/GMDatabaseSource.cpp:341 src/GMStreamSource.cpp:63 +#, fuzzy +msgid "No." +msgstr "Нет" + +#: src/GMDatabaseSource.cpp:342 +msgid "Queue" +msgstr "Очередь" + +#: src/GMDatabaseSource.cpp:343 src/GMDatabaseSource.cpp:1391 +#: src/GMTrackView.cpp:238 +msgid "Title" +msgstr "Название" + +#: src/GMDatabaseSource.cpp:344 src/GMDatabaseSource.cpp:1396 +#: src/GMTrackView.cpp:239 +msgid "Artist" +msgstr "Исполнитель" + +#: src/GMDatabaseSource.cpp:345 src/GMDatabaseSource.cpp:1397 +msgid "Album Artist" +msgstr "Исполнитель альбома" + +#: src/GMDatabaseSource.cpp:346 src/GMDatabaseSource.cpp:1405 +#: src/GMPreferencesDialog.cpp:540 src/GMTrackView.cpp:240 +msgid "Album" +msgstr "Альбом" + +#: src/GMDatabaseSource.cpp:347 src/GMDatabaseSource.cpp:1364 +#: src/GMDatabaseSource.cpp:1375 src/GMDatabaseSource.cpp:1381 +msgid "Disc" +msgstr "Диск" + +#: src/GMDatabaseSource.cpp:348 src/GMDatabaseSource.cpp:1408 +#: src/GMStreamSource.cpp:66 src/GMStreamSource.cpp:177 +#: src/GMStreamSource.cpp:216 src/GMTrackView.cpp:241 +msgid "Genre" +msgstr "Жанр" + +#: src/GMDatabaseSource.cpp:349 src/GMDatabaseSource.cpp:1369 +#: src/GMDatabaseSource.cpp:1387 +msgid "Year" +msgstr "Год" + +#: src/GMDatabaseSource.cpp:350 ../../../fox-1.6.37/src/FXFileSelector.cpp:730 +msgid "Time" +msgstr "Время" + +#: src/GMDatabaseSource.cpp:490 +msgid "Remove…\tDel\tRemove Genre from Library." +msgstr "Удалить...\tDel\tУдалить жанр из библиотеки." + +#: src/GMDatabaseSource.cpp:496 src/GMDatabaseSource.cpp:505 +#: src/GMPlayListSource.cpp:104 src/GMPlayListSource.cpp:110 +msgid "Copy\tCtrl-C\tCopy associated tracks to the clipboard." +msgstr "Копировать\tCtrl-C\tКопировать связанные треки в буфер обмена." + +#: src/GMDatabaseSource.cpp:499 src/GMDatabaseSource.cpp:508 +msgid "Remove…\tDel\tRemove associated tracks from library." +msgstr "Удалить...\tDel\tУдалить связанные треки из библиотеки." + +#: src/GMDatabaseSource.cpp:513 src/GMPlayListSource.cpp:116 +msgid "Edit…\tF2\tEdit Track Information." +msgstr "Редактировать...\tF2\tРедактировать информацию о треке." + +#: src/GMDatabaseSource.cpp:514 src/GMPlayListSource.cpp:117 +msgid "Copy\tCtrl-C\tCopy track(s) to clipboard." +msgstr "Копировать\tCtrl-C\tКопировать трек(и) в буфер обмена." + +#: src/GMDatabaseSource.cpp:518 src/GMPlayListSource.cpp:120 +msgid "Open Folder Location\t\tOpen Folder Location." +msgstr "" + +#: src/GMDatabaseSource.cpp:523 +msgid "Remove…\tDel\tRemove track(s) from library." +msgstr "Удалить...\tDel\tУдалить трек(и) из библиотеки." + +#: src/GMDatabaseSource.cpp:537 +msgid "New Play List…\t\tCreate a new play list." +msgstr "Новый плейлист...\t\tСоздать новый плейлист." + +#: src/GMDatabaseSource.cpp:539 src/GMPlayListSource.cpp:163 +msgid "Export…" +msgstr "Экспортировать..." + +#: src/GMDatabaseSource.cpp:540 +msgid "Information…\t\tLibrary Statistics" +msgstr "Информация...\t\tСтатистика библиотеки" + +#: src/GMDatabaseSource.cpp:542 +msgid "Remove All Tracks\t\tRemove all tracks from the library" +msgstr "Удалить все треки\t\tУдалить все треки из библиотеки" + +#: src/GMDatabaseSource.cpp:1272 +msgid "Edit Track Information" +msgstr "Редактировать информацию о треке" + +#: src/GMDatabaseSource.cpp:1275 src/GMPlayListSource.cpp:409 +#: src/GMStreamSource.cpp:205 ../../../fox-1.6.37/src/FXMessageBox.cpp:128 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:143 +msgid "&Save" +msgstr "Со&хранить" + +#: src/GMDatabaseSource.cpp:1315 +msgid "&Tag" +msgstr "" + +#: src/GMDatabaseSource.cpp:1320 +#, fuzzy +msgid "&Properties" +msgstr "Свойства" + +#: src/GMDatabaseSource.cpp:1325 src/GMImportDialog.cpp:525 +msgid "Filename" +msgstr "Имя файла" + +#: src/GMDatabaseSource.cpp:1329 ../../../fox-1.6.37/src/FXFileList.cpp:193 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:728 +msgid "Type" +msgstr "Тип" + +#: src/GMDatabaseSource.cpp:1333 ../../../fox-1.6.37/src/FXFileList.cpp:194 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:729 +msgid "Size" +msgstr "Размер" + +#: src/GMDatabaseSource.cpp:1341 src/GMStreamSource.cpp:65 +msgid "Bitrate" +msgstr "Битрейт" + +#: src/GMDatabaseSource.cpp:1345 +msgid "Samplerate" +msgstr "Семплрейт" + +#: src/GMDatabaseSource.cpp:1349 +msgid "Channels" +msgstr "Каналы" + +#: src/GMDatabaseSource.cpp:1358 src/GMPreferencesDialog.cpp:539 +msgid "Track" +msgstr "Трек" + +#: src/GMDatabaseSource.cpp:1403 +msgid "\tSeparate Artists" +msgstr "\tОбъединить исполнителей" + +#: src/GMDatabaseSource.cpp:1403 +msgid "\tShared Artists" +msgstr "\tРазделить исполнителей" + +#: src/GMDatabaseSource.cpp:1416 +msgid "Auto track number. Offset:" +msgstr "Автоматический номер трека. Смещение:" + +#: src/GMDatabaseSource.cpp:1425 +msgid "Update Tag in File" +msgstr "Обновить тег в файле" + +#: src/GMDatabaseSource.cpp:1430 +msgid "Update Filename" +msgstr "Обновить имя файла" + +#: src/GMDatabaseSource.cpp:1431 +msgid "Set export template…" +msgstr "Шаблон экспорта..." + +#: src/GMDatabaseSource.cpp:1645 +msgid "Update Tags?" +msgstr "Обновить теги?" + +#: src/GMDatabaseSource.cpp:1645 +msgid "" +"No tracks were updated.\n" +"Would you still like to write the tags for the selected tracks?" +msgstr "" +"Треки не были обновлены.\n" +"Хотите ли Вы записать теги в выделенные треки?" + +#: src/GMDatabaseSource.cpp:1700 +msgid "Remove Audio Files?" +msgstr "Удалить аудио-файлы?" + +#: src/GMDatabaseSource.cpp:1701 +msgid "Remove Audio Files..." +msgstr "Удалить аудио-файлы..." + +#: src/GMDatabaseSource.cpp:1701 +msgid "The following audio files are going to be removed" +msgstr "Следующие аудио-файлы будут удалены" + +#: src/GMDatabaseSource.cpp:1703 src/GMDatabaseSource.cpp:1877 +#: src/GMPlayListSource.cpp:280 src/GMStreamSource.cpp:270 +msgid "&Remove" +msgstr "&Удалить" + +#: src/GMDatabaseSource.cpp:1729 +msgid "Export Main Library" +msgstr "Экспортировать главную библиотеку" + +#: src/GMDatabaseSource.cpp:1731 +msgid "Export Play List" +msgstr "Экспортировать плейлист" + +#: src/GMDatabaseSource.cpp:1744 +msgid "Overwrite File?" +msgstr "Перезаписать файл?" + +#: src/GMDatabaseSource.cpp:1744 +msgid "File already exists. Would you like to overwrite it?" +msgstr "Файл уже существует. Перезаписать его?" + +#: src/GMDatabaseSource.cpp:1784 +msgid "Export Genre" +msgstr "Экспортировать жанр" + +#: src/GMDatabaseSource.cpp:1785 +msgid "Export tracks with genre to destination directory." +msgstr "Экспортировать треки выбранного жанра в выбранную папку." + +#: src/GMDatabaseSource.cpp:1787 +msgid "Export Artists" +msgstr "Экспортировать исполнителей" + +#: src/GMDatabaseSource.cpp:1788 +msgid "Export tracks from artist to destination directory." +msgstr "Экспортировать треки исполнителя в выбранную папку." + +#: src/GMDatabaseSource.cpp:1790 +msgid "Export Albums" +msgstr "Экспортировать альбомы" + +#: src/GMDatabaseSource.cpp:1791 +msgid "Export tracks from album to destination directory." +msgstr "Экспортировать реки из альбома в выбранную папку." + +#: src/GMDatabaseSource.cpp:1793 +msgid "Export Tracks" +msgstr "Экспортировать треки" + +#: src/GMDatabaseSource.cpp:1794 +msgid "Export tracks to destination directory." +msgstr "Экспортировать треки в выбранную папку." + +#: src/GMDatabaseSource.cpp:1803 +msgid "&Export" +msgstr "&Экспортировать" + +#: src/GMDatabaseSource.cpp:1853 src/GMPlayListSource.cpp:261 +msgid "Remove Genre?" +msgstr "Удалить жанр?" + +#: src/GMDatabaseSource.cpp:1854 +msgid "Remove tracks with genre from library?" +msgstr "Удалить треки выбранного жанра из библиотеки?" + +#: src/GMDatabaseSource.cpp:1858 src/GMPlayListSource.cpp:264 +msgid "Remove Artist?" +msgstr "Удалить исполнителя?" + +#: src/GMDatabaseSource.cpp:1859 +msgid "Remove tracks from artist from library?" +msgstr "Удалить треки выбранного исполнителя из библиотеки?" + +#: src/GMDatabaseSource.cpp:1863 src/GMPlayListSource.cpp:267 +msgid "Remove Album?" +msgstr "Удалить альбом?" + +#: src/GMDatabaseSource.cpp:1864 +msgid "Remove tracks from album from library?" +msgstr "Удалить треки выбранного альбома из библиотеки?" + +#: src/GMDatabaseSource.cpp:1868 src/GMPlayListSource.cpp:270 +msgid "Remove Track(s)?" +msgstr "Удалить трек(и)?" + +#: src/GMDatabaseSource.cpp:1869 +msgid "Remove track(s) from library?" +msgstr "Удалить трек(и) из библиотеки?" + +#: src/GMDatabaseSource.cpp:1881 src/GMPlayListSource.cpp:285 +msgid "Remove tracks from disk" +msgstr "Удалить треки с диска" + +#: src/GMDatabaseSource.cpp:1895 src/GMDatabaseSource.cpp:1899 +#: src/GMDatabaseSource.cpp:1903 src/GMDatabaseSource.cpp:1907 +#: src/GMPlayListSource.cpp:308 src/GMStreamSource.cpp:280 +msgid "Library Error" +msgstr "Ошибка библиотеки" + +#: src/GMDatabaseSource.cpp:1895 +msgid "Unable to remove genre from the library" +msgstr "Невозможно удалить жанр из библиотеки" + +#: src/GMDatabaseSource.cpp:1899 +msgid "Unable to remove artist from the library" +msgstr "Невозможно удалить исполнителя из библиотеки" + +#: src/GMDatabaseSource.cpp:1903 +msgid "Unable to remove album from the library" +msgstr "Невозможно удалить альбом из библиотеки" + +#: src/GMDatabaseSource.cpp:1907 src/GMPlayListSource.cpp:308 +msgid "Unable to remove track from the library." +msgstr "Невозможно удалить трек из библиотеки." + +#: src/GMDatabaseSource.cpp:2117 src/GMDatabaseSource.cpp:2118 +msgid "Create Playlist" +msgstr "Создать плейлист" + +#: src/GMDatabaseSource.cpp:2118 +msgid "Specify name of the new playlist" +msgstr "Задать имя нового плейлиста" + +#: src/GMDatabaseSource.cpp:2120 +msgid "&Create" +msgstr "&Создать" + +#: src/GMDatabaseSource.cpp:2125 src/GMPlayListSource.cpp:415 +#: ../../../fox-1.6.37/src/FXFileList.cpp:192 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:727 +msgid "Name" +msgstr "Имя" + +#: src/GMDatabaseSource.cpp:2127 +msgid "New Playlist" +msgstr "Новый плейлист" + +#: src/GMDatabaseSource.cpp:2178 src/GMDatabaseSource.cpp:2179 +msgid "Clear Music Library?" +msgstr "Очистить музыкальную библиотеку?" + +#: src/GMDatabaseSource.cpp:2179 +msgid "Remove all tracks from the music library?" +msgstr "Удалить все файлы из музыкальной библиотеки?" + +#: src/GMDatabaseSource.cpp:2181 +msgid "&Remove All" +msgstr "&Удалить все" + +#: src/GMDatabaseSource.cpp:2185 +msgid "Keep play lists" +msgstr "Оставить плейлисты" + +#: src/GMDatabaseSource.cpp:2212 src/GMDatabaseSource.cpp:2213 +msgid "Music Library Information" +msgstr "Информация о музыкальной библиотеке" + +#: src/GMDatabaseSource.cpp:2213 +msgid "You music collection consists of…" +msgstr "Ваша музыкальная коллекция состоит из..." + +#: src/GMDatabaseSource.cpp:2221 +msgid "Tracks:" +msgstr "Треков:" + +#: src/GMDatabaseSource.cpp:2226 +msgid "Artists:" +msgstr "Исполнителей:" + +#: src/GMDatabaseSource.cpp:2230 +msgid "Albums:" +msgstr "Альбомов:" + +#: src/GMDatabaseSource.cpp:2234 +msgid "Total Time:" +msgstr "Общее время:" + +#: src/GMEQDialog.cpp:96 +msgid "Equalizer" +msgstr "Эквалайзер" + +#: src/GMEQDialog.cpp:135 +msgid "Equalizer:" +msgstr "Эквалайзер:" + +#: src/GMEQDialog.cpp:137 +msgid "\tSave" +msgstr "\tСохранить" + +#: src/GMEQDialog.cpp:138 +msgid "\tReset" +msgstr "\tСбросить" + +#: src/GMEQDialog.cpp:139 +msgid "\tRemove" +msgstr "\tУдалить" + +#: src/GMEQDialog.cpp:172 +msgid "Pre-amp" +msgstr "Pre-amp" + +#: src/GMEQDialog.cpp:244 src/GMEQDialog.cpp:254 +msgid "Disabled" +msgstr "Недоступный" + +#: src/GMEQDialog.cpp:247 src/GMEQDialog.cpp:299 +msgid "Manual" +msgstr "Вручную" + +#: src/GMEQDialog.cpp:314 +msgid "Delete Preset" +msgstr "Удалить пресет" + +#: src/GMEQDialog.cpp:314 +#, c-format +msgid "Are you sure you want to delete %s preset?" +msgstr "Вы уверены, что хотите удалить %s пресет?" + +#: src/GMEQDialog.cpp:334 +msgid "Preset Name" +msgstr "Имя пресета" + +#: src/GMEQDialog.cpp:334 +msgid "Please enter preset name:" +msgstr "Введите имя пресета:" + +#: src/GMEQDialog.cpp:338 +msgid "Overwrite Preset" +msgstr "Перезаписать пресет" + +#: src/GMEQDialog.cpp:338 +#, c-format +msgid "Preset %s already exists. Would you like to overwrite it?" +msgstr "Пресет %s уже существует. Перезаписать его?" + +#: src/GMFontDialog.cpp:209 +#, fuzzy +msgid "Ultra Condensed" +msgstr "Ультра-уплотненная" + +#: src/GMFontDialog.cpp:210 +#, fuzzy +msgid "Extra Condensed" +msgstr "Экстра-уплотненная" + +#: src/GMFontDialog.cpp:211 +msgid "Condensed" +msgstr "Уплотненная" + +#: src/GMFontDialog.cpp:212 +#, fuzzy +msgid "Semi Condensed" +msgstr "Полу-уплотненная" + +#: src/GMFontDialog.cpp:214 +#, fuzzy +msgid "Semi Expanded" +msgstr "Полу-разреженная" + +#: src/GMFontDialog.cpp:215 +msgid "Expanded" +msgstr "Разреженная" + +#: src/GMFontDialog.cpp:216 +#, fuzzy +msgid "Extra Expanded" +msgstr "Экстра-разреженная" + +#: src/GMFontDialog.cpp:217 +#, fuzzy +msgid "Ultra Expanded" +msgstr "Ультра-разреженная" + +#: src/GMFontDialog.cpp:224 src/GMPreferencesDialog.cpp:1223 +msgid "Thin" +msgstr "" + +#: src/GMFontDialog.cpp:225 src/GMPreferencesDialog.cpp:1224 +#, fuzzy +msgid "Extra Light" +msgstr "экстра легкий" + +#: src/GMFontDialog.cpp:226 src/GMPreferencesDialog.cpp:1225 +#, fuzzy +msgid "Light" +msgstr "легкий" + +#: src/GMFontDialog.cpp:228 src/GMPreferencesDialog.cpp:1227 +#, fuzzy +msgid "Medium" +msgstr "средний" + +#: src/GMFontDialog.cpp:229 src/GMPreferencesDialog.cpp:1228 +#, fuzzy +msgid "Demibold" +msgstr "полужирный" + +#: src/GMFontDialog.cpp:230 src/GMPreferencesDialog.cpp:1229 +msgid "Bold" +msgstr "" + +#: src/GMFontDialog.cpp:231 src/GMPreferencesDialog.cpp:1230 +#, fuzzy +msgid "Extra Bold" +msgstr "очень жирный" + +#: src/GMFontDialog.cpp:232 src/GMPreferencesDialog.cpp:1231 +#, fuzzy +msgid "Heavy" +msgstr "тяжелый" + +#: src/GMFontDialog.cpp:239 +#, fuzzy +msgid "Reverse Oblique" +msgstr "обратный наклонный" + +#: src/GMFontDialog.cpp:240 +#, fuzzy +msgid "Reverse Italic" +msgstr "обратный курсив" + +#: src/GMFontDialog.cpp:242 +#, fuzzy +msgid "Italic" +msgstr "курсив" + +#: src/GMFontDialog.cpp:243 +#, fuzzy +msgid "Oblique" +msgstr "наклонный" + +#: src/GMFontDialog.cpp:265 +msgid "Normal" +msgstr "Нормальная" + +#: src/GMImportDialog.cpp:91 ../../../fox-1.6.37/src/FXDirSelector.cpp:137 +msgid "&Directory:" +msgstr "&Папка:" + +#: src/GMImportDialog.cpp:166 ../../../fox-1.6.37/src/FXFileSelector.cpp:196 +msgid "&File Name:" +msgstr "Имя &файла" + +#: src/GMImportDialog.cpp:168 ../../../fox-1.6.37/src/FXMessageBox.cpp:102 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:106 +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:134 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:198 +msgid "&OK" +msgstr "&OK" + +#: src/GMImportDialog.cpp:170 ../../../fox-1.6.37/src/FXFileSelector.cpp:200 +msgid "File F&ilter:" +msgstr "Файловый ф&ильтр" + +#: src/GMImportDialog.cpp:174 ../../../fox-1.6.37/src/FXFileSelector.cpp:204 +msgid "Read Only" +msgstr "Только чтение" + +#: src/GMImportDialog.cpp:179 src/GMSearch.cpp:565 src/GMSearch.cpp:647 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:208 +msgid "Directory:" +msgstr "Папка:" + +#: src/GMImportDialog.cpp:186 ../../../fox-1.6.37/src/FXFileSelector.cpp:228 +msgid "&Set bookmark\t\tBookmark current directory." +msgstr "Создать закладку\t\tСохранить данную директорию в закладках." + +#: src/GMImportDialog.cpp:187 ../../../fox-1.6.37/src/FXFileSelector.cpp:229 +msgid "&Clear bookmarks\t\tClear bookmarks." +msgstr "&Очистить закладки\t\tОчистить закладки." + +#: src/GMImportDialog.cpp:203 ../../../fox-1.6.37/src/FXFileSelector.cpp:244 +msgid "\tGo up one directory\tMove up to higher directory." +msgstr "\tПерейти на уровень выше\tПерейти на уровень выше" + +#: src/GMImportDialog.cpp:204 ../../../fox-1.6.37/src/FXFileSelector.cpp:245 +msgid "\tGo to home directory\tBack to home directory." +msgstr "\tПерейти в домашнюю папку\tВернуться в домашнюю папку." + +#: src/GMImportDialog.cpp:205 ../../../fox-1.6.37/src/FXFileSelector.cpp:246 +msgid "\tGo to work directory\tBack to working directory." +msgstr "\tПерейти в рабочую папку\tВернуться в рабочую папку." + +#: src/GMImportDialog.cpp:206 ../../../fox-1.6.37/src/FXFileSelector.cpp:247 +msgid "\tBookmarks\tVisit bookmarked directories." +msgstr "\tЗакладки\tПерейти в запомненные папки" + +#: src/GMImportDialog.cpp:209 ../../../fox-1.6.37/src/FXFileSelector.cpp:250 +msgid "\tCreate new directory\tCreate new directory." +msgstr "\tСоздать новую папку\tСоздать новую папку" + +#: src/GMImportDialog.cpp:210 ../../../fox-1.6.37/src/FXFileSelector.cpp:251 +msgid "\tShow list\tDisplay directory with small icons." +msgstr "\tПоказать список\tОтобразить папку с маленькими иконками." + +#: src/GMImportDialog.cpp:211 ../../../fox-1.6.37/src/FXFileSelector.cpp:252 +msgid "\tShow icons\tDisplay directory with big icons." +msgstr "\tПоказать иконки\tОтобразить папку с крупными иконками." + +#: src/GMImportDialog.cpp:212 ../../../fox-1.6.37/src/FXFileSelector.cpp:253 +msgid "\tShow details\tDisplay detailed directory listing." +msgstr "\tПоказать детальный список\tОтобразить детальный вид папки." + +#: src/GMImportDialog.cpp:213 ../../../fox-1.6.37/src/FXFileSelector.cpp:254 +msgid "\tShow hidden files\tShow hidden files and directories." +msgstr "\tПоказать скрытые файлы\tПоказать скрытые файлы и папки." + +#: src/GMImportDialog.cpp:213 ../../../fox-1.6.37/src/FXFileSelector.cpp:254 +msgid "\tHide Hidden Files\tHide hidden files and directories." +msgstr "\tСкрыть скрытые файлы\tСкрыть скрытые файлы и папки." + +#: src/GMImportDialog.cpp:412 +msgid "Synchronize Folder" +msgstr "Синхронизировать папку" + +#: src/GMImportDialog.cpp:414 +#, fuzzy +msgid "Import Playlist" +msgstr "Экспортировать плейлист" + +#: src/GMImportDialog.cpp:416 +msgid "Import Music" +msgstr "Импортировать музыку" + +#: src/GMImportDialog.cpp:448 +msgid "&File(s)" +msgstr "&Файл(ы)" + +#: src/GMImportDialog.cpp:475 +msgid "&Directory" +msgstr "&Папка" + +#: src/GMImportDialog.cpp:478 +msgid "Exclude Filter\tFilter out directories and/or files based on pattern" +msgstr "Отключить фильтр\tОтфильтровать папки и/или файлы по шаблону" + +#: src/GMImportDialog.cpp:481 +msgid "Folders:" +msgstr "Папки" + +#: src/GMImportDialog.cpp:483 +msgid "Files:" +msgstr "Файлы" + +#: src/GMImportDialog.cpp:499 src/GMImportDialog.cpp:560 +msgid "&Sync" +msgstr "&Синхронизировать" + +#: src/GMImportDialog.cpp:501 +msgid "Sync Operation" +msgstr "Операция синхронизации" + +#: src/GMImportDialog.cpp:504 +msgid "Import new tracks\tImports files not yet in the database." +msgstr "" +"Импортировать новые треки\tИмпортировать файлы, не содержащиеся в базе " +"данных." + +#: src/GMImportDialog.cpp:505 +msgid "Remove tracks that have been deleted from disk" +msgstr "Удалить треки, удаленные с диска" + +#: src/GMImportDialog.cpp:507 +msgid "Update existing tracks:" +msgstr "Обновить существующие треки:" + +#: src/GMImportDialog.cpp:508 +msgid "" +"Modified since last import\tOnly reread the tag when the file has been " +"modified." +msgstr "" +"Измененные после последнего импорта\tПеречитать тег,только если файл был " +"изменен." + +#: src/GMImportDialog.cpp:510 +msgid "All\tAlways read the tags" +msgstr "Все\tВсегда читать теги" + +#: src/GMImportDialog.cpp:511 +msgid "Remove tracks found in folder from database" +msgstr "Удалить найденные в папке треки из базы данных" + +#: src/GMImportDialog.cpp:514 +msgid "&Track" +msgstr "&Трек" + +#: src/GMImportDialog.cpp:517 +msgid "Parse Settings" +msgstr "Настройки разбора" + +#: src/GMImportDialog.cpp:521 +msgid "Parse info from:" +msgstr "Искать информацию в:" + +#: src/GMImportDialog.cpp:524 +msgid "Tag" +msgstr "Тег" + +#: src/GMImportDialog.cpp:526 +msgid "Both" +msgstr "Везде" + +#: src/GMImportDialog.cpp:528 +msgid "Default value:" +msgstr "По умолчанию:" + +#: src/GMImportDialog.cpp:532 +msgid "Set track number based on scan order." +msgstr "Установить номер трека по порядку сканирования." + +#: src/GMImportDialog.cpp:537 +msgid "" +"%T - title %A - album name\n" +"%P - album artist name %p - track artist name \n" +"%N - track number %G - genre" +msgstr "" +"%T - название %A - название альбома\n" +"%P - имя исполнителя альбома %p - имя исполнителя трека \n" +"%N - номер трека %G - жанр" + +#: src/GMImportDialog.cpp:549 +msgid "Replace underscores with spaces" +msgstr "Заменить подчеркивания пробелами" + +#: src/GMImportDialog.cpp:562 +msgid "&Import" +msgstr "&Импортировать" + +#: src/GMPlayer.cpp:212 +msgid "Unable to initialize audio driver." +msgstr "Невозможно инициализировать аудио драйвер." + +#: src/GMPlayer.cpp:714 +msgid "Unknown host." +msgstr "Неизвестный адрес." + +#: src/GMPlayer.cpp:715 +msgid "Unknown device" +msgstr "Неизвестное устройство" + +#: src/GMPlayer.cpp:716 +msgid "Network not reachable." +msgstr "Сеть недоступна." + +#: src/GMPlayer.cpp:717 +msgid "Audio output unavailable." +msgstr "Аудио выход недоступен." + +#: src/GMPlayer.cpp:718 +msgid "Connection Refused." +msgstr "Соединение отклонено." + +#: src/GMPlayer.cpp:719 +msgid "File not found." +msgstr "Файл не найден." + +#: src/GMPlayer.cpp:720 +msgid "Resource not accessible. Check permissions" +msgstr "Ресурс недоступен. Проверьте разрешения" + +#: src/GMPlayer.cpp:721 +msgid "Read Error" +msgstr "Ошибка чтения" + +#: src/GMPlayer.cpp:722 +msgid "Error while loading library/plugin" +msgstr "Ошибка загрузки библиотеки/плагина" + +#: src/GMPlayer.cpp:723 +msgid "Warning" +msgstr "Предупреждение" + +#: src/GMPlayer.cpp:724 +msgid "Security Warning" +msgstr "Предупреждение безопасности" + +#: src/GMPlayer.cpp:725 +msgid "Unknown Error" +msgstr "Неизвестная ошибка" + +#: src/GMPlayer.cpp:761 +msgid "Error" +msgstr "Ошибка" + +#: src/GMPlayerManager.cpp:439 +#, c-format +msgid "Unable to create directory %s\n" +msgstr "Невозможно создать папку %s\n" + +#: src/GMPlayerManager.cpp:612 +msgid "" +"For some reason the FOX library was compiled without PNG support.\n" +"In order to show all icons, Goggles Music Manager requires PNG\n" +"support in the FOX library. If you've compiled FOX yourself, most\n" +"likely the libpng header files were not installed on your system." +msgstr "" +"По каким-то причинам библиотека FOX была скомпилирована без поддержки PNG.\n" +"Для отображения всех иконок Goggles Music Manager требует поддержку PNG в\n" +"библиотеке FOX. Если Вы сами компилируете FOX, вероятно заголовочные файлы\n" +"libpng не установлены в Вашей системе." + +#: src/GMPlayerManager.cpp:623 +msgid "Session bus not available. All features requiring dbus are disabled." +msgstr "Сессия bus недоступна. Все функции, требующие dbus, недоступны." + +#: src/GMPlayerManager.cpp:633 +msgid "A DBus error occurred. All features requiring sessionbus are disabled." +msgstr "Произошла ошибка DBus. Все функции, требующие сессию bus, недоступны." + +#: src/GMPlayerManager.cpp:644 +msgid "" +"Session Bus not available. All features requiring sessionbus are disabled." +msgstr "Сессия bus недоступна. Все функции, требующие сессию bus, недоступны." + +#: src/GMPlayerManager.cpp:719 src/GMPreferencesDialog.cpp:594 +msgid "Audio Device Error" +msgstr "Ошибка аудио устройства" + +#: src/GMPlayerManager.cpp:1551 +msgid "Last.FM Error" +msgstr "Ошибка Last.FM" + +#: src/GMPlayerManager.cpp:1558 +msgid "Playback Error" +msgstr "Ошибка воспроизведения" + +#: src/GMPlayerManager.cpp:1708 +#, c-format +msgid "" +"%s\n" +"%s (%d)" +msgstr "" + +#: src/GMPlayListSource.cpp:99 src/GMPlayListSource.cpp:105 +#: src/GMPlayListSource.cpp:111 src/GMPlayListSource.cpp:121 +msgid "Remove…\tDel\tRemove track(s) from play list." +msgstr "Удалить...\tDel\tУдалить трек(и) из плейлиста." + +#: src/GMPlayListSource.cpp:161 +msgid "Edit…" +msgstr "Редактировать..." + +#: src/GMPlayListSource.cpp:162 +#, fuzzy +msgid "Import…" +msgstr "Экспортировать..." + +#: src/GMPlayListSource.cpp:164 +msgid "Remove Playlist" +msgstr "Удалить плейлист" + +#: src/GMPlayListSource.cpp:262 +msgid "Remove tracks with genre from play list?" +msgstr "Удалить треки выбранного жанра из плейлиста?" + +#: src/GMPlayListSource.cpp:265 +msgid "Remove tracks from artist from play list?" +msgstr "Удалить треки выбранного исполнителя из плейлиста?" + +#: src/GMPlayListSource.cpp:268 +msgid "Remove tracks from album from play list?" +msgstr "Удалить треки выбранного альбома из плейлиста?" + +#: src/GMPlayListSource.cpp:271 +msgid "Remove track(s) from play list?" +msgstr "Удалить трек(и) из плейлиста?" + +#: src/GMPlayListSource.cpp:284 +msgid "Remove tracks from music library" +msgstr "Удалить треки из музыкальной библиотеки" + +#: src/GMPlayListSource.cpp:406 src/GMPlayListSource.cpp:407 +msgid "Edit Playlist" +msgstr "Редактировать плейлист" + +#: src/GMPlayListSource.cpp:407 +msgid "Change playlist name" +msgstr "Переименовать плейлист" + +#: src/GMPlayListSource.cpp:432 +msgid "Delete Play List?" +msgstr "Удалить плейлист?" + +#: src/GMPlayListSource.cpp:432 +msgid "Are you sure you want to delete the playlist?" +msgstr "Вы уверены, что хотите удалить плейлист?" + +#: src/GMPlayListSource.cpp:432 ../../../fox-1.6.37/src/FXMessageBox.cpp:111 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:116 +msgid "&Yes" +msgstr "&Да" + +#: src/GMPlayListSource.cpp:432 ../../../fox-1.6.37/src/FXMessageBox.cpp:112 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:117 +msgid "&No" +msgstr "&Нет" + +#: src/GMPreferencesDialog.cpp:252 +msgid "Preferences" +msgstr "Опции" + +#: src/GMPreferencesDialog.cpp:286 +msgid "&General" +msgstr "&Общие" + +#: src/GMPreferencesDialog.cpp:289 +msgid "Sort Options" +msgstr "Опции сортировки" + +#: src/GMPreferencesDialog.cpp:293 +msgid "Ignore leading words" +msgstr "Игнорировать предшествующие слова" + +#: src/GMPreferencesDialog.cpp:296 +msgid "Album Covers" +msgstr "Обложки альбомов" + +#: src/GMPreferencesDialog.cpp:299 +msgid "Show album cover of playing track\tShow album cover of playing track" +msgstr "" +"Показывать обложку альбома проигрываемого трека\tПоказывать обложку альбома " +"проигрываемого трека" + +#: src/GMPreferencesDialog.cpp:300 +msgid "Show album covers in album browser\tShow album covers in album browser" +msgstr "" +"Показывать обложку альбома в браузере обложек\tПоказывать обложку альбома в " +"браузере обложек" + +#: src/GMPreferencesDialog.cpp:302 +msgid "System Tray" +msgstr "Системный трей" + +#: src/GMPreferencesDialog.cpp:304 +msgid "Show Tray Icon\tShow tray icon in the system tray." +msgstr "Отображать иконку в трее\tОтображать иконку в системном трее." + +#: src/GMPreferencesDialog.cpp:307 +msgid "" +"Show Track Change Notifications\tInform notification daemon of track changes." +msgstr "" +"Показывать сообщение при смене трека\tПоказывать информационное сообщение " +"при смене трека." + +#: src/GMPreferencesDialog.cpp:311 +msgid "Last.fm" +msgstr "Last.fm" + +#: src/GMPreferencesDialog.cpp:315 +msgid "" +"This version of Goggles Music Manager is\n" +"not supported by Last-FM. Please upgrade\n" +"to a newer version of GMM." +msgstr "" +"Эта версия Goggles Music Manager\n" +"не поддерживает Last-FM. Пожалуйста,\n" +"обновитесь до более новой версии GMM." + +#: src/GMPreferencesDialog.cpp:321 +msgid "Service:" +msgstr "" + +#: src/GMPreferencesDialog.cpp:338 +#, fuzzy +msgid "&Sign up…" +msgstr "&Зарегистрироваться на last.fm..." + +#: src/GMPreferencesDialog.cpp:344 +msgid "Username:" +msgstr "Логин:" + +#: src/GMPreferencesDialog.cpp:346 +msgid "Password:" +msgstr "Пароль:" + +#: src/GMPreferencesDialog.cpp:349 +msgid "Scrobble" +msgstr "" + +#: src/GMPreferencesDialog.cpp:359 +msgid "&Window" +msgstr "&Окно" + +#: src/GMPreferencesDialog.cpp:362 +msgid "Window" +msgstr "Окно" + +#: src/GMPreferencesDialog.cpp:365 +#, fuzzy +msgid "Close button minimizes to tray" +msgstr "Кнопка \"Закрыть\" прячет главное окно" + +#: src/GMPreferencesDialog.cpp:366 +msgid "Show Status Bar" +msgstr "Показывать строку состояния" + +#: src/GMPreferencesDialog.cpp:368 +msgid "Show Icons in Track Browser" +msgstr "Прятать иконке в браузере треков" + +#: src/GMPreferencesDialog.cpp:370 +msgid "Display playing track in title bar" +msgstr "Отображать проигрываемый файл в названии окна" + +#: src/GMPreferencesDialog.cpp:372 +msgid "Player Controls" +msgstr "Кнопки управления плеером" + +#: src/GMPreferencesDialog.cpp:376 +msgid "Location:" +msgstr "Расположение:" + +#: src/GMPreferencesDialog.cpp:378 +msgid "Top" +msgstr "Вверху" + +#: src/GMPreferencesDialog.cpp:379 +msgid "Bottom" +msgstr "Внизу" + +#: src/GMPreferencesDialog.cpp:387 +msgid "Title Format:" +msgstr "" + +#: src/GMPreferencesDialog.cpp:391 +msgid "Style:" +msgstr "Стиль:" + +#: src/GMPreferencesDialog.cpp:392 +msgid "Show Labels" +msgstr "Показывать подписи" + +#: src/GMPreferencesDialog.cpp:395 +msgid "Large Icons" +msgstr "Большие иконки" + +#: src/GMPreferencesDialog.cpp:399 +#, fuzzy +msgid "A&ppearance" +msgstr "&Вид" + +#: src/GMPreferencesDialog.cpp:402 +msgid "Colors" +msgstr "Цвета" + +#: src/GMPreferencesDialog.cpp:409 +msgid "fg\tForeground Color" +msgstr "fg\tЦвет переднего плана" + +#: src/GMPreferencesDialog.cpp:410 +msgid "bg\tBackground Color" +msgstr "bg\tЦвет фона" + +#: src/GMPreferencesDialog.cpp:411 +msgid "alt bg\tAlternative Background Color" +msgstr "alt bg\tАльтернативный цвет фона" + +#: src/GMPreferencesDialog.cpp:422 +msgid "Normal\tNormal Text Color" +msgstr "Нормальный\tЦвет нормального текста" + +#: src/GMPreferencesDialog.cpp:426 +msgid "Base\tBase Color" +msgstr "Базовый\tБазовый цвет" + +#: src/GMPreferencesDialog.cpp:429 +msgid "Selected\tSelected Text Color" +msgstr "Выделенный\tЦвет выделенного текста" + +#: src/GMPreferencesDialog.cpp:433 +#, fuzzy +msgid "Menu\tMenu Base Color" +msgstr "Меню\tЦвет текста меню" + +#: src/GMPreferencesDialog.cpp:437 +msgid "Menu\tMenu Text Color" +msgstr "Меню\tЦвет текста меню" + +#: src/GMPreferencesDialog.cpp:441 +msgid "Border\tBorder Color" +msgstr "Рамка\tЦвет рамки" + +#: src/GMPreferencesDialog.cpp:445 +msgid "Tooltip\tTooltip Color" +msgstr "Подсказка\tЦвет подсказки" + +#: src/GMPreferencesDialog.cpp:449 +msgid "Hilite\tHilite Color" +msgstr "Выделенный\tЦвет выделенного" + +#: src/GMPreferencesDialog.cpp:456 +msgid "Shadow\tShadow Color" +msgstr "Тень\tЦвет тени" + +#: src/GMPreferencesDialog.cpp:459 +msgid "Playing\tPlaying Track Color" +msgstr "Проигрываемый\tЦвет проигрываемого трека" + +#: src/GMPreferencesDialog.cpp:463 +#, fuzzy +msgid "Tray\tTray Background Color" +msgstr "bg\tЦвет фона" + +#: src/GMPreferencesDialog.cpp:473 +msgid "Presets:" +msgstr "Пресеты:" + +#: src/GMPreferencesDialog.cpp:481 +msgid "Font & Icons" +msgstr "" + +#: src/GMPreferencesDialog.cpp:487 +#, fuzzy +msgid "Default Font" +msgstr "По умолчанию:" + +#: src/GMPreferencesDialog.cpp:489 +msgid "Change…" +msgstr "Изменить..." + +#: src/GMPreferencesDialog.cpp:490 +msgid "Icons" +msgstr "Иконки" + +#: src/GMPreferencesDialog.cpp:509 +msgid "&Audio" +msgstr "&Аудио" + +#: src/GMPreferencesDialog.cpp:512 +msgid "Engine" +msgstr "Движок" + +#: src/GMPreferencesDialog.cpp:516 +msgid "Audio Driver:" +msgstr "Аудио драйвер" + +#: src/GMPreferencesDialog.cpp:527 +msgid "Close audio device on pause." +msgstr "Закрывать аудио устройство при паузе." + +#: src/GMPreferencesDialog.cpp:528 +msgid "Turn off playback engine on stop." +msgstr "Выключать движок проигрыванию при останове." + +#: src/GMPreferencesDialog.cpp:529 +msgid "" +"Turn on playback engine on startup.\tFor faster startup, playback engine is " +"normally started when first track is played.\tFor faster startup, playback " +"engine is normally started when first track is played." +msgstr "" +"Включение движка проигрывания при старте.\tДля быстрого старта, движок " +"проигрывания запускается при первом проигрывании трека.\tДля быстрого " +"старта, движок проигрывания запускается при первом проигрывании трека." + +#: src/GMPreferencesDialog.cpp:532 +msgid "Playback" +msgstr "Проигрывание" + +#: src/GMPreferencesDialog.cpp:536 +msgid "Replay Gain:" +msgstr "Replay Gain:" + +#: src/GMPreferencesDialog.cpp:538 +msgid "Off" +msgstr "" + +#: src/GMPreferencesDialog.cpp:543 +msgid "Gapless playback" +msgstr "Gapless проигрывание" + +#: src/GMPreferencesDialog.cpp:544 +msgid "Volume Normalization" +msgstr "Нормализация звука" + +#: src/GMPreferencesDialog.cpp:594 +#, c-format +msgid "Failed to open requested audio driver: %s" +msgstr "Не удается открыть требуемый аудио драйвер: %s" + +#: src/GMPreferencesDialog.cpp:678 +msgid "Current" +msgstr "Текущий" + +#: src/GMPreferencesDialog.cpp:696 +msgid "Custom" +msgstr "Выборочно" + +#: src/GMPreferencesDialog.cpp:769 src/GMPreferencesDialog.cpp:774 +#: src/GMWindow.cpp:1133 src/GMWindow.cpp:1140 src/GMWindow.cpp:1147 +#: src/GMWindow.cpp:1154 +msgid "Unable to launch webbrowser" +msgstr "Невозможно запустить веб-браузер" + +#: src/GMPreferencesDialog.cpp:1172 +msgid "Select Normal Font" +msgstr "Выбрать нормальный шрифт" + +#: src/GMRemote.cpp:295 +msgid "&Next" +msgstr "&Следующий" + +#: src/GMRemote.cpp:296 +msgid "P&revious" +msgstr "П&редыдущий" + +#: src/GMRemote.cpp:297 src/GMWindow.cpp:1197 +msgid "&Play" +msgstr "&Играть" + +#: src/GMRemote.cpp:298 +msgid "&Stop" +msgstr "&Стоп" + +#: src/GMRemote.cpp:300 +msgid "Show Browser" +msgstr "Показать браузер" + +#: src/GMRemote.cpp:301 src/GMTrayIcon.cpp:307 +msgid "Quit" +msgstr "Выйти" + +#: src/GMRemote.cpp:385 +#, fuzzy +msgid "\tShow Browser\tShow Browser" +msgstr "Показать браузер" + +#: src/GMRemote.cpp:387 +msgid "\tStart Playback\tStart Playback" +msgstr "\tНачать воспроизведение\tНачать воспроизведение" + +#: src/GMRemote.cpp:388 +msgid "\tStop Playback\tStop Playback" +msgstr "\tОстановить воспроизведение\tОстановить воспроизведение" + +#: src/GMRemote.cpp:390 +msgid "\tPlay Previous Track\tPlay previous track." +msgstr "\tПроиграть предыдущий трек\tПроиграть предыдущий трек" + +#: src/GMRemote.cpp:391 +msgid "\tPlay Next Track\tPlay next track." +msgstr "\tПроиграть следующий трек\tПроиграть следующий трек" + +#: src/GMRemote.cpp:397 src/GMWindow.cpp:222 +msgid "\tAdjust Volume\tAdjust Volume" +msgstr "\tВыровнять громкость\tВыровнять громкость" + +#: src/GMSearch.cpp:128 +msgid "Unable to open the database" +msgstr "Невозможно открыть базу данных" + +#: src/GMSearch.cpp:252 +msgid "Database Error: Unable to retrieve all filenames." +msgstr "Ошибка базы данных: Невозможно найти все имена файлов" + +#: src/GMSearch.cpp:280 +msgid "Unable to update track" +msgstr "Невозможно обновить трек" + +#: src/GMSearch.cpp:439 +msgid "Unable to insert track into the database" +msgstr "Невозможно добавить трек в базу данных" + +#: src/GMSearch.cpp:505 src/GMTrackDatabase.cpp:205 +msgid "Fatal Error" +msgstr "Критическая ошибка" + +#: src/GMSearch.cpp:505 +#, c-format +msgid "" +"%s\n" +"Please contact support if this error keeps occuring." +msgstr "" +"%s\n" +"Пожалуйста, обратитесь в поддержку, если эта ошибка продолжает появляться." + +#: src/GMSearch.cpp:534 src/GMSearch.cpp:584 +msgid "Updating Database..." +msgstr "Обновление базы данных" + +#: src/GMSearch.cpp:537 src/GMSearch.cpp:626 +msgid "Please wait. This may take a while." +msgstr "Пожалуйста, подождите. Это займет некоторое время." + +#: src/GMSearch.cpp:555 src/GMSearch.cpp:637 +msgid "New Tracks:" +msgstr "Новые треки:" + +#: src/GMSearch.cpp:561 src/GMSearch.cpp:643 +msgid "File:" +msgstr "Файл:" + +#: src/GMSearch.cpp:594 src/GMSearch.cpp:623 +msgid "Importing Files..." +msgstr "Импорт файлов..." + +#: src/GMSearch.cpp:652 +msgid "&Stop Import" +msgstr "О&становить импорт" + +#: src/GMSourceView.cpp:54 +msgid "Sources\tPress to change sorting order\tPress to change sorting order" +msgstr "" +"Источники\tНажмите, чтобы изменить порядок сортировки\tНажмите, чтобы " +"изменить порядок сортировки" + +#: src/GMSourceView.cpp:245 src/GMWindow.cpp:261 +msgid "New Playlist…\t\tCreate a new playlist" +msgstr "Новый плейлист...\t\tСоздать новый плейлист" + +#: src/GMSourceView.cpp:246 src/GMWindow.cpp:262 +#, fuzzy +msgid "Import Playlist…\t\tImport existing playlist" +msgstr "Новый плейлист...\t\tСоздать новый плейлист" + +#: src/GMSourceView.cpp:247 src/GMWindow.cpp:263 +msgid "New Radio Station…\t\tCreate a new playlist" +msgstr "Новая радио-станция...\t\tСоздать новый плейлист" + +#: src/GMStreamSource.cpp:64 +msgid "Station" +msgstr "Радио-станция" + +#: src/GMStreamSource.cpp:112 src/GMStreamSource.cpp:118 +msgid "New Station…\t\t" +msgstr "Новая радио-станция...\t\t" + +#: src/GMStreamSource.cpp:117 +msgid "Edit…\t\t" +msgstr "Редактировать...\t\t" + +#: src/GMStreamSource.cpp:119 +msgid "Remove\t\tRemove." +msgstr "Удалить\t\tУдалить" + +#: src/GMStreamSource.cpp:165 src/GMStreamSource.cpp:166 +msgid "New Internet Radio Station" +msgstr "Новая интернет радио-станция" + +#: src/GMStreamSource.cpp:166 +msgid "Specify url and description of new station" +msgstr "Укажите ссылку и описание новой радио-станции" + +#: src/GMStreamSource.cpp:168 +#, fuzzy +msgid "C&reate" +msgstr "&Создать" + +#: src/GMStreamSource.cpp:173 src/GMStreamSource.cpp:211 +msgid "Location" +msgstr "Расположение" + +#: src/GMStreamSource.cpp:175 src/GMStreamSource.cpp:214 +msgid "Description" +msgstr "Описание" + +#: src/GMStreamSource.cpp:189 +msgid "Untitled" +msgstr "Без имени" + +#: src/GMStreamSource.cpp:202 src/GMStreamSource.cpp:203 +msgid "Edit Internet Radio Station" +msgstr "Редактировать интернет радио-станцию" + +#: src/GMStreamSource.cpp:203 +msgid "Update url and description of station" +msgstr "Обновить ссылку и описание радио-станции" + +#: src/GMStreamSource.cpp:265 +msgid "Remove Internet Radio Station(s)?" +msgstr "Удалить интернет радио-станцию(и)?" + +#: src/GMStreamSource.cpp:266 +msgid "Remove Internet Radio Station(s) from library?" +msgstr "Удалить интернет радио-станцию(и) из библиотеки?" + +#: src/GMStreamSource.cpp:280 +#, c-format +msgid "Unable to remove station from the library." +msgstr "Невозможно удалить радио-станцию из библиотеки." + +#: src/GMTrackDatabase.cpp:193 +msgid "" +"An incompatible (future) version of the database was found.\n" +"This usually happens when you try to downgrade to a older version of GMM\n" +"Press OK to continue and reset the database (all information will be " +"lost!).\n" +"Press Cancel to quit now and leave the database as is." +msgstr "" + +#: src/GMTrackDatabase.cpp:205 +msgid "" +"Goggles Music Manager was unable to open the database.\n" +"The database may have been corrupted. Please remove ~/.goggles/goggles.db to " +"try again.\n" +"if the error keeps occuring, please file an issue at http://code.google.com/" +"p/gogglesmm" +msgstr "" +"Goggles Music Manager не может открыть базу данных.\n" +"Вероятно база данных повреждена. Удалить файл ~/.goggles/goggles.db и " +"попробуйте снова.\n" +"Если ошибка не исчезнет, сообщите о ней на http://code.google.com/p/gogglesmm" + +#: src/GMTrackView.cpp:245 +msgid "\tClose Filter\tClose Filter" +msgstr "" + +#: src/GMTrackView.cpp:246 +#, fuzzy +msgid "&Find" +msgstr "Найти" + +#: src/GMTrackView.cpp:252 +#, fuzzy +msgid "Tags\tPress to change sorting order\tPress to change sorting order" +msgstr "" +"Жанры\tНажмите, чтобы изменить порядок сортировки\tНажмите, чтобы изменить " +"порядок сортировки" + +#: src/GMTrackView.cpp:256 +msgid "Artists\tPress to change sorting order\tPress to change sorting order" +msgstr "" +"Исполнители\tНажмите, чтобы изменить порядок сортировки\tНажмите, чтобы " +"изменить порядок сортировки" + +#: src/GMTrackView.cpp:260 +msgid "Albums\tPress to change sorting order\tPress to change sorting order" +msgstr "" +"Альбомы\tНажмите, чтобы изменить порядок сортировки\tНажмите, чтобы изменить " +"порядок сортировки" + +#: src/GMTrackView.cpp:282 src/GMWindow.cpp:299 +msgid "&Configure Columns…" +msgstr "&Настроить столбцы..." + +#: src/GMTrackView.cpp:286 +msgid "Browse" +msgstr "Просмотреть" + +#: src/GMTrackView.cpp:287 +msgid "Shuffle\tCtrl-R" +msgstr "Перемешать\tCtrl-R" + +#: src/GMTrackView.cpp:294 ../../../fox-1.6.37/src/FXDirSelector.cpp:388 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:734 +msgid "Reverse" +msgstr "В обратном порядке" + +#: src/GMTrackView.cpp:804 +#, c-format +msgid "All %d Genres" +msgstr "Все %d жанры" + +#: src/GMTrackView.cpp:806 +msgid "All Genres" +msgstr "Все жанры" + +#: src/GMTrackView.cpp:832 +#, c-format +msgid "All %d Artists" +msgstr "Все %d исполнители" + +#: src/GMTrackView.cpp:872 +#, c-format +msgid "All %d Albums" +msgstr "Все %d альбомы" + +#: src/GMTrackView.cpp:1258 +#, c-format +msgid "By %s" +msgstr "По %s" + +#: src/GMTrackView.cpp:1586 src/GMTrackView.cpp:1609 +msgid "Sort by Album Year" +msgstr "Сортировать по году выхода альбома" + +#: src/GMTrackView.cpp:1637 +msgid "&Columns\t\tChange Visible Columns." +msgstr "&Столбцы\t\tИзменить видимые столбцы." + +#: src/GMTrackView.cpp:1638 +msgid "&Sort\t\tChange Sorting." +msgstr "Сор&тировка\t\tИзменить сортировку." + +#: src/GMTrayIcon.cpp:302 src/GMWindow.cpp:841 src/GMWindow.cpp:925 +#: src/GMWindow.cpp:948 src/GMWindow.cpp:954 +msgid "Play" +msgstr "Воспроизвести" + +#: src/GMTrayIcon.cpp:303 src/GMWindow.cpp:843 +msgid "Stop" +msgstr "Остановить" + +#: src/GMTrayIcon.cpp:304 +msgid "Previous Track" +msgstr "Предыдущий трек" + +#: src/GMTrayIcon.cpp:305 +msgid "Next Track" +msgstr "Следующий трек" + +#: src/GMWindow.cpp:205 +msgid "Play\tStart Playback\tStart Playback" +msgstr "Воспроизвести\tНачать воспроизведение\tНачать воспроизведение" + +#: src/GMWindow.cpp:205 +msgid "Pause\tPause\tPause Playback" +msgstr "Пауза\tПауза\tПриостановить воспроизведение" + +#: src/GMWindow.cpp:206 +msgid "Stop\tStop Playback\tStop Playback" +msgstr "Остановить\tОстановить воспроизведение\tОстановить воспроизведение" + +#: src/GMWindow.cpp:208 +msgid "Previous\tPlay Previous Track\tPlay previous track." +msgstr "" +"Предыдущий\tВоспроизвести предыдущий трек\tВоспроизвести предыдущий трек." + +#: src/GMWindow.cpp:209 +msgid "Next\tPlay Next Track\tPlay next track." +msgstr "Следующий\tВоспроизвести следующий трек\tВоспроизвести следующий трек." + +#: src/GMWindow.cpp:255 +msgid "&Music" +msgstr "&Музыка" + +#: src/GMWindow.cpp:256 +msgid "Import Folder…\tCtrl-O\tImport Music from folder into Library" +msgstr "" +"Импортировать папку...\tCtrl-O\tИмпортировать музыку из папки в библиотеку" + +#: src/GMWindow.cpp:257 +msgid "Sync Folder…\t\tSynchronize Folder with Music in Library" +msgstr "" +"Синхронизировать папку...\t\tСинхронизировать папку с музыкой и библиотеку" + +#: src/GMWindow.cpp:259 +msgid "Play File or Stream…\t\tPlay File or Stream" +msgstr "" + +#: src/GMWindow.cpp:266 +msgid "&Quit\tCtrl-Q\tQuit the application." +msgstr "Вы&ход\tCtrl-Q\tВыход из приложения." + +#: src/GMWindow.cpp:271 +msgid "&Edit" +msgstr "Р&едактировать" + +#: src/GMWindow.cpp:272 +msgid "&Copy\tCtrl-C\tCopy Selected Tracks" +msgstr "&Копировать\tCtrl-C\tКопировать выделенные треки" + +#: src/GMWindow.cpp:273 +msgid "&Cut\tCtrl-X\tCut Selected Tracks" +msgstr "&Вырезать\tCtrl-X\tВырезать выделенные треки" + +#: src/GMWindow.cpp:274 +msgid "&Paste\tCtrl-V\tPaste Clipboard Selection" +msgstr "Вст&авить\tCtrl-V\tВставить из буфера обмена" + +#: src/GMWindow.cpp:276 +msgid "Find…\tCtrl-F\tShow search filter." +msgstr "Найти...\tCtrl-F\tПоказать фильтр поиска." + +#: src/GMWindow.cpp:278 +msgid "Preferences…" +msgstr "Опции" + +#: src/GMWindow.cpp:282 +msgid "&View" +msgstr "&Вид" + +#: src/GMWindow.cpp:283 +msgid "&Browse\tCtrl-B\tShow genre artist and album browser." +msgstr "" +"&Просмотреть\tCtrl-B\tПоказать браузер по жанрам, исполнителям и альбомам." + +#: src/GMWindow.cpp:284 +msgid "Show &Genres\tCtrl-G\tShow genre browser." +msgstr "Показать &жанры\tCtrl-G\tПоказать браузер жанров." + +#: src/GMWindow.cpp:287 src/GMWindow.cpp:289 +msgid "Show &Sources\tCtrl-S\tShow source browser " +msgstr "Показать &источники\tCtrl-S\tПоказать браузер источников " + +#: src/GMWindow.cpp:294 +msgid "Show Full Screen\tF12\tToggle fullscreen mode." +msgstr "" +"Показать в полноэкранном режиме\tF12\tПереключить в полноэкранный режим." + +#: src/GMWindow.cpp:296 +#, fuzzy +msgid "Show Mini Player\tCtrl-M\tToggle Mini Player." +msgstr "Показать мини-плеер\tF11\tПерключиться на мини-плеер" + +#: src/GMWindow.cpp:300 +msgid "&Sort" +msgstr "&Сортировать" + +#: src/GMWindow.cpp:302 +msgid "&Jump to Current Track\tCtrl-J\tShow current playing track." +msgstr "&Перейти на проигрываемый трек\tCtrl-J\tПоказать проигрываемый трек." + +#: src/GMWindow.cpp:306 +msgid "&Control" +msgstr "&Управление" + +#: src/GMWindow.cpp:307 +msgid "Play\tCtrl-P\tStart playback." +msgstr "Воспроизвести\tCtrl-P\tНачать воспроизведение." + +#: src/GMWindow.cpp:308 +msgid "Stop\tCtrl-\\\tStop playback." +msgstr "Остановить\tCtrl-\\\tОстановить воспроизведение." + +#: src/GMWindow.cpp:309 +msgid "Previous Track\tCtrl-[\tPlay next track." +msgstr "Предыдущий трек\tCtrl-[\tВоспроизвести предыдущий трек." + +#: src/GMWindow.cpp:310 +msgid "Next Track\tCtrl-]\tPlay previous track." +msgstr "Следующий трек\tCtrl-]\tВоспроизвести следующий трек." + +#: src/GMWindow.cpp:312 +msgid "Repeat Off\tCtrl-,\tRepeat current track." +msgstr "Выключить повтор\tCtrl-,\tПовтор текущего трека." + +#: src/GMWindow.cpp:313 +msgid "Repeat Track\tCtrl-.\tRepeat current track." +msgstr "Повторять трек\tCtrl-.\tПовтор текущего трека." + +#: src/GMWindow.cpp:314 +msgid "Repeat All Tracks\tCtrl-/\tRepeat all tracks." +msgstr "Повторять все треки\tCtrl-/\tПовтор всех треков." + +#: src/GMWindow.cpp:315 +msgid "Repeat A-B\tCtrl-T\tRepeat section of track." +msgstr "Повторять A-B\tCtrl-T\tПовторять часть трека." + +#: src/GMWindow.cpp:316 +msgid "Shuffle Mode\tAlt-R\tPlay tracks in random order." +msgstr "Случайный режим\tAlt-R\tВоспроизводить треки в случайном порядке." + +#: src/GMWindow.cpp:318 +msgid "Equalizer\t\t" +msgstr "Эквалайзер\t\t" + +#: src/GMWindow.cpp:319 +msgid "Sleep Timer\t\tSetup sleeptimer." +msgstr "Таймер выключения\t\tУстановить таймер выключения." + +#: src/GMWindow.cpp:323 +msgid "&Help" +msgstr "&Справка" + +#: src/GMWindow.cpp:324 +msgid "&Homepage" +msgstr "&Домашняя страница" + +#: src/GMWindow.cpp:325 +msgid "&Report Issue…" +msgstr "Со&общить об ошибке..." + +#: src/GMWindow.cpp:327 +msgid "&Sign up for last.fm…" +msgstr "&Зарегистрироваться на last.fm..." + +#: src/GMWindow.cpp:328 +msgid "" +"&Join GMM on last.fm…\t\tJoin the Goggles Music Manager group on last.fm…" +msgstr "" +"Присоединиться к GMM на last.fm...\t\tПрисоединиться к группе Goggles Music " +"Manager group на last.fm..." + +#: src/GMWindow.cpp:330 +msgid "&About…" +msgstr "&О программе" + +#: src/GMWindow.cpp:842 src/GMWindow.cpp:940 +msgid "Pause" +msgstr "Пауза" + +#: src/GMWindow.cpp:844 +msgid "Previous" +msgstr "Предыдущий" + +#: src/GMWindow.cpp:845 +msgid "Next" +msgstr "Следующий" + +#: src/GMWindow.cpp:920 src/GMWindow.cpp:949 src/GMWindow.cpp:955 +#, fuzzy +msgid "Start playback." +msgstr "Воспроизвести\tCtrl-P\tНачать воспроизведение." + +#: src/GMWindow.cpp:926 src/GMWindow.cpp:927 +#, fuzzy +msgid "Start playback" +msgstr "Воспроизвести\tCtrl-P\tНачать воспроизведение." + +#: src/GMWindow.cpp:934 src/GMWindow.cpp:941 +#, fuzzy +msgid "Pause playback." +msgstr "Пауза\t\tПриостановить воспроизведение." + +#: src/GMWindow.cpp:935 +#, fuzzy +msgid "Pause playback" +msgstr "Пауза\t\tПриостановить воспроизведение." + +#: src/GMWindow.cpp:1052 src/GMWindow.cpp:1054 +msgid "Repeat A-B" +msgstr "Повтор A-B" + +#: src/GMWindow.cpp:1053 +msgid "Repeat A" +msgstr "Повтор А" + +#: src/GMWindow.cpp:1195 +msgid "Play File or Stream" +msgstr "" + +#: src/GMWindow.cpp:1202 +msgid "Please specify a file or url to play:" +msgstr "" + +#: src/GMWindow.cpp:1206 +#, fuzzy +msgid "…" +msgstr "Редактировать..." + +#: src/GMWindow.cpp:1212 +#, fuzzy +msgid "Select File" +msgstr "Выбрать все" + +#: src/GMWindow.cpp:1243 +msgid "Sleep Timer" +msgstr "Таймер выключения" + +#: src/GMWindow.cpp:1244 +msgid "Setup sleep timer" +msgstr "Установить таймер выключения" + +#: src/GMWindow.cpp:1244 +msgid "Stop playback within a certain time" +msgstr "Остановить воспроизведение через заданное время" + +#: src/GMWindow.cpp:1246 +msgid "&Start Timer" +msgstr "&Запустить таймер" + +#: src/GMWindow.cpp:1251 +msgid "Sleep in" +msgstr "Выключить через" + +#: src/GMWindow.cpp:1253 +msgid "hours and" +msgstr "часов и" + +#: src/GMWindow.cpp:1255 +msgid "minutes." +msgstr "минут." + +#: src/GMDatabaseSource.h:131 +msgid "Music Library" +msgstr "Музыкальная библиотека" + +#: src/GMStreamSource.h:57 +msgid "Internet Radio" +msgstr "Интернет радио" + +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:122 +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:127 +msgid "&Quit" +msgstr "В&ыход" + +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:133 +msgid "&Skip" +msgstr "&Пропустить" + +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:134 +msgid "Skip &All" +msgstr "Пропустить &все" + +#: ../../../fox-1.6.37/src/FXMessageBox.cpp:140 +msgid "&Don't Save" +msgstr "&Не сохранять" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:220 +msgid "\tPick color" +msgstr "\tВыбрать цвет" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:229 +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:274 +msgid "\tHue, Saturation, Value" +msgstr "\tТон, насыщенность, величина" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:239 +msgid "\tRed, Green, Blue" +msgstr "\tКрасный, зеленый, синий" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:245 +msgid "&Red:" +msgstr "&Красный:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:250 +msgid "&Green:" +msgstr "&Зеленый:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:255 +msgid "&Blue:" +msgstr "&Синий:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:260 +msgid "&Alpha:" +msgstr "&Прозрачность:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:280 +msgid "Hue:" +msgstr "Тон:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:285 +msgid "Saturation:" +msgstr "Насыщенность:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:290 +msgid "Value:" +msgstr "Величина:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:295 +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:330 +msgid "Alpha:" +msgstr "Прозрачность:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:309 +msgid "\tCyan, Magenta, Yellow" +msgstr "\tГолубой, пурпурный, желтый" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:315 +msgid "Cyan:" +msgstr "Голубой:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:320 +msgid "Magenta:" +msgstr "Пурпурный:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:325 +msgid "Yellow:" +msgstr "Желтый:" + +#: ../../../fox-1.6.37/src/FXColorSelector.cpp:344 +msgid "\tBy Name" +msgstr "\tПо имени" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:281 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:521 +msgid "Create New Directory" +msgstr "Создать новую папку" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:284 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:524 +msgid "Already Exists" +msgstr "Уже существует" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:288 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:528 +msgid "Cannot Create" +msgstr "Невозможно создать" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:309 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:595 +msgid "Copy File" +msgstr "Копировать файла" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:315 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:601 +msgid "Error Copying File" +msgstr "Ошибка при копировании файла" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:326 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:618 +msgid "Move File" +msgstr "Переместить файл" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:332 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:624 +msgid "Error Moving File" +msgstr "Ошибка при перемещении файла" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:343 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:641 +msgid "Link File" +msgstr "Создать файловую ссылку" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:349 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:647 +msgid "Error Linking File" +msgstr "Ошибка создания файловой ссылки" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:359 +msgid "Deleting file" +msgstr "Удаление файлов" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:361 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:667 +msgid "Error Deleting File" +msgstr "Ошибка удаления файлов" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:381 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:719 +msgid "Up one level" +msgstr "Перейти на уровень выше" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:382 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:720 +msgid "Home directory" +msgstr "Домашняя папка" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:383 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:721 +msgid "Work directory" +msgstr "Рабочая папка" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:387 +msgid "Sorting" +msgstr "Сортировка" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:389 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:735 +msgid "Ignore case" +msgstr "Игнорировать регистр" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:390 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:746 +msgid "Hidden files" +msgstr "Скрытые файлы" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:393 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:754 +msgid "Bookmarks" +msgstr "Закладки" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:394 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:757 +msgid "Set bookmark" +msgstr "Установить закладку" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:395 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:758 +msgid "Clear bookmarks" +msgstr "Очистить закладки" + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:411 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:774 +msgid "New directory..." +msgstr "Новая папка..." + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:412 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:775 +msgid "Copy..." +msgstr "Копировать..." + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:413 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:776 +msgid "Move..." +msgstr "Переместить..." + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:414 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:777 +msgid "Link..." +msgstr "Ссылка..." + +#: ../../../fox-1.6.37/src/FXDirSelector.cpp:415 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:778 +msgid "Delete..." +msgstr "Удалить..." + +#: ../../../fox-1.6.37/src/FXFileList.cpp:195 +msgid "Modified Date" +msgstr "Дата изменения" + +#: ../../../fox-1.6.37/src/FXFileList.cpp:196 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:731 +msgid "User" +msgstr "Пользователь" + +#: ../../../fox-1.6.37/src/FXFileList.cpp:197 +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:732 +msgid "Group" +msgstr "Группа" + +#: ../../../fox-1.6.37/src/FXFileList.cpp:198 +msgid "Attributes" +msgstr "Атрибуты" + +#: ../../../fox-1.6.37/src/FXFileList.cpp:200 +msgid "Link" +msgstr "Ссылка" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:521 +msgid "Create new directory with name: " +msgstr "Создать новую папку с именем: " + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:524 +#, c-format +msgid "File or directory %s already exists.\n" +msgstr "Файл или папка %s уже существует.\n" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:528 +#, c-format +msgid "Cannot create directory %s.\n" +msgstr "Невозможно создать папку %s.\n" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:594 +#, c-format +msgid "" +"Copy file from location:\n" +"\n" +"%s\n" +"\n" +"to location: " +msgstr "" +"Копировать файл из:\n" +"\n" +"%s\n" +"\n" +"в: " + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:601 +#, c-format +msgid "" +"Unable to copy file:\n" +"\n" +"%s to: %s\n" +"\n" +"Continue with operation?" +msgstr "" +"Невозможно скопировать файл:\n" +"\n" +"%s to: %s\n" +"\n" +"Продолжить?" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:617 +#, c-format +msgid "" +"Move file from location:\n" +"\n" +"%s\n" +"\n" +"to location: " +msgstr "" +"Переместить файл из:\n" +"\n" +"%s\n" +"\n" +"в: " + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:624 +#, c-format +msgid "" +"Unable to move file:\n" +"\n" +"%s to: %s\n" +"\n" +"Continue with operation?" +msgstr "" +"Невозможно переместить файл:\n" +"\n" +"%s to: %s\n" +"\n" +"Продолжить?" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:640 +#, c-format +msgid "" +"Link file from location:\n" +"\n" +"%s\n" +"\n" +"to location: " +msgstr "" +"Создать ссылку на файл из:\n" +"\n" +"%s\n" +"\n" +"в: " + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:647 +#, c-format +msgid "" +"Unable to link file:\n" +"\n" +"%s to: %s\n" +"\n" +"Continue with operation?" +msgstr "" +"Невозможно создать ссылку на файл:\n" +"\n" +"%s в: %s\n" +"\n" +"Продолжить?" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:663 +msgid "Deleting files" +msgstr "Удаление файлов" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:663 +#, c-format +msgid "" +"Are you sure you want to delete the file:\n" +"\n" +"%s" +msgstr "" +"Вы уверены, что хотите удалить файл:\n" +"\n" +"%s" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:667 +#, c-format +msgid "" +"Unable to delete file:\n" +"\n" +"%s\n" +"\n" +"Continue with operation?" +msgstr "" +"Невозможно удалить файл:\n" +"\n" +"%s\n" +"\n" +"Продолжить?" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:722 +msgid "Select all" +msgstr "Выбрать все" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:726 +msgid "Sort by" +msgstr "Сортировать по" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:738 +msgid "View" +msgstr "Вид" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:739 +msgid "Small icons" +msgstr "Мелкие значки" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:740 +msgid "Big icons" +msgstr "Крупные значки" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:741 +msgid "Details" +msgstr "Детали" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:743 +msgid "Rows" +msgstr "Строки" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:744 +msgid "Columns" +msgstr "Столбцы" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:747 +msgid "Preview images" +msgstr "Предпросмотр изображений" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:749 +msgid "Normal images" +msgstr "Нормальные изображения" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:750 +msgid "Medium images" +msgstr "Средние изображения" + +#: ../../../fox-1.6.37/src/FXFileSelector.cpp:751 +msgid "Giant images" +msgstr "Огромные изображения" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:111 +msgid "&Replace" +msgstr "&Заменить" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:112 +msgid "Re&place All" +msgstr "За&менить все" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:121 +msgid "S&earch for:" +msgstr "&Найти:" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:129 +msgid "Replace &with:" +msgstr "Заменить &на:" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:138 +msgid "Ex&act" +msgstr "&Точно" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:139 +msgid "&Ignore Case" +msgstr "&Игнорировать регистр" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:140 +msgid "E&xpression" +msgstr "В&ыражение" + +#: ../../../fox-1.6.37/src/FXReplaceDialog.cpp:141 +msgid "&Backward" +msgstr "В &обратном порядке" + +#: ../../../fox-1.6.37/src/FXSearchDialog.cpp:71 +msgid "&Search" +msgstr "&Найти" + +#: ../../../fox-1.6.37/src/FXStatusLine.cpp:82 +msgid "Ready." +msgstr "Готово." + +#~ msgid "Old Name" +#~ msgstr "Прежнее имя" + +#~ msgid "New Name" +#~ msgstr "Новое имя" + +#~ msgid "Pitch:" +#~ msgstr "Высота:" + +#~ msgid "Any" +#~ msgstr "Любая" + +#~ msgid "Fixed" +#~ msgstr "Фиксированная" + +#~ msgid "Variable" +#~ msgstr "Переменная" + +#, fuzzy +#~ msgid " Type:" +#~ msgstr "Тип" + +#, fuzzy +#~ msgid "Scalable" +#~ msgstr "Масштабируемая" + +#, fuzzy +#~ msgid "Size:" +#~ msgstr "Размер" + +#, fuzzy +#~ msgid "Family:" +#~ msgstr "Се&мейство:" + +#~ msgid "Always Show Remote" +#~ msgstr "Всегда показывать мини окно" + +#~ msgid "Source\tActive Source Color" +#~ msgstr "Источник\tЦвет активного источника" + +#~ msgid "About" +#~ msgstr "О программе" + +#~ msgid "" +#~ "An incompatible version of SQLite (%s) is being used.\n" +#~ "Goggles Music Manager requires at least SQLite 3.3.8.\n" +#~ "Please upgrade your SQLite installation." +#~ msgstr "" +#~ "Используется несовместимая версия SQLite (%s)\n" +#~ "Goggles Music Manager требует SQLite 3.3.8. или выше.\n" +#~ "Пожалуйста, обновите Вашу версию SQLite." + +#~ msgid "" +#~ "This version of SQLite (%s) is broken.\n" +#~ "Please upgrade your SQLite installation to at least 3.6.3." +#~ msgstr "" +#~ "Эта версия SQLite (%s) повреждена. \n" +#~ "Пожалуйста, обновите Вашу версию SQLite до 3.6.3 или выше." + +#~ msgid "" +#~ "&Join GMM on last.fm…\tJoin the Goggles Music Manager group on last.fm…" +#~ "\tJoin the Goggles Music Manager group on last.fm…" +#~ msgstr "" +#~ "&Присоединиться к GMM на last.fm...\tПрисоединиться к группе Goggles " +#~ "Music Manager на last.fm...\tПрисоединиться к группе Goggles Music " +#~ "Manager на last.fm..." + +#~ msgid "Font" +#~ msgstr "Шрифт" + +#~ msgid "Theme Directory:" +#~ msgstr "Папка темы:" + +#~ msgid "Select Theme Directory" +#~ msgstr "Выбрать папку тем" + +#~ msgid "thin" +#~ msgstr "тонкий" + +#~ msgid "normal" +#~ msgstr "нормальный" + +#~ msgid "bold" +#~ msgstr "жирный" + +#~ msgid "regular" +#~ msgstr "нормальный" + +#~ msgid "&Weight:" +#~ msgstr "&Толщина:" + +#~ msgid "&Style:" +#~ msgstr "&Стиль:" + +#~ msgid "Si&ze:" +#~ msgstr "Ра&змер" + +#~ msgid "Character Set:" +#~ msgstr "Кодировка:" + +#~ msgid "West European" +#~ msgstr "Западно-европейская" + +#~ msgid "East European" +#~ msgstr "Восточно-европейская" + +#~ msgid "South European" +#~ msgstr "Юго-европейская" + +#~ msgid "North European" +#~ msgstr "Северо-европейская" + +#~ msgid "Cyrillic" +#~ msgstr "Кириллица" + +#~ msgid "Arabic" +#~ msgstr "Арабская" + +#~ msgid "Greek" +#~ msgstr "Греческая" + +#~ msgid "Hebrew" +#~ msgstr "Иврит" + +#~ msgid "Turkish" +#~ msgstr "Турецкая" + +#~ msgid "Nordic" +#~ msgstr "Скандинавская" + +#~ msgid "Thai" +#~ msgstr "Тайская" + +#~ msgid "Baltic" +#~ msgstr "Прибалтийская" + +#~ msgid "Celtic" +#~ msgstr "Celtic" + +#~ msgid "Russian" +#~ msgstr "Русская" + +#~ msgid "Central European (cp1250)" +#~ msgstr "Центрально-европейская (cp1250)" + +#~ msgid "Russian (cp1251)" +#~ msgstr "Кириллица (cp1251)" + +#~ msgid "Latin1 (cp1252)" +#~ msgstr "Центрально-европейская (cp1252)" + +#~ msgid "Greek (cp1253)" +#~ msgstr "Греческая (cp1253)" + +#~ msgid "Turkish (cp1254)" +#~ msgstr "Турецкая (cp1254)" + +#~ msgid "Hebrew (cp1255)" +#~ msgstr "Иврит (cp1255)" + +#~ msgid "Arabic (cp1256)" +#~ msgstr "Арабская (cp1256)" + +#~ msgid "Baltic (cp1257)" +#~ msgstr "Прибалтийская (cp1257)" + +#~ msgid "Vietnam (cp1258)" +#~ msgstr "Вьетнамская (cp1258)" + +#~ msgid "Thai (cp874)" +#~ msgstr "Тайская (cp874)" + +#~ msgid "UNICODE" +#~ msgstr "Юникод" + +#~ msgid "Set Width:" +#~ msgstr "Задать толщину:" + +#~ msgid "All Fonts:" +#~ msgstr "Все шрифты:" + +#~ msgid "Preview:" +#~ msgstr "Предпросмотр:" + +#, fuzzy +#~ msgid "Import Playlist…\t\tImport a existing playlist" +#~ msgstr "Новый плейлист...\t\tСоздать новый плейлист" + +#~ msgid "Import Files?" +#~ msgstr "Импортировать файлы?" + +#~ msgid "" +#~ "Would you like import the pasted files and/or directories into the Music " +#~ "Library?" +#~ msgstr "" +#~ "Хотите ли Вы импортировать скопированные файлы и/или директории в " +#~ "музыкальную библиотеку?" + +#~ msgid "Yes" +#~ msgstr "Да" + +#~ msgid "Edit…\tF2\tEdit Genre." +#~ msgstr "Редактировать...\tF2\tРедактировать жанр." + +#~ msgid "Edit…\tF2\tEdit Artist." +#~ msgstr "Редактировать...\tF2\tРедактировать исполнителя." + +#~ msgid "Edit…\tF2\tEdit Album." +#~ msgstr "Редактировать...\tF2\tРедактировать альбом." + +#~ msgid "" +#~ "%s\n" +#~ "by: %s\n" +#~ "from: %s" +#~ msgstr "" +#~ "%s\n" +#~ "по: %s\n" +#~ "из: %s" + +#~ msgid "Now Playing" +#~ msgstr "Проигрывается" + +#~ msgid "Start Up:" +#~ msgstr "Запуск:" + +#~ msgid "Show Main Window" +#~ msgstr "Показать основное окно" + +#~ msgid "Show Mini Remote" +#~ msgstr "Показать мини окно" + +#~ msgid "Previous View" +#~ msgstr "Предыдущий вид" + +#~ msgid "P&ause" +#~ msgstr "&Пауза" + +#~ msgid "\tPause\tPause Playback" +#~ msgstr "\tПриостановить\tПриостановить воспроизведение" + +#~ msgid "Open URL…\t\tOpen Stream or File" +#~ msgstr "Открыть URL...\t\tОткрыть поток или файл" + +#~ msgid "Open MRL" +#~ msgstr "Открыть MRL" + +#~ msgid "A capella" +#~ msgstr "A capella" + +#~ msgid "Acid" +#~ msgstr "Acid" + +#~ msgid "Acid Jazz" +#~ msgstr "Acid Jazz" + +#~ msgid "Acid Punk" +#~ msgstr "Acid Punk" + +#~ msgid "Acoustic" +#~ msgstr "Acoustic" + +#~ msgid "Alternative" +#~ msgstr "Alternative" + +#~ msgid "AlternRockAmbient" +#~ msgstr "AlternRockAmbient" + +#~ msgid "Avantgarde" +#~ msgstr "Avantgarde" + +#~ msgid "Ballad" +#~ msgstr "Ballad" + +#~ msgid "Bass" +#~ msgstr "Bass" + +#~ msgid "Bebob" +#~ msgstr "Bebob" + +#~ msgid "Big Band" +#~ msgstr "Big Band" + +#~ msgid "Blues" +#~ msgstr "Blues" + +#~ msgid "Bluegrass" +#~ msgstr "Bluegrass" + +#~ msgid "Booty Bass" +#~ msgstr "Booty Bass" + +#~ msgid "Cabaret" +#~ msgstr "Cabaret" + +#~ msgid "Chamber Music" +#~ msgstr "Chamber Music" + +#~ msgid "Chanson" +#~ msgstr "Chanson" + +#~ msgid "Chorus" +#~ msgstr "Chorus" + +#~ msgid "Christian Rap" +#~ msgstr "Christian Rap" + +#~ msgid "Classical" +#~ msgstr "Classical" + +#~ msgid "Classic Rock" +#~ msgstr "Classic Rock" + +#~ msgid "Club" +#~ msgstr "Club" + +#~ msgid "Comedy" +#~ msgstr "Comedy" + +#~ msgid "Country" +#~ msgstr "Country" + +#~ msgid "Cult" +#~ msgstr "Cult" + +#~ msgid "Dance" +#~ msgstr "Dance" + +#~ msgid "Dance Hall" +#~ msgstr "Dance Hall" + +#~ msgid "Darkwave" +#~ msgstr "Darkwave" + +#~ msgid "Death Metal" +#~ msgstr "Death Metal" + +#~ msgid "Disco" +#~ msgstr "Disco" + +#~ msgid "Dream" +#~ msgstr "Dream" + +#~ msgid "Drum Solo" +#~ msgstr "Drum Solo" + +#~ msgid "Duet" +#~ msgstr "Duet" + +#~ msgid "Easy Listening" +#~ msgstr "Easy Listening" + +#~ msgid "Electronic" +#~ msgstr "Electronic" + +#~ msgid "Ethnic" +#~ msgstr "Ethnic" + +#~ msgid "Euro-Dance" +#~ msgstr "Euro-Dance" + +#~ msgid "Euro-House" +#~ msgstr "Euro-House" + +#~ msgid "Euro-Techno" +#~ msgstr "Euro-Techno" + +#~ msgid "Fast Fusion" +#~ msgstr "Fast Fusion" + +#~ msgid "Folk" +#~ msgstr "Folk" + +#~ msgid "Folk-Rock" +#~ msgstr "Folk-Rock" + +#~ msgid "Folklore" +#~ msgstr "Folklore" + +#~ msgid "Freestyle" +#~ msgstr "Freestyle" + +#~ msgid "Funk" +#~ msgstr "Funk" + +#~ msgid "Fusion" +#~ msgstr "Fusion" + +#~ msgid "Game" +#~ msgstr "Game" + +#~ msgid "Gangsta" +#~ msgstr "Gangsta" + +#~ msgid "Gospel" +#~ msgstr "Gospel" + +#~ msgid "Gothic" +#~ msgstr "Gothic" + +#~ msgid "Gothic Rock" +#~ msgstr "Gothic Rock" + +#~ msgid "Grunge" +#~ msgstr "Grunge" + +#~ msgid "Hard Rock" +#~ msgstr "Hard Rock" + +#~ msgid "Hip-Hop" +#~ msgstr "Hip-Hop" + +#~ msgid "House" +#~ msgstr "House" + +#~ msgid "Humour" +#~ msgstr "Humour" + +#~ msgid "Industrial" +#~ msgstr "Industrial" + +#~ msgid "Instrumental" +#~ msgstr "Instrumental" + +#~ msgid "Instrumental Pop" +#~ msgstr "Instrumental Pop" + +#~ msgid "Instrumental Rock" +#~ msgstr "Instrumental Rock" + +#~ msgid "Jazz" +#~ msgstr "Jazz" + +#~ msgid "Jazz+Funk" +#~ msgstr "Jazz+Funk" + +#~ msgid "Jungle" +#~ msgstr "Jungle" + +#~ msgid "Latin" +#~ msgstr "Latin" + +#~ msgid "Lo-Fi" +#~ msgstr "Lo-Fi" + +#~ msgid "Meditative" +#~ msgstr "Meditative" + +#~ msgid "Metal" +#~ msgstr "Metal" + +#~ msgid "Musical" +#~ msgstr "Musical" + +#~ msgid "National Folk" +#~ msgstr "National Folk" + +#~ msgid "Native American" +#~ msgstr "Native American" + +#~ msgid "New Age" +#~ msgstr "New Age" + +#~ msgid "New Wave" +#~ msgstr "New Wave" + +#~ msgid "Noise" +#~ msgstr "Noise" + +#~ msgid "Oldies" +#~ msgstr "Oldies" + +#~ msgid "Opera" +#~ msgstr "Opera" + +#~ msgid "Other" +#~ msgstr "Other" + +#~ msgid "Polka" +#~ msgstr "Polka" + +#~ msgid "Pop" +#~ msgstr "Pop" + +#~ msgid "Pop-Folk" +#~ msgstr "Pop-Folk" + +#~ msgid "Pop/Funk" +#~ msgstr "Pop/Funk" + +#~ msgid "Porn Groove" +#~ msgstr "Porn Groove" + +#~ msgid "Power Ballad" +#~ msgstr "Power Ballad" + +#~ msgid "Pranks" +#~ msgstr "Pranks" + +#~ msgid "Primus" +#~ msgstr "Primus" + +#~ msgid "Progressive Rock" +#~ msgstr "Progressive Rock" + +#~ msgid "Psychadelic" +#~ msgstr "Psychadelic" + +#~ msgid "Psychedelic Rock" +#~ msgstr "Psychedelic Rock" + +#~ msgid "Punk" +#~ msgstr "Punk" + +#~ msgid "Punk Rock" +#~ msgstr "Punk Rock" + +#~ msgid "R&B" +#~ msgstr "R&B" + +#~ msgid "Rap" +#~ msgstr "Rap" + +#~ msgid "Rave" +#~ msgstr "Rave" + +#~ msgid "Reggae" +#~ msgstr "Reggae" + +#~ msgid "Retro" +#~ msgstr "Retro" + +#~ msgid "Revival" +#~ msgstr "Revival" + +#~ msgid "Rhythmic Soul" +#~ msgstr "Rhythmic Soul" + +#~ msgid "Rock" +#~ msgstr "Rock" + +#~ msgid "Rock & Roll" +#~ msgstr "Rock & Roll" + +#~ msgid "Samba" +#~ msgstr "Samba" + +#~ msgid "Satire" +#~ msgstr "Satire" + +#~ msgid "Showtunes" +#~ msgstr "Showtunes" + +#~ msgid "Ska" +#~ msgstr "Ska" + +#~ msgid "Slow Jam" +#~ msgstr "Slow Jam" + +#~ msgid "Slow Rock" +#~ msgstr "Slow Rock" + +#~ msgid "Sonata" +#~ msgstr "Sonata" + +#~ msgid "Soul" +#~ msgstr "Soul" + +#~ msgid "Soundtrack" +#~ msgstr "Soundtrack" + +#~ msgid "Sound Clip" +#~ msgstr "Sound Clip" + +#~ msgid "Southern Rock" +#~ msgstr "Southern Rock" + +#~ msgid "Space" +#~ msgstr "Space" + +#~ msgid "Speech" +#~ msgstr "Speech" + +#~ msgid "Swing" +#~ msgstr "Swing" + +#~ msgid "Symphonic Rock" +#~ msgstr "Symphonic Rock" + +#~ msgid "Symphony" +#~ msgstr "Symphony" + +#~ msgid "Tango" +#~ msgstr "Tango" + +#~ msgid "Techno" +#~ msgstr "Techno" + +#~ msgid "Techno-Industrial" +#~ msgstr "Techno-Industrial" + +#~ msgid "Top 40" +#~ msgstr "Top 40" + +#~ msgid "Trailer" +#~ msgstr "Trailer" + +#~ msgid "Trance" +#~ msgstr "Trance" + +#~ msgid "Tribal" +#~ msgstr "Tribal" + +#~ msgid "Trip-Hop" +#~ msgstr "Trip-Hop" + +#~ msgid "Vocal" +#~ msgstr "Vocal" diff --git a/src/GMAbout.cpp b/src/GMAbout.cpp new file mode 100644 index 0000000..33a907a --- /dev/null +++ b/src/GMAbout.cpp @@ -0,0 +1,137 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#include "gmdefs.h" +#include "GMAbout.h" +#include "icons.h" + +#include +#ifdef HAVE_DBUS +#include "GMDBus.h" +#endif +#include + +#include +#include + +#define UTF8_COPYRIGHT_SIGN "\xc2\xa9" + +#define APPLICATION_TITLE "Goggles Music Manager" + +// Object implementation +FXIMPLEMENT(GMAboutDialog,FXDialogBox,NULL,0) + +GMAboutDialog::GMAboutDialog(FXApp * app) : FXDialogBox(app,FXString::null,DECOR_ALL,0,0,0,0,0,0,0,0,0,0) { + setup(); + } + +GMAboutDialog::GMAboutDialog(FXWindow* owner) : FXDialogBox(owner,FXString::null,DECOR_TITLE|DECOR_BORDER,0,0,0,0,0,0,0,0,0,0) { + setup(); + } + +GMAboutDialog::~GMAboutDialog(){ + } + + +void GMAboutDialog::setup(){ + + setTitle("About " APPLICATION_TITLE); + + logo = new FXPNGIcon(getApp(),about_png); + logo->blend(FXRGB(255,255,255)); + +#if FOXVERSION < FXVERSION(1,7,17) + FXFontDesc fontdescription; + getApp()->getNormalFont()->getFontDesc(fontdescription); +#else + FXFontDesc fontdescription = getApp()->getNormalFont()->getFontDesc(); +#endif + fontdescription.size += 10; + fontdescription.weight = FXFont::Bold; + titlefont = new FXFont(getApp(),fontdescription); + titlefont->create(); + +#if FOXVERSION < FXVERSION(1,7,17) + getApp()->getNormalFont()->getFontDesc(fontdescription); +#else + fontdescription = getApp()->getNormalFont()->getFontDesc(); +#endif + fontdescription.size -= 10; + licensefont = new FXFont(getApp(),fontdescription); + licensefont->create(); + + + FXLabel * label = new FXLabel(this,APPLICATION_TITLE,logo,ICON_ABOVE_TEXT|LAYOUT_CENTER_X|JUSTIFY_CENTER_X|LAYOUT_FILL_X,0,0,0,0,0,0,0,0); + label->setFont(titlefont); + label->setBackColor(FXRGB(255,255,255)); + label->setTextColor(FXRGB(0,0,0)); + + + const FXchar gpl[] = "This program is free software: you can\n" + "redistribute it and/or modify it under the\n" + "terms of the GNU General Public License\n" + "as published by the Free Software Foundation,\n" + "either version 3 of the License, or (at your\n" + "option) any later version."; + + label = new FXLabel(this,"v"APPLICATION_VERSION_STRING,NULL,LAYOUT_CENTER_X|LAYOUT_FILL_X,0,0,0,0,5,5,0,5); + label->setBackColor(FXRGB(255,255,255)); + label->setTextColor(FXRGB(0,0,0)); + + label = new FXLabel(this,"Copyright " UTF8_COPYRIGHT_SIGN " 2004-2011 Sander Jansen",NULL,LAYOUT_CENTER_X|LAYOUT_FILL_X,0,0,0,0,5,5,0,0); + label->setBackColor(FXRGB(255,255,255)); + label->setTextColor(FXRGB(0,0,0)); + + label = new FXLabel(this,gpl,NULL,LAYOUT_CENTER_X|LAYOUT_FILL_X,0,0,0,0,5,5,5,0); + label->setBackColor(FXRGB(255,255,255)); + label->setTextColor(FXRGB(0,0,0)); + label->setFont(licensefont); + + + FXString libraries; + + XML_Expat_Version expatversion = XML_ExpatVersionInfo(); + FXint xineversion[3]; + xine_get_version(&xineversion[0],&xineversion[1],&xineversion[2]); +#ifdef HAVE_DBUS + libraries.format("Build with FOX %d.%d.%d, Xine %d.%d.%d,\nSQLite %s, DBus %s, Expat %d.%d.%d\nand Taglib",fxversion[0],fxversion[1],fxversion[2],xineversion[0],xineversion[1],xineversion[2],sqlite3_libversion(),GMDBus::dbusversion().text(),expatversion.major,expatversion.minor,expatversion.micro); +#else + libraries.format("Build with FOX %d.%d.%d, Xine %d.%d.%d,\nSQLite %s, Expat %d.%d.%d and Taglib",fxversion[0],fxversion[1],fxversion[2],xineversion[0],xineversion[1],xineversion[2],sqlite3_libversion(),expatversion.major,expatversion.minor,expatversion.micro); +#endif + +#if defined(TAGLIB_WITH_ASF) && (TAGLIB_WITH_ASF==1) + #if defined(TAGLIB_WITH_MP4) && (TAGLIB_WITH_MP4==1) + libraries.append(" (ASF/MP4)."); + #else + libraries.append(" (ASF)."); + #endif +#elif defined(TAGLIB_WITH_MP4) && (TAGLIB_WITH_MP4==1) + libraries.append(" (MP4)."); +#else + libraries.append("."); +#endif + + label = new FXLabel(this,libraries,NULL,LAYOUT_CENTER_X|LAYOUT_FILL_X,0,0,0,0,5,5,5,5); + label->setBackColor(FXRGB(255,255,255)); + label->setTextColor(FXRGB(0,0,0)); + label->setFont(licensefont); + + new FXSeparator(this,SEPARATOR_GROOVE|LAYOUT_FILL_X); + FXHorizontalFrame *closebox=new FXHorizontalFrame(this,LAYOUT_BOTTOM|LAYOUT_FILL_X,0,0,0,0); + new GMButton(closebox,tr("&Close"),NULL,this,FXDialogBox::ID_CANCEL,BUTTON_INITIAL|BUTTON_DEFAULT|LAYOUT_CENTER_X|FRAME_RAISED|FRAME_THICK,0,0,0,0,20,20); + } diff --git a/src/GMAbout.h b/src/GMAbout.h new file mode 100644 index 0000000..358bb5c --- /dev/null +++ b/src/GMAbout.h @@ -0,0 +1,44 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#ifndef FXABOUTDIALOG_H +#define FXABOUTDIALOG_H + +class GMAboutDialog : public FXDialogBox { + FXDECLARE(GMAboutDialog) +private: + FXFontPtr titlefont; + FXFontPtr licensefont; + FXIconPtr logo; +private: + void setup(); +private: + GMAboutDialog(){} + GMAboutDialog(const GMAboutDialog&); + GMAboutDialog& operator=(const GMAboutDialog&); +public: +/// Construct free-floating About dialog + GMAboutDialog(FXApp* a); + + /// Construct dialog which will always float over the owner window + GMAboutDialog(FXWindow* owner); + + /// Destructor + virtual ~GMAboutDialog(); + }; +#endif diff --git a/src/GMAnimImage.cpp b/src/GMAnimImage.cpp new file mode 100644 index 0000000..0f86afb --- /dev/null +++ b/src/GMAnimImage.cpp @@ -0,0 +1,120 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#include +#include "gmdefs.h" + +#include "GMAnimImage.h" + +FXDEFMAP(GMAnimImage) GMAnimImageMap[]={ + FXMAPFUNC(SEL_PAINT,0,GMAnimImage::onPaint), + FXMAPFUNC(SEL_TIMEOUT,GMAnimImage::ID_TIMER,GMAnimImage::onTimer) + }; + +FXIMPLEMENT(GMAnimImage,FXImageFrame,GMAnimImageMap,ARRAYNUMBER(GMAnimImageMap)) + + +GMAnimImage::GMAnimImage(){ + index=1; + imgw=imgh=32; + nrow=8; + ncol=4; + } + +// Construct it +GMAnimImage::GMAnimImage(FXComposite* p,FXImage *img,FXint base,FXuint opts,FXint x,FXint y,FXint w,FXint h,FXint pl,FXint pr,FXint pt,FXint pb):FXImageFrame(p,img,opts,x,y,w,h,pl,pr,pt,pb){ + index=1; + imgw=imgh=base; + if (img) { + nrow=img->getWidth()/imgw; + ncol=img->getHeight()/imgh; + } + else { + nrow=1; + ncol=1; + } + } + +GMAnimImage::~GMAnimImage(){ + getApp()->removeTimeout(this,ID_TIMER); + } + +void GMAnimImage::show() { + FXImageFrame::show(); + } + +void GMAnimImage::hide() { + FXImageFrame::hide(); + } + + +void GMAnimImage::create() { + FXImageFrame::create(); + getApp()->addTimeout(this,ID_TIMER,TIME_MSEC(50)); + } + +// Get default width +FXint GMAnimImage::getDefaultWidth(){ + register FXint w=0; + if(image) w=imgw; + return w+padleft+padright+(border<<1); + } + +// Get default height +FXint GMAnimImage::getDefaultHeight(){ + register FXint h=0; + if(image) h=imgh; + return h+padtop+padbottom+(border<<1); + } + +long GMAnimImage::onTimer(FXObject*,FXSelector,void*){ + if (index==((nrow*ncol)-1)) + index=1; + else + index++; + update(); + getApp()->addTimeout(this,ID_TIMER,TIME_MSEC(100)); + return 0; + } + + +// Draw the image +long GMAnimImage::onPaint(FXObject*,FXSelector,void* ptr){ + FXEvent *ev=(FXEvent*)ptr; + FXDCWindow dc(this,ev); + FXint imgx,imgy; + dc.setForeground(backColor); + if(image){ + if(options&JUSTIFY_LEFT) imgx=padleft+border; + else if(options&JUSTIFY_RIGHT) imgx=width-padright-border-imgw; + else imgx=border+padleft+(width-padleft-padright-(border<<1)-imgw)/2; + if(options&JUSTIFY_TOP) imgy=padtop+border; + else if(options&JUSTIFY_BOTTOM) imgy=height-padbottom-border-imgh; + else imgy=border+padtop+(height-padbottom-padtop-(border<<1)-imgh)/2; + dc.fillRectangle(border,border,imgx-border,height-(border<<1)); + dc.fillRectangle(imgx+imgw,border,width-border-imgx-imgw,height-(border<<1)); + dc.fillRectangle(imgx,border,imgw,imgy-border); + dc.fillRectangle(imgx,imgy+imgh,imgw,height-border-imgy-imgh); + dc.drawArea(image,(index%nrow)*imgw,(index/nrow)*imgh,imgw,imgh,imgx,imgy); + } + else{ + dc.fillRectangle(border,border,width-(border<<1),height-(border<<1)); + } + drawFrame(dc,0,0,width,height); + return 1; + } diff --git a/src/GMAnimImage.h b/src/GMAnimImage.h new file mode 100644 index 0000000..0b93ac9 --- /dev/null +++ b/src/GMAnimImage.h @@ -0,0 +1,65 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#ifndef GMANIMIMAGE_H +#define GMANIMIMAGE_H + +class GMAnimImage : public FXImageFrame { +FXDECLARE(GMAnimImage) +protected: + FXuint index; + FXuint imgw; + FXuint imgh; + FXuint nframes; + FXuint nrow; + FXuint ncol; +protected: + GMAnimImage(); +private: + GMAnimImage(const GMAnimImage&); + GMAnimImage& operator=(const GMAnimImage&); +public: + enum { + ID_TIMER = FXImageFrame::ID_LAST, + ID_LAST + }; +public: + long onPaint(FXObject*,FXSelector,void*); + long onTimer(FXObject*,FXSelector,void*); +public: + /// Construct image frame and pass it an image + GMAnimImage(FXComposite* p,FXImage *img,FXint base,FXuint opts=FRAME_SUNKEN|FRAME_THICK,FXint x=0,FXint y=0,FXint w=0,FXint h=0,FXint pl=0,FXint pr=0,FXint pt=0,FXint pb=0); + + virtual void hide(); + + virtual void show(); + + virtual void create(); + + /// Get default width + virtual FXint getDefaultWidth(); + + /// Get default height + virtual FXint getDefaultHeight(); + + ~GMAnimImage(); + }; + + + +#endif diff --git a/src/GMApp.cpp b/src/GMApp.cpp new file mode 100644 index 0000000..333f3e4 --- /dev/null +++ b/src/GMApp.cpp @@ -0,0 +1,453 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#include + +#include "gmdefs.h" +#include "GMApp.h" +#include "GMList.h" +#include "GMTrackDatabase.h" +#include "GMTrackList.h" +#include "GMSource.h" +#include "GMPlayerManager.h" +#include "GMClipboard.h" +#include "GMTrayIcon.h" + +#include "icons.h" + + +#ifdef HAVE_NLS + +#define PACKAGE "gogglesmm" + +#ifndef LOCALEDIR +#error LOCALEDIR needs to be defined!! +#endif + +#include +#include + + + + +class GMTranslator : public FXTranslator { +FXDECLARE(GMTranslator) +private: +private: + GMTranslator(const GMTranslator&); + GMTranslator &operator=(const GMTranslator&); +#if FOXVERSION < FXVERSION(1,7,16) +protected: + GMTranslator(){} +public: + /// Construct translator + GMTranslator(FXApp* a): FXTranslator(a) { + setlocale(LC_MESSAGES,""); + setlocale(LC_NUMERIC,"C"); + bindtextdomain(PACKAGE,LOCALEDIR); + bind_textdomain_codeset(PACKAGE,"UTF-8"); + textdomain(PACKAGE); +#ifdef DEBUG + fxmessage("localedir: %s\n",LOCALEDIR); +#endif + }; +#else +public: + GMTranslator(){ + setlocale(LC_MESSAGES,""); + setlocale(LC_NUMERIC,"C"); + bindtextdomain(PACKAGE,LOCALEDIR); + bind_textdomain_codeset(PACKAGE,"UTF-8"); + textdomain(PACKAGE); +#ifdef DEBUG + fxmessage("localedir: %s\n",LOCALEDIR); +#endif + }; +#endif + +#if FOXVERSION < FXVERSION(1,7,16) + virtual const FXchar* tr(const FXchar* context,const FXchar* message,const FXchar* hint=NULL) const; +#else + virtual const FXchar* tr(const FXchar* context,const FXchar* message,const FXchar* hint=NULL,FXint count=-1) const; +#endif + + ~GMTranslator() { + } + + }; + +FXIMPLEMENT(GMTranslator,FXTranslator,NULL,0) + + +#if FOXVERSION < FXVERSION(1,7,16) +const FXchar* GMTranslator::tr(const FXchar*,const FXchar* message,const FXchar*) const { + return gettext(message); + } +#else +const FXchar* GMTranslator::tr(const FXchar*,const FXchar* message,const FXchar*,FXint) const { + return gettext(message); + } +#endif + +#endif + + +extern const FXchar * fxtr(const FXchar *x){ +#ifdef HAVE_NLS + return FXApp::instance()->getTranslator()->tr(NULL,x); +#else + return x; +#endif + } + + +FXIMPLEMENT(GMApp,FXApp,NULL,0) + +GMApp* GMApp::instance() { + return dynamic_cast(FXApp::instance()); + } + +GMApp::GMApp(const FXString& name,const FXString& vendor) : FXApp(name,vendor){ + clipboard = new GMClipboard(this); + xembed=0; + } + +GMApp::~GMApp(){ + delete clipboard; + } + + +FXString GMApp::getCacheDirectory(FXbool create) { + FXString xdg_cache_home = FXSystem::getEnvironment("XDG_CACHE_HOME"); + if (xdg_cache_home.empty()) + xdg_cache_home=FXSystem::getHomeDirectory()+PATHSEPSTRING ".cache" ; + + xdg_cache_home+=PATHSEPSTRING "gogglesmm"; + + if (create) + gm_make_path(xdg_cache_home); + + return xdg_cache_home; + } + + + +void GMApp::create() { + + FXString systemtray = GMStringFormat("_NET_SYSTEM_TRAY_S%d",DefaultScreen((Display*)getDisplay())); + + xembed = (FXID)XInternAtom((Display*)getDisplay(),"_XEMBED",False); + xmanager = (FXID)XInternAtom((Display*)getDisplay(),"MANAGER",True); + xsystemtray = (FXID)XInternAtom((Display*)getDisplay(),systemtray.text(),True); + + FXApp::create(); + + +#if FOXVERSION < FXVERSION(1,7,17) + FXFontDesc fontdescription; + getNormalFont()->getFontDesc(fontdescription); +#else + FXFontDesc fontdescription = getNormalFont()->getFontDesc(); +#endif + fontdescription.weight = FXFont::Bold; + + thickfont = new FXFont(this,fontdescription); + thickfont->create(); + + + XSelectInput((Display*)getDisplay(),getRootWindow()->id(),KeyPressMask|KeyReleaseMask|StructureNotifyMask); + } + + +void GMApp::setFont(const FXFontDesc & fnt){ + getNormalFont()->destroy(); + getNormalFont()->setFontDesc(fnt); + getNormalFont()->create(); + reg().writeStringEntry("SETTINGS","normalfont",getNormalFont()->getFont().text()); + +#if FOXVERSION < FXVERSION(1,7,17) + FXFontDesc fontdescription; + getNormalFont()->getFontDesc(fontdescription); +#else + FXFontDesc fontdescription = getNormalFont()->getFontDesc(); +#endif + fontdescription.weight = FXFont::Bold; + thickfont->destroy(); + thickfont->setFontDesc(fontdescription); + thickfont->create(); + } + +void GMApp::updateFont() { +#if FOXVERSION < FXVERSION(1,7,17) + FXFontDesc fontdescription; + getNormalFont()->getFontDesc(fontdescription); +#else + FXFontDesc fontdescription = getNormalFont()->getFontDesc(); +#endif + setFont(fontdescription); + } + + +#if FOXVERSION < FXVERSION(1,7,0) +void GMApp::init(int& argc,char** argv,bool connect) { +#else +void GMApp::init(int& argc,char** argv,FXbool connect) { +#endif + FXApp::init(argc,argv,connect); + +#ifdef HAVE_NLS +#if FOXVERSION < FXVERSION(1,7,16) + setTranslator(new GMTranslator(this)); +#else + setTranslator(new GMTranslator()); +#endif +#endif + + } + +enum { + XEMBED_EMBEDDED_NOTIFY = 0, + XEMBED_MODALITY_ON = 10, + XEMBED_MODALITY_OFF = 11, + XEMBED_REQUEST_FOCUS = 3 + }; + +#include + +// Get keysym; interprets the modifiers! +static FXuint keysym(FXRawEvent& event){ + KeySym sym=KEY_VoidSymbol; + char buffer[40]; + XLookupString(&event.xkey,buffer,sizeof(buffer),&sym,NULL); + return sym; + } + +#if FOXVERSION < FXVERSION(1,7,0) +bool GMApp::dispatchEvent(FXRawEvent & ev) { +#else +FXbool GMApp::dispatchEvent(FXRawEvent & ev) { +#endif + + /// Handle Global Hotkeys + if (ev.xany.window==getRootWindow()->id()){ + + if (ev.xany.type==KeyPress) { + //fxmessage("keypress %d %x\n",ev.xkey.keycode,keysym(ev)); + if (GMPlayerManager::instance()->handle_global_hotkeys(keysym(ev))) + return true; + } + else if (ev.xany.type==ClientMessage) { + if ((Atom)ev.xclient.message_type==(Atom)xmanager && (Atom)ev.xclient.data.l[1]==(Atom)xsystemtray) { + if (GMPlayerManager::instance()->getTrayIcon()) + GMPlayerManager::instance()->getTrayIcon()->dock(); + return true; + } + } + } + + FXWindow * window = findWindowWithId(ev.xany.window); + if (window && ev.xany.type==ClientMessage && ev.xclient.message_type==xembed) { + switch(ev.xclient.data.l[1]) { + case XEMBED_EMBEDDED_NOTIFY: window->tryHandle(this,FXSEL(SEL_EMBED_NOTIFY,0),(void*)(FXival)ev.xclient.data.l[3]); break; + case XEMBED_MODALITY_ON : window->tryHandle(this,FXSEL(SEL_EMBED_MODAL_ON,0),NULL); break; + case XEMBED_MODALITY_OFF : window->tryHandle(this,FXSEL(SEL_EMBED_MODAL_OFF,0),NULL); break; + default : /*fxmessage("Missed a message %d\n",ev.xclient.data.l[1]);*/ break; + } + return true; + } + return FXApp::dispatchEvent(ev); + } + + +FXDEFMAP(GMPlug) GMPlugMap[]={ + FXMAPFUNC(SEL_EMBED_NOTIFY,0,GMPlug::onEmbedded) + }; + +FXIMPLEMENT(GMPlug,FXTopWindow,GMPlugMap,ARRAYNUMBER(GMPlugMap)); + +GMPlug::GMPlug(){ + } + +GMPlug::GMPlug(FXApp * app) : FXTopWindow(app,"test",NULL,NULL,DECOR_NONE,0,0,1,1,0,0,0,0,0,0) , socket(0) { + } + +GMPlug::~GMPlug(){ + } + +#if FOXVERSION < FXVERSION(1,7,0) +bool GMPlug::doesOverrideRedirect() const { + return true; + } +#else +FXbool GMPlug::doesOverrideRedirect() const{ + return true; + } +#endif + +void GMPlug::setFocus(){ + FXShell::setFocus(); + if (xid && socket) { + XEvent ev; + memset(&ev, 0, sizeof(ev)); + ev.xclient.type = ClientMessage; + ev.xclient.window = socket; + ev.xclient.message_type = ((GMApp*)getApp())->xembed; + ev.xclient.format = 32; + ev.xclient.data.l[0] = CurrentTime; + ev.xclient.data.l[1] = XEMBED_REQUEST_FOCUS; + XSendEvent((Display*)getApp()->getDisplay(),socket, False, NoEventMask, &ev); + } + } + +void GMPlug::create() { + FXTopWindow::create(); + if (xid) { + Atom xembedinfo = XInternAtom((Display*)getApp()->getDisplay(),"_XEMBED_INFO",0); + if (xembedinfo!=None) { + unsigned long info[2]={0,(1<<0)}; + XChangeProperty((Display*)getApp()->getDisplay(),xid,xembedinfo,xembedinfo,32,PropModeReplace,(unsigned char*)info,2); + } + } + } + +long GMPlug::onEmbedded(FXObject*,FXSelector,void*ptr){ + flags|=FLAG_SHOWN; + socket=(FXID)(FXival)ptr; + return 1; + } + + + + +void ewmh_set_window_icon(const FXWindow * window,FXImage * icon) { +#ifndef WIN32 + Atom net_wm_icon = XInternAtom((Display*)window->getApp()->getDisplay(),"_NET_WM_ICON",False); + + unsigned long * data=NULL; + int nelems=2+(icon->getWidth()*icon->getHeight()); + + allocElms(data,nelems); + + data[0]=icon->getWidth(); + data[1]=icon->getHeight(); + for (FXint i=0;i<(icon->getWidth()*icon->getHeight());i++){ +#if FOXVERSION < FXVERSION(1,7,26) + const FXColor val = icon->getData()[i]; + data[i+2]=FXRGBA(FXBLUEVAL(val),FXGREENVAL(val),FXREDVAL(val),FXALPHAVAL(val)); +#else + data[i+2]=icon->getData()[i]; +#endif + } + + /// Set Property + XChangeProperty((Display*)window->getApp()->getDisplay(),window->id(),net_wm_icon,XA_CARDINAL,32,PropModeReplace,(unsigned char*)data,nelems); + + freeElms(data); +#endif + } + + + + +void fix_wm_properties(const FXWindow * window) { +#ifndef WIN32 + XTextProperty textprop; +#if FOXVERSION < FXVERSION(1,7,0) + FXString host=FXURL::hostname(); +#else + FXString host=FXSystem::getHostName(); +#endif + + /// set the name of the machine on which this application is running + textprop.value = (unsigned char *)host.text(); + textprop.encoding = XA_STRING; + textprop.format = 8; + textprop.nitems = host.length(); + XSetWMClientMachine((Display*)window->getApp()->getDisplay(),window->id(), &textprop); + + /// Override class hints + XClassHint hint; + hint.res_name=(char*)"gogglesmm"; + hint.res_class=(char*)"gogglesmm"; + XSetClassHint((Display*)window->getApp()->getDisplay(),window->id(),&hint); +#endif + } + +void ewmh_change_window_type(const FXWindow * window,FXuint kind) { +#ifndef WIN32 + static Atom net_wm_window_type = None; + static Atom net_wm_window_type_menu = None; + static Atom net_wm_window_type_dropdown_menu = None; + static Atom net_wm_window_type_popup_menu = None; + static Atom net_wm_window_type_combo = None; + static Atom net_wm_window_type_tooltip = None; + static Atom net_wm_window_type_dialog = None; + static Atom net_wm_window_type_normal = None; + + FXASSERT(window->getApp()); + FXASSERT(window->getApp()->getDisplay()); + FXASSERT(window->id()); + + if (net_wm_window_type==None){ + net_wm_window_type = XInternAtom((Display*)window->getApp()->getDisplay(),"_NET_WM_WINDOW_TYPE",False); + net_wm_window_type_menu = XInternAtom((Display*)window->getApp()->getDisplay(),"_NET_WM_WINDOW_TYPE_MENU",False); + net_wm_window_type_dropdown_menu = XInternAtom((Display*)window->getApp()->getDisplay(),"_NET_WM_WINDOW_TYPE_DROPDOWN_MENU",False); + net_wm_window_type_popup_menu = XInternAtom((Display*)window->getApp()->getDisplay(),"_NET_WM_WINDOW_TYPE_POPUP_MENU",False); + net_wm_window_type_combo = XInternAtom((Display*)window->getApp()->getDisplay(),"_NET_WM_WINDOW_TYPE_COMBO",False); + net_wm_window_type_tooltip = XInternAtom((Display*)window->getApp()->getDisplay(),"_NET_WM_WINDOW_TYPE_TOOLTIP",False); + net_wm_window_type_dialog = XInternAtom((Display*)window->getApp()->getDisplay(),"_NET_WM_WINDOW_TYPE_DIALOG",False); + net_wm_window_type_normal = XInternAtom((Display*)window->getApp()->getDisplay(),"_NET_WM_WINDOW_TYPE_NORMAL",False); + } + + + unsigned int ntypes=0; + Atom types[3]={0}; + + switch(kind) { + case WINDOWTYPE_DIALOG : types[0]=net_wm_window_type_dialog; + ntypes=1; + break; + case WINDOWTYPE_COMBO : types[0]=net_wm_window_type_combo; + types[1]=net_wm_window_type_dropdown_menu; + types[2]=net_wm_window_type_menu; + ntypes=3; + break; + case WINDOWTYPE_POPUP_MENU : types[0]=net_wm_window_type_popup_menu; + types[1]=net_wm_window_type_menu; + ntypes=2; + break; + case WINDOWTYPE_DROPDOWN_MENU : types[0]=net_wm_window_type_dropdown_menu; + types[1]=net_wm_window_type_menu; + ntypes=2; + break; + case WINDOWTYPE_TOOLTIP : types[0]=net_wm_window_type_tooltip; + ntypes=1; + break; + default : types[0]=net_wm_window_type_normal; + ntypes=1; + break; + } + + /// Set Property + XChangeProperty((Display*)window->getApp()->getDisplay(),window->id(),net_wm_window_type,XA_ATOM,32,PropModeReplace,(unsigned char*)&types,ntypes); +#endif + } + + + + diff --git a/src/GMApp.h b/src/GMApp.h new file mode 100644 index 0000000..02697a5 --- /dev/null +++ b/src/GMApp.h @@ -0,0 +1,105 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2010 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#ifndef GMAPP_H +#define GMAPP_H + +class GMClipboard; + +enum { + SEL_EMBED_NOTIFY = SEL_LAST, + SEL_EMBED_MODAL_ON, + SEL_EMBED_MODAL_OFF + }; + + +class GMApp : public FXApp { +FXDECLARE(GMApp) +friend class GMPlug; +protected: + FXID xembed; + FXID xsystemtray; + FXID xmanager; + GMClipboard * clipboard; + FXFontPtr thickfont; +protected: +#if FOXVERSION < FXVERSION(1,7,0) + virtual bool dispatchEvent(FXRawEvent & event); +#else + virtual FXbool dispatchEvent(FXRawEvent & event); +#endif +public: + static GMApp * instance(); +public: + GMApp(const FXString& name="Application",const FXString& vendor="FoxDefault"); + + static FXString getCacheDirectory(FXbool create=false); + +#if FOXVERSION < FXVERSION(1,7,0) + virtual void init(int& argc,char** argv,bool connect=true); +#else + virtual void init(int& argc,char** argv,FXbool connect=true); +#endif + + void setFont(const FXFontDesc &); + + void updateFont(); + + FXFont* getThickFont() const { return thickfont; } + + virtual void create(); + + virtual ~GMApp(); + }; + + +class GMPlug : public FXTopWindow { +FXDECLARE(GMPlug) +protected: + FXID socket; + FXuchar xembedflags; +protected: +#if FOXVERSION < FXVERSION(1,7,0) + virtual bool doesOverrideRedirect() const; +#else + virtual FXbool doesOverrideRedirect() const; +#endif +private: + GMPlug(const GMPlug*); + GMPlug& operator=(const GMPlug&); +protected: + GMPlug(); +public: + long onEmbedded(FXObject*,FXSelector,void*); +public: + GMPlug(FXApp * app); + + virtual void setFocus(); + + virtual void create(); + + virtual ~GMPlug(); + }; + + +#endif + + + + + diff --git a/src/GMAudioScrobbler.cpp b/src/GMAudioScrobbler.cpp new file mode 100644 index 0000000..87a5322 --- /dev/null +++ b/src/GMAudioScrobbler.cpp @@ -0,0 +1,1392 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2007-2010 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#include "gmdefs.h" +#include "gmutils.h" + + + +#include "ap_buffer.h" +#include "ap_http.h" +#include "ap_xml_parser.h" + + +#include "GMAudioScrobbler.h" + + + +#ifdef HAVE_GCRYPT +#include "gcrypt.h" +#else +#include "md5.h" +#endif + + + +using namespace ap; + + +/****************************************************************************** + * + * D E F I N E S + * + ******************************************************************************/ + +#define MAX_HANDSHAKE_TIMEOUT 7200 +#define MIN_HANDSHAKE_TIMEOUT 60 +#define DISABLE_HANDSHAKE_TIMEOUT 0 + + +/// Old Style +#define CLIENT_ID "gmm" +#define CLIENT_VERSION "0.1" + +/// New Style +#define CLIENT_KEY "76525254135cc544d13d381514222c56" +#define CLIENT_SECRET "09397d5d6a55858a6883735b7cb694f7" + +#define SCROBBLER_CACHE_FILE PATHSEPSTRING ".goggles" PATHSEPSTRING "scrobbler.cache" + +#define LASTFM_URL "http://ws.audioscrobbler.com:80/2.0/" +#define LASTFM_OLD_URL "http://post.audioscrobbler.com:80" +#define LIBREFM_URL "http://turtle.libre.fm:80" + +/// Error codes returned by last.fm +enum { + LASTFM_ERROR_UNKNOWN = 0, + LASTFM_ERROR_TOKEN_UNAUTHORIZED = 14, + LASTFM_ERROR_TOKEN_EXPIRED = 15, + LASTFM_ERROR_BADSESSION = 9, + LASTFM_ERROR_OFFLINE = 11, + LASTFM_ERROR_UNAVAILABLE = 16, + LASTFM_ERROR_KEY_INVALID = 10, + LASTFM_ERROR_KEY_SUSPENDED = 26 + }; + +/* +1 : This error does not exist +2 : Invalid service -This service does not exist +3 : Invalid Method - No method with that name in this package +4 : Authentication Failed - You do not have permissions to access the service +5 : Invalid format - This service doesn't exist in that format +6 : Invalid parameters - Your request is missing a required parameter +7 : Invalid resource specified +8 : Operation failed - Most likely the backend service failed. Please try again. +9 : Invalid session key - Please re-authenticate +10 : Invalid API key - You must be granted a valid key by last.fm +11 : Service Offline - This service is temporarily offline. Try again later. +12 : Subscribers Only - This station is only available to paid last.fm subscribers +13 : Invalid method signature supplied +14 : Unauthorized Token - This token has not been authorized +15 : This item is not available for streaming. +16 : The service is temporarily unavailable, please try again. +17 : Login: User requires to be logged in +18 : Trial Expired - This user has no free radio plays left. Subscription required. +19 : This error does not exist +20 : Not Enough Content - There is not enough content to play this station +21 : Not Enough Members - This group does not have enough members for radio +22 : Not Enough Fans - This artist does not have enough fans for for radio +23 : Not Enough Neighbours - There are not enough neighbours for radio +24 : No Peak Radio - This user is not allowed to listen to radio during peak usage +25 : Radio Not Found - Radio station not found +26 : API Key Suspended - This application is not allowed to make requests to the web services +27 : Deprecated - This type of request is no longer supported +*/ + + +/****************************************************************************** + * + * H E L P E R F U N C T I O N S + * + ******************************************************************************/ + +FXbool init_gcrypt() { +#ifdef HAVE_GCRYPT + if (!gcry_check_version(GCRYPT_VERSION)) { + fxwarning("libgcrypt version mismatch"); + return false; + } + gcry_control(GCRYCTL_DISABLE_SECMEM,0); + gcry_control(GCRYCTL_INITIALIZATION_FINISHED,0); +#endif + return true; + } + + +static void checksum(FXString & io){ + if (io.empty()) return; +#ifdef HAVE_GCRYPT + FXuchar digest[16]; + gcry_md_hash_buffer(GCRY_MD_MD5,(void*)digest,(const void*)io.text(),io.length()); +#else + md5_state_t pms; + md5_byte_t digest[16]; + md5_init(&pms); + md5_append(&pms,(const md5_byte_t*)io.text(),io.length()); + md5_finish(&pms,digest); +#endif + + io.length(32); +#if FOXVERSION < FXVERSION(1,7,0) + for (FXint i=0,d=0;i<32;i+=2,d++) { + io[i]=FXString::hex[(digest[d]/16)%16]; + io[i+1]=FXString::hex[digest[d]%16]; + } +#else + for (FXint i=0,d=0;i<32;i+=2,d++) { + io[i]=Ascii::toLower(FXString::value2Digit[(digest[d]/16)%16]); + io[i+1]=Ascii::toLower(FXString::value2Digit[digest[d]%16]); + } +#endif + } + +#define URL_UNSAFE "#$-_.+!*'><()\\,%\"" // Always Encode +#define URL_RESERVED ";/?:@=&" // Only encode if not used as reserved by scheme + + +/**********************************************************************************************************/ + + +void GMAudioScrobblerTrack::clear() { + artist.clear(); + album.clear(); + title.clear(); + duration=0; + no=0; + timestamp=0; + loveban=0; + } + +void GMAudioScrobblerTrack::save(FXStream & store) const { + store << artist; + store << album; + store << title; + store << duration; + store << no; + store << timestamp; + store << loveban; + } + +void GMAudioScrobblerTrack::load(FXStream & store) { + store >> artist; + store >> album; + store >> title; + store >> duration; + store >> no; + store >> timestamp; + store >> loveban; + } + + +class ServiceResponse : public XMLStream { +protected: + FXbool status; + FXint code; + FXString message; + FXString key; + FXString token; +protected: + FXint elem; +protected: + FXint begin(const FXchar *,const FXchar**); + void data(const FXchar *,FXint len); + void end(const FXchar *); + void parse_lfm_atts(const FXchar **); + void parse_lfm_error_atts(const FXchar **); +public: + enum { + Elem_None, + Elem_LastFM, + Elem_LastFM_Error, + Elem_LastFM_Token, + Elem_LastFM_Session, + Elem_LastFM_Session_Key, + }; +public: + ServiceResponse(); + ~ServiceResponse(); + + FXbool getStatus() const { return status; } + FXint getErrorCode() const { return code; } + const FXString & getErrorMessage() const { return message; } + const FXString & getSessionKey() const { return key; } + const FXString & getToken() const { return token; } + }; + + +ServiceResponse::ServiceResponse() : elem(Elem_None) { + } + +ServiceResponse::~ServiceResponse() { + } + + +void ServiceResponse::parse_lfm_atts(const FXchar ** attributes) { + status=false; + for (FXint i=0;attributes[i];i+=2){ + if (compare(attributes[i],"status")==0) { + if (comparecase(attributes[i+1],"ok")==0){ + status=true; + } + } + } + } + + +void ServiceResponse::parse_lfm_error_atts(const FXchar ** attributes) { + FXString cd; + for (FXint i=0;attributes[i];i+=2){ + if (compare(attributes[i],"code")==0) { + cd=attributes[i+1]; + code=GMIntVal(cd); + } + } + } + + +FXint ServiceResponse::begin(const FXchar * element,const FXchar ** attributes){ + switch(elem) { + case Elem_None: + { + if (compare(element,"lfm")==0) { + parse_lfm_atts(attributes); + elem=Elem_LastFM; + return 1; + } + } break; + case Elem_LastFM: + { + if (compare(element,"error")==0) { + parse_lfm_error_atts(attributes); + elem=Elem_LastFM_Error; + return 1; + } + else if (compare(element,"session")==0) { + elem=Elem_LastFM_Session; + return 1; + } + else if (compare(element,"token")==0) { + elem=Elem_LastFM_Token; + return 1; + } + } break; + case Elem_LastFM_Session: + { + if (compare(element,"key")==0) { + elem=Elem_LastFM_Session_Key; + return 1; + } + } break; + default: return 0; // skip + } + return 0; + } + + +void ServiceResponse::data(const FXchar* data,FXint len){ + if (elem==Elem_LastFM_Error) { + message.assign(data,len); + } + else if (elem==Elem_LastFM_Session_Key) { + key.assign(data,len); + } + else if (elem==Elem_LastFM_Token) { + token.assign(data,len); + } + } + +void ServiceResponse::end(const FXchar*) { + switch(elem){ + case Elem_LastFM_Session_Key : elem=Elem_LastFM_Session; break; + case Elem_LastFM_Token : + case Elem_LastFM_Error : + case Elem_LastFM_Session : elem=Elem_LastFM; break; + case Elem_LastFM : elem=Elem_None; break; + } + } + + + +FXbool GMAudioScrobbler::post_request(const FXString & url,const FXString & message,FXString & output) { + FXTRACE((60,"post_request %s - %s\n",url.text(),message.text())); + HttpClient client; + if (client.basic("POST",url, + "Content-Type: application/x-www-form-urlencoded\r\n" + "Connection: close\r\n", + message)) { + output = client.body(); + return true; + } + else { + FXTRACE((60,"post_request failed\n")); + set_timeout(); + return false; + } + } + +FXbool GMAudioScrobbler::get_request(const FXString & url,FXString & output) { + FXTRACE((60,"get_request\n")); + HttpClient client; + if (client.basic("GET",url,"Connection: close\r\n")) { + output = client.body(); + return true; + } + else { + FXTRACE((60,"get_request failed\n")); + set_timeout(); + return false; + } + } + + + + +/****************************************************************************** + * + * C O N S T R U C T O R + * + ******************************************************************************/ + + +GMAudioScrobbler::GMAudioScrobbler(FXObject* tgt,FXSelector msg) : + flags(FLAG_NONE), + feedback(FXApp::instance()), + target(tgt), + message(msg), + started(false), + mode(SERVICE_LASTFM), + timeout(DISABLE_HANDSHAKE_TIMEOUT), + nsubmitted(0), + nfailed(0) { + + username=FXApp::instance()->reg().readStringEntry("LastFM","username",NULL); + password=FXApp::instance()->reg().readStringEntry("LastFM","password",NULL); + + FXString uri = FXApp::instance()->reg().readStringEntry("LastFM","handshake-url",LASTFM_URL); + if (uri==LASTFM_OLD_URL) { + uri=LASTFM_URL; + username.clear(); + password.clear(); + FXApp::instance()->reg().writeBoolEntry("LastFM","client-banned",false); + } + + handshake_url = uri; + mode=getService(); + + /// Get last session + if (mode==SERVICE_LASTFM) { + session=FXApp::instance()->reg().readStringEntry("LastFM","session",NULL); + if (!session.empty()) { + FXTRACE((60,"GMAudioScrobbler::GMAudioScrobbler - Session: %s\n",session.text())); + nowplaying_url=handshake_url; + submit_url=handshake_url; + } + } + + /// Check if we're banned + if (compare(FXApp::instance()->reg().readStringEntry("LastFM","client-id",CLIENT_ID),CLIENT_ID)==0 && + compare(FXApp::instance()->reg().readStringEntry("LastFM","client-version",CLIENT_VERSION),CLIENT_VERSION)==0){ + if (FXApp::instance()->reg().readBoolEntry("LastFM","client-banned",false)) + flags|=FLAG_BANNED; + } + else { /// using different client, reset banned flag + FXApp::instance()->reg().writeBoolEntry("LastFM","client-banned",false); + } + + /// Check if we should scrobble tracks or not. + if (!FXApp::instance()->reg().readBoolEntry("LastFM","scrobble-tracks",false)) + flags|=FLAG_DISABLED; + + FXApp::instance()->reg().writeStringEntry("LastFM","client-id",CLIENT_ID); + FXApp::instance()->reg().writeStringEntry("LastFM","client-version",CLIENT_VERSION); + load_queue(); + + + } + +GMAudioScrobbler::~GMAudioScrobbler(){ + save_queue(); + } + + +/****************************************************************************** + * + * P U B L I C A P I + * + ******************************************************************************/ + +FXuint GMAudioScrobbler::getService() { + FXString host = GMURL::host(handshake_url); + if (host=="ws.audioscrobbler.com") + return SERVICE_LASTFM; + else if (host=="turtle.libre.fm") + return SERVICE_LIBREFM; + else + return SERVICE_CUSTOM; + } + +void GMAudioScrobbler::service(FXuint s) { + FXTRACE((60,"GMAudioScrobbler::service\n")); + if (s==SERVICE_LASTFM || s==SERVICE_LIBREFM) { + + mutex_data.lock(); + + if (s==SERVICE_LASTFM) + FXApp::instance()->reg().writeStringEntry("LastFM","handshake-url",LASTFM_URL); + else + FXApp::instance()->reg().writeStringEntry("LastFM","handshake-url",LIBREFM_URL); + + handshake_url=FXApp::instance()->reg().readStringEntry("LastFM","handshake-url",LASTFM_URL); + nowplaying_url.clear(); + submit_url.clear(); + session.clear(); + token.clear(); + + flags|=FLAG_SERVICE; + flags&=~FLAG_BADAUTH|FLAG_TIMEOUT|FLAG_BANNED|FLAG_BADTIME; + + username.clear(); + password.clear(); + FXApp::instance()->reg().writeStringEntry("LastFM","username",username.text()); + FXApp::instance()->reg().writeStringEntry("LastFM","password",password.text()); + FXApp::instance()->reg().writeStringEntry("LastFM","session",""); + FXApp::instance()->reg().writeBoolEntry("LastFM","client-banned",false); + + mode=s; + + reset_timeout(); + mutex_data.unlock(); + + wakeup(); + } + } + + + + +void GMAudioScrobbler::login(const FXString & user,const FXString & pass) { + FXTRACE((60,"GMAudioScrobbler::login\n")); + mutex_data.lock(); + flags&=~FLAG_DISABLED; + if ( (flags&FLAG_BANNED) || (flags&FLAG_BADTIME) ) { + mutex_data.unlock(); + shutdown(); + } + else { + if (mode==SERVICE_LIBREFM) { + FXString newpass=pass; + checksum(newpass); + if (user!=username || newpass!=password) { + username=user; + password=newpass; + FXApp::instance()->reg().writeStringEntry("LastFM","username",username.text()); + FXApp::instance()->reg().writeStringEntry("LastFM","password",password.text()); + flags|=FLAG_LOGIN_CHANGED; + flags&=~FLAG_BADAUTH; + mutex_data.unlock(); + if (username.empty() || password.empty()) + shutdown(); + else + runTask(); + } + else { + mutex_data.unlock(); + } + } + else { + flags|=FLAG_LOGIN_CHANGED; + flags&=~FLAG_BADAUTH; + mutex_data.unlock(); + runTask(); + } + } + } + +void GMAudioScrobbler::nudge(){ + FXTRACE((60,"GMAudioScrobbler::nudge\n")); + if (started) { + + mutex_data.lock(); + flags|=FLAG_NETWORK; + mutex_data.unlock(); + + wakeup(); + } + } + + +FXbool GMAudioScrobbler::can_submit() { + if (getService()==SERVICE_LASTFM) + return !( (flags&FLAG_DISABLED) || (flags&FLAG_BANNED) || (flags&FLAG_BADAUTH) || (flags&FLAG_BADTIME)); + else + return !( (flags&FLAG_DISABLED) || (flags&FLAG_BANNED) || (flags&FLAG_BADAUTH) || (flags&FLAG_BADTIME) || username.empty() || password.empty() ); + } + +void GMAudioScrobbler::nowplaying(GMTrack & info){ + mutex_data.lock(); + if (!can_submit()) { + mutex_data.unlock(); + shutdown(); + } + else { + nowplayingtrack=GMAudioScrobblerTrack(1,info,0); + mutex_data.unlock(); + runTask(); + } + } + +void GMAudioScrobbler::submit(FXlong timestamp,GMTrack & info){ + if (info.time<30) + return; + + mutex_data.lock(); + if (!can_submit()) { + mutex_data.unlock(); + shutdown(); + } + else { + submitqueue.append(GMAudioScrobblerTrack(timestamp,info,0)); + mutex_data.unlock(); + runTask(); + } + } + +void GMAudioScrobbler::loveban(GMTrack & /*info*/, FXint /*loveban*/){ +#if 0 + mutex_data.lock(); + if (!can_submit()) { + mutex_data.unlock(); + shutdown(); + } + else { + submitqueue.append(GMAudioScrobblerTrack(0,info,loveban)); + mutex_data.unlock(); + runTask(); + } +#endif + return; +} + +void GMAudioScrobbler::wakeup(){ + mutex_task.lock(); + condition_task.signal(); + mutex_task.unlock(); + } + +void GMAudioScrobbler::runTask() { + if (!started) { + start(); + started=true; + } + else { + wakeup(); + } + } + + +void GMAudioScrobbler::shutdown(){ + if (started) { + mutex_data.lock(); + flags|=FLAG_SHUTDOWN; + mutex_data.unlock(); + wakeup(); + join(); + started=false; + } + + /// Save Session + FXApp::instance()->reg().writeStringEntry("LastFM","session",session.text()); + FXApp::instance()->reg().writeBoolEntry("LastFM","client-banned",(flags&FLAG_BANNED)); + + } + +void GMAudioScrobbler::disable(){ + if (started) { + mutex_data.lock(); + flags|=FLAG_SHUTDOWN|FLAG_DISABLED; + mutex_data.unlock(); + wakeup(); + join(); + started=false; + } + FXApp::instance()->reg().writeBoolEntry("LastFM","scrobble-tracks",false); + } + +void GMAudioScrobbler::enable() { + mutex_data.lock(); + flags&=~FLAG_DISABLED; + flags&=~FLAG_SHUTDOWN; + mutex_data.unlock(); + FXApp::instance()->reg().writeBoolEntry("LastFM","scrobble-tracks",true); + } + + +FXString GMAudioScrobbler::getUsername() { + FXMutexLock lock(mutex_data); + return username; + } + +FXbool GMAudioScrobbler::hasPassword(){ + FXMutexLock lock(mutex_data); + return !password.empty(); + } + + +FXbool GMAudioScrobbler::isBanned(){ + FXMutexLock lock(mutex_data); + return (flags&FLAG_BANNED); + } + +FXbool GMAudioScrobbler::isEnabled(){ + FXMutexLock lock(mutex_data); + return !(flags&FLAG_DISABLED); + } + +/****************************************************************************** + * + * P R O T E C T E D A P I + * + ******************************************************************************/ + + +void GMAudioScrobbler::load_queue(){ + FXTRACE((60,"GMAudioScrobbler::load_queue\n")); + FXuint version,size; + FXString filename = FXSystem::getHomeDirectory() + SCROBBLER_CACHE_FILE; + FXFileStream store; + if (store.open(filename,FXStreamLoad)){ + store >> version; + if (version==20080501) { + store >> size; + submitqueue.no(size); + for (FXint i=0;i %d entries\n",submitqueue.no())); + FXuint version=20080501; + FXString filename = FXSystem::getHomeDirectory() + SCROBBLER_CACHE_FILE; + if (submitqueue.no()) { + FXFileStream store; + if (store.open(filename,FXStreamSave)){ + store << version; + store << submitqueue.no(); + for (FXint i=0;i0) { +#if FOXVERSION < FXVERSION(1,7,0) + FXlong wakeuptime = FXThread::time()+((FXlong)timeout*1000000000LL); // absolute time +#else + FXlong wakeuptime = ((FXlong)timeout*1000000000LL); // relative time + FXlong start = FXThread::time(); +#endif + while(1) { +#if FOXVERSION < FXVERSION(1,7,0) + FXTRACE((60,"GMAudioScrobbler::waitForTask => %ld\n",timeout)); +#else + FXTRACE((60,"GMAudioScrobbler::waitForTask => %ld\n",wakeuptime)); +#endif + mutex_task.lock(); + if (!condition_task.wait(mutex_task,wakeuptime)){ + mutex_task.unlock(); + return true; + } + else { + mutex_task.unlock(); + FXMutexLock lock(mutex_data); + + if (flags&FLAG_SHUTDOWN) + return false; + + if (flags&FLAG_NETWORK) { + flags&=~FLAG_NETWORK; + return true; + } + +#if FOXVERSION > FXVERSION(1,7,0) + FXlong now = FXThread::time(); + wakeuptime -= (now - start); + start = now; + + /// Done waiting + if (wakeuptime<=0) + return true; +#endif + FXTRACE((60,"GMAudioScrobbler::waitForTask => reset\n")); + } + } + } + else { + mutex_task.lock(); + condition_task.wait(mutex_task); + mutex_task.unlock(); + } + return true; + } + +FXuchar GMAudioScrobbler::getNextTask() { + FXTRACE((60,"GMAudioScrobbler::getNextTask\n")); + FXMutexLock lock(mutex_data); + + if (flags&FLAG_SHUTDOWN){ + flags&=~FLAG_SHUTDOWN; + return TASK_SHUTDOWN; + } + + if (flags&FLAG_SERVICE) { + session.clear(); + flags&=~FLAG_SERVICE; + return TASK_NONE; + } + + if (flags&FLAG_BADAUTH) + return TASK_NONE; + + if (flags&FLAG_TIMEOUT) { + flags&=~FLAG_TIMEOUT; + return TASK_NONE; + } + + if (flags&FLAG_LOGIN_CHANGED) { + session.clear(); + if (getService()==SERVICE_LASTFM) { + if (!token.empty()) + return TASK_LOGIN; + else + return TASK_AUTHENTICATE; + } + else { + return TASK_LOGIN; + } + } + + if (submitqueue.no() || nowplayingtrack.timestamp) { + if (session.empty()) { + if (getService()==SERVICE_LASTFM) { + if (!token.empty()) + return TASK_LOGIN; + else + return TASK_AUTHENTICATE; + } + else { + return TASK_LOGIN; + } + } + else if (submitqueue.no()) return TASK_SUBMIT; + else return TASK_NOWPLAYING; + } + + return TASK_NONE; + } + + + +FXint GMAudioScrobbler::run() { + FXTRACE((60,"GMAudioScrobbler::run\n")); + + FXuchar next=TASK_NONE; + do { + while((next=getNextTask())!=TASK_NONE) { + switch(next){ + case TASK_AUTHENTICATE : authenticate(); break; + case TASK_LOGIN : handshake(); break; + case TASK_SUBMIT : submit(); break; + case TASK_NOWPLAYING : nowplaying(); break; + case TASK_SHUTDOWN : goto done; break; + } + } + } while(waitForTask()); +done: + + mutex_data.lock(); + flags&=~FLAG_SHUTDOWN; + flags&=~FLAG_LOGIN_CHANGED; + flags&=~FLAG_TIMEOUT; + mutex_data.unlock(); + + FXTRACE((60,"GMAudioScrobbler::run -> shutdown\n")); + return 0; + } + + + +void GMAudioScrobbler::set_timeout(){ + flags|=FLAG_TIMEOUT; + if (timeout==0) + timeout=MIN_HANDSHAKE_TIMEOUT; + else + timeout=FXMIN(MAX_HANDSHAKE_TIMEOUT,timeout<<1); + } + +void GMAudioScrobbler::reset_timeout(){ + timeout=DISABLE_HANDSHAKE_TIMEOUT; + } + + +void GMAudioScrobbler::set_submit_failed() { + FXTRACE((60,"GMAudioScrobbler::set_failed\n")); + nfailed++; + if (nfailed==3) { + session.clear(); + nfailed=0; + } + } + + + + + +void GMAudioScrobbler::create_token_request(FXString & request) { + FXMutexLock lock(mutex_data); + FXTRACE((60,"GMAudioScrobbler::create_token_request\n")); + FXString signature="api_key"CLIENT_KEY"methodauth.getToken"CLIENT_SECRET; + checksum(signature); + request=GMStringFormat("method=auth.getToken&api_key="CLIENT_KEY"&api_sig=%s",signature.text()); + flags&=~(FLAG_LOGIN_CHANGED); + } + + +void GMAudioScrobbler::process_token_response(const FXString & response){ + FXMutexLock lock(mutex_data); + if (flags&FLAG_LOGIN_CHANGED) return; + ServiceResponse service; + FXTRACE((60,"GMAudioScrobbler::process_token_response\n%s\n",response.text())); + if (service.parse(response) && service.getStatus()) { + token=service.getToken(); + FXTRACE((60,"GMAudioScrobbler::process_token_response => token=%s\n",token.text())); + FXString url="http://www.last.fm/api/auth/?api_key="CLIENT_KEY"&token="+token; + feedback.message(target,FXSEL(SEL_OPENED,message),url.text(),url.length()); + reset_timeout(); /// Reset timer + set_timeout(); /// Let's wait at least 60s + } + else { + FXTRACE((60,"last.fm service failed with code %d: %s\n",service.getErrorCode(),service.getErrorMessage().text())); + flags|=FLAG_BADAUTH; + } + } + + + +FXuint GMAudioScrobbler::create_handshake_request(FXString & request) { + FXMutexLock lock(mutex_data); + FXTRACE((60,"GMAudioScrobbler::create_handshake_request\n")); + if (mode==SERVICE_LASTFM) { + FXString signature=GMStringFormat("api_key%smethodauth.getSessiontoken%s%s",CLIENT_KEY,token.text(),CLIENT_SECRET); + checksum(signature); + request=GMStringFormat("method=auth.getSession&api_key=%s&api_sig=%s&token=%s",CLIENT_KEY,signature.text(),token.text()); + } + else { + FXlong timestamp = FXThread::time()/1000000000; + FXString timestamp_text = GMStringVal(timestamp); + FXString tk = password + timestamp_text; + checksum(tk); + request=GMStringFormat("/?hs=true&p=1.2&c="CLIENT_ID"&v="CLIENT_VERSION"&u=%s&t=%s&a=%s",username.text(),timestamp_text.text(),tk.text()); + } + flags&=~(FLAG_LOGIN_CHANGED); + return mode; + } + + + + +void GMAudioScrobbler::process_handshake_response(const FXString & response){ + FXMutexLock lock(mutex_data); + if (flags&FLAG_LOGIN_CHANGED) return; + FXTRACE((60,"GMAudioScrobbler::process_handshake_response\n%s",response.text())); + + if (mode==SERVICE_LASTFM) { + ServiceResponse service; + if (!service.parse(response) || !service.getStatus()){ + FXTRACE((60,"last.fm service failed with code %d: %s\n",service.getErrorCode(),service.getErrorMessage().text())); + switch(service.getErrorCode()) { + case LASTFM_ERROR_TOKEN_EXPIRED : token.clear(); break; + case LASTFM_ERROR_TOKEN_UNAUTHORIZED: + case LASTFM_ERROR_OFFLINE : + case LASTFM_ERROR_UNAVAILABLE : set_timeout(); break; + default : flags|=FLAG_BADAUTH; break; + } + } + else { + session=service.getSessionKey(); + nowplaying_url=handshake_url; + submit_url=handshake_url; + flags&=~FLAG_BADAUTH; + reset_timeout(); + } + } + else { + FXString code; + code=response.section('\n',0); + if (compare(code,"OK",2)==0) { + session = response.section('\n',1); + nowplaying_url=response.section('\n',2); + submit_url=response.section('\n',3); + flags&=~FLAG_BADAUTH; + reset_timeout(); + } + else if (compare(code,"BANNED",6)==0){ + FXTRACE((60,"\t=> BANNED\n")); + const FXchar msg[] = "This version of Goggles Music Manager is not supported\nby scrobbler service. Please upgrade to a newer version of GMM."; + feedback.message(target,FXSEL(SEL_COMMAND,message),msg,ARRAYNUMBER(msg)); + flags|=FLAG_BANNED; + //FXApp::instance()->reg().writeBoolEntry("LastFM","client-banned",true); + } + else if (compare(code,"BADTIME",7)==0){ + FXTRACE((60,"\t=> BADTIME\n")); + const FXchar msg[] = "Unable submit tracks scrobbler service. The system time doesn't match\n" + "the scrobbler server time. Please adjust your system time\n" + "and restart GMM to start scrobbling."; + feedback.message(target,FXSEL(SEL_COMMAND,message),msg,ARRAYNUMBER(msg)); + flags|=FLAG_BADTIME; + } + else if (compare(code,"BADAUTH",7)==0){ + FXTRACE((60,"\t=> BADAUTH\n")); + const FXchar msg[] = "Unable to login to scrobbler service.\nUsername and password do not match."; + feedback.message(target,FXSEL(SEL_COMMAND,message),msg,ARRAYNUMBER(msg)); + flags|=FLAG_BADAUTH; + } + else if (compare(code,"FAILED",6)==0){ + FXTRACE((60,"\t=> FAILED\n")); + set_timeout(); + } + else { + FXTRACE((60,"\t=> Unknown\n")); + FXTRACE((60,"%s\n",response.text())); + set_timeout(); + } + } + } + + + +void GMAudioScrobbler::authenticate() { + FXString request; + FXString response; + create_token_request(request); + if (post_request(handshake_url,request,response)) + process_token_response(response); + } + +void GMAudioScrobbler::handshake() { + FXString request; + FXString response; + FXbool result; + + if (create_handshake_request(request)==SERVICE_LASTFM) + result = post_request(handshake_url,request,response); + else + result = get_request(handshake_url+request,response); + + if (result) + process_handshake_response(response); + } + +void GMAudioScrobbler::nowplaying() { + FXString request; + FXString response; + create_nowplaying_request(request); + if (post_request(nowplaying_url,request,response)) + process_nowplaying_response(response); + } + +void GMAudioScrobbler::submit() { + FXString request; + FXString response; + create_submit_request(request); + if (post_request(submit_url,request,response)) + process_submit_response(response); + } + +void GMAudioScrobbler::loveban() { + FXString request; + FXString response; + create_loveban_request(request); + if (post_request(submit_url,request,response)) + process_loveban_response(response); + } + + + + + + + +void GMAudioScrobbler::create_nowplaying_request(FXString & request) { + FXMutexLock lock(mutex_data); + FXTRACE((60,"GMAudioScrobbler::create_nowplaying_request\n")); + if (mode==SERVICE_LASTFM) { + FXString signature=GMStringFormat("album%s" + "api_key"CLIENT_KEY + "artist%s" + "duration%d" + "methodtrack.updateNowPlaying" + "sk%s" + "track%s" + "trackNumber%d" + CLIENT_SECRET, + nowplayingtrack.album.text(), + nowplayingtrack.artist.text(), + nowplayingtrack.duration, + session.text(), + nowplayingtrack.title.text(), + nowplayingtrack.no); + + checksum(signature); + + request=GMStringFormat("method=track.updateNowPlaying" + "&track=%s" + "&artist=%s" + "&album=%s" + "&trackNumber=%d" + "&duration=%d" + "&api_key="CLIENT_KEY + "&api_sig=%s" + "&sk=%s", + gm_url_encode(nowplayingtrack.title).text(), + gm_url_encode(nowplayingtrack.artist).text(), + gm_url_encode(nowplayingtrack.album).text(), + nowplayingtrack.no, + nowplayingtrack.duration, + signature.text(), + session.text()); + } + else { + request=GMStringFormat("s=%s&a=%s&t=%s&b=%s&l=%d&n=%d&m",gm_url_encode(session).text(), + gm_url_encode(nowplayingtrack.artist).text(), + gm_url_encode(nowplayingtrack.title).text(), + gm_url_encode(nowplayingtrack.album).text(), + nowplayingtrack.duration, + nowplayingtrack.no); + } + nowplayingtrack.clear(); + } + + +void GMAudioScrobbler::process_nowplaying_response(const FXString & response){ + FXMutexLock lock(mutex_data); + if (flags&FLAG_LOGIN_CHANGED) return; + FXTRACE((60,"GMAudioScrobbler::process_nowplaying_response:\n %s\n",response.text())); + if (mode==SERVICE_LASTFM) { + ServiceResponse service; + if (!service.parse(response) || !service.getStatus()) { + FXTRACE((60,"last.fm service failed with code %d: %s\n",service.getErrorCode(),service.getErrorMessage().text())); + switch(service.getErrorCode()) { + case LASTFM_ERROR_UNKNOWN : + case LASTFM_ERROR_OFFLINE : + case LASTFM_ERROR_UNAVAILABLE : set_timeout(); break; + case LASTFM_ERROR_BADSESSION : session.clear(); break; + case LASTFM_ERROR_KEY_INVALID : + case LASTFM_ERROR_KEY_SUSPENDED: flags|=FLAG_BANNED; break; + default : flags|=FLAG_BADAUTH; break; + } + } + } + else { + FXString response; + FXString code=response.section('\n',0); + FXTRACE((70,"Now Playing Response: %s\n\n",response.text())); + if (compare(code,"BADSESSION",10)==0) { + session.clear(); + } + } + } + + +void GMAudioScrobbler::create_submit_request(FXString & request) { + FXMutexLock lock(mutex_data); + FXTRACE((60,"GMAudioScrobbler::create_submit_request\n")); + FXint i,s; + FXint ntracks = FXMIN(50,submitqueue.no()); + FXString signature; + + if (mode==SERVICE_LASTFM) { + if (ntracks==1) { + signature=GMStringFormat("album[0]%s" + "api_key"CLIENT_KEY + "artist[0]%s" + "duration[0]%d" + "methodtrack.scrobble" + "sk%s" + "timestamp[0]%u" + "trackNumber[0]%d" + "track[0]%s" + CLIENT_SECRET, + submitqueue[0].album.text(), + submitqueue[0].artist.text(), + submitqueue[0].duration, + session.text(), + submitqueue[0].getTimeStamp(), + submitqueue[0].no, + submitqueue[0].title.text()); + } + else if (ntracks<10) { + + for (i=0;i GMAudioScrobblerTrackList; + +enum { + SERVICE_LASTFM, + SERVICE_LIBREFM, + SERVICE_CUSTOM + }; + + +class GMAudioScrobbler : public FXThread { +private: + FXMutex mutex_task; + FXMutex mutex_data; + FXCondition condition_task; + FXuchar flags; + FXMessageChannel feedback; + FXObject* target; + FXSelector message; + FXbool started; +private: + FXuint mode; + FXString handshake_url; + FXString nowplaying_url; + FXString submit_url; + + + FXString username; + FXString password; + FXString session; + FXString token; +protected: + FXlong timeout; +private: + enum { + TASK_NONE = 0x0, + TASK_LOGIN = 0x1, + TASK_NOWPLAYING = 0x2, + TASK_SUBMIT = 0x4, + TASK_SHUTDOWN = 0x8, + TASK_AUTHENTICATE = 0x10, + }; +private: + enum { + FLAG_NONE = 0, + FLAG_LOGIN_CHANGED = 0x1, + FLAG_BANNED = 0x2, + FLAG_BADAUTH = 0x4, + FLAG_BADTIME = 0x8, + FLAG_SHUTDOWN = 0x10, + FLAG_TIMEOUT = 0x20, + FLAG_NETWORK = 0x40, + FLAG_DISABLED = 0x80, + FLAG_SERVICE = 0x100 + }; +protected: + FXint run(); +protected: + FXbool post_request(const FXString & url,const FXString & request,FXString & response); + FXbool get_request(const FXString & url,FXString & response); +protected: + FXuchar getNextTask(); + FXbool waitForTask(); + void runTask(); + void wakeup(); +protected: + GMAudioScrobblerTrack nowplayingtrack; + GMAudioScrobblerTrackList submitqueue; + FXint nsubmitted; + FXint nfailed; +protected: + void authenticate(); + void handshake(); + void submit(); + void nowplaying(); + void loveban(); + void create_loveban_request(FXString &); + void process_loveban_response(const FXString&); + void create_token_request(FXString &); + void process_token_response(const FXString&); + FXuint create_handshake_request(FXString &); + void process_handshake_response(const FXString&); + void create_nowplaying_request(FXString &); + void process_nowplaying_response(const FXString&); + void create_submit_request(FXString &); + void process_submit_response(const FXString&); + void set_timeout(); + void reset_timeout(); + void set_submit_failed(); + void load_queue(); + void save_queue(); + FXbool can_submit(); +public: + GMAudioScrobbler(FXObject* tgt,FXSelector msg); + FXString getUsername(); + FXbool hasPassword(); + FXbool isBanned(); + FXbool isEnabled(); + FXuint getService(); + + void nowplaying(GMTrack & info); + void loveban(GMTrack & info, FXint loveban); + + void service(FXuint s); + void submit(FXlong timestamp,GMTrack & info); + void login(const FXString & user,const FXString & pass); + void shutdown(); + void nudge(); + void disable(); + void enable(); + + virtual ~GMAudioScrobbler(); + }; + +extern FXbool init_gcrypt(); + + +#endif + + diff --git a/src/GMAutoPtr.h b/src/GMAutoPtr.h new file mode 100644 index 0000000..19536c7 --- /dev/null +++ b/src/GMAutoPtr.h @@ -0,0 +1,69 @@ +/******************************************************************************** +* * +* A u t o m a t i c P o i n t e r * +* * +********************************************************************************* +* Copyright (C) 2007-2011 by Jeroen van der Zijp. All Rights Reserved. * +********************************************************************************* +* This library is free software; you can redistribute it and/or modify * +* it under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 3 of the License, or * +* (at your option) any later version. * +* * +* 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 program. If not, see * +********************************************************************************* +* $Id: FXAutoPtr.h,v 1.11 2007/07/09 16:02:41 fox Exp $ * +********************************************************************************/ +#ifndef GMAUTOPTR_H +#define GMAUTOPTR_H +#if FOXVERSION < FXVERSION(1,7,0) + +namespace FX { + +/// Automatic pointer +template class FXAutoPtr { +private: + TYPE* ptr; +public: + + /// Construct with optional pointer + FXAutoPtr(TYPE* p=NULL):ptr(p){ } + + /// Copy constructor from an automatic pointer with compatible type + template FXAutoPtr(FXAutoPtr& orig):ptr(orig.release()){ } + + /// Assign from pointer + FXAutoPtr& operator=(TYPE *p){ ptr=p; return *this; } + + /// Assign from an automatic pointer with compatible type + template FXAutoPtr& operator=(FXAutoPtr& orig){ reset(orig.release()); return *this; } + + /// Conversion operators + operator TYPE*() const { return ptr; } + + /// Dereference operator + TYPE& operator*() const { return *ptr; } + + /// Follow pointer operator + TYPE* operator->() const { return ptr; } + + /// Release hold on the pointer + TYPE* release(){ TYPE* tmp=ptr; ptr=NULL; return tmp; } + + /// Delete old object, replace by new, if any + void reset(TYPE* p=NULL){ if(p!=ptr){ delete ptr; ptr=p; } } + + /// Destruction deletes pointer + ~FXAutoPtr(){ delete ptr; } + }; + +} + +#endif +#endif diff --git a/src/GMClipboard.cpp b/src/GMClipboard.cpp new file mode 100644 index 0000000..23a0e23 --- /dev/null +++ b/src/GMClipboard.cpp @@ -0,0 +1,123 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#include "gmdefs.h" +#include "GMClipboard.h" + + +FXDEFMAP(GMClipboard) GMClipboardMap[]={ + FXMAPFUNC(SEL_CLIPBOARD_LOST,0,GMClipboard::onClipboardLost), + FXMAPFUNC(SEL_CLIPBOARD_GAINED,0,GMClipboard::onClipboardGained), + FXMAPFUNC(SEL_CLIPBOARD_REQUEST,0,GMClipboard::onClipboardRequest), + }; + +FXIMPLEMENT(GMClipboard,FXShell,GMClipboardMap,ARRAYNUMBER(GMClipboardMap)) + +GMClipboard * GMClipboard::me=NULL; + +FXDragType GMClipboard::kdeclipboard=0; +FXDragType GMClipboard::gnomeclipboard=0; +FXDragType GMClipboard::gnomedragndrop=0; +FXDragType GMClipboard::trackdatabase=0; +FXDragType GMClipboard::selectedtracks=0; +FXDragType GMClipboard::alltracks=0; + +GMClipboard * GMClipboard::instance(){ + return me; + } + +GMClipboard::GMClipboard() { + } + +GMClipboard::GMClipboard(FXApp * app) : FXShell(app,0,0,0,0,0), clipdata(NULL), clipowner(NULL) { + me=this; + } + +void GMClipboard::create(){ + FXShell::create(); + + kdeclipboard = getApp()->registerDragType("application/x-kde-cutselection"); + gnomeclipboard = getApp()->registerDragType("x-special/gnome-copied-files"); + gnomedragndrop = getApp()->registerDragType("x-special/gnome-icon-list"); + trackdatabase = getApp()->registerDragType("application/goggles-music-manager-database"); + selectedtracks = getApp()->registerDragType("application/goggles-dnd-selected-tracks"); + alltracks = getApp()->registerDragType("application/goggles-dnd-all-tracks"); + + if (FXWindow::urilistType==0){ + FXWindow::urilistType=getApp()->registerDragType(FXWindow::urilistTypeName); + } + } + +GMClipboard::~GMClipboard(){ + if (clipdata) delete clipdata; + clipowner=NULL; + } + +bool GMClipboard::doesOverrideRedirect() const { + return true; + } + +FXbool GMClipboard::acquire(FXObject * owner,const FXDragType * types,FXuint num_types,GMClipboardData * data){ + if (acquireClipboard(types,num_types)){ +// fxmessage("acquired clipboard %d\n",hasClipboard()); + clipowner=owner; + clipdata=data; + return true; + } + return false; + } + +FXbool GMClipboard::owned(FXObject * obj){ + if (hasClipboard() && obj==clipowner) return true; + return false; + } + +FXbool GMClipboard::release(){ + return false; + } + + +long GMClipboard::onClipboardLost(FXObject*,FXSelector,void*){ +// fxmessage("lost clipboard\n"); + if (clipdata) delete clipdata; + clipowner=NULL; + return 1; + } + +long GMClipboard::onClipboardGained(FXObject*,FXSelector,void*){ +// fxmessage("gained clipboard\n"); + return 1; + } + + +long GMClipboard::onClipboardRequest(FXObject*,FXSelector,void*ptr){ + FXEvent *event=(FXEvent*)ptr; +// fxmessage("got request: %s\n",getApp()->getDragTypeName(event->target).text()); + if (clipdata && clipdata->request(event->target,this)){ + return 1; + } + return 0; + } + + + + + + + + diff --git a/src/GMClipboard.h b/src/GMClipboard.h new file mode 100644 index 0000000..ba0940a --- /dev/null +++ b/src/GMClipboard.h @@ -0,0 +1,79 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#ifndef GMCLIPBOARD_H +#define GMCLIPBOARD_H + +class GMClipboard; + +class GMClipboardData{ +public: + GMClipboardData(){} + virtual FXbool request(FXDragType dragtype,GMClipboard*)=0; + virtual ~GMClipboardData() {} + }; + + +class GMClipboard : public FXShell { +FXDECLARE(GMClipboard) +private: + static GMClipboard * me; +public: + static FXDragType kdeclipboard; + static FXDragType gnomeclipboard; + static FXDragType gnomedragndrop; + static FXDragType trackdatabase; + static FXDragType selectedtracks; + static FXDragType alltracks; +protected: + GMClipboardData * clipdata; + FXObject * clipowner; +protected: + GMClipboard(); + virtual bool doesOverrideRedirect() const; +private: + GMClipboard(const GMClipboard&); + GMClipboard& operator=(const GMClipboard&); +public: + long onClipboardLost(FXObject*,FXSelector,void*); + long onClipboardGained(FXObject*,FXSelector,void*); + long onClipboardRequest(FXObject*,FXSelector,void*); +public: + static GMClipboard * instance(); +public: + GMClipboard(FXApp * app); + + FXbool acquire(FXObject * owner,const FXDragType * types,FXuint num_types,GMClipboardData * data); + + FXbool owned(FXObject * obj); + + FXbool release(); + + GMClipboardData * getClipData() const { return clipdata; } + + virtual void create(); + + ~GMClipboard(); + }; + +#endif + + + + + diff --git a/src/GMColumnDialog.cpp b/src/GMColumnDialog.cpp new file mode 100644 index 0000000..028ecc9 --- /dev/null +++ b/src/GMColumnDialog.cpp @@ -0,0 +1,283 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2007-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#include "gmdefs.h" +#include "GMList.h" +#include "GMTrackList.h" +#include "GMColumnDialog.h" + +#define ICON_SPACING 4 // Spacing between icon and label +#define SIDE_SPACING 6 // Left or right spacing between items +#define LINE_SPACING 4 // Line spacing between items + + +class FXCheckListItem : public GMListItem { + FXDECLARE(FXCheckListItem) + friend class FXList; +protected: + FXCheckListItem(){} + +//#if FOXVERSION < FXVERSION(1,7,0) +// virtual void draw(const GMList* list,FXDC& dc,FXint x,FXint y,FXint w,FXint h); +//#else + virtual void draw(const GMList* list,FXDC& dc,FXint x,FXint y,FXint w,FXint h) const; +//#endif + virtual FXint hitItem(const FXList* list,FXint x,FXint y) const; +public: + enum { + CHECKED = 32 + }; +public: + /// Construct new item with given text, icon, and user-data + FXCheckListItem(const FXString& text,FXbool check=true,void* ptr=NULL):GMListItem(text,NULL,ptr) {if (check) state|=CHECKED; } + + /// Return width of item as drawn in list + virtual FXint getWidth(const FXList* list) const; + + /// Return height of item as drawn in list + virtual FXint getHeight(const FXList* list) const; + + FXbool checked() const { return (state&CHECKED); } + + void toggle() { if (state&CHECKED) state&=~CHECKED; else state|=CHECKED; } + }; + +FXIMPLEMENT(FXCheckListItem,FXListItem,NULL,0); + +void FXCheckListItem::draw(const GMList* list,FXDC& dc,FXint xx,FXint yy,FXint ww,FXint hh) const { +//#endif + register FXFont *font=list->getFont(); + register FXint ih=0,th=0; + if(icon) ih=icon->getHeight(); + if(!label.empty()) th=font->getFontHeight(); + if(isSelected()) + dc.setForeground(list->getSelBackColor()); + else + dc.setForeground(list->getBackColor()); // FIXME maybe paint background in onPaint? + dc.fillRectangle(xx,yy,ww,hh); + + if(hasFocus()){ + dc.drawFocusRectangle(xx+1,yy+1,ww-2,hh-2); + } + xx+=SIDE_SPACING/2; + + FXint yyy=yy+(hh-10)/2; + + if(!isEnabled()) + dc.setForeground(makeShadowColor(list->getBackColor())); + else + dc.setForeground(list->getBackColor()); + + dc.fillRectangle(xx+1,yyy+1,9,9); + dc.setForeground(list->getApp()->getBorderColor()); + +// dc.setForeground(shadowColor); + dc.drawRectangle(xx,yyy,10,10); + + +// dc.drawRectangle(xx,yyy,9,9); + + dc.setForeground(list->getTextColor()); + // Draw the check + if(state&CHECKED){ + FXSegment seg[6]; + seg[0].x1=2+xx; seg[0].y1=4+yyy; seg[0].x2=4+xx; seg[0].y2=6+yyy; + seg[1].x1=2+xx; seg[1].y1=5+yyy; seg[1].x2=4+xx; seg[1].y2=7+yyy; + seg[2].x1=2+xx; seg[2].y1=6+yyy; seg[2].x2=4+xx; seg[2].y2=8+yyy; + seg[3].x1=4+xx; seg[3].y1=6+yyy; seg[3].x2=8+xx; seg[3].y2=2+yyy; + seg[4].x1=4+xx; seg[4].y1=7+yyy; seg[4].x2=8+xx; seg[4].y2=3+yyy; + seg[5].x1=4+xx; seg[5].y1=8+yyy; seg[5].x2=8+xx; seg[5].y2=4+yyy; + dc.drawLineSegments(seg,6); + } + + xx+=ICON_SPACING+9; + if(icon){ + dc.drawIcon(icon,xx,yy+(hh-ih)/2); + xx+=ICON_SPACING+icon->getWidth(); + } + + if(!label.empty()){ + dc.setFont(font); + if(!isEnabled()) + dc.setForeground(makeShadowColor(list->getBackColor())); + else if(isSelected()) + dc.setForeground(list->getSelTextColor()); + else + dc.setForeground(list->getTextColor()); + dc.drawText(xx,yy+(hh-th)/2+font->getFontAscent(),label); + } + } + + +// See if item got hit, and where: 0 is outside, 1 is icon, 2 is text +FXint FXCheckListItem::hitItem(const FXList* list,FXint xx,FXint yy) const { + register FXFont *font=list->getFont(); + register FXint iw=0,ih=0,tw=0,th=0,cx,ix,iy,tx,ty,h; + if(icon){ + iw=icon->getWidth(); + ih=icon->getHeight(); + } + if(!label.empty()){ + tw=4+font->getTextWidth(label.text(),label.length()); + th=4+font->getFontHeight(); + } + h=LINE_SPACING+FXMAX3(th,ih,9); + cx=SIDE_SPACING/2; + ix=cx+9+ICON_SPACING; + tx=cx+9+ICON_SPACING; + if(iw) tx+=iw+ICON_SPACING; + iy=(h-ih)/2; + ty=(h-th)/2; + + if (cx<=xx && xxgetFont(); + register FXint w=9; + if(icon){ + w=icon->getWidth()+ICON_SPACING; + } + if(!label.empty()){ + if(w) w+=ICON_SPACING; + w+=font->getTextWidth(label.text(),label.length()); + } + return SIDE_SPACING+w; + } + + +// Get height of item +FXint FXCheckListItem::getHeight(const FXList* list) const { + register FXFont *font=list->getFont(); + register FXint th=0,ih=0; + if(icon){ + ih=icon->getHeight(); + } + if(!label.empty()){ + th=font->getFontHeight(); + } + return LINE_SPACING+FXMAX3(th,ih,9); + } + + +FXDEFMAP(GMColumnDialog) GMColumnDialogMap[]={ + FXMAPFUNC(SEL_UPDATE,GMColumnDialog::ID_MOVE_UP,GMColumnDialog::onUpdMoveUp), + FXMAPFUNC(SEL_UPDATE,GMColumnDialog::ID_MOVE_DOWN,GMColumnDialog::onUpdMoveDown), + FXMAPFUNC(SEL_COMMAND,GMColumnDialog::ID_MOVE_UP,GMColumnDialog::onCmdMoveUp), + FXMAPFUNC(SEL_COMMAND,GMColumnDialog::ID_MOVE_DOWN,GMColumnDialog::onCmdMoveDown), + FXMAPFUNC(SEL_LEFTBUTTONPRESS,GMColumnDialog::ID_LIST,GMColumnDialog::onListLeftBtnPress), + + }; + +FXIMPLEMENT(GMColumnDialog,FXDialogBox,GMColumnDialogMap,ARRAYNUMBER(GMColumnDialogMap)); + +GMColumnDialog::GMColumnDialog(){ + } + +GMColumnDialog::GMColumnDialog(FXWindow *window,GMColumnList & cols) : FXDialogBox(window,FXString::null,DECOR_ALL,0,0,0,0,3,3,3,3) { + + setTitle(tr("Configure Columns")); + + FXHorizontalFrame * buttonframe = new FXHorizontalFrame(this,LAYOUT_FILL_X|LAYOUT_SIDE_BOTTOM,0,0,0,0,0,0,0,0); + new GMButton(buttonframe,tr("&Accept"),NULL,this,FXDialogBox::ID_ACCEPT,BUTTON_INITIAL|BUTTON_DEFAULT|LAYOUT_RIGHT|FRAME_RAISED|FRAME_THICK,0,0,0,0,20,20); + new GMButton(buttonframe,tr("&Cancel"),NULL,this,FXDialogBox::ID_CANCEL,BUTTON_DEFAULT|LAYOUT_RIGHT|FRAME_RAISED|FRAME_THICK,0,0,0,0,20,20); + + new FXSeparator(this,SEPARATOR_GROOVE|LAYOUT_FILL_X|LAYOUT_SIDE_BOTTOM); + + new FXLabel(this,tr("Choose the order of information to appear\nin the track list."),NULL,LAYOUT_SIDE_TOP|JUSTIFY_LEFT); + + + FXHorizontalFrame * main = new FXHorizontalFrame(this,LAYOUT_FILL_X|LAYOUT_FILL_Y,0,0,0,0,0,0,0,0); + + FXVerticalFrame * control = new FXVerticalFrame(main,LAYOUT_FILL_Y|LAYOUT_RIGHT|PACK_UNIFORM_WIDTH,0,0,0,0,0,0,0); + new GMButton(control,tr("Move Up"),NULL,this,ID_MOVE_UP); + new GMButton(control,tr("Move Down"),NULL,this,ID_MOVE_DOWN); + + GMScrollFrame * sunken = new GMScrollFrame(main); + list = new GMList(sunken,this,ID_LIST,LAYOUT_FILL_X|LAYOUT_FILL_Y|LIST_BROWSESELECT); + + FXbool insert=false; + for (FXint i=0;igetNumItems();j++) { + if (cols[i].index < ((GMColumn*)list->getItemData(j))->index) { + list->insertItem(j,new FXCheckListItem(cols[i].name,cols[i].show,&cols[i])); + insert=true; + break; + } + } + if (!insert) list->appendItem(new FXCheckListItem(cols[i].name,cols[i].show,&cols[i])); + } + list->setNumVisible(10); + } + +void GMColumnDialog::saveIndex() { + for (FXint i=0;igetNumItems();i++){ + GMColumn * c = (GMColumn*)list->getItemData(i); + c->index = i; + c->show = ((FXCheckListItem*)list->getItem(i))->checked(); + } + } + +long GMColumnDialog::onListLeftBtnPress(FXObject*,FXSelector,void*ptr){ + FXEvent* event=(FXEvent*)ptr; + FXint index,code; + index=list->getItemAt(event->win_x,event->win_y); + if (index>=0) { + code=list->hitItem(index,event->win_x,event->win_y); + if (code==3) { ((FXCheckListItem*)list->getItem(index))->toggle(); list->updateItem(index); } + } + return 0; + } + + +long GMColumnDialog::onCmdMoveUp(FXObject*,FXSelector,void*){ + list->moveItem(list->getCurrentItem()-1,list->getCurrentItem()); + return 1; + } + +long GMColumnDialog::onUpdMoveUp(FXObject*sender,FXSelector,void*){ + if (list->getCurrentItem()>0) + sender->handle(this,FXSEL(SEL_COMMAND,ID_ENABLE),NULL); + else + sender->handle(this,FXSEL(SEL_COMMAND,ID_DISABLE),NULL); + return 1; + } + +long GMColumnDialog::onCmdMoveDown(FXObject*,FXSelector,void*){ + list->moveItem(list->getCurrentItem()+1,list->getCurrentItem()); + return 1; + } + +long GMColumnDialog::onUpdMoveDown(FXObject*sender,FXSelector,void*){ + if (list->getCurrentItem()getNumItems()-1) + sender->handle(this,FXSEL(SEL_COMMAND,ID_ENABLE),NULL); + else + sender->handle(this,FXSEL(SEL_COMMAND,ID_DISABLE),NULL); + return 1; + } diff --git a/src/GMColumnDialog.h b/src/GMColumnDialog.h new file mode 100644 index 0000000..dc58db2 --- /dev/null +++ b/src/GMColumnDialog.h @@ -0,0 +1,50 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2007-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#ifndef GMCOLUMNDIALOG_H +#define GMCOLUMNDIALOG_H + +class GMColumnDialog : public FXDialogBox { +FXDECLARE(GMColumnDialog) +protected: + GMList * list; +protected: + GMColumnDialog(); +private: + GMColumnDialog(const GMColumnDialog&); + GMColumnDialog& operator=(const GMColumnDialog&); +public: + enum { + ID_MOVE_UP = FXDialogBox::ID_LAST, + ID_MOVE_DOWN, + ID_LIST, + }; +public: + long onCmdMoveUp(FXObject*,FXSelector,void*); + long onUpdMoveUp(FXObject*,FXSelector,void*); + long onCmdMoveDown(FXObject*,FXSelector,void*); + long onUpdMoveDown(FXObject*,FXSelector,void*); + long onListLeftBtnPress(FXObject*,FXSelector,void*); +public: + /// Construct button with text and icon + GMColumnDialog(FXWindow* p,GMColumnList & cols); + + void saveIndex(); + }; + +#endif diff --git a/src/GMCoverCache.cpp b/src/GMCoverCache.cpp new file mode 100644 index 0000000..6a9db6d --- /dev/null +++ b/src/GMCoverCache.cpp @@ -0,0 +1,425 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2010 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#include "gmdefs.h" +#include "gmutils.h" +#include "GMTaskManager.h" +//#include "GMTrack.h" +#include "GMApp.h" +//#include "GMCover.h" +#include "GMTag.h" +#include "GMList.h" +#include "GMTrackList.h" +#include "GMSource.h" +#include "GMTrackView.h" +#include "GMTrackDatabase.h" +#include "GMPlayerManager.h" +#include "GMIconTheme.h" +#include "GMCoverCache.h" + + +void FXIntMap::save(FXStream & store) const { + store << no(); + for (FXuint i=0;i> no; + for (FXint i=0;i> key; + store >> value; + insert(key,value); + } + } + + + +class GMCompressedImage { +public: + FXuchar * buffer; + FXuval len; +public: + GMCompressedImage() : buffer(NULL),len(0){} + GMCompressedImage(FXuchar * b,FXuval l) : len(l) { + allocElms(buffer,len); + memcpy(buffer,b,len); + } + + void make(FXImage * img) { + FXint ww,hh,dd; + FXColor * data=NULL; + FXMemoryStream ms(FXStreamLoad,buffer,len,false); + fxloadJPG(ms,data,ww,hh,dd); + ms.close(); + FXASSERT(ww==128); + FXASSERT(hh==128); + + img->setData(data,IMAGE_OWNED); + img->render(); + } + + ~GMCompressedImage() { + freeElms(buffer); + } + + void load(FXStream & store) { + store >> len; + allocElms(buffer,len); + store.load(buffer,len); + } + + void save(FXStream & store) const { + store << len; + store.save(buffer,len); + } + + }; + + + + +class CoverLoader: public GMTask { +protected: + FXuchar * compress_buffer; + FXuval compress_buffer_length; + FXuval total_c; + FXuval total_u; +public: + GMAlbumPathList albums; + GMCoverCache cache; +protected: + GMCompressedImage* compress(FXColor*,FXint,FXint); + GMCompressedImage* compress(FXImage*); + GMCompressedImage* compress_fit(FXImage*); + FXint run(); +public: + CoverLoader(GMAlbumPathList & l,FXint sz,FXObject *tgt=NULL,FXSelector sel=0); + ~CoverLoader(); + }; + + + + +CoverLoader::CoverLoader(GMAlbumPathList & list,FXint sz,FXObject * tgt,FXSelector sel) + : GMTask(tgt,sel), cache(sz) { + albums.adopt(list); + compress_buffer=NULL; + compress_buffer_length=0; + } + +CoverLoader::~CoverLoader(){ + freeElms(compress_buffer); + } + + +FXint CoverLoader::run() { + FXdouble fraction; + GMCover * cover=NULL; + for (FXint i=0;isetStatus(FXString::value("Loading Covers %d%%",(FXint)(100.0*fraction))); + cover = GMCover::fromTag(albums[i].path); + if (cover==NULL) + cover = GMCover::fromPath(FXPath::directory(albums[i].path)); + if (cover) { + cache.insertCover(albums[i].id,compress(GMCover::toImage(cover,cache.getCoverSize(),1))); + } + else { + cache.insertCover(albums[i].id,NULL); + } + } + if (processing) cache.save(); + return 0; + } + + +GMCompressedImage * CoverLoader::compress_fit(FXImage*img) { + FXint size = cache.getCoverSize(); + + FXColor * pix=NULL; + allocElms(pix,size*size); + + + memset(pix,255,4*size*size); + + FXuchar * dst = (FXuchar*)pix; + FXuchar * src = (FXuchar*)img->getData(); + FXint sw=img->getWidth()*4; + FXint sh=img->getHeight(); + + + if (img->getHeight()getHeight())>>1); + + if (img->getWidth()getWidth())>>1); + + do { + memcpy(dst,src,sw); + dst+=size*4; + src+=sw; + } + while(--sh); + + GMCompressedImage * c = compress(pix,size,size); + freeElms(pix); + return c; + } + +GMCompressedImage * CoverLoader::compress(FXColor*pix,FXint width,FXint height){ + FXMemoryStream ms(FXStreamSave,compress_buffer,compress_buffer_length,true); + fxsaveJPG(ms,pix,width,height,75); + ms.takeBuffer(compress_buffer,compress_buffer_length); + ms.close(); + return new GMCompressedImage(compress_buffer,ms.position()); + } + + +GMCompressedImage * CoverLoader::compress(FXImage*img) { + GMCompressedImage * cimage = NULL; + if (img->getWidth()!=cache.getCoverSize() || img->getHeight()!=cache.getCoverSize()) { + cimage = compress_fit(img); + } + else { + cimage = compress(img->getData(),cache.getCoverSize(),cache.getCoverSize()); + } + delete img; + return cimage; + } + + + + + + + + + + + + + + +FXDEFMAP(GMCoverCache) GMCoverCacheMap[]={ + FXMAPFUNC(SEL_TASK_COMPLETED,GMCoverCache::ID_COVER_LOADER,GMCoverCache::onCmdCoversLoaded), + }; + +FXIMPLEMENT(GMCoverCache,FXObject,GMCoverCacheMap,ARRAYNUMBER(GMCoverCacheMap)); + + + + +GMCoverCache::GMCoverCache(FXint size) : basesize(size),initialized(false) { + } + + +GMCoverCache::~GMCoverCache() { + for (FXint i=0;i0) { + FXImage * image = getCoverImage(id); + dc.drawImage(image,x,y); + return; + } + else { + FXIcon * ic = GMIconTheme::instance()->icon_nocover; + if (ic->getHeight()getHeight())>>1; + if (ic->getWidth()getWidth())>>1; + dc.drawIcon(ic,x,y); + } + } + + +void GMCoverCache::insertCover(FXint id,GMCompressedImage * image) { + if (image) { + covers.append(image); + map.insert(id,covers.no()); + } + } + +void GMCoverCache::markCover(FXint id) { + for (FXint i=0,index;igetUserData(); + if (index==-id) { + buffers[i]->setUserData((void*)(FXival)id); + break; + } + } + } + + +void GMCoverCache::reset() { + for (FXint i=0,index;igetUserData(); + buffers[i]->setUserData((void*)(FXival)-index); + } + } + +FXImage* GMCoverCache::getCoverImage(FXint id) { + FXint i,index; + FXImage * image=NULL; + + /// existing + for (i=0;igetUserData(); + if (index==id) return buffers[i]; + } + + /// find empty + for (i=0;igetUserData(); + if (index<0) { + buffers[i]->setUserData((void*)(FXival)id); + image=buffers[i]; + break; + } + } + + /// Create new one + if (image==NULL) { + image = new FXImage(FXApp::instance(),NULL,0,basesize,basesize); + image->setUserData((void*)(FXival)id); + image->create(); + buffers.append(image); + } + + index = map.find(id); + covers[index-1]->make(image); + return image; + } + + +void GMCoverCache::init(GMTrackDatabase * database){ + if (!initialized && !load()) { + refresh(database); + } + } + +void GMCoverCache::refresh(GMTrackDatabase * database){ + + /// Remove the cache file. + if (FXStat::exists(getCacheFile())) + FXFile::remove(getCacheFile()); + + /// Only scan for covers if needed + if (initialized) { + GMAlbumPathList list; + database->listAlbumPaths(list); + if (list.no()){ + CoverLoader * task = new CoverLoader(list,basesize,this,ID_COVER_LOADER); + GMPlayerManager::instance()->runTask(task); + } + } + } + +#define COVERTHUMBS_CACHE_FILE_VERSION 20110920 + +void GMCoverCache::save() const { + const FXuint version=COVERTHUMBS_CACHE_FILE_VERSION; + FXFileStream store; + if (store.open(getCacheFile(),FXStreamSave)){ + store << version; + store << basesize; + map.save(store); + store << (FXint)covers.no(); + for (FXint i=0;isave(store); + } + } + } + +FXbool GMCoverCache::load() { + GM_TICKS_START(); + initialized=true; + FXFileStream store; + if (store.open(getCacheFile(),FXStreamLoad)) { + FXint no,size; + FXuint version; + + store >> version; + if (version!=COVERTHUMBS_CACHE_FILE_VERSION) + return false; + + store >> size; + if (basesize!=size) + return false; + + map.load(store); + store >> no; + for (FXint i=0;iload(store); + covers.append(cover); + } + return true; + } + GM_TICKS_END(); + return false; + } + + +long GMCoverCache::onCmdCoversLoaded(FXObject*,FXSelector,void*ptr){ + CoverLoader * task = reinterpret_cast(*((void**)ptr)); + adopt(task->cache); + delete task; + GMPlayerManager::instance()->getTrackView()->redrawAlbumList(); + return 1; + } + + diff --git a/src/GMCoverCache.h b/src/GMCoverCache.h new file mode 100644 index 0000000..19923db --- /dev/null +++ b/src/GMCoverCache.h @@ -0,0 +1,82 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2010 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#ifndef GMCOVERTHUMBS_H +#define GMCOVERTHUMBS_H + +class GMImageMap; +class GMCompressedImage; + +class FXIntMap : public FXHash { +public: + FXint insert(FXint name,FXint value) { return (FXint)(FXival)FXHash::insert((void*)(FXival)name,(void*)(FXival)value); } + FXint replace(FXint name,FXint value) { return (FXint)(FXival)FXHash::replace((void*)(FXival)name,(void*)(FXival)value); } + FXint remove(FXint name) { return (FXint)(FXival)FXHash::remove((void*)(FXival)name); } + FXint find(FXint name) const { return (FXint)(FXival)FXHash::find((void*)(FXival)name); } + FXint key(FXuint pos) const { return (FXint)(FXival)table[pos].name; } + FXint value(FXuint pos) const { return (FXint)(FXival)table[pos].data; } + void load(FXStream & store); + void save(FXStream & store) const; + }; + + + +class GMCoverCache : FXObject { +FXDECLARE(GMCoverCache) +protected: + FXPtrListOf covers; + FXPtrListOf buffers; + FXIntMap map; + FXint basesize; + FXbool initialized; +protected: + FXImage * getCoverImage(FXint id); + void adopt(GMCoverCache &); + FXbool load(); + FXString getCacheFile() const; +public: + enum { + ID_COVER_LOADER = 1 + }; +public: + long onCmdCoversLoaded(FXObject*,FXSelector,void*ptr); +public: + GMCoverCache(FXint size=128); + + void init(GMTrackDatabase*); + + void refresh(GMTrackDatabase*); + + void drawCover(FXint id,FXDC & dc,FXint x,FXint y); + + void markCover(FXint id); + + void reset(); + + void clear(); + + FXint getCoverSize() const { return basesize; } + + void insertCover(FXint id,GMCompressedImage*); + + void save() const; + + ~GMCoverCache(); + }; + +#endif diff --git a/src/GMDBus.cpp b/src/GMDBus.cpp new file mode 100644 index 0000000..28f57b6 --- /dev/null +++ b/src/GMDBus.cpp @@ -0,0 +1,934 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2007-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#include "gmdefs.h" +#include "GMDBus.h" +#include "FXThread.h" + +#ifndef DBUS_MAJOR_VERSION +#define DBUS_MAJOR_VERSION 1 +#endif + +#ifndef DBUS_MINOR_VERSION +#define DBUS_MINOR_VERSION 0 +#endif + +#ifndef DBUS_MICRO_VERSION +#define DBUS_MICRO_VERSION 0 +#endif + +#define DBUSVERSION ((DBUS_MICRO_VERSION) + (DBUS_MINOR_VERSION*1000) + (DBUS_MAJOR_VERSION*100000)) +#define MKDBUSVERSION(major,minor,release) ((release)+(minor*1000)+(major*100000)) + + +/*******************************************************************************************************/ +/* GLOBAL INIT */ +/*******************************************************************************************************/ + +class GMDBusTimeout; + +class GMDBusGlobal { +private: + FXMutex mutex; + FXHash tm; + FXHash connections; +#if FOXVERSION < FXVERSION(1,7,0) + FXHash watches; +#endif +public: + GMDBusGlobal() { + dbus_threads_init_default(); + } + + GMDBus * find(DBusConnection* dc) { + FXMutexLock lock(mutex); + return reinterpret_cast(connections.find(dc)); + } + + void setuphooks() { +#if FOXVERSION < FXVERSION(1,7,0) + for (FXint i=0;isetup_event_loop(); + } + } + } + + void insert(DBusConnection * dc,GMDBus * fxdc) { + FXMutexLock lock(mutex); + connections.insert(dc,fxdc); + } + + void remove(DBusConnection * dc) { + FXMutexLock lock(mutex); + connections.remove(dc); + } + +#if FOXVERSION < FXVERSION(1,7,0) + DBusWatch * find(FXint fd) { + FXMutexLock lock(mutex); + return (DBusWatch*)watches.find((void*)(FXival)fd); + } + + void insert(FXint fd,DBusWatch * watch){ + FXMutexLock lock(mutex); + watches.insert((void*)(FXival)fd,watch); + } + + void remove(FXint fd){ + FXMutexLock lock(mutex); + watches.remove((void*)(FXival)fd); + } +#endif + + GMDBusTimeout * find(DBusTimeout*t) { + return reinterpret_cast(tm.find(t)); + } + + void insert(DBusTimeout*t,GMDBusTimeout*f) { + tm.insert(t,f); + } + + void remove(DBusTimeout*t) { + tm.remove(t); + } + }; + +static GMDBusGlobal fxdbus; + +class GMDBusTimeout : public FXObject { +FXDECLARE(GMDBusTimeout); +protected: + DBusTimeout* timeout; + FXuchar flags; +protected: + GMDBusTimeout(); +private: + GMDBusTimeout(const GMDBusTimeout*); + GMDBusTimeout& operator=(const GMDBusTimeout&); +protected: + enum { + FLAG_CALLBACK = 0x1, + FLAG_REMOVED = 0x2 + }; +public: + enum { + ID_TIMEOUT = 1 + }; +public: + long onTimeout(FXObject*,FXSelector,void*); +public: + GMDBusTimeout(DBusTimeout *t); + void add(); + void remove(); + ~GMDBusTimeout(); + }; + +FXDEFMAP(GMDBusTimeout) GMDBusTimeoutMap[]={ + FXMAPFUNC(SEL_TIMEOUT,GMDBusTimeout::ID_TIMEOUT,GMDBusTimeout::onTimeout), + }; +FXIMPLEMENT(GMDBusTimeout,FXObject,GMDBusTimeoutMap,ARRAYNUMBER(GMDBusTimeoutMap)); + + + +GMDBusTimeout::GMDBusTimeout() { + } + +GMDBusTimeout::GMDBusTimeout(DBusTimeout *t) : timeout(t),flags(0) { + fxdbus.insert(timeout,this); + } + +GMDBusTimeout::~GMDBusTimeout() { + fxdbus.remove(timeout); + } + +void GMDBusTimeout::add() { + flags&=~FLAG_REMOVED; + FXApp::instance()->addTimeout(this,GMDBusTimeout::ID_TIMEOUT,TIME_MSEC(dbus_timeout_get_interval(timeout))); + } + +void GMDBusTimeout::remove() { + if (flags&FLAG_CALLBACK) { + flags|=FLAG_REMOVED; + return; + } + FXApp::instance()->removeTimeout(this,GMDBusTimeout::ID_TIMEOUT); + delete this; + } + + +static dbus_bool_t fxdbus_addtimeout(DBusTimeout *timeout,void */*ptr*/) { +// fxmessage("fxdbus_addtimeout %p\n",timeout); + GMDBusTimeout * tm = fxdbus.find(timeout); + if (tm==NULL) + tm = new GMDBusTimeout(timeout); + + tm->add(); + return true; + } + + +static void fxdbus_removetimeout(DBusTimeout *timeout,void * /*data*/) { +// fxmessage("fxdbus_removetimeout %p\n",timeout); + GMDBusTimeout * tm = fxdbus.find(timeout); + if (tm) tm->remove(); + } + +static void fxdbus_toggletimeout(DBusTimeout *timeout,void* data) { +// fxmessage("fxdbus_toggletimeout %p\n",timeout); + if (dbus_timeout_get_enabled(timeout) && dbus_timeout_get_interval(timeout)>0) + fxdbus_addtimeout(timeout,data); + else + fxdbus_removetimeout(timeout,data); + } + +long GMDBusTimeout::onTimeout(FXObject*,FXSelector,void*){ +// fxmessage("onTimeout() %p {\n",timeout); + flags|=FLAG_CALLBACK; + dbus_timeout_handle(timeout); + if (flags&FLAG_REMOVED) { + delete this; + fxmessage("}\n"); + return 1; + } + else { + if (dbus_timeout_get_enabled(timeout) && dbus_timeout_get_interval(timeout)>0 && !FXApp::instance()->hasTimeout(this,ID_TIMEOUT)){ + FXApp::instance()->addTimeout(this,GMDBusTimeout::ID_TIMEOUT,TIME_MSEC(dbus_timeout_get_interval(timeout))); + } + } + flags&=~FLAG_CALLBACK; +// fxmessage("}\n"); + return 1; + } + + + + + + + + + + + +/*******************************************************************************************************/ +/* Call Backs */ +/*******************************************************************************************************/ + +static dbus_bool_t fxdbus_addwatch(DBusWatch *watch,void * data) { + FXTRACE((35,"fxdbus_addwatch()\n")); + GMDBus * dc = reinterpret_cast(data); + FXuint mode = INPUT_EXCEPT; + FXuint flags = dbus_watch_get_flags(watch); + + /// If it's not enabled, we're not going to add it + if (!dbus_watch_get_enabled(watch)) return true; + + if (flags&DBUS_WATCH_READABLE) + mode|=INPUT_READ; + if (flags&DBUS_WATCH_WRITABLE) + mode|=INPUT_WRITE; + +#if DBUSVERSION == MKDBUSVERSION(1,1,20) || DBUSVERSION >= MKDBUSVERSION(1,2,0) +#if FOXVERSION < FXVERSION(1,7,0) + fxdbus.insert(dbus_watch_get_unix_fd(watch),watch); + return FXApp::instance()->addInput((FXInputHandle)dbus_watch_get_unix_fd(watch),mode,dc,GMDBus::ID_HANDLE); +#else + return FXApp::instance()->addInput(dc,GMDBus::ID_HANDLE,(FXInputHandle)dbus_watch_get_unix_fd(watch),mode,watch); +#endif +#else +#if FOXVERSION < FXVERSION(1,7,0) + fxdbus.insert(dbus_watch_get_fd(watch),watch); + return FXApp::instance()->addInput((FXInputHandle)dbus_watch_get_fd(watch),mode,dc,GMDBus::ID_HANDLE); +#else + return FXApp::instance()->addInput(dc,GMDBus::ID_HANDLE,(FXInputHandle)dbus_watch_get_fd(watch),mode,watch); +#endif +#endif + + } + +static void fxdbus_removewatch(DBusWatch *watch,void *) { + FXTRACE((35,"fxdbus_removewatch()\n")); +/* + FXuint mode=INPUT_EXCEPT; + unsigned int flags = dbus_watch_get_flags(watch); + if (flags&DBUS_WATCH_READABLE) + mode|=INPUT_READ; + if (flags&DBUS_WATCH_WRITABLE) { + mode|=INPUT_WRITE; + return; + } +*/ + FXuint mode=INPUT_READ|INPUT_WRITE|INPUT_EXCEPT; +#if DBUSVERSION == MKDBUSVERSION(1,1,20) || DBUSVERSION >= MKDBUSVERSION(1,2,0) +#if FOXVERSION < FXVERSION(1,7,0) + fxdbus.remove(dbus_watch_get_unix_fd(watch)); +#endif + FXApp::instance()->removeInput(dbus_watch_get_unix_fd(watch),mode); +#else +#if FOXVERSION < FXVERSION(1,7,0) + fxdbus.remove(dbus_watch_get_fd(watch)); +#endif + FXApp::instance()->removeInput(dbus_watch_get_fd(watch),mode); +#endif + + } + +static void fxdbus_togglewatch(DBusWatch *watch,void* data) { + FXTRACE((35,"fxdbus_togglewatch()\n")); + if (dbus_watch_get_enabled(watch)) + fxdbus_addwatch(watch,data); + else + fxdbus_removewatch(watch,data); + } + +static void fxdbus_wakeup(void *){ + /// To Do + } + + + +/*******************************************************************************************************/ +/* PUBLIC API */ +/*******************************************************************************************************/ + +FXDEFMAP(GMDBus) GMDBusMap[]={ + FXMAPFUNC(SEL_IO_READ,GMDBus::ID_HANDLE,GMDBus::onHandleRead), + FXMAPFUNC(SEL_IO_WRITE,GMDBus::ID_HANDLE,GMDBus::onHandleWrite), + FXMAPFUNC(SEL_IO_EXCEPT,GMDBus::ID_HANDLE,GMDBus::onHandleExcept), + FXMAPFUNC(SEL_CHORE,GMDBus::ID_DISPATCH,GMDBus::onDispatch) + }; + +FXIMPLEMENT(GMDBus,FXObject,GMDBusMap,ARRAYNUMBER(GMDBusMap)); + +GMDBus::GMDBus() : dc(NULL) { + } + +GMDBus::~GMDBus(){ + if (dc) { + fxdbus.remove(dc); + dbus_connection_unref(dc); + dc=NULL; + FXApp::instance()->removeChore(this,ID_DISPATCH); + } + } + + +GMDBus * GMDBus::find(DBusConnection * dc) { + return fxdbus.find(dc); + } + +void GMDBus::initEventLoop() { + fxdbus.setuphooks(); + } + + +FXbool GMDBus::open(DBusBusType bustype/*=DBUS_BUS_SESSION*/){ + FXASSERT(dc==NULL); + dc = dbus_bus_get(bustype,NULL); + if (dc==NULL) return false; + if (fxdbus.find(dc)) { + dbus_connection_unref(dc); + dc=NULL; + return false; + } + fxdbus.insert(dc,this); + dbus_connection_set_exit_on_disconnect(dc,false); + return true; + } + + +FXbool GMDBus::connected() const { + if (dc) + return dbus_connection_get_is_connected(dc); + else + return false; + } + +FXbool GMDBus::authenticated() const { + if (dc) + return dbus_connection_get_is_authenticated(dc); + else + return false; + } + +void GMDBus::flush() { + if (dc) dbus_connection_flush(dc); + } + + +FXString GMDBus::dbusversion() { +#if (DBUSVERSION == MKDBUSVERSION(1,1,20)) || DBUSVERSION >= MKDBUSVERSION(1,2,0) + int major,minor,micro; + dbus_get_version(&major,&minor,µ); + return GMStringFormat("%d.%d.%d",major,minor,micro); +#else + return FXString("1.0.x"); +#endif + } + +struct CallTarget{ + FXObject * target; + FXSelector message; + }; + +static void fxdbus_pendingcallfree(void *memory){ + //fxmessage("fxdbuspendingcallfree\n"); + if (memory){ + CallTarget * call = (CallTarget*)memory; + delete call; + } + } + +static void fxdbus_pendingcallnotify(DBusPendingCall*pending,void*data){ + DBusMessage * msg = dbus_pending_call_steal_reply(pending); + if (msg) { + CallTarget * call = (CallTarget*)data; + if (call && call->target && call->message) { + call->target->handle(NULL,FXSEL(SEL_COMMAND,call->message),msg); + } + dbus_message_unref(msg); + } + } + + +FXbool GMDBus::sendWithReply(DBusMessage * msg,FXint timeout,FXObject*tgt,FXSelector sel){ + FXASSERT(msg); + DBusPendingCall * pending = NULL; + if (dbus_connection_send_with_reply(dc,msg,&pending,timeout)) { + if (pending) { + CallTarget * call = new CallTarget; + call->target=tgt; + call->message=sel; + dbus_pending_call_set_notify(pending,fxdbus_pendingcallnotify,(void*)call,fxdbus_pendingcallfree); + dbus_pending_call_unref(pending); + } + dbus_message_unref(msg); + return true; + } + return false; + } + + +void GMDBus::send(DBusMessage * msg){ + FXuint serial; + dbus_connection_send(dc,msg,&serial); + dbus_message_unref(msg); + } + +void GMDBus::send(DBusMessage * msg,FXuint & serial){ + dbus_connection_send(dc,msg,&serial); + dbus_message_unref(msg); + } + + + +/*******************************************************************************************************/ +/* MESSAGE HANDLERS */ +/*******************************************************************************************************/ + +long GMDBus::onHandleRead(FXObject*,FXSelector,void*ptr){ +#if FOXVERSION < FXVERSION(1,7,0) + DBusWatch * watch = fxdbus.find((FXint)(FXival)ptr); +#else + DBusWatch * watch = reinterpret_cast(ptr); +#endif + dbus_watch_handle(watch,DBUS_WATCH_READABLE); + FXApp::instance()->addChore(this,ID_DISPATCH); + return 0; + } + +long GMDBus::onHandleWrite(FXObject*,FXSelector,void*ptr){ +#if FOXVERSION < FXVERSION(1,7,0) + DBusWatch * watch = fxdbus.find((FXint)(FXival)ptr); +#else + DBusWatch * watch = reinterpret_cast(ptr); +#endif + dbus_watch_handle(watch,DBUS_WATCH_WRITABLE); + return 0; + } + +long GMDBus::onHandleExcept(FXObject*,FXSelector,void*ptr){ +#if FOXVERSION < FXVERSION(1,7,0) + DBusWatch * watch = fxdbus.find((FXint)(FXival)ptr); +#else + DBusWatch * watch = reinterpret_cast(ptr); +#endif + dbus_watch_handle(watch,DBUS_WATCH_ERROR|DBUS_WATCH_HANGUP); + return 0; + } + +long GMDBus::onDispatch(FXObject*,FXSelector,void*/*ptr*/){ + if (dbus_connection_dispatch((DBusConnection*)dc)==DBUS_DISPATCH_DATA_REMAINS) { + FXApp::instance()->addChore(this,ID_DISPATCH); + } + return 0; + } + + +/*******************************************************************************************************/ +/* PROTECTED API */ +/*******************************************************************************************************/ + +void GMDBus::setup_event_loop() { + FXASSERT(dc); + + dbus_connection_set_watch_functions(dc, + fxdbus_addwatch, + fxdbus_removewatch, + fxdbus_togglewatch, + this,NULL); + + dbus_connection_set_timeout_functions(dc, + fxdbus_addtimeout, + fxdbus_removetimeout, + fxdbus_toggletimeout, + this,NULL); + + dbus_connection_set_wakeup_main_function(dc,fxdbus_wakeup,NULL,NULL); + } + + + + + +static DBusHandlerResult dbus_proxy_filter(DBusConnection *,DBusMessage * msg,void * ptr){ + GMDBusProxy * proxy = reinterpret_cast(ptr); + FXASSERT(proxy); + FXuint type = dbus_message_get_type(msg); + + if (type==DBUS_MESSAGE_TYPE_METHOD_CALL) { + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + else if (type==DBUS_MESSAGE_TYPE_METHOD_RETURN || type==DBUS_MESSAGE_TYPE_ERROR) { + if (proxy->matchSerial(msg)) + return DBUS_HANDLER_RESULT_HANDLED; + else + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + else { /* SIGNALS */ + DEBUG_DBUS_MESSAGE(msg); + + if (dbus_message_has_path(msg,DBUS_PATH_DBUS)) { + if (dbus_message_is_signal(msg,DBUS_INTERFACE_DBUS,"NameOwnerChanged")) { + const FXchar * dbus_name=NULL; + const FXchar * new_owner=NULL; + const FXchar * old_owner=NULL; + if (dbus_message_get_args(msg,NULL,DBUS_TYPE_STRING,&dbus_name,DBUS_TYPE_STRING,&old_owner,DBUS_TYPE_STRING,&new_owner,DBUS_TYPE_INVALID)) { + if (compare(proxy->getName(),dbus_name)==0) { + if (new_owner==NULL || compare(new_owner,"")==0) { + proxy->handle(proxy,FXSEL(SEL_DESTROY,0),NULL); + } + else if (old_owner==NULL || compare(old_owner,"")==0){ + proxy->handle(proxy,FXSEL(SEL_CREATE,0),NULL); + } + else { + proxy->handle(proxy,FXSEL(SEL_REPLACED,0),NULL); + } + return DBUS_HANDLER_RESULT_HANDLED; + } + } + } + } + /// Make sure it is for us... + if (dbus_message_has_path(msg,proxy->getPath().text()) && dbus_message_has_interface(msg,proxy->getInterface().text())) { + return proxy->handle(proxy,FXSEL(SEL_SIGNAL,0),msg) ? DBUS_HANDLER_RESULT_HANDLED : DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + } + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + +FXDEFMAP(GMDBusProxy) GMDBusProxyMap[]={ + FXMAPFUNC(SEL_CREATE, 0,GMDBusProxy::onCreate), + FXMAPFUNC(SEL_DESTROY, 0,GMDBusProxy::onDestroy), + FXMAPFUNC(SEL_REPLACED, 0,GMDBusProxy::onReplaced), + FXMAPFUNC(SEL_SIGNAL, 0,GMDBusProxy::onSignal), + FXMAPFUNC(SEL_COMMAND, 0,GMDBusProxy::onMethod), + }; + +FXIMPLEMENT(GMDBusProxy,FXObject,GMDBusProxyMap,ARRAYNUMBER(GMDBusProxyMap)); + + +GMDBusProxy::GMDBusProxy() : target(NULL) { + } + +GMDBusProxy::~GMDBusProxy() { + FXString rule = "type='signal',sender='"+ name +"',path='" + path +"',interface='"+interface+"'"; + dbus_bus_remove_match(bus->connection(),rule.text(),NULL); + dbus_connection_remove_filter(bus->connection(),dbus_proxy_filter,this); + } + +GMDBusProxy::GMDBusProxy(GMDBus *c,const FXchar * n,const FXchar * p,const FXchar * i) : bus(c),name(n),path(p),interface(i),associated(true),target(NULL) { + FXString rule = "type='signal',sender='"+ name +"',path='" + path +"',interface='"+interface+"'"; + dbus_bus_add_match(bus->connection(),rule.text(),NULL); + dbus_connection_add_filter(bus->connection(),dbus_proxy_filter,this,NULL); + } + + +struct GMDBusProxyReply { + FXObject * target; + FXSelector message; + GMDBusProxyReply(FXObject * t,FXSelector m) : target(t), message(m) {} + }; + +FXbool GMDBusProxy::matchSerial(DBusMessage * msg) { + void * ptr; + FXuint s=dbus_message_get_reply_serial(msg); + if (s && (ptr=serial.find((void*)(FXuval)s))!=NULL) { + GMDBusProxyReply * reply = reinterpret_cast(ptr); + if (reply->target) reply->target->handle(this,FXSEL(SEL_COMMAND,reply->message),msg); + serial.remove((void*)(FXuval)dbus_message_get_reply_serial(msg)); + delete reply; + return true; + } + else { + return false; + } + } + +void GMDBusProxy::send(DBusMessage*msg,FXObject * obj,FXSelector m) { + FXuint s; + dbus_connection_send(bus->connection(),msg,&s); + dbus_message_unref(msg); + serial.insert((void*)(FXuval)s,new GMDBusProxyReply(obj,m)); + } + + + +DBusMessage * GMDBusProxy::method(const FXchar * method){ + return dbus_message_new_method_call(name.text(),path.text(),interface.text(),method); + } + +long GMDBusProxy::onCreate(FXObject*,FXSelector,void*ptr) { + associated=true; + return target && target->tryHandle(this,FXSEL(SEL_DESTROY,message),ptr); + } +long GMDBusProxy::onDestroy(FXObject*,FXSelector,void*ptr) { + associated=false; + return target && target->tryHandle(this,FXSEL(SEL_DESTROY,message),ptr); + } +long GMDBusProxy::onReplaced(FXObject*,FXSelector,void*ptr) { + return target && target->tryHandle(this,FXSEL(SEL_REPLACED,message),ptr); + } +long GMDBusProxy::onSignal(FXObject*,FXSelector,void*ptr) { + return target && target->tryHandle(this,FXSEL(SEL_SIGNAL,message),ptr); + } +long GMDBusProxy::onMethod(FXObject*,FXSelector,void*ptr) { + return target && target->tryHandle(this,FXSEL(SEL_COMMAND,message),ptr); + } + + + + + + + + + + + + + + + + + + + + + +/*******************************************************************************************************/ +/* HELPER API */ +/*******************************************************************************************************/ + + +void gm_dbus_variant_append_basic(DBusMessageIter * iter,const FXchar * element_type_string,FXint element_type,const void * value) { + DBusMessageIter container; + dbus_message_iter_open_container(iter,DBUS_TYPE_VARIANT,element_type_string,&container); + dbus_message_iter_append_basic(&container,element_type,value); + dbus_message_iter_close_container(iter,&container); + } + + +void gm_dbus_variant_append_string(DBusMessageIter * iter,const FXchar * value) { + gm_dbus_variant_append_basic(iter,DBUS_TYPE_STRING_AS_STRING,DBUS_TYPE_STRING,&value); + } + +void gm_dbus_variant_append_int32(DBusMessageIter * iter,const FXint value){ + gm_dbus_variant_append_basic(iter,DBUS_TYPE_INT32_AS_STRING,DBUS_TYPE_INT32,&value); + } + +void gm_dbus_variant_append_uint32(DBusMessageIter * iter,const FXuint value){ + gm_dbus_variant_append_basic(iter,DBUS_TYPE_UINT32_AS_STRING,DBUS_TYPE_UINT32,&value); + } + +void gm_dbus_variant_append_path(DBusMessageIter * iter,const FXchar * value){ + gm_dbus_variant_append_basic(iter,DBUS_TYPE_OBJECT_PATH_AS_STRING,DBUS_TYPE_OBJECT_PATH,&value); + } + +void gm_dbus_variant_append_bool(DBusMessageIter * iter,const dbus_bool_t value){ + gm_dbus_variant_append_basic(iter,DBUS_TYPE_BOOLEAN_AS_STRING,DBUS_TYPE_BOOLEAN,&value); + } + +void gm_dbus_variant_append_double(DBusMessageIter * iter,const FXdouble value){ + gm_dbus_variant_append_basic(iter,DBUS_TYPE_DOUBLE_AS_STRING,DBUS_TYPE_DOUBLE,&value); + } + +void gm_dbus_variant_append_long(DBusMessageIter * iter,const FXlong value){ + gm_dbus_variant_append_basic(iter,DBUS_TYPE_INT64_AS_STRING,DBUS_TYPE_INT64,&value); + } + +void gm_dbus_variant_append_string_list(DBusMessageIter * iter,const FXchar * value[]){ + DBusMessageIter container; + DBusMessageIter array; + dbus_message_iter_open_container(iter,DBUS_TYPE_VARIANT,DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_STRING_AS_STRING,&container); + dbus_message_iter_open_container(&container,DBUS_TYPE_ARRAY,DBUS_TYPE_STRING_AS_STRING,&array); + for (FXint n=0;value[n];n++) { + dbus_message_iter_append_basic(&array,DBUS_TYPE_STRING,&value[n]); + } + dbus_message_iter_close_container(&container,&array); + dbus_message_iter_close_container(iter,&container); + } + +void gm_dbus_variant_append_string_list(DBusMessageIter * iter,const FXStringList & list){ + DBusMessageIter container; + DBusMessageIter array; + dbus_message_iter_open_container(iter,DBUS_TYPE_VARIANT,DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_STRING_AS_STRING,&container); + dbus_message_iter_open_container(&container,DBUS_TYPE_ARRAY,DBUS_TYPE_STRING_AS_STRING,&array); + for (FXint n=0;list.no();n++) { + dbus_message_iter_append_basic(&array,DBUS_TYPE_STRING,list[n].text()); + } + dbus_message_iter_close_container(&container,&array); + dbus_message_iter_close_container(iter,&container); + } + + + +void gm_dbus_dict_append_int32(DBusMessageIter * iter,const FXchar * key,const FXint value){ + DBusMessageIter container; + dbus_message_iter_open_container(iter,DBUS_TYPE_DICT_ENTRY,0,&container); + dbus_message_iter_append_basic(&container,DBUS_TYPE_STRING,&key); + gm_dbus_variant_append_int32(&container,value); + dbus_message_iter_close_container(iter,&container); + } + +void gm_dbus_dict_append_uint32(DBusMessageIter * iter,const FXchar * key,const FXuint value){ + DBusMessageIter container; + dbus_message_iter_open_container(iter,DBUS_TYPE_DICT_ENTRY,0,&container); + dbus_message_iter_append_basic(&container,DBUS_TYPE_STRING,&key); + gm_dbus_variant_append_uint32(&container,value); + dbus_message_iter_close_container(iter,&container); + } + + +void gm_dbus_dict_append_string(DBusMessageIter * iter,const FXchar * key,const FXchar * value){ + DBusMessageIter container; + dbus_message_iter_open_container(iter,DBUS_TYPE_DICT_ENTRY,0,&container); + dbus_message_iter_append_basic(&container,DBUS_TYPE_STRING,&key); + gm_dbus_variant_append_string(&container,value); + dbus_message_iter_close_container(iter,&container); + } + +void gm_dbus_dict_append_string(DBusMessageIter * iter,const FXchar * key,const FXString & value){ + DBusMessageIter container; + dbus_message_iter_open_container(iter,DBUS_TYPE_DICT_ENTRY,0,&container); + dbus_message_iter_append_basic(&container,DBUS_TYPE_STRING,&key); + gm_dbus_variant_append_string(&container,value.text()); + dbus_message_iter_close_container(iter,&container); + } + +void gm_dbus_dict_append_path(DBusMessageIter * iter,const FXchar * key,const FXchar * value){ + DBusMessageIter container; + dbus_message_iter_open_container(iter,DBUS_TYPE_DICT_ENTRY,0,&container); + dbus_message_iter_append_basic(&container,DBUS_TYPE_STRING,&key); + gm_dbus_variant_append_path(&container,value); + dbus_message_iter_close_container(iter,&container); + } + +void gm_dbus_dict_append_bool(DBusMessageIter * iter,const FXchar * key,const dbus_bool_t value){ + DBusMessageIter container; + dbus_message_iter_open_container(iter,DBUS_TYPE_DICT_ENTRY,0,&container); + dbus_message_iter_append_basic(&container,DBUS_TYPE_STRING,&key); + gm_dbus_variant_append_bool(&container,value); + dbus_message_iter_close_container(iter,&container); + } + + +void gm_dbus_dict_append_double(DBusMessageIter * iter,const FXchar * key,const FXdouble & value){ + DBusMessageIter container; + dbus_message_iter_open_container(iter,DBUS_TYPE_DICT_ENTRY,0,&container); + dbus_message_iter_append_basic(&container,DBUS_TYPE_STRING,&key); + gm_dbus_variant_append_double(&container,value); + dbus_message_iter_close_container(iter,&container); + } + +void gm_dbus_dict_append_long(DBusMessageIter * iter,const FXchar * key,const FXlong & value){ + DBusMessageIter container; + dbus_message_iter_open_container(iter,DBUS_TYPE_DICT_ENTRY,0,&container); + dbus_message_iter_append_basic(&container,DBUS_TYPE_STRING,&key); + gm_dbus_variant_append_long(&container,value); + dbus_message_iter_close_container(iter,&container); + } + + +void gm_dbus_dict_append_string_list(DBusMessageIter * iter,const FXchar * key,const FXchar * value[]){ + DBusMessageIter container; + dbus_message_iter_open_container(iter,DBUS_TYPE_DICT_ENTRY,0,&container); + dbus_message_iter_append_basic(&container,DBUS_TYPE_STRING,&key); + gm_dbus_variant_append_string_list(&container,value); + dbus_message_iter_close_container(iter,&container); + } + +void gm_dbus_dict_append_string_list(DBusMessageIter * iter,const FXchar * key,const FXStringList &value){ + DBusMessageIter container; + dbus_message_iter_open_container(iter,DBUS_TYPE_DICT_ENTRY,0,&container); + dbus_message_iter_append_basic(&container,DBUS_TYPE_STRING,&key); + gm_dbus_variant_append_string_list(&container,value); + dbus_message_iter_close_container(iter,&container); + } + +void gm_dbus_append_string(DBusMessageIter *iter,const FXString & value){ + const FXchar * v = value.text(); + dbus_message_iter_append_basic(iter,DBUS_TYPE_STRING,&v); + } + + +void gm_dbus_append_string_pair(DBusMessageIter *iter,const FXchar * key,const FXchar * value){ + dbus_message_iter_append_basic(iter,DBUS_TYPE_STRING,&key); + dbus_message_iter_append_basic(iter,DBUS_TYPE_STRING,&value); + } + + +DBusHandlerResult gm_dbus_reply_string(DBusConnection * connection,DBusMessage * msg,const FXchar * data) { + FXuint serial; + DBusMessage * reply = dbus_message_new_method_return(msg); + if (reply) { + dbus_message_append_args(reply,DBUS_TYPE_STRING,&data,DBUS_TYPE_INVALID); + dbus_connection_send(connection,reply,&serial); + dbus_message_unref(reply); + } + return DBUS_HANDLER_RESULT_HANDLED; + } + +DBusHandlerResult gm_dbus_reply_uint_string(DBusConnection * connection,DBusMessage * msg,const FXuint val,const FXchar * data) { + FXuint serial; + DBusMessage * reply = dbus_message_new_method_return(msg); + if (reply) { + dbus_message_append_args(reply,DBUS_TYPE_UINT32,&val,DBUS_TYPE_STRING,&data,DBUS_TYPE_INVALID); + dbus_connection_send(connection,reply,&serial); + dbus_message_unref(reply); + } + return DBUS_HANDLER_RESULT_HANDLED; + } + +DBusHandlerResult gm_dbus_reply_int(DBusConnection * connection,DBusMessage * msg,const FXint data) { + FXuint serial; + DBusMessage * reply = dbus_message_new_method_return(msg); + if (reply) { + dbus_message_append_args(reply,DBUS_TYPE_INT32,&data,DBUS_TYPE_INVALID); + dbus_connection_send(connection,reply,&serial); + dbus_message_unref(reply); + } + return DBUS_HANDLER_RESULT_HANDLED; + } + +DBusHandlerResult gm_dbus_reply_double(DBusConnection * connection,DBusMessage * msg,const FXdouble data) { + FXuint serial; + DBusMessage * reply = dbus_message_new_method_return(msg); + if (reply) { + dbus_message_append_args(reply,DBUS_TYPE_DOUBLE,&data,DBUS_TYPE_INVALID); + dbus_connection_send(connection,reply,&serial); + dbus_message_unref(reply); + } + return DBUS_HANDLER_RESULT_HANDLED; + } + +DBusHandlerResult gm_dbus_reply_long(DBusConnection * connection,DBusMessage * msg,const FXlong data) { + FXuint serial; + DBusMessage * reply = dbus_message_new_method_return(msg); + if (reply) { + dbus_message_append_args(reply,DBUS_TYPE_INT64,&data,DBUS_TYPE_INVALID); + dbus_connection_send(connection,reply,&serial); + dbus_message_unref(reply); + } + return DBUS_HANDLER_RESULT_HANDLED; + } + +DBusHandlerResult gm_dbus_reply_unsigned_int(DBusConnection * connection,DBusMessage * msg,const FXuint data) { + FXuint serial; + DBusMessage * reply = dbus_message_new_method_return(msg); + if (reply) { + dbus_message_append_args(reply,DBUS_TYPE_UINT32,&data,DBUS_TYPE_INVALID); + dbus_connection_send(connection,reply,&serial); + dbus_message_unref(reply); + } + return DBUS_HANDLER_RESULT_HANDLED; + } + +DBusHandlerResult gm_dbus_reply_bool(DBusConnection * connection,DBusMessage * msg,const dbus_bool_t data) { + FXuint serial; + DBusMessage * reply = dbus_message_new_method_return(msg); + if (reply) { + dbus_message_append_args(reply,DBUS_TYPE_BOOLEAN,&data,DBUS_TYPE_INVALID); + dbus_connection_send(connection,reply,&serial); + dbus_message_unref(reply); + } + return DBUS_HANDLER_RESULT_HANDLED; + } + +DBusHandlerResult gm_dbus_reply_string_list(DBusConnection * connection,DBusMessage * msg,const FXchar * data[]) { + FXuint serial; + DBusMessage * reply = dbus_message_new_method_return(msg); + if (reply) { + DBusMessageIter iter; + DBusMessageIter array; + dbus_message_iter_init_append(reply,&iter); + dbus_message_iter_open_container(&iter,DBUS_TYPE_ARRAY,DBUS_TYPE_STRING_AS_STRING,&array); + for (FXint n=0;data[n];n++) { + dbus_message_iter_append_basic(&array,DBUS_TYPE_STRING,&data[n]); + } + dbus_message_iter_close_container(&iter,&array); + dbus_connection_send(connection,reply,&serial); + dbus_message_unref(reply); + } + return DBUS_HANDLER_RESULT_HANDLED; + } + + + + +DBusHandlerResult gm_dbus_reply_if_needed(DBusConnection * connection,DBusMessage * msg) { + if (!dbus_message_get_no_reply(msg)) { + FXuint serial; + DBusMessage * reply = dbus_message_new_method_return(msg); + if (reply) { + dbus_connection_send(connection,reply,&serial); + dbus_message_unref(reply); + } + } + return DBUS_HANDLER_RESULT_HANDLED; + } + +void gm_dbus_match_signal(DBusConnection*connection,const FXchar * path,const FXchar * interface,const FXchar * member){ + FXString rule = GMStringFormat("type='signal',path='%s',interface='%s',member='%s'",path,interface,member); + dbus_bus_add_match(connection,rule.text(),NULL); + } diff --git a/src/GMDBus.h b/src/GMDBus.h new file mode 100644 index 0000000..3fe4e2d --- /dev/null +++ b/src/GMDBus.h @@ -0,0 +1,225 @@ +/******************************************************************************** +* * +* F O X D B U S S U P P O R T * +* * +********************************************************************************* +* Copyright (C) 2007-2011 by Sander Jansen. All Rights Reserved. * +********************************************************************************* +* This library is free software; you can redistribute it and/or * +* modify it under the terms of the GNU Lesser General Public * +* License as published by the Free Software Foundation; either * +* version 2.1 of the License, or (at your option) any later version. * +* * +* 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 GMDBUS_H +#define GMDBUS_H + +#ifndef DBUS_BUS_H +#include +#endif + +/** +* GMDBus is a thin wrapper of DBusConnection with the purpose of integrating it +* into the FOX event loop (1.6), so that activity on DBus will be properly handled. +* DBusConnections may only be managed by one GMDBus. The APIs are strictly enforcing this +* by keeping a global (threadsafe) map of GMDBus/DBusConnection references. +** +* In the future I hope to support the new FXReactor framework in FOX. +* +*/ + +class GMDBusManager; + +class GMDBus : public FXObject { +FXDECLARE(GMDBus) +private: + DBusConnection * dc; +private: + GMDBus(const GMDBus &); + GMDBus &operator=(const GMDBus&); +public: + enum { + ID_HANDLE = 1, + ID_DISPATCH, + ID_LAST, + }; +public: + long onHandleRead(FXObject*,FXSelector,void*); + long onHandleWrite(FXObject*,FXSelector,void*); + long onHandleExcept(FXObject*,FXSelector,void*); + long onDispatch(FXObject*,FXSelector,void*); +public: + /** + * Construct non active Dbus Connection Hook + */ + GMDBus(); + + /** + * Open Standard Bus + * return false if already managed by other FXDBusConnection + */ + FXbool open(DBusBusType bustype=DBUS_BUS_SESSION); + + /** + * Return FXDBusConnection for given DBusConnection. Return NULL if not found. + */ + static GMDBus * find(DBusConnection * dc); + + /** + * Init the event loop for all connections + */ + static void initEventLoop(); + + /** + * Return DBUS version + */ + static FXString dbusversion(); + + /** + * Setup Callback Hooks + */ + virtual void setup_event_loop(); + + /** + * Return DBusConnection + */ + DBusConnection * connection() const { return dc; } + + /** + * Returns whether we're connected or not + */ + FXbool connected() const; + + /** + * Returns whether we're authenticated + */ + FXbool authenticated() const; + + + /** + * Flush + */ + void flush(); + + /** + * Send with Reply + */ + FXbool sendWithReply(DBusMessage * msg,FXint timeout,FXObject*,FXSelector); + + /** + * Send + */ + void send(DBusMessage * msg,FXuint & serial); + void send(DBusMessage * msg); + + /** + * Destructor. Existing DBusConnection will be unreffed. + */ + virtual ~GMDBus(); + }; + + +/* A Remote Object */ +class GMDBusProxy : public FXObject { +FXDECLARE(GMDBusProxy) +protected: + GMDBus * bus; /// Bus + FXString name; /// Name + FXString path; /// Path + FXString interface; /// Interface +protected: + FXbool associated; /// Are we associated + FXObject * target; /// Target object + FXSelector message; /// Message ID +protected: + FXHash serial; +protected: + GMDBusProxy(); +private: + GMDBusProxy(const GMDBusProxy&); + GMDBusProxy& operator=(const GMDBusProxy&); +public: + long onCreate(FXObject*,FXSelector,void*); + long onDestroy(FXObject*,FXSelector,void*); + long onReplaced(FXObject*,FXSelector,void*); + long onMethod(FXObject*,FXSelector,void*); + long onSignal(FXObject*,FXSelector,void*); +public: + GMDBusProxy(GMDBus*,const FXchar * name,const FXchar * path,const FXchar * interface); + + FXString getPath() const { return path; } + + FXString getName() const { return name; } + + FXString getInterface() const { return interface; } + + FXbool matchSerial(DBusMessage * msg); + + DBusMessage * method(const FXchar * method); + + void send(DBusMessage*,FXObject*,FXSelector); + + virtual ~GMDBusProxy(); + }; + + + +#define DEBUG_DBUS_MESSAGE(msg) { \ + FXTRACE((80,"-----%s-------\n",__func__)); \ + FXTRACE((80,"type: %s\n",dbus_message_type_to_string(dbus_message_get_type(msg))));\ + FXTRACE((80,"path: %s\n",dbus_message_get_path(msg))); \ + FXTRACE((80,"member: \"%s\"\n",dbus_message_get_member(msg))); \ + FXTRACE((80,"interface: %s\n",dbus_message_get_interface(msg))); \ + FXTRACE((80,"sender: %s\n",dbus_message_get_sender(msg))); \ + FXTRACE((80,"signature: %s\n",dbus_message_get_signature(msg))); } + +/* Some Helper Functions */ +extern void gm_dbus_variant_append_basic(DBusMessageIter * iter,const FXchar * element_string,FXint element,const void * value); +extern void gm_dbus_variant_append_string(DBusMessageIter * iter,const FXchar * value); +extern void gm_dbus_variant_append_path(DBusMessageIter * iter,const FXchar * value); +extern void gm_dbus_variant_append_int32(DBusMessageIter * iter,const FXint value); +extern void gm_dbus_variant_append_uint32(DBusMessageIter * iter,const FXuint value); +extern void gm_dbus_variant_append_bool(DBusMessageIter * iter,const dbus_bool_t value); +extern void gm_dbus_variant_append_double(DBusMessageIter * iter,const FXdouble value); +extern void gm_dbus_variant_append_long(DBusMessageIter * iter,const FXlong value); +extern void gm_dbus_variant_append_string_list(DBusMessageIter * iter,const FXchar * data[]); +extern void gm_dbus_variant_append_string_list(DBusMessageIter * iter,const FXStringList&); + +extern void gm_dbus_append_string(DBusMessageIter *iter,const FXString & value); +extern void gm_dbus_append_string_pair(DBusMessageIter *iter,const FXchar * key,const FXchar * value); + +extern void gm_dbus_dict_append_int32(DBusMessageIter * dict,const FXchar * key,const FXint value); +extern void gm_dbus_dict_append_uint32(DBusMessageIter * dict,const FXchar * key,const FXuint value); +extern void gm_dbus_dict_append_string(DBusMessageIter * dict,const FXchar * key,const FXchar * value); +extern void gm_dbus_dict_append_string(DBusMessageIter * dict,const FXchar * key,const FXString & value); +extern void gm_dbus_dict_append_double(DBusMessageIter * dict,const FXchar * key,const FXdouble & value); +extern void gm_dbus_dict_append_long(DBusMessageIter * dict,const FXchar * key,const FXlong & value); +extern void gm_dbus_dict_append_path(DBusMessageIter * dict,const FXchar * key,const FXchar * value); +extern void gm_dbus_dict_append_bool(DBusMessageIter * dict,const FXchar * key,const dbus_bool_t value); +extern void gm_dbus_dict_append_string_list(DBusMessageIter * dict,const FXchar * key,const FXchar * data[]); +extern void gm_dbus_dict_append_string_list(DBusMessageIter * dict,const FXchar * key,const FXStringList &); + +extern DBusHandlerResult gm_dbus_reply_string(DBusConnection * connection,DBusMessage * msg,const FXchar * xml); +extern DBusHandlerResult gm_dbus_reply_uint_string(DBusConnection * connection,DBusMessage * msg,const FXuint val,const FXchar * xml); +extern DBusHandlerResult gm_dbus_reply_int(DBusConnection * connection,DBusMessage * msg,const FXint value); +extern DBusHandlerResult gm_dbus_reply_double(DBusConnection * connection,DBusMessage * msg,const FXdouble value); +extern DBusHandlerResult gm_dbus_reply_unsigned_int(DBusConnection * connection,DBusMessage * msg,const FXuint value); +extern DBusHandlerResult gm_dbus_reply_long(DBusConnection * connection,DBusMessage * msg,const FXlong value); +extern DBusHandlerResult gm_dbus_reply_if_needed(DBusConnection * connection,DBusMessage * msg); +extern DBusHandlerResult gm_dbus_reply_bool(DBusConnection*connection,DBusMessage*msg,const dbus_bool_t); +extern DBusHandlerResult gm_dbus_reply_string_list(DBusConnection * connection,DBusMessage * msg,const FXchar * data[]); + +extern void gm_dbus_match_signal(DBusConnection*,const FXchar * path,const FXchar * interface,const FXchar * member); + +#endif + + + diff --git a/src/GMDatabase.cpp b/src/GMDatabase.cpp new file mode 100644 index 0000000..60771c3 --- /dev/null +++ b/src/GMDatabase.cpp @@ -0,0 +1,241 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#include "gmdefs.h" + +// Database +GMDatabase::GMDatabase() : opened(FALSE),db(NULL){ + } + +GMDatabase::~GMDatabase(){ + close(); + } + +FXbool GMDatabase::open(const FXString & filename){ + close(); + + if (sqlite3_open(filename.text(),&db)!=SQLITE_OK){ + return FALSE; + } + + if (!execute("PRAGMA database_list;")) + return FALSE; + + opened=TRUE; + return TRUE; + } + +/// Close the database +void GMDatabase::close(){ + if (db) { + sqlite3_close(db); + db=NULL; + } + opened=FALSE; + } + +/// Clear the database +void GMDatabase::clear(){ + FXStringList tables; + listTables(tables); + for (FXint i=0;i +#include "icons.h" + +static void updateTrackFilenames(GMTrackDatabase * db,FXIntList & tracks) { + register FXint i=0; + FXint numchanges=0; + FXString mrl; + GMTrack trackinfo; + FXStringList newmrls; + FXStringList oldmrls; + + if (!GMPlayerManager::instance()->getPreferences().export_format_template.contains("%T")) { + FXMessageBox::error(GMPlayerManager::instance()->getMainWindow(),MBOX_OK,fxtr("Invalid Template"),fxtr("The provided template is invalid. The track title %%T needs to be specified.\nPlease fix the filename template in the preference panel.")); + return; + } + + FXTextCodec * codec = GMFilename::findcodec(GMPlayerManager::instance()->getPreferences().export_encoding); + + FXuint options=0; + + if (GMPlayerManager::instance()->getPreferences().export_lowercase) + options|=GMFilename::LOWERCASE; + + if (GMPlayerManager::instance()->getPreferences().export_lowercase_extension) + options|=GMFilename::LOWERCASE_EXTENSION; + + if (GMPlayerManager::instance()->getPreferences().export_underscore) + options|=GMFilename::NOSPACES; + + /// Create New Mrls. + for (i=0;igetTrack(tracks[i],trackinfo)) { + FXMessageBox::error(GMPlayerManager::instance()->getMainWindow(),MBOX_OK,fxtr("Database Error"),fxtr("Oops. Database Error")); + return; + } + if (GMFilename::create(mrl,trackinfo,GMPlayerManager::instance()->getPreferences().export_format_template,GMPlayerManager::instance()->getPreferences().export_character_filter,options,codec) && mrl!=trackinfo.mrl) { + newmrls.append(mrl); + oldmrls.append(trackinfo.mrl); + numchanges++; + } + else { + newmrls.append(FXString::null); + oldmrls.append(FXString::null); + } + } + + if (numchanges==0){ + FXMessageBox::information(GMPlayerManager::instance()->getMainWindow(),MBOX_OK,fxtr("No changes"),fxtr("Filenames did not require any changes")); + delete codec; + return; + } + + + /// Ask For Permission + FXDialogBox dialog(GMPlayerManager::instance()->getMainWindow(),fxtr("Rename Audio Files?"),DECOR_TITLE|DECOR_BORDER|DECOR_RESIZE|DECOR_CLOSE,0,0,600,400,0,0,0,0,0,0); + GMPlayerManager::instance()->getMainWindow()->create_dialog_header(&dialog,fxtr("Renaming Audio Files…"),fxtr("The following audio files are going to be renamed")); + FXHorizontalFrame *closebox=new FXHorizontalFrame(&dialog,LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X|PACK_UNIFORM_WIDTH,0,0,0,0); + new GMButton(closebox,fxtr("&Rename"),NULL,&dialog,FXDialogBox::ID_ACCEPT,BUTTON_INITIAL|BUTTON_DEFAULT|LAYOUT_RIGHT|FRAME_RAISED|FRAME_THICK,0,0,0,0, 15,15); + new GMButton(closebox,fxtr("&Cancel"),NULL,&dialog,FXDialogBox::ID_CANCEL,BUTTON_DEFAULT|LAYOUT_RIGHT|FRAME_RAISED|FRAME_THICK,0,0,0,0, 15,15); + new FXSeparator(&dialog,SEPARATOR_GROOVE|LAYOUT_FILL_X|LAYOUT_SIDE_BOTTOM); + + FXVerticalFrame * main = new FXVerticalFrame(&dialog,LAYOUT_FILL_X|LAYOUT_FILL_Y); + GMScrollFrame * sunken = new GMScrollFrame(main); + GMList * list = new GMList(sunken,NULL,0,LAYOUT_FILL_X|LAYOUT_FILL_Y); + + for (i=0;iappendItem(newmrls[i]); + } + } + + if (dialog.execute()) { + for (i=0;isetTrackFilename(tracks[i],newmrls[i]); + } + else { + if (i+1getMainWindow(),MBOX_YES_NO,fxtr("Unable to rename file"),fxtrformat("Unable to rename:\n%s\n\nto:%s\nContinue renaming files?"),oldmrls[i].text(),newmrls[i].text())==MBOX_CLICKED_NO) + break; + } + else { + FXMessageBox::error(GMPlayerManager::instance()->getMainWindow(),MBOX_OK,fxtr("Unable to rename file"),fxtrformat("Unable to rename:\n%s\n\nto:%s"),oldmrls[i].text(),newmrls[i].text()); + } + } + } + } + } + } + } + delete codec; + } + + +class GMFilenameTemplateDialog : public FXDialogBox { +FXDECLARE(GMFilenameTemplateDialog) +protected: + FXFontPtr font_fixed; + FXDataTarget target_format_template; + FXDataTarget target_export_lowercase; + FXDataTarget target_export_lowercase_extension; + FXDataTarget target_export_underscore; + FXDataTarget target_export_encoding; + FXDataTarget target_export_filter; +protected: + GMFilenameTemplateDialog(){} +private: + GMFilenameTemplateDialog(const GMFilenameTemplateDialog&); + GMFilenameTemplateDialog &operator=(const GMFilenameTemplateDialog&); +public: + GMFilenameTemplateDialog(FXWindow*); + ~GMFilenameTemplateDialog(); + }; + +FXIMPLEMENT(GMFilenameTemplateDialog,FXDialogBox,0,0); + +GMFilenameTemplateDialog::GMFilenameTemplateDialog(FXWindow*p) : FXDialogBox(p,FXString::null,DECOR_TITLE|DECOR_BORDER,0,0,0,0,0,0,0,0,0,0) { + setTitle(tr("Filename Template")); + + const FXuint labelstyle=LAYOUT_CENTER_Y|LABEL_NORMAL|LAYOUT_RIGHT; + + target_format_template.connect(GMPlayerManager::instance()->getPreferences().export_format_template); + target_export_lowercase.connect(GMPlayerManager::instance()->getPreferences().export_lowercase); + target_export_lowercase_extension.connect(GMPlayerManager::instance()->getPreferences().export_lowercase_extension); + target_export_underscore.connect(GMPlayerManager::instance()->getPreferences().export_underscore); + target_export_encoding.connect(GMPlayerManager::instance()->getPreferences().export_encoding); + target_export_filter.connect(GMPlayerManager::instance()->getPreferences().export_character_filter); + + /// Create a fixed font, about the same size as the normal font + FXint size = FXApp::instance()->getNormalFont()->getSize(); + font_fixed = new FXFont(FXApp::instance(),"mono",(int)size/10,FXFont::Normal,FXFont::Straight,FONTENCODING_UNICODE,FXFont::NonExpanded,FXFont::Modern|FXFont::Fixed); + + FXVerticalFrame * main=new FXVerticalFrame(this,LAYOUT_FILL_X|LAYOUT_FILL_Y); + + new FXLabel(main,tr("Template may contain absolute or relative path, environment variables\nand ~. Relative paths are based on the location of the original file. The\nfile extension gets automatically added. The following macros\nmay be used:"),NULL,JUSTIFY_LEFT); + FXLabel * label = new FXLabel(main,tr("%T - title %A - album name\n" + "%P - album artist name %p - track artist name\n" + "%y - year %d - disc number\n" + "%N - track number (2 digits) %n - track number \n%G - genre" + ),NULL,JUSTIFY_LEFT,0,0,0,0,30); + label->setFont(font_fixed); + + new FXLabel(main,tr("Conditions may be used as well:"),NULL,JUSTIFY_LEFT); + label = new FXLabel(main,tr("?c - display a if c is not empty else display b.\n" + "?c - display c if not empty\n" + ),NULL,JUSTIFY_LEFT,0,0,0,0,30); + label->setFont(font_fixed); + + + new FXSeparator(main,SEPARATOR_GROOVE|LAYOUT_FILL_X); + + FXMatrix * matrix = new FXMatrix(main,2,MATRIX_BY_COLUMNS|LAYOUT_FILL_X,0,0,0,0,0,0,4,0); + new FXLabel(matrix,tr("Template:"),NULL,labelstyle); + FXTextField * textfield = new GMTextField(matrix,20,&target_format_template,FXDataTarget::ID_VALUE,LAYOUT_FILL_X|TEXTFIELD_ENTER_ONLY|FRAME_SUNKEN|FRAME_THICK|LAYOUT_FILL_COLUMN); + textfield->setFont(font_fixed); + + new FXLabel(matrix,tr("Encoding:"),NULL,labelstyle); + GMListBox * list_codecs = new GMListBox(matrix,&target_export_encoding,FXDataTarget::ID_VALUE,FRAME_SUNKEN|FRAME_THICK|LAYOUT_FILL_COLUMN); + for (int i=0;gmcodecnames[i]!=NULL;i++) + list_codecs->appendItem(gmcodecnames[i]); + list_codecs->setNumVisible(9); + + new FXLabel(matrix,tr("Exclude:"),NULL,labelstyle); + textfield = new GMTextField(matrix,15,&target_export_filter,FXDataTarget::ID_VALUE,LAYOUT_FILL_X|TEXTFIELD_ENTER_ONLY|FRAME_SUNKEN|FRAME_THICK|LAYOUT_FILL_COLUMN); + textfield->setFont(font_fixed); + + new FXLabel(matrix,tr("Options:"),NULL,labelstyle); + new GMCheckButton(matrix,tr("Replace spaces with underscores"),&target_export_underscore,FXDataTarget::ID_VALUE,LAYOUT_FILL_COLUMN|CHECKBUTTON_NORMAL); + new FXFrame(matrix,FRAME_NONE); + new GMCheckButton(matrix,fxtr("Lower case"),&target_export_lowercase,FXDataTarget::ID_VALUE,LAYOUT_FILL_COLUMN|CHECKBUTTON_NORMAL); + new FXFrame(matrix,FRAME_NONE); + new GMCheckButton(matrix,fxtr("Lower case extension"),&target_export_lowercase_extension,FXDataTarget::ID_VALUE,LAYOUT_FILL_COLUMN|CHECKBUTTON_NORMAL); + new FXSeparator(main,SEPARATOR_GROOVE|LAYOUT_FILL_X); + + FXHorizontalFrame *closebox=new FXHorizontalFrame(main,LAYOUT_BOTTOM|LAYOUT_FILL_X|PACK_UNIFORM_WIDTH,0,0,0,0,0,0,0,0); + new GMButton(closebox,fxtr("&Close"),NULL,this,FXDialogBox::ID_ACCEPT,BUTTON_INITIAL|BUTTON_DEFAULT|LAYOUT_RIGHT|FRAME_RAISED|FRAME_THICK,0,0,0,0,20,20); + } + +GMFilenameTemplateDialog::~GMFilenameTemplateDialog(){ + GMPlayerManager::instance()->getPreferences().export_format_template.trim(); + } + + + +FXbool GMDatabaseClipboardData::request(FXDragType target,GMClipboard * clipboard){ + if (target==GMClipboard::urilistType){ + FXString uri; + FXStringList filenames; + db->getTrackFilenames(tracks,filenames); + gm_convert_filenames_to_uri(filenames,uri); + clipboard->setDNDData(FROM_CLIPBOARD,target,uri); + return true; + } + else if (target==GMClipboard::kdeclipboard){ + clipboard->setDNDData(FROM_CLIPBOARD,target,"0"); + return true; + } + else if (target==GMClipboard::gnomeclipboard){ + FXString clipdata; + FXStringList filenames; + db->getTrackFilenames(tracks,filenames); + gm_convert_filenames_to_gnomeclipboard(filenames,clipdata); + clipboard->setDNDData(FROM_CLIPBOARD,target,clipdata); + return true; + } + return false; + } + + +FXDEFMAP(GMDatabaseSource) GMDatabaseSourceMap[]={ +// FXMAPFUNC(SEL_COMMAND,GMDatabaseSource::ID_EDIT_GENRE,GMDatabaseSource::onCmdEditGenre), +// FXMAPFUNC(SEL_COMMAND,GMDatabaseSource::ID_EDIT_ARTIST,GMDatabaseSource::onCmdEditArtist), +// FXMAPFUNC(SEL_COMMAND,GMDatabaseSource::ID_EDIT_ALBUM,GMDatabaseSource::onCmdEditAlbum), + FXMAPFUNC(SEL_COMMAND,GMDatabaseSource::ID_EDIT_TRACK,GMDatabaseSource::onCmdEditTrack), + FXMAPFUNC(SEL_COMMAND,GMDatabaseSource::ID_DELETE_GENRE,GMDatabaseSource::onCmdDelete), + FXMAPFUNC(SEL_COMMAND,GMDatabaseSource::ID_DELETE_ARTIST,GMDatabaseSource::onCmdDelete), + FXMAPFUNC(SEL_COMMAND,GMDatabaseSource::ID_DELETE_ALBUM,GMDatabaseSource::onCmdDelete), + FXMAPFUNC(SEL_COMMAND,GMDatabaseSource::ID_DELETE_TRACK,GMDatabaseSource::onCmdDelete), + + FXMAPFUNC(SEL_COMMAND,GMDatabaseSource::ID_EXPORT_GENRE,GMDatabaseSource::onCmdExportTracks), + FXMAPFUNC(SEL_COMMAND,GMDatabaseSource::ID_EXPORT_ARTIST,GMDatabaseSource::onCmdExportTracks), + FXMAPFUNC(SEL_COMMAND,GMDatabaseSource::ID_EXPORT_ALBUM,GMDatabaseSource::onCmdExportTracks), + FXMAPFUNC(SEL_COMMAND,GMDatabaseSource::ID_EXPORT_TRACK,GMDatabaseSource::onCmdExportTracks), + + FXMAPFUNC(SEL_COMMAND,GMDatabaseSource::ID_COPY_ARTIST,GMDatabaseSource::onCmdCopyArtistAlbum), + FXMAPFUNC(SEL_COMMAND,GMDatabaseSource::ID_COPY_ALBUM,GMDatabaseSource::onCmdCopyArtistAlbum), + FXMAPFUNC(SEL_COMMAND,GMDatabaseSource::ID_COPY_TRACK,GMDatabaseSource::onCmdCopyTrack), + FXMAPFUNC(SEL_DND_REQUEST,GMDatabaseSource::ID_COPY_ARTIST,GMDatabaseSource::onCmdRequestArtistAlbum), + FXMAPFUNC(SEL_DND_REQUEST,GMDatabaseSource::ID_COPY_ALBUM,GMDatabaseSource::onCmdRequestArtistAlbum), + FXMAPFUNC(SEL_DND_REQUEST,GMDatabaseSource::ID_COPY_TRACK,GMDatabaseSource::onCmdRequestTrack), + FXMAPFUNC(SEL_COMMAND,GMDatabaseSource::ID_EXPORT,GMDatabaseSource::onCmdExport), + FXMAPFUNC(SEL_UPDATE,GMDatabaseSource::ID_EXPORT,GMDatabaseSource::onUpdExport), + + FXMAPFUNC(SEL_COMMAND,GMDatabaseSource::ID_NEW_PLAYLIST,GMDatabaseSource::onCmdNewPlayList), + FXMAPFUNC(SEL_COMMAND,GMDatabaseSource::ID_IMPORT_PLAYLIST,GMDatabaseSource::onCmdImportPlayList), + + FXMAPFUNC(SEL_COMMAND,GMDatabaseSource::ID_CLEAR,GMDatabaseSource::onCmdClear), + FXMAPFUNC(SEL_COMMAND,GMDatabaseSource::ID_INFO,GMDatabaseSource::onCmdInfo), + FXMAPFUNC(SEL_DND_DROP,GMDatabaseSource::ID_DROP,GMDatabaseSource::onCmdDrop), + + FXMAPFUNC(SEL_COMMAND,GMDatabaseSource::ID_PASTE,GMDatabaseSource::onCmdPaste), + FXMAPFUNC(SEL_UPDATE,GMDatabaseSource::ID_PASTE,GMDatabaseSource::onUpdPaste), + FXMAPFUNC(SEL_TIMEOUT,GMDatabaseSource::ID_TRACK_PLAYED,GMDatabaseSource::onCmdTrackPlayed), + + FXMAPFUNC(SEL_COMMAND,GMDatabaseSource::ID_FILENAME_TEMPLATE,GMDatabaseSource::onCmdFilenameTemplate), + FXMAPFUNC(SEL_COMMAND,GMDatabaseSource::ID_OPEN_FOLDER,GMDatabaseSource::onCmdOpenFolder) + }; + +FXIMPLEMENT(GMDatabaseSource,GMSource,GMDatabaseSourceMap,ARRAYNUMBER(GMDatabaseSourceMap)); + +GMDatabaseSource* GMDatabaseSource::filterowner=NULL; + +GMDatabaseSource::GMDatabaseSource() : dbowned(true), db(NULL),filtermask(0),hasfilter(false) { + sort_browse=GMDBTrackItem::browseSort; + } + +GMDatabaseSource::GMDatabaseSource(GMTrackDatabase * database) : dbowned(true), db(database),filtermask(FILTER_DEFAULT),hasfilter(false) { + FXASSERT(db); + sort_browse=GMDBTrackItem::browseSort; + } + +GMDatabaseSource::~GMDatabaseSource() { + clearAlbumIconCache(); + if (dbowned) delete db; + } + + +#include // need this for rand() + +//generates a psuedo-random integer between min and max +int randint(int min, int max,unsigned int * random_seed) +{ + return min+int( ((double)(max-min+1))*rand_r(random_seed)/(RAND_MAX+1.0)); +} + + +void GMDatabaseSource::shuffle(GMTrackList*list,FXuint sort_seed) const { + list->setSortFunc(GMDBTrackItem::ascendingAlbum); + list->sortItems(); + list->setSortFunc(NULL); + + /// Initial Value comes from sort_seed (read from registry and such...) + FXuint random_seed = sort_seed; + rand_r(&random_seed); + + FXint n; + FXint nitems=list->getNumItems()-1; + for (FXint i=0;igetNumItems()); + list->moveItem(i,n); + } + } + + + +void GMDatabaseSource::configure(GMColumnList& list) const { + list.no(10); + list[0]=GMColumn(notr("No."),HEADER_TRACK,GMDBTrackItem::ascendingTrack,GMDBTrackItem::descendingTrack,43,(getPlayList()==-1) ? true : false ,true,0); + list[1]=GMColumn(notr("Queue"),HEADER_QUEUE,GMDBTrackItem::ascendingQueue,GMDBTrackItem::descendingQueue,60,(getPlayList()==-1) ? false : true,false,1); + list[2]=GMColumn(notr("Title"),HEADER_TITLE,GMDBTrackItem::ascendingTitle,GMDBTrackItem::descendingTitle,360,true,true,2); + list[3]=GMColumn(notr("Artist"),HEADER_ARTIST,GMDBTrackItem::ascendingArtist,GMDBTrackItem::descendingArtist,400,true,false,3); + list[4]=GMColumn(notr("Album Artist"),HEADER_ALBUM_ARTIST,GMDBTrackItem::ascendingAlbumArtist,GMDBTrackItem::descendingAlbumArtist,200,true,false,4); + list[5]=GMColumn(notr("Album"),HEADER_ALBUM,GMDBTrackItem::ascendingAlbum,GMDBTrackItem::descendingAlbum,200,true,false,5); + list[6]=GMColumn(notr("Disc"),HEADER_DISC,GMDBTrackItem::ascendingDisc,GMDBTrackItem::descendingDisc,43,false,false,6); + list[7]=GMColumn(notr("Genre"),HEADER_GENRE,GMDBTrackItem::ascendingGenre,GMDBTrackItem::descendingGenre,200,true,false,7); + list[8]=GMColumn(notr("Year"),HEADER_YEAR,GMDBTrackItem::ascendingYear,GMDBTrackItem::descendingYear,60,true,true,8); + list[9]=GMColumn(notr("Time"),HEADER_TIME,GMDBTrackItem::ascendingTime,GMDBTrackItem::descendingTime,60,true,true,9); + } + + +FXbool GMDatabaseSource::hasCurrentTrack(GMSource * src) const { + if (src==this) return true; + return false; + } + +FXbool GMDatabaseSource::findCurrent(GMTrackList * list,GMSource * src) { + GMDatabaseSource * db = dynamic_cast(src); + if (db && db->getCurrentTrack()!=-1 ) + return GMSource::findCurrent(list,db); + return false; + } + + +FXbool GMDatabaseSource::findCurrentArtist(GMList * list,GMSource * src) { + GMDatabaseSource * dbs = dynamic_cast(src); + if (dbs && dbs->getCurrentTrack()!=-1 ){ + FXint artist,album; + db->getTrackAssociation(dbs->getCurrentTrack(),artist,album); + if (artist==-1) return false; + for (FXint i=0;igetNumItems();i++){ + if (artist==(FXint)(FXival)list->getItemData(i)){ + list->selectItem(i); + list->makeItemVisible(i); + return true; + } + } + } + return false; + } + + +FXbool GMDatabaseSource::findCurrentAlbum(GMList * list,GMSource * src) { + GMDatabaseSource * dbs = dynamic_cast(src); + if (dbs && dbs->getCurrentTrack()!=-1 ){ + FXint artist=-1,album=-1,i,j; + GMAlbumListItem * item=NULL; + db->getTrackAssociation(dbs->getCurrentTrack(),artist,album); + if (album==-1) return false; + for (i=0;igetNumItems();i++){ + item = dynamic_cast(list->getItem(i)); + if (!item) continue; + if (album==(FXint)(FXival)list->getItemData(i)){ + list->selectItem(i); + list->makeItemVisible(i); + return true; + } + for (j=0;jalbums.no();j++){ + if (album==item->albums[j]){ + list->selectItem(i); + list->makeItemVisible(i); + return true; + } + } + } + } + return false; + } + + +FXIcon * GMDatabaseSource::getAlbumIcon(FXint aid,FXbool cacheonly){ + FXIconSource src(FXApp::instance()); + GMCover * cover=NULL; + FXIcon * albumicon=NULL; + FXString path; + albumicon = (FXIcon*)albumicons.find((void*)(FXival)aid); + if (albumicon==NULL && !cacheonly && db->getAlbumTrack(aid,path)) { + cover = GMCover::fromTag(path,48); + if (cover==NULL) { + cover = GMCover::fromPath(FXPath::directory(path),48); + } + if (cover) { + albumicon = GMCover::toIcon(cover); + albumicon->create(); + albumicons.insert((void*)(FXival)aid,(void*)albumicon); + } + } + return albumicon; + } + +FXIcon * GMDatabaseSource::getAlbumIcon(){ + return getAlbumIcon(db->getTrackAlbum(current_track),false); + } + + +FXbool GMDatabaseSource::hasTrack(const FXString & mrl,FXint & id) { + return db->hasTrack(mrl,id); + } + + +void GMDatabaseSource::clearAlbumIconCache() { + FXIcon * albumicon; +#if FOXVERSION < FXVERSION(1,7,0) + for (FXint i=0;ilistPlaylists(playlists)) { + for (FXint i=0;igetNumTracks(); + } + +FXint GMDatabaseSource::getNumStreams() const{ + return db->getNumStreams(); + } + + +FXString GMDatabaseSource::getTrackFilename(FXint id) const{ + return db->getTrackFilename(id); + } + +FXbool GMDatabaseSource::getTrack(GMTrack & info) const{ + return db->getTrack(current_track,info); + } + +FXbool GMDatabaseSource::genre_context_menu(FXMenuPane * pane) { + //new FXMenuCommand(pane,fxtr("Edit…\tF2\tEdit Genre."),GMIconTheme::instance()->icon_edit,this,GMDatabaseSource::ID_EDIT_GENRE); +// new FXMenuCommand(pane,"Export" … "\t\tCopy associated tracks to destination.",GMIconTheme::instance()->icon_export,this,ID_EXPORT_GENRE); +// new FXMenuSeparator(pane); + new GMMenuCommand(pane,fxtr("Remove…\tDel\tRemove Genre from Library."),GMIconTheme::instance()->icon_delete,this,GMSource::ID_DELETE_GENRE); + return true; + } + +FXbool GMDatabaseSource::artist_context_menu(FXMenuPane * pane){ + //new FXMenuCommand(pane,fxtr("Edit…\tF2\tEdit Artist."),GMIconTheme::instance()->icon_edit,this,GMDatabaseSource::ID_EDIT_ARTIST); + new GMMenuCommand(pane,fxtr("Copy\tCtrl-C\tCopy associated tracks to the clipboard."),GMIconTheme::instance()->icon_copy,this,ID_COPY_ARTIST); +// new FXMenuCommand(pane,"Export" … "\t\tCopy associated tracks to destination.",GMIconTheme::instance()->icon_export,this,ID_EXPORT_ARTIST); + new FXMenuSeparator(pane); + new GMMenuCommand(pane,fxtr("Remove…\tDel\tRemove associated tracks from library."),GMIconTheme::instance()->icon_delete,this,GMSource::ID_DELETE_ARTIST); + return true; + } + +FXbool GMDatabaseSource::album_context_menu(FXMenuPane * pane){ + //new FXMenuCommand(pane,fxtr("Edit…\tF2\tEdit Album."),GMIconTheme::instance()->icon_edit,this,GMDatabaseSource::ID_EDIT_ALBUM); + new GMMenuCommand(pane,fxtr("Copy\tCtrl-C\tCopy associated tracks to the clipboard."),GMIconTheme::instance()->icon_copy,this,ID_COPY_ALBUM); +// new FXMenuCommand(pane,"Export" … "\t\tCopy associated tracks to destination.",GMIconTheme::instance()->icon_export,this,ID_EXPORT_ALBUM); + new FXMenuSeparator(pane); + new GMMenuCommand(pane,fxtr("Remove…\tDel\tRemove associated tracks from library."),GMIconTheme::instance()->icon_delete,this,GMSource::ID_DELETE_ALBUM); + return true; + } + +FXbool GMDatabaseSource::track_context_menu(FXMenuPane * pane){ + new GMMenuCommand(pane,fxtr("Edit…\tF2\tEdit Track Information."),GMIconTheme::instance()->icon_edit,this,GMDatabaseSource::ID_EDIT_TRACK); + new GMMenuCommand(pane,fxtr("Copy\tCtrl-C\tCopy track(s) to clipboard."),GMIconTheme::instance()->icon_copy,this,ID_COPY_TRACK); +// new FXMenuCommand(pane,"Export" … "\t\tCopy tracks to destination.",GMIconTheme::instance()->icon_export,this,ID_EXPORT_TRACK); + new FXMenuSeparator(pane); + if (GMPlayerManager::instance()->getTrackView()->numTrackSelected()==1) + new GMMenuCommand(pane,fxtr("Open Folder Location\t\tOpen Folder Location."),NULL,this,ID_OPEN_FOLDER); + + +// if (GMPlayerManager::instance()->getTrackView()->numTrackSelected()==1) +// new FXMenuCommand(pane,"Copy Folder Location\t\tCopy Folder Location to clipboard.",GMIconTheme::instance()->icon_copy,this,ID_COPY_LOCATION); + new GMMenuCommand(pane,fxtr("Remove…\tDel\tRemove track(s) from library."),GMIconTheme::instance()->icon_delete,this,GMSource::ID_DELETE_TRACK); + +/* + if (getCurrentSourceType()==SOURCE_PLAYLIST && !browserframe->shown()) { + new FXMenuCommand(&pane,"Remove from Playlist\t\tRemove track(s) from playlist.",icon_delete,this,ID_PLAYLIST_DEL_TRACK); + new FXMenuSeparator(&pane); + new FXMenuCommand(&pane,tr("Reorder Playlist"),NULL,this,ID_REORDER_PLAYLIST); + } +*/ + return true; + } + +FXbool GMDatabaseSource::source_context_menu(FXMenuPane * pane){ +// new FXMenuCommand(pane,fxtr("Import Folder…\tCtrl-O\tImport Music from folder into Library"),GMIconTheme::instance()->icon_import,GMPlayerManager::instance()->getMainWindow(),GMWindow::ID_IMPORT_DIRS); + new GMMenuCommand(pane,fxtr("New Play List…\t\tCreate a new play list."),GMIconTheme::instance()->icon_playlist,this,GMDatabaseSource::ID_NEW_PLAYLIST); + new FXMenuSeparator(pane); + new GMMenuCommand(pane,fxtr("Export…"),GMIconTheme::instance()->icon_export,this,GMDatabaseSource::ID_EXPORT); + new GMMenuCommand(pane,fxtr("Information…\t\tLibrary Statistics"),GMIconTheme::instance()->icon_info,this,GMDatabaseSource::ID_INFO); + new FXMenuSeparator(pane); + new GMMenuCommand(pane,fxtr("Remove All Tracks\t\tRemove all tracks from the library"),GMIconTheme::instance()->icon_delete,this,GMDatabaseSource::ID_CLEAR); + return true; + } + + + +FXbool GMDatabaseSource::dnd_source_accepts(FXDragType*types,FXuint ntypes){ + if (FXApp::instance()->getDragWindow()) return false; + for (FXuint i=0;idatabase()->execute("DROP VIEW IF EXISTS filtered;"); + if (keywords.no() && filtermask) { + FXString query = "CREATE TEMP VIEW filtered AS SELECT tracks.id as track ,albumartists.id as artist ,albums.id as album ,genre FROM tracks,artists AS trackartists, artists AS albumartists,albums,genres WHERE tracks.album == albums.id AND albumartists.id == albums.artist AND trackartists.id == tracks.artist AND tracks.genre == genres.id "; + if (getPlayList()!=-1) { + query+=" AND tracks.id IN (SELECT DISTINCT(track) FROM playlist_tracks WHERE playlist == " + GMStringVal(getPlayList()) + ") "; + } + for (FXint i=0;idatabase()->execute(query); +#ifdef DEBUG + FXlong end = fxgetticks(); + fxmessage("setFilter(): %lld\n",end-start); +#endif + hasfilter=true; + } + else { +#ifdef DEBUG + fxmessage("No Filter\n"); +#endif + hasfilter=false; + } + filterowner=this; + return true; + } + +FXbool GMDatabaseSource::listGenres(GMList * list,FXIcon * icon) { + FXint id; + const FXchar * name; + GMQuery q; + FXString query; + +#ifdef DEBUG + FXlong start = fxgetticks(); +#endif + + try { + if (getPlayList()==-1) { + if (!hasFilter()) { + while(db->list_genres.execute()) { + db->list_genres.getResult(0,id); + name = db->list_genres.getResult(1); + list->appendItem(name,icon,(void*)(FXival)id); + } + db->list_genres.reset(); +#ifdef DEBUG + FXlong end = fxgetticks(); + fxmessage("listGenres(): %30lld\n",end-start); +#endif + return true; + } + else { + query = "SELECT genres.id,genres.name FROM genres WHERE id IN (SELECT genre FROM filtered); "; + } + } + else { + if (!hasFilter()) { + query = "SELECT id,name " + "FROM genres " + "WHERE id IN ( " + "SELECT DISTINCT(genre) " + "FROM tracks,playlist_tracks " + "WHERE id == playlist_tracks.track AND playlist_tracks.playlist == " + GMStringVal(getPlayList()) + + ");"; + } + else { + query = "SELECT genres.id,genres.name FROM genres WHERE id IN (SELECT genre FROM filtered); "; + } + } + + q.compile(db->database(),query); + while( q.execute()){ + q.getResult(0,id); + name=q.getResult(1); + list->appendItem(name,icon,(void*)(FXival)id); + } + q.reset(); + + } + catch(FXCompileException &){ + list->clearItems(); + return false; + } + catch(FXExecuteException &){ + list->clearItems(); + return false; + } +#ifdef DEBUG + FXlong end = fxgetticks(); + fxmessage("listGenres(): %30lld\n",end-start); +#endif + return true; + } + + +FXbool GMDatabaseSource::listArtists(GMList * list,FXIcon * icon,const FXIntList & genrelist){ + GMListItem * item; + const FXchar * name; + FXint id; + GMQuery q; + FXString query; + FXString filterquery; +#ifdef DEBUG + FXlong start = fxgetticks(); +#endif + + try { + if (!hasFilter()){ + if (genrelist.no()==0) { + if (getPlayList()==-1) { + query = "SELECT id,name FROM artists WHERE id IN (SELECT DISTINCT(artist) FROM albums);"; + } + else{ + query = "SELECT DISTINCT(artists.id), artists.name " + "FROM artists,albums " + "WHERE albums.artist == artists.id AND albums.id IN ( " + "SELECT DISTINCT(album) FROM tracks WHERE id IN ( " + "SELECT DISTINCT(track) FROM playlist_tracks WHERE playlist == " + GMStringVal(getPlayList()) + "));"; + } + } + else { + if (getPlayList()==-1) { + if (genrelist.no()>1) { + query = "SELECT id, name FROM artists " + "WHERE id IN ( " + "SELECT DISTINCT(artist) " + "FROM albums WHERE id IN ( " + "SELECT DISTINCT(album) FROM tracks "; + + query+=" WHERE genre IN ( " + GMStringVal(genrelist[0]); + for (FXint i=1;i1) { + query = "SELECT DISTINCT(artists.id), artists.name " + "FROM artists,albums " + "WHERE albums.artist == artists.id AND albums.id IN ( " + "SELECT DISTINCT(album) " + "FROM tracks "; + + query+=" WHERE genre IN ( " + GMStringVal(genrelist[0]); + for (FXint i=1;i1) { + query+=" WHERE genre IN ( " + GMStringVal(genrelist[0]); + for (FXint i=1;idatabase(),query); + while( q.execute()){ + q.getResult(0,id); + name=q.getResult(1); + item = new GMListItem(name,icon,(void*)(FXival)id); + item->setDraggable(true); + list->appendItem(item); + } + q.reset(); + } + catch(FXCompileException &){ + list->clearItems(); + return false; + } + catch(FXExecuteException &){ + list->clearItems(); + return false; + } +#ifdef DEBUG + FXlong end = fxgetticks(); + fxmessage("listArtist(): %30lld\n",end-start); +#endif + return true; + } + + +FXbool GMDatabaseSource::listAlbums(GMList * list,FXIcon * icon,const FXIntList & artistlist,const FXIntList & genrelist){ + const FXchar * c_name=NULL; + FXint id; + FXint year; + FXString name; + FXString query; + FXIcon * albumicon=NULL; + +#ifdef DEBUG + FXlong start = fxgetticks(); +#endif + + GMAlbumListItem * item=NULL; + GMQuery q; + try { + if (hasFilter()){ + query = "SELECT albums.id,albums.name,albums.year FROM albums WHERE id IN (SELECT album FROM filtered "; + if (artistlist.no()>1) { + query+=" WHERE artist IN ( " + GMStringVal(artistlist[0]); + for (FXint i=1;i1) { + query+=" AND genre IN ( " + GMStringVal(genrelist[0]); + for (FXint i=1;i1) { + query+=" AND genre IN ( " + GMStringVal(genrelist[0]); + for (FXint i=1;i1) { + query+=" WHERE genre IN ( " + GMStringVal(genrelist[0]); + for (FXint i=1;i1) { + query+=" WHERE albums.artist IN ( " + GMStringVal(artistlist[0]); + for (FXint i=1;i=0) + query+=",playlist_tracks"; + +// if (artistlist.no() || !filter.empty()) +// query+=" WHERE album_artist.album == tracks.album AND album_artist.album == albums.id"; +// else + query+=" WHERE albums.id == tracks.album"; + + if (getPlayList()>=0) { + query+=" AND playlist_tracks.track == tracks.id"; + query+=" AND playlist_tracks.playlist == "+GMStringVal(getPlayList()); + } + + if (genrelist.no()) { + if (genrelist.no()>1) { + query+=" AND genre IN ( " + GMStringVal(genrelist[0]); + for (FXint i=1;i1) { + query+=" AND albums.artist IN ( " + GMStringVal(artistlist[0]); + for (FXint i=1;idatabase(),query); + while(q.execute()){ + q.getResult(0,id); + c_name = q.getResult(1); + q.getResult(2,year); + if (c_name!=NULL && item && c_name==name) { + FXASSERT(item); + item->albums.append(id); + } + else { + name=c_name; + if (GMPlayerManager::instance()->getPreferences().gui_show_albumcovers){ + albumicon = getAlbumIcon(id,true); + if (albumicon) + item = new GMAlbumListItem(c_name,albumicon,id,year); + else + item = new GMAlbumListItem(c_name,icon,id,year); + } + else { + item = new GMAlbumListItem(c_name,icon,id,year); + } + list->appendItem(item); + } + } + } + catch(FXCompileException &){ + list->clearItems(); + return false; + } + catch(FXExecuteException &){ + list->clearItems(); + return false; + } +#ifdef DEBUG + FXlong end = fxgetticks(); + fxmessage("listAlbums(): %30lld\n",end-start); +#endif + return true; + } + + +FXbool GMDatabaseSource::listTracks(GMTrackList * tracklist,const FXIntList & albumlist,const FXIntList & genrelist){ + GMQuery q; + FXString query; + const FXchar * c_artist; + const FXchar * c_albumname; + const FXchar * c_albumartist; + const FXchar * c_title; + const FXchar * c_genre; + FXint time; + FXuint no; + FXint id; + FXint queue=1; + FXint year; + FXint trackyear; + GMDBTrackItem * item; + + GMDBTrackItem::max_queue=0; + GMDBTrackItem::max_trackno=0; + GMDBTrackItem::max_time=0; + +#ifdef DEBUG + FXlong start = fxgetticks(); +#endif + + try { + + if (getPlayList()>=0) { + query = "SELECT tracks.id,title,time,no,tracks.year,genres.name,a1.name,a2.name,albums.name,albums.year, playlist_tracks.queue " + "FROM tracks, genres, albums, artists AS a1, artists AS a2, playlist_tracks "; + } + else { + query = "SELECT tracks.id,title,time,no,tracks.year,genres.name,a1.name,a2.name,albums.name,albums.year " + "FROM tracks, genres, albums, artists AS a1, artists AS a2 "; + } + + query += " WHERE genres.id == tracks.genre AND " + "a1.id == albums.artist AND " + "a2.id == tracks.artist AND " + "tracks.album == albums.id"; + + if (genrelist.no()) { + if (genrelist.no()>1) { + query+=" AND genre IN ( " + GMStringVal(genrelist[0]); + for (FXint i=1;i=0) { + query+=" AND playlist_tracks.track == tracks.id"; + if (!hasFilter()) query+=" AND playlist_tracks.playlist == "+GMStringVal(getPlayList()); + } + + if (albumlist.no()) { + if (albumlist.no()>1) { + query+=" AND tracks.album IN ( "; + query+=GMStringVal(albumlist[0]); + + for (FXint i=1;i=0) + query+=" ORDER BY playlist_tracks.queue;"; + else + query+=";"; + + //fxmessage("query: %s\n",query.text()); + q.compile(db->database(),query); + + if (getPlayList()>=0) { + while(q.execute()){ + q.getResult(0,id); + c_title = q.getResult(1); + q.getResult(2,time); + q.getResult(3,no); + q.getResult(4,year); + c_genre = q.getResult(5); + c_albumartist = q.getResult(6); + c_artist = q.getResult(7); + c_albumname = q.getResult(8); + q.getResult(9,trackyear); + q.getResult(10,queue); + + GMDBTrackItem::max_trackno=FXMAX(GMDBTrackItem::max_digits(GMTRACKNO(no)),GMDBTrackItem::max_trackno); + GMDBTrackItem::max_queue=FXMAX(GMDBTrackItem::max_digits(queue),GMDBTrackItem::max_queue); + item = new GMDBTrackItem(id,c_title,c_artist,c_albumartist,c_albumname,c_genre,time,no,queue,(FXushort)year,(FXushort)trackyear); + tracklist->appendItem(item); + } + } + else { + while(q.execute()){ + q.getResult(0,id); + c_title = q.getResult(1); + q.getResult(2,time); + q.getResult(3,no); + q.getResult(4,year); + c_genre = q.getResult(5); + c_albumartist = q.getResult(6); + c_artist = q.getResult(7); + c_albumname = q.getResult(8); + q.getResult(9,trackyear); + + GMDBTrackItem::max_trackno=FXMAX(GMDBTrackItem::max_digits(GMTRACKNO(no)),GMDBTrackItem::max_trackno); + item = new GMDBTrackItem(id,c_title,c_artist,c_albumartist,c_albumname,c_genre,time,no,queue++,(FXushort)year,(FXushort)trackyear); + tracklist->appendItem(item); + } + } + GMDBTrackItem::max_trackno = tracklist->getFont()->getTextWidth(FXString('8',GMDBTrackItem::max_trackno)); + GMDBTrackItem::max_queue = tracklist->getFont()->getTextWidth(FXString('8',GMDBTrackItem::max_digits(queue))); + GMDBTrackItem::max_time = tracklist->getFont()->getTextWidth("88:88",5); + } + catch(FXCompileException &){ + tracklist->clearItems(); + return false; + } + catch(FXExecuteException &){ + tracklist->clearItems(); + return false; + } +#ifdef DEBUG + FXlong end = fxgetticks(); + fxmessage("listTracks(): %30lld\n",end-start); +#endif + return true; + } + + +static const FXchar defaults_section[] = "dialog defaults"; + + +class GMEditTrackDialog : public FXDialogBox { +FXDECLARE(GMEditTrackDialog) +protected: + GMTrackDatabase * db; + FXuint samemask; + FXIntList tracks; + GMTrack info; +public: + GMComboBox * trackartistbox; + GMComboBox * albumartistbox; + GMComboBox * genrebox; + GMComboBox * albumbox; + FXTextField * yearfield; + FXSpinner * discspinner; + FXTextField * discfield; + FXTextField * titlefield; + FXCheckButton * updatetags; + FXCheckButton * updatefilename; + FXCheckButton * autonumber; + FXSpinner * autonumberoffset; + FXSpinner * trackspinner; +protected: + GMEditTrackDialog(){} +private: + GMEditTrackDialog(const GMEditTrackDialog&); + GMEditTrackDialog &operator=(const GMEditTrackDialog&); +public: + enum { + ID_FILENAME_TEMPLATE=FXDialogBox::ID_LAST + }; +protected: + enum { + SAME_ALBUM =0x1, + SAME_ARTIST =0x2, + SAME_ALBUMARTIST=0x4, + SAME_GENRE =0x8, + SAME_YEAR =0x10, + SAME_DISC =0x20, + }; + +public: + long onCmdAccept(FXObject*,FXSelector,void*); + long onCmdFilenameTemplate(FXObject*,FXSelector,void*); +public: + GMEditTrackDialog(FXWindow *,GMTrackDatabase*); + virtual ~GMEditTrackDialog(); + }; + + + + +FXDEFMAP(GMEditTrackDialog) GMEditTrackDialogMap[]={ + FXMAPFUNC(SEL_COMMAND,GMEditTrackDialog::ID_FILENAME_TEMPLATE,GMEditTrackDialog::onCmdFilenameTemplate), + FXMAPFUNC(SEL_COMMAND,GMEditTrackDialog::ID_ACCEPT,GMEditTrackDialog::onCmdAccept) + }; + +FXIMPLEMENT(GMEditTrackDialog,FXDialogBox,GMEditTrackDialogMap,ARRAYNUMBER(GMEditTrackDialogMap)); + + +static const FXchar update_tags_key[] = "track-update-tags"; +static const FXchar update_filenames_key[] = "track-update-filenames"; + + + +GMEditTrackDialog::GMEditTrackDialog(FXWindow*p,GMTrackDatabase * d) : FXDialogBox(p,FXString::null,DECOR_TITLE|DECOR_BORDER|DECOR_RESIZE,0,0,0,0,0,0,0,0,0,0), db(d) { + FXPacker * main=NULL; + FXTextField * textfield = NULL; + FXHorizontalFrame * hframe = NULL; + GMTabBook * tabbook = NULL; + FXMatrix * matrix = NULL; + GMTabFrame * tabframe = NULL; + + titlefield=NULL; + discfield=NULL; + discspinner=NULL; + + + setTitle(tr("Edit Track Information")); + FXHorizontalFrame *closebox=new FXHorizontalFrame(this,LAYOUT_SIDE_BOTTOM|LAYOUT_RIGHT|PACK_UNIFORM_WIDTH,0,0,0,0); + new GMButton(closebox,tr("&Cancel"),NULL,this,FXDialogBox::ID_CANCEL,BUTTON_DEFAULT|FRAME_RAISED|FRAME_THICK,0,0,0,0, 15,15); + new GMButton(closebox,tr("&Save"),NULL,this,FXDialogBox::ID_ACCEPT,BUTTON_INITIAL|BUTTON_DEFAULT|FRAME_RAISED|FRAME_THICK,0,0,0,0, 15,15); + + GMTrack other; + GMTag::Properties prop; + + + GMPlayerManager::instance()->getTrackView()->getSelectedTracks(tracks); + + db->getTrack(tracks[0],info); + if (tracks.no()==1) + GMTag::properties(info.mrl,prop); + + + samemask=SAME_ALBUM|SAME_ARTIST|SAME_ALBUMARTIST|SAME_GENRE|SAME_YEAR|SAME_DISC; + + if (tracks.no()>1) { + for (FXint i=1;igetTrack(tracks[i],other); + if (other.album!=info.album) samemask&=~SAME_ALBUM; + if (other.artist!=info.artist) samemask&=~SAME_ARTIST; + if (other.album_artist!=info.album_artist) samemask&=~SAME_ALBUMARTIST; + if (other.genre!=info.genre) samemask&=~SAME_GENRE; + if (other.year!=info.year) samemask&=~SAME_YEAR; + if (GMDISCNO(other.no)!=GMDISCNO(info.no)) samemask&=~SAME_DISC; + } + } + + + if (tracks.no()==1) { /* only show spinner when one track is selected */ + main = new FXPacker(this,LAYOUT_FILL_X|LAYOUT_FILL_Y,0,0,0,0,0,0,0,0); + } + else { + new FXSeparator(this,SEPARATOR_GROOVE|LAYOUT_FILL_X|LAYOUT_SIDE_BOTTOM); + main = new FXPacker(this,LAYOUT_FILL_X|LAYOUT_FILL_Y,0,0,0,0,5,5,5,5); + } + + if (tracks.no()==1) { /* only show spinner when one track is selected */ + + tabbook = new GMTabBook(main,NULL,0,LAYOUT_FILL_X|LAYOUT_FILL_Y); + + new GMTabItem(tabbook,tr("&Tag"),NULL,TAB_TOP_NORMAL,0,0,0,0,5,5); + tabframe = new GMTabFrame(tabbook); + + FXMatrix * tagmatrix = new FXMatrix(tabframe,2,LAYOUT_FILL_X|MATRIX_BY_COLUMNS,0,0,0,0,10,10,10,10); + + new GMTabItem(tabbook,tr("&Properties"),NULL,TAB_TOP_NORMAL,0,0,0,0,5,5); + tabframe = new GMTabFrame(tabbook); + + matrix = new FXMatrix(tabframe,2,LAYOUT_FILL_X|MATRIX_BY_COLUMNS,0,0,0,0,10,10,10,10); + + new FXLabel(matrix,tr("Filename"),NULL,LABEL_NORMAL|LAYOUT_RIGHT|LAYOUT_CENTER_Y); + textfield = new GMTextField(matrix,30,NULL,0,LAYOUT_FILL_X|LAYOUT_FILL_COLUMN|FRAME_SUNKEN|FRAME_THICK|TEXTFIELD_READONLY); + textfield->setText(info.mrl); + + new FXLabel(matrix,tr("Type"),NULL,LABEL_NORMAL|LAYOUT_RIGHT|LAYOUT_CENTER_Y); + textfield = new GMTextField(matrix,20,NULL,0,LAYOUT_FILL_X|LAYOUT_FILL_COLUMN|FRAME_SUNKEN|FRAME_THICK|TEXTFIELD_READONLY); + textfield->setText(FXPath::extension(info.mrl).upper()); + + new FXLabel(matrix,tr("Size"),NULL,LABEL_NORMAL|LAYOUT_RIGHT|LAYOUT_CENTER_Y); + textfield = new GMTextField(matrix,20,NULL,0,LAYOUT_FILL_X|LAYOUT_FILL_COLUMN|FRAME_SUNKEN|FRAME_THICK|TEXTFIELD_READONLY); +#if defined(__LP64__) || defined(_LP64) || (_MIPS_SZLONG == 64) || (__WORDSIZE == 64) + textfield->setText(GMStringFormat("%'ld",FXStat::size(info.mrl))); +#else + textfield->setText(GMStringFormat("%'lld",FXStat::size(info.mrl))); +#endif + + new FXLabel(matrix,tr("Bitrate"),NULL,LABEL_NORMAL|LAYOUT_RIGHT|LAYOUT_CENTER_Y); + textfield = new GMTextField(matrix,20,NULL,0,LAYOUT_FILL_X|LAYOUT_FILL_COLUMN|FRAME_SUNKEN|FRAME_THICK|TEXTFIELD_READONLY); + textfield->setText(GMStringFormat("%dkbs",prop.bitrate)); + + new FXLabel(matrix,tr("Samplerate"),NULL,LABEL_NORMAL|LAYOUT_RIGHT|LAYOUT_CENTER_Y); + textfield = new GMTextField(matrix,20,NULL,0,LAYOUT_FILL_X|LAYOUT_FILL_COLUMN|FRAME_SUNKEN|FRAME_THICK|TEXTFIELD_READONLY); + textfield->setText(GMStringFormat("%dHz",prop.samplerate)); + + new FXLabel(matrix,tr("Channels"),NULL,LABEL_NORMAL|LAYOUT_RIGHT|LAYOUT_CENTER_Y); + textfield = new GMTextField(matrix,20,NULL,0,LAYOUT_FILL_X|LAYOUT_FILL_COLUMN|FRAME_SUNKEN|FRAME_THICK|TEXTFIELD_READONLY); + textfield->setText(GMStringFormat("%d",prop.channels)); + + matrix = tagmatrix; + +// new FXFrame(matrix,FRAME_NONE,0,0,0,0,0,0,3); +// new FXFrame(matrix,FRAME_NONE|LAYOUT_FILL_COLUMN,0,0,0,0,0,0,3); + + new FXLabel(matrix,tr("Track"),NULL,LABEL_NORMAL|LAYOUT_RIGHT|LAYOUT_CENTER_Y); + hframe = new FXHorizontalFrame(matrix,LAYOUT_FILL_X|LAYOUT_FILL_COLUMN,0,0,0,0,0,0,0,0); + + trackspinner = new GMSpinner(hframe,4,NULL,0,FRAME_SUNKEN|FRAME_THICK|LAYOUT_LEFT); + trackspinner->setRange(0,1000); + + new FXLabel(hframe,tr("Disc"),NULL,LABEL_NORMAL|LAYOUT_LEFT|LAYOUT_CENTER_Y); + discspinner = new GMSpinner(hframe,3,NULL,0,FRAME_SUNKEN|FRAME_THICK|LAYOUT_LEFT); + discspinner->setRange(0,100); + + yearfield = new GMTextField(hframe,4,NULL,0,FRAME_SUNKEN|FRAME_THICK|LAYOUT_RIGHT|TEXTFIELD_INTEGER|TEXTFIELD_LIMITED); + new FXLabel(hframe,tr("Year"),NULL,LABEL_NORMAL|LAYOUT_CENTER_Y|LAYOUT_RIGHT); + } + else { + matrix = new FXMatrix(main,2,LAYOUT_FILL_X|MATRIX_BY_COLUMNS,0,0,0,0,0,0,0,0); + + if (samemask&SAME_DISC) { + new FXLabel(matrix,tr("Disc"),NULL,LABEL_NORMAL|LAYOUT_RIGHT|LAYOUT_CENTER_Y); + hframe = new FXHorizontalFrame(matrix,LAYOUT_FILL_X|LAYOUT_FILL_COLUMN,0,0,0,0,0,0,0,0); + discspinner = new GMSpinner(hframe,3,NULL,0,FRAME_SUNKEN|FRAME_THICK|LAYOUT_LEFT); + discspinner->setRange(0,100); + } + else { + new FXLabel(matrix,tr("Disc"),NULL,LABEL_NORMAL|LAYOUT_RIGHT|LAYOUT_CENTER_Y); + hframe = new FXHorizontalFrame(matrix,LAYOUT_FILL_X|LAYOUT_FILL_COLUMN,0,0,0,0,0,0,0,0); + discfield = new GMTextField(hframe,3,NULL,0,FRAME_SUNKEN|FRAME_THICK|LAYOUT_LEFT|TEXTFIELD_INTEGER|TEXTFIELD_LIMITED|JUSTIFY_RIGHT); + } + + yearfield = new GMTextField(hframe,4,NULL,0,FRAME_SUNKEN|FRAME_THICK|LAYOUT_RIGHT|TEXTFIELD_INTEGER|TEXTFIELD_LIMITED|JUSTIFY_RIGHT); + new FXLabel(hframe,tr("Year"),NULL,LABEL_NORMAL|LAYOUT_CENTER_Y|LAYOUT_RIGHT); + } + + if (tracks.no()==1) { + new FXLabel(matrix,tr("Title"),NULL,LABEL_NORMAL|LAYOUT_RIGHT|LAYOUT_CENTER_Y); + titlefield = new GMTextField(matrix,20,NULL,0,LAYOUT_FILL_X|LAYOUT_FILL_COLUMN|FRAME_SUNKEN|FRAME_THICK); + } + + new FXLabel(matrix,tr("&Artist"),NULL,LABEL_NORMAL|LAYOUT_RIGHT|LAYOUT_CENTER_Y); + trackartistbox = new GMComboBox(matrix,30,NULL,0,LAYOUT_FILL_X|LAYOUT_FILL_COLUMN|FRAME_THICK|FRAME_SUNKEN); + + new FXLabel(matrix,tr("Album A&rtist"),NULL,LABEL_NORMAL|LAYOUT_RIGHT|LAYOUT_CENTER_Y); + albumartistbox = new GMComboBox(matrix,30,NULL,0,LAYOUT_FILL_X|LAYOUT_FILL_COLUMN|FRAME_THICK|FRAME_SUNKEN); + + new FXLabel(matrix,tr("Album"),NULL,LABEL_NORMAL|LAYOUT_RIGHT|LAYOUT_CENTER_Y); + albumbox = new GMComboBox(matrix,30,NULL,0,LAYOUT_FILL_X|LAYOUT_FILL_COLUMN|FRAME_THICK|FRAME_SUNKEN); + + new FXLabel(matrix,tr("Genre"),NULL,LABEL_NORMAL|LAYOUT_RIGHT|LAYOUT_CENTER_Y); + hframe = new FXHorizontalFrame(matrix,LAYOUT_FILL_X|LAYOUT_FILL_COLUMN,0,0,0,0,0,0,0,0); + + genrebox = new GMComboBox(hframe,20,NULL,0,LAYOUT_FILL_X|FRAME_THICK|FRAME_SUNKEN); + + if (tracks.no()>1 && tracks.no()<=0xFFFF) { + new FXFrame(matrix,FRAME_NONE); + hframe = new FXHorizontalFrame(matrix,LAYOUT_FILL_X|LAYOUT_FILL_COLUMN,0,0,0,0,0,0,0,0); + autonumber = new GMCheckButton(hframe,tr("Auto track number. Offset:"),NULL,0,LAYOUT_FILL_COLUMN|CHECKBUTTON_NORMAL|LAYOUT_CENTER_Y); + autonumberoffset = new GMSpinner(hframe,2,NULL,0,FRAME_SUNKEN|FRAME_THICK|LAYOUT_LEFT); + autonumber->setTarget(autonumberoffset); + autonumber->setSelector(FXWindow::ID_TOGGLEENABLED); + autonumberoffset->disable(); + autonumberoffset->setRange(1,99); + } + + new FXFrame(matrix,FRAME_NONE); + updatetags = new GMCheckButton(matrix,tr("Update Tag in File"),NULL,0,LAYOUT_FILL_COLUMN|CHECKBUTTON_NORMAL); + new FXFrame(matrix,FRAME_NONE); + + hframe = new FXHorizontalFrame(matrix,LAYOUT_FILL_X|LAYOUT_FILL_COLUMN,0,0,0,0,0,0,0,0); + + updatefilename = new GMCheckButton(hframe,fxtr("Update Filename"),NULL,0,LAYOUT_FILL_COLUMN|CHECKBUTTON_NORMAL|LAYOUT_CENTER_Y); + new GMButton(hframe,tr("Set export template…") ,NULL,this,ID_FILENAME_TEMPLATE,FRAME_RAISED,0,0,0,0); + + updatetags->setCheck(getApp()->reg().readBoolEntry(defaults_section,update_tags_key,false)); + updatefilename->setCheck(getApp()->reg().readBoolEntry(defaults_section,update_filenames_key,false)); + + db->listAlbums(albumbox,db->getTrackAlbum(tracks[0])); + albumbox->setSortFunc(album_list_sort); + albumbox->setNumVisible(FXMIN(10,albumbox->getNumItems())); + albumbox->sortItems(); + albumbox->setCurrentItem(-1); + + db->listArtists(trackartistbox); + trackartistbox->setSortFunc(artist_list_sort); + trackartistbox->setNumVisible(FXMIN(10,trackartistbox->getNumItems())); + trackartistbox->sortItems(); + trackartistbox->setCurrentItem(-1); + + db->listArtists(albumartistbox); + albumartistbox->setSortFunc(artist_list_sort); + albumartistbox->setNumVisible(FXMIN(10,albumartistbox->getNumItems())); + albumartistbox->sortItems(); + albumartistbox->setCurrentItem(-1); + + db->listGenres(genrebox); + genrebox->setSortFunc(genre_list_sort); + genrebox->setNumVisible(FXMIN(10,genrebox->getNumItems())); + genrebox->sortItems(); + genrebox->setCurrentItem(-1); + + if (tracks.no()==1) { + trackspinner->setValue(GMTRACKNO(info.no)); + albumbox->setCurrentItem(albumbox->findItem(info.album)); + trackartistbox->setCurrentItem(trackartistbox->findItem(info.artist)); + albumartistbox->setCurrentItem(albumartistbox->findItem(info.album_artist)); + genrebox->setCurrentItem(genrebox->findItem(info.genre)); + yearfield->setText(GMStringVal(info.year)); + titlefield->setText(info.title); + discspinner->setValue(GMDISCNO(info.no)); + } + else { + if (samemask&SAME_ALBUM) albumbox->setCurrentItem(albumbox->findItem(info.album)); + if (samemask&SAME_ARTIST) trackartistbox->setCurrentItem(trackartistbox->findItem(info.artist)); + if (samemask&SAME_ALBUMARTIST) albumartistbox->setCurrentItem(albumartistbox->findItem(info.album_artist)); + if (samemask&SAME_GENRE) genrebox->setCurrentItem(genrebox->findItem(info.genre)); + if (samemask&SAME_YEAR) yearfield->setText(GMStringVal(info.year)); + if (samemask&SAME_DISC) discspinner->setValue(GMDISCNO(info.no)); + } + } + + +GMEditTrackDialog::~GMEditTrackDialog() { + } + +long GMEditTrackDialog::onCmdFilenameTemplate(FXObject*,FXSelector,void*){ + GMFilenameTemplateDialog dialog(this); + dialog.execute(); + return 1; + } + +long GMEditTrackDialog::onCmdAccept(FXObject*sender,FXSelector sel,void*ptr){ + FXbool changed=false; + FXbool sync=false; + FXint i; + FXString field; + FXString altfield; + + FXDialogBox::onCmdAccept(sender,sel,ptr); + + db->beginEdit(); + + /// TITLE + if (tracks.no()==1) { + field=titlefield->getText().trim().simplify(); + if (!field.empty() && info.title!=field){ + db->setTrackTitle(tracks[0],field); + changed=true; + } + } + /// GENRE + field=genrebox->getText().trim().simplify(); + if (( !field.empty()) && ( + ( tracks.no()>1 && ( (!(samemask&SAME_GENRE)) || field!=info.genre )) || + ( tracks.no()==1 && info.genre!=field ) )) { + db->setTrackGenre(tracks,field); + changed=true; + sync=true; + } + + /// ARTIST + field=trackartistbox->getText().trim().simplify(); + if (( !field.empty()) && ( + ( tracks.no()>1 && ( (!(samemask&SAME_ARTIST)) || field!=info.artist )) || + ( tracks.no()==1 && info.artist!=field ) )) { + db->setTrackArtist(tracks,field); + changed=true; + sync=true; + } + + /// YEAR + field=yearfield->getText().trim().simplify(); + if (!field.empty()){ +#if FOXVERSION >= FXVERSION(1,7,12) + FXint year=yearfield->getText().toInt(); +#else + FXint year=FXIntVal(yearfield->getText()); +#endif + if ( ( tracks.no()>1 && ( (!(samemask&SAME_YEAR)) || info.year!=year ) ) || + ( tracks.no()==1 && info.year!=year )) { + db->setTrackYear(tracks,year); + changed=true; + } + } + + /// DISC and TRACK number + if (tracks.no()==1) { + if (GMTRACKNO(info.no)!=trackspinner->getValue() || GMDISCNO(info.no)!=discspinner->getValue() ) { + db->setTrackDiscNumber(tracks[0],GMALBUMNO(discspinner->getValue(),trackspinner->getValue())); + changed=true; + sync=true; + } + } + else { + if (autonumber->getCheck()) { + if (discspinner) { /// all tracks had the same disc number + for (i=0;isetTrackDiscNumber(tracks[i],GMALBUMNO(discspinner->getValue(),autonumberoffset->getValue()+i)); + } + changed=true; + sync=true; + } + else { /// disc field + if (discfield->getText().empty()) { + for (i=0;isetTrackNumber(tracks[i],autonumberoffset->getValue()+i); + } + changed=true; + sync=true; + } + else { + for (i=0;i= FXVERSION(1,7,12) + db->setTrackDiscNumber(tracks[i],GMALBUMNO(discfield->getText().toUInt(),autonumberoffset->getValue()+i)); +#else + db->setTrackDiscNumber(tracks[i],GMALBUMNO(FXUIntVal(discfield->getText()),autonumberoffset->getValue()+i)); +#endif + } + changed=true; + sync=true; + } + } + } + else { + if (discspinner) { + if (GMDISCNO(info.no)!=discspinner->getValue()){ + db->setTrackDisc(tracks,discspinner->getValue()); + changed=true; + sync=true; + } + } + else { // disc field + field=discfield->getText().trim().simplify(); + if (!field.empty()) { +#if FOXVERSION >= FXVERSION(1,7,12) + FXint disc=discfield->getText().toInt(); +#else + FXint disc=FXIntVal(discfield->getText()); +#endif + db->setTrackDisc(tracks,disc); + changed=true; + sync=true; + } + } + } + } + + field=albumartistbox->getText().trim().simplify(); + altfield=albumbox->getText().trim().simplify(); + + /// ALBUM ARTIST + if (( !field.empty()) && ( + ( tracks.no()>1 && ( (!(samemask&SAME_ALBUMARTIST)) || field!=info.album_artist )) || + ( tracks.no()==1 && info.album_artist!=field ) )) { + + if (altfield.empty() && (samemask&SAME_ALBUM)) { + altfield=info.album; + } + db->setTrackAlbumArtist(tracks,field,altfield); + changed=true; + sync=true; + } + else if (( !altfield.empty()) && ( + ( tracks.no()>1 && ( (!(samemask&SAME_ALBUM)) || altfield!=info.album )) || + ( tracks.no()==1 && info.album!=altfield ) )) { + db->setTrackAlbum(tracks,altfield,(samemask&SAME_ALBUMARTIST)); + changed=true; + sync=true; + } + + if (updatetags->getCheck()) { + if (changed || (FXMessageBox::question(GMPlayerManager::instance()->getMainWindow(),MBOX_YES_NO,fxtr("Update Tags?"),fxtr("No tracks were updated.\nWould you still like to write the tags for the selected tracks?"))==MBOX_CLICKED_YES)) { + GMTrack info; + for (i=0;igetTrack(tracks[i],info)) break; + info.saveTag(info.mrl); + db->setTrackImported(tracks[i],FXThread::time()); + } + } + } + + if (updatefilename->getCheck()) { + updateTrackFilenames(db,tracks); + } + + getApp()->reg().writeBoolEntry(defaults_section,update_tags_key,updatetags->getCheck()); + getApp()->reg().writeBoolEntry(defaults_section,update_filenames_key,updatefilename->getCheck()); + + db->endEdit(sync); + GMPlayerManager::instance()->getTrackView()->refresh(); + return 1; + } + + +long GMDatabaseSource::onCmdEditTrack(FXObject*,FXSelector,void*){ + GMEditTrackDialog dialog(GMPlayerManager::instance()->getMainWindow(),db); + dialog.execute(); + return 1; + } + + + + + + + + + + + + +long GMDatabaseSource::onCmdFilenameTemplate(FXObject*,FXSelector,void*){ + GMFilenameTemplateDialog dialog(FXApp::instance()->getActiveWindow()); + dialog.execute(); + return 1; + } + +void GMDatabaseSource::getTrackFilenames(const FXIntList & tracks,FXStringList & files){ + files.no(tracks.no()); + for (int i=0;igetTrackFilename(tracks[i]); + } + } + + +void GMDatabaseSource::removeFiles(const FXStringList & files) { + FXDialogBox dialog(GMPlayerManager::instance()->getMainWindow(),fxtr("Remove Audio Files?"),DECOR_TITLE|DECOR_BORDER|DECOR_RESIZE|DECOR_CLOSE,0,0,600,400,0,0,0,0,0,0); + GMPlayerManager::instance()->getMainWindow()->create_dialog_header(&dialog,fxtr("Remove Audio Files..."),fxtr("The following audio files are going to be removed")); + FXHorizontalFrame *closebox=new FXHorizontalFrame(&dialog,LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X|PACK_UNIFORM_WIDTH,0,0,0,0); + new GMButton(closebox,fxtr("&Remove"),NULL,&dialog,FXDialogBox::ID_ACCEPT,BUTTON_INITIAL|BUTTON_DEFAULT|LAYOUT_RIGHT|FRAME_RAISED|FRAME_THICK,0,0,0,0, 15,15); + new GMButton(closebox,fxtr("&Cancel"),NULL,&dialog,FXDialogBox::ID_CANCEL,BUTTON_DEFAULT|LAYOUT_RIGHT|FRAME_RAISED|FRAME_THICK,0,0,0,0, 15,15); + new FXSeparator(&dialog,SEPARATOR_GROOVE|LAYOUT_FILL_X|LAYOUT_SIDE_BOTTOM); + + FXVerticalFrame * main = new FXVerticalFrame(&dialog,LAYOUT_FILL_X|LAYOUT_FILL_Y); + GMScrollFrame * sunken = new GMScrollFrame(main); + FXList * list = new GMList(sunken,NULL,0,LAYOUT_FILL_X|LAYOUT_FILL_Y); + + for (int i=0;iappendItem(files[i]); + } + if (dialog.execute()) { + for (int i=0;ireg().readStringEntry("Settings","last-export-directory",FXSystem::getHomeDirectory().text()); + FXString title; + FXuint opts=0; + if (getPlayList()==-1) + title=fxtr("Export Main Library"); + else + title=fxtr("Export Play List"); + + GMExportDialog dialog(GMPlayerManager::instance()->getMainWindow(),title); + dialog.setDirectory(searchdir); + dialog.setSelectMode(SELECTFILE_ANY); + dialog.setPatternList(patterns); + dialog.setCurrentPattern(0); +#if FOXVERSION < FXVERSION(1,7,20) + dialog.setMatchMode(FILEMATCH_CASEFOLD); +#else + dialog.setMatchMode(FXPath::CaseFold); + #endif + + dialog.setRelativePath(FXApp::instance()->reg().readBoolEntry("Settings","export-relative-paths",false)); + + + if (dialog.execute()){ + if (FXStat::exists(dialog.getFilename())){ + if (FXMessageBox::question(GMPlayerManager::instance()->getMainWindow(),MBOX_YES_NO,fxtr("Overwrite File?"),fxtr("File already exists. Would you like to overwrite it?"))!=MBOX_CLICKED_YES) + return 1; + } + + if (dialog.getRelativePath()) + opts|=PLAYLIST_OPTIONS_RELATIVE; + + FXApp::instance()->reg().writeStringEntry("Settings","last-export-directory",dialog.getDirectory().text()); + FXApp::instance()->reg().writeBoolEntry("Settings","export-relative-paths",dialog.getRelativePath()); + + FXApp::instance()->beginWaitCursor(); + switch(dialog.getCurrentPattern()){ + case 0: db->exportList(dialog.getFilename(),getPlayList(),PLAYLIST_XSPF,opts); break; + case 1: db->exportList(dialog.getFilename(),getPlayList(),PLAYLIST_PLS,opts); break; + case 2: db->exportList(dialog.getFilename(),getPlayList(),PLAYLIST_M3U_EXTENDED,opts); break; + case 3: db->exportList(dialog.getFilename(),getPlayList(),PLAYLIST_M3U,opts); break; + case 4: db->exportList(dialog.getFilename(),getPlayList(),PLAYLIST_CSV,opts); break; + } + FXApp::instance()->endWaitCursor(); + } + return 1; + } + +long GMDatabaseSource::onUpdExport(FXObject*sender,FXSelector,void*){ + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_ENABLE),NULL); + return 1; + } + + + +long GMDatabaseSource::onCmdExportTracks(FXObject*,FXSelector sel,void*){ + const FXuint labelstyle=LAYOUT_CENTER_Y|LABEL_NORMAL|LAYOUT_RIGHT; + + FXIntList tracks; + if (FXSELID(sel)==ID_EXPORT_TRACK) { + GMPlayerManager::instance()->getTrackView()->getSelectedTracks(tracks); + } + else { + GMPlayerManager::instance()->getTrackView()->getTracks(tracks); + } + if (tracks.no()==0) return 1; + + FXString title; + FXString subtitle; + + switch(FXSELID(sel)){ + case ID_EXPORT_GENRE: title=fxtr("Export Genre"); + subtitle=fxtr("Export tracks with genre to destination directory."); + break; + case ID_EXPORT_ARTIST:title=fxtr("Export Artists"); + subtitle=fxtr("Export tracks from artist to destination directory."); + break; + case ID_EXPORT_ALBUM: title=fxtr("Export Albums"); + subtitle=fxtr("Export tracks from album to destination directory."); + break; + case ID_EXPORT_TRACK: title=fxtr("Export Tracks"); + subtitle=fxtr("Export tracks to destination directory."); + break; + default: FXASSERT(0); break; + } + +// FXTextField * textfield; + FXDialogBox dialog(GMPlayerManager::instance()->getMainWindow(),title,DECOR_TITLE|DECOR_BORDER|DECOR_RESIZE|DECOR_CLOSE,0,0,0,0,0,0,0,0,0,0); + GMPlayerManager::instance()->getMainWindow()->create_dialog_header(&dialog,title,subtitle,NULL); + FXHorizontalFrame *closebox=new FXHorizontalFrame(&dialog,LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X|PACK_UNIFORM_WIDTH,0,0,0,0); + new GMButton(closebox,fxtr("&Export"),NULL,&dialog,FXDialogBox::ID_ACCEPT,BUTTON_INITIAL|BUTTON_DEFAULT|LAYOUT_RIGHT|FRAME_RAISED|FRAME_THICK,0,0,0,0, 15,15); + new GMButton(closebox,fxtr("&Cancel"),NULL,&dialog,FXDialogBox::ID_CANCEL,BUTTON_DEFAULT|LAYOUT_RIGHT|FRAME_RAISED|FRAME_THICK,0,0,0,0, 15,15); + new FXSeparator(&dialog,SEPARATOR_GROOVE|LAYOUT_FILL_X|LAYOUT_SIDE_BOTTOM); + FXVerticalFrame * main = new FXVerticalFrame(&dialog,LAYOUT_FILL_X|LAYOUT_FILL_Y,0,0,0,0,30,20,10,10); + FXMatrix * matrix = new FXMatrix(main,2,MATRIX_BY_COLUMNS|LAYOUT_FILL_X,0,0,0,0,0,0,4,0); +/* + new FXLabel(matrix,"Directory:",NULL,labelstyle); + FXHorizontalFrame * hframe = new FXHorizontalFrame(matrix,LAYOUT_FILL_X|LAYOUT_FILL_COLUMN,0,0,0,0,0,0,0,0); + FXTextField * textfield = new FXTextField(hframe,20,NULL,0,LAYOUT_FILL_X|TEXTFIELD_ENTER_ONLY|FRAME_SUNKEN|FRAME_THICK); + new FXButton(hframe,… "\tSelect Directory",NULL,NULL,0); + +*/ + new FXLabel(matrix,fxtr("Template:"),NULL,labelstyle); + new GMTextField(matrix,20,NULL,0,LAYOUT_FILL_X|TEXTFIELD_ENTER_ONLY|FRAME_SUNKEN|FRAME_THICK|LAYOUT_FILL_COLUMN); + // textfield->setFont(font_fixed); + + new FXLabel(matrix,fxtr("Encoding:"),NULL,labelstyle); + GMListBox * list_codecs = new GMListBox(matrix,NULL,0,FRAME_SUNKEN|FRAME_THICK|LAYOUT_FILL_COLUMN); + for (int i=0;gmcodecnames[i]!=NULL;i++) + list_codecs->appendItem(gmcodecnames[i]); + list_codecs->setNumVisible(9); + + new FXLabel(matrix,fxtr("Options:"),NULL,labelstyle); + new GMCheckButton(matrix,fxtr("Replace spaces with underscores"),NULL,0,LAYOUT_FILL_COLUMN|CHECKBUTTON_NORMAL); + new FXFrame(matrix,FRAME_NONE); + new GMCheckButton(matrix,fxtr("Lower case"),NULL,0,LAYOUT_FILL_COLUMN|CHECKBUTTON_NORMAL); + new FXFrame(matrix,FRAME_NONE); + new GMCheckButton(matrix,fxtr("Lower case extension"),NULL,0,LAYOUT_FILL_COLUMN|CHECKBUTTON_NORMAL); + + if (dialog.execute()){ + } + return 1; + } + +long GMDatabaseSource::onCmdDelete(FXObject*,FXSelector sel,void*){ + FXIntList tracks; + FXIntList selected; + FXStringList files; + if (FXSELID(sel)==ID_DELETE_TRACK) { + GMPlayerManager::instance()->getTrackView()->getSelectedTracks(tracks); + } + else { + GMPlayerManager::instance()->getTrackView()->getTracks(tracks); + } + if (tracks.no()==0) return 1; + + FXString title; + FXString subtitle; + + switch(FXSELID(sel)){ + case ID_DELETE_GENRE: title=fxtr("Remove Genre?"); + subtitle=fxtr("Remove tracks with genre from library?"); + GMPlayerManager::instance()->getTrackView()->getSelectedGenres(selected); + if (selected.no()==0) return 1; + break; + case ID_DELETE_ARTIST:title=fxtr("Remove Artist?"); + subtitle=fxtr("Remove tracks from artist from library?"); + GMPlayerManager::instance()->getTrackView()->getSelectedArtists(selected); + if (selected.no()==0) return 1; + break; + case ID_DELETE_ALBUM: title=fxtr("Remove Album?"); + subtitle=fxtr("Remove tracks from album from library?"); + GMPlayerManager::instance()->getTrackView()->getSelectedAlbums(selected); + if (selected.no()==0) return 1; + break; + case ID_DELETE_TRACK: title=fxtr("Remove Track(s)?"); + subtitle=fxtr("Remove track(s) from library?"); + break; + default: FXASSERT(0); break; + } + + FXDialogBox dialog(GMPlayerManager::instance()->getMainWindow(),title,DECOR_TITLE|DECOR_BORDER|DECOR_RESIZE|DECOR_CLOSE,0,0,0,0,0,0,0,0,0,0); + GMPlayerManager::instance()->getMainWindow()->create_dialog_header(&dialog,title,subtitle,NULL); + FXHorizontalFrame *closebox=new FXHorizontalFrame(&dialog,LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X|PACK_UNIFORM_WIDTH,0,0,0,0); + new GMButton(closebox,fxtr("&Remove"),NULL,&dialog,FXDialogBox::ID_ACCEPT,BUTTON_INITIAL|BUTTON_DEFAULT|LAYOUT_RIGHT|FRAME_RAISED|FRAME_THICK,0,0,0,0, 15,15); + new GMButton(closebox,fxtr("&Cancel"),NULL,&dialog,FXDialogBox::ID_CANCEL,BUTTON_DEFAULT|LAYOUT_RIGHT|FRAME_RAISED|FRAME_THICK,0,0,0,0, 15,15); + new FXSeparator(&dialog,SEPARATOR_GROOVE|LAYOUT_FILL_X|LAYOUT_SIDE_BOTTOM); + FXVerticalFrame * main = new FXVerticalFrame(&dialog,LAYOUT_FILL_X|LAYOUT_FILL_Y,0,0,0,0,30,20,10,10); + FXCheckButton * from_disk = new GMCheckButton(main,fxtr("Remove tracks from disk")); + from_disk->setCheck(FXApp::instance()->reg().readBoolEntry("delete dialog","from-disk",false)); + + if (dialog.execute()){ + + db->beginDelete(); + + if (from_disk->getCheck()) + getTrackFilenames(tracks,files); + + + switch(FXSELID(sel)){ + case ID_DELETE_GENRE: + if (!db->removeGenre(selected[0])) + FXMessageBox::error(GMPlayerManager::instance()->getMainWindow(),MBOX_OK,fxtr("Library Error"),fxtr("Unable to remove genre from the library")); + break; + case ID_DELETE_ARTIST: + if (!db->removeArtist(selected[0])) + FXMessageBox::error(GMPlayerManager::instance()->getMainWindow(),MBOX_OK,fxtr("Library Error"),fxtr("Unable to remove artist from the library")); + break; + case ID_DELETE_ALBUM: + if (!db->removeAlbum(selected[0])) + FXMessageBox::error(GMPlayerManager::instance()->getMainWindow(),MBOX_OK,fxtr("Library Error"),fxtr("Unable to remove album from the library")); + break; + case ID_DELETE_TRACK: + if (!db->removeTracks(tracks)) + FXMessageBox::error(GMPlayerManager::instance()->getMainWindow(),MBOX_OK,fxtr("Library Error"),fxtr("Unable to remove track from the library.")); + break; + default: FXASSERT(0); break; + } + + if (from_disk->getCheck()) + removeFiles(files); + + FXApp::instance()->reg().writeBoolEntry("delete dialog","from-disk",from_disk->getCheck()); + + db->endDelete(); + GMPlayerManager::instance()->getTrackView()->refresh(); + } + return 1; + } + + +long GMDatabaseSource::onCmdCopyArtistAlbum(FXObject*,FXSelector,void*){ + FXDragType types[4]={GMClipboard::trackdatabase,GMClipboard::kdeclipboard,GMClipboard::gnomeclipboard,FXWindow::urilistType}; + GMDatabaseClipboardData * data = new GMDatabaseClipboardData; + if (GMClipboard::instance()->acquire(this,types,4,data)){ + FXApp::instance()->beginWaitCursor(); + data->db=db; + GMPlayerManager::instance()->getTrackView()->getTracks(data->tracks); + FXApp::instance()->endWaitCursor(); + } + else { + delete data; + FXApp::instance()->beep(); + } + return 1; + } + +long GMDatabaseSource::onCmdCopyTrack(FXObject*,FXSelector,void*){ + FXDragType types[4]={GMClipboard::trackdatabase,GMClipboard::kdeclipboard,GMClipboard::gnomeclipboard,FXWindow::urilistType}; + GMDatabaseClipboardData * data = new GMDatabaseClipboardData; + if (GMClipboard::instance()->acquire(this,types,4,data)){ + FXApp::instance()->beginWaitCursor(); + data->db=db; + GMPlayerManager::instance()->getTrackView()->getSelectedTracks(data->tracks); + FXApp::instance()->endWaitCursor(); + } + else { + delete data; + FXApp::instance()->beep(); + } + return 1; + } + + + +long GMDatabaseSource::onCmdRequestArtistAlbum(FXObject*sender,FXSelector,void*ptr){ + FXEvent* event=(FXEvent*)ptr; + FXWindow*window=(FXWindow*)sender; + if(event->target==GMClipboard::urilistType){ + FXStringList filenames; + FXIntList tracks; + FXString uri; + GMPlayerManager::instance()->getTrackView()->getTracks(tracks); + db->getTrackFilenames(tracks,filenames); + gm_convert_filenames_to_uri(filenames,uri); + window->setDNDData(FROM_DRAGNDROP,event->target,uri); + return 1; + } + else if (event->target==GMClipboard::kdeclipboard){ + window->setDNDData(FROM_DRAGNDROP,event->target,"0"); // copy + return 1; + } + return 0; + } + +long GMDatabaseSource::onCmdRequestTrack(FXObject*sender,FXSelector,void*ptr){ + FXEvent* event=(FXEvent*)ptr; + FXWindow*window=(FXWindow*)sender; + if(event->target==GMClipboard::urilistType){ + FXStringList filenames; + FXIntList tracks; + FXString uri; + GMPlayerManager::instance()->getTrackView()->getSelectedTracks(tracks); + db->getTrackFilenames(tracks,filenames); + gm_convert_filenames_to_uri(filenames,uri); + window->setDNDData(FROM_DRAGNDROP,event->target,uri); + return 1; + } + else if (event->target==GMClipboard::kdeclipboard){ + window->setDNDData(FROM_DRAGNDROP,event->target,"0"); // copy + return 1; + } + return 0; + } + + +long GMDatabaseSource::onCmdDrop(FXObject*sender,FXSelector,void*){ + FXWindow * window=(FXWindow*)sender; + FXString files; + FXStringList filelist; +// FXbool from_kde=false; + FXbool from_uri=false; + FXDragType * types; + FXuint ntypes; + + if (getPlayList()==-1 && FXApp::instance()->getDragWindow()) return 0; + + if (window->inquireDNDTypes(FROM_DRAGNDROP,types,ntypes)){ + for (FXuint i=0;igetDragTypeName(types[i]).text()); + +// if (types[i]==GMClipboard::kdeclipboard) from_kde=true; + if (types[i]==FXWindow::urilistType) from_uri=true; + } + +// else if (from_kde && from_uri) { +// } + if (from_uri) { + if (window->getDNDData(FROM_DRAGNDROP,FXWindow::urilistType,files)){ + gm_convert_uri_to_filenames(files,filelist); + } + } + + window->dropFinished(DRAG_ACCEPT); + + if (filelist.no()) { + GMImportDialog dialog(GMPlayerManager::instance()->getMainWindow(),IMPORT_FROMPASTE); + if (dialog.execute()) { + GMPlayerManager::instance()->stop(); + GMImportDatabase searchdialog(GMPlayerManager::instance()->getMainWindow(),filelist,GMPlayerManager::instance()->getPreferences().import,getPlayList(),GMPlayerManager::instance()->getMainWindow()->getThickFont()); + searchdialog.execute(); + GMPlayerManager::instance()->getTrackView()->refresh(); + } + } + else { + FXApp::instance()->beep(); + } + } + return 1; + } + + + +long GMDatabaseSource::onCmdPaste(FXObject*,FXSelector,void*){ + FXString files; + FXStringList filelist; + FXDragType * types; + FXuint num; +// FXbool from_db=false; + FXbool from_kde=false; + FXbool from_gnome=false; + FXbool from_uri=false; + + GMClipboard * clipboard = GMClipboard::instance(); + + if (clipboard->inquireDNDTypes(FROM_CLIPBOARD,types,num)){ + + for (FXuint i=0;ihasClipboard() ) { + return 1; + } + + if (from_gnome) { + if (clipboard->getDNDData(FROM_CLIPBOARD,GMClipboard::gnomeclipboard,files)){ + gm_convert_gnomeclipboard_to_filenames(files,filelist); + } + } + else if (from_kde && from_uri) { + if (clipboard->getDNDData(FROM_CLIPBOARD,GMClipboard::kdeclipboard,files)){ +#ifdef DEBUG + if (files=="1") { + fxmessage("We do not cut files...\n"); + } +#endif + } + if (clipboard->getDNDData(FROM_CLIPBOARD,FXWindow::urilistType,files)){ + gm_convert_uri_to_filenames(files,filelist); + } + } + + if (filelist.no()){ + GMImportDialog dialog(GMPlayerManager::instance()->getMainWindow(),IMPORT_FROMPASTE); + if (dialog.execute()) { + GMPlayerManager::instance()->stop(); + GMImportDatabase searchdialog(GMPlayerManager::instance()->getMainWindow(),filelist,GMPlayerManager::instance()->getPreferences().import,getPlayList(),GMPlayerManager::instance()->getMainWindow()->getThickFont()); + searchdialog.execute(); + GMPlayerManager::instance()->getTrackView()->refresh(); + } + } + else { + FXApp::instance()->beep(); + } + + return 1; + } + return 0; + } + +long GMDatabaseSource::onUpdPaste(FXObject*,FXSelector,void*){ + return 1; + } + + +long GMDatabaseSource::onCmdNewPlayList(FXObject*,FXSelector,void*){ + FXDialogBox dialog(GMPlayerManager::instance()->getMainWindow(),fxtr("Create Playlist"),DECOR_TITLE|DECOR_BORDER|DECOR_RESIZE|DECOR_CLOSE,0,0,0,0,0,0,0,0,0,0); + GMPlayerManager::instance()->getMainWindow()->create_dialog_header(&dialog,fxtr("Create Playlist"),fxtr("Specify name of the new playlist"),NULL); + FXHorizontalFrame *closebox=new FXHorizontalFrame(&dialog,LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X|PACK_UNIFORM_WIDTH,0,0,0,0); + new GMButton(closebox,fxtr("&Create"),NULL,&dialog,FXDialogBox::ID_ACCEPT,BUTTON_INITIAL|BUTTON_DEFAULT|LAYOUT_RIGHT|FRAME_RAISED|FRAME_THICK,0,0,0,0, 15,15); + new GMButton(closebox,fxtr("&Cancel"),NULL,&dialog,FXDialogBox::ID_CANCEL,BUTTON_DEFAULT|LAYOUT_RIGHT|FRAME_RAISED|FRAME_THICK,0,0,0,0, 15,15); + new FXSeparator(&dialog,SEPARATOR_GROOVE|LAYOUT_FILL_X|LAYOUT_SIDE_BOTTOM); + FXVerticalFrame * main = new FXVerticalFrame(&dialog,LAYOUT_FILL_X|LAYOUT_FILL_Y,0,0,0,0,30,20,10,10); + FXMatrix * matrix = new FXMatrix(main,2,LAYOUT_FILL_X|MATRIX_BY_COLUMNS); + new FXLabel(matrix,fxtr("Name"),NULL,LABEL_NORMAL|LAYOUT_RIGHT|LAYOUT_CENTER_Y); + FXTextField * name_field = new GMTextField(matrix,20,&dialog,FXDialogBox::ID_ACCEPT,LAYOUT_FILL_X|LAYOUT_FILL_COLUMN|FRAME_SUNKEN|FRAME_THICK|TEXTFIELD_ENTER_ONLY); + name_field->setText(fxtr("New Playlist")); + name_field->setSelection(0,name_field->getText().length()); + dialog.create(); + gm_focus_and_select(name_field); + if (dialog.execute()) { + FXString label= name_field->getText().trim(); + if (!label.empty()) { + FXint pl; + db->insertPlaylist(label,pl); + GMPlayerManager::instance()->insertSource(new GMPlayListSource(db,pl)); + GMPlayerManager::instance()->getSourceView()->refresh(); + } + } + return 1; + } + +long GMDatabaseSource::onCmdImportPlayList(FXObject*,FXSelector,void*){ + GMImportDialog dialog(GMPlayerManager::instance()->getMainWindow(),IMPORT_FROMFILE|IMPORT_PLAYLIST); + if (dialog.execute()){ + FXString buffer; + FXStringList urls; + if (gm_buffer_file(dialog.getFilename(),buffer)) { + FXString title; + FXString extension = FXPath::extension(dialog.getFilename()); + + if (comparecase(extension,"m3u")==0) + gm_parse_m3u(buffer,urls); + else if (comparecase(extension,"pls")==0) + gm_parse_pls(buffer,urls); + else + gm_parse_xspf(buffer,urls,title); + + if (urls.no()) { + + gm_make_absolute_path(FXPath::directory(dialog.getFilename()),urls); + + title.trim(); + if (title.empty()) title = FXPath::title(dialog.getFilename()); + + FXint pl = GMPlayerManager::instance()->createPlaylist(title); + GMImportDatabase searchdialog(GMPlayerManager::instance()->getMainWindow(),urls,GMPlayerManager::instance()->getPreferences().import,pl,GMPlayerManager::instance()->getMainWindow()->getThickFont()); + searchdialog.execute(); + GMPlayerManager::instance()->getTrackView()->refresh(); + } + } + } + return 1; + } + + +long GMDatabaseSource::onCmdClear(FXObject*,FXSelector,void*){ + FXDialogBox dialog(GMPlayerManager::instance()->getMainWindow(),fxtr("Clear Music Library?"),DECOR_TITLE|DECOR_BORDER|DECOR_RESIZE|DECOR_CLOSE,0,0,0,0,0,0,0,0,0,0); + GMPlayerManager::instance()->getMainWindow()->create_dialog_header(&dialog,fxtr("Clear Music Library?"),fxtr("Remove all tracks from the music library?"),NULL); + FXHorizontalFrame *closebox=new FXHorizontalFrame(&dialog,LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X|PACK_UNIFORM_WIDTH,0,0,0,0); + new GMButton(closebox,fxtr("&Remove All"),NULL,&dialog,FXDialogBox::ID_ACCEPT,BUTTON_INITIAL|BUTTON_DEFAULT|LAYOUT_RIGHT|FRAME_RAISED|FRAME_THICK,0,0,0,0, 15,15); + new GMButton(closebox,fxtr("&Cancel"),NULL,&dialog,FXDialogBox::ID_CANCEL,BUTTON_DEFAULT|LAYOUT_RIGHT|FRAME_RAISED|FRAME_THICK,0,0,0,0, 15,15); + new FXSeparator(&dialog,SEPARATOR_GROOVE|LAYOUT_FILL_X|LAYOUT_SIDE_BOTTOM); + FXVerticalFrame * main = new FXVerticalFrame(&dialog,LAYOUT_FILL_X|LAYOUT_FILL_Y,0,0,0,0,30,20,10,10); + FXCheckButton * playlist_check = new GMCheckButton(main,fxtr("Keep play lists")); + playlist_check->setCheck(FXApp::instance()->reg().readBoolEntry("clear dialog","keep-play-lists",true)); + if (dialog.execute()){ + GMPlayerManager::instance()->stop(); + db->clearTracks(!playlist_check->getCheck()); + GMPlayerManager::instance()->removePlayListSources(); + GMPlayerManager::instance()->getSourceView()->refresh(); + GMPlayerManager::instance()->getTrackView()->refresh(); + FXApp::instance()->reg().writeBoolEntry("clear dialog","keep-play-lists",playlist_check->getCheck()); + } + return 1; + } + + +long GMDatabaseSource::onCmdInfo(FXObject*,FXSelector,void*){ + FXint num_tracks = db->getNumTracks(); + FXint num_albums= db->getNumAlbums(); + FXint num_artists= db->getNumArtists(); + FXint time = db->getTotalTime(); + FXint days = (FXint) floor((double)time/86400.0); + time -= (FXint) (86400.0*days); + FXint hours = (FXint) floor((double)time/3600.0); + time -= (FXint) (3600.0*hours); + FXint minutes = (FXint) floor((double)time/60.0); + time -= (FXint) (60.0*minutes); + FXint seconds = (FXint) floor((double)time); + + FXDialogBox dialog(GMPlayerManager::instance()->getMainWindow(),fxtr("Music Library Information"),DECOR_TITLE|DECOR_BORDER|DECOR_CLOSE,0,0,0,0,0,0,0,0,0,0); + GMPlayerManager::instance()->getMainWindow()->create_dialog_header(&dialog,fxtr("Music Library Information"),fxtr("You music collection consists of…")); + FXHorizontalFrame *closebox=new FXHorizontalFrame(&dialog,LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X|PACK_UNIFORM_WIDTH,0,0,0,0); + new GMButton(closebox,fxtr("&Close"),NULL,&dialog,FXDialogBox::ID_ACCEPT,BUTTON_INITIAL|BUTTON_DEFAULT|LAYOUT_CENTER_X|FRAME_RAISED|FRAME_THICK,0,0,0,0, 15,15); + new FXSeparator(&dialog,SEPARATOR_GROOVE|LAYOUT_FILL_X|LAYOUT_SIDE_BOTTOM); + + + FXVerticalFrame * main = new FXVerticalFrame(&dialog,LAYOUT_FILL_X|LAYOUT_FILL_Y,0,0,0,0,20,10,5,5); + FXMatrix * matrix = new FXMatrix(main,2,LAYOUT_FILL_X|MATRIX_BY_COLUMNS); + FXLabel * label = new FXLabel(matrix,fxtr("Tracks:"),NULL,LABEL_NORMAL|LAYOUT_RIGHT|LAYOUT_CENTER_Y); + + label = new FXLabel(matrix,GMStringVal(num_tracks),NULL,LABEL_NORMAL|JUSTIFY_LEFT); + label->setTextColor(FXRGB(0,0,255)); + + new FXLabel(matrix,fxtr("Artists:"),NULL,LABEL_NORMAL|LAYOUT_RIGHT|LAYOUT_CENTER_Y); + label = new FXLabel(matrix,GMStringVal(num_artists),NULL,LABEL_NORMAL|JUSTIFY_LEFT); + label->setTextColor(FXRGB(0,0,255)); + + new FXLabel(matrix,fxtr("Albums:"),NULL,LABEL_NORMAL|LAYOUT_RIGHT|LAYOUT_CENTER_Y); + label = new FXLabel(matrix,GMStringVal(num_albums),NULL,LABEL_NORMAL|JUSTIFY_LEFT); + label->setTextColor(FXRGB(0,0,255)); + + new FXLabel(matrix,fxtr("Total Time:"),NULL,LABEL_NORMAL|LAYOUT_RIGHT|LAYOUT_CENTER_Y); + FXString duration; + if (days) { + if (days>=2) + duration+=GMStringFormat("%d days",days); + else + duration+=GMStringFormat("%d day",days); + + if (seconds==0 && minutes==0 && hours) duration+="."; else duration+=", "; + } + if (hours) { + if (hours>=2) + duration+=GMStringFormat("%d hours",hours); + else + duration+=GMStringFormat("%d hour",hours); + + if (seconds==0 && minutes==0) duration+="."; else duration+=", "; + } + if (minutes) { + if (minutes>=2) + duration+=GMStringFormat("%d minutes",minutes); + else + duration+=GMStringFormat("%d minute",minutes); + + if (seconds==0) duration+="."; else duration+=", "; + } + if (seconds) { + if (seconds>=2) + duration+=GMStringFormat("%d seconds. ",seconds); + else + duration+=GMStringFormat("%d second.",seconds); + } + new FXLabel(matrix,duration,NULL,LABEL_NORMAL|JUSTIFY_LEFT); + + + dialog.execute(); + return 1; + } + + +long GMDatabaseSource::onCmdTrackPlayed(FXObject*,FXSelector,void*) { + FXTRACE((60,"%s::onCmdTrackPlayed\n",getClassName())); + FXASSERT(current_track>=0); + FXlong timestamp = (FXlong)FXThread::time(); + db->playedTrack(current_track,timestamp); + GMTrack info; + if (getTrack(info) && GMPlayerManager::instance()->getAudioScrobbler()) + GMPlayerManager::instance()->getAudioScrobbler()->submit(timestamp,info); + return 1; + } + +long GMDatabaseSource::onCmdOpenFolder(FXObject*,FXSelector,void*){ + FXIntList tracks; + GMPlayerManager::instance()->getTrackView()->getSelectedTracks(tracks); + if (tracks.no()==1) { + gm_open_folder(FXPath::directory(db->getTrackFilename(tracks[0]))); + } + return 1; + } + + diff --git a/src/GMDatabaseSource.h b/src/GMDatabaseSource.h new file mode 100644 index 0000000..ad7bc5d --- /dev/null +++ b/src/GMDatabaseSource.h @@ -0,0 +1,170 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2007-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#ifndef GMDATABASESOURCE_H +#define GMDATABASESOURCE_H + +#ifndef GMCLIPBOARD_H +#include "GMClipboard.h" +#endif + +class GMSource; +class GMTrackDatabase; + +class GMDatabaseClipboardData : public GMClipboardData { +public: + GMTrackDatabase * db; + FXIntList tracks; +public: + FXbool request(FXDragType target,GMClipboard * clipboard); + + ~GMDatabaseClipboardData() { + db=NULL; + } + }; + +class GMDatabaseSource : public GMSource { +FXDECLARE(GMDatabaseSource) +protected: + FXbool dbowned; + GMTrackDatabase * db; + FXIntList clipboard; + FXHash albumicons; + FXString filter; + FXuint filtermask; + FXbool hasfilter; + static GMDatabaseSource* filterowner; +protected: + GMDatabaseSource(); +private: + GMDatabaseSource(const GMDatabaseSource&); + GMDatabaseSource& operator=(const GMDatabaseSource&); +protected: + void clearAlbumIconCache(); + void removeFiles(const FXStringList & files); + void getTrackFilenames(const FXIntList & tracks,FXStringList & files); + virtual FXint getPlayList() const { return -1; } + FXbool hasFilter() const { return hasfilter; } +public: + enum { + ID_NEW_PLAYLIST = GMSource::ID_LAST, + ID_IMPORT_PLAYLIST, + ID_EXPORT_GENRE, + ID_EXPORT_ARTIST, + ID_EXPORT_ALBUM, + ID_EXPORT_TRACK, + ID_CLEAR, + ID_INFO, + ID_COLUMN_TRACK, + ID_COLUMN_QUEUE, + ID_COLUMN_ARTIST, + ID_COLUMN_ALBUM, + ID_COLUMN_GENRE, + ID_COLUMN_TIME, + ID_LOAD_ALBUM_ICONS, + ID_FILENAME_TEMPLATE, + ID_OPEN_FOLDER, + ID_LAST + }; +public: + long onCmdEditTrack(FXObject*,FXSelector,void*); + long onCmdDelete(FXObject*,FXSelector,void*); + long onCmdCopyArtistAlbum(FXObject*,FXSelector,void*); + long onCmdCopyTrack(FXObject*,FXSelector,void*); + long onCmdRequestArtistAlbum(FXObject*,FXSelector,void*); + long onCmdRequestTrack(FXObject*,FXSelector,void*); + long onCmdExport(FXObject*,FXSelector,void*); + long onUpdExport(FXObject*,FXSelector,void*); + long onCmdExportTracks(FXObject*,FXSelector,void*); + long onCmdDrop(FXObject*,FXSelector,void*); + long onCmdPaste(FXObject*,FXSelector,void*); + long onUpdPaste(FXObject*,FXSelector,void*); + long onCmdNewPlayList(FXObject*,FXSelector,void*); + long onCmdImportPlayList(FXObject*,FXSelector,void*); + long onCmdClear(FXObject*,FXSelector,void*); + long onCmdInfo(FXObject*,FXSelector,void*); + long onCmdTrackPlayed(FXObject*,FXSelector,void*); + long onCmdShowColumn(FXObject*,FXSelector,void*); + long onUpdShowColumn(FXObject*,FXSelector,void*); + long onCmdFilenameTemplate(FXObject*,FXSelector,void*); + long onCmdOpenFolder(FXObject*,FXSelector,void*); +public: + GMDatabaseSource(GMTrackDatabase * db); + + virtual FXbool canFilter() const { return true; } + + virtual void shuffle(GMTrackList*,FXuint) const; + + virtual void configure(GMColumnList&) const; + + virtual FXbool hasCurrentTrack(GMSource * ) const; + + virtual FXbool hasTrack(const FXString & mrl,FXint & id); + + virtual FXbool findCurrent(GMTrackList * tracklist,GMSource * src); + + virtual FXbool findCurrentArtist(GMList * tracklist,GMSource * src); + + virtual FXbool findCurrentAlbum(GMList * tracklist,GMSource * src); + + virtual FXIcon * getAlbumIcon(FXint id,FXbool cacheonly=false); + + virtual FXIcon * getAlbumIcon(); + + void initPlaylists(GMSourceList & sources); + + virtual FXString getName() const { return notr("Music Library"); } + + virtual FXint getNumTracks() const; + + virtual FXint getNumStreams() const; + + virtual FXString getTrackFilename(FXint id) const; + + virtual FXbool getTrack(GMTrack & info) const; + + virtual FXint getType() const { return SOURCE_DATABASE; } + + virtual FXString settingKey() const { return "database"; } + + virtual FXbool setFilter(const FXString&,FXuint); + + virtual FXbool listGenres(GMList * genrelist,FXIcon * icon); + + virtual FXbool listArtists(GMList * artistlist,FXIcon * icon,const FXIntList & genrelist); + + virtual FXbool listAlbums(GMList * albumlist,FXIcon * icon,const FXIntList & artistlist,const FXIntList & genrelist); + + virtual FXbool listTracks(GMTrackList * tracklist,const FXIntList & albumlist,const FXIntList & genrelist); + + virtual FXbool genre_context_menu(FXMenuPane * pane); + + virtual FXbool artist_context_menu(FXMenuPane * pane); + + virtual FXbool album_context_menu(FXMenuPane * pane); + + virtual FXbool track_context_menu(FXMenuPane * pane); + + virtual FXbool source_context_menu(FXMenuPane * pane); + + virtual FXbool dnd_source_accepts(FXDragType*,FXuint); + + virtual ~GMDatabaseSource(); + }; + +#endif diff --git a/src/GMEQDialog.cpp b/src/GMEQDialog.cpp new file mode 100644 index 0000000..57c4c9c --- /dev/null +++ b/src/GMEQDialog.cpp @@ -0,0 +1,440 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#include "gmdefs.h" +#include "icons.h" + +#include +#include + +#include "GMPlayer.h" +#include "GMTrackList.h" +#include "GMList.h" +#include "GMRemote.h" +#include "GMSource.h" +#include "GMPlayerManager.h" +#include "GMTrackDatabase.h" +#include "GMDatabaseSource.h" +#include "GMTrackView.h" +#include "GMSourceView.h" +#include "GMIconTheme.h" +#include "GMEQDialog.h" +#include "GMWindow.h" + + +const FXchar * default_presets[]={ + "Classical,0,0,0,0,0,0,-2.4,-2.4,-2.4,-3.0", + "Club, 0,0,1.2,1.8,1.8,1.8,1.2,0,0,0", + "Dance,3.0,2.1,0.6,0,0,-1.8,-2.4,-2.4,0,0", + "Full Bass,4.2,4.2,4.2,2.4,1.2,-1.2,-2.7,-3.0,-3.3,-3.3", + "Full Treble,-3.0,-3.0,-3.0,-1.5,0.9,3.3,4.8,4.8,4.8,5.1", + "Full Bass+Treble,2.1,1.8,0,-2.4,-1.5,0.6,2.7,3.3,3.6,3.6", + "Laptop/Headphones,1.5,3.0,1.5,-1.2,0,-1.8,-2.4,-2.4,0,0", + "Large Hall,3.0,3.0,1.8,1.8,0,-1.5,-1.5,-1.5,0,0", + "Live,-1.5,0,1.2,1.5,1.8,1.8,1.2,0.9,0.9,0.6", + "Party,2.1,2.1,0,0,0,0,0,0,2.1,2.1", + "Pop,-0.6,1.5,2.1,2.4,1.5,-0.3,-0.9,-0.9,-0.6,-0.6", + "Reggae,0,0,-0.3,-1.8,0,-2.1,-2.1,0,0,0", + "Rock,2.4,1.5,-1.8,-2.4,-1.2,1.2,2.7,3.3,3.3,3.3", + "Soft,1.5,0.6,-0.3,-0.9,-0.3,1.2,2.7,3.0,3.3,3.6", + "Ska,-0.9,-1.5,-1.5,-0.3,1.2,1.8,2.7,3.0,3.3,3.0", + "Soft Rock,1.2,1.2,0.6,-0.3,-1.5,-1.8,-1.2,-0.3,0.9,2.7", + "Techno,2.4,1.8,0,-1.8,-1.5,0,2.4,3.0,3.0,2.7", + "Zero,0,0,0,0,0,0,0,0,0,0" + }; + + + +GMEQDialog * GMEQDialog::myself = NULL; + + +FXDEFMAP(GMEQDialog) GMEQDialogMap[]={ + FXMAPFUNCS(SEL_UPDATE,GMEQDialog::ID_EQ_30HZ,GMEQDialog::ID_EQ_16000HZ,GMEQDialog::onUpdEQ), + FXMAPFUNCS(SEL_COMMAND,GMEQDialog::ID_EQ_30HZ,GMEQDialog::ID_EQ_16000HZ,GMEQDialog::onCmdEQ), + FXMAPFUNCS(SEL_CHANGED,GMEQDialog::ID_EQ_30HZ,GMEQDialog::ID_EQ_16000HZ,GMEQDialog::onCmdEQ), + + FXMAPFUNC(SEL_COMMAND,GMEQDialog::ID_PRESET_EQ,GMEQDialog::onCmdPresetEQ), +#if FOXVERSION >= FXVERSION(1,7,0) + FXMAPFUNC(SEL_CHANGED,GMEQDialog::ID_PRESET_EQ,GMEQDialog::onCmdPresetEQ), +#endif + FXMAPFUNC(SEL_COMMAND,GMEQDialog::ID_RESET,GMEQDialog::onCmdReset), + FXMAPFUNC(SEL_COMMAND,GMEQDialog::ID_SAVE,GMEQDialog::onCmdSave), + FXMAPFUNC(SEL_COMMAND,GMEQDialog::ID_DELETE,GMEQDialog::onCmdDelete), + + FXMAPFUNC(SEL_UPDATE,GMEQDialog::ID_RESET,GMEQDialog::onUpdReset), + FXMAPFUNC(SEL_UPDATE,GMEQDialog::ID_SAVE,GMEQDialog::onUpdSave), + FXMAPFUNC(SEL_UPDATE,GMEQDialog::ID_DELETE,GMEQDialog::onUpdDelete), + + }; + +FXIMPLEMENT(GMEQDialog,FXDialogBox,GMEQDialogMap,ARRAYNUMBER(GMEQDialogMap)) + +GMEQDialog::GMEQDialog(FXWindow * p) : FXDialogBox(p,FXString::null,DECOR_BORDER|DECOR_TITLE|DECOR_CLOSE,0,0,0,0,2,2,2,2) { + + FXASSERT(myself==NULL); + myself=this; + //FXHorizontalFrame *closebox=new FXHorizontalFrame(this,LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X|PACK_UNIFORM_WIDTH,0,0,0,0,0,0,0,0); + // new FXButton(closebox,tr("&Close"),NULL,this,FXDialogBox::ID_ACCEPT,BUTTON_INITIAL|BUTTON_DEFAULT|LAYOUT_RIGHT|FRAME_RAISED|FRAME_THICK,0,0,0,0, 20,20); + //new FXSeparator(this,LAYOUT_FILL_X|SEPARATOR_GROOVE|LAYOUT_SIDE_BOTTOM); + + + setTitle(tr("Equalizer")); + + + /// Create a fixed font, about the same size as the normal font + FXint size = FXApp::instance()->getNormalFont()->getSize() - 10; + font_small = new FXFont(FXApp::instance(),"mono",(int)size/10,FXFont::Normal,FXFont::Straight,FONTENCODING_UNICODE,FXFont::NonExpanded,FXFont::Modern|FXFont::Fixed); + + + FXRealSlider *slider; + FXLabel * label; + FXTextField * textfield; + + { + + FXStringDict * dict = getApp()->reg().find("equalizer-presets"); + + /// Add default presets if they don't exist. + if (!dict || dict->no()==0){ + for (FXint i=0;i<(FXint)ARRAYNUMBER(default_presets);i++){ + getApp()->reg().writeStringEntry("equalizer-presets",GMStringVal(i).text(),default_presets[i]); + } + } + + FXString entry; + dict = getApp()->reg().find("equalizer-presets"); + if (dict && dict->no()>0) { + presets.no(dict->no()); + for (FXint pos=dict->first(),i=0;possize();pos=dict->next(pos),i++){ + entry=dict->data(pos); + presets[i].name = entry.section(',',0); + presets[i].bands.parse(entry.after(',',1)); + } + } + + } + + + + FXHorizontalFrame * presetframe = new FXHorizontalFrame(this,LAYOUT_FILL_X,0,0,0,0,0,0,0,0); + new FXLabel(presetframe,tr("Equalizer:"),NULL,LAYOUT_CENTER_Y); + presetlist = new GMListBox(presetframe,this,ID_PRESET_EQ,LAYOUT_FILL_X|FRAME_SUNKEN|FRAME_THICK); + new FXButton(presetframe,tr("\tSave"),GMIconTheme::instance()->icon_export,this,ID_SAVE,FRAME_RAISED|BUTTON_TOOLBAR); + new FXButton(presetframe,tr("\tReset"),GMIconTheme::instance()->icon_undo,this,ID_RESET,FRAME_RAISED|BUTTON_TOOLBAR); + new FXButton(presetframe,tr("\tRemove"),GMIconTheme::instance()->icon_delete,this,ID_DELETE,FRAME_RAISED|BUTTON_TOOLBAR); + + + listPresets(); + + + const FXuint sliderstyle = LAYOUT_FILL_Y|SLIDER_VERTICAL|LAYOUT_CENTER_X|SLIDER_TICKS_LEFT|SLIDER_TICKS_RIGHT|LAYOUT_FILL_ROW; + const FXchar * labels[]={"30","60","125","250","500","1k","2k","4k","8k","16k"}; + + + new FXSeparator(this,LAYOUT_FILL_X|SEPARATOR_GROOVE); + + FXMatrix * eqmatrix = new FXMatrix(this,3,MATRIX_BY_ROWS|LAYOUT_CENTER_X|LAYOUT_CENTER_Y|LAYOUT_FIX_HEIGHT,0,0,0,200,0,0,0,0,3,2); + + + new FXFrame(eqmatrix,FRAME_NONE); + FXVerticalFrame * vframe = new FXVerticalFrame(eqmatrix,LAYOUT_FILL_Y|LAYOUT_FILL_ROW,0,0,0,0,0,0,0,0,0,0); + label = new FXLabel(vframe,"+12db",NULL,LAYOUT_TOP|LAYOUT_RIGHT,0,0,0,0,2,2,0,0); + label->setFont(font_small); + label = new FXLabel(vframe,"0",NULL,LAYOUT_CENTER_Y|LAYOUT_RIGHT,0,0,0,0,2,2,0,0); + label->setFont(font_small); + label = new FXLabel(vframe,"-12db",NULL,LAYOUT_BOTTOM|LAYOUT_RIGHT,0,0,0,0,2,2,0,0); + label->setFont(font_small); + new FXFrame(eqmatrix,FRAME_NONE); + + textfield=new FXTextField(eqmatrix,4,GMPlayerManager::instance()->getPlayer(),GMPlayer::ID_PREAMP,LAYOUT_CENTER_X|TEXTFIELD_INTEGER|FRAME_SUNKEN|JUSTIFY_RIGHT,0,0,0,0,2,2,0,0); + textfield->setFont(font_small); + + FXRealSlider * rslider = new FXRealSlider(eqmatrix,GMPlayerManager::instance()->getPlayer(),GMPlayer::ID_PREAMP,sliderstyle,0,0,0,0,0,0,0,0); + rslider->setRange(-12.0,12.0); + rslider->setTickDelta(12.0); + rslider->setIncrement(1); + rslider->setValue(0.0); + label = new FXLabel(eqmatrix,tr("Pre-amp"),NULL,LAYOUT_CENTER_X,0,0,0,0,2,2,0,0); + label->setFont(font_small); + + for (FXint i=0;i<10;i++) { + textfield = new FXTextField(eqmatrix,4,this,ID_EQ_30HZ+i,LAYOUT_CENTER_X|TEXTFIELD_REAL|FRAME_SUNKEN|JUSTIFY_RIGHT,0,0,0,0,2,2,0,0); + textfield->setFont(font_small); + slider = new FXRealSlider(eqmatrix,this,ID_EQ_30HZ+i,sliderstyle,0,0,0,0,0,0,0,0); + slider->setRange(-6,6); + slider->setTickDelta(6); + slider->setIncrement(0.5); +#if FOXVERSION >= FXVERSION(1,7,0) + slider->setGranularity(0.1); +#endif + slider->setValue(0); + eqslider[i]=slider; + FXLabel * label = new FXLabel(eqmatrix,labels[i],NULL,LAYOUT_CENTER_X,0,0,0,0,2,2,0,0); + label->setFont(font_small); + } + + new FXFrame(eqmatrix,FRAME_NONE); + vframe = new FXVerticalFrame(eqmatrix,LAYOUT_FILL_Y|LAYOUT_FILL_ROW,0,0,0,0,0,0,0,0,0,0); + label = new FXLabel(vframe,"+6db",NULL,LAYOUT_TOP|LAYOUT_LEFT,0,0,0,0,2,2,0,0); + label->setFont(font_small); + label = new FXLabel(vframe,"0",NULL,LAYOUT_CENTER_Y|LAYOUT_LEFT,0,0,0,0,2,2,0,0); + label->setFont(font_small); + label = new FXLabel(vframe,"-6db",NULL,LAYOUT_BOTTOM|LAYOUT_LEFT,0,0,0,0,2,2,0,0); + label->setFont(font_small); + new FXFrame(eqmatrix,FRAME_NONE); + + if (getApp()->reg().readIntEntry("eqdialog","x",-1)!=-1) { + FXint xx=getApp()->reg().readIntEntry("eqdialog","x",getX()); + FXint yy=getApp()->reg().readIntEntry("eqdialog","y",getY()); + move(xx,yy); + } + else { + place(PLACEMENT_OWNER); + } + } + +GMEQDialog::~GMEQDialog(){ + myself=NULL; + FXString entry; + getApp()->reg().deleteSection("equalizer-presets"); + for (FXint i=0;ireg().writeFormatEntry("equalizer-presets",GMStringVal(i).text(),"%s,%s",presets[i].name.text(),entry.text()); + } + } + + + + +void GMEQDialog::listPresets() { + + for (FXint i=0;iappendItem(presets[i].name,NULL,(void*)(FXival)(i+1)); + } + + GMEqualizer eq; + GMPlayerManager::instance()->getPlayer()->getEqualizer(eq); + + if (eq.enabled) { + FXbool found=false; + for (FXint i=0;!found && isetCurrentItem(i); + found=true; + } + } + presetlist->setSortFunc(FXList::ascending); + presetlist->sortItems(); + presetlist->prependItem(tr("Disabled")); + + if (!found) { + presetlist->insertItem(1,tr("Manual"),NULL,(void*)(FXival)-1); + presetlist->setCurrentItem(1); + } + } + else { + presetlist->setSortFunc(FXList::ascending); + presetlist->sortItems(); + presetlist->prependItem(tr("Disabled")); + presetlist->setCurrentItem(0); + } + presetlist->setNumVisible(FXMIN(9,presetlist->getNumItems())); + } + + +void GMEQDialog::hide() { + FXDialogBox::hide(); + + /// Save Position + getApp()->reg().writeIntEntry("eqdialog","x",getX()); + getApp()->reg().writeIntEntry("eqdialog","y",getY()); + + + delete this; + } + + +GMEQDialog * GMEQDialog::instance() { + return myself; + } + + +long GMEQDialog::onCmdEQ(FXObject*sender,FXSelector sel,void*ptr){ + long result = GMPlayerManager::instance()->getPlayer()->handle(sender,FXSEL(FXSELTYPE(sel),GMPlayer::ID_EQ_30HZ+(FXSELID(sel)-ID_EQ_30HZ)),ptr); + + GMEqualizer eq; + GMPlayerManager::instance()->getPlayer()->getEqualizer(eq); + if (eq.enabled) { + FXbool found=false; + for (FXint i=0;!found && isetCurrentItem(presetlist->findItemByData((void*)(FXival)(i+1))); + FXint item = presetlist->findItemByData((void*)(FXival)(-1)); + if (item>=0) presetlist->removeItem(item); + found=true; + } + } + if (!found) { + FXint item = presetlist->findItemByData((void*)(FXival)(-1)); + if (item>=0) { + presetlist->setCurrentItem(item); + } + else { + presetlist->insertItem(1,tr("Manual"),NULL,(void*)(FXival)-1); + presetlist->setCurrentItem(1); + } + } + } + return result; + } + +long GMEQDialog::onUpdEQ(FXObject*sender,FXSelector sel,void*ptr){ + return GMPlayerManager::instance()->getPlayer()->handle(sender,FXSEL(SEL_UPDATE,GMPlayer::ID_EQ_30HZ+(FXSELID(sel)-ID_EQ_30HZ)),ptr); + } + + +long GMEQDialog::onCmdDelete(FXObject*,FXSelector,void*){ + FXint p = ((FXint)(FXival)presetlist->getItemData(presetlist->getCurrentItem()))-1; + if (FXMessageBox::question(this,MBOX_YES_NO,tr("Delete Preset"),fxtrformat("Are you sure you want to delete %s preset?"),presets[p].name.text())==MBOX_CLICKED_YES){ + presets.erase(p); + presetlist->clearItems(); + listPresets(); + } + return 1; + } + +long GMEQDialog::onUpdDelete(FXObject*sender,FXSelector,void*){ + FXint p = (FXint)(FXival)presetlist->getItemData(presetlist->getCurrentItem()); + if (0handle(this,FXSEL(SEL_COMMAND,ID_ENABLE),NULL); + else + sender->handle(this,FXSEL(SEL_COMMAND,ID_DISABLE),NULL); + return 1; + } + + +long GMEQDialog::onCmdSave(FXObject*,FXSelector,void*){ + FXString name; + if (FXInputDialog::getString(name,this,tr("Preset Name"),tr("Please enter preset name:"),NULL)){ + if (!name.empty()) { + for (FXint i=0;igetValue(); + } + } + /// cancel + return 1; + } + } + /// Everything ok + + presets.no(presets.no()+1); + presets[presets.no()-1].name=name; + for (FXint b=0;b<10;b++){ + presets[presets.no()-1].bands[b]=eqslider[b]->getValue(); + } + + presetlist->appendItem(name,NULL,(void*)(FXival)(presets.no())); + presetlist->setCurrentItem(presetlist->getNumItems()-1); + presetlist->setNumVisible(FXMIN(9,presetlist->getNumItems())); + presetlist->sortItems(); + presetlist->moveItem(0,presetlist->findItemByData(NULL)); + } + } + return 1; + } + +long GMEQDialog::onUpdSave(FXObject*sender,FXSelector,void*){ + FXint p = (FXint)(FXival)presetlist->getItemData(presetlist->getCurrentItem()); + if (p!=0) + sender->handle(this,FXSEL(SEL_COMMAND,ID_ENABLE),NULL); + else + sender->handle(this,FXSEL(SEL_COMMAND,ID_DISABLE),NULL); + return 1; + } + + +long GMEQDialog::onCmdReset(FXObject*,FXSelector,void*){ + FXint p = (FXint)(FXival)presetlist->getItemData(presetlist->getCurrentItem()); + FXString entry; + for (FXint i=0;i<(FXint)ARRAYNUMBER(default_presets);i++){ + entry = default_presets[i]; + if (presets[p-1].name==entry.before(',')){ + presets[p-1].bands.parse(entry.after(',',1)); + GMPlayerManager::instance()->getPlayer()->setEqualizer(presets[p-1].bands); + for (FXint i=0;i<10;i++){ + eqslider[i]->setValue(presets[p-1].bands[i]); + } + break; + } + } + return 1; + } + +long GMEQDialog::onUpdReset(FXObject*sender,FXSelector,void*){ + FXint p = (FXint)(FXival)presetlist->getItemData(presetlist->getCurrentItem()); + if (0handle(this,FXSEL(SEL_COMMAND,ID_ENABLE),NULL); + else + sender->handle(this,FXSEL(SEL_COMMAND,ID_DISABLE),NULL); + return 1; + } + +#if FOXVERSION >= FXVERSION(1,7,0) +long GMEQDialog::onCmdPresetEQ(FXObject*sender,FXSelector sel,void*ptr){ + GMListBox * list = reinterpret_cast(sender); + if (FXSELTYPE(sel)==SEL_CHANGED && list->isMenuShown()) return 1; +#else +long GMEQDialog::onCmdPresetEQ(FXObject*sender,FXSelector,void*ptr){ + GMListBox * list = reinterpret_cast(sender); +#endif + FXint item = (FXint)(FXival)ptr; + FXint p = ((FXint)(FXival)list->getItemData(item))-1; + if (p>=0) { + GMPlayerManager::instance()->getPlayer()->setEqualizer(presets[p].bands); + for (FXint i=0;i<10;i++){ + eqslider[i]->setValue(presets[p].bands[i]); + } + item = presetlist->findItemByData((void*)(FXival)(-1)); + if (item>=0) presetlist->removeItem(item); + } + else if (p==-1) { + GMPlayerManager::instance()->getPlayer()->disableEqualizer(); + item = presetlist->findItemByData((void*)(FXival)(-1)); + if (item>=0) presetlist->removeItem(item); + } + return 1; + } + + + + + + + + + + + + + + diff --git a/src/GMEQDialog.h b/src/GMEQDialog.h new file mode 100644 index 0000000..1956441 --- /dev/null +++ b/src/GMEQDialog.h @@ -0,0 +1,80 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#ifndef GMEQDIALOG_H +#define GMEQDIALOG_H + + +class GMEQPreset { +public: + FXString name; + GMEQBands bands; + }; + + +class GMEQDialog : public FXDialogBox { +FXDECLARE(GMEQDialog) + static GMEQDialog * myself; +protected: + FXFontPtr font_small; +protected: + FXArray presets; + FXRealSlider * eqslider[10]; + GMListBox * presetlist; +protected: + GMEQDialog(){} +private: + GMEQDialog(const GMEQDialog&); + GMEQDialog &operator=(const GMEQDialog&); +protected: + void listPresets(); +public: + static GMEQDialog * instance(); +public: + enum { + ID_PRESET_EQ=FXDialogBox::ID_LAST, + ID_SAVE, + ID_RESET, + ID_DELETE, + ID_EQ_30HZ, + ID_EQ_60HZ, + ID_EQ_125HZ, + ID_EQ_250HZ, + ID_EQ_500HZ, + ID_EQ_1000HZ, + ID_EQ_2000HZ, + ID_EQ_4000HZ, + ID_EQ_8000HZ, + ID_EQ_16000HZ + }; +public: + long onCmdEQ(FXObject*,FXSelector,void*); + long onUpdEQ(FXObject*,FXSelector,void*); + long onCmdPresetEQ(FXObject*,FXSelector,void*); + long onCmdReset(FXObject*,FXSelector,void*); + long onCmdSave(FXObject*,FXSelector,void*); + long onCmdDelete(FXObject*,FXSelector,void*); + long onUpdReset(FXObject*,FXSelector,void*); + long onUpdSave(FXObject*,FXSelector,void*); + long onUpdDelete(FXObject*,FXSelector,void*); +public: + GMEQDialog(FXWindow * p); + virtual void hide(); + virtual ~GMEQDialog(); + }; +#endif diff --git a/src/GMFetch.cpp b/src/GMFetch.cpp new file mode 100644 index 0000000..b337eae --- /dev/null +++ b/src/GMFetch.cpp @@ -0,0 +1,95 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2007-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ + +#include "gmdefs.h" +#include "GMFetch.h" +#include "GMPlayerManager.h" + +#include "ap_buffer.h" +#include "ap_http.h" +using namespace ap; + +GMFetch * GMFetch::fetch = NULL; + +GMFetch::GMFetch() : gui(FXApp::instance()) { + } + + +FXint GMFetch::run() { + GMFetchResponse * response = new GMFetchResponse; + response->url = url; + HttpClient client; + + // HEAD request don't always work... + if (client.basic("GET",url) && client.status.code==200) { + response->content_type = client.getHeader("content-type"); + if (response->content_type.find("audio/mpegurl")!=-1 || + response->content_type.find("audio/x-mpegurl")!=-1 || + response->content_type.find("application/pls+xml")!=-1 || + response->content_type.find("audio/x-scpls")!=-1) { + response->data=client.body(); + } + gui.message(GMPlayerManager::instance(),FXSEL(SEL_COMMAND,GMPlayerManager::ID_DOWNLOAD_COMPLETE),&response,sizeof(GMFetchResponse*)); + } + else { + errormsg="Failed to retrieve"; + gui.message(GMPlayerManager::instance(),FXSEL(SEL_COMMAND,GMPlayerManager::ID_DOWNLOAD_COMPLETE)); + } + return 0; + } + +void GMFetch::init() { + } + +void GMFetch::exit() { + if (fetch) { + + if (fetch->running()) + fetch->join(); + + delete fetch; + fetch=NULL; + } + } + + +void GMFetch::download(const FXString & url) { + if (fetch==NULL) { + fetch = new GMFetch(); + } + else { + if (fetch->running()) + fetch->join(); + } + fetch->url = url; + fetch->start(); + } + +void GMFetch::cancel_and_wait(){ + if (fetch && fetch->running() ) { + fetch->join(); + } + } + +FXbool GMFetch::busy(){ + if (fetch && fetch->running() ) + return true; + else + return false; + } diff --git a/src/GMFetch.h b/src/GMFetch.h new file mode 100644 index 0000000..e1394e4 --- /dev/null +++ b/src/GMFetch.h @@ -0,0 +1,55 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2007-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#ifndef GMFETCH_H +#define GMFETCH_H + +struct GMFetchResponse { + FXString url; + FXString data; + FXString content_type; + }; + +class GMFetch : public FXThread { +protected: + static GMFetch * fetch; +public: + FXMessageChannel gui; + FXStringList mrl; + FXString url; + FXString errormsg; +protected: + FXint run(); +protected: + GMFetch(); +public: + static void download(const FXString & url); + + static void init(); + + static void cancel_and_wait(); + + static FXbool busy(); + + static void exit(); + }; + +#endif + + + diff --git a/src/GMFilename.cpp b/src/GMFilename.cpp new file mode 100644 index 0000000..9077fe9 --- /dev/null +++ b/src/GMFilename.cpp @@ -0,0 +1,575 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#include "gmdefs.h" +#include "FXTextCodec.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "GMFilename.h" + +/* + + Conditionals + ------------ + + ?c => display a if c is not empty, display b if c is empty) + ?c => display c if not empty + + T => track title + A => album title + P => album artist name + p => track artist name + G => genre + N => 2 digit track number + n => track number + d => disc number + y => track year + + +*/ + +const char * gmcodecnames[]={ + "7-bit Ascii", + "UTF-8 Unicode", + "ISO 8859-1", + "ISO 8859-2", + "ISO 8859-3", + "ISO 8859-4", + "ISO 8859-5", + "ISO 8859-6", + "ISO 8859-7", + "ISO 8859-8", + "ISO 8859-9", + "ISO 8859-10", + "ISO 8859-11", + "ISO 8859-13", + "ISO 8859-14", + "ISO 8859-15", + "ISO 8859-16", + "CP-437", + "CP-850", + "CP-852", + "CP-855", + "CP-856", + "CP-857", + "CP-860", + "CP-861", + "CP-862", + "CP-863", + "CP-864", + "CP-865", + "CP-866", + "CP-869", + "CP-874", + "CP-1250", + "CP-1251", + "CP-1252", + "CP-1253", + "CP-1254", + "CP-1255", + "CP-1256", + "CP-1257", + "CP-1258", + "KOI8-R", + NULL, + }; + + +using namespace GMFilename; + +namespace GMFilename { + + +FXTextCodec * findcodec(const FXuint & codec) { + switch(codec) { + case ENCODING_ASCII : return NULL; break; + case ENCODING_UTF8 : return new FXUTF8Codec; break; + case ENCODING_8859_1 : return new FX88591Codec; break; + case ENCODING_8859_2 : return new FX88592Codec; break; + case ENCODING_8859_3 : return new FX88593Codec; break; + case ENCODING_8859_4 : return new FX88594Codec; break; + case ENCODING_8859_5 : return new FX88595Codec; break; + case ENCODING_8859_6 : return new FX88596Codec; break; + case ENCODING_8859_7 : return new FX88597Codec; break; + case ENCODING_8859_8 : return new FX88598Codec; break; + case ENCODING_8859_9 : return new FX88599Codec; break; + case ENCODING_8859_10 : return new FX885910Codec; break; + case ENCODING_8859_11 : return new FX885911Codec; break; + case ENCODING_8859_13 : return new FX885913Codec; break; + case ENCODING_8859_14 : return new FX885914Codec; break; + case ENCODING_8859_15 : return new FX885915Codec; break; + case ENCODING_8859_16 : return new FX885916Codec; break; + case ENCODING_CP437 : return new FXCP437Codec; break; + case ENCODING_CP850 : return new FXCP850Codec; break; + case ENCODING_CP852 : return new FXCP852Codec; break; + case ENCODING_CP855 : return new FXCP855Codec; break; + case ENCODING_CP856 : return new FXCP856Codec; break; + case ENCODING_CP857 : return new FXCP857Codec; break; + case ENCODING_CP860 : return new FXCP860Codec; break; + case ENCODING_CP861 : return new FXCP861Codec; break; + case ENCODING_CP862 : return new FXCP862Codec; break; + case ENCODING_CP863 : return new FXCP863Codec; break; + case ENCODING_CP864 : return new FXCP864Codec; break; + case ENCODING_CP865 : return new FXCP865Codec; break; + case ENCODING_CP866 : return new FXCP866Codec; break; + case ENCODING_CP869 : return new FXCP869Codec; break; + case ENCODING_CP874 : return new FXCP874Codec; break; + case ENCODING_CP1250 : return new FXCP1250Codec; break; + case ENCODING_CP1251 : return new FXCP1251Codec; break; + case ENCODING_CP1252 : return new FXCP1252Codec; break; + case ENCODING_CP1253 : return new FXCP1253Codec; break; + case ENCODING_CP1254 : return new FXCP1254Codec; break; + case ENCODING_CP1255 : return new FXCP1255Codec; break; + case ENCODING_CP1256 : return new FXCP1256Codec; break; + case ENCODING_CP1257 : return new FXCP1257Codec; break; + case ENCODING_CP1258 : return new FXCP1258Codec; break; + case ENCODING_KOIR8 : return new FXKOI8RCodec; break; + default : return NULL; + } + return NULL; + } + + +/* + 0) trim white spaces + 1) only want printable characters + 2) substitute spaces for underscores [optional] + 3) make everything lowercase [optional] + 4) do not use any of the shell dangerous character set \'\\#~!\"$&();<>|`^*?[]/ +*/ +static FXString filter(const FXString & input,const FXString & forbidden,FXuint options){ +// const FXString forbidden = "\'\\#~!\"$&();<>|`^*?[]/.:"; // Allowed %+,-=@_{} + FXString result; + FXwchar w; + FXint i; + FXbool lastspace=false; + for (i=0;iutf2mb(&c,1,&input[i],input.extent(i)); + if (len>0 && c!=0x1A) { + result+=c; + } + else { + input_decompose.assign(&input[i],input.extent(i)); +#if FOXVERSION < FXVERSION(1,7,29) + input_decompose = decompose(input_decompose,DecCompat); +#else + input_decompose = decompose(input_decompose,DecomposeCompat); +#endif + for (j=0;jutf2mb(&c,1,&input_decompose[j],input_decompose.extent(j)); + if (len>0 && c!=0x1A) { + result+=c; + } + } + } + } + return result; + } + +/* convert UTF8 to given 7 bit assci */ +static FXString convert_and_decompose(const FXString & input) { + register FXint i=0; + FXString result; +#if FOXVERSION < FXVERSION(1,7,29) + FXString in = decompose(input,DecCanonical); +#else + FXString in = decompose(input,DecomposeCanonical); +#endif + for (i=0;i(codec)==NULL) + result = convert_and_decompose(result,codec); + + return result; + } + + +static FXString to_8bit_ascii(const FXString & input,const FXString & forbidden,FXuint opts) { + FXString result; + + /// Filter the input + result = filter(input,forbidden,opts); + + /// Make sure it is properly composed. Should we do this? +#if FOXVERSION < FXVERSION(1,7,29) + result = compose(result,DecCompat); +#else + result = compose(result,DecomposeCompat); +#endif + + /// convert to given codec. + result = convert_and_decompose(result); + + /// Return result + return result; + } + +static FXString convert(const FXString & input,FXTextCodec * codec,const FXString & forbidden,FXuint opts) { + if (codec) + return to_8bit_codec(input,codec,forbidden,opts); + else + return to_8bit_ascii(input,forbidden,opts); + } + +static FXString get_field(FXchar field,const GMTrack & track,FXTextCodec * codec,const FXString & forbidden,FXuint opts){ + switch(field) { + case 'T': return convert(track.title,codec,forbidden,opts); break; + case 'A': return convert(track.album,codec,forbidden,opts); break; + case 'P': return convert(track.album_artist,codec,forbidden,opts); break; + case 'p': return convert(track.artist,codec,forbidden,opts); break; + case 'G': return convert(track.genre,codec,forbidden,opts); break; + case 'N': return GMStringFormat("%.2d",GMTRACKNO(track.no)); break; + case 'n': return GMStringVal(GMTRACKNO(track.no)); break; + case 'd': return GMStringVal(GMDISCNO(track.no)); break; + case 'y': return GMStringVal(track.year); break; + } + return FXString::null; + } + + +static FXbool eval_field(FXchar field,const GMTrack & track,FXTextCodec * codec,const FXString & forbidden,FXuint opts){ + switch(field) { + case 'T': return !convert(track.title,codec,forbidden,opts).empty(); break; + case 'A': return !convert(track.album,codec,forbidden,opts).empty(); break; + case 'P': return !convert(track.album_artist,codec,forbidden,opts).empty(); break; + case 'p': return !convert(track.artist,codec,forbidden,opts).empty(); break; + case 'G': return !convert(track.genre,codec,forbidden,opts).empty(); break; + case 'N': return GMTRACKNO(track.no)!=0; break; + case 'n': return GMTRACKNO(track.no)!=0; break; + case 'd': return GMDISCNO(track.no)!=0; break; + case 'y': return track.year!=0; break; + } + return false; + } + + + +FXString format_track(const GMTrack & track,const FXString & path,const FXString & forbidden,const FXuint & options,FXTextCodec * textcodec){ + FXString field; + FXwchar w; + + /// Valid field identifiers + const FXchar valid[]="TAPpGNndy"; + + /// Condition Stack + FXint depth=0; + FXArray cs(1); + cs[0]=true; + + FXString result; + + /// Parse the field entries + for (FXint i=0;i') { + depth--; + cs.erase(cs.no()-1); + continue; + } + } + + /// Check beginning of conditional + if (path[i]=='?' && strchr(valid,path[i+1]) ) { + + /// Condition with if else clause + if (path[i+2]=='<') { + /// condition is true if eval_fied returns true and the parent condition is also true. + cs.append(cs[depth] && eval_field(path[i+1],track,textcodec,forbidden,options)); + i+=2; + depth++; + continue; + } + /// Simplified condition just display if not empty + else if (cs[depth]) { + if (eval_field(path[i+1],track,textcodec,forbidden,options)) { + field = get_field(path[i+1],track,textcodec,forbidden,options); + if (field.empty()) + result.trim(); + else + result+=field; + } + i+=1; + continue; + } + } + + /// only add stuff if our current condition allows it + if (cs[depth]) { + + /// get field + if (path[i]=='%' && strchr(valid,path[i+1]) ) { + field = get_field(path[i+1],track,textcodec,forbidden,options); + if (field.empty()) + result.trim(); + else + result+=field; + i+=1; + continue; + } + + /// Add anything else + w = path.wc(i); + result.append(&w,1); + } + + } + + result.trim(); + return result; + } + + + +FXbool create(FXString & result,const GMTrack & track, const FXString & format,const FXString & forbidden,const FXuint & options,FXTextCodec * textcodec) { + FXString path; + + /// Expand Environment Variables and such... + path = FXPath::expand(format); + + /// Make absolute + if (!FXPath::isAbsolute(path)) { + path = FXPath::absolute(FXPath::directory(track.mrl),path); + } + + /// Simplify things + path = FXPath::simplify(path); + + /// Parse field entries + result = format_track(track,path,forbidden,options,textcodec); + + /// Add extension + result+="."; + if (options&LOWERCASE_EXTENSION || options&LOWERCASE) + result.append(FXPath::extension(track.mrl).lower()); + else + result.append(FXPath::extension(track.mrl)); + return true; + } + + +void parse(GMTrack & track,const FXString & mask,FXuint options) { + FXint nsep=0,i,j; + FXString input,field; + FXString dir; + FXchar sep,item; + FXint beg=0,end=0; + + // Determine number of path separators + for (i=0;i=mask.length()) { + sep='\0'; + } + else if (mask[j]!='%') { + i++; + if (mask[j]=='\\') { + j++;i++; + if (j0) { + field=input.mid(beg,end-beg).simplify(); + if (options&REPLACE_UNDERSCORE) field.substitute('_',' '); + switch(item) { + case 'T' : if (options&OVERWRITE || track.title.empty()) track.title.adopt(field); break; + case 'P' : if (options&OVERWRITE || track.album_artist.empty()) track.album_artist.adopt(field); break; + case 'A' : if (options&OVERWRITE || track.album.empty()) track.album.adopt(field); break; + case 'p' : if (options&OVERWRITE || track.artist.empty()) track.artist.adopt(field); break; + case 'n' : +#if FOXVERSION >= FXVERSION(1,7,12) + case 'N' : if (options&OVERWRITE) track.setTrackNumber(field.toInt()); break; + case 'd' : if (options&OVERWRITE) track.setDiscNumber(field.toInt()); break; +#else + case 'N' : if (options&OVERWRITE) track.setTrackNumber(FXIntVal(field)); break; + case 'd' : if (options&OVERWRITE) track.setDiscNumber(FXIntVal(field)); break; +#endif + } + //fxmessage("%%%c=\"%s\"\n",item,field.text()); + beg=end+1; + } + } + else { + //fxmessage("eat %c: %s\n",mask[i],&input[beg]); + while(input[beg]!=mask[i] && begsetLayoutHints(LAYOUT_BOTTOM|LAYOUT_FILL_X|LAYOUT_FILL_Y); + + FXScrollWindow *scroll=new FXScrollWindow(sunken,LAYOUT_FILL_X|LAYOUT_FILL_Y); + GMScrollArea::replaceScrollbars(scroll); + preview=new FXLabel(scroll,"ABCDEFGHIJKLMNOPQRSTUVWXYZ\nabcdefghijklmnopqrstuvwxyz\n0123456789",NULL,LAYOUT_CENTER_X|LAYOUT_CENTER_Y); + preview->setBackColor(getApp()->getBackColor()); +// new FXLabel(main,"Preview:",NULL,LAYOUT_BOTTOM); + + FXHorizontalFrame * filterframe = new FXHorizontalFrame(main,LAYOUT_FILL_X|LAYOUT_BOTTOM,0,0,0,0,0,0,3,3,0,0); + new FXLabel(filterframe,"Pitch:",NULL,LABEL_NORMAL|LAYOUT_CENTER_Y,0,0,0,0); + pitchlist = new GMListBox(filterframe,this,ID_PITCH); + pitchlist->appendItem("Any"); + pitchlist->appendItem("Fixed",NULL,(void*)(FXuval)FXFont::Fixed); + pitchlist->appendItem("Variable",NULL,(void*)(FXuval)FXFont::Variable); + pitchlist->setNumVisible(3); + + new FXLabel(filterframe," Type:",NULL,LABEL_NORMAL|LAYOUT_CENTER_Y); + scalelist = new GMListBox(filterframe,this,ID_SCALABLE); + scalelist->appendItem("Any"); + scalelist->appendItem("Scalable",NULL,(void*)(FXuval)FXFont::Scalable); + scalelist->setNumVisible(2); + + FXHorizontalFrame * input = new FXHorizontalFrame(main,LAYOUT_FILL_X|LAYOUT_FILL_Y,0,0,0,0,0,0,0,0); + + FXVerticalFrame * sizeframe = new FXVerticalFrame(input,LAYOUT_RIGHT|LAYOUT_FILL_Y,0,0,0,0,0,0,0,0,0,0); + new FXLabel(sizeframe,"Size:",NULL,LABEL_NORMAL,0,0,0,0,0,0); + + FXVerticalFrame * sizeinputframe = new FXVerticalFrame(sizeframe,LAYOUT_RIGHT|LAYOUT_FILL_Y,0,0,0,0,0,0,0,0); + sizefield=new GMTextField(sizeinputframe,3,this,ID_SIZE_TEXT,TEXTFIELD_NORMAL|TEXTFIELD_INTEGER|LAYOUT_FILL_X); + sunken=new GMScrollFrame(sizeinputframe); + sizelist=new GMList(sunken,this,ID_SIZE,LAYOUT_FILL_Y|LAYOUT_FILL_X|LIST_SINGLESELECT); + + FXHorizontalFrame * mainframe = new FXHorizontalFrame(input,LAYOUT_LEFT|LAYOUT_FILL_Y|LAYOUT_FILL_X,0,0,0,0,0,0,0,0); + FXSpring * familyframe = new FXSpring(mainframe,LAYOUT_FILL_X|LAYOUT_FILL_Y,4,0,0,0,0,0,0,0,0,0,0,0); + new FXLabel(familyframe,"Family:",NULL,LABEL_NORMAL,0,0,0,0); + sunken=new GMScrollFrame(familyframe); + familylist = new GMList(sunken,this,ID_FAMILY,LAYOUT_FILL_Y|LAYOUT_FILL_X|LIST_BROWSESELECT); + familylist->setSortFunc(FXList::ascending); + + FXSpring * styleframe = new FXSpring(mainframe,LAYOUT_FILL_X|LAYOUT_FILL_Y,3,0,0,0,0,0,0,0,0,0,0,0); + new FXLabel(styleframe,"Style:",NULL,LABEL_NORMAL,0,0,0,0); + sunken=new GMScrollFrame(styleframe); + stylelist=new GMList(sunken,this,ID_STYLE,LAYOUT_FILL_Y|LAYOUT_FILL_X|LIST_BROWSESELECT); + stylelist->setSortFunc(FXList::ascending); + + +#if FOXVERSION < FXVERSION(1,7,17) + getApp()->getNormalFont()->getFontDesc(selected); +#else + selected = getApp()->getNormalFont()->getActualFontDesc(); +#endif + selected.flags|=FXFont::Scalable; + } + +GMFontDialog::~GMFontDialog(){ + } + + +void GMFontDialog::setFontDesc(const FXFontDesc& fontdesc){ + selected=fontdesc; + } + +const FXFontDesc& GMFontDialog::getFontDesc() const { + return selected; + } + + +void GMFontDialog::create() { + FXDialogBox::create(); + listFontFamily(); + listFontStyle(); + listFontSize(); + } + +// Preview +void GMFontDialog::previewFont(){ + FXFont *old; + + // Save old font + old=previewfont; + + // Get new font + previewfont=new FXFont(getApp(),selected); + + // Realize new font + previewfont->create(); + + // Set new font + preview->setFont(previewfont); + +/* + FXString previewtext; + for (FXwchar w=previewfont->getMinChar();w<=previewfont->getMaxChar()&& previewtext.length()<64;w++) { + if (previewfont->hasChar(w)) { + previewtext.append(&w,1); + } + if (previewtext.length()==26) + previewtext+="\n"; + if (previewtext.length()==53) + previewtext+="\n"; + } + preview->setText(previewtext); +*/ + // Delete old font + delete old; + } + + +void GMFontDialog::listFontFamily(){ + FXFontDesc * fonts=NULL; + FXuint numfonts,f; + FXint last=-1,selindex=-1; + FXString face,family,pfamily; + + familylist->clearItems(); + if (FXFont::listFonts(fonts,numfonts,FXString::null,0,0,0,selected.encoding,selected.flags)) { + for(f=0;f=0) { + familylist->setItemText(last,fonts[f-1].face); + last=familylist->appendItem(fonts[f].face,NULL,(void*)(FXuval)fonts[f].flags); + } + else { + last=familylist->appendItem(family,NULL,(void*)(FXuval)fonts[f].flags); + } + pfamily.adopt(family); + if(compare(selected.face,fonts[f].face)==0) selindex=f; + } + if(selindex==-1) selindex=0; + if(0getNumItems()){ + familylist->setCurrentItem(selindex); + familylist->makeItemVisible(selindex); + strncpy(selected.face,familylist->getItemText(selindex).text(),sizeof(selected.face)); + } + FXFREE(&fonts); + } + familylist->sortItems(); + } + +void GMFontDialog::listFontStyle(){ + FXFontDesc * fonts=NULL; + FXint selindex=-1; + FXuint numfonts,f; + FXushort wi,ww,lastwi=0,lastww=0,sl,lastsl=0; + const FXchar *wgt=NULL,*slt=NULL,*wid=NULL; + stylelist->clearItems(); + if (FXFont::listFonts(fonts,numfonts,selected.face,0,0,0/*selected.weight,selected.slant,selected.setwidth*/,selected.encoding,selected.flags)) { + for(f=0;fappendItem(GMStringFormat("%s %s %s",wgt,wid,slt),NULL,(void*)(FXuval)style); + else if (wgt && slt) + stylelist->appendItem(GMStringFormat("%s %s",wgt,slt),NULL,(void*)(FXuval)style); + else if (wgt && wid) + stylelist->appendItem(GMStringFormat("%s %s",wgt,wid),NULL,(void*)(FXuval)style); + else if (wid && slt) + stylelist->appendItem(GMStringFormat("%s %s",wid,slt),NULL,(void*)(FXuval)style); + else if (slt) + stylelist->appendItem(tr(slt),NULL,(void*)(FXuval)style); + else if (wgt) + stylelist->appendItem(tr(wgt),NULL,(void*)(FXuval)style); + else if (wid) + stylelist->appendItem(tr(wid),NULL,(void*)(FXuval)style); + else + stylelist->appendItem(tr("Normal"),NULL,(void*)(FXuval)style); + + if (ww==selected.weight && sl==selected.slant && wi==selected.setwidth) { + selindex=stylelist->getNumItems()-1; + } + } + } + FXFREE(&fonts); + if(selindex==-1) selindex=0; + if(0getNumItems()){ + stylelist->setCurrentItem(selindex); + FXuint style=(FXuint)(FXuval)stylelist->getItemData(selindex); + selected.weight=FXREDVAL(style); + selected.slant=FXGREENVAL(style); + selected.setwidth=FXBLUEVAL(style); + stylelist->makeItemVisible(selindex); + //stylelist->sortItems(); + } + } + } + + +void GMFontDialog::listFontSize(){ + const FXuint sizeint[]={60,80,90,100,110,120,140,160,200,240,300,360,420,480,640,720}; + FXFontDesc *fonts; + FXuint numfonts,f,s,lasts; + FXint selindex=-1; + sizelist->clearItems(); + sizefield->setText(FXString::null); + FXString string; + if(FXFont::listFonts(fonts,numfonts,selected.face,selected.weight,selected.slant,selected.setwidth,selected.encoding,selected.flags)){ + FXASSERT(0getListStyle(); + style&=~LIST_BROWSESELECT; + style|=LIST_SINGLESELECT; + sizelist->setListStyle(style); + for(f=0; fappendItem(string,NULL,(void*)(FXuval)s); + if(selected.size == s) selindex=sizelist->getNumItems()-1; + lasts=s; + } + } + else{ + FXuint style=sizelist->getListStyle(); + style&=~LIST_SINGLESELECT; + style|=LIST_BROWSESELECT; + sizelist->setListStyle(style); + for(f=0; fappendItem(string,NULL,(void*)(FXuval)s); + if(selected.size==s) selindex=sizelist->getNumItems()-1; + lasts=s; + } + } + } + if(selindex==-1) selindex=0; + if(0getNumItems()){ + sizelist->selectItem(selindex); + sizelist->makeItemVisible(selindex); + sizefield->setText(sizelist->getItemText(selindex)); + selected.size=(FXuint)(FXuval)sizelist->getItemData(selindex); + } + FXFREE(&fonts); + } + } + +long GMFontDialog::onCmdFamily(FXObject*,FXSelector,void* ptr){ + strncpy(selected.face,familylist->getItemText((FXint)(FXival)ptr).text(),sizeof(selected.face)); + listFontStyle(); + listFontSize(); + previewFont(); + return 1; + } + +long GMFontDialog::onCmdStyle(FXObject*,FXSelector,void* ptr){ + FXuint style=(FXuint)(FXuval)stylelist->getItemData((FXint)(FXival)ptr); + selected.weight=FXREDVAL(style); + selected.slant=FXGREENVAL(style); + selected.setwidth=FXBLUEVAL(style); + listFontSize(); + previewFont(); + return 1; + } + +long GMFontDialog::onCmdSize(FXObject*,FXSelector,void*ptr){ + selected.size=(FXuint)(FXuval)sizelist->getItemData((FXint)(FXival)ptr); + sizefield->setText(sizelist->getItemText((FXint)(FXival)ptr)); + previewFont(); + return 1; + } +long GMFontDialog::onCmdSizeText(FXObject*,FXSelector,void*){ + selected.size=(FXuint)(10.0*GMFloatVal(sizefield->getText())); + if(selected.size<60) selected.size=60; + if(selected.size>2400) selected.size=2400; + previewFont(); + return 1; + } + + +// Changed pitch +long GMFontDialog::onCmdPitch(FXObject*,FXSelector,void*ptr){ + FXint index=(FXint)(FXival)ptr; + selected.flags&=~(FXFont::Fixed|FXFont::Variable); + selected.flags|=(FXuint)(FXuval)pitchlist->getItemData(index); + listFontFamily(); + listFontStyle(); + listFontSize(); + previewFont(); + return 1; + } + +long GMFontDialog::onUpdPitch(FXObject*sender,FXSelector,void*){ + FXint value=(selected.flags&FXFont::Fixed) ? 1 : (selected.flags&FXFont::Variable) ? 2 : 0; + sender->handle(this,FXSEL(SEL_COMMAND,ID_SETINTVALUE),&value); + return 1; + } + +// Changed pitch +long GMFontDialog::onCmdScalable(FXObject*,FXSelector,void*ptr){ + FXint index=(FXint)(FXival)ptr; + selected.flags&=~(FXFont::Scalable); + selected.flags|=(FXuint)(FXuval)scalelist->getItemData(index); + listFontFamily(); + listFontStyle(); + listFontSize(); + previewFont(); + return 1; + } + +long GMFontDialog::onUpdScalable(FXObject*sender,FXSelector,void*){ + FXint value=(selected.flags&FXFont::Scalable) ? 1 : 0; + sender->handle(this,FXSEL(SEL_COMMAND,ID_SETINTVALUE),&value); + return 1; + } diff --git a/src/GMFontDialog.h b/src/GMFontDialog.h new file mode 100644 index 0000000..4ae439f --- /dev/null +++ b/src/GMFontDialog.h @@ -0,0 +1,78 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2009-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#ifndef GMFONTDIALOG_H +#define GMFONTDIALOG_H + +class GMFontDialog : public FXDialogBox { +FXDECLARE(GMFontDialog) +protected: + FXFontDesc selected; + GMList * familylist; + GMList * stylelist; + GMList * sizelist; + GMListBox * pitchlist; + GMListBox * scalelist; + GMTextField * sizefield; + FXLabel * preview; + FXFont * previewfont; +protected: + GMFontDialog(); +private: + GMFontDialog(const GMFontDialog&); + GMFontDialog &operator=(const GMFontDialog&); +protected: + void listFontFamily(); + void listFontStyle(); + void listFontSize(); + void previewFont(); +public: + enum { + ID_FAMILY = FXDialogBox::ID_LAST, + ID_STYLE, + ID_SIZE, + ID_PITCH, + ID_SCALABLE, + ID_SIZE_TEXT + }; +public: + long onCmdFamily(FXObject*,FXSelector,void*); + long onCmdStyle(FXObject*,FXSelector,void*); + long onCmdSize(FXObject*,FXSelector,void*); + long onCmdSizeText(FXObject*,FXSelector,void*); + long onCmdPitch(FXObject*,FXSelector,void*); + long onUpdPitch(FXObject*,FXSelector,void*); + long onCmdScalable(FXObject*,FXSelector,void*); + long onUpdScalable(FXObject*,FXSelector,void*); +public: + GMFontDialog(FXApp * app,const FXString& name,FXuint opts=0,FXint x=0,FXint y=0,FXint w=400,FXint h=400); + + GMFontDialog(FXWindow* owner,const FXString& name,FXuint opts=DECOR_BORDER|DECOR_TITLE|DECOR_RESIZE,FXint x=0,FXint y=0,FXint w=400,FXint h=400); + + virtual void create(); + + /// Set the current font selection + void setFontDesc(const FXFontDesc& fontdesc); + + /// Get the current font selection + const FXFontDesc& getFontDesc() const; + + virtual ~GMFontDialog(); + }; + +#endif diff --git a/src/GMIconTheme.cpp b/src/GMIconTheme.cpp new file mode 100644 index 0000000..ea111e5 --- /dev/null +++ b/src/GMIconTheme.cpp @@ -0,0 +1,700 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#include "gmdefs.h" +#include "gmutils.h" +#include "GMIconTheme.h" +#include "GMApp.h" +#include "GMPlayerManager.h" +#include "GMPreferences.h" +#include "icons.h" + +#include +#include +#include +#include + +#define SMALL_SIZE 16 +#define MEDIUM_SIZE 22 +#define LARGE_SIZE 48 + + +void GMIconSet::save(FXStream & store) { + store << name; + store << dir; + store << small; + store << medium; + store << large; + } + + +void GMIconSet::load(FXStream & store) { + store >> name; + store >> dir; + store >> small; + store >> medium; + store >> large; + } + + + +void gm_set_application_icon(FXWindow * window) { + FXPNGImage * image = new FXPNGImage(FXApp::instance(),gogglesmm_32_png,0,0); + ewmh_set_window_icon(window,image); + delete image; + } + +static const FXuint CACHE_FILE_VERSION = 20101108; +static const FXchar CACHE_FILE_NAME[] = PATHSEPSTRING "icontheme.cache"; +static const FXchar CACHE_SVG_NAME[] = PATHSEPSTRING "svg"; + +void GMIconTheme::save_cache() { + const FXuint cache_file_version = CACHE_FILE_VERSION; + + FXString dirs; + for (FXint i=0;itheme_dir_date) theme_dir_date=tm; + } + + /// Any dirs newer, then reload. + if (theme_dir_date==0 || cache_dir_date==0 || theme_dir_date>cache_dir_date) + return false; + + FXFileStream store; + if (store.open(GMApp::getCacheDirectory()+PATHSEPSTRING+"icontheme.cache",FXStreamLoad)) { + + store >> cache_file_version; + if (cache_file_version!=CACHE_FILE_VERSION) { + return false; + } + + for (FXint i=0;i> cache_dirs; + if (dirs!=cache_dirs) + return false; + + store >> cache_size; + if (cache_size!=smallsize) + return false; + + store >> cache_size; + if (cache_size!=mediumsize) + return false; + + store >> cache_size; + if (cache_size!=largesize) + return false; + + store >> n; + iconsets.no(n); + for (FXint i=0;ireg().readIntEntry("user-interface","icon-theme-small-size",SMALL_SIZE); + mediumsize = app->reg().readIntEntry("user-interface","icon-theme-medium-size",MEDIUM_SIZE); + largesize = app->reg().readIntEntry("user-interface","icon-theme-large-size",LARGE_SIZE); + + if (!FXPath::search(FXSystem::getEnvironment("PATH"),"rsvg-convert").empty()){ + rsvg=true; + } + + init_basedirs(basedirs); + if (!load_cache()) { + clear_svg_cache(); + build(); + save_cache(); + } + + const FXchar * theme = app->reg().readStringEntry("user-interface","icon-theme","Tango"); + if (theme) { + for (FXint i=0;icreate(); + } + + +GMIconTheme::~GMIconTheme() { + } + +GMIconTheme * GMIconTheme::instance(){ + return me; + } + +static void init_themedict(FXStringList & basedirs,FXStringDict & themedict){ + FXString * dirs=NULL; + for (FXint i=0;i=0;) { + themedirs = index[xx].readStringEntry("Icon Theme","Directories",NULL); + parents = index[xx].readStringEntry("Icon Theme","Inherits","hicolor"); + + inherits[x].insert(base.text(),(void*)(FXival)1); + + for (s=0;;s++) { + + dir = themedirs.section(',',s); + if (dir.empty()) break; + + const FXchar * type = index[xx].readStringEntry(dir.text(),"Type","Threshold"); + const FXint size = index[xx].readIntEntry(dir.text(),"Size",0); + const FXint threshold = index[xx].readIntEntry(dir.text(),"Threshold",2); + const FXint minsize = index[xx].readIntEntry(dir.text(),"MinSize",size); + const FXint maxsize = index[xx].readIntEntry(dir.text(),"MaxSize",size); + + if (comparecase(type,"scalable")==0) { + if (smallsize>=minsize && smallsize<=maxsize) + add_path(basedirs,base,dir,smallpath); + if (mediumsize>=minsize && mediumsize<=maxsize) + add_path(basedirs,base,dir,mediumpath); + if (largesize>=minsize && largesize<=maxsize) + add_path(basedirs,base,dir,largepath); + } + else if (comparecase(type,"fixed")==0) { + if (size==smallsize) + add_path(basedirs,base,dir,smallpath); + else if (size==mediumsize) + add_path(basedirs,base,dir,mediumpath); + else if (size==largesize) + add_path(basedirs,base,dir,largepath); + } + else { + if (FXABS(smallsize-size)<=threshold) + add_path(basedirs,base,dir,smallpath); + else if (FXABS(mediumsize-size)<=threshold) + add_path(basedirs,base,dir,mediumpath); + else if (FXABS(largesize-size)<=threshold) + add_path(basedirs,base,dir,largepath); + } + } + + /// Find next inherited + for (s=0,xx=-1;xx==-1;s++) { + base = parents.section(',',s); + if (base.empty() || inherits[x].find(base.text())) + break; + xx = (FXint)(FXival)indexmap.find(base.text()) - 1; + } + } + + if (smallpath.empty() && mediumpath.empty() && largepath.empty()) + continue; + + /// Finally add the theme + const FXint current=iconsets.no(); + iconsets.no(current+1); + iconsets[current].name = index[x].readStringEntry("Icon Theme","Name",themedir); + iconsets[current].dir = themedir; + iconsets[current].small.adopt(smallpath); + iconsets[current].medium.adopt(mediumpath); + iconsets[current].large.adopt(largepath); + } + + delete [] index; + delete [] inherits; + } + } + +FXImage * GMIconTheme::loadImage(const FXString & filename) { + FXImage * img=NULL; + const FXString ext = FXPath::extension(filename); + FXFileStream store; + if(store.open(filename,FXStreamLoad,65536)){ + if(comparecase(FXPNGImage::fileExt,ext)==0){ + img=new FXPNGImage(app); + } + else if(comparecase(FXJPGImage::fileExt,ext)==0 || comparecase("jpeg",ext)==0){ + img=new FXJPGImage(app); + } + else if(comparecase(FXBMPIcon::fileExt,ext)==0){ + img=new FXBMPImage(app); + } + else if(comparecase(FXGIFIcon::fileExt,ext)==0){ + img=new FXGIFImage(app); + } + else { + img=NULL; + } + if(img){ + if(img->loadPixels(store)) return img; + delete img; + img=NULL; + } + } + return NULL; + } + + +FXIcon * GMIconTheme::loadIcon(const FXString & filename) { + FXIcon * icon=NULL; + const FXString ext = FXPath::extension(filename); + FXFileStream store; + if(store.open(filename,FXStreamLoad,65536)){ + if(comparecase(FXPNGImage::fileExt,ext)==0){ + icon=new FXPNGIcon(app); + } + else if(comparecase(FXJPGImage::fileExt,ext)==0 || comparecase("jpeg",ext)==0){ + icon=new FXJPGIcon(app); + } + else if(comparecase(FXBMPIcon::fileExt,ext)==0){ + icon=new FXBMPIcon(app); + } + else if(comparecase(FXGIFIcon::fileExt,ext)==0){ + icon=new FXGIFIcon(app); + } + else if(comparecase(FXICOIcon::fileExt,ext)==0 || comparecase("cur",ext)==0){ + icon=new FXICOIcon(app); + } + else { + icon=NULL; + } + if(icon){ + icon->setOptions(IMAGE_SHMI|IMAGE_SHMP); + if(icon->loadPixels(store)) return icon; + delete icon; + icon=NULL; + } + } + return NULL; + } + +void GMIconTheme::clear_svg_cache() { +#ifdef DEBUG + fxmessage("clear svg cache\n"); +#endif + FXFile::removeFiles(get_svg_cache(),true); + } + +FXString GMIconTheme::get_svg_cache() { + return GMApp::getCacheDirectory(false) + CACHE_SVG_NAME; + } + + +void GMIconTheme::loadIcon(FXIconPtr & icon,const FXString & pathlist,FXint size,const FXchar * value,const FXColor blendcolor) { + FXIcon * ic=NULL; + FXString name,path,item; + FXint beg,end; + + FXString file = value; + FXString png = file + ".png"; + FXString svg = file + ".svg"; + + ///FIXME: Linux/Unix only + for(beg=0; pathlist[beg]; beg=end){ + while(pathlist[beg]==PATHLISTSEP) beg++; + for(end=beg; pathlist[end] && pathlist[end]!=PATHLISTSEP; end++){} + if(beg==end) break; + item=FXPath::expand(pathlist.mid(beg,end-beg)); + path=FXPath::absolute(item,png); + if(FXStat::exists(path)){ + name.adopt(path); + break; + } + if (rsvg) { + path=FXPath::absolute(item,svg); + if(FXStat::exists(path)){ + FXString dest = get_svg_cache() + PATHSEPSTRING + GMStringVal(size); + FXString target = dest + PATHSEPSTRING + svg + ".png"; + if (!FXStat::exists(target)) { + gm_make_path(dest); +#ifdef DEBUG + fxmessage("make %s\n",target.text()); +#endif + if (system(GMStringFormat("rsvg-convert --format=png --width=%d --height=%d -o %s %s\n",size,size,target.text(),path.text()).text())==0){ + name.adopt(target); + break; + } + } + else { + name.adopt(target); + break; + } + } + } + } + + if (!name.empty()) + ic=loadIcon(name); + + if (ic==NULL) + ic = new FXIcon(app,NULL,0,IMAGE_OWNED,size,size); + + if (icon) { + icon->destroy(); + icon->setData(ic->getData(),ic->getOptions(),ic->getWidth(),ic->getHeight()); + FXImageOwner::clear(ic); + delete ic; + } + else { + icon=ic; + } + + icon->blend(blendcolor); + FXIconThreshold::set(icon); + icon->create(); + } + +void GMIconTheme::loadSmall(FXIconPtr & icon,const FXchar * value,const FXColor blendcolor){ + if (iconsets.no()) + loadIcon(icon,iconsets[set].small,smallsize,value,blendcolor); + else + loadIcon(icon,FXString::null,smallsize,value,blendcolor); + } + +void GMIconTheme::loadMedium(FXIconPtr & icon,const FXchar * value,const FXColor blendcolor){ + if (iconsets.no()) + loadIcon(icon,iconsets[set].medium,mediumsize,value,blendcolor); + else + loadIcon(icon,FXString::null,mediumsize,value,blendcolor); + } + +void GMIconTheme::loadLarge(FXIconPtr & icon,const FXchar * value,const FXColor blendcolor){ + if (iconsets.no()) + loadIcon(icon,iconsets[set].large,largesize,value,blendcolor); + else + loadIcon(icon,FXString::null,largesize,value,blendcolor); + } + +void GMIconTheme::loadResource(FXIconPtr & icon,const unsigned char * data,const char * type) { + FXIconSource source(app); + FXIcon * newicon = source.loadIconData(data,type); + FXASSERT(newicon); + if (icon) { + icon->destroy(); + icon->setData(newicon->getData(),newicon->getOptions(),newicon->getWidth(),newicon->getHeight()); + FXImageOwner::clear(newicon); + delete newicon; + } + else { + icon=newicon; + } + } + +/* +FXImage* GMIconTheme::loadLarge(const char * value) { + FXImage * image = NULL; + if (iconsets.no()) { + FXASSERT(set>=0 && set=0 && setgetBaseColor(); + const FXColor backcolor = app->getBackColor(); + + + loadSmall(icon_copy,"edit-copy",backcolor); + loadSmall(icon_cut,"edit-cut",backcolor); + loadSmall(icon_paste,"edit-paste",backcolor); + loadSmall(icon_delete,"edit-delete",backcolor); + loadSmall(icon_undo,"edit-undo",backcolor); + loadSmall(icon_play,"media-playback-start",backcolor); + loadSmall(icon_pause,"media-playback-pause",backcolor); + loadSmall(icon_next,"media-skip-forward",backcolor); + loadSmall(icon_prev,"media-skip-backward",backcolor); + loadSmall(icon_stop,"media-playback-stop",backcolor); + + loadSmall(icon_import,"folder",backcolor); + loadSmall(icon_importfile,"document-open",backcolor); + loadSmall(icon_exit,"exit",backcolor); + loadSmall(icon_close,"window-close",backcolor); + loadSmall(icon_find,"edit-find",basecolor); + loadSmall(icon_sync,"view-refresh",basecolor); + loadSmall(icon_album,"media-optical",basecolor); + loadSmall(icon_artist,"system-users",basecolor); + loadSmall(icon_genre,"bookmark-new",basecolor); + loadSmall(icon_export,"document-save",basecolor); + loadSmall(icon_homepage,"applications-internet",basecolor); + loadSmall(icon_info,"help-browser",basecolor); + + loadSmall(icon_fullscreen,"window-fullscreen",backcolor); + + + loadMedium(icon_source_library,"user-home",backcolor); + loadMedium(icon_source_internetradio,"applications-internet",backcolor); + loadMedium(icon_source_playlist,"user-bookmarks",backcolor); + + loadSmall(icon_playlist,"user-bookmarks",basecolor); + loadSmall(icon_settings,"preferences-desktop",basecolor); + loadSmall(icon_edit,"accessories-text-editor",basecolor); + loadSmall(icon_sort,"view-sort-descending",basecolor); + + loadResource(icon_applogo,gogglesmm_32_png,"png"); + loadResource(icon_applogo_small,gogglesmm_16_png,"png"); + + loadLarge(icon_nocover,"media-optical",backcolor); + + loadMedium(icon_audio_volume_high,"audio-volume-high",basecolor); + loadMedium(icon_audio_volume_medium,"audio-volume-medium",basecolor); + loadMedium(icon_audio_volume_low,"audio-volume-low",basecolor); + loadMedium(icon_audio_volume_muted,"audio-volume-muted",basecolor); + loadMedium(icon_play_toolbar,"media-playback-start",basecolor); + loadMedium(icon_pause_toolbar,"media-playback-pause",basecolor); + loadMedium(icon_next_toolbar,"media-skip-forward",basecolor); + loadMedium(icon_prev_toolbar,"media-skip-backward",basecolor); + loadMedium(icon_stop_toolbar,"media-playback-stop",basecolor); + + icon_applogo->blend(basecolor); + icon_applogo_small->blend(basecolor); + icon_applogo->create(); + icon_applogo_small->create(); + } + + +FXint GMIconTheme::getNumThemes() const{ + return iconsets.no(); + } + +void GMIconTheme::setCurrentTheme(FXint s) { + set=s; + if (set>=0 && iconsets.no()) + app->reg().writeStringEntry("user-interface","icon-theme",iconsets[set].dir.text()); + else + app->reg().writeStringEntry("user-interface","icon-theme",""); + + clear_svg_cache(); + } + +FXint GMIconTheme::getCurrentTheme() const { + return set; + } + +FXString GMIconTheme::getThemeName(FXint i){ + FXASSERT(i>=0 && i GMIconSetList; + +class GMIconTheme { +private: + FXApp * app; +private: + GMIconSetList iconsets; + FXStringList basedirs; + FXint set; + FXint smallsize; + FXint mediumsize; + FXint largesize; + FXbool rsvg; +protected: + FXIcon * loadIcon(const FXString & filename); + FXImage * loadImage(const FXString & filename); +protected: + void loadIcon(FXIconPtr & icon,const FXString &pathlist,FXint size,const char * value,const FXColor blend); + void loadResource(FXIconPtr & icon,const unsigned char * data,const char * type); +protected: + FXbool load_cache(); + void save_cache(); + void build(); + FXString get_svg_cache(); + void clear_svg_cache(); +public: + FXIconPtr icon_applogo; + FXIconPtr icon_applogo_small; +public: + FXIconPtr icon_play; + FXIconPtr icon_pause; + FXIconPtr icon_stop; + FXIconPtr icon_next; + FXIconPtr icon_prev; + FXIconPtr icon_play_toolbar; + FXIconPtr icon_pause_toolbar; + FXIconPtr icon_stop_toolbar; + FXIconPtr icon_next_toolbar; + FXIconPtr icon_prev_toolbar; + FXIconPtr icon_copy; + FXIconPtr icon_cut; + FXIconPtr icon_paste; + FXIconPtr icon_exit; + FXIconPtr icon_close; + FXIconPtr icon_settings; + FXIconPtr icon_import; + FXIconPtr icon_importfile; + FXIconPtr icon_delete; + FXIconPtr icon_edit; + FXIconPtr icon_undo; + FXIconPtr icon_info; + FXIconPtr icon_homepage; + FXIconPtr icon_sort; + FXIconPtr icon_columns; + FXIconPtr icon_album; + FXIconPtr icon_artist; + FXIconPtr icon_genre; + FXIconPtr icon_source_library; + FXIconPtr icon_source_playlist; + FXIconPtr icon_source_internetradio; + FXIconPtr icon_playlist; + FXIconPtr icon_audio_volume_high; + FXIconPtr icon_audio_volume_medium; + FXIconPtr icon_audio_volume_low; + FXIconPtr icon_audio_volume_muted; + FXIconPtr icon_export; + FXIconPtr icon_find; + FXIconPtr icon_sync; + FXIconPtr icon_nocover; + FXIconPtr icon_fullscreen; +public: + FXCursorPtr cursor_hand; +private: + static GMIconTheme * me; +public: + static GMIconTheme * instance(); +public: + GMIconTheme(FXApp * app); + + void loadSmall(FXIconPtr & icon,const char * value,const FXColor blend); + + void loadMedium(FXIconPtr & icon,const char * value,const FXColor blend); + + void loadLarge(FXIconPtr & icon,const char * value,const FXColor blend); + + FXImage * loadMedium(const char * value); + + FXint getSmallSize() const { return smallsize; } + + FXint getMediumSize() const { return mediumsize; } + + FXint getLargeSize() const { return largesize; } + + FXint getNumThemes() const; + + void setCurrentTheme(FXint i); + + FXint getCurrentTheme() const; + + FXString getThemeName(FXint i); + + void load(); + + ~GMIconTheme(); + }; + +extern void gm_set_application_icon(FXWindow*); + + +#endif + diff --git a/src/GMImageView.cpp b/src/GMImageView.cpp new file mode 100644 index 0000000..ebf336d --- /dev/null +++ b/src/GMImageView.cpp @@ -0,0 +1,251 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2009-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#include "gmdefs.h" +#include "GMImageView.h" +#include +#include + +// non power of two textures are currently broken in Mesa 7.10 in combination with mip mapping +// fixed in Mesa 7.11-rc4 +//#define DISABLE_TEXTURE_NON_POWER_OF_TWO 1 + +// Disable MIPMAP if needed +//#define DISABLE_MIPMAP 1 + +FXDEFMAP(GMImageView) GMImageViewMap[]={ + FXMAPFUNC(SEL_PAINT,0,GMImageView::onPaint) + }; + +FXIMPLEMENT(GMImageView,FXGLCanvas,GMImageViewMap,ARRAYNUMBER(GMImageViewMap)); + + +GMImageView::GMImageView(){ + image_width=0; + image_height=0; + texture_id=0; + } + +GMImageView::GMImageView(FXComposite* p,FXGLVisual *vis,FXuint opts,FXint x,FXint y,FXint w,FXint h) : FXGLCanvas(p,vis,NULL,0,opts,x,y,w,h){ + image_width=0; + image_height=0; + texture_id=0; + } + +GMImageView::~GMImageView(){ + image_width=0; + image_height=0; + texture_id=0; + } + +void GMImageView::updateTexture(FXImage * image) { + FXint texture_max; + + if (!makeCurrent()) return; + if (image) { + + image_width=image->getWidth(); + image_height=image->getHeight(); + + /// Query Maximum Texture Size + glGetIntegerv(GL_MAX_TEXTURE_SIZE,&texture_max); + +#ifndef DISABLE_TEXTURE_NON_POWER_OF_TWO + /// Check if non power of two textures are supported. + const GLubyte * extensions = glGetString(GL_EXTENSIONS); + texture_power_of_two = (strstr((const char*)extensions,"GL_ARB_texture_non_power_of_two")==NULL); +#else + texture_power_of_two = true; +#endif + + /// Prescale to maximum texture size if necessary + if((image_width>texture_max) || (image_height>texture_max)){ + + if(image_width>image_height) + image->scale(texture_max,(texture_max*image_height)/image_width,1); + else + image->scale((texture_max*image_width)/image_height,texture_max,1); + + image_width=image->getWidth(); + image_height=image->getHeight(); + } + + /// Get a nice texture size + if (texture_power_of_two) { + texture_width=1; + texture_height=1; + while(image_width>texture_width) texture_width<<=1; + while(image_height>texture_height) texture_height<<=1; + } + else { + texture_width=image_width; + texture_height=image_height; + } + + FXASSERT(texture_width<=texture_max); + FXASSERT(texture_height<=texture_max); + + /// Generate a new texture if required. + if (texture_id==0) { + glGenTextures(1,&texture_id); + } + + glBindTexture(GL_TEXTURE_2D,texture_id); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP); + +#if defined(GL_VERSION_1_4) && !defined(DISABLE_MIPMAP) + glHint(GL_GENERATE_MIPMAP_HINT,GL_NICEST); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D,GL_GENERATE_MIPMAP,GL_TRUE); +#else + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); +#endif + + +#if FOXVERSION < FXVERSION(1,7,26) + if (texture_width==image_width && texture_height==image_height) { + glTexImage2D(GL_TEXTURE_2D,0,GL_RGB8,texture_width,texture_height,0,GL_RGBA,GL_UNSIGNED_BYTE,image->getData()); + } + else { + glTexImage2D(GL_TEXTURE_2D,0,GL_RGB8,texture_width,texture_height,0,GL_RGBA,GL_UNSIGNED_BYTE,NULL); + glTexSubImage2D(GL_TEXTURE_2D,0,0,0,image_width,image_height,GL_RGBA,GL_UNSIGNED_BYTE,image->getData()); + } +#else + if (texture_width==image_width && texture_height==image_height) { + glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA8,texture_width,texture_height,0,GL_BGRA,GL_UNSIGNED_INT_8_8_8_8_REV,image->getData()); + } + else { + glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA8,texture_width,texture_height,0,GL_BGRA,GL_UNSIGNED_INT_8_8_8_8_REV,NULL); + glTexSubImage2D(GL_TEXTURE_2D,0,0,0,image_width,image_height,GL_BGRA,GL_UNSIGNED_INT_8_8_8_8_REV,image->getData()); + } +#endif + } + else { + image_width=0; + image_height=0; + if (texture_id) { + glDeleteTextures(1,&texture_id); + texture_id=0; + } + } + makeNonCurrent(); + } + + +void GMImageView::setImage(FXImage * img) { + updateTexture(img); + recalc(); + update(); + } + + +FXint GMImageView::getDefaultWidth() const { + return 256; + } + +FXint GMImageView::getDefaultHeight() const { + return 256; + } + + +// Repaint the GL window +long GMImageView::onPaint(FXObject*,FXSelector,void*){ + FXGLVisual *vis=(FXGLVisual*)getVisual(); + FXfloat th=1.0f; + FXfloat tw=1.0f; +#if FOXVERSION < FXVERSION(1,7,0) + FXVec4f background(backColor); +#else + FXVec4f background=colorToVec4f(backColor); +#endif + FXASSERT(xid); + if(makeCurrent()){ + glViewport(0,0,getWidth(),getHeight()); + + glClearColor(background.x,background.y,background.z,background.w); + glClear(GL_COLOR_BUFFER_BIT); + + if (image_width && image_height) { + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + gluOrtho2D(0,getWidth(),getHeight(),0); + + glEnable(GL_TEXTURE_2D); + glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_DECAL); + glBindTexture(GL_TEXTURE_2D,texture_id); + + FXfloat scale = FXMIN(( width/(FXfloat)image_width),(height/(FXfloat)image_height)); + FXint xmin=0,xmax=image_width*scale; + FXint ymin=0,ymax=image_height*scale; + xmin=(getWidth()-xmax)/2.0; + ymin=(getHeight()-ymax)/2.0; + xmax+=xmin; + ymax+=ymin; + + if (texture_width!=image_width || texture_height!=image_height){ + th = (1.0f / (FXfloat)(texture_height))*image_height; + tw = (1.0f / (FXfloat)(texture_width))*image_width; + } + +#ifndef GL_VERSION_1_1 + FXint coords[8]={xmin,ymin, + xmin,ymax, + xmax,ymax, + xmax,ymin}; + FXfloat tex[8]={0.0f,0.0f, + 0.0f,th, + tw,th, + tw,0.0f}; + FXfloat colors[12]={background.x,background.y,background.z, + background.x,background.y,background.z, + background.x,background.y,background.z, + background.x,background.y,background.z}; + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + glVertexPointer(2,GL_INT,0,coords); + glColorPointer(3,GL_FLOAT,0,colors); + glTexCoordPointer(2,GL_FLOAT,0,tex); + + glDrawArrays(GL_QUADS,0,4); + + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); +#else + glColor4fv(background); + glBegin(GL_QUADS); + glTexCoord2f(0.0,0.0); glVertex2i(xmin,ymin); + glTexCoord2f(0.0,th); glVertex2i(xmin,ymax); + glTexCoord2f(tw,th); glVertex2i(xmax,ymax); + glTexCoord2f(tw,0.0); glVertex2i(xmax,ymin); + glEnd(); +#endif + + glDisable(GL_TEXTURE_2D); + } + if(vis->isDoubleBuffer()) swapBuffers(); + makeNonCurrent(); + } + return 1; + } diff --git a/src/GMImageView.h b/src/GMImageView.h new file mode 100644 index 0000000..01f58d4 --- /dev/null +++ b/src/GMImageView.h @@ -0,0 +1,52 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2009-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#ifndef GMIMAGEVIEW_H +#define GMIMAGEVIEW_H + +class GMImageView : public FXGLCanvas { +FXDECLARE(GMImageView) +protected: + FXint image_width; + FXint image_height; + FXuint texture_id; + FXint texture_width; + FXint texture_height; + FXbool texture_power_of_two; +protected: + void updateTexture(FXImage*); +public: + long onPaint(FXObject*,FXSelector,void*); +protected: + GMImageView(); +private: + GMImageView(const GMImageView&); + GMImageView &operator=(const GMImageView&); +public: + GMImageView(FXComposite* p,FXGLVisual *vis,FXuint opts=0,FXint x=0,FXint y=0,FXint w=0,FXint h=0); + + virtual FXint getDefaultWidth() const; + + virtual FXint getDefaultHeight() const; + + void setImage(FXImage * img); + + ~GMImageView(); + }; + +#endif diff --git a/src/GMImportDialog.cpp b/src/GMImportDialog.cpp new file mode 100644 index 0000000..cd28766 --- /dev/null +++ b/src/GMImportDialog.cpp @@ -0,0 +1,708 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2009-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#include +#include "gmdefs.h" +#include +#include +#include "icons.h" +#include "GMApp.h" +#include "GMIconTheme.h" +#include "GMPlayerManager.h" +#include "GMWindow.h" +#include "GMImportDialog.h" + + +extern const FXchar gmfilepatterns[]="All Music (*.mp3,*.ogg,*.oga,*.flac,*.mpc,*.wav" +#if defined(TAGLIB_WITH_MP4) && (TAGLIB_WITH_MP4==1) +",*.aac,*.m4a,*.m4p,*.mp4,*.m4b" +#endif +#if defined(TAGLIB_WITH_ASF) && (TAGLIB_WITH_ASF==1) +",*.wma,*.asf" +#endif +")\nFree Lossless Audio Codec (*.flac)\nWaveform Audio File Format (*.wav)\n" +"MPEG-1 Audio Layer 3 (*.mp3)\n" +#if defined(TAGLIB_WITH_MP4) && (TAGLIB_WITH_MP4==1) +"MPEG-4 Part 14 (*.mp4,*.m4a,*.m4p,*.aac,*.m4b)\n" +#endif +"Musepack (*.mpc)\n" +"Ogg Vorbis (*.ogg)\n" +"Ogg Audio (*.oga)\n" +#if defined(TAGLIB_WITH_ASF) && (TAGLIB_WITH_ASF==1) +"Window Media (*.wma,*,asf)\n" +#endif +"All Files (*.*)"; + + +const FXchar playlist_patterns[]="All Playlists (*.xspf,*.pls,*.m3u)\nXML Shareable Playlist (*.xspf)\nPLS (*.pls)\nM3U (*.m3u)"; + + +class GMDirSelector : public FXDirSelector { +FXDECLARE(GMDirSelector) +protected: + FXFileDict * filedict; + FXSettings * fileassoc; +protected: + FXIconPtr icon_folder; + FXIconPtr icon_folderopen; +protected: + GMDirSelector(){} +private: + GMDirSelector(const GMDirSelector&); + GMDirSelector &operator=(const GMDirSelector&); +public: + GMDirSelector(FXComposite *p,FXObject* tgt=NULL,FXSelector sel=0,FXuint opts=0,FXint x=0,FXint y=0,FXint w=0,FXint h=0); + ~GMDirSelector(); + void initFileDict(); + }; + + +FXIMPLEMENT(GMDirSelector,FXDirSelector,NULL,0); + +GMDirSelector::GMDirSelector(FXComposite *p,FXObject* tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h):FXDirSelector(p,tgt,sel,opts,x,y,w,h) { + fileassoc=new FXSettings(); + filedict=new FXFileDict(getApp(),fileassoc); + initFileDict(); + + delete accept->getParent(); + delete dirbox->getParent(); + delete dirname->getParent(); + + +// FXHorizontalFrame *buttons=new FXHorizontalFrame(this,LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X|PACK_UNIFORM_WIDTH); +// accept=new FXButton(buttons,tr("&OK"),NULL,NULL,0,BUTTON_INITIAL|BUTTON_DEFAULT|LAYOUT_RIGHT|FRAME_RAISED|FRAME_THICK,0,0,0,0,20,20); +// cancel=new FXButton(buttons,tr("&Cancel"),NULL,NULL,0,BUTTON_DEFAULT|LAYOUT_RIGHT|FRAME_RAISED|FRAME_THICK,0,0,0,0,20,20); + FXHorizontalFrame *field=new FXHorizontalFrame(this,LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X,0,0,0,0,0,0,0,0); + new FXLabel(field,tr("&Directory:"),NULL,JUSTIFY_LEFT|LAYOUT_CENTER_Y); + dirname=new GMTextField(field,25,this,ID_DIRNAME,LAYOUT_FILL_X|LAYOUT_CENTER_Y|FRAME_SUNKEN|FRAME_THICK); + + GMScrollFrame *frame=new GMScrollFrame(this); + dirbox=new FXDirList(frame,this,ID_DIRLIST,LAYOUT_FILL_X|LAYOUT_FILL_Y|LAYOUT_TOP|TREELIST_SHOWS_LINES|TREELIST_SHOWS_BOXES|TREELIST_BROWSESELECT); + + + + + GMScrollArea::replaceScrollbars(dirbox); + ((FXVerticalFrame*)dirbox->getParent())->setFrameStyle(FRAME_LINE); + +#if FOXVERSION >= FXVERSION(1,7,11) + dirbox->setAssociations(filedict,false); +#else + FXFileDict * old = dirbox->getAssociations(); + dirbox->setAssociations(filedict); + delete old; +#endif + } + +GMDirSelector::~GMDirSelector(){ + delete fileassoc; +#if FOXVERSION >= FXVERSION(1,7,11) + delete filedict; +#endif + } + + + +class GMFileSelector : public FXFileSelector { +FXDECLARE(GMFileSelector) +protected: + FXFileDict * filedict; + FXSettings * fileassoc; +protected: + FXIconPtr icon_file_small; + FXIconPtr icon_file_big; + FXIconPtr icon_audio_small; + FXIconPtr icon_audio_big; + FXIconPtr icon_folder_small; + FXIconPtr icon_folder_big; +protected: + GMFileSelector(){} +private: + GMFileSelector(const GMFileSelector&); + GMFileSelector &operator=(const GMFileSelector&); +public: + GMFileSelector(FXComposite *p,FXObject* tgt=NULL,FXSelector sel=0,FXuint opts=0,FXint x=0,FXint y=0,FXint w=0,FXint h=0); + ~GMFileSelector(); + + void initFileDict(); + void hideButtons(); + void getSelectedFiles(FXStringList & files); + + FXMatrix * optionFrame() const { return entryblock; } + }; + +FXIMPLEMENT(GMFileSelector,FXFileSelector,NULL,0); + +GMFileSelector::GMFileSelector(FXComposite *p,FXObject* tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h):FXFileSelector(p,tgt,sel,opts,x,y,w,h) { + FXWindow * window=NULL; + while((window=getFirst())!=NULL) delete window; + delete bookmarkmenu; bookmarkmenu=NULL; + + FXAccelTable *table=getShell()->getAccelTable(); + + navbuttons=new FXHorizontalFrame(this,LAYOUT_SIDE_TOP|LAYOUT_FILL_X,0,0,0,0, 0,0,0,0, 0,0); + entryblock=new FXMatrix(this,3,MATRIX_BY_COLUMNS|LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X,0,0,0,0,0,0,0,0); + new FXLabel(entryblock,tr("&File Name:"),NULL,JUSTIFY_LEFT|LAYOUT_CENTER_Y); + filename=new GMTextField(entryblock,25,this,ID_ACCEPT,TEXTFIELD_ENTER_ONLY|LAYOUT_FILL_COLUMN|LAYOUT_FILL_X|FRAME_SUNKEN|FRAME_THICK|LAYOUT_CENTER_Y); + new GMButton(entryblock,tr("&OK"),NULL,this,ID_ACCEPT,BUTTON_INITIAL|BUTTON_DEFAULT|FRAME_RAISED|FRAME_THICK|LAYOUT_FILL_X,0,0,0,0,20,20); + accept=new FXButton(navbuttons,FXString::null,NULL,NULL,0,LAYOUT_FIX_X|LAYOUT_FIX_Y|LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT,0,0,0,0, 0,0,0,0); + new FXLabel(entryblock,tr("File F&ilter:"),NULL,JUSTIFY_LEFT|LAYOUT_CENTER_Y); + + FXHorizontalFrame *filterframe=new FXHorizontalFrame(entryblock,LAYOUT_FILL_COLUMN|LAYOUT_FILL_X|LAYOUT_FILL_Y,0,0,0,0, 0,0,0,0); + filefilter=new GMComboBox(filterframe,10,this,ID_FILEFILTER,COMBOBOX_STATIC|LAYOUT_FILL_X|FRAME_SUNKEN|FRAME_THICK|LAYOUT_CENTER_Y); + filefilter->setNumVisible(4); + readonly=new GMCheckButton(filterframe,tr("Read Only"),NULL,0,ICON_BEFORE_TEXT|JUSTIFY_LEFT|LAYOUT_CENTER_Y); + cancel=new GMButton(entryblock,tr("&Cancel"),NULL,NULL,0,BUTTON_DEFAULT|FRAME_RAISED|FRAME_THICK|LAYOUT_FILL_X,0,0,0,0,20,20); + + fileboxframe=new GMScrollHFrame(this); + filebox=new FXFileList(fileboxframe,this,ID_FILELIST,ICONLIST_MINI_ICONS|ICONLIST_BROWSESELECT|ICONLIST_AUTOSIZE|LAYOUT_FILL_X|LAYOUT_FILL_Y); + GMScrollArea::replaceScrollbars(filebox); + new FXLabel(navbuttons,tr("Directory:"),NULL,LAYOUT_CENTER_Y); + dirbox=new FXDirBox(navbuttons,this,ID_DIRTREE,DIRBOX_NO_OWN_ASSOC|FRAME_LINE|LAYOUT_FILL_X|LAYOUT_CENTER_Y,0,0,0,0,1,1,1,1); + dirbox->setNumVisible(5); + dirbox->setAssociations(filebox->getAssociations()); + GMTreeListBox::replace(dirbox); + + bookmarkmenu=new GMMenuPane(this,POPUP_SHRINKWRAP); + new GMMenuCommand(bookmarkmenu,tr("&Set bookmark\t\tBookmark current directory."),markicon,this,ID_BOOKMARK); + new GMMenuCommand(bookmarkmenu,tr("&Clear bookmarks\t\tClear bookmarks."),clearicon,&bookmarks,FXRecentFiles::ID_CLEAR); + FXMenuSeparator* sep1=new FXMenuSeparator(bookmarkmenu); + sep1->setTarget(&bookmarks); + sep1->setSelector(FXRecentFiles::ID_ANYFILES); + new GMMenuCommand(bookmarkmenu,FXString::null,NULL,&bookmarks,FXRecentFiles::ID_FILE_1); + new GMMenuCommand(bookmarkmenu,FXString::null,NULL,&bookmarks,FXRecentFiles::ID_FILE_2); + new GMMenuCommand(bookmarkmenu,FXString::null,NULL,&bookmarks,FXRecentFiles::ID_FILE_3); + new GMMenuCommand(bookmarkmenu,FXString::null,NULL,&bookmarks,FXRecentFiles::ID_FILE_4); + new GMMenuCommand(bookmarkmenu,FXString::null,NULL,&bookmarks,FXRecentFiles::ID_FILE_5); + new GMMenuCommand(bookmarkmenu,FXString::null,NULL,&bookmarks,FXRecentFiles::ID_FILE_6); + new GMMenuCommand(bookmarkmenu,FXString::null,NULL,&bookmarks,FXRecentFiles::ID_FILE_7); + new GMMenuCommand(bookmarkmenu,FXString::null,NULL,&bookmarks,FXRecentFiles::ID_FILE_8); + new GMMenuCommand(bookmarkmenu,FXString::null,NULL,&bookmarks,FXRecentFiles::ID_FILE_9); + new GMMenuCommand(bookmarkmenu,FXString::null,NULL,&bookmarks,FXRecentFiles::ID_FILE_10); + + new FXFrame(navbuttons,LAYOUT_FIX_WIDTH,0,0,4,1); + new GMButton(navbuttons,tr("\tGo up one directory\tMove up to higher directory."),updiricon,this,ID_DIRECTORY_UP,BUTTON_TOOLBAR|FRAME_RAISED,0,0,0,0, 3,3,3,3); + new GMButton(navbuttons,tr("\tGo to home directory\tBack to home directory."),homeicon,this,ID_HOME,BUTTON_TOOLBAR|FRAME_RAISED,0,0,0,0, 3,3,3,3); + new GMButton(navbuttons,tr("\tGo to work directory\tBack to working directory."),workicon,this,ID_WORK,BUTTON_TOOLBAR|FRAME_RAISED,0,0,0,0, 3,3,3,3); + GMMenuButton *bookmenu=new GMMenuButton(navbuttons,tr("\tBookmarks\tVisit bookmarked directories."),markicon,bookmarkmenu,MENUBUTTON_NOARROWS|MENUBUTTON_ATTACH_LEFT|MENUBUTTON_TOOLBAR|FRAME_RAISED,0,0,0,0, 3,3,3,3); + bookmenu->setTarget(this); + bookmenu->setSelector(ID_BOOKMENU); + new GMButton(navbuttons,tr("\tCreate new directory\tCreate new directory."),newicon,this,ID_NEW,BUTTON_TOOLBAR|FRAME_RAISED,0,0,0,0, 3,3,3,3); + new GMButton(navbuttons,tr("\tShow list\tDisplay directory with small icons."),listicon,filebox,FXFileList::ID_SHOW_MINI_ICONS,BUTTON_TOOLBAR|FRAME_RAISED,0,0,0,0, 3,3,3,3); + new GMButton(navbuttons,tr("\tShow icons\tDisplay directory with big icons."),iconsicon,filebox,FXFileList::ID_SHOW_BIG_ICONS,BUTTON_TOOLBAR|FRAME_RAISED,0,0,0,0, 3,3,3,3); + new GMButton(navbuttons,tr("\tShow details\tDisplay detailed directory listing."),detailicon,filebox,FXFileList::ID_SHOW_DETAILS,BUTTON_TOOLBAR|FRAME_RAISED,0,0,0,0, 3,3,3,3); + new GMToggleButton(navbuttons,tr("\tShow hidden files\tShow hidden files and directories."),tr("\tHide Hidden Files\tHide hidden files and directories."),hiddenicon,shownicon,filebox,FXFileList::ID_TOGGLE_HIDDEN,TOGGLEBUTTON_TOOLBAR|FRAME_RAISED,0,0,0,0, 3,3,3,3); + bookmarks.setTarget(this); + bookmarks.setSelector(ID_VISIT); + readonly->hide(); + if(table){ + table->addAccel(MKUINT(KEY_BackSpace,0),this,FXSEL(SEL_COMMAND,ID_DIRECTORY_UP)); + table->addAccel(MKUINT(KEY_Delete,0),this,FXSEL(SEL_COMMAND,ID_DELETE)); + table->addAccel(MKUINT(KEY_h,CONTROLMASK),this,FXSEL(SEL_COMMAND,ID_HOME)); + table->addAccel(MKUINT(KEY_w,CONTROLMASK),this,FXSEL(SEL_COMMAND,ID_WORK)); + table->addAccel(MKUINT(KEY_n,CONTROLMASK),this,FXSEL(SEL_COMMAND,ID_NEW)); + table->addAccel(MKUINT(KEY_a,CONTROLMASK),filebox,FXSEL(SEL_COMMAND,FXFileList::ID_SELECT_ALL)); + table->addAccel(MKUINT(KEY_b,CONTROLMASK),filebox,FXSEL(SEL_COMMAND,FXFileList::ID_SHOW_BIG_ICONS)); + table->addAccel(MKUINT(KEY_s,CONTROLMASK),filebox,FXSEL(SEL_COMMAND,FXFileList::ID_SHOW_MINI_ICONS)); + table->addAccel(MKUINT(KEY_l,CONTROLMASK),filebox,FXSEL(SEL_COMMAND,FXFileList::ID_SHOW_DETAILS)); + } + setSelectMode(SELECTFILE_ANY); // For backward compatibility, this HAS to be the default! + //setPatternList(allfiles); + setDirectory(FXSystem::getCurrentDirectory()); + filebox->setFocus(); + accept->hide(); + navigable=TRUE; + + + fileassoc=new FXSettings(); + filedict=new FXFileDict(getApp(),fileassoc); + initFileDict(); +#if FOXVERSION >= FXVERSION(1,7,11) + filebox->setAssociations(filedict,false); + dirbox->setAssociations(filedict,false); +#else + FXFileDict * old = filebox->getAssociations(); + filebox->setAssociations(filedict); + dirbox->setAssociations(filedict); + delete old; +#endif + } + +GMFileSelector::~GMFileSelector(){ + delete fileassoc; +#if FOXVERSION >= FXVERSION(1,7,11) + delete filedict; +#endif + } + + +void GMFileSelector::hideButtons() { + entryblock->childAtIndex(2)->hide(); + cancel->hide(); + } + +void GMFileSelector::getSelectedFiles(FXStringList & files) { + if(selectmode==SELECTFILE_MULTIPLE_ALL){ + for(FXint i=0; igetNumItems(); i++){ + if(filebox->isItemSelected(i) && filebox->getItemFilename(i)!=".." && filebox->getItemFilename(i)!="."){ + files.append(filebox->getItemPathname(i)); + } + } + } + else { + /// implement me + FXASSERT(0); + } + } + + + + + +static const FXchar * const filetypes[]={ + "mp3",";MPEG-1 Audio Layer 3", + "ogg",";Ogg Vorbis", + "oga",";Ogg Audio", + "flac",";Free Lossless Audio Codec", + "wav",";Waveform Audio File Format", + "mpc",";Musepack", +#if defined(TAGLIB_WITH_ASF) && (TAGLIB_WITH_ASF==1) + "wma",";Windows Media", + "asf",";Windows Media", +#endif +#if defined(TAGLIB_WITH_MP4) && (TAGLIB_WITH_MP4==1) + "m4a",";MPEG-4 Part 14", + "m4b",";MPEG-4 Part 14", + "m4p",";MPEG-4 Part 14", + "aac",";MPEG-4 Part 14", +#endif + "m3u",";M3U Playlist", + "pls",";PLS Playlist", + "xspf",";XML Shareable Playlist" + }; + + +void GMFileSelector::initFileDict() { + FXFileAssoc * assoc=NULL; + const FXColor backcolor = FXApp::instance()->getBackColor(); + + GMIconTheme::instance()->loadSmall(icon_file_small,"text-x-generic",backcolor); + GMIconTheme::instance()->loadMedium(icon_file_big,"text-x-generic",backcolor); + GMIconTheme::instance()->loadSmall(icon_audio_small,"audio-x-generic",backcolor); + GMIconTheme::instance()->loadMedium(icon_audio_big,"audio-x-generic",backcolor); + GMIconTheme::instance()->loadSmall(icon_folder_small,"folder",backcolor); + GMIconTheme::instance()->loadMedium(icon_folder_big,"folder",backcolor); + + for (FXuint i=0;ireplace(filetypes[i],filetypes[i+1]); + assoc->bigicon = icon_audio_big; + assoc->miniicon = icon_audio_small; + assoc = filedict->replace(FXString(filetypes[i]).upper().text(),filetypes[i+1]); + assoc->bigicon = icon_audio_big; + assoc->miniicon = icon_audio_small; + } + + assoc = filedict->replace(FXFileDict::defaultFileBinding,";"); + assoc->bigicon = icon_file_big; + assoc->miniicon = icon_file_small; + + assoc = filedict->replace(FXFileDict::defaultDirBinding,";"); + assoc->bigicon = icon_folder_big; + assoc->miniicon = icon_folder_small; + } + +void GMDirSelector::initFileDict() { + FXFileAssoc * assoc=NULL; + const FXColor backcolor = FXApp::instance()->getBackColor(); + GMIconTheme::instance()->loadSmall(icon_folder,"folder",backcolor); + GMIconTheme::instance()->loadSmall(icon_folderopen,"folder-open",backcolor); + + assoc = filedict->replace(FXFileDict::defaultDirBinding,";"); + assoc->miniiconopen = icon_folderopen; + assoc->miniicon = icon_folder; + } + + + + + + + + + + + +FXIMPLEMENT(GMFileDialog,FXFileDialog,NULL,0); + +// Construct file fialog box +GMFileDialog::GMFileDialog(FXWindow* owner,const FXString& name,FXuint opts,FXint x,FXint y,FXint w,FXint h): + FXFileDialog(owner,name,opts,x,y,w,h){ + delete filebox; + filebox=new GMFileSelector(this,NULL,0,LAYOUT_FILL_X|LAYOUT_FILL_Y); + filebox->acceptButton()->setTarget(this); + filebox->acceptButton()->setSelector(FXDialogBox::ID_ACCEPT); + filebox->cancelButton()->setTarget(this); + filebox->cancelButton()->setSelector(FXDialogBox::ID_CANCEL); + } + +// Construct free-floating file dialog box +GMFileDialog::GMFileDialog(FXApp* a,const FXString& name,FXuint opts,FXint x,FXint y,FXint w,FXint h): + FXFileDialog(a,name,opts,x,y,w,h){ + delete filebox; + filebox=new GMFileSelector(this,NULL,0,LAYOUT_FILL_X|LAYOUT_FILL_Y); + filebox->acceptButton()->setTarget(this); + filebox->acceptButton()->setSelector(FXDialogBox::ID_ACCEPT); + filebox->cancelButton()->setTarget(this); + filebox->cancelButton()->setSelector(FXDialogBox::ID_CANCEL); + } + + +FXIMPLEMENT(GMExportDialog,GMFileDialog,NULL,0); + +// Construct file fialog box +GMExportDialog::GMExportDialog(FXWindow* owner,const FXString& name,FXuint opts,FXint x,FXint y,FXint w,FXint h): + GMFileDialog(owner,name,opts,x,y,w,h){ + GMFileSelector * fileselector = dynamic_cast(filebox); + FXMatrix * entryblock = fileselector->optionFrame(); + new FXLabel(entryblock,tr("&Options:"),NULL,JUSTIFY_LEFT|LAYOUT_CENTER_Y); + check_relative=new GMCheckButton(entryblock,"Relative Paths",NULL,0,CHECKBUTTON_NORMAL|LAYOUT_FILL_COLUMN); + new FXFrame(entryblock,FRAME_NONE); + } + +// Construct free-floating file dialog box +GMExportDialog::GMExportDialog(FXApp* a,const FXString& name,FXuint opts,FXint x,FXint y,FXint w,FXint h): + GMFileDialog(a,name,opts,x,y,w,h){ + GMFileSelector * fileselector = dynamic_cast(filebox); + FXMatrix * entryblock = fileselector->optionFrame(); + new FXLabel(entryblock,tr("&Options:"),NULL,JUSTIFY_LEFT|LAYOUT_CENTER_Y); + check_relative=new GMCheckButton(entryblock,"Relative Paths",NULL,0,CHECKBUTTON_NORMAL|LAYOUT_FILL_COLUMN); + new FXFrame(entryblock,FRAME_NONE); + } + + + + + + + + + +FXDEFMAP(GMImportDialog) GMImportDialogMap[]={ + FXMAPFUNC(SEL_UPDATE,FXDialogBox::ID_ACCEPT,GMImportDialog::onUpdAccept), + FXMAPFUNCS(SEL_UPDATE,GMImportDialog::ID_SYNC_NEW,GMImportDialog::ID_SYNC_REMOVE_ALL,GMImportDialog::onUpdSync), + FXMAPFUNCS(SEL_COMMAND,GMImportDialog::ID_SYNC_NEW,GMImportDialog::ID_SYNC_REMOVE_ALL,GMImportDialog::onCmdSync), + FXMAPFUNC(SEL_COMMAND,GMImportDialog::ID_PARSE_METHOD,GMImportDialog::onCmdParseMethod) + + }; + +FXIMPLEMENT(GMImportDialog,FXDialogBox,GMImportDialogMap,ARRAYNUMBER(GMImportDialogMap)) + +GMImportDialog::GMImportDialog(FXWindow *p,FXuint m) : FXDialogBox(p,FXString::null,DECOR_BORDER|DECOR_TITLE|DECOR_RESIZE,0,0,500,400,0,0,0,0,0,0), mode(m) { + + + if (mode&IMPORT_SYNC) + setTitle(tr("Synchronize Folder")); + else if (mode&IMPORT_PLAYLIST) + setTitle(tr("Import Playlist")); + else + setTitle(tr("Import Music")); + + + /// Create a fixed font, about the same size as the normal font + FXint size = FXApp::instance()->getNormalFont()->getSize(); + font_fixed = new FXFont(FXApp::instance(),"mono",(int)size/10,FXFont::Normal,FXFont::Straight,FONTENCODING_UNICODE,FXFont::NonExpanded,FXFont::Modern|FXFont::Fixed); + + dirselector=NULL; + fileselector=NULL; + + target_track_from_filelist.connect(GMPlayerManager::instance()->getPreferences().import.track_from_filelist); + target_replace_underscores.connect(GMPlayerManager::instance()->getPreferences().import.replace_underscores); + target_default_field.connect(GMPlayerManager::instance()->getPreferences().import.default_field); + target_parse_method.connect(GMPlayerManager::instance()->getPreferences().import.parse_method,this,ID_PARSE_METHOD); + target_filename_template.connect(GMPlayerManager::instance()->getPreferences().import.filename_template); + + target_exclude_dir.connect(GMPlayerManager::instance()->getPreferences().import.exclude_folder); + target_exclude_file.connect(GMPlayerManager::instance()->getPreferences().import.exclude_file); + + const FXuint labelstyle=LAYOUT_CENTER_Y|LABEL_NORMAL|LAYOUT_RIGHT; + const FXuint textfieldstyle=TEXTFIELD_ENTER_ONLY|LAYOUT_FILL_X|FRAME_SUNKEN|FRAME_THICK|LAYOUT_FILL_COLUMN; + + FXGroupBox * grpbox; + FXMatrix * matrix; + FXVerticalFrame * vframe; + FXHorizontalFrame * hframe; + + FXVerticalFrame * main=new FXVerticalFrame(this,LAYOUT_FILL_X|LAYOUT_FILL_Y); + GMTabBook * tabbook=new GMTabBook(main,NULL,0,LAYOUT_FILL_X|LAYOUT_FILL_Y|LAYOUT_RIGHT,0,0,0,0,0,0,0,0); + + + if (mode&IMPORT_FROMFILE) { + new GMTabItem(tabbook,tr("&File(s)"),NULL,TAB_TOP_NORMAL,0,0,0,0,5,5); + vframe = new GMTabFrame(tabbook); + fileselector = new GMFileSelector(vframe,NULL,0,LAYOUT_FILL_X|LAYOUT_FILL_Y); + FXString searchdir = getApp()->reg().readStringEntry("directories","last-import-files",NULL); + if (searchdir.empty()) getDefaultSearchDirectory(searchdir); + if (mode&IMPORT_PLAYLIST) { + fileselector->setPatternList(playlist_patterns); + fileselector->setSelectMode(SELECTFILE_EXISTING); + } + else { + fileselector->setPatternList(gmfilepatterns); + fileselector->setSelectMode(SELECTFILE_MULTIPLE); + } + fileselector->setCurrentPattern(0); +#if FOXVERSION < FXVERSION(1,7,20) + fileselector->setMatchMode(FILEMATCH_CASEFOLD); +#else + fileselector->setMatchMode(FXPath::CaseFold); +#endif + fileselector->setDirectory(searchdir); + fileselector->hideButtons(); + fileselector->acceptButton()->setTarget(this); + fileselector->acceptButton()->setSelector(FXDialogBox::ID_ACCEPT); + fileselector->cancelButton()->setTarget(this); + fileselector->cancelButton()->setSelector(FXDialogBox::ID_CANCEL); + } + else if (mode&IMPORT_FROMDIR) { + new GMTabItem(tabbook,tr("&Directory"),NULL,TAB_TOP_NORMAL,0,0,0,0,5,5); + vframe = new GMTabFrame(tabbook); + + GMCheckButton * excludetoggle = new GMCheckButton(vframe,tr("Exclude Filter\tFilter out directories and/or files based on pattern"),NULL,0,CHECKBUTTON_NORMAL|CHECKBUTTON_PLUS); + + matrix = new FXMatrix(vframe,2,MATRIX_BY_COLUMNS|LAYOUT_FILL_X,0,0,0,0,16,4,0,0); + new FXLabel(matrix,tr("Folders:"),NULL,labelstyle); + new GMTextField(matrix,20,&target_exclude_dir,FXDataTarget::ID_VALUE,textfieldstyle); + new FXLabel(matrix,tr("Files:"),NULL,labelstyle); + new GMTextField(matrix,20,&target_exclude_file,FXDataTarget::ID_VALUE,textfieldstyle); + + excludetoggle->setTarget(matrix); + excludetoggle->setSelector(FXWindow::ID_TOGGLESHOWN); + + if (GMPlayerManager::instance()->getPreferences().import.exclude_folder.empty() && GMPlayerManager::instance()->getPreferences().import.exclude_file.empty()) + matrix->hide(); + + dirselector = new GMDirSelector(vframe,NULL,0,LAYOUT_FILL_X|LAYOUT_FILL_Y); + FXString searchdir = getApp()->reg().readStringEntry("directories","last-import-dirs",NULL); + if (searchdir.empty()) getDefaultSearchDirectory(searchdir); + dirselector->setDirectory(searchdir); + } + + if (mode&IMPORT_SYNC) { + new GMTabItem(tabbook,tr("&Sync"),NULL,TAB_TOP_NORMAL,0,0,0,0,5,5); + vframe = new GMTabFrame(tabbook); + grpbox = new FXGroupBox(vframe,tr("Sync Operation"),FRAME_NONE|LAYOUT_FILL_X,0,0,0,0,20); + grpbox->setFont(GMApp::instance()->getThickFont()); + + new GMCheckButton(grpbox,tr("Import new tracks\tImports files not yet in the database."),this,ID_SYNC_NEW); + new GMCheckButton(grpbox,tr("Remove tracks that have been deleted from disk"),this,ID_SYNC_REMOVE_MISSING); + matrix = new FXMatrix(grpbox,2,MATRIX_BY_COLUMNS|LAYOUT_FILL_X,0,0,0,0,0,0,0,0); + new GMCheckButton(matrix,tr("Update existing tracks:"),this,ID_SYNC_UPDATE); + new FXRadioButton(matrix,tr("Modified since last import\tOnly reread the tag when the file has been modified."),this,ID_SYNC_UPDATE_MODIFIED,RADIOBUTTON_NORMAL|LAYOUT_CENTER_Y|LAYOUT_LEFT,0,0,0,0); + new FXFrame(matrix,FRAME_NONE); + new FXRadioButton(matrix,tr("All\tAlways read the tags"),this,ID_SYNC_UPDATE_ALL,RADIOBUTTON_NORMAL|LAYOUT_CENTER_Y|LAYOUT_LEFT,0,0,0,0); + new GMCheckButton(grpbox,tr("Remove tracks found in folder from database"),this,ID_SYNC_REMOVE_ALL); + } + + new GMTabItem(tabbook,tr("&Track"),NULL,TAB_TOP_NORMAL,0,0,0,0,5,5); + vframe = new GMTabFrame(tabbook); + + grpbox = new FXGroupBox(vframe,tr("Parse Settings"),FRAME_NONE|LAYOUT_FILL_X,0,0,0,0,20); + grpbox->setFont(GMApp::instance()->getThickFont()); + + matrix = new FXMatrix(grpbox,2,MATRIX_BY_COLUMNS|LAYOUT_FILL_X); + new FXLabel(matrix,tr("Parse info from:"),NULL,labelstyle); + + hframe = new FXHorizontalFrame(matrix,LAYOUT_FILL_COLUMN,0,0,0,0,0,0,0,0); + new GMRadioButton(hframe,tr("Tag"),&target_parse_method,FXDataTarget::ID_OPTION+GMImportOptions::PARSE_TAG,JUSTIFY_LEFT|ICON_BEFORE_TEXT|LAYOUT_CENTER_Y); + new GMRadioButton(hframe,tr("Filename"),&target_parse_method,FXDataTarget::ID_OPTION+GMImportOptions::PARSE_FILENAME,JUSTIFY_LEFT|ICON_BEFORE_TEXT|LAYOUT_CENTER_Y); + new GMRadioButton(hframe,tr("Both"),&target_parse_method,FXDataTarget::ID_OPTION+GMImportOptions::PARSE_BOTH,JUSTIFY_LEFT|ICON_BEFORE_TEXT|LAYOUT_CENTER_Y); + + new FXLabel(matrix,tr("Default value:"),NULL,labelstyle); + new GMTextField(matrix,10,&target_default_field,FXDataTarget::ID_VALUE,textfieldstyle|LAYOUT_FILL_COLUMN); + + new FXFrame(matrix,FRAME_NONE); + new GMCheckButton(matrix,tr("Set track number based on scan order."),&target_track_from_filelist,FXDataTarget::ID_VALUE,LAYOUT_FILL_COLUMN|CHECKBUTTON_NORMAL); + + template_grpbox = new FXGroupBox(vframe,tr("Filename Template"),FRAME_NONE|LAYOUT_FILL_X,0,0,0,0,20); + template_grpbox->setFont(GMApp::instance()->getThickFont()); + + FXLabel * label = new FXLabel(template_grpbox,tr("%T - title %A - album name\n" + "%P - album artist name %p - track artist name \n" + "%N - track number %G - genre"),NULL,FRAME_LINE|JUSTIFY_LEFT,0,0,0,0,30); + label->setFont(font_fixed); + label->setBackColor(getApp()->getTipbackColor()); + label->setBorderColor(getApp()->getShadowColor()); + + new FXSeparator(template_grpbox,SEPARATOR_GROOVE|LAYOUT_FILL_X); + matrix = new FXMatrix(template_grpbox,2,MATRIX_BY_COLUMNS|LAYOUT_FILL_X); + new FXLabel(matrix,tr("Template:"),NULL,labelstyle); + new GMTextField(matrix,10,&target_filename_template,FXDataTarget::ID_VALUE,textfieldstyle); + new FXFrame(matrix,FRAME_NONE); + new GMCheckButton(matrix,tr("Replace underscores with spaces"),&target_replace_underscores,FXDataTarget::ID_VALUE,CHECKBUTTON_NORMAL|LAYOUT_FILL_COLUMN); + + if (GMPlayerManager::instance()->getPreferences().import.parse_method==GMImportOptions::PARSE_TAG) { + template_grpbox->hide(); + } + else { + template_grpbox->show(); + } + + FXHorizontalFrame *closebox=new FXHorizontalFrame(main,LAYOUT_BOTTOM|LAYOUT_FILL_X|PACK_UNIFORM_WIDTH,0,0,0,0,0,0,0,0); + if (mode&IMPORT_SYNC) + new GMButton(closebox,tr("&Sync"),NULL,this,FXDialogBox::ID_ACCEPT,BUTTON_INITIAL|BUTTON_DEFAULT|LAYOUT_RIGHT|FRAME_RAISED|FRAME_THICK,0,0,0,0, 20,20); + else + new GMButton(closebox,tr("&Import"),NULL,this,FXDialogBox::ID_ACCEPT,BUTTON_INITIAL|BUTTON_DEFAULT|LAYOUT_RIGHT|FRAME_RAISED|FRAME_THICK,0,0,0,0, 20,20); + new GMButton(closebox,tr("&Cancel"),NULL,this,FXDialogBox::ID_CANCEL,BUTTON_DEFAULT|LAYOUT_RIGHT|FRAME_RAISED|FRAME_THICK,0,0,0,0, 20,20); + } + +long GMImportDialog::onCmdAccept(FXObject*sender,FXSelector sel,void*ptr){ + return FXDialogBox::onCmdAccept(sender,sel,ptr); + } + +long GMImportDialog::onUpdAccept(FXObject*sender,FXSelector,void*){ + if (mode&IMPORT_SYNC) { + if (GMPlayerManager::instance()->getPreferences().sync.import_new || + GMPlayerManager::instance()->getPreferences().sync.remove_missing || + GMPlayerManager::instance()->getPreferences().sync.update || + GMPlayerManager::instance()->getPreferences().sync.remove_all) + sender->handle(this,FXSEL(SEL_COMMAND,ID_ENABLE),NULL); + else + sender->handle(this,FXSEL(SEL_COMMAND,ID_DISABLE),NULL); + } + else { + sender->handle(this,FXSEL(SEL_COMMAND,ID_ENABLE),NULL); + } + return 1; + } + +long GMImportDialog::onCmdSync(FXObject*,FXSelector sel,void*ptr){ + FXbool check=((FXint)(FXival)ptr)!=0; + switch(FXSELID(sel)){ + case ID_SYNC_NEW : GMPlayerManager::instance()->getPreferences().sync.import_new=check; + break; + case ID_SYNC_REMOVE_MISSING : GMPlayerManager::instance()->getPreferences().sync.remove_missing=check; + break; + case ID_SYNC_UPDATE : GMPlayerManager::instance()->getPreferences().sync.update=check; + break; + case ID_SYNC_UPDATE_ALL : GMPlayerManager::instance()->getPreferences().sync.update_always=check; + break; + case ID_SYNC_UPDATE_MODIFIED: GMPlayerManager::instance()->getPreferences().sync.update_always=!check; + break; + case ID_SYNC_REMOVE_ALL : GMPlayerManager::instance()->getPreferences().sync.remove_all=check; + break; + } + return 1; + } + + +long GMImportDialog::onUpdSync(FXObject*sender,FXSelector sel,void*){ + FXbool enable=true; + FXbool check=false; + switch(FXSELID(sel)){ + case ID_SYNC_NEW : check= (GMPlayerManager::instance()->getPreferences().sync.remove_all) ? false : GMPlayerManager::instance()->getPreferences().sync.import_new; + enable=!GMPlayerManager::instance()->getPreferences().sync.remove_all; + break; + case ID_SYNC_REMOVE_MISSING : check= (GMPlayerManager::instance()->getPreferences().sync.remove_all) ? false : GMPlayerManager::instance()->getPreferences().sync.remove_missing; + enable=!GMPlayerManager::instance()->getPreferences().sync.remove_all; + break; + case ID_SYNC_UPDATE : check= (GMPlayerManager::instance()->getPreferences().sync.remove_all) ? false : GMPlayerManager::instance()->getPreferences().sync.update; + enable=!GMPlayerManager::instance()->getPreferences().sync.remove_all; + break; + case ID_SYNC_UPDATE_ALL : check= (GMPlayerManager::instance()->getPreferences().sync.remove_all) ? false : GMPlayerManager::instance()->getPreferences().sync.update_always; + enable=(!GMPlayerManager::instance()->getPreferences().sync.remove_all && GMPlayerManager::instance()->getPreferences().sync.update); + break; + case ID_SYNC_UPDATE_MODIFIED: check= (GMPlayerManager::instance()->getPreferences().sync.remove_all) ? false : !GMPlayerManager::instance()->getPreferences().sync.update_always; + enable=(!GMPlayerManager::instance()->getPreferences().sync.remove_all && GMPlayerManager::instance()->getPreferences().sync.update); + break; + case ID_SYNC_REMOVE_ALL : check=GMPlayerManager::instance()->getPreferences().sync.remove_all; + break; + } + if (enable) + sender->handle(this,FXSEL(SEL_COMMAND,ID_ENABLE),NULL); + else + sender->handle(this,FXSEL(SEL_COMMAND,ID_DISABLE),NULL); + + if (check) + sender->handle(this,FXSEL(SEL_COMMAND,ID_CHECK),NULL); + else + sender->handle(this,FXSEL(SEL_COMMAND,ID_UNCHECK),NULL); + return 1; + } + + +void GMImportDialog::getSelectedFiles(FXStringList & files){ + if (dirselector) { + getApp()->reg().writeStringEntry("directories","last-import-dirs",dirselector->getDirectory().text()); + files.append(dirselector->getDirectory()); + } + else if (fileselector){ + getApp()->reg().writeStringEntry("directories","last-import-files",fileselector->getDirectory().text()); + fileselector->getSelectedFiles(files); + } + } + +FXString GMImportDialog::getFilename() const{ + getApp()->reg().writeStringEntry("directories","last-import-files",fileselector->getDirectory().text()); + return fileselector->getFilename(); + } + +long GMImportDialog::onCmdParseMethod(FXObject*,FXSelector,void*){ + if (GMPlayerManager::instance()->getPreferences().import.parse_method==GMImportOptions::PARSE_TAG) { + template_grpbox->hide(); + } + else { + template_grpbox->show(); + } + template_grpbox->getParent()->recalc(); + template_grpbox->getParent()->layout(); + return 1; + } + + + +void GMImportDialog::getDefaultSearchDirectory(FXString & directory){ + const FXchar * musicdir[]={ + "Music", + "music", + "mp3", + "MP3", + "Mp3", + "ogg", + "OGG", + "Ogg", + "wav", +#if defined(TAGLIB_WITH_MP4) && (TAGLIB_WITH_MP4==1) + "mp4", + "MP4", + "Mp4", +#endif + NULL + }; + FXString test; + FXString home=FXSystem::getHomeDirectory(); + for (int i=0;musicdir[i]!=NULL;i++){ + test = home + PATHSEPSTRING + musicdir[i]; + if (FXStat::exists(test) && FXStat::isDirectory(test)){ + directory=test; + break; + } + } + if (directory.empty()) directory=home; + } + + diff --git a/src/GMImportDialog.h b/src/GMImportDialog.h new file mode 100644 index 0000000..431aa21 --- /dev/null +++ b/src/GMImportDialog.h @@ -0,0 +1,111 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#ifndef GMIMPORTDIALOG_H +#define GMIMPORTDIALOG_H + +class GMFileSelector; +class GMDirSelector; + + +enum { + IMPORT_FROMPASTE = 0x0, + IMPORT_FROMFILE = 0x1, + IMPORT_FROMDIR = 0x2, + IMPORT_SYNC = 0x4, + IMPORT_PLAYLIST = 0x8 + }; + +class GMFileDialog : public FXFileDialog { +FXDECLARE(FXFileDialog) +protected: + GMFileDialog(){} +private: + GMFileDialog(const GMFileDialog&); + GMFileDialog &operator=(const GMFileDialog&); +public: + GMFileDialog(FXWindow* owner,const FXString& name,FXuint opts=0,FXint x=0,FXint y=0,FXint w=500,FXint h=300); + GMFileDialog(FXApp* a,const FXString& name,FXuint opts=0,FXint x=0,FXint y=0,FXint w=500,FXint h=300); + }; + + +class GMExportDialog : public GMFileDialog { +FXDECLARE(GMExportDialog) +protected: + GMCheckButton * check_relative; +protected: + GMExportDialog(){} +private: + GMExportDialog(const GMExportDialog&); + GMExportDialog &operator=(const GMExportDialog&); +public: + GMExportDialog(FXWindow* owner,const FXString& name,FXuint opts=0,FXint x=0,FXint y=0,FXint w=500,FXint h=300); + GMExportDialog(FXApp* a,const FXString& name,FXuint opts=0,FXint x=0,FXint y=0,FXint w=500,FXint h=300); + + void setRelativePath(FXbool b) { check_relative->setCheck(b); } + FXbool getRelativePath() const { return check_relative->getCheck(); } + }; + + +class GMImportDialog : public FXDialogBox { +FXDECLARE(GMImportDialog) +protected: + FXuint mode; +protected: + FXDataTarget target_track_from_filelist; + FXDataTarget target_replace_underscores; + FXDataTarget target_default_field; + FXDataTarget target_exclude_dir; + FXDataTarget target_exclude_file; + FXDataTarget target_parse_method; + FXDataTarget target_filename_template; +protected: + GMFileSelector * fileselector; + GMDirSelector * dirselector; + FXGroupBox * template_grpbox; +protected: + FXFontPtr font_fixed; +protected: + GMImportDialog(){} + void getDefaultSearchDirectory(FXString&); +private: + GMImportDialog(const GMImportDialog&); + GMImportDialog &operator=(const GMImportDialog&); +public: + enum { + ID_SYNC_NEW = FXDialogBox::ID_LAST, + ID_SYNC_REMOVE_MISSING, + ID_SYNC_UPDATE, + ID_SYNC_UPDATE_ALL, + ID_SYNC_UPDATE_MODIFIED, + ID_SYNC_REMOVE_ALL, + ID_PARSE_METHOD + }; +public: + long onCmdSync(FXObject*,FXSelector,void*); + long onUpdSync(FXObject*,FXSelector,void*); + long onCmdAccept(FXObject*,FXSelector,void*); + long onUpdAccept(FXObject*,FXSelector,void*); + long onCmdParseMethod(FXObject*,FXSelector,void*); +public: + GMImportDialog(FXWindow * p,FXuint mode=IMPORT_FROMDIR); + void getSelectedFiles(FXStringList & files); + FXString getFilename() const; + }; + +#endif diff --git a/src/GMList.cpp b/src/GMList.cpp new file mode 100644 index 0000000..71a95f2 --- /dev/null +++ b/src/GMList.cpp @@ -0,0 +1,430 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#include "gmdefs.h" +#include "GMApp.h" +#include "GMList.h" +#include "GMSource.h" +#include "GMPlayerManager.h" +#include "GMTrackList.h" +#include "GMTrackView.h" + +#define ICON_SPACING 4 // Spacing between icon and label +#define SIDE_SPACING 6 // Left or right spacing between items +#define LINE_SPACING 4 // Line spacing between items + + +static inline FXbool begins_with_keyword(const FXString & t){ + for (FXint i=0;igetPreferences().gui_sort_keywords.no();i++){ + if (comparecase(t,GMPlayerManager::instance()->getPreferences().gui_sort_keywords[i],GMPlayerManager::instance()->getPreferences().gui_sort_keywords[i].length())==0) return TRUE; + } + return FALSE; + } + +FXint genre_list_sort(const FXListItem* pa,const FXListItem* pb){ + return comparecase(pa->getText(),pb->getText()); + } +FXint genre_list_sort_reverse(const FXListItem* pa,const FXListItem* pb){ + return -comparecase(pa->getText(),pb->getText()); + } + +FXint artist_list_sort(const FXListItem* pa,const FXListItem* pb){ + register FXint a=0,b=0; + if (begins_with_keyword(pa->getText())) a=FXMIN(pa->getText().length()-1,pa->getText().find(' ')+1); + if (begins_with_keyword(pb->getText())) b=FXMIN(pb->getText().length()-1,pb->getText().find(' ')+1); + return comparecase(&pa->getText()[a],&pb->getText()[b]); + } + +FXint artist_list_sort_reverse(const FXListItem* pa,const FXListItem* pb){ + register FXint a=0,b=0; + if (begins_with_keyword(pa->getText())) a=FXMIN(pa->getText().length()-1,pa->getText().find(' ')+1); + if (begins_with_keyword(pb->getText())) b=FXMIN(pb->getText().length()-1,pb->getText().find(' ')+1); + return comparecase(&pb->getText()[b],&pa->getText()[a]); + } + +FXint album_list_sort(const FXListItem* pa,const FXListItem* pb){ + register FXint a=0,b=0; + const GMAlbumListItem * sa = (GMAlbumListItem*)pa; + const GMAlbumListItem * sb = (GMAlbumListItem*)pb; + if (GMTrackView::album_by_year) { + if (sa->year>sb->year) return 1; + else if (sa->yearyear) return -1; + } + if (begins_with_keyword(pa->getText())) a=FXMIN(pa->getText().length()-1,pa->getText().find(' ')+1); + if (begins_with_keyword(pb->getText())) b=FXMIN(pb->getText().length()-1,pb->getText().find(' ')+1); + return comparecase(&pa->getText()[a],&pb->getText()[b]); + } + +FXint album_list_sort_reverse(const FXListItem* pa,const FXListItem* pb){ + register FXint a=0,b=0; + const GMAlbumListItem * sa = (GMAlbumListItem*)pa; + const GMAlbumListItem * sb = (GMAlbumListItem*)pb; + if (GMTrackView::album_by_year) { + if (sa->year>sb->year) return -1; + else if (sa->yearyear) return 1; + } + if (begins_with_keyword(pa->getText())) a=FXMIN(pa->getText().length()-1,pa->getText().find(' ')+1); + if (begins_with_keyword(pb->getText())) b=FXMIN(pb->getText().length()-1,pb->getText().find(' ')+1); + return -comparecase(&pa->getText()[a],&pb->getText()[b]); + } + + +FXint source_list_sort(const FXTreeItem* pa,const FXTreeItem* pb){ + const GMSource * const sa = (const GMSource*)pa->getData(); + const GMSource * const sb = (const GMSource*)pb->getData(); + if (sa->getType()>sb->getType()) return 1; + else if (sa->getType()getType()) return -1; + register FXint a=0,b=0; + if (begins_with_keyword(pa->getText())) a=FXMIN(pa->getText().length()-1,pa->getText().find(' ')+1); + if (begins_with_keyword(pb->getText())) b=FXMIN(pb->getText().length()-1,pb->getText().find(' ')+1); + return compareversion(&pa->getText()[a],&pb->getText()[b]); + } + +FXint source_list_sort_reverse(const FXTreeItem* pa,const FXTreeItem* pb){ + const GMSource * const sa = (const GMSource*)pa->getData(); + const GMSource * const sb = (const GMSource*)pb->getData(); + if (sa->getType()>sb->getType()) return 1; + else if (sa->getType()getType()) return -1; + register FXint a=0,b=0; + if (begins_with_keyword(pa->getText())) a=FXMIN(pa->getText().length()-1,pa->getText().find(' ')+1); + if (begins_with_keyword(pb->getText())) b=FXMIN(pb->getText().length()-1,pb->getText().find(' ')+1); + return -compareversion(&pa->getText()[a],&pb->getText()[b]); + } + + + +// Object implementation +FXIMPLEMENT(GMListItem,FXListItem,NULL,0) + +// Object implementation +FXIMPLEMENT(GMAlbumListItem,GMListItem,NULL,0) + + + +// Draw item +void GMListItem::draw(const GMList* list,FXDC& dc,FXint xx,FXint yy,FXint ww,FXint hh) const { + register FXFont *font=list->getFont(); + register FXint ih=0,th=0; + if(icon) ih=icon->getHeight(); + if(!label.empty()) th=font->getFontHeight(); + if(isSelected()) + dc.setForeground(list->getSelBackColor()); +// else +// dc.setForeground(list->getBackColor()); // FIXME maybe paint background in onPaint? + + dc.fillRectangle(xx,yy,ww,hh); + if(hasFocus()){ + dc.drawFocusRectangle(xx+1,yy+1,ww-2,hh-2); + } + xx+=SIDE_SPACING/2; + if(icon){ + dc.drawIcon(icon,xx,yy+(hh-ih)/2); + xx+=ICON_SPACING+icon->getWidth(); + } + if(!label.empty()){ + if(!isEnabled()) + dc.setForeground(makeShadowColor(list->getBackColor())); + else if(isSelected()) + dc.setForeground(list->getSelTextColor()); + else + dc.setForeground(list->getTextColor()); + dc.drawText(xx,yy+(hh-th)/2+font->getFontAscent(),label); + } + } + + + + + +// Map +FXDEFMAP(GMList) GMListMap[]={ + FXMAPFUNC(SEL_PAINT,0,GMList::onPaint), + }; + +// Object implementation +FXIMPLEMENT(GMList,FXList,GMListMap,ARRAYNUMBER(GMListMap)) + +// List +GMList::GMList(){ + rowcolor=FXRGB(255,255,255); + } + +GMList::GMList(FXComposite *p,FXObject* tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h) : FXList(p,tgt,sel,opts,x,y,w,h) { + rowcolor=GMPlayerManager::instance()->getPreferences().gui_row_color; + thickfont=font; + + delete vertical; + delete horizontal; + delete corner; + + vertical=new GMScrollBar(this,this,GMList::ID_VSCROLLED,SCROLLBAR_VERTICAL); + horizontal=new GMScrollBar(this,this,GMList::ID_HSCROLLED,SCROLLBAR_HORIZONTAL); + corner=new GMScrollCorner(this); + } + +GMList::~GMList(){ + } + +// Create custom item +FXListItem *GMList::createItem(const FXString& text,FXIcon* icon,void* ptr){ + return new GMListItem(text,icon,ptr); + } + +// Draw item list +long GMList::onPaint(FXObject*,FXSelector,void* ptr){ + FXEvent* event=(FXEvent*)ptr; + FXDCWindow dc(this,event); + FXint i,y,h; + + // Set font + dc.setFont(font); + + // Paint items + y=pos_y; + for(i=0; igetHeight(this); + if(event->rect.y<=y+h && yrect.y+event->rect.h){ + if (i%2) + dc.setForeground(rowcolor); + else + dc.setForeground(backColor); + + if (items[i]->getData()==(void*)(FXival)-1) + dc.setFont(thickfont); + else + dc.setFont(font); + +#if FOXVERSION < FXVERSION(1,7,0) + ((GMListItem*)items[i])->draw(this,dc,pos_x,y,FXMAX(listWidth,viewport_w),h); +#else + ((GMListItem*)items[i])->draw(this,dc,pos_x,y,FXMAX(listWidth,getVisibleWidth()),h); +#endif + } + y+=h; + } + + // Paint blank area below items + if(yrect.y+event->rect.h){ + dc.setForeground(backColor); + dc.fillRectangle(event->rect.x,y,event->rect.w,event->rect.y+event->rect.h-y); + } + return 1; + } + + +// Object implementation +FXIMPLEMENT(GMTreeItem,FXTreeItem,NULL,0) + +GMTreeItem::GMTreeItem(){ + } + +// Get item height +FXint GMTreeItem::getHeight(const FXTreeList* list) const { + register FXFont *font=list->getFont(); + register FXint th=0,oih=0,cih=0; + if(openIcon) oih=openIcon->getHeight(); + if(closedIcon) cih=closedIcon->getHeight(); + if(!label.empty()) th=font->getFontHeight(); +// if (oih>16) oih+=4; +// if (cih>16) cih+=4; + return FXMAX3(th,oih,cih)+4; + } + + +// Draw item +void GMTreeItem::draw(const FXTreeList* list,FXDC& dc,FXint xx,FXint yy,FXint /* ww*/,FXint hh) const { + register FXIcon *icon=(state&OPENED)?openIcon:closedIcon; + register FXFont *font=list->getFont(); + register FXint th=0,tw=0,ih=0,iw=0;//ox=xx,oy=yy; + xx+=SIDE_SPACING/2; + if(icon){ + iw=icon->getWidth(); + ih=icon->getHeight(); + dc.drawIcon(icon,xx,yy+(hh-ih)/2); + xx+=ICON_SPACING+iw; + } + + if(!label.empty()){ + tw=4+font->getTextWidth(label.text(),label.length()); + th=4+font->getFontHeight(); + yy+=(hh-th)/2; +// if(isSelected()){ +// dc.setForeground(list->getSelBackColor()); +// dc.fillRectangle(xx,yy,tw,th); +// dc.fillRectangle(ox,oy,ww,hh); +// } + if(hasFocus()){ + dc.drawFocusRectangle(xx+1,yy+1,tw-2,th-2); + } + if(!isEnabled()) + dc.setForeground(makeShadowColor(list->getBackColor())); + else if(isSelected()) + dc.setForeground(list->getSelTextColor()); + else + dc.setForeground(list->getTextColor()); + dc.drawText(xx+2,yy+font->getFontAscent()+2,label); + } + } + + + +#define DEFAULT_INDENT 8 // Indent between parent and child +#define HALFBOX_SIZE 4 // Half box size +#define BOX_FUDGE 3 // Fudge border around box + + +// Map +FXDEFMAP(GMTreeList) GMTreeListMap[]={ + FXMAPFUNC(SEL_PAINT,0,GMTreeList::onPaint) + }; + +// Object implementation +FXIMPLEMENT(GMTreeList,FXTreeList,GMTreeListMap,ARRAYNUMBER(GMTreeListMap)) + +GMTreeList::GMTreeList(){ + } + /// Construct a new, initially empty tree list +GMTreeList::GMTreeList(FXComposite *p,FXObject* tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h) : FXTreeList(p,tgt,sel,opts,x,y,w,h) { + rowcolor=GMPlayerManager::instance()->getPreferences().gui_row_color; + GMScrollArea::replaceScrollbars(this); + } + +FXTreeItem* GMTreeList::createItem(const FXString& text,FXIcon* oi,FXIcon* ci,void* ptr) { + return (FXTreeItem*)new GMTreeItem(text,oi,ci,ptr); + } + +// Draw item list +long GMTreeList::onPaint(FXObject*,FXSelector,void* ptr){ + FXEvent* event=(FXEvent*)ptr; + FXTreeItem* item=firstitem; + FXTreeItem* p; + FXint yh,xh,x,y,w,h,xp,hh,cc=0; + FXDCWindow dc(this,event); + dc.setFont(font); + x=pos_x; + y=pos_y; + if(options&TREELIST_ROOT_BOXES) x+=(4+indent); + while(item && yrect.y+event->rect.h){ + w=item->getWidth(this); + h=item->getHeight(this); + cc++; + if(event->rect.y<=y+h){ + + // Draw item + dc.setForeground(backColor); + dc.fillRectangle(0,y,width,h); + + if (!item->isSelected()) { + if (cc%2) { +// dc.setForeground(backColor); +// dc.fillRectangle(0,y,x+2,h); + + dc.setForeground(rowcolor); + dc.fillRectangle(x,y,width-x,h); + // dc.fillRectangle(x+2,y,width-x-2,h); + } + else { + dc.setForeground(backColor); + dc.fillRectangle(0,y,width,h); + } + } + else { +// dc.setForeground(backColor); +// dc.fillRectangle(0,y,x+2,h); + + dc.setForeground(getSelBackColor()); +// dc.fillRectangle(x+2,y,width-x-2,h); + dc.fillRectangle(x,y,width-x,h); + } + + ((GMTreeItem*)item)->draw(this,dc,x,y,w,h); + + + // Show other paraphernalia such as dotted lines and expand-boxes + if((options&(TREELIST_SHOWS_LINES|TREELIST_SHOWS_BOXES)) && (((GMTreeItem*)item)->parent || (options&TREELIST_ROOT_BOXES))){ + hh=h/2; + yh=y+hh; + xh=x-indent+(SIDE_SPACING/2); + dc.setForeground(lineColor); + dc.setBackground(backColor); + dc.setStipple(STIPPLE_GRAY,pos_x&1,pos_y&1); + if(options&TREELIST_SHOWS_LINES){ // Connect items with lines + p=((GMTreeItem*)item)->parent; + xp=xh; + dc.setFillStyle(FILL_OPAQUESTIPPLED); + while(p){ + xp-=(indent+p->getHeight(this)/2); + if(((GMTreeItem*)p)->next) dc.fillRectangle(xp,y,1,h); + p=((GMTreeItem*)p)->parent; + } + if((options&TREELIST_SHOWS_BOXES) && (item->hasItems() || item->getFirst())){ + if(((GMTreeItem*)item)->prev || ((GMTreeItem*)item)->parent) dc.fillRectangle(xh,y,1,yh-y-HALFBOX_SIZE); + if(((GMTreeItem*)item)->next) dc.fillRectangle(xh,yh+HALFBOX_SIZE,1,y+h-yh-HALFBOX_SIZE); + } + else{ + if(((GMTreeItem*)item)->prev || ((GMTreeItem*)item)->parent) dc.fillRectangle(xh,y,1,hh); + if(((GMTreeItem*)item)->next) dc.fillRectangle(xh,yh,1,h); + dc.fillRectangle(xh,yh,x+(SIDE_SPACING/2)-2-xh,1); + } + dc.setFillStyle(FILL_SOLID); + } + + // Boxes before items for expand/collapse of item + if((options&TREELIST_SHOWS_BOXES) && (item->hasItems() || item->getFirst())){ + dc.setFillStyle(FILL_OPAQUESTIPPLED); + dc.fillRectangle(xh+4,yh,(SIDE_SPACING/2)-2,1); + dc.setFillStyle(FILL_SOLID); + dc.drawRectangle(xh-HALFBOX_SIZE,yh-HALFBOX_SIZE,HALFBOX_SIZE+HALFBOX_SIZE,HALFBOX_SIZE+HALFBOX_SIZE); + dc.setForeground(textColor); + dc.fillRectangle(xh-HALFBOX_SIZE+2,yh,HALFBOX_SIZE+HALFBOX_SIZE-3,1); + if(!(options&TREELIST_AUTOSELECT) && !item->isExpanded()){ + dc.fillRectangle(xh,yh-HALFBOX_SIZE+2,1,HALFBOX_SIZE+HALFBOX_SIZE-3); + } + } + } + } + + y+=h; + + // Move on to the next item + if(((GMTreeItem*)item)->first && ((options&TREELIST_AUTOSELECT) || ((GMTreeItem*)item)->isExpanded())){ + x+=(indent+h/2); + item=((GMTreeItem*)item)->first; + continue; + } + while(!((GMTreeItem*)item)->next && ((GMTreeItem*)item)->parent){ + item=((GMTreeItem*)item)->parent; + x-=(indent+item->getHeight(this)/2); + } + item=((GMTreeItem*)item)->next; + } + if(yrect.y+event->rect.h){ + dc.setForeground(backColor); + dc.fillRectangle(event->rect.x,y,event->rect.w,event->rect.y+event->rect.h-y); + } + return 1; + } + + + + + + + + + diff --git a/src/GMList.h b/src/GMList.h new file mode 100644 index 0000000..f75cbd1 --- /dev/null +++ b/src/GMList.h @@ -0,0 +1,131 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#ifndef GMLIST_H +#define GMLIST_H + +extern FXint genre_list_sort(const FXListItem* pa,const FXListItem* pb); +extern FXint genre_list_sort_reverse(const FXListItem* pa,const FXListItem* pb); +extern FXint artist_list_sort(const FXListItem* pa,const FXListItem* pb); +extern FXint artist_list_sort_reverse(const FXListItem* pa,const FXListItem* pb); +extern FXint album_list_sort(const FXListItem* pa,const FXListItem* pb); +extern FXint album_list_sort_reverse(const FXListItem* pa,const FXListItem* pb); +extern FXint source_list_sort(const FXTreeItem* pa,const FXTreeItem* pb); +extern FXint source_list_sort_reverse(const FXTreeItem* pa,const FXTreeItem* pb); + +class GMList; + +class GMListItem : public FXListItem { + FXDECLARE(GMListItem) + friend class GMList; +private: + GMListItem(const GMListItem&); + GMListItem& operator=(const GMListItem&); +protected: + GMListItem() {} + virtual void draw(const GMList* list,FXDC& dc,FXint x,FXint y,FXint w,FXint h) const; +public: + /// Construct new item with given text, icon, and user-data + GMListItem(const FXString& text,FXIcon* ic=NULL,void* ptr=NULL): FXListItem(text,ic,ptr) { } + }; + +class GMAlbumListItem : public GMListItem { + FXDECLARE(GMAlbumListItem) + friend class GMList; +public: + FXIntList albums; + FXint year; +private: + GMAlbumListItem(const GMAlbumListItem&); + GMAlbumListItem& operator=(const GMAlbumListItem&); +protected: + GMAlbumListItem() {} +public: + /// Construct new item with given text, icon, and user-data + GMAlbumListItem(const FXString& text,FXIcon* ic=NULL,FXint id=0,FXint _year=0): GMListItem(text,ic,(void*)(FXival)(id)), year(_year) { state|=DRAGGABLE; } + }; + + +class GMList : public FXList { + FXDECLARE(GMList) +protected: + FXFont * thickfont; + FXColor rowcolor; +protected: + GMList(); + void recompute(); + virtual FXListItem *createItem(const FXString& text,FXIcon* icon,void* ptr); +private: + GMList(const GMList&); + GMList &operator=(const GMList&); +public: + long onPaint(FXObject*,FXSelector,void*); +public: + /// Construct a list with initially no items in it + GMList(FXComposite *p,FXObject* tgt=NULL,FXSelector sel=0,FXuint opts=LIST_NORMAL,FXint x=0,FXint y=0,FXint w=0,FXint h=0); + + FXColor getRowColor() const { return rowcolor; } + + void setRowColor(FXColor c) { rowcolor=c; update(); } + + void setThickFont(FXFont * f) { thickfont=f;} + + virtual ~GMList(); + }; + +/// Tree list Item +class GMTreeItem : public FXTreeItem { + FXDECLARE(GMTreeItem) + friend class GMTreeList; +protected: + GMTreeItem(); +private: + GMTreeItem(const GMTreeItem&); + GMTreeItem& operator=(const GMTreeItem&); +protected: + virtual void draw(const FXTreeList* list,FXDC& dc,FXint x,FXint y,FXint w,FXint h) const; +public: + /// Constructor + GMTreeItem(const FXString& text,FXIcon* oi=NULL,FXIcon* ci=NULL,void* ptr=NULL): FXTreeItem(text,oi,ci,ptr){} + + virtual FXuint getItemType() const { return SOURCE_UNKNOWN; } + + /// Return height of item as drawn in list + virtual FXint getHeight(const FXTreeList* list) const; + }; + +class GMTreeList : public FXTreeList { + FXDECLARE(GMTreeList) +protected: + FXColor rowcolor; +protected: + GMTreeList(); + virtual FXTreeItem* createItem(const FXString& text,FXIcon* oi,FXIcon* ci,void* ptr); +private: + GMTreeList(const GMTreeList&); + GMTreeList& operator=(const GMTreeList&); +public: + long onPaint(FXObject*,FXSelector,void*); +public: + /// Construct a new, initially empty tree list + GMTreeList(FXComposite *p,FXObject* tgt=NULL,FXSelector sel=0,FXuint opts=TREELIST_NORMAL,FXint x=0,FXint y=0,FXint w=0,FXint h=0); + + void setRowColor(FXColor c) { rowcolor=c; update(); } + }; + +#endif diff --git a/src/GMMediaPlayerService.cpp b/src/GMMediaPlayerService.cpp new file mode 100644 index 0000000..f41c6e9 --- /dev/null +++ b/src/GMMediaPlayerService.cpp @@ -0,0 +1,339 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2010-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#include "gmdefs.h" +#include "GMDBus.h" +#include "GMSource.h" +#include "GMWindow.h" + +#include +#include "GMPlayer.h" +#include "GMPlayerManager.h" +#include "GMMediaPlayerService.h" + +#include "mpris_xml.h" + +static void gm_mpris_track_to_dict(DBusMessageIter * iter,const GMTrack & track) { + DBusMessageIter array; + dbus_message_iter_open_container(iter,DBUS_TYPE_ARRAY,"{sv}",&array); + gm_dbus_dict_append_string(&array,"title",track.title); + gm_dbus_dict_append_string(&array,"artist",track.artist); + gm_dbus_dict_append_string(&array,"album",track.album); + gm_dbus_dict_append_string(&array,"tracknumber",GMStringVal(track.no)); + gm_dbus_dict_append_uint32(&array,"time",track.time); + gm_dbus_dict_append_uint32(&array,"year",track.year); + gm_dbus_dict_append_string(&array,"genre",track.genre); + gm_dbus_dict_append_string(&array,"location",gm_make_url(track.mrl)); + dbus_message_iter_close_container(iter,&array); + } + +/* +static void gm_dbus_dict_append_track(DBusMessageIter * iter,const FXchar * key,const GMTrack & track) { + DBusMessageIter entry; + DBusMessageIter variant; + dbus_message_iter_open_container(iter,DBUS_TYPE_DICT_ENTRY,0,&entry); + dbus_message_iter_append_basic(&entry,DBUS_TYPE_STRING,&key); + dbus_message_iter_open_container(&entry,DBUS_TYPE_VARIANT,"a{sv}",&variant); + gm_mpris_track_to_dict(&variant,track); + dbus_message_iter_close_container(&entry,&variant); + dbus_message_iter_close_container(iter,&entry); + } + +*/ + +static const FXchar MPRIS_DBUS_NAME[]="org.mpris.gogglesmm"; +static const FXchar MPRIS_DBUS_INTERFACE[]="org.freedesktop.MediaPlayer"; +static const FXchar MPRIS_DBUS_PLAYER[]="/Player"; +static const FXchar MPRIS_DBUS_ROOT[]="/"; +static const FXchar MPRIS_DBUS_TRACKLIST[]="/TrackList"; + +static FXint mpris_get_caps(GMPlayerManager * p) { + FXint caps=0; + enum { + MPRIS_CAPS_NONE = 0, + MPRIS_CAPS_CAN_GO_NEXT = 1 << 0, + MPRIS_CAPS_CAN_GO_PREV = 1 << 1, + MPRIS_CAPS_CAN_PAUSE = 1 << 2, + MPRIS_CAPS_CAN_PLAY = 1 << 3, + MPRIS_CAPS_CAN_SEEK = 1 << 4, + MPRIS_CAPS_CAN_PROVIDE_METADATA = 1 << 5, + MPRIS_CAPS_CAN_HAS_TRACKLIST = 1 << 6 + }; + if (p->can_play() || p->can_unpause()) caps|=MPRIS_CAPS_CAN_PLAY; + if (p->can_pause()) caps|=MPRIS_CAPS_CAN_PAUSE; + if (p->can_prev()) caps|=MPRIS_CAPS_CAN_GO_PREV; + if (p->can_next()) caps|=MPRIS_CAPS_CAN_GO_NEXT; + caps|=MPRIS_CAPS_CAN_PROVIDE_METADATA; + return caps; + } + + +static void gm_mpris_get_status(DBusMessageIter * iter,GMPlayerManager * p) { + enum { + MPRIS_STATUS_PLAYING = 0, + MPRIS_STATUS_PAUSED = 1, + MPRIS_STATUS_STOPPED = 2, + }; + + FXint playstatus=0; + FXint playmode=0; + FXint playnext=0; + FXint playrepeat=0; + + if (p->can_unpause()) + playstatus=MPRIS_STATUS_PAUSED; + else if (p->can_pause()) + playstatus=MPRIS_STATUS_PLAYING; + else + playstatus=MPRIS_STATUS_STOPPED; + + DBusMessageIter str; + dbus_message_iter_open_container(iter,DBUS_TYPE_STRUCT,NULL,&str); + dbus_message_iter_append_basic(&str,DBUS_TYPE_INT32,&playstatus); + dbus_message_iter_append_basic(&str,DBUS_TYPE_INT32,&playmode); + dbus_message_iter_append_basic(&str,DBUS_TYPE_INT32,&playnext); + dbus_message_iter_append_basic(&str,DBUS_TYPE_INT32,&playrepeat); + dbus_message_iter_close_container(iter,&str); + } + +FXIMPLEMENT(GMMediaPlayerService,FXObject,NULL,0) + +GMMediaPlayerService::GMMediaPlayerService(GMDBus * b) : bus(b),published(false){ + + memset(&root_vtable,0,sizeof(DBusObjectPathVTable)); + root_vtable.message_function=&root_filter; + memset(&player_vtable,0,sizeof(DBusObjectPathVTable)); + player_vtable.message_function=&player_filter; + memset(&tracklist_vtable,0,sizeof(DBusObjectPathVTable)); + tracklist_vtable.message_function=&tracklist_filter; + + int result = dbus_bus_request_name(bus->connection(),MPRIS_DBUS_NAME,DBUS_NAME_FLAG_DO_NOT_QUEUE,NULL); + if (result == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER ) { + dbus_connection_register_object_path(bus->connection(),MPRIS_DBUS_ROOT,&root_vtable,this); + dbus_connection_register_object_path(bus->connection(),MPRIS_DBUS_PLAYER,&player_vtable,this); + dbus_connection_register_object_path(bus->connection(),MPRIS_DBUS_TRACKLIST,&tracklist_vtable,this); + published=true; + } + } + +GMMediaPlayerService::~GMMediaPlayerService(){ + if (published) { + dbus_connection_unregister_object_path(bus->connection(),MPRIS_DBUS_ROOT); + dbus_connection_unregister_object_path(bus->connection(),MPRIS_DBUS_PLAYER); + dbus_connection_unregister_object_path(bus->connection(),MPRIS_DBUS_TRACKLIST); + published=false; + dbus_bus_release_name(bus->connection(),MPRIS_DBUS_NAME,NULL); + } + } + + +void GMMediaPlayerService::notify_track_change(const GMTrack & info) { + if (published) { + DBusMessage * msg = dbus_message_new_signal(MPRIS_DBUS_PLAYER,MPRIS_DBUS_INTERFACE,"TrackChange"); + if (msg) { + DBusMessageIter iter; + dbus_message_iter_init_append(msg,&iter); + gm_mpris_track_to_dict(&iter,info); + bus->send(msg); + } + } + } + +void GMMediaPlayerService::notify_status_change() { + DBusMessage * msg = dbus_message_new_signal(MPRIS_DBUS_PLAYER,MPRIS_DBUS_INTERFACE,"StatusChange"); + if (msg) { + DBusMessageIter iter; + dbus_message_iter_init_append(msg,&iter); + gm_mpris_get_status(&iter,GMPlayerManager::instance()); + bus->send(msg); + } + } + +void GMMediaPlayerService::notify_caps_change() { + if (published) { + DBusMessage * msg = dbus_message_new_signal(MPRIS_DBUS_PLAYER,MPRIS_DBUS_INTERFACE,"CapsChange"); + if (msg) { + FXint caps = mpris_get_caps(GMPlayerManager::instance()); + dbus_message_append_args(msg,DBUS_TYPE_INT32,&caps,DBUS_TYPE_INVALID); + bus->send(msg); + } + } + } + +DBusHandlerResult GMMediaPlayerService::root_filter(DBusConnection *connection,DBusMessage * msg,void * /*ptr*/){ + DEBUG_DBUS_MESSAGE(msg); + DBusMessage * reply=NULL; + FXuint serial; + GMPlayerManager * p = GMPlayerManager::instance(); + if (dbus_message_is_method_call(msg,"org.freedesktop.DBus.Introspectable","Introspect")){ + FXString xml(mpris_root_xml); + char ** children=NULL; + if (dbus_connection_list_registered(connection,"/",&children)) { + for (FXint i=0;children[i]!=NULL;i++) { + xml+=GMStringFormat("\t\n",children[i]); + } + dbus_free_string_array(children); + } + xml+=""; + return gm_dbus_reply_string(connection,msg,xml.text()); + } + else if (dbus_message_is_method_call(msg,MPRIS_DBUS_INTERFACE,"Identity")) { + return gm_dbus_reply_string(connection,msg,"Goggles Music Manager"); + } + else if (dbus_message_is_method_call(msg,MPRIS_DBUS_INTERFACE,"MprisVersion")) { + reply = dbus_message_new_method_return(msg); + if (reply) { + FXushort major=1,minor=0; + DBusMessageIter iter; + DBusMessageIter str; + dbus_message_iter_init_append(reply,&iter); + dbus_message_iter_open_container(&iter,DBUS_TYPE_STRUCT,NULL,&str); + dbus_message_iter_append_basic(&str,DBUS_TYPE_UINT16,&major); + dbus_message_iter_append_basic(&str,DBUS_TYPE_UINT16,&minor); + dbus_message_iter_close_container(&iter,&str); + dbus_connection_send(connection,reply,&serial); + dbus_message_unref(reply); + } + return DBUS_HANDLER_RESULT_HANDLED; + } + if (dbus_message_is_method_call(msg,MPRIS_DBUS_INTERFACE,"Quit")) { + gm_dbus_reply_if_needed(connection,msg); + if (p->getMainWindow()) p->getMainWindow()->handle(p,FXSEL(SEL_COMMAND,GMWindow::ID_QUIT),NULL); + return DBUS_HANDLER_RESULT_HANDLED; + } + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + +DBusHandlerResult GMMediaPlayerService::player_filter(DBusConnection *connection,DBusMessage * msg,void * /*ptr*/){ + DEBUG_DBUS_MESSAGE(msg); + DBusMessage * reply=NULL; + FXuint serial; + GMPlayerManager * p = GMPlayerManager::instance(); + + if (dbus_message_is_method_call(msg,"org.freedesktop.DBus.Introspectable","Introspect")){ + return gm_dbus_reply_string(connection,msg,mpris_player_xml); + } + else if (dbus_message_is_method_call(msg,MPRIS_DBUS_INTERFACE,"GetCaps")) { + FXint caps = mpris_get_caps(p); + return gm_dbus_reply_int(connection,msg,caps); + } + else if (dbus_message_is_method_call(msg,MPRIS_DBUS_INTERFACE,"PositionGet")) { + return gm_dbus_reply_int(connection,msg,p->getPlayer()->getPositionMS()); + } + else if (dbus_message_is_method_call(msg,MPRIS_DBUS_INTERFACE,"VolumeSet")) { + return gm_dbus_reply_if_needed(connection,msg); + } + else if (dbus_message_is_method_call(msg,MPRIS_DBUS_INTERFACE,"VolumeGet")) { + return gm_dbus_reply_int(connection,msg,p->volume()); + } + else if (dbus_message_is_method_call(msg,MPRIS_DBUS_INTERFACE,"GetStatus")) { + reply = dbus_message_new_method_return(msg); + if (reply) { + DBusMessageIter iter; + dbus_message_iter_init_append(reply,&iter); + gm_mpris_get_status(&iter,p); + dbus_connection_send(connection,reply,&serial); + dbus_message_unref(reply); + } + return DBUS_HANDLER_RESULT_HANDLED; + } + else if (dbus_message_is_method_call(msg,MPRIS_DBUS_INTERFACE,"GetMetadata")) { + reply = dbus_message_new_method_return(msg); + if (reply) { + DBusMessageIter iter; + GMTrack track; + p->getTrackInformation(track); + dbus_message_iter_init_append(reply,&iter); + gm_mpris_track_to_dict(&iter,track); + dbus_connection_send(connection,reply,&serial); + dbus_message_unref(reply); + } + return DBUS_HANDLER_RESULT_HANDLED; + } + else if (dbus_message_is_method_call(msg,MPRIS_DBUS_INTERFACE,"Play")){ + p->cmd_play(); + return gm_dbus_reply_if_needed(connection,msg); + } + else if (dbus_message_is_method_call(msg,MPRIS_DBUS_INTERFACE,"Stop")){ + p->cmd_stop(); + return gm_dbus_reply_if_needed(connection,msg); + } + else if (dbus_message_is_method_call(msg,MPRIS_DBUS_INTERFACE,"Pause")){ + p->cmd_pause(); + return gm_dbus_reply_if_needed(connection,msg); + } + else if (dbus_message_is_method_call(msg,MPRIS_DBUS_INTERFACE,"Next")){ + p->cmd_next(); + return gm_dbus_reply_if_needed(connection,msg); + } + else if (dbus_message_is_method_call(msg,MPRIS_DBUS_INTERFACE,"Prev")){ + p->cmd_prev(); + return gm_dbus_reply_if_needed(connection,msg); + } + else if (dbus_message_is_method_call(msg,MPRIS_DBUS_INTERFACE,"Repeat")){ + return gm_dbus_reply_if_needed(connection,msg); + } + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + + + +DBusHandlerResult GMMediaPlayerService::tracklist_filter(DBusConnection *connection,DBusMessage * msg,void * /*ptr*/){ + DEBUG_DBUS_MESSAGE(msg); + DBusMessage * reply=NULL; + FXuint serial; + GMPlayerManager * p = GMPlayerManager::instance(); + + if (dbus_message_is_method_call(msg,"org.freedesktop.DBus.Introspectable","Introspect")){ + return gm_dbus_reply_string(connection,msg,mpris_tracklist_xml); + } + else if (dbus_message_is_method_call(msg,MPRIS_DBUS_INTERFACE,"GetMetadata")) { + reply = dbus_message_new_method_return(msg); + if (reply) { + DBusMessageIter iter; + GMTrack track; + p->getTrackInformation(track); + dbus_message_iter_init_append(reply,&iter); + gm_mpris_track_to_dict(&iter,track); + dbus_connection_send(connection,reply,&serial); + dbus_message_unref(reply); + } + return DBUS_HANDLER_RESULT_HANDLED; + } + else if (dbus_message_is_method_call(msg,MPRIS_DBUS_INTERFACE,"GetLength")) { + return gm_dbus_reply_int(connection,msg,0); + } + else if (dbus_message_is_method_call(msg,MPRIS_DBUS_INTERFACE,"GetCurrentTrack")) { + return gm_dbus_reply_int(connection,msg,0); + } + else if (dbus_message_is_method_call(msg,MPRIS_DBUS_INTERFACE,"AddTrack")) { + return gm_dbus_reply_int(connection,msg,1); + } + else if (dbus_message_is_method_call(msg,MPRIS_DBUS_INTERFACE,"DelTrack")) { + return gm_dbus_reply_if_needed(connection,msg); + } + else if (dbus_message_is_method_call(msg,MPRIS_DBUS_INTERFACE,"SetLoop")) { + return gm_dbus_reply_if_needed(connection,msg); + } + else if (dbus_message_is_method_call(msg,MPRIS_DBUS_INTERFACE,"SetRandom")) { + return gm_dbus_reply_if_needed(connection,msg); + } + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } diff --git a/src/GMMediaPlayerService.h b/src/GMMediaPlayerService.h new file mode 100644 index 0000000..69b75b1 --- /dev/null +++ b/src/GMMediaPlayerService.h @@ -0,0 +1,55 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2010-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#ifndef GMMPRISSERVICE_H +#define GMMPRISSERVICE_H +/* + MPRIS v1 +*/ +class GMMediaPlayerService : public FXObject { +FXDECLARE(GMMediaPlayerService) +protected: + GMDBus * bus; + FXbool published; +protected: + DBusObjectPathVTable root_vtable; + DBusObjectPathVTable player_vtable; + DBusObjectPathVTable tracklist_vtable; +protected: + static DBusHandlerResult root_filter(DBusConnection*,DBusMessage*,void *); + static DBusHandlerResult player_filter(DBusConnection*,DBusMessage*,void *); + static DBusHandlerResult tracklist_filter(DBusConnection*,DBusMessage*,void *); +protected: + GMMediaPlayerService(){} +private: + GMMediaPlayerService(const GMMediaPlayerService&); + GMMediaPlayerService& operator=(const GMMediaPlayerService&); +public: + GMMediaPlayerService(GMDBus*); + + void notify_track_change(const GMTrack &); + + void notify_status_change(); + + void notify_caps_change(); + + virtual ~GMMediaPlayerService(); + }; + + +#endif diff --git a/src/GMMessageChannel.cpp b/src/GMMessageChannel.cpp new file mode 100644 index 0000000..f0f0429 --- /dev/null +++ b/src/GMMessageChannel.cpp @@ -0,0 +1,153 @@ +/******************************************************************************** +* * +* I n t e r - T h r e a d M e s s a g i n g C h a n n e l * +* * +********************************************************************************* +* Copyright (C) 2006-2011 by Jeroen van der Zijp. All Rights Reserved. * +********************************************************************************* +* This library is free software; you can redistribute it and/or modify * +* it under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 3 of the License, or * +* (at your option) any later version. * +* * +* 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 program. If not, see * +********************************************************************************* +* $Id: FXMessageChannel.cpp,v 1.11 2007/07/09 16:31:34 fox Exp $ * +********************************************************************************/ +#include +#include +#include "gmdefs.h" +#include "GMMessageChannel.h" + + +/* + Notes: + - Inter-thread messaging is handy to have. + - Redo this in terms of FXPipe when that becomes possible. + - Because of unbelievably retarded design of Windows, we need to + use an Event-object to actually signal the GUI thread when we've + written something to the pipe. + - Possible problem: should probably NOT reset Event unless pipe is + empty. But are we actually falling out of MsgWaitForMultipleObject if + Event is already signalled when we enter MsgWaitForMultipleObject? +*/ + + +// Maximum message size +#define MAXMESSAGE 8192 + +// Bad handle value +#ifdef WIN32 +#define BadHandle INVALID_HANDLE_VALUE +#else +#define BadHandle -1 +#endif + + +using namespace FX; + + +/*******************************************************************************/ + +namespace FX { + + +// Structure of message +struct FXMessage { + FXObject *target; // Message target + FXSelector message; // Message type,id +#if !(defined(__LP64__) || defined(_LP64) || (_MIPS_SZLONG == 64) || (__WORDSIZE == 64) || defined(_WIN64)) + FXint pad; // Padding for 32-bit +#endif + FXint size; // Message size + }; + + +// Structure of message+payload +struct FXDataMessage { + FXObject *target; // Message target + FXSelector message; // Message type,id +#if !(defined(__LP64__) || defined(_LP64) || (_MIPS_SZLONG == 64) || (__WORDSIZE == 64) || defined(_WIN64)) + FXint pad; // Padding for 32-bit +#endif + FXint size; // Message size + FXlong data[MAXMESSAGE/sizeof(FXlong)]; + }; + + +// Map +FXDEFMAP(FXMessageChannel) FXMessageChannelMap[]={ + FXMAPFUNC(SEL_IO_READ,FXMessageChannel::ID_IO_READ,FXMessageChannel::onMessage) + }; + + +// Object implementation +FXIMPLEMENT(FXMessageChannel,FXObject,FXMessageChannelMap,ARRAYNUMBER(FXMessageChannelMap)); + + +// Initialize to empty +FXMessageChannel::FXMessageChannel():app((FXApp*)-1L){ + h[0]=h[1]=h[2]=BadHandle; + } + + +// Add handler to application +FXMessageChannel::FXMessageChannel(FXApp* a):app(a){ + if(::pipe(h)!=0){ throw FXResourceException("unable to create pipe."); } + ::fcntl(h[0],F_SETFD,FD_CLOEXEC); + ::fcntl(h[1],F_SETFD,FD_CLOEXEC); +#if FOXVERSION < FXVERSION(1,7,0) + app->addInput(h[0],INPUT_READ,this,ID_IO_READ); +#else + app->addInput(this,ID_IO_READ,h[0],INPUT_READ,NULL); +#endif + } + + +// Fire signal message to target +long FXMessageChannel::onMessage(FXObject*,FXSelector,void*){ + FXDataMessage pkg; + if(::read(h[0],&pkg,sizeof(FXMessage))==sizeof(FXMessage)){ + if(0tryHandle(this,pkg.message,pkg.data); + } + return pkg.target && pkg.target->tryHandle(this,pkg.message,NULL); + } + return 0; + } + + +// Send a message to a target +FXbool FXMessageChannel::message(FXObject* tgt,FXSelector msg,const void* data,FXint size){ + FXMutexLock locker(m); + FXMessage pkg; + pkg.target=tgt; + pkg.message=msg; +#if !(defined(__LP64__) || defined(_LP64) || (_MIPS_SZLONG == 64) || (__WORDSIZE == 64) || defined(_WIN64)) + pkg.pad=0; +#endif + pkg.size=size; + if(::write(h[1],&pkg,sizeof(FXMessage))==sizeof(FXMessage)){ + if(pkg.size<=0 || (::write(h[1],data,pkg.size)==pkg.size)){ + return true; + } + } + return false; + } + + +// Remove handler from application +FXMessageChannel::~FXMessageChannel(){ + app->removeInput(h[0],INPUT_READ); + ::close(h[0]); + ::close(h[1]); + app=(FXApp*)-1L; + } + +} diff --git a/src/GMMessageChannel.h b/src/GMMessageChannel.h new file mode 100644 index 0000000..704481e --- /dev/null +++ b/src/GMMessageChannel.h @@ -0,0 +1,94 @@ +/******************************************************************************** +* * +* I n t e r - T h r e a d M e s s a g i n g C h a n n e l * +* * +********************************************************************************* +* Copyright (C) 2006-2011 by Jeroen van der Zijp. All Rights Reserved. * +********************************************************************************* +* This library is free software; you can redistribute it and/or modify * +* it under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 3 of the License, or * +* (at your option) any later version. * +* * +* 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 program. If not, see * +********************************************************************************* +* $Id: FXMessageChannel.h,v 1.6 2007/07/09 16:02:46 fox Exp $ * +********************************************************************************/ +#ifndef GMMESSAGECHANNEL_H +#define GMMESSAGECHANNEL_H + +#ifndef HAVE_FOX16 +#error Only needed for FOX 1.6 +#endif + +#ifndef FXOBJECT_H +#include "FXObject.h" +#endif + + +namespace FX { + +class FXApp; + + +/** +* FXMessageChannel manages a messaging channel between a worker thread and the main +* user-interface thread. +* When an FXMessageChannel is constructed, it automatically calls addInput() function to +* register itself as the message handler for the SEL_IO_READ message from FXApp. +* Likewise, when FXMessageChannel is destroyed, it calls removeInput() to remove itself +* as the message handler for the SEL_IO_READ message from FXApp. +* When a worker thread calls the message() API, the target and message, as well +* as optional message data, are written into the message channel. +* The main user-interface thread is awakened and subsequently dispatches to the +* onMessage handler of FXMessageChannel, which reads the target, selector, and optional +* message data from the channel and then dispatches to this target using the given +* selector. +* Thus, FXMessageChannel provides a worker thread with a way to asynchronously invoke +* any message handler in the context of the main user-interface thread. +* If the size of the optional data is zero, the message handler will be passed a +* NULL pointer. +*/ +class FXMessageChannel : public FXObject { + FXDECLARE(FXMessageChannel) +private: + FXApp *app; +private: + FXInputHandle h[3]; + FXMutex m; +protected: + FXMessageChannel(); +private: + FXMessageChannel(const FXMessageChannel&); + FXMessageChannel& operator=(const FXMessageChannel&); +public: + enum{ + ID_IO_READ=1, + ID_LAST + }; +public: + long onMessage(FXObject*,FXSelector,void*); +public: + + /// Initialize message channel + FXMessageChannel(FXApp* a); + + /// Get application pointer + FXApp* getApp() const { return app; } + + /// Send a message msg comprising of FXSEL(type,id) to a target tgt, and pass optional data of size bytes + FXbool message(FXObject* tgt,FXSelector msg,const void* data=NULL,FXint size=0); + + /// Clean up message channel + virtual ~FXMessageChannel(); + }; + +} + +#endif diff --git a/src/GMNotifyDaemon.cpp b/src/GMNotifyDaemon.cpp new file mode 100644 index 0000000..165e1d6 --- /dev/null +++ b/src/GMNotifyDaemon.cpp @@ -0,0 +1,392 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2007-2010 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#include "gmdefs.h" +#include "gmutils.h" +#include "GMDBus.h" +#include "GMPlayerManager.h" +#include "GMNotifyDaemon.h" + +#define GALAGO_NOTIFY_NAME "org.freedesktop.Notifications" +#define GALAGO_NOTIFY_PATH "/org/freedesktop/Notifications" +#define GALAGO_NOTIFY_INTERFACE "org.freedesktop.Notifications" + +/* + +/// gnome 3.2.0 +name: gnome-shell +vendor: GNOME +version: 3.2.0 +spec: 1.2 +quirks: + +/// gnome3 +name: gnome-shell +vendor: GNOME +version: 3.0.2 +spec: 1.2 +quirks: IMAGE_WITHOUT_APPICON + + +/// gnome-fallback - notification-daemon +name: Notification Daemon +vendor: GNOME +version: 0.7.2 +spec: 1.2 +quirks: + + +/// XFCE xfce4-notifyd +name: Xfce Notify Daemon +vendor: Xfce +version: 0.2.2 +spec: 0.9 +quirks: IMAGE_WITHOUT_APPICON + + +/// KDE +name: Plasma +vendor: KDE +version: 1.0 +spec: 1.1 +quirks: 0 + + +/// Notify-OSD +name: notify-osd +vendor: Canonical Ltd +version: 1.0 +spec: 1.1 +icondata: image_data +flags: 0 + + +*/ + + +FXDEFMAP(GMNotifyDaemon) GMNotifyDaemonMap[]={ + FXMAPFUNC(SEL_SIGNAL,0,GMNotifyDaemon::onSignal), + FXMAPFUNC(SEL_COMMAND,0,GMNotifyDaemon::onMethod), + FXMAPFUNC(SEL_COMMAND,GMNotifyDaemon::ID_NOTIFY_REPLY,GMNotifyDaemon::onNotifyReply), + FXMAPFUNC(SEL_COMMAND,GMNotifyDaemon::ID_NOTIFY_CAPABILITIES,GMNotifyDaemon::onNotifyCapabilities), + FXMAPFUNC(SEL_COMMAND,GMNotifyDaemon::ID_NOTIFY_SERVER,GMNotifyDaemon::onNotifyServer) + + }; + +FXIMPLEMENT(GMNotifyDaemon,GMDBusProxy,GMNotifyDaemonMap,ARRAYNUMBER(GMNotifyDaemonMap)); + +GMNotifyDaemon::GMNotifyDaemon(){ + } + +GMNotifyDaemon::GMNotifyDaemon(GMDBus * b) : GMDBusProxy(b,GALAGO_NOTIFY_NAME,GALAGO_NOTIFY_PATH,GALAGO_NOTIFY_INTERFACE),flags(0),msgid(0),persistent(false){ + appicon="gogglesmm"; + appname="gogglesmm"; + icondata="icon_data"; + } + +long GMNotifyDaemon::onSignal(FXObject*,FXSelector,void*ptr){ + DBusMessage * msg = reinterpret_cast(ptr); + FXuint id,reason; + FXchar * action; + if (dbus_message_is_signal(msg,GALAGO_NOTIFY_INTERFACE,"NotificationClosed")){ + if ((dbus_message_has_signature(msg,"u") && dbus_message_get_args(msg,NULL,DBUS_TYPE_UINT32,&id,DBUS_TYPE_INVALID)) || + (dbus_message_has_signature(msg,"uu") && dbus_message_get_args(msg,NULL,DBUS_TYPE_UINT32,&id,DBUS_TYPE_UINT32,&reason,DBUS_TYPE_INVALID))) { + if (id==msgid) { + msgid=0; + } + } + return 1; + } + else if (dbus_message_is_signal(msg,GALAGO_NOTIFY_INTERFACE,"ActionInvoked")){ + if (dbus_message_has_signature(msg,"us") && dbus_message_get_args(msg,NULL,DBUS_TYPE_UINT32,&id,DBUS_TYPE_STRING,&action,DBUS_TYPE_INVALID)) { + if (compare(action,"media-skip-backward")==0) { + GMPlayerManager::instance()->cmd_prev(); + } + else if (compare(action,"media-skip-forward")==0) { + GMPlayerManager::instance()->cmd_next(); + } + else if (compare(action,"media-playback-pause")==0) { + GMPlayerManager::instance()->cmd_pause(); + } + else if (compare(action,"media-playback-start")==0) { + GMPlayerManager::instance()->cmd_play(); + } + else if (compare(action,"media-playback-stop")==0) { + GMPlayerManager::instance()->cmd_stop(); + } + else { + fxmessage("unhandled action: %s\n",action); + } + return 1; + } + } + return 0; + } + + +long GMNotifyDaemon::onMethod(FXObject*,FXSelector,void*){ + return 1; + } + +long GMNotifyDaemon::onNotifyServer(FXObject*,FXSelector,void*ptr){ + DBusMessage * msg = reinterpret_cast(ptr); + const FXchar * name=NULL; + const FXchar * vendor=NULL; + const FXchar * version=NULL; + const FXchar * spec=NULL; + + if ((dbus_message_get_type(msg)==DBUS_MESSAGE_TYPE_METHOD_RETURN) && dbus_message_get_args(msg,NULL,DBUS_TYPE_STRING,&name,DBUS_TYPE_STRING,&vendor,DBUS_TYPE_STRING,&version,DBUS_TYPE_STRING,&spec,DBUS_TYPE_INVALID)) { + + if (compareversion(spec,"1.1")==0) { + icondata="image_data"; + } + else if (compareversion(spec,"1.2")>=0) { + icondata="image-data"; + } + else { + icondata="icon_data"; + } + + if (comparecase(vendor,"xfce")==0 && comparecase(name,"xfce notify daemon")==0) { + flags|=IMAGE_WITHOUT_APPICON; + } + + if (comparecase(name,"gnome-shell")==0 && comparecase(vendor,"gnome")==0) { + GMPlayerManager::instance()->getPreferences().gui_tray_icon_disabled=true; + flags|=ACTION_ITEMS; + if (compareversion(version,"3.2.0")<0){ + flags|=IMAGE_WITHOUT_APPICON; + } + } +#ifdef DEBUG + fxmessage("name: %s\n",name); + fxmessage("vendor: %s\n",vendor); + fxmessage("version: %s\n",version); + fxmessage("spec: %s\n",spec); + fxmessage("icondata: %s\n",icondata.text()); + fxmessage("flags: %x\n",flags); +#endif + } + + GMPlayerManager::instance()->update_tray_icon(); + return 1; + } + +long GMNotifyDaemon::onNotifyCapabilities(FXObject*,FXSelector,void*ptr){ + DBusMessage * msg = reinterpret_cast(ptr); + FXchar ** caps; + int ncaps; + if ((dbus_message_get_type(msg)==DBUS_MESSAGE_TYPE_METHOD_RETURN) && dbus_message_get_args(msg,NULL,DBUS_TYPE_ARRAY,DBUS_TYPE_STRING,&caps,&ncaps,DBUS_TYPE_INVALID)) { + FXbool has_action_icons=false; + FXbool has_actions=false; + FXbool has_persistence=false; + + for (FXint i=0;i(ptr); + FXASSERT(msg); + if (dbus_message_get_type(msg)==DBUS_MESSAGE_TYPE_METHOD_RETURN) { + dbus_message_get_args(msg,NULL,DBUS_TYPE_UINT32,&msgid,DBUS_TYPE_INVALID); + } + return 1; + } + + +void GMNotifyDaemon::reset() { + if (persistent) { + notify("Goggles Music Manager","",-1,NULL); + } + else { + close(); + } + } + +void GMNotifyDaemon::close() { +#ifdef DEBUG + fxmessage("GMNotifyDaemon::close()\n"); +#endif + if (msgid>0) { + DBusMessage * msg = method("CloseNotification"); + if (msg) { + dbus_message_append_args(msg,DBUS_TYPE_UINT32,&msgid,DBUS_TYPE_INVALID); + dbus_message_set_no_reply(msg,true); + bus->send(msg); + } + } + } + +void GMNotifyDaemon::init() { + { + DBusMessage * msg = method("GetServerInformation"); + send(msg,this,ID_NOTIFY_SERVER); + } + + + { + DBusMessage * msg = method("GetCapabilities"); + send(msg,this,ID_NOTIFY_CAPABILITIES); + } + + } + +void GMNotifyDaemon::notify_track_change(const GMTrack & track,FXImage * cover){ + FXString body = GMStringFormat(fxtrformat("%s\n%s (%d)"),track.artist.text(),track.album.text(),track.year); + /// Dirty Hack. According to the spec, we shouldn't have to do this, + /// but try finding a notification notifydaemon that actually implements it... + /// http://www.galago-project.org/specs/notification/0.9/index.html + body.substitute("&","&"); + notify(track.title.text(),body.text(),-1,cover); + } + + +void GMNotifyDaemon::notify(const FXchar * summary,const FXchar * body,FXint timeout,FXImage* image){ + FXint iw,ih,is,ibps,ichannels,isize; + dbus_bool_t ialpha; + + const FXchar * idata=NULL; + + DBusMessage * msg = method("Notify"); + if (msg){ + DBusMessageIter iter; + DBusMessageIter array; + DBusMessageIter dict; + DBusMessageIter value; + DBusMessageIter variant; + DBusMessageIter data; + + dbus_message_iter_init_append(msg,&iter); + + gm_dbus_append_string(&iter,appname); + dbus_message_iter_append_basic(&iter,DBUS_TYPE_UINT32,&msgid); + + if (image && (flags&IMAGE_WITHOUT_APPICON)) { + FXString empty; + gm_dbus_append_string(&iter,empty); + } + else { + gm_dbus_append_string(&iter,appicon); + } + + dbus_message_iter_append_basic(&iter,DBUS_TYPE_STRING,&summary); + dbus_message_iter_append_basic(&iter,DBUS_TYPE_STRING,&body); + + dbus_message_iter_open_container(&iter,DBUS_TYPE_ARRAY,DBUS_TYPE_STRING_AS_STRING,&array); + if (persistent) { + if (GMPlayerManager::instance()->can_prev()) + gm_dbus_append_string_pair(&array,"media-skip-backward","Previous"); + if (GMPlayerManager::instance()->can_pause()) + gm_dbus_append_string_pair(&array,"media-playback-pause","Pause"); + else if (GMPlayerManager::instance()->can_play()) + gm_dbus_append_string_pair(&array,"media-playback-start","Play"); + if (GMPlayerManager::instance()->can_stop()) + gm_dbus_append_string_pair(&array,"media-playback-stop","Stop"); + if (GMPlayerManager::instance()->can_next()) + gm_dbus_append_string_pair(&array,"media-skip-forward","Next"); + } + dbus_message_iter_close_container(&iter,&array); + + + dbus_message_iter_open_container(&iter,DBUS_TYPE_ARRAY,"{sv}",&array); + if (image && image->getData()) { +// const FXchar * icon_data="icon_data"; /// spec 0.9 says "image_data". some use "icon_data" though.. +// const FXchar * icon_data="image-data"; /// spec 0.9 says "image_data". some use "icon_data" though.. + + ialpha = true; + iw = image->getWidth(); + ih = image->getHeight(); + is = iw*4; + ibps = 8; + ichannels = 4; + isize = iw*ih*4; + + +#if FOXVERSION >= FXVERSION(1,7,26) + FXColor * bgra = NULL; + allocElms(bgra,(iw*ih)); + gm_bgra_to_rgba(image->getData(),bgra,(iw*ih)); + + idata = (const FXchar*)bgra; +#else + idata = (const FXchar*)image->getData(); +#endif + + dbus_message_iter_open_container(&array,DBUS_TYPE_DICT_ENTRY,0,&dict); + gm_dbus_append_string(&dict,icondata); + dbus_message_iter_open_container(&dict,DBUS_TYPE_VARIANT,"(iiibiiay)",&variant); + dbus_message_iter_open_container(&variant,DBUS_TYPE_STRUCT,NULL,&value); + dbus_message_iter_append_basic(&value,DBUS_TYPE_INT32,&iw); + dbus_message_iter_append_basic(&value,DBUS_TYPE_INT32,&ih); + dbus_message_iter_append_basic(&value,DBUS_TYPE_INT32,&is); + dbus_message_iter_append_basic(&value,DBUS_TYPE_BOOLEAN,&ialpha); + dbus_message_iter_append_basic(&value,DBUS_TYPE_INT32,&ibps); + dbus_message_iter_append_basic(&value,DBUS_TYPE_INT32,&ichannels); + dbus_message_iter_open_container(&value,DBUS_TYPE_ARRAY,DBUS_TYPE_BYTE_AS_STRING,&data); + dbus_message_iter_append_fixed_array(&data,DBUS_TYPE_BYTE,&idata,isize); + dbus_message_iter_close_container(&value,&data); + dbus_message_iter_close_container(&variant,&value); + dbus_message_iter_close_container(&dict,&variant); + dbus_message_iter_close_container(&array,&dict); + +#if FOXVERSION >= FXVERSION(1,7,29) + freeElms(bgra); +#endif + } + + //gm_dbus_dict_append_bool(&array,"transient",true); + + if (persistent) { +// if (GMPlayerManager::instance()->playing()) + gm_dbus_dict_append_bool(&array,"resident",true); +// else +// gm_dbus_dict_append_bool(&array,"transient",true); + gm_dbus_dict_append_bool(&array,"action-icons",true); + } + dbus_message_iter_close_container(&iter,&array); + dbus_message_iter_append_basic(&iter,DBUS_TYPE_INT32,&timeout); + + send(msg,this,ID_NOTIFY_REPLY); + } + } + + diff --git a/src/GMNotifyDaemon.h b/src/GMNotifyDaemon.h new file mode 100644 index 0000000..1a0b896 --- /dev/null +++ b/src/GMNotifyDaemon.h @@ -0,0 +1,66 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2010 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#ifndef GMNOTIFY_H +#define GMNOTIFY_H + +enum { + IMAGE_WITHOUT_APPICON = 0x1, // Set if image and appicon may not be set at the same time + ACTION_ITEMS = 0x2 + }; + + +class GMNotifyDaemon : public GMDBusProxy { +FXDECLARE(GMNotifyDaemon) +protected: + FXuint flags; + FXString appname; + FXString appicon; + FXString icondata; + FXuint msgid; + FXbool persistent; +protected: + GMNotifyDaemon(); +private: + GMNotifyDaemon(const GMNotifyDaemon&); + GMNotifyDaemon& operator=(const GMNotifyDaemon&); +public: + enum { + ID_NOTIFY_REPLY=1, + ID_NOTIFY_CAPABILITIES, + ID_NOTIFY_SERVER + }; +public: + long onSignal(FXObject*,FXSelector,void*); + long onMethod(FXObject*,FXSelector,void*); + long onNotifyReply(FXObject*,FXSelector,void*); + long onNotifyServer(FXObject*,FXSelector,void*); + long onNotifyCapabilities(FXObject*,FXSelector,void*); +public: + GMNotifyDaemon(GMDBus*); + void init(); + void close(); + + void reset(); + void notify(const FXchar * summary,const FXchar * body,FXint timeout,FXImage* img); + +public: + void notify_track_change(const GMTrack & track,FXImage * cover); + }; + +#endif diff --git a/src/GMPlayListSource.cpp b/src/GMPlayListSource.cpp new file mode 100644 index 0000000..ac7c976 --- /dev/null +++ b/src/GMPlayListSource.cpp @@ -0,0 +1,472 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#include "gmdefs.h" +#include "GMList.h" +#include "GMDatabase.h" +#include "GMTrackDatabase.h" +#include "GMTrackList.h" +#include "GMTrackItem.h" +#include "GMTrackView.h" +#include "GMSourceView.h" +#include "GMWindow.h" +#include "GMSource.h" +#include "GMDatabaseSource.h" +#include "GMPlayListSource.h" +#include "GMPlayerManager.h" +#include "GMThread.h" +#include "GMSearch.h" +#include "GMImportDialog.h" + +#include "GMTag.h" +#include "GMIconTheme.h" +#include "GMClipboard.h" + +FXDEFMAP(GMPlayListSource) GMPlayListSourceMap[]={ + FXMAPFUNC(SEL_COMMAND,GMPlayListSource::ID_EDIT_NAME,GMPlayListSource::onCmdEditName), + FXMAPFUNC(SEL_COMMAND,GMPlayListSource::ID_REMOVE,GMPlayListSource::onCmdRemove), + FXMAPFUNC(SEL_COMMAND,GMPlayListSource::ID_DELETE_GENRE,GMPlayListSource::onCmdRemoveInPlaylist), + FXMAPFUNC(SEL_COMMAND,GMPlayListSource::ID_DELETE_ARTIST,GMPlayListSource::onCmdRemoveInPlaylist), + FXMAPFUNC(SEL_COMMAND,GMPlayListSource::ID_DELETE_ALBUM,GMPlayListSource::onCmdRemoveInPlaylist), + FXMAPFUNC(SEL_COMMAND,GMPlayListSource::ID_DELETE_TRACK,GMPlayListSource::onCmdRemoveInPlaylist), + FXMAPFUNC(SEL_COMMAND,GMPlayListSource::ID_PASTE,GMPlayListSource::onCmdPaste), + FXMAPFUNC(SEL_COMMAND,GMPlayListSource::ID_IMPORT,GMPlayListSource::onCmdImport), + FXMAPFUNC(SEL_DND_DROP,GMPlayListSource::ID_DROP,GMPlayListSource::onCmdDrop), + }; + +FXIMPLEMENT(GMPlayListSource,GMDatabaseSource,GMPlayListSourceMap,ARRAYNUMBER(GMPlayListSourceMap)); + +GMPlayListSource::GMPlayListSource() : playlist(-1) { + dbowned=false; + } + +GMPlayListSource::GMPlayListSource(GMTrackDatabase * database,FXint pl) : GMDatabaseSource(database), playlist(pl) { + dbowned=false; + } + +GMPlayListSource::~GMPlayListSource() { + } + +void GMPlayListSource::orderChanged(GMTrackList*tracklist) const{ + FXint nitems = tracklist->getNumItems(); + if (tracklist->getSortMethod()==HEADER_QUEUE){ + if (tracklist->getSortFunc()==GMDBTrackItem::descendingQueue) { + for (FXint i=0;igetItem(i))->setTrackQueue(nitems-i); + } + } + else { + for (FXint i=0;igetItem(i))->setTrackQueue(i+1); + } + } + } + } + +void GMPlayListSource::getSelectedTrackQueues(FXIntList & list) { + FXint nitems = GMPlayerManager::instance()->getTrackView()->getNumTracks(); + for (FXint i=0;igetTrackView()->isTrackItemSelected(i)) + list.append(((GMDBTrackItem*)GMPlayerManager::instance()->getTrackView()->getTrackItem(i))->getTrackQueue()); + } + } + +void GMPlayListSource::getTrackQueues(FXIntList & list) { + FXint nitems = GMPlayerManager::instance()->getTrackView()->getNumTracks(); + list.no(nitems); + for (FXint i=0;igetTrackView()->getTrackItem(i))->getTrackQueue(); + } + } + + +FXbool GMPlayListSource::genre_context_menu(FXMenuPane * pane) { + new GMMenuCommand(pane,fxtr("Remove…\tDel\tRemove track(s) from play list."),GMIconTheme::instance()->icon_delete,this,ID_DELETE_GENRE); + return true; + } + +FXbool GMPlayListSource::artist_context_menu(FXMenuPane * pane){ + new GMMenuCommand(pane,fxtr("Copy\tCtrl-C\tCopy associated tracks to the clipboard."),GMIconTheme::instance()->icon_copy,this,ID_COPY_ARTIST); + new GMMenuCommand(pane,fxtr("Remove…\tDel\tRemove track(s) from play list."),GMIconTheme::instance()->icon_delete,this,ID_DELETE_ARTIST); + return true; + } + +FXbool GMPlayListSource::album_context_menu(FXMenuPane * pane){ + new GMMenuCommand(pane,fxtr("Copy\tCtrl-C\tCopy associated tracks to the clipboard."),GMIconTheme::instance()->icon_copy,this,ID_COPY_ALBUM); + new GMMenuCommand(pane,fxtr("Remove…\tDel\tRemove track(s) from play list."),GMIconTheme::instance()->icon_delete,this,ID_DELETE_ALBUM); + return true; + } + +FXbool GMPlayListSource::track_context_menu(FXMenuPane * pane){ + new GMMenuCommand(pane,fxtr("Edit…\tF2\tEdit Track Information."),GMIconTheme::instance()->icon_edit,this,GMDatabaseSource::ID_EDIT_TRACK); + new GMMenuCommand(pane,fxtr("Copy\tCtrl-C\tCopy track(s) to clipboard."),GMIconTheme::instance()->icon_copy,this,ID_COPY_TRACK); + new FXMenuSeparator(pane); + if (GMPlayerManager::instance()->getTrackView()->numTrackSelected()==1) + new GMMenuCommand(pane,fxtr("Open Folder Location\t\tOpen Folder Location."),NULL,this,ID_OPEN_FOLDER); + new GMMenuCommand(pane,fxtr("Remove…\tDel\tRemove track(s) from play list."),GMIconTheme::instance()->icon_delete,this,ID_DELETE_TRACK); + return true; + } + +FXbool GMPlayListSource::findCurrent(GMTrackList * list,GMSource * src) { + if (src->getCurrentTrack()==-1) return false; + if (src==this) { + for (FXint i=0;igetNumItems();i++){ + if (list->getItemId(i)==current_track && ((GMDBTrackItem*)list->getItem(i))->getTrackQueue()==current_queue) { + list->setActiveItem(i); + list->setCurrentItem(i); + return true; + } + } + } + else { + GMDatabaseSource * db = dynamic_cast(src); + if (db && db->getCurrentTrack()!=-1 ) + return GMSource::findCurrent(list,db); + } + return false; + } + +FXbool GMPlayListSource::hasCurrentTrack(GMSource * src) const { + if (src==this) return true; + else if (db->trackInPlaylist(src->getCurrentTrack(),playlist)) return true; + return false; + } + +void GMPlayListSource::markCurrent(GMTrackList * list,FXint item) { + current_track=-1; + current_queue=-1; + if (list->getNumItems()) { + current_track = list->getItemId(item); + current_queue = ((GMDBTrackItem*)list->getItem(item))->getTrackQueue(); + } + } + + +FXbool GMPlayListSource::source_context_menu(FXMenuPane * pane){ + new GMMenuCommand(pane,fxtr("Edit…"),GMIconTheme::instance()->icon_edit,this,GMPlayListSource::ID_EDIT_NAME); + new GMMenuCommand(pane,fxtr("Import…"),GMIconTheme::instance()->icon_import,this,GMPlayListSource::ID_IMPORT); + new GMMenuCommand(pane,fxtr("Export…"),GMIconTheme::instance()->icon_export,this,GMPlayListSource::ID_EXPORT); + new GMMenuCommand(pane,fxtr("Remove Playlist"),GMIconTheme::instance()->icon_delete,this,GMPlayListSource::ID_REMOVE); + return true; + } + + +FXString GMPlayListSource::getName() const{ + return db->getPlaylistName(playlist); + } + + +FXbool GMPlayListSource::dnd_source_accepts(FXDragType*types,FXuint ntypes){ + FXWindow * src = FXApp::instance()->getDragWindow(); + for (FXuint i=0;igetSortMethod()!=HEADER_QUEUE) return false; + FXint fromq = ((GMDBTrackItem*)tracklist->getItem(from))->getTrackQueue(); + FXint toq = ((GMDBTrackItem*)tracklist->getItem(to))->getTrackQueue(); + db->moveTrack(playlist,fromq,toq); + if (current_queue==fromq) { + current_queue=toq; + } + else if (fromq<=current_queue && current_queue<=toq) { + current_queue-=1; + } + else if (fromq>=current_queue && current_queue>=toq) { + current_queue+=1; + } + return true; + } + +class FromDiskTarget : public FXObject { +FXDECLARE(FromDiskTarget) +protected: + FXCheckButton * from_library; +protected: + FromDiskTarget(){} +private: + FromDiskTarget(const FromDiskTarget&); + FromDiskTarget& operator=(const FromDiskTarget&); +public: + enum { + ID_FROM_DISK = 1 + }; +public: + + FromDiskTarget(FXCheckButton*disk,FXCheckButton*library) : from_library(library) { + disk->setTarget(this); + disk->setSelector(FromDiskTarget::ID_FROM_DISK); + } + + long onUpdFromDisk(FXObject*sender,FXSelector,void*) { + if (from_library->getCheck()) + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_ENABLE),NULL); + else + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_DISABLE),NULL); + return 1; + } + }; + +FXDEFMAP(FromDiskTarget) FromDiskTargetMap[]={ + FXMAPFUNC(SEL_UPDATE,FromDiskTarget::ID_FROM_DISK,FromDiskTarget::onUpdFromDisk) + }; + +FXIMPLEMENT(FromDiskTarget,FXObject,FromDiskTargetMap,ARRAYNUMBER(FromDiskTargetMap)) + + + + + +long GMPlayListSource::onCmdRemoveInPlaylist(FXObject*,FXSelector sel,void*){ + FXIntList queue; + FXIntList tracks; + FXStringList files; + if (FXSELID(sel)==ID_DELETE_TRACK) { + getSelectedTrackQueues(queue); + GMPlayerManager::instance()->getTrackView()->getSelectedTracks(tracks); + } + else { + getTrackQueues(queue); + GMPlayerManager::instance()->getTrackView()->getTracks(tracks); + } + if (tracks.no()==0) return 1; + + FXString title; + FXString subtitle; + + switch(FXSELID(sel)){ + case ID_DELETE_GENRE: title=fxtr("Remove Genre?"); + subtitle=fxtr("Remove tracks with genre from play list?"); + break; + case ID_DELETE_ARTIST:title=fxtr("Remove Artist?"); + subtitle=fxtr("Remove tracks from artist from play list?"); + break; + case ID_DELETE_ALBUM: title=fxtr("Remove Album?"); + subtitle=fxtr("Remove tracks from album from play list?"); + break; + case ID_DELETE_TRACK: title=fxtr("Remove Track(s)?"); + subtitle=fxtr("Remove track(s) from play list?"); + break; + default: FXASSERT(0); break; + } + + + FXDialogBox dialog(GMPlayerManager::instance()->getMainWindow(),title,DECOR_TITLE|DECOR_BORDER|DECOR_RESIZE|DECOR_CLOSE,0,0,0,0,0,0,0,0,0,0); + GMPlayerManager::instance()->getMainWindow()->create_dialog_header(&dialog,title,subtitle,NULL); + FXHorizontalFrame *closebox=new FXHorizontalFrame(&dialog,LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X|PACK_UNIFORM_WIDTH,0,0,0,0); + new GMButton(closebox,fxtr("&Remove"),NULL,&dialog,FXDialogBox::ID_ACCEPT,BUTTON_INITIAL|BUTTON_DEFAULT|LAYOUT_RIGHT|FRAME_RAISED|FRAME_THICK,0,0,0,0, 15,15); + new GMButton(closebox,fxtr("&Cancel"),NULL,&dialog,FXDialogBox::ID_CANCEL,BUTTON_DEFAULT|LAYOUT_RIGHT|FRAME_RAISED|FRAME_THICK,0,0,0,0, 15,15); + new FXSeparator(&dialog,SEPARATOR_GROOVE|LAYOUT_FILL_X|LAYOUT_SIDE_BOTTOM); + FXVerticalFrame * main = new FXVerticalFrame(&dialog,LAYOUT_FILL_X|LAYOUT_FILL_Y,0,0,0,0,30,20,10,10); + FXCheckButton * library_check = new GMCheckButton(main,fxtr("Remove tracks from music library")); + FXCheckButton * from_disk = new GMCheckButton(main,fxtr("Remove tracks from disk")); + + FromDiskTarget disktgt(from_disk,library_check); + + if (dialog.execute()){ + + // Check current queue... + if (current_queue >= 0) { + for (FXint i=0;igetCheck()) { + db->beginDelete(); + + if (from_disk->getCheck()) + getTrackFilenames(tracks,files); + + if (!db->removeTracks(tracks)) { + FXMessageBox::error(GMPlayerManager::instance()->getMainWindow(),MBOX_OK,fxtr("Library Error"),fxtr("Unable to remove track from the library.")); + } + + if (from_disk->getCheck()) + removeFiles(files); + + db->endDelete(); + } + else { + db->removeTracksFromPlaylist(queue,playlist); + } + GMPlayerManager::instance()->getTrackView()->refresh(); + } + return 1; + } + + + + + + +long GMPlayListSource::onCmdPaste(FXObject*sender,FXSelector sel,void*ptr){ + GMClipboard * clipboard = GMClipboard::instance(); + if (clipboard->offeredDNDType(FROM_CLIPBOARD,GMClipboard::trackdatabase)){ + GMDatabaseClipboardData * clipdata = dynamic_cast(clipboard->getClipData()); + if (clipdata && clipdata->tracks.no()) { + db->insertTrackInPlaylist(playlist,clipdata->tracks); + GMPlayerManager::instance()->getTrackView()->refresh(); + } + else { + FXApp::instance()->beep(); + } + } + else { + return GMDatabaseSource::onCmdPaste(sender,sel,ptr); + } + return 1; + } + +long GMPlayListSource::onCmdDrop(FXObject*sender,FXSelector,void*){ + FXWindow * window=(FXWindow*)sender; + FXString files; + FXStringList filelist; + FXIntList tracks; +// FXbool from_kde=false; + FXbool from_uri=false; + FXbool from_db_all=false; + FXbool from_db=false; + FXDragType * types; + FXuint ntypes; + + if (window->inquireDNDTypes(FROM_DRAGNDROP,types,ntypes)){ + for (FXuint i=0;idropFinished(DRAG_ACCEPT); + FXApp::instance()->beginWaitCursor(); + if (from_db) + GMPlayerManager::instance()->getTrackView()->getSelectedTracks(tracks); + else + GMPlayerManager::instance()->getTrackView()->getTracks(tracks); + + if (tracks.no()) db->insertTrackInPlaylist(playlist,tracks); + FXApp::instance()->endWaitCursor(); + return 1; + } + else if (from_uri) { + if (window->getDNDData(FROM_DRAGNDROP,FXWindow::urilistType,files)){ + gm_convert_uri_to_filenames(files,filelist); + } + window->dropFinished(DRAG_ACCEPT); + } + + if (filelist.no()) { + GMImportDialog dialog(GMPlayerManager::instance()->getMainWindow(),IMPORT_FROMPASTE); + if (dialog.execute()) { + GMPlayerManager::instance()->stop(); + GMImportDatabase searchdialog(GMPlayerManager::instance()->getMainWindow(),filelist,GMPlayerManager::instance()->getPreferences().import,getPlayList(),GMPlayerManager::instance()->getMainWindow()->getThickFont()); + searchdialog.execute(); + GMPlayerManager::instance()->getTrackView()->refresh(); + } + } + else { + FXApp::instance()->beep(); + } + } + return 1; + } + + +long GMPlayListSource::onCmdEditName(FXObject*,FXSelector,void *){ + FXDialogBox dialog(GMPlayerManager::instance()->getMainWindow(),fxtr("Edit Playlist"),DECOR_TITLE|DECOR_BORDER|DECOR_RESIZE|DECOR_CLOSE,0,0,0,0,0,0,0,0,0,0); + GMPlayerManager::instance()->getMainWindow()->create_dialog_header(&dialog,fxtr("Edit Playlist"),fxtr("Change playlist name")); + FXHorizontalFrame *closebox=new FXHorizontalFrame(&dialog,LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X|PACK_UNIFORM_WIDTH,0,0,0,0); + new GMButton(closebox,fxtr("&Save"),NULL,&dialog,FXDialogBox::ID_ACCEPT,BUTTON_INITIAL|BUTTON_DEFAULT|LAYOUT_RIGHT|FRAME_RAISED|FRAME_THICK,0,0,0,0, 15,15); + new GMButton(closebox,fxtr("&Cancel"),NULL,&dialog,FXDialogBox::ID_CANCEL,BUTTON_DEFAULT|LAYOUT_RIGHT|FRAME_RAISED|FRAME_THICK,0,0,0,0, 15,15); + new FXSeparator(&dialog,SEPARATOR_GROOVE|LAYOUT_FILL_X|LAYOUT_SIDE_BOTTOM); + + FXVerticalFrame * main = new FXVerticalFrame(&dialog,LAYOUT_FILL_X|LAYOUT_FILL_Y,0,0,0,0,30,20,10,10); + FXMatrix * matrix = new FXMatrix(main,2,LAYOUT_FILL_X|MATRIX_BY_COLUMNS); + new FXLabel(matrix,fxtr("Name"),NULL,LABEL_NORMAL|LAYOUT_RIGHT|LAYOUT_CENTER_Y); + FXTextField * name_field = new GMTextField(matrix,20,&dialog,FXDialogBox::ID_ACCEPT,LAYOUT_FILL_X|LAYOUT_FILL_COLUMN|FRAME_SUNKEN|FRAME_THICK|TEXTFIELD_ENTER_ONLY); + name_field->setText(getName()); + dialog.create(); + gm_focus_and_select(name_field); + if (dialog.execute()) { + FXString label= name_field->getText().trim(); + if (!label.empty()) { + db->setPlaylistName(playlist,label); + GMPlayerManager::instance()->getSourceView()->updateSource(this); + } + } + return 1; + } + + +long GMPlayListSource::onCmdRemove(FXObject*,FXSelector,void *){ + if (GMPlayerManager::instance()->getMainWindow()->question(fxtr("Delete Play List?"),fxtr("Are you sure you want to delete the playlist?"),fxtr("&Yes"),fxtr("&No"))){ + db->beginDelete(); + db->removePlaylist(playlist); + db->endDelete(); + GMPlayerManager::instance()->stop(); // for now we call stop... + GMPlayerManager::instance()->removeSource(this); + GMPlayerManager::instance()->getSourceView()->refresh(); + FXApp::instance()->reg().deleteSection(settingKey().text()); + delete this; + } + return 1; + } + + +long GMPlayListSource::onCmdImport(FXObject*,FXSelector,void*){ + GMImportDialog dialog(GMPlayerManager::instance()->getMainWindow(),IMPORT_FROMFILE|IMPORT_PLAYLIST); + if (dialog.execute()){ + FXString buffer; + FXStringList urls; + FXString title; + + if (gm_buffer_file(dialog.getFilename(),buffer)) { + FXString extension = FXPath::extension(dialog.getFilename()); + if (comparecase(extension,"m3u")==0) + gm_parse_m3u(buffer,urls); + else if (comparecase(extension,"pls")==0) + gm_parse_pls(buffer,urls); + else + gm_parse_xspf(buffer,urls,title); + + if (urls.no()) { + gm_make_absolute_path(FXPath::directory(dialog.getFilename()),urls); + GMImportDatabase searchdialog(GMPlayerManager::instance()->getMainWindow(),urls,GMPlayerManager::instance()->getPreferences().import,getPlayList(),GMPlayerManager::instance()->getMainWindow()->getThickFont()); + searchdialog.execute(); + GMPlayerManager::instance()->getTrackView()->refresh(); + } + } + } + return 1; + } + diff --git a/src/GMPlayListSource.h b/src/GMPlayListSource.h new file mode 100644 index 0000000..d122eb1 --- /dev/null +++ b/src/GMPlayListSource.h @@ -0,0 +1,97 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2007-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#ifndef GMPLAYLISTSOURCE_H +#define GMPLAYLISTSOURCE_H + +class GMPlayListSource : public GMDatabaseSource { +FXDECLARE(GMPlayListSource) +protected: + FXint playlist; + FXint current_queue; +protected: + GMPlayListSource(); +private: + GMPlayListSource(const GMPlayListSource&); + GMPlayListSource& operator=(const GMPlayListSource&); +protected: + virtual FXint getPlayList() const { return playlist; } + void getSelectedTrackQueues(FXIntList & q); + void getTrackQueues(FXIntList & q); +public: + enum { + ID_EDIT_NAME = GMDatabaseSource::ID_LAST, + ID_REMOVE, + ID_IMPORT, + ID_LAST + }; +public: + long onCmdEditName(FXObject*,FXSelector,void*); + long onCmdRemove(FXObject*,FXSelector,void*); + long onCmdRemoveInPlaylist(FXObject*,FXSelector,void*); + long onCmdPaste(FXObject*,FXSelector,void*); + long onCmdDrop(FXObject*,FXSelector,void*); + long onCmdImport(FXObject*,FXSelector,void*); +public: + GMPlayListSource(GMTrackDatabase * db,FXint playlist); + + virtual void orderChanged(GMTrackList*) const; + + virtual FXbool hasCurrentTrack(GMSource * ) const; + + virtual FXbool findCurrent(GMTrackList * tracklist,GMSource * src); + + virtual void resetCurrent() { current_track=-1; current_queue=-1; } + + virtual void markCurrent(GMTrackList * tracklist,FXint item); + + virtual FXbool moveTrack(GMTrackList *,FXint from,FXint to); + + virtual FXString getName() const; + + virtual FXint getType() const { return SOURCE_DATABASE_PLAYLIST; } + + virtual FXint getSortColumn(FXbool browse) const { if (browse) return HEADER_BROWSE; else return HEADER_QUEUE; } + + virtual FXbool getQueueColumn(FXbool browse) const { if (browse) return false; else return true; } + + virtual FXbool defaultBrowse() const { return false; } + +#if FOXVERSION >= FXVERSION(1,7,12) + virtual FXString settingKey() const { return "database_playlist_" + FXString::value(playlist); } +#else + virtual FXString settingKey() const { return "database_playlist_" + FXStringVal(playlist); } +#endif + + + virtual FXbool source_context_menu(FXMenuPane * pane); + + virtual FXbool genre_context_menu(FXMenuPane * pane); + + virtual FXbool artist_context_menu(FXMenuPane * pane); + + virtual FXbool album_context_menu(FXMenuPane * pane); + + virtual FXbool track_context_menu(FXMenuPane * pane); + + virtual FXbool dnd_source_accepts(FXDragType*,FXuint); + + virtual ~GMPlayListSource(); + }; + +#endif diff --git a/src/GMPlayer.cpp b/src/GMPlayer.cpp new file mode 100644 index 0000000..9f71a59 --- /dev/null +++ b/src/GMPlayer.cpp @@ -0,0 +1,1075 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#include +#include +#include +#include "gmdefs.h" +#include "GMTrackDatabase.h" +#include "GMList.h" +#include "GMTrackList.h" +#include "GMSource.h" +#include "GMPlayerManager.h" +#include "GMPlayer.h" +#include "GMWindow.h" +#include "GMRemote.h" +#include "GMTag.h" + +#define XINEVERSION ((XINE_SUB_VERSION)+(XINE_MINOR_VERSION*100)+(XINE_MAJOR_VERSION*10000)) + +GMEQBands::GMEQBands() { + for (FXint i=0;i<10;i++) + bands[i]=0; + } + +GMEQBands::GMEQBands(FXdouble e0,FXdouble e1,FXdouble e2,FXdouble e3,FXdouble e4,FXdouble e5,FXdouble e6,FXdouble e7,FXdouble e8,FXdouble e9){ + bands[0]=e0; + bands[1]=e1; + bands[2]=e2; + bands[3]=e3; + bands[4]=e4; + bands[5]=e5; + bands[6]=e6; + bands[7]=e7; + bands[8]=e8; + bands[9]=e9; + } + +GMEQBands::GMEQBands(const GMEQBands & v){ + for (FXint i=0;i<10;i++) + bands[i]=v.bands[i]; + } + +void GMEQBands::unparse(FXString & preset) const { + + /// This is a really dirty and quick hack that is not garanteed to work. We need + /// it for proper floating point to string conversion. + /// xine will set the LC_NUMERIC locale in its threads, so we're not sure, + /// when xine will change it. Luckily xine is already shutdown when we call this. + setlocale(LC_NUMERIC,"C"); + + preset+=GMStringFormat("%lg,%lg,%lg,%lg,%lg,%lg,%lg,%lg,%lg,%lg",bands[0],bands[1],bands[2],bands[3],bands[4],bands[5],bands[6],bands[7],bands[8],bands[9]); + } + +void GMEQBands::parse(const FXString & preset) { + + /// This is a really dirty and quick hack that is not garanteed to work. We need + /// it for proper floating point to string conversion. + /// xine will set the LC_NUMERIC locale in its threads, so we're not sure, + /// when xine will change it. Luckily xine is already shutdown when we call this. + setlocale(LC_NUMERIC,"C"); + + if (!preset.empty()) { + for (FXint i=0;i<10;i++) +#if FOXVERSION < FXVERSION(1,7,0) + bands[i]=FXDoubleVal(preset.section(',',i)); +#else + bands[i]=preset.section(',',i).toDouble(); +#endif + } + } + + + +GMEqualizer::GMEqualizer() : preamp(0),enabled(false) { + } + +GMEqualizer::GMEqualizer(const GMEQBands & v) : bands(v),preamp(0),enabled(true) { + } + +void GMEqualizer::load(FXSettings & settings) { + enabled=settings.readBoolEntry("audio-plugins","equalizer",enabled); + FXString entry = settings.readStringEntry("audio-plugins","equalizer-bands",NULL); + bands.parse(entry); + } + +void GMEqualizer::save(FXSettings & settings) const { + settings.writeBoolEntry("audio-plugins","equalizer",enabled); + FXString entry; + bands.unparse(entry); + settings.writeStringEntry("audio-plugins","equalizer-bands",entry.text()); + } + +const FXString xineconfigfile = FXSystem::getHomeDirectory() + PATHSEPSTRING + ".goggles" + PATHSEPSTRING + "xineconf"; + +FXDEFMAP(GMPlayer) GMPlayerMap[]={ + FXMAPFUNCS(SEL_UPDATE,GMPlayer::ID_EQ_30HZ,GMPlayer::ID_EQ_16000HZ,GMPlayer::onUpdEqualizer), + FXMAPFUNC(SEL_UPDATE,GMPlayer::ID_PREAMP,GMPlayer::onUpdPreamp), + + FXMAPFUNC(SEL_UPDATE,GMPlayer::ID_VOLUME,GMPlayer::onUpdVolume), + FXMAPFUNC(SEL_UPDATE,GMPlayer::ID_MUTE,GMPlayer::onUpdMute), + FXMAPFUNC(SEL_UPDATE,GMPlayer::ID_UNMUTE,GMPlayer::onUpdUnMute), + FXMAPFUNC(SEL_UPDATE,GMPlayer::ID_TOGGLE_MUTE,GMPlayer::onUpdToggleMute), + FXMAPFUNCS(SEL_COMMAND,GMPlayer::ID_EQ_30HZ,GMPlayer::ID_EQ_16000HZ,GMPlayer::onCmdEqualizer), + FXMAPFUNCS(SEL_CHANGED,GMPlayer::ID_EQ_30HZ,GMPlayer::ID_EQ_16000HZ,GMPlayer::onCmdEqualizer), + FXMAPFUNC(SEL_COMMAND,GMPlayer::ID_PREAMP,GMPlayer::onCmdPreamp), + FXMAPFUNC(SEL_CHANGED,GMPlayer::ID_PREAMP,GMPlayer::onCmdPreamp), + + FXMAPFUNC(SEL_COMMAND,GMPlayer::ID_VOLUME,GMPlayer::onCmdVolume), + FXMAPFUNC(SEL_CHANGED,GMPlayer::ID_VOLUME,GMPlayer::onCmdVolume), + FXMAPFUNC(SEL_COMMAND,GMPlayer::ID_MUTE,GMPlayer::onCmdMute), + FXMAPFUNC(SEL_COMMAND,GMPlayer::ID_UNMUTE,GMPlayer::onCmdUnMute), + FXMAPFUNC(SEL_COMMAND,GMPlayer::ID_TOGGLE_MUTE,GMPlayer::onCmdToggleMute), + + }; + +FXIMPLEMENT(GMPlayer,FXObject,GMPlayerMap,ARRAYNUMBER(GMPlayerMap)) + +GMPlayer::GMPlayer(){ + } + +GMPlayer::GMPlayer(int argc,char** argv) : FXObject(), xine(NULL),ao(NULL),so(NULL),queue(NULL), post_volume_normalize(NULL) { + position=0; + ctime=0; + ttime=0; + hours=0; + minutes=0; + seconds=0; + volume=0; + repeat_a=-1; + repeat_b=-1; + ignore_uimsg=false; + debug=false; + + for (FXint i=1;ireg()); + } + + +GMPlayer::~GMPlayer(){ + close(); + } + + +FXbool GMPlayer::opened() const { + if (xine && so) return true; + else return false; + } + + +FXbool GMPlayer::init() { + if (xine == NULL) { + + /// Create Xine + xine = xine_new(); + + /// load xine config file and init xine + xine_config_load(xine,xineconfigfile.text()); + + /// Init the Engine + xine_init(xine); + + /// Enable Debug Output + if (debug) xine_engine_set_param(xine,XINE_ENGINE_PARAM_VERBOSITY,XINE_VERBOSITY_DEBUG); + } + + /// Open Audio Driver + if (ao == NULL) { + + FXString audio_driver = FXApp::instance()->reg().readStringEntry("xine","driver",NULL); + + if (!audio_driver.empty() && audio_driver!="auto") { + ao = xine_open_audio_driver(xine,audio_driver.text(),NULL); + if (ao) FXApp::instance()->reg().writeStringEntry("xine","driver",audio_driver.text()); + } + + if (ao == NULL) { + const char *const * plugins = xine_list_audio_output_plugins(xine); + for (FXint i=0;plugins[i]!=NULL;i++){ + if (compare(plugins[i],"none") && compare(plugins[i],"file") ){ + ao = xine_open_audio_driver(xine,plugins[i],NULL); + if (ao) { + FXApp::instance()->reg().writeStringEntry("xine","driver",plugins[i]); + break; + } + } + } + } + + if (ao == NULL ) { + msg=fxtr("Unable to initialize audio driver."); + return false; + } + } + + FXASSERT(so==NULL); + + /// Open Default Stream + so = xine_stream_new(xine,ao,NULL); + if (so == NULL) { + msg="Unable to create a new stream for playback."; + xine_close_audio_driver(xine,ao); + return false; + } + + + if (FXApp::instance()->reg().readBoolEntry("audio-plugins","volume-normalization",false) && + hasVolumeNormalization()) { + setVolumeNormalization(true); + } + + /// Create Event Queue for Stream + queue = xine_event_new_queue(so); + + /// Init Volume + volume = xine_get_param(so,XINE_PARAM_AUDIO_VOLUME); + + /// Faster Seeking + //xine_set_param(so,XINE_PARAM_METRONOM_PREBUFFER,6000); + + /// Ignore Video + xine_set_param(so,XINE_PARAM_IGNORE_VIDEO,1); + + /// Ignore SPU + xine_set_param(so,XINE_PARAM_IGNORE_SPU,1); + + /// Init the Equalizer + setEqualizer(equalizer); + + /// Debug Output + if (debug) xine_set_param(so,XINE_PARAM_VERBOSITY,XINE_VERBOSITY_DEBUG); + + return true; + } + +void GMPlayer::getCurrentDriver(FXString & driver) { + driver=FXApp::instance()->reg().readStringEntry("xine","driver","auto"); + } + +void GMPlayer::getAvailableDrivers(FXString & drivers) { + const char *const * plugins = xine_list_audio_output_plugins(xine); + for (FXint i=0;plugins[i]!=NULL;i++){ + if (compare(plugins[i],"none") && compare(plugins[i],"file") ){ + if (!drivers.empty()) drivers+="\n"; + drivers+=plugins[i]; + } + } +/* + xine_cfg_entry_t entry; + for (xine_config_get_first_entry(xine,&entry);xine_config_get_next_entry(xine,&entry);){ + if (entry.exp_level==0) fxmessage("key %d: %s\n",entry.exp_level,entry.key); + } +*/ + } + + +void GMPlayer::setupGapless() { + if (so) { +#ifdef XINE_PARAM_EARLY_FINISHED_EVENT + if (xine_check_version(1,1,1) && GMPlayerManager::instance()->getPreferences().play_gapless && gm_is_local_file(mrl)) { + xine_set_param(so,XINE_PARAM_EARLY_FINISHED_EVENT,1); + } + else { + xine_set_param(so,XINE_PARAM_EARLY_FINISHED_EVENT,0); + } +#endif + } + } + +FXbool GMPlayer::hasGapless() const{ +#ifdef XINE_PARAM_EARLY_FINISHED_EVENT + if (xine_check_version(1,1,1)) + return true; +#endif + return false; + } + +FXbool GMPlayer::hasVolumeNormalization() const { + if (post_volume_normalize) return true; + const char * const * filters = xine_list_post_plugins_typed(xine,XINE_POST_TYPE_AUDIO_FILTER); + for (FXint i=0;filters[i];i++){ + if (comparecase(filters[i],"volnorm")==0) + return true; + } + return false; + } + + +void GMPlayer::setVolumeNormalization(FXbool enable){ + if (enable) { + + if (post_volume_normalize) + return; + + xine_audio_port_t * targets[2]={ao,NULL}; + + post_volume_normalize = xine_post_init(xine,"volnorm",1,targets,NULL); + if (post_volume_normalize) { + + xine_post_out_t * source = xine_get_audio_source(so); + + xine_post_wire_audio_port(source,post_volume_normalize->audio_input[0]); + + FXApp::instance()->reg().writeBoolEntry("audio-plugins","volume-normalization",true); + } + + } + else { + if (!post_volume_normalize) + return; + + xine_post_out_t * source = xine_get_audio_source(so); + + xine_post_wire_audio_port(source,ao); + + xine_post_dispose(xine,post_volume_normalize); + + post_volume_normalize = NULL; + + FXApp::instance()->reg().writeBoolEntry("audio-plugins","volume-normalization",false); + } + } + + + + + +void GMPlayer::getErrorMessage(FXString & errormsg) { + errormsg=msg; + } + +void GMPlayer::check_xine_error() { + FXint error = xine_get_error(so); + switch(error) { + case XINE_ERROR_NO_INPUT_PLUGIN: + msg="No input plugin found to play:\n" + mrl; + break; + case XINE_ERROR_NO_DEMUX_PLUGIN: + msg="No demux plugin found to play:\n" + mrl; + break; + case XINE_ERROR_DEMUX_FAILED: + msg="Unable to play:\n" + mrl; + break; + case XINE_ERROR_INPUT_FAILED: + msg="Unable to open:\n" + mrl; + break; + case XINE_ERROR_MALFORMED_MRL: + msg="Resource does not exist:\n" + mrl; + break; + case XINE_ERROR_NONE: + msg="Nothing to report\n"; + default: + msg="Unknown error while trying to play:\n" + mrl; + break; + } + ignore_uimsg=true; + } + +FXbool GMPlayer::checkInitialized() { + if (!so && !init()) return false; + return true; + } + +FXbool GMPlayer::initialize() { + if (!so && !init()) return false; + return true; + } + + + + +FXbool GMPlayer::open(const FXString & mrl_in){ + + if (!so && !init()) + return false; + + mrl=mrl_in; + + /// Make sure we encode #. + if (mrl[0]=='/') { + mrl.substitute("#","%23"); + mrl.prepend("file:"); + } + + /// Open Mrl + if (xine_open(so,mrl.text())==0){ + + /// Disable Gapless Playback +#ifdef XINE_PARAM_GAPLESS_SWITCH + if (xine_check_version(1,1,1)){ + xine_set_param(so,XINE_PARAM_GAPLESS_SWITCH,0); + } +#endif + check_xine_error(); + return false; + } + + setupGapless(); + + progress=100; + return true; + } + +void GMPlayer::close_device(){ +#ifdef XINE_PARAM_AUDIO_CLOSE_DEVICE + if (xine && so) { + xine_set_param(so,XINE_PARAM_AUDIO_CLOSE_DEVICE,1); + } +#endif + } + +FXbool GMPlayer::changeDriver(const FXString & driver) { + + if (post_volume_normalize) { + + xine_post_out_t * source = xine_get_audio_source(so); + + xine_post_wire_audio_port(source,ao); + + xine_post_dispose(xine,post_volume_normalize); + + post_volume_normalize=NULL; + } + + if (queue) { + xine_event_dispose_queue(queue); + queue=NULL; + } + + if (so) { + stop(); + xine_dispose(so); + so=NULL; + } + + if (ao) { + xine_close_audio_driver(xine,ao); + ao=NULL; + } + + + /// Set the new driver + FXApp::instance()->reg().writeStringEntry("xine","driver",driver.text()); + + /// Initialize the system + return initialize(); + } + + +void GMPlayer::close(){ + +#if XINEVERSION >= 10001 + if (xine && xine_check_version(1,0,1)) { + xine_plugins_garbage_collector(xine); + } +#endif + + if (xine) { + xine_config_save(xine,xineconfigfile.text()); + } + + if (post_volume_normalize) { + + xine_post_out_t * source = xine_get_audio_source(so); + + xine_post_wire_audio_port(source,ao); + + xine_post_dispose(xine,post_volume_normalize); + + post_volume_normalize=NULL; + } + + if (queue) { + xine_event_dispose_queue(queue); + queue=NULL; + } + + if (so) { + stop(); + xine_dispose(so); + so=NULL; + } + + if (ao) { + xine_close_audio_driver(xine,ao); + ao=NULL; + } + + if (xine){ + xine_exit(xine); + xine=NULL; + } + equalizer.save(FXApp::instance()->reg()); + } + +FXbool GMPlayer::seekable() const { + if (xine && so && (xine_get_status(so)==XINE_STATUS_PLAY) && xine_get_stream_info(so,XINE_STREAM_INFO_SEEKABLE)) + return true; + else + return false; + } + +void GMPlayer::setRepeatAB() { + if ((repeat_a>=0 && repeat_b>=0) || !seekable()) { + repeat_a=-1; + repeat_b=-1; + } + else if (repeat_a==-1 || position==repeat_a) + repeat_a=position; + else if (repeat_a=0 && repeat_b>=0){ + if (pos>repeat_b || pos=100) + return true; + else + return false; + } + +FXbool GMPlayer::playing() const { + return (so && xine_get_status(so)==XINE_STATUS_PLAY); + } + +void GMPlayer::setSpeed(FXint speed){ + if (so) xine_set_param(so,XINE_PARAM_SPEED,speed); + } + +FXint GMPlayer::getSpeed() const { + return so ? xine_get_param(so,XINE_PARAM_SPEED) : 0; + } + + +void GMPlayer::incSpeed(){ + FXint oldspeed=getSpeed(); + if (oldspeed>=XINE_SPEED_FAST_4) return; + setSpeed(oldspeed*2); + } + +void GMPlayer::decSpeed(){ + FXint oldspeed=getSpeed(); + if (oldspeed<=XINE_SPEED_SLOW_4) return; + setSpeed(oldspeed/2); + } + + +void GMPlayer::handle_async_events(){ + if (queue == NULL) return; + + xine_event_t * event =NULL; + xine_ui_message_data_t * data =NULL; + FXString uimsg; +static FXString newmrl; + + + /// First Get All Pending Events + while((event = xine_event_get(queue))!=NULL){ + switch(event->type){ + case XINE_EVENT_AUDIO_LEVEL : + volume=static_cast(event->data)->left; + GMPlayerManager::instance()->getMainWindow()->update_volume_display(volume); + break; + case XINE_EVENT_UI_CHANNELS_CHANGED : + break; + case XINE_EVENT_UI_SET_TITLE : + //fxmessage("Title Changed\n"); + FXApp::instance()->addTimeout(GMPlayerManager::instance(),GMPlayerManager::ID_UPDATE_TRACK_DISPLAY,TIME_SEC(5)); + + //GMPlayerManager::instance()->update_track_display(); + break; + case XINE_EVENT_FRAME_FORMAT_CHANGE : break; + case XINE_EVENT_QUIT : break; + case XINE_EVENT_PROGRESS : + //fxmessage("progress callback %d\n",xine_get_status(so)); + if (xine_get_status(so)==XINE_STATUS_PLAY) { + progress=static_cast(event->data)->percent; + if (progress>=100){ + //fxmessage("Update track display %d\n",progress); + //GMPlayerManager::instance()->update_track_display(); + /// let's wait a little, to give xine some time to get the latest track information... + FXApp::instance()->addTimeout(GMPlayerManager::instance(),GMPlayerManager::ID_UPDATE_TRACK_DISPLAY,TIME_SEC(5)); + } + else { + FXString status; + status.format("%s - %d%%\n",static_cast(event->data)->description,static_cast(event->data)->percent); + GMPlayerManager::instance()->setStatus(status); + } + } + break; +#ifdef XINE_EVENT_MRL_REFERENCE_EXT + case XINE_EVENT_MRL_REFERENCE_EXT : + newmrl=static_cast(event->data)->mrl; +// fxmessage("Received MRL reference: %s\n",static_cast(event->data)->mrl); +// GMPlayerManager::instance()->play(static_cast(event->data)->mrl); + break; +#else + case XINE_EVENT_MRL_REFERENCE : + newmrl=static_cast(event->data)->mrl; +// fxmessage("Received MRL reference: %s\n",static_cast(event->data)->mrl); + // GMPlayerManager::instance()->play(static_cast(event->data)->mrl); + break; +#endif + case XINE_EVENT_UI_NUM_BUTTONS : break; + case XINE_EVENT_SPU_BUTTON : break; + case XINE_EVENT_DROPPED_FRAMES : break; + case XINE_EVENT_UI_PLAYBACK_FINISHED: + + if (!newmrl.empty()) { + if (xine_get_status(so)==XINE_STATUS_STOP) { + GMPlayerManager::instance()->play(newmrl); + } + newmrl=FXString::null; + } + else { + +#ifdef XINE_PARAM_GAPLESS_SWITCH + if (xine_check_version(1,1,1) && GMPlayerManager::instance()->getPreferences().play_gapless && !GMPlayerManager::instance()->playlist_empty() && gm_is_local_file(mrl) ){ + xine_set_param(so,XINE_PARAM_GAPLESS_SWITCH,1); + } +#endif + /// For very short tracks we need to get the latest time. + xine_get_pos_length(so,&position,&ctime,&ttime); + GMPlayerManager::instance()->notify_playback_finished(); + /* + Return here so we don't want to update the time. Xine will report 0, + if we call it too soon which we don't want. + */ + xine_event_free(event); + return; + } + break; + + case XINE_EVENT_UI_MESSAGE : + //fxmessage("UI message\n"); + if (!ignore_uimsg) { + data = (xine_ui_message_data_t*)event->data; + switch(data->type) { + case XINE_MSG_NO_ERROR : break; + case XINE_MSG_ENCRYPTED_SOURCE : break; + case XINE_MSG_UNKNOWN_HOST : uimsg = fxtr("Unknown host."); break; + case XINE_MSG_UNKNOWN_DEVICE : uimsg = fxtr("Unknown device"); break; + case XINE_MSG_NETWORK_UNREACHABLE : uimsg = fxtr("Network not reachable."); break; + case XINE_MSG_AUDIO_OUT_UNAVAILABLE : uimsg = fxtr("Audio output unavailable."); break; + case XINE_MSG_CONNECTION_REFUSED : uimsg = fxtr("Connection Refused."); break; + case XINE_MSG_FILE_NOT_FOUND : uimsg = fxtr("File not found."); break; + case XINE_MSG_PERMISSION_ERROR : uimsg = fxtr("Resource not accessible. Check permissions"); break; + case XINE_MSG_READ_ERROR : uimsg = fxtr("Read Error"); break; + case XINE_MSG_LIBRARY_LOAD_ERROR : uimsg = fxtr("Error while loading library/plugin"); break; + case XINE_MSG_GENERAL_WARNING : uimsg = fxtr("Warning"); break; + case XINE_MSG_SECURITY : uimsg = fxtr("Security Warning"); break; + default : uimsg = fxtr("Unknown Error"); break; + } + } + ignore_uimsg=false; + break; + default: /*unhandled event*/ break; + } + xine_event_free(event); + event=NULL; + } + + + if (xine_get_status(so)==XINE_STATUS_PLAY) { + if (xine_get_param(so,XINE_PARAM_SPEED)!=XINE_SPEED_PAUSE) { + if (xine_get_pos_length(so,&position,&ctime,&ttime)){ + FXint time=ctime; + hours = (FXint) floor((double)time/3600000.0); + time -= (FXint) (3600000.0*hours); + minutes = (FXint) floor((double)time/60000.0); + time -= (FXint) (60000.0*minutes); + seconds = (FXint) floor((double)time/1000.0); + } + } + + if (repeat_b>=0 && repeat_a>=0 && repeat_b>repeat_a && (position>repeat_b || positionstop(); + GMPlayerManager::instance()->show_message(fxtr("Error"),uimsg.text()); + } + + } + +/// Audio Controls + +FXint GMPlayer::remaining() const { + FXint r = ttime-ctime; + return (FXint) floor((double)r/1000.0); + } + +FXint GMPlayer::getVolume() const { + return volume; + } + +void GMPlayer::setVolume(FXint level){ + if (so == NULL) return; + volume=level; + + //xine_set_param( so, XINE_PARAM_AUDIO_AMP_LEVEL,(FXuint)(level*preamp)); + + + xine_set_param(so,XINE_PARAM_AUDIO_VOLUME,level); + if (level==0 && !pausing() ) pause(); + if (level>0 && pausing() ) unpause(); + } + +FXbool GMPlayer::isMute() const { + return so ? xine_get_param(so,XINE_PARAM_AUDIO_MUTE) : true; + } + +void GMPlayer::mute(){ + if (so) xine_set_param(so,XINE_PARAM_AUDIO_MUTE,1); + } + +void GMPlayer::unmute(){ + if (so) xine_set_param(so,XINE_PARAM_AUDIO_MUTE,0); + } + + +void GMPlayer::disableEqualizer() { + equalizer.enabled=false; + if (so) { + for (FXint i=0;i<10;i++){ + xine_set_param(so,XINE_PARAM_EQ_30HZ+i,0); + } + } + } + +void GMPlayer::getEqualizer(GMEqualizer & eq){ + eq=equalizer; + } + +void GMPlayer::setEqualizer(const GMEqualizer & eq) { + FXint oldamp=equalizer.preamp; + equalizer=eq; + equalizer.preamp=oldamp; + if (so) { + if (equalizer.enabled) { + for (FXint i=0;i<10;i++){ + xine_set_param(so,XINE_PARAM_EQ_30HZ+i,equalizer.to_xine(i)); + } + } + else { + for (FXint i=0;i<10;i++){ + xine_set_param(so,XINE_PARAM_EQ_30HZ+i,0); + } + } + } + } + + +void GMPlayer::setReplayGain(FXdouble gain,FXdouble peak){ + if (replaygain.gain!=gain || replaygain.peak!=peak) { + replaygain.gain=gain; + replaygain.peak=peak; +#ifdef DEBUG + fxmessage("replay gain: %g %g\n",gain,peak); +#endif + set_preamp(); + } + } + + +void GMPlayer::set_preamp() { + FXdouble scale; + FXdouble gain_scale; + + if (!isnan(replaygain.gain)){ + + gain_scale=pow(10.0,replaygain.gain/20.0); + + if (equalizer.enabled) { /// with preamp + + scale=(equalizer.preamp_scale()*gain_scale); + + /// avoid clipping + if (!isnan(replaygain.peak) && replaygain.peak!=0.0) { + if ((scale*replaygain.peak)>1.0){ + if ((gain_scale*replaygain.peak)>1.0) + scale=1.0 / replaygain.peak; + else + scale=gain_scale; + } + } + } + else { /// no preamp + scale = gain_scale; + + /// avoid clipping + if (!isnan(replaygain.peak) && replaygain.peak!=0.0 && (gain_scale*replaygain.peak)>1.0) + scale=1.0 / replaygain.peak; + } + xine_set_param(so,XINE_PARAM_AUDIO_AMP_LEVEL,(FXint)(100.0*scale)); + } + else if (equalizer.enabled) { /// preamp only + //fxmessage("preamp: %d\n",equalizer.to_xine_preamp()); + xine_set_param(so,XINE_PARAM_AUDIO_AMP_LEVEL,equalizer.to_xine_preamp()); + } + else { /// disabled + xine_set_param(so,XINE_PARAM_AUDIO_AMP_LEVEL,100); + } + } + + +long GMPlayer::onCmdPreamp(FXObject*sender,FXSelector,void*){ + if (so) { + FXdouble value; + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_GETREALVALUE),(void*)&value); + equalizer.preamp=value;//FXCLAMP(-100,value,100); + set_preamp(); + } + return 1; + } + + +long GMPlayer::onUpdPreamp(FXObject*sender,FXSelector,void*){ + if (equalizer.enabled) { + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_ENABLE),NULL); + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_SETREALVALUE),(void*)&equalizer.preamp); + } + else { + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_DISABLE),NULL); + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_SETVALUE),NULL); /// for textfield NULL is string, for slider, NULL will be 0 + } + return 1; + } + +long GMPlayer::onUpdEqualizer(FXObject*sender,FXSelector sel,void*){ + if (equalizer.enabled) { + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_ENABLE),NULL); + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_SETREALVALUE),(void*)&equalizer.bands[FXSELID(sel)-ID_EQ_30HZ]); + } + else { + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_DISABLE),NULL); + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_SETVALUE),NULL); /// for textfield NULL is string, for slider, NULL will be 0 + } + return 1; + } + +long GMPlayer::onCmdEqualizer(FXObject*sender,FXSelector sel,void*){ + /// don't bother reading EQ values from xine. + /// due to rounding errors, the correct value won't be returned. + FXdouble value; + FXint which=FXSELID(sel)-ID_EQ_30HZ; + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_GETREALVALUE),(void*)&value); + equalizer.bands[which]=FXCLAMP(-6,value,6); + if (so) { + xine_set_param(so,XINE_PARAM_EQ_30HZ+which,equalizer.to_xine(which)); + } + return 1; + } + + +const char * GMPlayer::getVersion() const{ + return xine_get_version_string(); + } + +/// Message Handlers + +long GMPlayer::onCmdVolume(FXObject*,FXSelector,void*ptr){ + FXint level = (FXint)(FXival)ptr; + setVolume(level); + return 1; + } + +long GMPlayer::onUpdVolume(FXObject*sender,FXSelector,void*){ + FXWindow * window = (FXWindow*)sender; + if (window->getShell()->shown()){ + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_SETINTVALUE),&volume); + return 1; + } + return 0; + } + +long GMPlayer::onCmdMute(FXObject*,FXSelector,void*){ + mute(); + return 1; + } + +long GMPlayer::onUpdMute(FXObject*sender,FXSelector,void*){ + if (isMute()) + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_DISABLE),NULL); + else + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_ENABLE),NULL); + return 1; + } + +long GMPlayer::onCmdUnMute(FXObject*,FXSelector,void*){ + unmute(); + return 1; + } + +long GMPlayer::onUpdUnMute(FXObject*sender,FXSelector,void*){ + if (isMute()) + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_ENABLE),NULL); + else + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_DISABLE),NULL); + return 1; + } + + +long GMPlayer::onCmdToggleMute(FXObject*,FXSelector,void*){ + if (isMute()) + unmute(); + else + mute(); + return 1; + } + +long GMPlayer::onUpdToggleMute(FXObject*sender,FXSelector,void*){ + if (isMute()) + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_CHECK),NULL); + else + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_UNCHECK),NULL); + return 1; + } + + +void GMPlayer::getTrackInformation(GMTrack & info){ + info.clear(); + info.mrl = mrl; + info.artist = xine_get_meta_info(so,XINE_META_INFO_ARTIST); + info.album = xine_get_meta_info(so,XINE_META_INFO_ALBUM); + info.title = xine_get_meta_info(so,XINE_META_INFO_TITLE); + info.genre = xine_get_meta_info(so,XINE_META_INFO_GENRE); +#if FOXVERSION >= FXVERSION(1,7,12) + info.year = FXString(xine_get_meta_info(so,XINE_META_INFO_YEAR)).toInt(); + info.no = FXString(xine_get_meta_info(so,XINE_META_INFO_TRACK_NUMBER)).toInt(); +#else + info.year = FXIntVal(xine_get_meta_info(so,XINE_META_INFO_YEAR)); + info.no = FXIntVal(xine_get_meta_info(so,XINE_META_INFO_TRACK_NUMBER)); +#endif + info.time = 0; + if (xine_get_pos_length(so,NULL,NULL,&info.time)){ + info.time/=1000; + } + + info.bitrate=xine_get_stream_info(so,XINE_STREAM_INFO_BITRATE); + if (info.bitrate==0) + info.bitrate=xine_get_stream_info(so,XINE_STREAM_INFO_AUDIO_BITRATE); + + + //fxmessage("Playing %s - %s\n",info.artist.text(),info.title.text()); + } + +FXbool GMPlayer::setStringValue(const FXString & entry,const FXString & value){ + xine_cfg_entry_t config; + if (xine_config_lookup_entry(xine,entry.text(),&config)) { + config.str_value = (FXchar*)value.text(); + xine_config_update_entry (xine,&config); + return true; + } + return false; + } +#if 0 +void GMPlayer::list_cda_tracks(GMTrackDatabase * db,const FXString & device) { + GMTrack track; + FXchar ** tracks; + FXint num_tracks; + FXint id; + + if (!setStringValue("media.audio_cd.device",device)) + return; + + tracks = xine_get_autoplay_mrls(xine,"CD",&num_tracks); + if (num_tracks) { + for (int i=0;igetPreferences().import_default_user_title; + if (track.artist.empty()) track.artist = GMPlayerManager::instance()->getPreferences().import_default_user_artist; + if (track.album.empty()) track.album = GMPlayerManager::instance()->getPreferences().import_default_user_album; + if (track.genre.empty()) track.genre = GMPlayerManager::instance()->getPreferences().import_default_user_genre; + if (track.no==0) track.no=i+1; + db->insertTrack(track,id); + } + } + } + } +#endif diff --git a/src/GMPlayer.h b/src/GMPlayer.h new file mode 100644 index 0000000..b92fa21 --- /dev/null +++ b/src/GMPlayer.h @@ -0,0 +1,278 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#ifndef DVD_XINE +#define DVD_XINE + + +class GMTrackDatabase; + +enum { + REPEAT_AB_OFF=0, + REPEAT_AB_A, + REPEAT_AB_B + }; + + +class GMEQBands { +public: + FXdouble bands[10]; +public: + GMEQBands(); + GMEQBands(FXdouble e0,FXdouble e1,FXdouble e2,FXdouble e3,FXdouble e4,FXdouble e5,FXdouble e6,FXdouble e7,FXdouble e8,FXdouble e9); + GMEQBands(const GMEQBands &); + + FXdouble& operator[](FXint i){ return bands[i]; } + const FXdouble& operator[](FXint i) const { return bands[i]; } + + GMEQBands& operator=(const GMEQBands& src){ + for (FXint i=0;i<10;i++) bands[i]=src.bands[i]; + return *this; + } + + FXbool operator==(const GMEQBands& v) const { + for (FXint i=0;i<10;i++) if (v.bands[i]!=bands[i]) return false; + return true; + } + + FXbool operator!=(const GMEQBands& v) const { + for (FXint i=0;i<10;i++) if (v.bands[i]!=bands[i]) return true; + return false; + } + + void unparse(FXString & preset) const; + void parse(const FXString & preset) ; + }; + +class GMEqualizer{ +public: + GMEQBands bands; + FXdouble preamp; + FXbool enabled; +public: + GMEqualizer(); + GMEqualizer(const GMEQBands&); + + FXint to_xine(FXint i) const { + /// Adapted from Amarok, kaffeine, xfmedia. + /// Transform (-100 ... 100) -> (1 ... 200) + /// The original code was without the ceilf which returned 0 for -100. + /// return (FXint)ceilf(100.0f+((float)bands[i]*0.995f)); + + /// I think we can go beyond the 200 limit.... + return (FXint)(pow(10.0,bands[i]/20.0)*100.0); + } + + FXint to_xine_preamp() const { + return (FXint)(pow(10.0,preamp/20.0)*100.0); + } + + FXdouble preamp_scale() const { + return pow(10.0,preamp/20.0); //((FXdouble)(100+preamp))/100.0; + } + + void load(FXSettings&); + void save(FXSettings&) const; + }; + + +struct GMReplayGain { + FXdouble gain; + FXdouble peak; + GMReplayGain() : gain(NAN),peak(NAN) {} + }; + +/// Interface to Xine +class GMPlayer : public FXObject { +FXDECLARE(GMPlayer) +private: + xine_t * xine; + xine_audio_port_t * ao; + xine_stream_t * so; + xine_event_queue_t * queue; + xine_post_t * post_volume_normalize; +private: + FXString mrl; + FXint position; /// Position of Stream 0...65535 + FXint ctime; + FXint ttime; + FXint hours; /// Position -> hours + FXint minutes; /// Position -> minutes + FXint seconds; /// Position -> seconds + FXint volume; /// Volume Level + FXint progress; /// load progress + FXint repeat_a; + FXint repeat_b; + FXString msg; /// Message from Xine + FXbool ignore_uimsg; + FXbool debug; + GMEqualizer equalizer; + GMReplayGain replaygain; + +// FXdouble replaygain; +protected: + FXbool setStringValue(const FXString & entry,const FXString & value); + FXbool init(); + void set_preamp(); + void check_xine_error(); +protected: + GMPlayer(); +public: + enum { + ID_VOLUME, + ID_MUTE, + ID_UNMUTE, + ID_TOGGLE_MUTE, + ID_PREAMP, + ID_EQ_30HZ, + ID_EQ_60HZ, + ID_EQ_125HZ, + ID_EQ_250HZ, + ID_EQ_500HZ, + ID_EQ_1000HZ, + ID_EQ_2000HZ, + ID_EQ_4000HZ, + ID_EQ_8000HZ, + ID_EQ_16000HZ, + }; +public: + long onCmdVolume(FXObject*,FXSelector,void*); + long onUpdVolume(FXObject*,FXSelector,void*); + long onCmdMute(FXObject*,FXSelector,void*); + long onUpdMute(FXObject*,FXSelector,void*); + long onCmdUnMute(FXObject*,FXSelector,void*); + long onUpdUnMute(FXObject*,FXSelector,void*); + long onCmdToggleMute(FXObject*,FXSelector,void*); + long onUpdToggleMute(FXObject*,FXSelector,void*); + long onCmdEqualizer(FXObject*,FXSelector,void*); + long onUpdEqualizer(FXObject*,FXSelector,void*); + long onCmdPreamp(FXObject*,FXSelector,void*); + long onUpdPreamp(FXObject*,FXSelector,void*); +public: + GMPlayer(int argc,char** argv); + + FXbool initialize(); + + FXbool opened() const; + + + /// Open a new stream + FXbool open(const FXString & mrl); + + /// Play the current stream from position + FXbool play(FXint pos=0); + + FXbool seekable() const; + + void setRepeatAB(); + + FXuint getRepeatAB() const; + + /// Close the current stream + void close(); + + void close_device(); + + + FXbool changeDriver(const FXString & driver); + void getAvailableDrivers(FXString & drivers); + void getCurrentDriver(FXString & driver); + + + /// Stop the current stream + void stop(); + + /// Pause the current stream + void pause(); + + /// Continue playback + void unpause(); + + /// Are we currently pausing? + FXbool pausing(); + + /// Set the playback speed + void setSpeed(FXint level); + + /// Get the playback speed + FXint getSpeed() const; + + void incSpeed(); + + void decSpeed(); + + FXint remaining() const; + + /// Are we currently playing + FXbool playing() const; + + /// Get the current position + FXint getHours() const {return hours;} + FXint getMinutes() const {return minutes;} + FXint getSeconds() const {return seconds;} + FXint getPosition() const {return position;} + FXint getPositionMS() const { return ctime; } + + /// Set Audio Volume (0...100) + void setVolume(FXint level); + + /// Get Audio Volume (0...100) + FXint getVolume() const; + + /// Mute the Audio + void mute(); + + /// Unmute the Audiu + void unmute(); + + /// mute or not + FXbool isMute() const; + + FXbool checkInitialized(); + + FXbool hasVolumeNormalization() const; + + FXbool hasGapless() const; + + void setupGapless(); + + void setVolumeNormalization(FXbool enable); + + FXbool getVolumeNormalization() { return post_volume_normalize!=NULL; } + + const char * getVersion() const; + + /// Update the player state. + void handle_async_events(); + + void getTrackInformation(GMTrack &); + + void getErrorMessage(FXString & errormsg); + + void disableEqualizer(); + + void setEqualizer(const GMEqualizer &); + + void getEqualizer(GMEqualizer&); + + void setReplayGain(FXdouble gain,FXdouble peak); + + ~GMPlayer(); + }; + +#endif diff --git a/src/GMPlayerManager.cpp b/src/GMPlayerManager.cpp new file mode 100644 index 0000000..8cf8b5b --- /dev/null +++ b/src/GMPlayerManager.cpp @@ -0,0 +1,1781 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#include +#include +#include +#include +#include +#include + +#include +#include + + +#include +#include "icons.h" +#include "gmdefs.h" + +#ifdef HAVE_DBUS +#include "GMDBus.h" +#include "GMNotifyDaemon.h" +#include "GMMediaPlayerService.h" +#include "GMSettingsDaemon.h" +#include "gogglesmm_xml.h" +#endif + +#include +#include "GMApp.h" +#include "GMPlayer.h" +#include "GMAbout.h" +#include "GMWindow.h" +#include "GMTrackList.h" +#include "GMRemote.h" +#include "GMTag.h" +#include "GMThread.h" +#include "GMSearch.h" +#include "GMList.h" +#include "GMTrackView.h" +#include "GMSource.h" +#include "GMTrackDatabase.h" +#include "GMDatabaseSource.h" +#include "GMStreamSource.h" +#include "GMPlayListSource.h" + +#include "GMIconTheme.h" +#include "GMPlayerManager.h" +#include "GMFetch.h" +#include "GMEQDialog.h" +#include "GMTrayIcon.h" +#include "GMSourceView.h" +#include "GMAudioScrobbler.h" + +enum { + FIFO_STATUS_ERROR = 0, + FIFO_STATUS_OWNER = 1, + FIFO_STATUS_EXISTS = 2 + }; + +#if APPLICATION_BETA_DB > 0 +#define DATABASE_FILENAME "goggles_beta.db" +#else +#define DATABASE_FILENAME "goggles.db" +#endif + + +FXDEFMAP(GMPlayerManager) GMPlayerManagerMap[]={ + FXMAPFUNC(SEL_TIMEOUT,GMPlayerManager::ID_UPDATE_TRACK_DISPLAY,GMPlayerManager::onUpdTrackDisplay), + FXMAPFUNC(SEL_TIMEOUT,GMPlayerManager::ID_HANDLE_EVENTS,GMPlayerManager::onUpdEvents), + FXMAPFUNC(SEL_TIMEOUT,GMPlayerManager::ID_SLEEP_TIMER,GMPlayerManager::onCmdSleepTimer), + FXMAPFUNC(SEL_TIMEOUT,GMPlayerManager::ID_PLAY_NOTIFY,GMPlayerManager::onPlayNotify), + FXMAPFUNC(SEL_IO_READ,GMPlayerManager::ID_DDE_MESSAGE,GMPlayerManager::onDDEMessage), + FXMAPFUNC(SEL_CHORE,GMPlayerManager::ID_PLAYER_ERROR,GMPlayerManager::onPlayerError), + FXMAPFUNC(SEL_CLOSE,GMPlayerManager::ID_WINDOW,GMPlayerManager::onCmdCloseWindow), + FXMAPFUNC(SEL_SIGNAL,GMPlayerManager::ID_CHILD,GMPlayerManager::onCmdChild), + + FXMAPFUNC(SEL_COMMAND,GMPlayerManager::ID_SCROBBLER,GMPlayerManager::onScrobblerError), + FXMAPFUNC(SEL_OPENED,GMPlayerManager::ID_SCROBBLER,GMPlayerManager::onScrobblerOpen), + + + FXMAPFUNC(SEL_COMMAND,GMPlayerManager::ID_EQUALIZER,GMPlayerManager::onCmdEqualizer), + FXMAPFUNC(SEL_COMMAND,GMPlayerManager::ID_SHOW_MANAGER,GMPlayerManager::onCmdShowManager), + +#ifdef HAVE_DBUS + FXMAPFUNC(SEL_KEYPRESS,GMPlayerManager::ID_GNOME_SETTINGS_DAEMON,GMPlayerManager::onCmdSettingsDaemon), +#endif + FXMAPFUNC(SEL_COMMAND,GMPlayerManager::ID_DOWNLOAD_COMPLETE,GMPlayerManager::onCmdDownloadComplete) + }; + +FXIMPLEMENT(GMPlayerManager,FXObject,GMPlayerManagerMap,ARRAYNUMBER(GMPlayerManagerMap)) + +#ifdef HAVE_DBUS + +#define GOGGLESMM_DBUS_NAME "org.fifthplanet.gogglesmm" +#define GOGGLESMM_DBUS_PATH "/org/fifthplanet/gogglesmm" +#define GOGGLESMM_DBUS_INTERFACE "org.fifthplanet.gogglesmm" + +DBusHandlerResult dbus_systembus_filter(DBusConnection *,DBusMessage * msg,void * data){ + + FXTRACE((80,"------------\n")); + FXTRACE((80,"path: %s\n",dbus_message_get_path(msg))); + FXTRACE((80,"member: \"%s\"\n",dbus_message_get_member(msg))); + FXTRACE((80,"interface: %s\n",dbus_message_get_interface(msg))); + FXTRACE((80,"sender: %s\n",dbus_message_get_sender(msg))); + + GMPlayerManager * p = (GMPlayerManager*)data; + if (dbus_message_has_path(msg,"/org/freedesktop/NetworkManager")){ + if (dbus_message_is_signal(msg,"org.freedesktop.NetworkManager","StateChanged") || + dbus_message_is_signal(msg,"org.freedesktop.NetworkManager","StateChange")) { + + FXuint state=0; + enum { +/* + // Network Manager 0.7 / 0.8 + + NM7_STATE_UNKNOWN = 0, + NM7_STATE_ASLEEP = 1, + NM7_STATE_CONNECTING = 2, + NM7_STATE_CONNECTED = 3, + NM7_STATE_DISCONNECTED = 4, + + // Network Manager 0.9 + NM9_STATE_UNKNOWN = 0, + NM9_STATE_ASLEEP = 10, + NM9_STATE_DISCONNECTED = 20, + NM9_STATE_DISCONNECTING = 30, + NM9_STATE_CONNECTING = 40, + NM9_STATE_CONNECTED_LOCAL = 50, + NM9_STATE_CONNECTED_SITE = 60, + NM9_STATE_CONNECTED_GLOBAL = 70, + +*/ + NM7_STATE_CONNECTED = 3, + NM9_STATE_CONNECTED_GLOBAL = 70, + }; + + if (dbus_message_get_args(msg,NULL,DBUS_TYPE_UINT32,&state,DBUS_TYPE_INVALID)) { + if (p->getAudioScrobbler() && (state==NM7_STATE_CONNECTED || + state==NM9_STATE_CONNECTED_GLOBAL)) + p->getAudioScrobbler()->nudge(); + } + return DBUS_HANDLER_RESULT_HANDLED; + } + } + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + +DBusHandlerResult dbus_playermanager_filter(DBusConnection *connection,DBusMessage * msg,void * data){ + FXchar * mrl; + GMPlayerManager * p = (GMPlayerManager*)data; + if (dbus_message_has_path(msg,GOGGLESMM_DBUS_PATH)){ + if (dbus_message_is_method_call(msg,GOGGLESMM_DBUS_INTERFACE,"play")){ + p->cmd_play(); + return gm_dbus_reply_if_needed(connection,msg); + } + else if (dbus_message_is_method_call(msg,GOGGLESMM_DBUS_INTERFACE,"playpause")){ + p->cmd_playpause(); + return gm_dbus_reply_if_needed(connection,msg); + } + else if (dbus_message_is_method_call(msg,GOGGLESMM_DBUS_INTERFACE,"stop")){ + p->cmd_stop(); + return gm_dbus_reply_if_needed(connection,msg); + } + else if (dbus_message_is_method_call(msg,GOGGLESMM_DBUS_INTERFACE,"pause")){ + p->cmd_pause(); + return gm_dbus_reply_if_needed(connection,msg); + } + else if (dbus_message_is_method_call(msg,GOGGLESMM_DBUS_INTERFACE,"next")){ + p->cmd_next(); + return gm_dbus_reply_if_needed(connection,msg); + } + else if (dbus_message_is_method_call(msg,GOGGLESMM_DBUS_INTERFACE,"prev")){ + p->cmd_prev(); + return gm_dbus_reply_if_needed(connection,msg); + } + else if (dbus_message_is_method_call(msg,GOGGLESMM_DBUS_INTERFACE,"open")){ + if (dbus_message_get_args(msg,NULL,DBUS_TYPE_STRING,&mrl,DBUS_TYPE_INVALID)) { + p->open(mrl); + } + return gm_dbus_reply_if_needed(connection,msg); + } + else if (dbus_message_is_method_call(msg,GOGGLESMM_DBUS_INTERFACE,"raise")){ + if (p->getMainWindow()->shown()) + p->getMainWindow()->raise(); + return gm_dbus_reply_if_needed(connection,msg); + } + else if (dbus_message_is_method_call(msg,GOGGLESMM_DBUS_INTERFACE,"notify")){ + p->display_track_notification(); + return gm_dbus_reply_if_needed(connection,msg); + } + else if (dbus_message_is_method_call(msg,GOGGLESMM_DBUS_INTERFACE,"toggleshown")){ + p->cmd_toggle_shown(); + return gm_dbus_reply_if_needed(connection,msg); + } + else if (dbus_message_is_method_call(msg,GOGGLESMM_DBUS_INTERFACE,"getactions")){ + FXuint actions=0; + enum { + CAN_PLAY = 0x1, + CAN_PAUSE = 0x2, + CAN_STOP = 0x4, + CAN_NEXT = 0x8, + CAN_PREV = 0x10, + }; + if (p->can_play() || p->can_unpause()) actions|=CAN_PLAY; + if (p->can_stop()) actions|=CAN_STOP; + if (p->can_pause()) actions|=CAN_PAUSE; + if (p->can_prev()) actions|=CAN_PREV; + if (p->can_next()) actions|=CAN_NEXT; + return gm_dbus_reply_unsigned_int(connection,msg,actions); + } + else if (dbus_message_is_method_call(msg,GOGGLESMM_DBUS_INTERFACE,"exit")){ + gm_dbus_reply_if_needed(connection,msg); + if (p->getMainWindow()) p->getMainWindow()->handle(p,FXSEL(SEL_COMMAND,GMWindow::ID_QUIT),NULL); + return DBUS_HANDLER_RESULT_HANDLED; + } + else if (dbus_message_is_method_call(msg,"org.freedesktop.DBus.Introspectable","Introspect")){ + return gm_dbus_reply_string(connection,msg,gogglesmm_xml); + } + } + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + +DBusObjectPathVTable org_fifthplanet_gogglesmm={ + NULL, + &dbus_playermanager_filter, + NULL, + NULL, + NULL, + NULL + }; + + +void dbus_send_to_self(DBusConnection * connection,const FXchar * signal,FXString argument) { + DBusMessage * msg; + msg = dbus_message_new_method_call(GOGGLESMM_DBUS_NAME,GOGGLESMM_DBUS_PATH,GOGGLESMM_DBUS_INTERFACE,signal); + if (msg){ + if (!argument.empty()) { + const FXchar * arg=argument.text(); + dbus_message_append_args(msg,DBUS_TYPE_STRING,&arg,DBUS_TYPE_INVALID); + } + dbus_message_set_no_reply(msg,true); + dbus_connection_send(connection,msg,NULL); + dbus_connection_flush(connection); + dbus_message_unref(msg); + } + } + + +FXint dbus_send_commands(DBusConnection * connection,int& argc,char** argv){ + FXString cmd="raise"; + FXString url; + if (argc>1) { + if (compare(argv[1],"--previous")==0) + cmd="prev"; + else if (compare(argv[1],"--play")==0) + cmd="play"; + else if (compare(argv[1],"--play-pause")==0) + cmd="playpause"; + else if (compare(argv[1],"--pause")==0) + cmd="pause"; + else if (compare(argv[1],"--next")==0) + cmd="next"; + else if (compare(argv[1],"--stop")==0) + cmd="stop"; + else if (compare(argv[1],"--toggle-shown")==0) + cmd="toggleshown"; + else if (compare(argv[1],"--raise")==0) + cmd="raise"; + else if (compare(argv[1],"--now-playing")==0) + cmd="notify"; + else { + cmd="open"; + url=argv[1]; + } + } + dbus_send_to_self(connection,cmd.text(),url); + return 1; + } +#endif + + + +long GMPlayerManager::onPlayNotify(FXObject*,FXSelector,void*){ + update_cover_display(); + if (!trackinfo.title.empty() && !trackinfo.artist.empty()) { + display_track_notification(); + if (lastfm) lastfm->nowplaying(trackinfo); + } + return 0; + } + +long GMPlayerManager::onUpdTrackDisplay(FXObject*,FXSelector,void*){ + update_track_display(); + if (source) + getTrackView()->showCurrent(); + return 0; + } + +long GMPlayerManager::onUpdEvents(FXObject*,FXSelector,void*){ + handle_async_events(); + application->addTimeout(this,GMPlayerManager::ID_HANDLE_EVENTS,TIME_MSEC(500)); + return 0; + } + +/* Stop Playback! */ +long GMPlayerManager::onCmdSleepTimer(FXObject*,FXSelector,void*){ + cmd_stop(); + return 1; + } + +#ifdef HAVE_DBUS +long GMPlayerManager::onCmdSettingsDaemon(FXObject*,FXSelector,void*ptr){ + const FXchar * cmd = (const FXchar*)ptr; + if (comparecase(cmd,"play")==0) cmd_playpause(); + else if (comparecase(cmd,"pause")==0) cmd_playpause(); + else if (comparecase(cmd,"stop")==0) cmd_stop(); + else if (comparecase(cmd,"previous")==0) cmd_prev(); + else if (comparecase(cmd,"next")==0) cmd_next(); + else { + GM_DEBUG_PRINT("Unknown or unhandled key press: %s\n",cmd); + } + return 1; + } +#endif + + +long GMPlayerManager::onDDEMessage(FXObject*,FXSelector,void*){ + FXString cmd; + FXchar buffer[1024]; + int nread=fifo.readBlock(buffer,1024); + if (nread>0) { + cmd=FXString(buffer,nread); + cmd.trim(); + if (cmd=="--previous") cmd_prev(); + else if (cmd=="--play") cmd_play(); + else if (cmd=="--play-pause") cmd_playpause(); + else if (cmd=="--pause") cmd_pause(); + else if (cmd=="--next") cmd_next(); + else if (cmd=="--stop") cmd_stop(); + else if (cmd=="--toggle-shown") cmd_toggle_shown(); + else if (cmd=="--now-playing") display_track_notification(); + else if (cmd=="--raise") { + if (mainwindow->shown()) + mainwindow->raise(); + } + else if (cmd.length() && cmd[0]!='-') { + open(cmd); + } + } + return 1; + } + + + +GMPlayerManager * GMPlayerManager::myself = NULL; + +GMPlayerManager* GMPlayerManager::instance() { + return myself; + } + + + +/// Constructor +GMPlayerManager::GMPlayerManager() : + count_track_remaining(0), +#ifdef HAVE_DBUS + sessionbus(NULL), + systembus(NULL), + notifydaemon(NULL), + mpris(NULL), + gsd(NULL), +#endif + application(NULL), + mainwindow(NULL), + player(NULL), + trayicon(NULL), + lastfm(NULL), + source(NULL) { + FXASSERT(myself==NULL); + myself=this; + database=NULL; + } + + + + +/// Destructor +GMPlayerManager::~GMPlayerManager() { + + /// Remove Signal Handlers +#ifndef DEBUG + application->removeSignal(SIGINT); + application->removeSignal(SIGQUIT); + application->removeSignal(SIGTERM); + application->removeSignal(SIGHUP); + application->removeSignal(SIGPIPE); +#endif + application->removeSignal(SIGCHLD); + + + /// Cleanup fifo crap + if (fifo.isOpen()) + fifo.close(); + if (!fifofilename.empty()) + FXFile::remove(fifofilename); + + delete lastfm; + + /// Delete Sources + for (FXint i=0;itrackview; + } + +GMSourceView * GMPlayerManager::getSourceView() const { + return mainwindow->sourceview; + } + + + + +FXint GMPlayerManager::init_fifo(int& argc,char** argv){ + FXString fifodir = FXSystem::getHomeDirectory() + PATHSEPSTRING + ".goggles"; + FXStat info; + + if ( (!FXStat::exists(fifodir) && !FXDir::create(fifodir) ) || !FXStat::isDirectory(fifodir) ) { + FXMessageBox::error(application,MBOX_OK,"Goggles Music Manager",fxtrformat("Unable to create directory %s\n"),fifodir.text()); + return FIFO_STATUS_ERROR; + } + + fifofilename = fifodir + PATHSEPSTRING + "gmm.dde"; + + /// Find existing fifo + if (FXStat::statFile(fifofilename,info)) { + + /// File exists, but it's not a fifo... try removing it + if (!info.isFifo() && !FXFile::remove(fifofilename)) { + fifofilename=FXString::null; + return FIFO_STATUS_ERROR; + } + + if (fifo.open(fifofilename,FXIO::WriteOnly|FXIO::NonBlocking)){ + FXString commandline; + + for (FXint i=1;iinitPlaylists(sources); + + sources.append(new GMStreamSource(database)); + + return true; + } + + + + +void GMPlayerManager::init_window(FXbool wizard) { + const FXint argc = application->getArgc(); + const FXchar *const * argv = application->getArgv(); + + /// Create Main Window + mainwindow = new GMWindow(application,this,ID_WINDOW); + mainwindow->create(); + + // Register Global Hotkeys + register_global_hotkeys(); + + /// Handle interrupt to save stuff nicely +#ifndef DEBUG + application->addSignal(SIGINT,mainwindow,GMWindow::ID_QUIT); + application->addSignal(SIGQUIT,mainwindow,GMWindow::ID_QUIT); + application->addSignal(SIGTERM,mainwindow,GMWindow::ID_QUIT); + application->addSignal(SIGHUP,mainwindow,GMWindow::ID_QUIT); + application->addSignal(SIGPIPE,mainwindow,GMWindow::ID_QUIT); +#endif + + /// Create Tooltip Window + FXToolTip * tooltip = new FXToolTip(application); + tooltip->create(); + ewmh_change_window_type(tooltip,WINDOWTYPE_TOOLTIP); + + GMDatabaseSource * dbsrc = dynamic_cast(sources[0]); + if (dbsrc->getNumTracks()==0 && dbsrc->getNumStreams()==0 && wizard) { + cleanSourceSettings(); + mainwindow->init(SHOW_WIZARD); + } + else { + FXbool start_as_tray=false; + for(FXint i=1;iinit(SHOW_TRAY); + else + mainwindow->init(SHOW_NORMAL); + } + application->addTimeout(this,GMPlayerManager::ID_HANDLE_EVENTS,TIME_MSEC(500)); + } + + +static FXString get_cmdline_url(int& argc,char** argv) { + FXString url; + for (FXint i=1;iinit(argc,argv); + application->create(); + + /// Keep track of child processes + application->addSignal(SIGCHLD,this,GMPlayerManager::ID_CHILD); + + /// Give warning when PNG is not compiled in... + if (FXPNGIcon::supported==false) { + FXMessageBox::warning(application,MBOX_OK,"Goggles Music Manager", + fxtr("For some reason the FOX library was compiled without PNG support.\n" + "In order to show all icons, Goggles Music Manager requires PNG\n" + "support in the FOX library. If you've compiled FOX yourself, most\n" + "likely the libpng header files were not installed on your system.")); + } + +#ifdef HAVE_DBUS + + sessionbus=new GMDBus(); + systembus=new GMDBus(); + if (!sessionbus->open(DBUS_BUS_SESSION) || !sessionbus->connected()) { + FXMessageBox::warning(application,MBOX_OK,"Goggles Music Manager",fxtr("Session bus not available. All features requiring dbus are disabled.")); + delete sessionbus; + sessionbus=NULL; + } + else { + FXint result = dbus_bus_request_name(sessionbus->connection(),GOGGLESMM_DBUS_NAME,DBUS_NAME_FLAG_DO_NOT_QUEUE,NULL); + switch(result) { + case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER: + { + if (!dbus_connection_register_object_path(sessionbus->connection(),"/org/fifthplanet/gogglesmm",&org_fifthplanet_gogglesmm,this)){ + FXMessageBox::warning(application,MBOX_OK,"Goggles Music Manager",fxtr("A DBus error occurred. All features requiring sessionbus are disabled.")); + delete sessionbus; + sessionbus=NULL; + } + } + break; + case DBUS_REQUEST_NAME_REPLY_EXISTS: + dbus_send_commands(sessionbus->connection(),argc,argv); + return 0; + break; + default: + FXMessageBox::warning(application,MBOX_OK,"Goggles Music Manager",fxtr("Session Bus not available. All features requiring sessionbus are disabled.")); + delete sessionbus; + sessionbus=NULL; + break; + } + } + + /// Fallback to fifo method to check for existing instance + if (sessionbus==NULL) { + result = init_fifo(argc,argv); + if (result==FIFO_STATUS_ERROR) + return 1; + else if (result==FIFO_STATUS_EXISTS) + return 0; + } + + + if (!systembus->open(DBUS_BUS_SYSTEM) || !systembus->connected()) { + delete systembus; + systembus=NULL; + } + + if (systembus && !dbus_connection_add_filter(systembus->connection(),dbus_systembus_filter,this,NULL)){ + delete systembus; + systembus=NULL; + } + + if (systembus) { + DBusError error; + dbus_error_init(&error); + dbus_bus_add_match(systembus->connection(),"type='signal',path='/org/freedesktop/NetworkManager',interface='org.freedesktop.NetworkManager',member='StateChanged'",&error); + if (dbus_error_is_set(&error)) { + dbus_error_free(&error); + delete systembus; + systembus=NULL; + } + } + + if (systembus) { + DBusError error; + dbus_error_init(&error); + dbus_bus_add_match(systembus->connection(),"type='signal',path='/org/freedesktop/NetworkManager',interface='org.freedesktop.NetworkManager',member='StateChange'",&error); + if (dbus_error_is_set(&error)) { + dbus_error_free(&error); + delete systembus; + systembus=NULL; + } + } + +#else + result = init_fifo(argc,argv); + if (result==FIFO_STATUS_ERROR) + return 1; + else if (result==FIFO_STATUS_EXISTS) + return 0; +#endif + + /// Open Database and initialize all sources. + if (!init_sources()) + return false; + + /// Everything opened succesfully... now create the GUI + player = new GMPlayer(argc,argv); + + /// Load Application Preferences + preferences.load(application->reg()); + + /// Check for overrides on the command line + preferences.parseCommandLine(argc,argv); + + /// Open Audio Device if needed. + if (preferences.play_open_device_on_startup) { + if (!player->initialize()) { + FXString errormsg; + player->getErrorMessage(errormsg); + FXMessageBox::error(application,MBOX_OK,fxtr("Audio Device Error"),"%s",errormsg.text()); + } + } + + /// Receive events from fifo + if (fifo.isOpen()) { +#if FOXVERSION < FXVERSION(1,7,0) + application->addInput(fifo.handle(),INPUT_READ,this,GMPlayerManager::ID_DDE_MESSAGE); +#else + application->addInput(this,GMPlayerManager::ID_DDE_MESSAGE,fifo.handle(),INPUT_READ); +#endif + } + + FXString url = get_cmdline_url(argc,argv); + + /// Show user interface + init_window(url.empty()); + +#ifdef HAVE_DBUS + if (sessionbus) { + /// Integrate Dbus into FOX Event Loop + GMDBus::initEventLoop(); + } +#endif + + lastfm = new GMAudioScrobbler(this,ID_SCROBBLER); + +#ifdef HAVE_DBUS + if (sessionbus) { + notifydaemon = new GMNotifyDaemon(sessionbus); + mpris = new GMMediaPlayerService(sessionbus); + gsd = new GMSettingsDaemon(sessionbus,this,ID_GNOME_SETTINGS_DAEMON); + gsd->GrabMediaPlayerKeys("gogglesmm"); + + notifydaemon->init(); + } +#endif + + /// Open url from command line + if (!url.empty()) + open(url); + +#ifndef HAVE_DBUS + update_tray_icon(); +#endif + + /// Run the application + return application->run(); + } + + + + + +void GMPlayerManager::removeSource(GMSource *src) { + sources.remove(src); + } + +void GMPlayerManager::exit() { + + /// Stop Playing + stop(); + + /// Close and Save Settings + player->close(); + +#ifdef HAVE_DBUS + if (sessionbus) { + DBusMessage * msg = dbus_message_new_signal(GOGGLESMM_DBUS_PATH,GOGGLESMM_DBUS_INTERFACE,"quit"); + if (msg) { + dbus_connection_send(sessionbus->connection(),msg,NULL); + dbus_message_unref(msg); + } + } +#endif + + application->removeTimeout(this,GMPlayerManager::ID_HANDLE_EVENTS); + + application->removeTimeout(this,GMPlayerManager::ID_UPDATE_TRACK_DISPLAY); + + application->removeTimeout(this,GMPlayerManager::ID_SLEEP_TIMER); + + application->removeTimeout(this,GMPlayerManager::ID_PLAY_NOTIFY); + + application->removeTimeout(source,GMSource::ID_TRACK_PLAYED); + + preferences.save(application->reg()); + + if (lastfm) lastfm->shutdown(); + +#ifdef HAVE_DBUS + if (sessionbus) { + dbus_connection_unregister_object_path(sessionbus->connection(),"/org/fifthplanet/gogglesmm"); + + gsd->ReleaseMediaPlayerKeys("gogglesmm"); + delete gsd; + gsd=NULL; + + + if (notifydaemon) delete notifydaemon; + if (mpris) delete mpris; + } + + if (systembus) { + dbus_connection_remove_filter(systembus->connection(),dbus_systembus_filter,this); + } +#endif + + application->exit(0); + } + + +void GMPlayerManager::update_tray_icon() { + if (!preferences.gui_tray_icon_disabled) { + if (trayicon && !preferences.gui_tray_icon) { + delete trayicon; + trayicon=NULL; + } + else if (!trayicon && preferences.gui_tray_icon) { + trayicon = new GMTrayIcon(application); + trayicon->create(); + if (!trayicon->dock()) { + preferences.gui_tray_icon=false; + delete trayicon; + trayicon=NULL; + } + } + } + } + + + +FXbool GMPlayerManager::init_database(GMTrackDatabase *& db, const FXString & filename){ + FXString dir = FXSystem::getHomeDirectory() + PATHSEPSTRING + ".goggles"; + FXString databasefilename = dir + PATHSEPSTRING + filename; + + if (FXStat::exists(dir)) { + + if (!FXStat::isDirectory(dir)) + return FALSE; + + } + else { + + if (!FXDir::create(dir)) + return FALSE; + } + + if (!FXStat::isWritable(dir) || !FXStat::isReadable(dir)) + return FALSE; + + if (FXStat::exists(databasefilename) && (!FXStat::isWritable(databasefilename) || !FXStat::isReadable(databasefilename))) + return FALSE; + + if (!db) { + db = new GMTrackDatabase(); + } + + /// Init Database + if (!db->init(databasefilename,true)) { + return false; + } + return TRUE; + } + +FXbool GMPlayerManager::hasSourceWithKey(const char * key) const{ + for (FXint i=0;isettingKey()==key) + return true; + } + return false; + } + + +void GMPlayerManager::cleanSourceSettings() { + FXint s; + FXStringList keys; + + for (s=application->reg().first();sreg().size();s=application->reg().next(s)){ + if (comparecase(application->reg().key(s),"database",8)==0){ + if (!hasSourceWithKey(application->reg().key(s))) { + keys.append(application->reg().key(s)); + } + } + } + + for (s=0;sreg().deleteSection(keys[s].text()); + } + } + +void GMPlayerManager::removePlayListSources(){ + GMSource * src; + for (FXint i=sources.no()-1;i>=0;i--){ + src=sources[i]; + if (src->getType()==SOURCE_DATABASE_PLAYLIST) { + FXApp::instance()->reg().deleteSection(sources[i]->settingKey().text()); + sources.erase(i); + delete src; + } + } + } + +void GMPlayerManager::download(const FXString & filename){ + FXString status = "Downloading " + filename + " ..."; + GMPlayerManager::instance()->setStatus(status); + GMFetch::download(filename); + } + +FXbool GMPlayerManager::play(const FXString & filename) { + FXString errormsg; + + + /// Open Filename + if (!player->open(filename)){ + + /// Reset Source + if (source) { + application->removeTimeout(source,GMSource::ID_TRACK_PLAYED); + source->resetCurrent(); + source=NULL; + } + + /// Reset Track Display + reset_track_display(); + + /// Show error dialog once we return to event loop + application->addChore(this,ID_PLAYER_ERROR); + + /// Close + if (preferences.play_close_stream) + player->close(); + + return false; + } + + /// Start Playback + if (!player->play()) { + + /// Reset Source + if (source) { + application->removeTimeout(source,GMSource::ID_TRACK_PLAYED); + source->resetCurrent(); + source=NULL; + } + + /// Reset Track Display + reset_track_display(); + + /// Show error dialog once we return to event loop + application->addChore(this,ID_PLAYER_ERROR); + + /// Close + if (preferences.play_close_stream) + player->close(); + + return false; + } + return true; + } + + + + +FXbool GMPlayerManager::play(const FXStringList & list) { + FXString errormsg; + + for (FXint i=0;iopen(list[i])){ + continue; + } + /// Start Playback + if (!player->play()) { + continue; + } + return true; + } + + + /// Show error dialog once we return to event loop + application->addChore(this,ID_PLAYER_ERROR); + + + /// Reset Source + if (source) { + application->removeTimeout(source,GMSource::ID_TRACK_PLAYED); + source->resetCurrent(); + source=NULL; + } + + /// Reset Track Display + reset_track_display(); + + /// Close + if (preferences.play_close_stream) + player->close(); + + return false; + } + + +void GMPlayerManager::open(const FXString & uri) { + + /// Make sure we have something + if (uri.empty()) + return; + + /// Get a uri we can process + FXString filename = gm_parse_uri(uri); + + /// Stop fetching + GMFetch::cancel_and_wait(); + + /// Reset track info + trackinfoset=false; + + /// Stop Current Playback + player->stop(); + + /// Remove any pending track display updates + application->removeTimeout(this,GMPlayerManager::ID_UPDATE_TRACK_DISPLAY); + + /// Remove Current Timeout + if (source) { + application->removeTimeout(source,GMSource::ID_TRACK_PLAYED); + source->resetCurrent(); + source=NULL; + } + + /// Check for pls or m3u, since xine cannot handle that + FXbool local = gm_is_local_file(filename); + if (!local) { + trackinfo.mrl = filename; + download(filename); + return; + } + + if (local) { + FXint id; + if (sources[0]->hasTrack(filename,id)) { + sources[0]->setCurrentTrack(id); + source=sources[0]; + getTrackView()->handle(this,FXSEL(SEL_COMMAND,GMTrackView::ID_SHOW_CURRENT),NULL); + trackinfoset = source->getTrack(trackinfo); + } + else { + /// Reset Active Track + trackinfoset = trackinfo.loadTag(filename); + getTrackView()->mark(-1); + } + } + else { + /// Reset Active Track + getTrackView()->mark(-1); + } + + /// Play File + if (play(filename) && local) { + update_track_display(); + } + + } + + +void GMPlayerManager::play() { + FXString filename; + FXint track; + + /// Stop fetching + GMFetch::cancel_and_wait(); + + /// Reset trackinfoset + trackinfoset=false; + + /// Stop Current Playback + player->stop(); + + /// Remove any pending track display updates + application->removeTimeout(this,GMPlayerManager::ID_UPDATE_TRACK_DISPLAY); + + /// Remove Current Timeout + if (source) { + application->removeTimeout(source,GMSource::ID_TRACK_PLAYED); + source->resetCurrent(); + source=NULL; + } + + track = getTrackView()->getCurrent(); + if (track==-1) return; + + /// Mark Track + source = getTrackView()->getSource(); + getTrackView()->mark(track); + + /// Get the track info + trackinfoset = source->getTrack(trackinfo); + + /// Check for pls or m3u, since xine cannot handle that + FXbool local = gm_is_local_file(trackinfo.mrl); + if (!local) { + download(trackinfo.mrl); + return; + } + + if (play(trackinfo.mrl)){ + if (local) + update_track_display(); + else + reset_track_display(); + } + + + } + + +void GMPlayerManager::stop(FXbool force_close) { + + /// Wait for download to finish. + GMFetch::cancel_and_wait(); + + /// Reset Source + if (source) { + application->removeTimeout(source,GMSource::ID_TRACK_PLAYED); + source->resetCurrent(); + source=NULL; + } + + if (preferences.play_close_stream || force_close){ + player->stop(); + player->close(); + } + else { + player->stop(); + } + + /// Reset Track Display + reset_track_display(); + } + +namespace FX { +extern FXlong fxgetticks(); +} + +//static FXint next_track; + +void GMPlayerManager::next() { + FXint track; + + GMFetch::cancel_and_wait(); + + player->stop(); + + /// Remove any pending track display updates + application->removeTimeout(this,GMPlayerManager::ID_UPDATE_TRACK_DISPLAY); + + /// Remove Current Timeout + if (source) { + application->removeTimeout(source,GMSource::ID_TRACK_PLAYED); + source->resetCurrent(); + source=NULL; + } + + track = getTrackView()->getNext(true); + if (track==-1) return; + + /// Mark Track + source = getTrackView()->getSource(); + getTrackView()->mark(track); + + /// Get the Track info + trackinfoset = source->getTrack(trackinfo); + + /// Check for pls or m3u, since xine cannot handle that + FXbool local = gm_is_local_file(trackinfo.mrl); + if (!local) { + download(trackinfo.mrl); + return; + } + + if (play(trackinfo.mrl) && gm_is_local_file(trackinfo.mrl)) + update_track_display(); + } + + +void GMPlayerManager::prev() { + FXString filename; + FXint track; + + GMFetch::cancel_and_wait(); + + /// Stop Current Playback + player->stop(); + + /// Remove any pending track display updates + application->removeTimeout(this,GMPlayerManager::ID_UPDATE_TRACK_DISPLAY); + + /// Remove Current Timeout + if (source) { + application->removeTimeout(source,GMSource::ID_TRACK_PLAYED); + source->resetCurrent(); + source=NULL; + } + + track = getTrackView()->getPrevious(); + if (track==-1) return; + + /// Mark Track + source = getTrackView()->getSource(); + getTrackView()->mark(track); + + /// Get Track Information + trackinfoset = source->getTrack(trackinfo); + + /// Check for pls or m3u, since xine cannot handle that + FXbool local = gm_is_local_file(trackinfo.mrl); + if (!local) { + download(trackinfo.mrl); + return; + } + + if (play(trackinfo.mrl) && gm_is_local_file(trackinfo.mrl)) + update_track_display(); + } + +void GMPlayerManager::seek(FXint pos) { + + /// Remove Current Timeout + application->removeTimeout(source,GMSource::ID_TRACK_PLAYED); + + player->play(pos); + /// Prevent jumping of time slider + application->addTimeout(this,GMPlayerManager::ID_HANDLE_EVENTS,TIME_MSEC(500)); + } + +void GMPlayerManager::pause() { + if (preferences.play_pause_close_device){ + player->pause(); + player->close_device(); + } + else { + player->pause(); + } + count_track_remaining = application->remainingTimeout(source,GMSource::ID_TRACK_PLAYED); + application->removeTimeout(source,GMSource::ID_TRACK_PLAYED); + } + +void GMPlayerManager::unpause() { + player->unpause(); + + if (player->getVolume()==0) + player->setVolume(50); + + if (count_track_remaining>0 && source){ + application->addTimeout(source,GMSource::ID_TRACK_PLAYED,count_track_remaining); + } + count_track_remaining=0; + + mainwindow->update_volume_display(player->getVolume()); + } + +FXbool GMPlayerManager::playlist_empty() { + if (getTrackView()->hasTracks()) return false; + if (preferences.play_repeat!=REPEAT_OFF) return false; + if (getTrackView()->getNext()!=-1) return false; + return true; + } + +void GMPlayerManager::notify_playback_finished() { + FXString errormsg; + FXString filename; + FXint track; + FXint remaining = player->remaining(); + + + /// Can we just start playback without user interaction + if (!getTrackView()->getSource()->autoPlay()) { + + /// Reset Source + if (source) { + source->resetCurrent(); + source=NULL; + } + + reset_track_display(); + return; + } + + if (source) { + source->resetCurrent(); + source=NULL; + } + + if (preferences.play_repeat==REPEAT_TRACK) + track = getTrackView()->getCurrent(); + else + track = getTrackView()->getNext(); + + if (track==-1) { + reset_track_display(); + return; + } + + getTrackView()->mark(track,(remaining<=0)); + + source = getTrackView()->getSource(); + trackinfoset = source->getTrack(trackinfo); + + if (play(trackinfo.mrl)) { + if (remaining>0) + application->addTimeout(this,GMPlayerManager::ID_UPDATE_TRACK_DISPLAY,TIME_SEC(remaining),(void*)(FXival)track); + else + update_track_display(); + } + + } + + +FXbool GMPlayerManager::playing() const { + return player->playing() || GMFetch::busy() ; + } + +FXbool GMPlayerManager::audio_device_opened() const{ + return player->opened(); + } + +FXint GMPlayerManager::current_position() const { + return player->getPosition(); + } + +void GMPlayerManager::reset_track_display() { + FXTRACE((51,"GMPlayerManager::reset_track_display()\n")); + + /// Reset Main Window + mainwindow->reset(); + + if (trayicon) trayicon->reset(); + + /// Reset Active Track + getTrackView()->mark(-1); + + /// Remove Notify + application->removeTimeout(this,ID_PLAY_NOTIFY); + + +#ifdef HAVE_DBUS + if (notifydaemon && preferences.dbus_notify_daemon) notifydaemon->close(); +#endif + + /// Schedule a GUI update + application->refresh(); + } + + +void GMPlayerManager::setStatus(const FXString & text){ + mainwindow->statusbar->getStatusLine()->setNormalText(text); + } + + + + +void GMPlayerManager::update_track_display(FXbool notify) { + FXTRACE((51,"GMPlayerManager::update_track_display()\n")); + + /// If track information is not set, we need to get the latest from the player. + if (!trackinfoset && player->playing()) { + player->getTrackInformation(trackinfo); + if (source) source->setTrack(trackinfo); + } + + if (source) { + FXint time = (FXint) (((double)trackinfo.time) * 0.80); + if (time <= 5) { + application->removeTimeout(source,GMSource::ID_TRACK_PLAYED); + source->handle(this,FXSEL(SEL_TIMEOUT,GMSource::ID_TRACK_PLAYED),NULL); + } + else { + count_track_remaining=0; + application->addTimeout(source,GMSource::ID_TRACK_PLAYED,TIME_SEC(time)); + } + } + + if (trayicon && player->playing()) { + trayicon->display(trackinfo); + } + + mainwindow->display(trackinfo); + + /// Make sure Volume Level is up 2 date + mainwindow->update_volume_display(player->getVolume()); + + update_replay_gain(); + + if (notify) application->addTimeout(this,ID_PLAY_NOTIFY,TIME_MSEC(500)); + } + +void GMPlayerManager::update_replay_gain() { + switch(preferences.play_replaygain){ + case REPLAYGAIN_OFF : player->setReplayGain(NAN,NAN); break; + case REPLAYGAIN_TRACK : player->setReplayGain(trackinfo.track_gain,trackinfo.track_peak); break; + case REPLAYGAIN_ALBUM : player->setReplayGain(trackinfo.album_gain,trackinfo.album_peak); break; + } + } + + +void GMPlayerManager::handle_async_events() { + /// Get Events from player + player->handle_async_events(); + + /// Mark Time + mainwindow->update_elapsed_time(player->getHours(),player->getMinutes(),player->getSeconds(),player->getPosition(),player->playing(),player->seekable()); + } + +FXint GMPlayerManager::volume() const{ + return player->getVolume(); + } + +void GMPlayerManager::volume(FXint l) { + player->setVolume(l); + } + +FXbool GMPlayerManager::can_stop() const { + if (player->playing() || GMFetch::busy() ) return true; + return false; + } + +FXbool GMPlayerManager::can_play() const { + if (!player->playing() && getTrackView()->hasTracks() && !GMFetch::busy()) return true; + return false; + } + +FXbool GMPlayerManager::can_pause() const { + if (player->playing() && !player->pausing()) + return true; + return false; + } + +FXbool GMPlayerManager::can_unpause() const { + if (player->playing() && player->pausing()) + return true; + return false; + } + +FXbool GMPlayerManager::can_next() const { + if (player->playing() && !player->pausing() && getTrackView()->getNumTracks()>1) + return true; + return false; + } + +FXbool GMPlayerManager::can_prev() const { + if (player->playing() && !player->pausing() && getTrackView()->getNumTracks()>1) + return true; + return false; + } + +#if FOXVERSION < FXVERSION(1,7,0) +void GMPlayerManager::setSleepTimer(FXuint ms) { + if (ms==0) + application->removeTimeout(this,GMPlayerManager::ID_SLEEP_TIMER); + else + application->addTimeout(this,GMPlayerManager::ID_SLEEP_TIMER,ms); + } +#else +void GMPlayerManager::setSleepTimer(FXlong ns) { + if (ns==0) + application->removeTimeout(this,GMPlayerManager::ID_SLEEP_TIMER); + else + application->addTimeout(this,GMPlayerManager::ID_SLEEP_TIMER,ns); + } +#endif + +FXbool GMPlayerManager::hasSleepTimer() { + return application->hasTimeout(this,GMPlayerManager::ID_SLEEP_TIMER); + } + +void GMPlayerManager::show_message(const FXchar * title,const FXchar * msg){ + if (application->getActiveWindow() && application->getActiveWindow()->shown()) { + FXMessageBox::error(application->getActiveWindow(),MBOX_OK,title,"%s",msg); + } + else { + if (mainwindow && mainwindow->shown()) + FXMessageBox::error(mainwindow,MBOX_OK,title,"%s",msg); + else if (mainwindow->getRemote()) + FXMessageBox::error(mainwindow->getRemote(),MBOX_OK,title,"%s",msg); + else + FXMessageBox::error(application,MBOX_OK,title,"%s",msg); + } + } + + + +long GMPlayerManager::onCmdShowManager(FXObject*,FXSelector,void*){ + mainwindow->hideRemote(); + return 1; + } + +long GMPlayerManager::onCmdCloseWindow(FXObject*sender,FXSelector,void*){ + FXWindow * window = reinterpret_cast(sender); + if (getPreferences().gui_hide_player_when_close && trayicon) { + window->hide(); + } + else { + getMainWindow()->handle(this,FXSEL(SEL_COMMAND,GMWindow::ID_QUIT),NULL); + } + return 1; + } + + +long GMPlayerManager::onCmdChild(FXObject*,FXSelector,void*){ + FXint pid; + FXint status; + while(1) { + pid = waitpid(-1,&status,WNOHANG); + if (pid>0) { + continue; + } + break; + } + return 1; + } + + +long GMPlayerManager::onScrobblerError(FXObject*,FXSelector,void*ptr){ + show_message(fxtr("Last.FM Error"),(const FXchar*)ptr); + return 1; + } + +long GMPlayerManager::onScrobblerOpen(FXObject*,FXSelector,void*ptr){ + gm_open_browser((const FXchar*)ptr); + return 1; + } + +long GMPlayerManager::onPlayerError(FXObject*,FXSelector,void*){ + FXString errormsg; + player->getErrorMessage(errormsg); + show_message(fxtr("Playback Error"),errormsg.text()); + return 1; + } + + + +long GMPlayerManager::onCmdDownloadComplete(FXObject*,FXSelector,void*ptr){ + GMFetchResponse * response = NULL; + if (ptr) + response = *((GMFetchResponse**)ptr); + + if (response) { + if (response->url == trackinfo.mrl ) { + FXStringList list; + if ((response->content_type.find("audio/mpegurl")!=-1 || + response->content_type.find("audio/x-mpegurl")!=-1) && response->data.length()){ + gm_parse_m3u(response->data,list); + } + else if ((response->content_type.find("application/pls+xml")!=-1 || + response->content_type.find("audio/x-scpls")!=-1) && response->data.length()) { + + gm_parse_pls(response->data,list); + } + else { + list.append(response->url); + } + play(list); + } + else { + reset_track_display(); + } + delete response; + } + else { + reset_track_display(); + } + return 1; + } + + +long GMPlayerManager::onCmdEqualizer(FXObject *,FXSelector,void*){ + GMEQDialog * eqdialog = GMEQDialog::instance(); + if (eqdialog==NULL) { + eqdialog = new GMEQDialog(mainwindow); + eqdialog->create(); + } + eqdialog->show(); + return 1; + } + + +// Perhaps should do something else... +static int xregisterhotkeys(Display* dpy,XErrorEvent* eev){ + char buf[256]; + + if(eev->error_code==BadAccess && eev->request_code==33) return 0; + + // A BadWindow due to X_SendEvent is likely due to XDND + if(eev->error_code==BadWindow && eev->request_code==25) return 0; + + // WM_TAKE_FOCUS causes sporadic errors for X_SetInputFocus + if(eev->request_code==42) return 0; + + // Get error codes + XGetErrorText(dpy,eev->error_code,buf,sizeof(buf)); + + // Print out meaningful warning + fxwarning("GMM X Error: code %d major %d minor %d: %s.\n",eev->error_code,eev->request_code,eev->minor_code,buf); + return 1; + } + + +void GMPlayerManager::register_global_hotkeys() { + Window root = application->getRootWindow()->id(); + Display * display = (Display*) application->getDisplay(); + KeyCode keycode; + + XErrorHandler previous = XSetErrorHandler(xregisterhotkeys); + + /// Only register hotkeys on the rootwindow. +#ifdef XF86XK_AudioPlay + keycode = XKeysymToKeycode(display,XF86XK_AudioPlay); + if (keycode) XGrabKey(display,keycode,AnyModifier,root,False,GrabModeAsync,GrabModeAsync); +#endif +#ifdef XF86XK_AudioPause + keycode = XKeysymToKeycode(display,XF86XK_AudioPause); + if (keycode) XGrabKey(display,keycode,AnyModifier,root,False,GrabModeAsync,GrabModeAsync); +#endif +#ifdef XF86XK_AudioStop + keycode = XKeysymToKeycode(display,XF86XK_AudioStop); + if (keycode) XGrabKey(display,keycode,AnyModifier,root,False,GrabModeAsync,GrabModeAsync); +#endif +#ifdef XF86XK_AudioNext + keycode = XKeysymToKeycode(display,XF86XK_AudioNext); + if (keycode) XGrabKey(display,keycode,AnyModifier,root,False,GrabModeAsync,GrabModeAsync); +#endif +#ifdef XF86XK_AudioPrev + keycode = XKeysymToKeycode(display,XF86XK_AudioPrev); + if (keycode) XGrabKey(display,keycode,AnyModifier,root,False,GrabModeAsync,GrabModeAsync); +#endif + XSync (display,False); + XSetErrorHandler(previous); + } + + + + +FXbool GMPlayerManager::handle_global_hotkeys(FXuint code) { + switch(code) { +#ifdef XF86XK_AudioPlay + case XF86XK_AudioPlay : cmd_playpause(); break; +#endif +#ifdef XF86XK_AudioPause + case XF86XK_AudioPause : cmd_playpause(); break; +#endif +#ifdef XF86XK_AudioStop + case XF86XK_AudioStop : cmd_stop(); break; +#endif +#ifdef XF86XK_AudioPrev + case XF86XK_AudioPrev : cmd_prev(); break; +#endif +#ifdef XF86XK_AudioNext + case XF86XK_AudioNext : cmd_next(); break; +#endif + default : return false; break; + } + return true; + } + + +void GMPlayerManager::update_cover_display() { + if (preferences.gui_show_playing_albumcover && gm_is_local_file(trackinfo.mrl)) + mainwindow->loadCover(trackinfo.mrl); + } + + +FXint GMPlayerManager::createPlaylist(const FXString & name) { + FXint playlist; + database->insertPlaylist(name,playlist); + insertSource(new GMPlayListSource(database,playlist)); + getSourceView()->refresh(); + return playlist; + } + + +GMDatabaseSource * GMPlayerManager::getDatabaseSource() const { + return dynamic_cast(sources[0]); + } + +void GMPlayerManager::display_track_notification() { +#ifdef HAVE_DBUS + if (sessionbus && !trackinfo.title.empty() && !trackinfo.artist.empty()) { + if (notifydaemon && preferences.dbus_notify_daemon) { + FXString body = GMStringFormat(fxtrformat("%s\n%s (%d)"),trackinfo.artist.text(),trackinfo.album.text(),trackinfo.year); + /// Dirty Hack. According to the spec, we shouldn't have to do this, + /// but try finding a notification daemon that actually implements it... + /// http://www.galago-project.org/specs/notification/0.9/index.html + body.substitute("&","&"); + notifydaemon->notify(trackinfo.title.text(),body.text(),-1,mainwindow->getSmallCover()); + } + if (mpris) mpris->notify_track_change(trackinfo); + } +#endif + } + +void GMPlayerManager::cmd_play(){ + if (can_unpause()) + unpause(); + else if (can_play()) + play(); + } + + +void GMPlayerManager::cmd_playpause(){ + if (can_pause()) + pause(); + else if (can_unpause()) + unpause(); + else if (can_play()) + play(); + } + + +void GMPlayerManager::cmd_pause(){ + if (can_pause()) + pause(); + else if (can_unpause()) + unpause(); + } + +void GMPlayerManager::cmd_stop(){ + if (can_stop()) + stop(); + } + +void GMPlayerManager::cmd_next(){ + if (can_next()) + next(); + } +void GMPlayerManager::cmd_prev(){ + if (can_prev()) + prev(); + } + +void GMPlayerManager::cmd_toggle_shown(){ + getMainWindow()->toggleShown(); + } diff --git a/src/GMPlayerManager.h b/src/GMPlayerManager.h new file mode 100644 index 0000000..5ed9a80 --- /dev/null +++ b/src/GMPlayerManager.h @@ -0,0 +1,272 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#ifndef GMPLAYERMANAGER_H +#define GMPLAYERMANAGER_H + +#ifndef GMPREFERENCES_H +#include "GMPreferences.h" +#endif + +#ifndef GMSOURCE_H +#include "GMSource.h" +#endif + + +class GMPlayList; +class GMTrackList; +class GMTrackView; +class GMPlayer; +class GMWindow; +class GMRemote; +class GMTrackDatabase; +class GMPlayer; +class GMPlayList; +class GMSource; +class GMSourceView; +class GMFetch; +class GMEQDialog; +#ifdef HAVE_DBUS +class GMDBus; +class GMNotifyDaemon; +class GMMediaPlayerService; +class GMSettingsDaemon; +#endif +class GMAudioScrobbler; +class GMTrayIcon; +class GMDatabaseSource; + + +class GMPlayerManager : public FXObject { +FXDECLARE(GMPlayerManager) +private: + static GMPlayerManager * myself; +protected: + GMPreferences preferences; + GMSourceList sources; + FXString fifofilename; + FXFile fifo; +#if FOXVERSION < FXVERSION(1,7,0) + FXuint count_track_remaining; +#else + FXlong count_track_remaining; +#endif +protected: +#ifdef HAVE_DBUS + GMDBus * sessionbus; + GMDBus * systembus; + GMNotifyDaemon * notifydaemon; + GMMediaPlayerService * mpris; + GMSettingsDaemon * gsd; +#endif + FXApp * application; + GMWindow * mainwindow; + GMPlayer * player; + GMTrayIcon * trayicon; + GMAudioScrobbler * lastfm; + GMTrackDatabase * database; +protected: + GMSource * source; + GMTrack trackinfo; + FXbool trackinfoset; +protected: + FXbool hasSourceWithKey(const char * key) const; + void cleanSourceSettings(); +public: + static GMPlayerManager * instance(); +public: + enum { + ID_UPDATE_TRACK_DISPLAY = 1, + ID_HANDLE_EVENTS, + ID_COUNT_TRACK, + ID_SLEEP_TIMER, + ID_SCAN_AUDIOCD, + ID_DDE_MESSAGE, + ID_DOWNLOAD_COMPLETE, + ID_PLAY_NOTIFY, + ID_REMOTE, + ID_WINDOW, + ID_EQUALIZER, + ID_SCROBBLER, + ID_PLAYER_ERROR, + ID_GNOME_SETTINGS_DAEMON, + ID_SHOW_MANAGER, + ID_CHILD + }; +public: + long onUpdTrackDisplay(FXObject*,FXSelector,void*); + long onUpdEvents(FXObject*,FXSelector,void*); + long onCmdCountTrack(FXObject*,FXSelector,void*); + long onCmdSleepTimer(FXObject*,FXSelector,void*); + long onDDEMessage(FXObject*,FXSelector,void*); + long onCmdDownloadComplete(FXObject*,FXSelector,void*); + long onCmdCloseWindow(FXObject*,FXSelector,void*); + long onCmdShowManager(FXObject*,FXSelector,void*); + long onPlayNotify(FXObject*,FXSelector,void*); + long onCmdChild(FXObject*,FXSelector,void*); + long onCmdEqualizer(FXObject*,FXSelector,void*); + long onScrobblerError(FXObject*,FXSelector,void*); + long onScrobblerOpen(FXObject*,FXSelector,void*); + + long onPlayerError(FXObject*,FXSelector,void*); +#ifdef HAVE_DBUS + long onCmdSettingsDaemon(FXObject*,FXSelector,void*); +#endif +protected: + FXint init_fifo(int & argc,char**argv); + FXbool init_database(GMTrackDatabase *&,const FXString & filename); + FXbool init_sources(); + void init_window(FXbool wizard); +public: + GMPlayerManager(); + + void getTrackInformation(GMTrack & t) const { t=trackinfo; } + + + FXint run(int & argc,char**argv); + + /// Change Source + void setSource(FXuint source); + + + void update_tray_icon(); + + + FXint getNumSources() const { return sources.no(); } + + GMSource * getSource(FXint i) const { return sources[i]; } + + void removeSource(GMSource * src); + + void insertSource(GMSource * src) { sources.append(src); } + + void removePlayListSources(); + + + + GMSource * getSource() const { return source; } + + GMDatabaseSource * getDatabaseSource() const; + + GMWindow * getMainWindow() const { return mainwindow; } + + GMTrackView * getTrackView() const; + + GMSourceView * getSourceView() const; + + GMPlayer * getPlayer() const { return player; } + + GMPreferences & getPreferences() { return preferences; } + + GMAudioScrobbler * getAudioScrobbler() { return lastfm; } + + GMTrayIcon * getTrayIcon() { return trayicon; } + +#ifdef HAVE_DBUS + FXbool hasSessionBus() const { return (sessionbus!=NULL) ; } +#endif + + void exit(); + + + + + void cmd_play(); + void cmd_playpause(); + void cmd_pause(); + void cmd_stop(); + void cmd_next(); + void cmd_prev(); + void cmd_toggle_shown(); + + FXbool can_play() const; + FXbool can_pause() const; + FXbool can_unpause() const; + FXbool can_next() const; + FXbool can_prev() const; + FXbool can_stop() const; + + void play(); + FXbool play(const FXString & mrl); + FXbool play(const FXStringList & mrl); + + void download(const FXString & mrl); + + void open(const FXString & mrl); + + void pause(); + void unpause(); + void stop(FXbool closedevice=false); + void next(); + void prev(); + + void volume(FXint l); + FXint volume() const; + + + void seek(FXint pos); + + FXbool playlist_empty(); + + void notify_playback_finished(); + + void reset_track_display(); + + void update_track_display(FXbool notify=true); + + void update_replay_gain(); + + FXint current_position() const; + + FXbool playing() const; + + FXbool audio_device_opened() const; + + FXint get_prev() const; + + FXint get_next() const; + + /// Set Sleep Timer - 0 turns the timer off +#if FOXVERSION < FXVERSION(1,7,0) + void setSleepTimer(FXuint ms); +#else + void setSleepTimer(FXlong ns); +#endif + + FXbool hasSleepTimer(); + + void handle_async_events(); + + void show_message(const FXchar * title,const FXchar * msg); + + void setStatus(const FXString & msg); + + void register_global_hotkeys(); + + FXbool handle_global_hotkeys(FXuint code); + + void update_cover_display(); + + FXint createPlaylist(const FXString & name); + + void display_track_notification(); + + ~GMPlayerManager(); + }; + +#endif diff --git a/src/GMPreferences.cpp b/src/GMPreferences.cpp new file mode 100644 index 0000000..3a95e65 --- /dev/null +++ b/src/GMPreferences.cpp @@ -0,0 +1,359 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#include "gmdefs.h" +#include "GMFilename.h" + +const char section_window[] = "window"; +const char section_import[] = "import"; +const char section_export[] = "export"; +const char section_player[] = "player"; +const char section_colors[] = "colors"; +const char section_dbus[] = "dbus"; +const char section_app[] = "application"; +const char section_sync[] = "sync"; + +const char key_import_default_field[]="default-user-title"; +const char key_import_track_from_filelist[]="track-from-filelist"; +const char key_import_replace_underscores[]="replace-underscores"; +const char key_import_parse_filename_only[]="parse-filename-only"; +const char key_import_filename_template[]="filename-template"; +const char key_import_parse_method[]="parse-method"; +const char key_import_exclude_folder[]="exclude-folder"; +const char key_import_exclude_file[]="exclude-file"; + +const char key_export_format_template[]="format-template"; +const char key_export_character_filter[]="character-filter"; +const char key_export_encoding[]="encoding"; +const char key_export_lowercase[]="lowercase"; +const char key_export_lowercase_extension[]="lowercase-extension"; +const char key_export_underscore[]="underscore"; + +const char key_gui_format_title[]="title-format"; +const char key_gui_show_status_bar[]="show-statusbar"; +const char key_gui_hide_player_when_close[]="hide-player-when-close"; +const char key_gui_toolbar_bigicons[]="toolbar-bigicons"; +const char key_gui_toolbar_docktop[]="toolbar-docktop"; +const char key_gui_toolbar_showlabels[]="toolbar-labels"; +const char key_gui_show_browser_icons[]="browser-icons"; +const char key_gui_keywords[]="sort-keywords"; +const char key_gui_show_playing_albumcover[]="show-playing-albumcover"; +const char key_gui_show_albumcovers[]="show-albumcovers"; +const char key_gui_tray_icon[]="tray-icon"; +const char key_gui_show_playing_titlebar[]="show-playing-titlebar"; +const char key_gui_coverdisplay_size[]="cover-display-size"; +const char key_gui_show_opengl_coverview[]="show-opengl-coverview"; + + +const char key_gui_row_color[]="row-color"; +const char key_gui_play_color[]="play-color"; +const char key_gui_playtext_color[]="playtext-color"; +const char key_gui_tray_color[]="tray-back-color"; + +const char key_play_repeat[]="repeat-mode"; +const char key_play_replaygain[]="replay-gain"; +const char key_play_close_stream[]="close-audio-stream"; +const char key_play_pause_close_device[]="pause-close-device"; +const char key_play_gapless[]="gapless-playback"; +const char key_play_shuffle[]="shuffle"; +const char key_play_open_device_on_startup[]="open_audio_device_on_startup"; + +const char key_dbus_notify_daemon[]="notification-daemon"; + +const char key_sync_import_new[]="import-new"; +const char key_sync_remove_missing[]="remove-missing"; +const char key_sync_remove_all[]="remove-all"; +const char key_sync_update[]="update"; +const char key_sync_update_always[]="update-always"; + + + +GMImportOptions::GMImportOptions() : + default_field("Untitled"), + filename_template("%P/%A/%N %T"), + track_from_filelist(false), + replace_underscores(true), + parse_method(PARSE_BOTH){ + } + +void GMImportOptions::save(FXSettings & reg) const { + reg.writeBoolEntry(section_import,key_import_track_from_filelist,track_from_filelist); + reg.writeBoolEntry(section_import,key_import_replace_underscores,replace_underscores); + reg.writeStringEntry(section_import,key_import_default_field,default_field.text()); + reg.writeStringEntry(section_import,key_import_filename_template,filename_template.text()); + reg.writeStringEntry(section_import,key_import_exclude_folder,exclude_folder.text()); + reg.writeStringEntry(section_import,key_import_exclude_file,exclude_file.text()); +#if FOXVERSION < FXVERSION(1,7,0) + reg.writeUnsignedEntry(section_import,key_import_parse_method,parse_method); +#else + reg.writeUIntEntry(section_import,key_import_parse_method,parse_method); +#endif + } + +void GMImportOptions::load(FXSettings & reg) { + track_from_filelist = reg.readBoolEntry(section_import,key_import_track_from_filelist,track_from_filelist); + replace_underscores = reg.readBoolEntry(section_import,key_import_replace_underscores,replace_underscores); + default_field = reg.readStringEntry(section_import,key_import_default_field,default_field.text()); + filename_template = reg.readStringEntry(section_import,key_import_filename_template,filename_template.text()); + exclude_folder = reg.readStringEntry(section_import,key_import_exclude_folder,exclude_folder.text()); + exclude_file = reg.readStringEntry(section_import,key_import_exclude_file,exclude_file.text()); +#if FOXVERSION < FXVERSION(1,7,0) + parse_method = FXMIN(reg.readUnsignedEntry(section_import,key_import_parse_method,parse_method),(FXuint)PARSE_BOTH); +#else + parse_method = FXMIN(reg.readUIntEntry(section_import,key_import_parse_method,parse_method),(FXuint)PARSE_BOTH); +#endif + + } + + +GMSyncOptions::GMSyncOptions() : + import_new(true), + remove_missing(true), + remove_all(false), + update(false), + update_always(false) { + } + +void GMSyncOptions::save(FXSettings & reg) const { + reg.writeBoolEntry(section_sync,key_sync_import_new,import_new); + reg.writeBoolEntry(section_sync,key_sync_remove_missing,remove_missing); + reg.writeBoolEntry(section_sync,key_sync_remove_all,remove_all); + reg.writeBoolEntry(section_sync,key_sync_update,update); + reg.writeBoolEntry(section_sync,key_sync_update_always,update_always); + } + +void GMSyncOptions::load(FXSettings & reg) { + import_new = reg.readBoolEntry(section_sync,key_sync_import_new,import_new); + remove_missing = reg.readBoolEntry(section_sync,key_sync_remove_missing,remove_missing); + remove_all = reg.readBoolEntry(section_sync,key_sync_remove_all,remove_all); + update = reg.readBoolEntry(section_sync,key_sync_update,update); + update_always = reg.readBoolEntry(section_sync,key_sync_update_always,update_always); + } + + +GMPreferences::GMPreferences() : + export_format_template("%N %T"), + export_character_filter("\'\\#~!\"$&();<>|`^*?[]/.:"), + gui_format_title("%N - %T - %P"), + + gui_show_status_bar(true), + gui_hide_player_when_close(false), + gui_toolbar_bigicons(true), + gui_toolbar_docktop(true), + gui_toolbar_showlabels(true), + gui_show_browser_icons(true), + gui_show_playing_albumcover(false), + gui_show_albumcovers(false), + gui_tray_icon(false), + gui_tray_icon_disabled(false), + gui_show_playing_titlebar(false), + gui_show_opengl_coverview(true), + gui_coverdisplay_size(256), + + play_replaygain(REPLAYGAIN_OFF), + play_repeat(REPEAT_ALL), + play_close_stream(false), + play_pause_close_device(false), + play_gapless(true), + play_shuffle(false), + play_open_device_on_startup(false), + + export_encoding(GMFilename::ENCODING_ASCII), + export_lowercase(false), + export_lowercase_extension(true), + export_underscore(false), + + dbus_notify_daemon(false) { + resetColors(); + } + +void GMPreferences::save(FXSettings & reg) const { + FXString keywords; + getKeyWords(keywords); + + /// Write out version information + reg.writeIntEntry(section_app,"major-version",APPLICATION_MAJOR); + reg.writeIntEntry(section_app,"minor-version",APPLICATION_MINOR); + reg.writeIntEntry(section_app,"level-version",APPLICATION_LEVEL); + + import.save(reg); + sync.save(reg); + + /// Export + reg.writeBoolEntry(section_export,key_export_lowercase,export_lowercase); + reg.writeBoolEntry(section_export,key_export_lowercase_extension,export_lowercase_extension); + reg.writeBoolEntry(section_export,key_export_underscore,export_underscore); + reg.writeStringEntry(section_export,key_export_format_template,export_format_template.text()); + reg.writeStringEntry(section_export,key_export_character_filter,export_character_filter.text()); + +#if FOXVERSION < FXVERSION(1,7,0) + reg.writeUnsignedEntry(section_export,key_export_encoding,export_encoding); +#else + reg.writeUIntEntry(section_export,key_export_encoding,export_encoding); +#endif + + /// Colors + reg.writeColorEntry(section_colors,key_gui_row_color,gui_row_color); + reg.writeColorEntry(section_colors,key_gui_play_color,gui_play_color); + reg.writeColorEntry(section_colors,key_gui_playtext_color,gui_playtext_color); + reg.writeColorEntry(section_colors,key_gui_tray_color,gui_tray_color); + + /// Window + reg.writeBoolEntry(section_window,key_gui_hide_player_when_close,gui_hide_player_when_close); + reg.writeBoolEntry(section_window,key_gui_show_status_bar,gui_show_status_bar); + reg.writeBoolEntry(section_window,key_gui_toolbar_bigicons,gui_toolbar_bigicons); + reg.writeBoolEntry(section_window,key_gui_toolbar_docktop,gui_toolbar_docktop); + reg.writeBoolEntry(section_window,key_gui_toolbar_showlabels,gui_toolbar_showlabels); + reg.writeBoolEntry(section_window,key_gui_show_browser_icons,gui_show_browser_icons); + reg.writeStringEntry(section_window,key_gui_keywords,keywords.text()); + reg.writeBoolEntry(section_window,key_gui_show_playing_albumcover,gui_show_playing_albumcover); + reg.writeBoolEntry(section_window,key_gui_show_albumcovers,gui_show_albumcovers); + reg.writeBoolEntry(section_window,key_gui_tray_icon,gui_tray_icon); + reg.writeBoolEntry(section_window,key_gui_show_playing_titlebar,gui_show_playing_titlebar); + reg.writeBoolEntry(section_window,key_gui_show_opengl_coverview,gui_show_opengl_coverview); + reg.writeIntEntry(section_window,key_gui_coverdisplay_size,gui_coverdisplay_size); + reg.writeStringEntry(section_window,key_gui_format_title,gui_format_title.text()); + + + /// Player + reg.writeIntEntry(section_player,key_play_repeat,play_repeat); + reg.writeIntEntry(section_player,key_play_replaygain,play_replaygain); + reg.writeBoolEntry(section_player,key_play_close_stream,play_close_stream); + reg.writeBoolEntry(section_player,key_play_pause_close_device,play_pause_close_device); + reg.writeBoolEntry(section_player,key_play_gapless,play_gapless); + reg.writeBoolEntry(section_player,key_play_shuffle,play_shuffle); + reg.writeBoolEntry(section_player,key_play_open_device_on_startup,play_open_device_on_startup); + + /// Dbus + reg.writeBoolEntry(section_dbus,key_dbus_notify_daemon,dbus_notify_daemon); + } + + +void GMPreferences::load(FXSettings & reg) { + FXString keywords="a;an;the"; + + /// Remove Keys that interfere with new ones.. + if (reg.readIntEntry(section_app,"major-version",0)==0 && reg.readIntEntry(section_app,"minor-version",8)<9) { + FXint s; + FXStringDict *dict; + for (s = reg.first(); s < reg.size(); s = reg.next(s)){ + dict = reg.data(s); + dict->remove("browse-showcolumn-no"); + dict->remove("list-showcolumn-no"); + } + } + + /// Write out version information + reg.writeIntEntry(section_app,"major-version",APPLICATION_MAJOR); + reg.writeIntEntry(section_app,"minor-version",APPLICATION_MINOR); + reg.writeIntEntry(section_app,"level-version",APPLICATION_LEVEL); + + + import.load(reg); + sync.load(reg); + + /// Export + export_lowercase = reg.readBoolEntry(section_export,key_export_lowercase,export_lowercase); + export_lowercase_extension = reg.readBoolEntry(section_export,key_export_lowercase_extension,export_lowercase_extension); + export_underscore = reg.readBoolEntry(section_export,key_export_underscore,export_underscore); + export_format_template = reg.readStringEntry(section_export,key_export_format_template,export_format_template.text()); + export_character_filter = reg.readStringEntry(section_export,key_export_character_filter,export_character_filter.text()); + +#if FOXVERSION < FXVERSION(1,7,0) + export_encoding = FXMIN(GMFilename::ENCODING_LAST-1,reg.readUnsignedEntry(section_export,key_export_encoding,export_encoding)); +#else + export_encoding = FXMIN(GMFilename::ENCODING_LAST-1,reg.readUIntEntry(section_export,key_export_encoding,export_encoding)); +#endif + + /// Colors + gui_row_color = reg.readColorEntry(section_colors,key_gui_row_color,gui_row_color); + gui_play_color = reg.readColorEntry(section_colors,key_gui_play_color,gui_play_color); + gui_playtext_color = reg.readColorEntry(section_colors,key_gui_playtext_color,gui_playtext_color); + gui_tray_color = reg.readColorEntry(section_colors,key_gui_tray_color,FXApp::instance()->getBaseColor()); + + /// Window + gui_hide_player_when_close = reg.readBoolEntry(section_window,key_gui_hide_player_when_close,gui_hide_player_when_close); + gui_show_status_bar = reg.readBoolEntry(section_window,key_gui_show_status_bar,gui_show_status_bar); + gui_toolbar_bigicons = reg.readBoolEntry(section_window,key_gui_toolbar_bigicons,gui_toolbar_bigicons); + gui_toolbar_docktop = reg.readBoolEntry(section_window,key_gui_toolbar_docktop,gui_toolbar_docktop); + gui_toolbar_showlabels = reg.readBoolEntry(section_window,key_gui_toolbar_showlabels,gui_toolbar_showlabels); + gui_show_browser_icons = reg.readBoolEntry(section_window,key_gui_show_browser_icons,gui_show_browser_icons); + keywords = reg.readStringEntry(section_window,key_gui_keywords,keywords.text()); + gui_show_playing_albumcover = reg.readBoolEntry(section_window,key_gui_show_playing_albumcover,gui_show_playing_albumcover); + gui_show_albumcovers = reg.readBoolEntry(section_window,key_gui_show_albumcovers,gui_show_albumcovers); + gui_tray_icon = reg.readBoolEntry(section_window,key_gui_tray_icon,gui_tray_icon); + gui_show_playing_titlebar = reg.readBoolEntry(section_window,key_gui_show_playing_titlebar,gui_show_playing_titlebar); + gui_show_opengl_coverview = reg.readBoolEntry(section_window,key_gui_show_opengl_coverview,gui_show_opengl_coverview); + gui_coverdisplay_size = reg.readIntEntry(section_window,key_gui_coverdisplay_size,gui_coverdisplay_size); + gui_format_title = reg.readStringEntry(section_window,key_gui_format_title,gui_format_title.text()); + + + /// Player + play_repeat = reg.readIntEntry(section_player,key_play_repeat,play_repeat); + play_replaygain = reg.readIntEntry(section_player,key_play_replaygain,play_replaygain); + play_close_stream = reg.readBoolEntry(section_player,key_play_close_stream,play_close_stream); + play_pause_close_device = reg.readBoolEntry(section_player,key_play_pause_close_device,play_pause_close_device); + play_gapless = reg.readBoolEntry(section_player,key_play_gapless,play_gapless); + play_shuffle = reg.readBoolEntry(section_player,key_play_shuffle,play_shuffle); + play_open_device_on_startup = reg.readBoolEntry(section_player,key_play_open_device_on_startup,play_open_device_on_startup); + + /// Dbus + dbus_notify_daemon = reg.readBoolEntry(section_dbus,key_dbus_notify_daemon,dbus_notify_daemon); + setKeyWords(keywords); + } + + + +void GMPreferences::resetColors(){ + gui_row_color=FXRGB(240,240,240); + gui_play_color=FXRGB(210,230,210); + gui_playtext_color=FXRGB(0,0,0); + gui_tray_color=FXRGB(0,0,0); + } + +void GMPreferences::setKeyWords(const FXString & keywords) { + gui_sort_keywords.clear(); + FXint numkeywords=keywords.contains(';')+1; + FXString key; + for (FXint i=0;i +#include + +#include "GMPlayer.h" +#include "GMWindow.h" +#include "GMTrackList.h" +#include "GMList.h" +#include "GMRemote.h" +#include "GMSource.h" +#include "GMPlayerManager.h" +#include "GMDatabaseSource.h" +#include "GMTrackView.h" +#include "GMSourceView.h" +#include "GMAudioScrobbler.h" +#include "GMIconTheme.h" +#include "GMPreferencesDialog.h" +#include "GMApp.h" +#include "GMTrayIcon.h" +#include "GMImageView.h" +#include "GMFontDialog.h" + + +ColorTheme::ColorTheme() { + base = FXApp::instance()->getBaseColor(); + border = FXApp::instance()->getBorderColor(); + back = FXApp::instance()->getBackColor(); + fore = FXApp::instance()->getForeColor(); + selfore = FXApp::instance()->getSelforeColor(); + selback = FXApp::instance()->getSelbackColor(); + tipfore = FXApp::instance()->getTipforeColor(); + tipback = FXApp::instance()->getTipbackColor(); + menufore = FXApp::instance()->getSelMenuTextColor(); + menuback = FXApp::instance()->getSelMenuBackColor(); + shadow = FXApp::instance()->getShadowColor(); + hilite = FXApp::instance()->getHiliteColor(); + playfore = GMPlayerManager::instance()->getPreferences().gui_playtext_color; + playback = GMPlayerManager::instance()->getPreferences().gui_play_color; + altback = GMPlayerManager::instance()->getPreferences().gui_row_color; + trayback = GMPlayerManager::instance()->getPreferences().gui_tray_color; + } + +ColorTheme::ColorTheme(const FXchar * _name,FXColor _base,FXColor _border,FXColor _back,FXColor _altback,FXColor _fore,FXColor _selback,FXColor _selfore,FXColor _tipback,FXColor _tipfore,FXColor _psback,FXColor _psfore) : + name(_name), + base(_base), + border(_border), + back(_back), + altback(_altback), + fore(_fore), + selback(_selback), + selfore(_selfore), + tipback(_tipback), + tipfore(_tipfore), + menuback(_selback), + menufore(_selfore), + playfore(_psfore), + playback(_psback), + hilite(makeHiliteColor(base)), + shadow(makeShadowColor(base)), + trayback(_base) { + } + + +void ColorTheme::save() const { + FXApp::instance()->setBaseColor(base); + FXApp::instance()->setBorderColor(border); + FXApp::instance()->setBackColor(back); + FXApp::instance()->setForeColor(fore); + FXApp::instance()->setSelbackColor(selback); + FXApp::instance()->setSelforeColor(selfore); + FXApp::instance()->setTipbackColor(tipback); + FXApp::instance()->setTipforeColor(tipfore); + FXApp::instance()->setSelMenuBackColor(menuback); + FXApp::instance()->setSelMenuTextColor(menufore); + FXApp::instance()->setShadowColor(shadow); + FXApp::instance()->setHiliteColor(hilite); + GMPlayerManager::instance()->getPreferences().gui_playtext_color=playfore; + GMPlayerManager::instance()->getPreferences().gui_play_color=playback; + GMPlayerManager::instance()->getPreferences().gui_row_color=altback; + GMPlayerManager::instance()->getPreferences().gui_tray_color=trayback; + } + +bool operator==(const ColorTheme& t1,const ColorTheme& t2) { + return (t1.base==t2.base && + t1.border==t2.border && + t1.back==t2.back && + t1.altback==t2.altback && + t1.fore==t2.fore && + t1.selback==t2.selback && + t1.selfore==t2.selfore && + t1.tipback==t2.tipback && + t1.tipfore==t2.tipfore && + t1.menuback==t2.menuback && + t1.menufore==t2.menufore && + t1.playfore==t2.playfore && + t1.playback==t2.playback && + t1.hilite==t2.hilite && + t1.shadow==t2.shadow //&& +// t1.trayback==t2.trayback + ); + } + + +/// Think you have a great color theme, mail them to s.jansen@gmail.com +const ColorTheme ColorThemes[]={ + ColorTheme("FOX", // name + FXRGB(212,208,200), // base + FXRGB( 0, 0, 0), // boder + FXRGB(255,255,255), // back + FXRGB(240,240,240), // alt back + FXRGB( 0, 0, 0), // fore + FXRGB( 10, 36,106), // selback + FXRGB(255,255,255), // selfore + FXRGB(255,255,225), // tipback + FXRGB(0,0,0)), // tipfore + + ColorTheme("Clearlooks" , // name + FXRGB(237,236,235), // base + FXRGB( 0, 0, 0), // boder + FXRGB(255,255,255), // back + FXRGB(240,240,240), // alt back + FXRGB( 26, 26, 25), // fore + FXRGB(134,171,217), // selback + FXRGB(255,255,255), // selfore + FXRGB(245,245,181), // tipback + FXRGB( 0, 0, 0)), // tipfore + + ColorTheme("Honeycomb" , // name + FXRGB(213,215,209), // base + FXRGB( 0, 0, 0), // boder + FXRGB(255,255,255), // back + FXRGB(238,238,238), // alt back + FXRGB( 0, 0, 0), // fore + FXRGB(227,167, 0), // selback + FXRGB(255,255,255), // selfore + FXRGB(255,242,153), // tipback + FXRGB( 64, 48, 0)), // tipfore + + ColorTheme("Norway" , // name + FXRGB(235,226,210), // base + FXRGB( 0, 0, 0), // boder + FXRGB(253,252,251), // back + FXRGB(238,238,238), // alt back + FXRGB( 0, 0, 0), // fore + FXRGB( 29,135,205), // selback + FXRGB(255,255,255), // selfore + FXRGB(253,252,251), // tipback + FXRGB( 0, 0, 0)), // tipfore + + ColorTheme("Oxygen" , // name + FXRGB(224,223,223), // base + FXRGB( 0, 0, 0), // boder + FXRGB(255,255,255), // back + FXRGB(238,238,238), // alt back + FXRGB( 20, 19, 18), // fore + FXRGB( 65,141,212), // selback + FXRGB(255,255,255), // selfore + FXRGB(192,218,255), // tipback + FXRGB( 20, 19, 18)), // tipfore + + ColorTheme("Obsidian Coast" , // name + FXRGB( 48, 47, 47), // base + FXRGB( 0, 0, 0), // boder + FXRGB( 32, 31, 31), // back + FXRGB( 38, 38, 38), // alt back + FXRGB(224,223,220), // fore + FXRGB( 24,72,128), // selback + FXRGB(255,255,255), // selfore + FXRGB( 16, 48, 80), // tipback + FXRGB(196,209,224),// tipfore + FXRGB(24,128,73), // psback + FXRGB(224,223,220)), // psfore + + ColorTheme("Steel" , // name + FXRGB(224,223,216), // base + FXRGB( 0, 0, 0), // boder + FXRGB(255,255,255), // back + FXRGB(238,238,238), // alt back + FXRGB( 0, 0, 0), // fore + FXRGB(123,161,173), // selback + FXRGB(255,255,255), // selfore + FXRGB(220,231,235), // tipback + FXRGB( 37, 34, 28)), // tipfore + + ColorTheme("Wonton Soup" , // name + FXRGB (71, 76, 86), // base + FXRGB( 0, 0, 0), // boder + FXRGB( 58, 62, 70), // back + FXRGB( 62, 66, 75), // alt back + FXRGB(182,193,208), // fore + FXRGB(117,133,153), // selback + FXRGB(209,225,244), // selfore + FXRGB(182,193,208), // tipback + FXRGB(42,44,48), // tipfore + FXRGB(134,147,134), // psback + FXRGB(209,225,244)),// psfore + }; + + + +FXDEFMAP(GMPreferencesDialog) GMPreferencesDialogMap[]={ + FXMAPFUNC(SEL_COMMAND,GMPreferencesDialog::ID_LASTFM_SCROBBLE,GMPreferencesDialog::onCmdLastFMScrobble), + FXMAPFUNC(SEL_COMMAND,GMPreferencesDialog::ID_LASTFM_SERVICE,GMPreferencesDialog::onCmdLastFMService), + FXMAPFUNC(SEL_COMMAND,GMPreferencesDialog::ID_LASTFM_USERNAME,GMPreferencesDialog::onCmdLastFMUserName), + FXMAPFUNC(SEL_CHANGED,GMPreferencesDialog::ID_LASTFM_USERNAME,GMPreferencesDialog::onCmdLastFMUserName), + FXMAPFUNC(SEL_FOCUSIN,GMPreferencesDialog::ID_LASTFM_PASSWORD,GMPreferencesDialog::onFocusLastFMPassWord), + FXMAPFUNC(SEL_COMMAND,GMPreferencesDialog::ID_LASTFM_PASSWORD,GMPreferencesDialog::onCmdLastFMPassWord), + FXMAPFUNC(SEL_COMMAND,GMPreferencesDialog::ID_LASTFM_JOIN,GMPreferencesDialog::onCmdLastFMJoin), + + FXMAPFUNCS(SEL_COMMAND,GMPreferencesDialog::ID_BASE_COLOR,GMPreferencesDialog::ID_BORDER_COLOR,GMPreferencesDialog::onCmdElementColor), + FXMAPFUNCS(SEL_UPDATE,GMPreferencesDialog::ID_BASE_COLOR,GMPreferencesDialog::ID_BORDER_COLOR,GMPreferencesDialog::onUpdElementColor), + FXMAPFUNC(SEL_COMMAND,GMPreferencesDialog::ID_COLOR_THEME,GMPreferencesDialog::onCmdColorTheme), + FXMAPFUNC(SEL_UPDATE,GMPreferencesDialog::ID_COLOR_THEME,GMPreferencesDialog::onUpdColorTheme), + FXMAPFUNC(SEL_UPDATE,GMPreferencesDialog::ID_FONT,GMPreferencesDialog::onUpdFont), + FXMAPFUNC(SEL_COMMAND,GMPreferencesDialog::ID_CHANGE_FONT,GMPreferencesDialog::onCmdChangeFont), + FXMAPFUNC(SEL_COMMAND,FXDialogBox::ID_ACCEPT,GMPreferencesDialog::onCmdAccept), + FXMAPFUNC(SEL_COMMAND,GMPreferencesDialog::ID_AUDIO_DRIVER,GMPreferencesDialog::onCmdAudioDriver), + FXMAPFUNC(SEL_COMMAND,GMPreferencesDialog::ID_REPLAY_GAIN,GMPreferencesDialog::onCmdReplayGain), + FXMAPFUNC(SEL_COMMAND,GMPreferencesDialog::ID_ICON_THEME,GMPreferencesDialog::onCmdIconTheme) + }; + +FXIMPLEMENT(GMPreferencesDialog,FXDialogBox,GMPreferencesDialogMap,ARRAYNUMBER(GMPreferencesDialogMap)) + +GMPreferencesDialog::GMPreferencesDialog(FXWindow * p) : FXDialogBox(p,FXString::null,DECOR_BORDER|DECOR_TITLE,0,0,0,0,0,0,0,0,0,0), + password_set(false) { + + setTitle(tr("Preferences")); + + target_closeishide.connect(GMPlayerManager::instance()->getPreferences().gui_hide_player_when_close); + target_keywords.connect(keywords); + + target_close_audio.connect(GMPlayerManager::instance()->getPreferences().play_close_stream); + target_pause_close_device.connect(GMPlayerManager::instance()->getPreferences().play_pause_close_device); + target_gapless.connect(GMPlayerManager::instance()->getPreferences().play_gapless); + target_open_device_on_startup.connect(GMPlayerManager::instance()->getPreferences().play_open_device_on_startup); + target_show_playing_albumcover.connect(GMPlayerManager::instance()->getPreferences().gui_show_playing_albumcover); + target_show_albumcovers.connect(GMPlayerManager::instance()->getPreferences().gui_show_albumcovers); +#ifdef HAVE_DBUS + target_dbus_notify_daemon.connect(GMPlayerManager::instance()->getPreferences().dbus_notify_daemon); +#endif + target_gui_tray_icon.connect(GMPlayerManager::instance()->getPreferences().gui_tray_icon); + target_replaygain.connect(GMPlayerManager::instance()->getPreferences().play_replaygain,this,ID_REPLAY_GAIN); + target_gui_format_title.connect(GMPlayerManager::instance()->getPreferences().gui_format_title); + target_gui_show_playing_titlebar.connect(GMPlayerManager::instance()->getPreferences().gui_show_playing_titlebar); + + GMPlayerManager::instance()->getPlayer()->checkInitialized(); + GMPlayerManager::instance()->getPreferences().getKeyWords(keywords); + + const FXuint labelstyle=LAYOUT_CENTER_Y|LABEL_NORMAL|LAYOUT_RIGHT; + const FXuint textfieldstyle=TEXTFIELD_ENTER_ONLY|LAYOUT_FILL_X|FRAME_SUNKEN|FRAME_THICK|LAYOUT_FILL_COLUMN; + + FXGroupBox * grpbox; + FXMatrix * matrix; + FXVerticalFrame * vframe; + FXVerticalFrame * vframe2; + + + FXVerticalFrame * main=new FXVerticalFrame(this,LAYOUT_FILL_X|LAYOUT_FILL_Y); + GMTabBook * tabbook=new GMTabBook(main,NULL,0,LAYOUT_FILL_X|LAYOUT_FILL_Y|LAYOUT_RIGHT,0,0,0,0,0,0,0,0); + + new GMTabItem(tabbook,tr("&General"),NULL,TAB_TOP_NORMAL,0,0,0,0,5,5); + vframe = new GMTabFrame(tabbook); + + grpbox = new FXGroupBox(vframe,tr("Sort Options"),FRAME_NONE|LAYOUT_FILL_X,0,0,0,0,20); + grpbox->setFont(GMApp::instance()->getThickFont()); + + matrix = new FXMatrix(grpbox,2,MATRIX_BY_COLUMNS|LAYOUT_FILL_X,0,0,0,0,0,0,0,0); + new FXLabel(matrix,tr("Ignore leading words"),NULL,labelstyle); + new GMTextField(matrix,10,&target_keywords,FXDataTarget::ID_VALUE,textfieldstyle); + + grpbox = new FXGroupBox(vframe,tr("Album Covers"),FRAME_NONE|LAYOUT_FILL_X,0,0,0,0,20); + grpbox->setFont(GMApp::instance()->getThickFont()); + + new GMCheckButton(grpbox,tr("Show album cover of playing track\tShow album cover of playing track"),&target_show_playing_albumcover,FXDataTarget::ID_VALUE); + new GMCheckButton(grpbox,tr("Show album covers in album browser\tShow album covers in album browser"),&target_show_albumcovers,FXDataTarget::ID_VALUE); + + grpbox = new FXGroupBox(vframe,tr("System Tray"),FRAME_NONE|LAYOUT_FILL_X,0,0,0,0,20); + grpbox->setFont(GMApp::instance()->getThickFont()); + + if (!GMPlayerManager::instance()->getPreferences().gui_tray_icon_disabled) + new GMCheckButton(grpbox,tr("Show Tray Icon\tShow tray icon in the system tray."),&target_gui_tray_icon,FXDataTarget::ID_VALUE); +#ifdef HAVE_DBUS + if (GMPlayerManager::instance()->hasSessionBus()) { + new GMCheckButton(grpbox,tr("Show Track Change Notifications\tInform notification daemon of track changes."),&target_dbus_notify_daemon,FXDataTarget::ID_VALUE); + } +#endif + + + grpbox = new FXGroupBox(vframe,tr("Last.fm"),FRAME_NONE|LAYOUT_FILL_X,0,0,0,0,23,3,0,0,0,0); + grpbox->setFont(GMApp::instance()->getThickFont()); + + + if (GMPlayerManager::instance()->getAudioScrobbler()->isBanned()) { + new FXLabel(grpbox,tr("This version of Goggles Music Manager is\n" + "not supported by Last-FM. Please upgrade\n" + "to a newer version of GMM."),NULL,JUSTIFY_LEFT); + } + else { + matrix = new FXMatrix(grpbox,2,MATRIX_BY_COLUMNS|LAYOUT_FILL_X,0,0,0,0); + new FXLabel(matrix,tr("Service:"),NULL,labelstyle); + FXHorizontalFrame * hframe = new FXHorizontalFrame(matrix,FRAME_NONE,0,0,0,0,0,0,0,0); + FXuint current_service = GMPlayerManager::instance()->getAudioScrobbler()->getService(); + + lastfm_service = new GMListBox(hframe,this,ID_LASTFM_SERVICE,FRAME_SUNKEN|FRAME_THICK); + lastfm_service->appendItem("Last.fm"); + lastfm_service->appendItem("Libre.fm"); + + if (current_service==SERVICE_CUSTOM) { + lastfm_service->appendItem("Custom"); + lastfm_service->setNumVisible(3); + } + else { + lastfm_service->setNumVisible(2); + } + lastfm_service->setCurrentItem(current_service); + + lastfm_join = new FXButton(hframe,tr("&Sign up…"),NULL,this,ID_LASTFM_JOIN,ICON_AFTER_TEXT|FRAME_RAISED|JUSTIFY_CENTER_Y|JUSTIFY_LEFT|BUTTON_TOOLBAR,0,0,0,0,7); + lastfm_join->setTextColor(FXRGB(0,0,255)); + lastfm_join->setDefaultCursor(GMIconTheme::instance()->cursor_hand); + if (current_service==SERVICE_CUSTOM) lastfm_join->hide(); + + + lastfm_username_label = new FXLabel(matrix,tr("Username:"),NULL,labelstyle); + lastfm_username = new GMTextField(matrix,20,this,ID_LASTFM_USERNAME,FRAME_SUNKEN|FRAME_THICK); + lastfm_password_label = new FXLabel(matrix,tr("Password:"),NULL,labelstyle); + lastfm_password = new GMTextField(matrix,20,this,ID_LASTFM_PASSWORD,FRAME_SUNKEN|FRAME_THICK|TEXTFIELD_PASSWD); + + new FXFrame(matrix,FRAME_NONE); + lastfm_scrobble = new GMCheckButton(grpbox,tr("Scrobble"),this,ID_LASTFM_SCROBBLE,LAYOUT_FIX_X|LAYOUT_FIX_Y|CHECKBUTTON_NORMAL,6,0); + lastfm_scrobble->setFont(GMApp::instance()->getThickFont()); + + lastfm_username->setText(GMPlayerManager::instance()->getAudioScrobbler()->getUsername()); + if (GMPlayerManager::instance()->getAudioScrobbler()->hasPassword()) + lastfm_password->setText("1234567890"); + + lastfm_scrobble->setCheck(GMPlayerManager::instance()->getAudioScrobbler()->isEnabled()); + + if (current_service==SERVICE_LASTFM){ + lastfm_username->hide(); + lastfm_username_label->hide(); + lastfm_password->hide(); + lastfm_password_label->hide(); + } + else { + lastfm_username->show(); + lastfm_username_label->show(); + lastfm_password->show(); + lastfm_password_label->show(); + } + } + + + new GMTabItem(tabbook,tr("&Window"),NULL,TAB_TOP_NORMAL,0,0,0,0,5,5); + vframe = new GMTabFrame(tabbook); + + grpbox = new FXGroupBox(vframe,tr("Window"),FRAME_NONE|LAYOUT_FILL_X,0,0,0,0,20); + grpbox->setFont(GMApp::instance()->getThickFont()); + + new GMCheckButton(grpbox,tr("Close button minimizes to tray"),&target_closeishide,FXDataTarget::ID_VALUE); + statusbarbutton = new GMCheckButton(grpbox,tr("Show Status Bar"),NULL,0); + statusbarbutton->setCheck(GMPlayerManager::instance()->getPreferences().gui_show_status_bar); + browser_icons = new GMCheckButton(grpbox,tr("Show Icons in Track Browser"),NULL,0); + browser_icons->setCheck(GMPlayerManager::instance()->getPreferences().gui_show_browser_icons); + new GMCheckButton(grpbox,tr("Display playing track in title bar"),&target_gui_show_playing_titlebar,FXDataTarget::ID_VALUE); + + grpbox = new FXGroupBox(vframe,tr("Player Controls"),FRAME_NONE|LAYOUT_FILL_X,0,0,0,0,20); + grpbox->setFont(GMApp::instance()->getThickFont()); + + matrix = new FXMatrix(grpbox,2,MATRIX_BY_COLUMNS); + new FXLabel(matrix,tr("Location:"),NULL,labelstyle); + toolbar_docktop = new GMListBox(matrix,NULL,0,FRAME_SUNKEN|FRAME_THICK|LAYOUT_FILL_COLUMN|LAYOUT_FILL_X); + toolbar_docktop->appendItem(tr("Top")); + toolbar_docktop->appendItem(tr("Bottom")); + toolbar_docktop->setNumVisible(2); + + if (GMPlayerManager::instance()->getPreferences().gui_toolbar_docktop) + toolbar_docktop->setCurrentItem(0); + else + toolbar_docktop->setCurrentItem(1); + + new FXLabel(matrix,tr("Title Format:"),NULL,labelstyle); + new GMTextField(matrix,20,&target_gui_format_title,FXDataTarget::ID_VALUE,TEXTFIELD_NORMAL|LAYOUT_FILL_COLUMN|LAYOUT_FILL_X); + + + new FXLabel(matrix,tr("Style:"),NULL,labelstyle); + toolbar_showlabels = new GMCheckButton(matrix,tr("Show Labels"),NULL,0); + toolbar_showlabels->setCheck(GMPlayerManager::instance()->getPreferences().gui_toolbar_showlabels); + new FXFrame(matrix,FRAME_NONE); + toolbar_bigicons = new GMCheckButton(matrix,tr("Large Icons"),NULL,0); + toolbar_bigicons->setCheck(GMPlayerManager::instance()->getPreferences().gui_toolbar_bigicons); + + + new GMTabItem(tabbook,tr("A&ppearance"),NULL,TAB_TOP_NORMAL,0,0,0,0,5,5); + vframe = new GMTabFrame(tabbook); + + grpbox = new FXGroupBox(vframe,tr("Colors"),FRAME_NONE|LAYOUT_FILL_X,0,0,0,0,2,2,0,2); + grpbox->setFont(GMApp::instance()->getThickFont()); + + matrix = new FXMatrix(grpbox,6,MATRIX_BY_COLUMNS|LAYOUT_SIDE_LEFT,0,0,0,0,0,0,0,0,0,0); + + + new FXFrame(matrix,FRAME_NONE); + new FXLabel(matrix,tr("fg\tForeground Color"),NULL,LAYOUT_CENTER_Y|LABEL_NORMAL|LAYOUT_CENTER_X); + new FXLabel(matrix,tr("bg\tBackground Color"),NULL,LAYOUT_CENTER_Y|LABEL_NORMAL|LAYOUT_CENTER_X); + new FXLabel(matrix,tr("alt bg\tAlternative Background Color"),NULL,LAYOUT_CENTER_Y|LABEL_NORMAL|LAYOUT_CENTER_X); + new FXFrame(matrix,FRAME_NONE); + new FXFrame(matrix,FRAME_NONE); + + new FXFrame(matrix,FRAME_NONE); + new FXSeparator(matrix,SEPARATOR_LINE|LAYOUT_FILL_X); + new FXSeparator(matrix,SEPARATOR_LINE|LAYOUT_FILL_X); + new FXSeparator(matrix,SEPARATOR_LINE|LAYOUT_FILL_X); + new FXFrame(matrix,FRAME_NONE); + new FXFrame(matrix,FRAME_NONE); + + new FXLabel(matrix,tr("Normal\tNormal Text Color"),NULL,LAYOUT_CENTER_Y|LABEL_NORMAL|LAYOUT_RIGHT); + new FXColorWell(matrix,0,this,ID_FORE_COLOR,COLORWELL_OPAQUEONLY|FRAME_LINE|LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT|LAYOUT_FILL_ROW|LAYOUT_CENTER_Y,0,0,40,24); + new FXColorWell(matrix,0,this,ID_BACK_COLOR,COLORWELL_OPAQUEONLY|FRAME_LINE|LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT|LAYOUT_FILL_ROW|LAYOUT_CENTER_Y,0,0,40,24); + new FXColorWell(matrix,0,this,ID_ALTERNATIVE_BACK_COLOR,COLORWELL_OPAQUEONLY|FRAME_LINE|LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT|LAYOUT_FILL_ROW|LAYOUT_CENTER_Y,0,0,40,24); + new FXLabel(matrix,tr("Base\tBase Color"),NULL,LAYOUT_CENTER_Y|LABEL_NORMAL|LAYOUT_RIGHT); + new FXColorWell(matrix,0,this,ID_BASE_COLOR,COLORWELL_OPAQUEONLY|FRAME_LINE|LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT|LAYOUT_FILL_ROW|LAYOUT_CENTER_Y,0,0,40,24); + + new FXLabel(matrix,tr("Selected\tSelected Text Color"),NULL,LAYOUT_CENTER_Y|LABEL_NORMAL|LAYOUT_RIGHT); + new FXColorWell(matrix,0,this,ID_SEL_FORE_COLOR,COLORWELL_OPAQUEONLY|FRAME_LINE|LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT|LAYOUT_FILL_ROW|LAYOUT_CENTER_Y,0,0,40,24); + new FXColorWell(matrix,0,this,ID_SEL_BACK_COLOR,COLORWELL_OPAQUEONLY|FRAME_LINE|LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT|LAYOUT_FILL_ROW|LAYOUT_CENTER_Y,0,0,40,24); + new FXFrame(matrix,FRAME_NONE); + new FXLabel(matrix,tr("Border\tBorder Color"),NULL,LAYOUT_CENTER_Y|LABEL_NORMAL|LAYOUT_RIGHT); + new FXColorWell(matrix,0,this,ID_BORDER_COLOR,COLORWELL_OPAQUEONLY|FRAME_LINE|LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT|LAYOUT_FILL_ROW|LAYOUT_CENTER_Y,0,0,40,24); + +// new FXLabel(matrix,tr("Menu\tMenu Base Color"),NULL,LAYOUT_CENTER_Y|LABEL_NORMAL|LAYOUT_RIGHT); +// new FXColorWell(matrix,0,this,ID_MENU_BASE_COLOR,COLORWELL_OPAQUEONLY|FRAME_LINE|LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT|LAYOUT_FILL_ROW|LAYOUT_CENTER_Y,0,0,40,24); + + + new FXLabel(matrix,tr("Menu\tMenu Text Color"),NULL,LAYOUT_CENTER_Y|LABEL_NORMAL|LAYOUT_RIGHT); + new FXColorWell(matrix,0,this,ID_MENU_FORE_COLOR,COLORWELL_OPAQUEONLY|FRAME_LINE|LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT|LAYOUT_FILL_ROW|LAYOUT_CENTER_Y,0,0,40,24); + new FXColorWell(matrix,0,this,ID_MENU_BACK_COLOR,COLORWELL_OPAQUEONLY|FRAME_LINE|LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT|LAYOUT_FILL_ROW|LAYOUT_CENTER_Y,0,0,40,24); + new FXFrame(matrix,FRAME_NONE); + new FXLabel(matrix,tr("Hilite\tHilite Color"),NULL,LAYOUT_CENTER_Y|LABEL_NORMAL|LAYOUT_RIGHT); + new FXColorWell(matrix,0,this,ID_HILITE_COLOR,COLORWELL_OPAQUEONLY|FRAME_SUNKEN|FRAME_THICK|LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT|LAYOUT_FILL_ROW|LAYOUT_CENTER_Y,0,0,40,24); + + + new FXLabel(matrix,tr("Tooltip\tTooltip Color"),NULL,LAYOUT_CENTER_Y|LABEL_NORMAL|LAYOUT_RIGHT); + new FXColorWell(matrix,0,this,ID_TIP_FORE_COLOR,COLORWELL_OPAQUEONLY|FRAME_LINE|LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT|LAYOUT_FILL_ROW|LAYOUT_CENTER_Y,0,0,40,24); + new FXColorWell(matrix,0,this,ID_TIP_BACK_COLOR,COLORWELL_OPAQUEONLY|FRAME_LINE|LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT|LAYOUT_FILL_ROW|LAYOUT_CENTER_Y,0,0,40,24); + new FXFrame(matrix,FRAME_NONE); + new FXLabel(matrix,tr("Shadow\tShadow Color"),NULL,LAYOUT_CENTER_Y|LABEL_NORMAL|LAYOUT_RIGHT); + new FXColorWell(matrix,0,this,ID_SHADOW_COLOR,COLORWELL_OPAQUEONLY|FRAME_LINE|LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT|LAYOUT_FILL_ROW|LAYOUT_CENTER_Y,0,0,40,24); + + new FXLabel(matrix,tr("Playing\tPlaying Track Color"),NULL,LAYOUT_CENTER_Y|LABEL_NORMAL|LAYOUT_RIGHT); + new FXColorWell(matrix,0,this,ID_PLAY_FORE_COLOR,COLORWELL_OPAQUEONLY|FRAME_LINE|LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT|LAYOUT_FILL_ROW|LAYOUT_CENTER_Y,0,0,40,24); + new FXColorWell(matrix,0,this,ID_PLAY_BACK_COLOR,COLORWELL_OPAQUEONLY|FRAME_LINE|LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT|LAYOUT_FILL_ROW|LAYOUT_CENTER_Y,0,0,40,24); + new FXFrame(matrix,FRAME_NONE); + new FXLabel(matrix,tr("Tray\tTray Background Color"),NULL,LAYOUT_CENTER_Y|LABEL_NORMAL|LAYOUT_RIGHT); + new FXColorWell(matrix,0,this,ID_TRAY_COLOR,COLORWELL_OPAQUEONLY|FRAME_LINE|LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT|LAYOUT_FILL_ROW|LAYOUT_CENTER_Y,0,0,40,24); + + + new FXSeparator(grpbox,SEPARATOR_GROOVE|LAYOUT_FILL_Y|LAYOUT_SIDE_LEFT); + + vframe2 = new FXVerticalFrame(grpbox,LAYOUT_SIDE_RIGHT|LAYOUT_FILL_X|LAYOUT_FILL_Y,0,0,0,0,0,0,0,0,0,0); + new FXLabel(vframe2,tr("Presets:")); + GMScrollFrame * sunken = new GMScrollFrame(vframe2); + colorpresets = new GMList(sunken,this,ID_COLOR_THEME,LIST_BROWSESELECT|LAYOUT_FILL_X|LAYOUT_FILL_Y); + colorpresets->setNumVisible(9); + colorpresets->setScrollStyle(HSCROLLING_OFF); + + initColorThemes(); + + grpbox = new FXGroupBox(vframe,tr("Font & Icons"),FRAME_NONE|LAYOUT_FILL_X,0,0,0,0); + grpbox->setFont(GMApp::instance()->getThickFont()); + + + matrix = new FXMatrix(grpbox,3,MATRIX_BY_COLUMNS|LAYOUT_FILL_X,0,0,0,0,20); + + new FXLabel(matrix,tr("Default Font"),NULL,LAYOUT_RIGHT|LAYOUT_CENTER_Y); + new GMTextField(matrix,20,this,ID_FONT,LAYOUT_CENTER_Y|LABEL_NORMAL|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN|TEXTFIELD_NORMAL|TEXTFIELD_READONLY); + new GMButton(matrix,tr("Change…"),NULL,this,ID_CHANGE_FONT,BUTTON_NORMAL|LAYOUT_CENTER_Y); + new FXLabel(matrix,tr("Icons"),NULL,LAYOUT_RIGHT|LAYOUT_CENTER_Y); + +// if (GMIconTheme::instance()->getNumThemes()==0) +// GMIconTheme::instance()->listThemes(); + + themelist = new GMListBox(matrix,this,ID_ICON_THEME,FRAME_SUNKEN|FRAME_THICK|LAYOUT_FILL_COLUMN|LAYOUT_FILL_X); + for (FXint i=0;igetNumThemes();i++) { + themelist->appendItem(GMIconTheme::instance()->getThemeName(i),NULL,(void*)(FXival)i); + } + themelist->setSortFunc(FXList::ascending); + themelist->sortItems(); + themelist->setNumVisible(FXMIN(9,themelist->getNumItems())); + for (FXint i=0;igetNumItems();i++) { + if (GMIconTheme::instance()->getCurrentTheme()==(FXint)(FXival)themelist->getItemData(i)) { + themelist->setCurrentItem(i); + break; + } + } + + new GMTabItem(tabbook,tr("&Audio"),NULL,TAB_TOP_NORMAL,0,0,0,0,5,5); + vframe = new GMTabFrame(tabbook); + + grpbox = new FXGroupBox(vframe,tr("Engine"),FRAME_NONE|LAYOUT_FILL_X,0,0,0,0,20); + grpbox->setFont(GMApp::instance()->getThickFont()); + + matrix = new FXMatrix(grpbox,2,MATRIX_BY_COLUMNS|LAYOUT_SIDE_TOP,0,0,0,0,0,0,0,0); + new FXLabel(matrix,tr("Audio Driver:"),NULL,labelstyle); + + driverlist = new GMListBox(matrix,this,ID_AUDIO_DRIVER); + FXString available,current; + GMPlayerManager::instance()->getPlayer()->getAvailableDrivers(available); + GMPlayerManager::instance()->getPlayer()->getCurrentDriver(current); + driverlist->clearItems(); + driverlist->fillItems(available); + driverlist->setCurrentItem(driverlist->findItem(current)); + driverlist->setNumVisible(FXMIN(9,driverlist->getNumItems())); + + new GMCheckButton(grpbox,tr("Close audio device on pause."),&target_pause_close_device,FXDataTarget::ID_VALUE); + new GMCheckButton(grpbox,tr("Turn off playback engine on stop."),&target_close_audio,FXDataTarget::ID_VALUE); + new GMCheckButton(grpbox,tr("Turn on playback engine on startup.\tFor faster startup, playback engine is normally started when first track is played.\tFor faster startup, playback engine is normally started when first track is played."),&target_open_device_on_startup,FXDataTarget::ID_VALUE); + + + grpbox = new FXGroupBox(vframe,tr("Playback"),FRAME_NONE|LAYOUT_FILL_X,0,0,0,0,20); + grpbox->setFont(GMApp::instance()->getThickFont()); + matrix = new FXMatrix(grpbox,2,MATRIX_BY_COLUMNS|LAYOUT_SIDE_TOP,0,0,0,0,0,0,0,0); + + new FXLabel(matrix,tr("Replay Gain:"),NULL,labelstyle); + GMListBox * gainlist = new GMListBox(matrix,&target_replaygain,FXDataTarget::ID_VALUE); + gainlist->appendItem(tr("Off")); + gainlist->appendItem(tr("Track")); + gainlist->appendItem(tr("Album")); + gainlist->setNumVisible(3); + + new GMCheckButton(grpbox,tr("Gapless playback"),&target_gapless,FXDataTarget::ID_VALUE); + check_audio_normalization = new GMCheckButton(grpbox,tr("Volume Normalization")); + + + if (GMPlayerManager::instance()->getPlayer()->hasVolumeNormalization() && GMPlayerManager::instance()->getPlayer()->getVolumeNormalization()) + check_audio_normalization->setCheck(true); + + FXHorizontalFrame *closebox=new FXHorizontalFrame(main,LAYOUT_BOTTOM|LAYOUT_FILL_X|PACK_UNIFORM_WIDTH,0,0,0,0,0,0,0,0); + new GMButton(closebox,tr("&Close"),NULL,this,FXDialogBox::ID_ACCEPT,BUTTON_INITIAL|BUTTON_DEFAULT|LAYOUT_RIGHT|FRAME_RAISED|FRAME_THICK,0,0,0,0, 20,20); + + } + +GMPreferencesDialog::~GMPreferencesDialog(){ + } + + +void GMPreferencesDialog::redraw(){ + register FXWindow *w=GMPlayerManager::instance()->getMainWindow(); + while(w){ + w->recalc(); + w->update(); + if(w->getFirst()){ + w=w->getFirst(); + continue; + } + while(!w->getNext() && w->getParent()){ + w=w->getParent(); + } + w=w->getNext(); + } + } + + +long GMPreferencesDialog::onCmdAudioDriver(FXObject*,FXSelector,void*){ + FXString selected=driverlist->getItemText(driverlist->getCurrentItem()); + FXString current; + FXString available; + + /// Stop Playback + GMPlayerManager::instance()->stop(); + + if (!GMPlayerManager::instance()->getPlayer()->changeDriver(selected)) { + + //FXMessageBox::error(this,MBOX_OK,"Audio Device Error","Failed to open requested audio driver: %s",selected.text()); + + + driverlist->disable(); + } + else { + GMPlayerManager::instance()->getPlayer()->getCurrentDriver(current); + if (selected!=current) { + FXMessageBox::error(this,MBOX_OK,tr("Audio Device Error"),fxtrformat("Failed to open requested audio driver: %s"),selected.text()); + } + GMPlayerManager::instance()->getPlayer()->getAvailableDrivers(available); + driverlist->clearItems(); + driverlist->fillItems(available); + driverlist->setCurrentItem(driverlist->findItem(current)); + driverlist->setNumVisible(FXMIN(9,driverlist->getNumItems())); + } + return 1; + } + +long GMPreferencesDialog::onCmdReplayGain(FXObject*,FXSelector,void*){ + GMPlayerManager::instance()->update_replay_gain(); + return 1; + } + + + +long GMPreferencesDialog::onCmdAccept(FXObject*,FXSelector,void*) { + getApp()->stopModal(this,TRUE); + hide(); + + GMWindow * mainwindow = GMPlayerManager::instance()->getMainWindow(); + + if (!GMPlayerManager::instance()->getAudioScrobbler()->isBanned() && password_set) { + GMPlayerManager::instance()->getAudioScrobbler()->login(lastfm_username->getText(),lastfm_password->getText()); + } + + GMPlayerManager::instance()->getPreferences().setKeyWords(keywords); + + if (check_audio_normalization->getCheck()) { + GMPlayerManager::instance()->getPlayer()->setVolumeNormalization(true); + } + else { + GMPlayerManager::instance()->getPlayer()->setVolumeNormalization(false); + } + GMPlayerManager::instance()->getPlayer()->setupGapless(); + + if (browser_icons->getCheck()!=GMPlayerManager::instance()->getPreferences().gui_show_browser_icons){ + GMPlayerManager::instance()->getPreferences().gui_show_browser_icons=browser_icons->getCheck(); + mainwindow->getTrackView()->updateIcons(); + } + + if (!(selected==current)) { + GMIconTheme::instance()->load(); + redraw(); + } + + mainwindow->configureStatusbar(statusbarbutton->getCheck()); + mainwindow->configureToolbar((toolbar_docktop->getCurrentItem()==0),toolbar_showlabels->getCheck(),toolbar_bigicons->getCheck()); + + if (GMPlayerManager::instance()->getPreferences().gui_show_playing_albumcover) { + if (GMPlayerManager::instance()->playing()) + GMPlayerManager::instance()->update_track_display(false); + } + else { + mainwindow->loadCover(FXString::null); + } + + GMPlayerManager::instance()->update_tray_icon(); + + // Key Words may have changed. + mainwindow->getTrackView()->resort(); + mainwindow->getSourceView()->resort(); + return 1; + } + + + + +void GMPreferencesDialog::initColorThemes() { + for(FXuint i=0;iappendItem(ColorThemes[i].name,NULL,(void*)&ColorThemes[i]); + } + + theme=-1; + for(FXuint i=0;iprependItem(tr("Current"),NULL,(void*)¤t); + theme=0; + } + } + + +void GMPreferencesDialog::updateColorThemes() { + theme=-1; + + for (FXint i=0;igetNumItems();i++){ + if (selected==*(ColorTheme*)colorpresets->getItemData(i)){ + theme=i; + return; + } + } + + if (theme==-1) { + theme=colorpresets->getNumItems(); + colorpresets->appendItem(tr("Custom"),NULL,(void*)&selected); + } + else if (themegetNumItems()-1) { + if (colorpresets->getItemData(colorpresets->getNumItems()-1)==&selected) + colorpresets->removeItem(colorpresets->getNumItems()-1); + } + } + + +long GMPreferencesDialog::onCmdIconTheme(FXObject*,FXSelector,void*){ + GMIconTheme::instance()->setCurrentTheme((FXint)(FXival)themelist->getItemData(themelist->getCurrentItem())); + GMIconTheme::instance()->load(); + redraw(); + return 1; + } + + + + + + + + + +long GMPreferencesDialog::onCmdLastFMScrobble(FXObject*,FXSelector,void*){ + if (lastfm_scrobble->getCheck()) + GMPlayerManager::instance()->getAudioScrobbler()->enable(); + else + GMPlayerManager::instance()->getAudioScrobbler()->disable(); + return 1; + } + + +long GMPreferencesDialog::onCmdLastFMService(FXObject*,FXSelector,void*){ + if (lastfm_service->getCurrentItem()!=SERVICE_CUSTOM) { + + + GMPlayerManager::instance()->getAudioScrobbler()->service(lastfm_service->getCurrentItem()); + lastfm_username->setText(FXString::null,false); + lastfm_password->setText(FXString::null,false); + password_set=false; + if (lastfm_service->getNumItems()==3) { + lastfm_service->removeItem(2); + lastfm_service->setNumVisible(2); + } + lastfm_join->show(); + } + + if (lastfm_service->getCurrentItem()==SERVICE_LASTFM){ + lastfm_username->hide(); + lastfm_username_label->hide(); + lastfm_password->hide(); + lastfm_password_label->hide(); + } + else { + lastfm_username->show(); + lastfm_username_label->show(); + lastfm_password->show(); + lastfm_password_label->show(); + } + return 1; + } + + +long GMPreferencesDialog::onCmdLastFMPassWord(FXObject*,FXSelector,void*){ +// fxmessage("onCmdLastFMPassWord %d\n",password_set); + if (!GMPlayerManager::instance()->getAudioScrobbler()->isBanned() && password_set) { + lastfm_scrobble->setCheck(true); + GMPlayerManager::instance()->getAudioScrobbler()->login(lastfm_username->getText(),lastfm_password->getText()); + } + return 1; + } + +long GMPreferencesDialog::onFocusLastFMPassWord(FXObject*,FXSelector,void*){ +// fxmessage("onFocusLastFMPassWord %d\n",password_set); + if (password_set==false) { + password_set=true; + lastfm_password->setText(FXString::null,false); + } + return 0; + } + +long GMPreferencesDialog::onCmdLastFMUserName(FXObject*,FXSelector sel,void*){ + // if (GMPlayerManager::instance()->getAudioScrobbler()->getUsername()!=lastfm_username->getText()){ + lastfm_password->setText(FXString::null,false); + password_set=true; + if (FXSELTYPE(sel)==SEL_COMMAND) lastfm_password->setFocus(); + // } + return 1; + } + +long GMPreferencesDialog::onCmdLastFMJoin(FXObject*,FXSelector,void*){ + FXuint service = GMPlayerManager::instance()->getAudioScrobbler()->getService(); + if (service==SERVICE_LASTFM) { + if (!gm_open_browser("https://www.last.fm/join/")){ + FXMessageBox::error(this,MBOX_OK,tr("Unable to launch webbrowser"),"Goggles Music Manager was unable to launch a webbrowser.\nPlease visit https://www.last.fm/join/"); + } + } + else if (service==SERVICE_LIBREFM) { + if (!gm_open_browser("http://alpha.libre.fm/register.php")){ + FXMessageBox::error(this,MBOX_OK,tr("Unable to launch webbrowser"),"Goggles Music Manager was unable to launch a webbrowser.\nPlease visit http://alpha.libre.fm/register.php"); + } + } + return 1; + } + + +long GMPreferencesDialog::onCmdElementColor(FXObject*,FXSelector sel,void* rgba) { + FXColor color = (FXColor)(FXuval)rgba; + + switch(FXSELID(sel)){ + case ID_BASE_COLOR : selected.base = color; + selected.shadow = makeShadowColor(selected.base); + selected.hilite = makeHiliteColor(selected.base); + break; + case ID_BORDER_COLOR : selected.border = color; break; + case ID_BACK_COLOR : selected.back = color; break; + case ID_FORE_COLOR : selected.fore = color; break; + case ID_SHADOW_COLOR : selected.shadow=color; break; + case ID_HILITE_COLOR : selected.hilite=color; break; + + + case ID_SEL_BACK_COLOR: selected.selback=color; break; + case ID_SEL_FORE_COLOR: selected.selfore=color; break; + case ID_MENU_BACK_COLOR: selected.menuback=color; break; + case ID_MENU_FORE_COLOR: selected.menufore=color; break; + case ID_TIP_BACK_COLOR: selected.tipback=color; break; + case ID_TIP_FORE_COLOR: selected.tipfore=color; break; + case ID_PLAY_BACK_COLOR: selected.playback=color; break; + case ID_PLAY_FORE_COLOR: selected.playfore=color; break; + case ID_TRAY_COLOR: selected.trayback=color; break; + case ID_ALTERNATIVE_BACK_COLOR: selected.altback=color; break; + + } + updateColors(); + + updateColorThemes(); + return 1; + } + +long GMPreferencesDialog::onUpdElementColor(FXObject*sender,FXSelector sel,void*) { + FXuval rgba = 0; + switch(FXSELID(sel)){ + case ID_BASE_COLOR : rgba=selected.base; break; + case ID_BORDER_COLOR : rgba=selected.border; break; + case ID_BACK_COLOR : rgba=selected.back; break; + case ID_FORE_COLOR : rgba=selected.fore; break; + case ID_SHADOW_COLOR : rgba=selected.shadow; break; + case ID_HILITE_COLOR : rgba=selected.hilite; break; + + case ID_SEL_BACK_COLOR: rgba=selected.selback; break; + case ID_SEL_FORE_COLOR: rgba=selected.selfore; break; + case ID_MENU_BACK_COLOR: rgba=selected.menuback; break; + case ID_MENU_FORE_COLOR: rgba=selected.menufore; break; + case ID_TIP_BACK_COLOR: rgba=selected.tipback; break; + case ID_TIP_FORE_COLOR: rgba=selected.tipfore; break; + case ID_PLAY_BACK_COLOR: rgba=selected.playback; break; + case ID_PLAY_FORE_COLOR: rgba=selected.playfore; break; + case ID_TRAY_COLOR: rgba=selected.trayback; break; + case ID_ALTERNATIVE_BACK_COLOR: rgba=selected.altback; break; + + + } + sender->tryHandle(this,FXSEL(SEL_COMMAND,FXColorWell::ID_SETINTVALUE),&rgba); + return 1; + } +long GMPreferencesDialog::onCmdColorTheme(FXObject*,FXSelector,void*ptr) { + theme=(FXint)(FXival)ptr; + ColorTheme *theme_selected=reinterpret_cast(colorpresets->getItemData(theme)); + + selected.base = theme_selected->base; + selected.border = theme_selected->border; + selected.back = theme_selected->back; + selected.altback = theme_selected->altback; + selected.fore = theme_selected->fore; + selected.selfore = theme_selected->selfore; + selected.selback = theme_selected->selback; + selected.tipfore = theme_selected->tipfore; + selected.tipback = theme_selected->tipback; + selected.menufore= theme_selected->menufore; + selected.menuback= theme_selected->menuback; + selected.shadow = makeShadowColor(selected.base); + selected.hilite = makeHiliteColor(selected.base); + selected.playback = theme_selected->playback; + selected.playfore = theme_selected->playfore; + selected.trayback = theme_selected->base; + + updateColors(); + return 1; + } + +long GMPreferencesDialog::onUpdColorTheme(FXObject*sender,FXSelector,void*) { + sender->tryHandle(this,FXSEL(SEL_COMMAND,FXWindow::ID_SETINTVALUE),&theme); + return 1; + } + + + +void GMPreferencesDialog::updateColors(){ + register FXWindow *w=FXApp::instance()->getRootWindow(); + + FX7Segment * sevensegment; + FXTextField * gmtextfield; + FXIconList * iconlist; + FXList * list; + FXListBox * listbox; + FXTreeList * treelist; + FXComboBox * combobox; + FXButton * button; + FXFrame * frame; + FXLabel * label; + FXPopup * popup; + FXMenuTitle * menutitle; + FXMenuCheck * menucheck; + FXMenuRadio * menuradio; + FXMenuCaption * menucaption; + FXMenuSeparator * menuseparator; + FXText * text; + FXFoldingList * foldinglist; + FXMDIChild * mdichild; + FXTable * table; + FXDockTitle * docktitle; + FXPacker * packer; + FXHeader * header; + FXGroupBox * groupbox; + FXScrollBar * scrollbar; + FXSlider * slider; + FXStatusLine * statusline; + FXDragCorner * dragcorner; + GMTreeList * gmtreelist; + GMTrackList * gmtracklist; + FXRadioButton * radiobutton; + GMCheckButton * checkbutton; + FXToolTip * tooltip; + + GMList * gmlist; + GMListBox * gmlistbox; + GMComboBox * gmcombobox; + GMScrollFrame * gmscrollframe; + GMTabFrame * gmtabframe; + GMImageFrame * gmimageframe; + GMCoverFrame * gmcoverframe; + GMImageView * gmimageview; + GMMenuPane * gmmenupane; + GMProgressBar * gmprogressbar; +// GMTrackProgressBar * gmtrackprogressbar; + GMSpinner * gmspinner; + + while(w){ + w->setBackColor(selected.base); + if ((frame=dynamic_cast(w))) { + + frame->setBaseColor(selected.base); + frame->setBackColor(selected.base); + frame->setShadowColor(selected.shadow); + frame->setHiliteColor(selected.hilite); + frame->setBorderColor(selected.border); + + if ((label=dynamic_cast(w))) { + label->setTextColor(selected.fore); + if ((button=dynamic_cast(w))) { + if (dynamic_cast(button->getParent())){ + w->setBackColor(selected.back); + } + else { + w->setBackColor(selected.base); + } + } + else if ((checkbutton=dynamic_cast(w))) { + checkbutton->setCheckColor(selected.fore); + checkbutton->setBoxColor(selected.back); + } + else if ((radiobutton=dynamic_cast(w))) { + radiobutton->setRadioColor(selected.fore); + radiobutton->setDiskColor(selected.back); + } + } + else if ((gmtextfield=dynamic_cast(w))) { + w->setBackColor(selected.back); + gmtextfield->setTextColor(selected.fore); + gmtextfield->setSelTextColor(selected.selfore); + gmtextfield->setSelBackColor(selected.selback); + gmtextfield->setBorderColor(selected.shadow); + } + else if ((docktitle=dynamic_cast(w))) { + docktitle->setCaptionColor(selected.selfore); + docktitle->setBackColor(selected.selback); + } + else if ((header=dynamic_cast(w))) { + header->setTextColor(selected.fore); + } + else if ((statusline=dynamic_cast(w))) { + statusline->setTextColor(selected.fore); + } + else if ((sevensegment=dynamic_cast(w))) { + sevensegment->setTextColor(selected.fore); + } + else if ((slider=dynamic_cast(w))) { + slider->setSlotColor(selected.back); + } + else if ((gmimageframe=dynamic_cast(w))) { + gmimageframe->setBorderColor(selected.shadow); + gmimageframe->setBackColor(selected.back); /// fixme, only for coverframe in mainwindow + } + else if ((gmprogressbar=dynamic_cast(w))) { + gmprogressbar->setBorderColor(selected.shadow); + gmprogressbar->setBarColor(selected.selback); + gmprogressbar->setTextAltColor(selected.selfore); + } +/* + else if ((gmtrackprogressbar=dynamic_cast(w))) { + gmtrackprogressbar->setBorderColor(selected.shadow); + gmtrackprogressbar->setBarColor(selected.selback); + gmtrackprogressbar->setTextAltColor(selected.selfore); + } +*/ + } + else if ((packer=dynamic_cast(w))) { + packer->setBaseColor(selected.base); + packer->setBackColor(selected.base); + packer->setShadowColor(selected.shadow); + packer->setHiliteColor(selected.hilite); + packer->setBorderColor(selected.border); + if ((gmscrollframe=dynamic_cast(w))){ + gmscrollframe->setBorderColor(selected.shadow); + } + else if ((gmtabframe=dynamic_cast(w))){ + gmtabframe->setBorderColor(selected.shadow); + } + else if ((gmcoverframe=dynamic_cast(w))){ + gmcoverframe->setBorderColor(selected.shadow); + gmcoverframe->setBackColor(selected.back); + } + else if ((combobox=dynamic_cast(w))) { + w->setBackColor(selected.back); + } + else if ((listbox=dynamic_cast(w))) { + //w->setBackColor(selected.back); + if ((gmlistbox=dynamic_cast(w))) { + gmlistbox->setBorderColor(selected.shadow); + } + } + else if ((groupbox=dynamic_cast(w))) { + groupbox->setTextColor(selected.fore); + } + else if ((gmspinner=dynamic_cast(w))) { + gmspinner->setBorderColor(selected.shadow); + gmspinner->setUpArrowColor(selected.fore); + gmspinner->setDownArrowColor(selected.fore); + } + } + else if ((popup=dynamic_cast(w))){ + popup->setBaseColor(selected.base); + popup->setShadowColor(selected.shadow); + popup->setHiliteColor(selected.hilite); + popup->setBorderColor(selected.border); + if ((gmmenupane=dynamic_cast(w)) || (gmlistbox=dynamic_cast(w->getParent())) || (gmcombobox=dynamic_cast(w->getParent()))){ + popup->setBorderColor(selected.shadow); + } + } + else if ((menucaption=dynamic_cast(w))) { + w->setBackColor(selected.back); + menucaption->setTextColor(selected.fore); + menucaption->setSelTextColor(selected.menufore); + menucaption->setSelBackColor(selected.menuback); + menucaption->setShadowColor(makeShadowColor(selected.back)); + menucaption->setHiliteColor(makeHiliteColor(selected.back)); + + if ((menucheck=dynamic_cast(w))) { + menucheck->setBoxColor(selected.back); + } + else if ((menuradio=dynamic_cast(w))) { + menuradio->setRadioColor(selected.back); + } + else if ((menutitle=dynamic_cast(w))) { + w->setBackColor(selected.base); + menutitle->setTextColor(selected.fore); + menutitle->setSelTextColor(selected.menufore); + menutitle->setSelBackColor(selected.menuback); + menutitle->setShadowColor(selected.shadow); + menutitle->setHiliteColor(selected.hilite); + } + } + else if ((menuseparator=dynamic_cast(w))) { + menuseparator->setShadowColor(makeShadowColor(selected.back)); + menuseparator->setHiliteColor(makeHiliteColor(selected.back)); + } + else if ((scrollbar=dynamic_cast(w))) { + scrollbar->setShadowColor(selected.shadow); + scrollbar->setHiliteColor(selected.hilite); + scrollbar->setBorderColor(selected.border); + scrollbar->setArrowColor(selected.fore); + } + else if ((dragcorner=dynamic_cast(w))) { + dragcorner->setShadowColor(selected.shadow); + dragcorner->setHiliteColor(selected.hilite); + } + else if (dynamic_cast(w)) { + if ((text=dynamic_cast(w))) { + w->setBackColor(selected.back); + text->setTextColor(selected.fore); + text->setSelTextColor(selected.selfore); + text->setSelBackColor(selected.selback); + } + else if ((list=dynamic_cast(w))) { + w->setBackColor(selected.back); + list->setTextColor(selected.fore); + list->setSelTextColor(selected.selfore); + list->setSelBackColor(selected.selback); + if ((gmlist=dynamic_cast(w))) { + gmlist->setRowColor(selected.altback); + ((FXFrame*)gmlist->getParent())->setBorderColor(selected.shadow); + } + } + else if ((treelist=dynamic_cast(w))) { + w->setBackColor(selected.back); + treelist->setTextColor(selected.fore); + treelist->setLineColor(selected.shadow); + treelist->setSelTextColor(selected.selfore); + treelist->setSelBackColor(selected.selback); + if ((gmtreelist=dynamic_cast(w))) { + gmtreelist->setRowColor(selected.altback); + ((FXFrame*)gmtreelist->getParent())->setBorderColor(selected.shadow); + } + else { + treelist->setSelTextColor(selected.selfore); + treelist->setSelBackColor(selected.selback); + } + } + else if ((iconlist=dynamic_cast(w))) { + w->setBackColor(selected.back); + iconlist->setTextColor(selected.fore); + iconlist->setSelTextColor(selected.selfore); + iconlist->setSelBackColor(selected.selback); + } + else if ((gmtracklist=dynamic_cast(w))) { + w->setBackColor(selected.back); + //((FXFrame*)gmtracklist->getParent())->setBorderColor(selected.shadow); + gmtracklist->setTextColor(selected.fore); + gmtracklist->setSelTextColor(selected.selfore); + gmtracklist->setSelBackColor(selected.selback); + gmtracklist->setRowColor(selected.altback); + gmtracklist->setActiveTextColor(selected.playfore); + gmtracklist->setActiveColor(selected.playback); + } + else if ((foldinglist=dynamic_cast(w))) { + w->setBackColor(selected.back); + foldinglist->setTextColor(selected.fore); + foldinglist->setSelTextColor(selected.selfore); + foldinglist->setSelBackColor(selected.selback); + foldinglist->setLineColor(selected.shadow); + } + else if ((table=dynamic_cast(w))) { + w->setBackColor(selected.back); + table->setTextColor(selected.fore); + table->setSelTextColor(selected.selfore); + table->setSelBackColor(selected.selback); + } + } + else if ((mdichild=dynamic_cast(w))) { + mdichild->setBackColor(selected.base); + mdichild->setBaseColor(selected.base); + mdichild->setShadowColor(selected.shadow); + mdichild->setHiliteColor(selected.hilite); + mdichild->setBorderColor(selected.border); + mdichild->setTitleColor(selected.selfore); + mdichild->setTitleBackColor(selected.selback); + } + else if ((tooltip=dynamic_cast(w))){ + tooltip->setTextColor(selected.tipfore); + tooltip->setBackColor(selected.tipback); + } + else if ((gmimageview=dynamic_cast(w))){ + gmimageview->setBackColor(selected.back); + } + w->update(); + if(w->getFirst()){ + w=w->getFirst(); + continue; + } + while(!w->getNext() && w->getParent()){ + w=w->getParent(); + } + w=w->getNext(); + } + selected.save(); + + if (GMPlayerManager::instance()->getTrayIcon()) + GMPlayerManager::instance()->getTrayIcon()->updateIcon(); + } + + + +long GMPreferencesDialog::onCmdChangeFont(FXObject*,FXSelector,void*){ + GMFontDialog dialog(this,tr("Select Normal Font")); + FXFont * font = FXApp::instance()->getNormalFont(); + FXFontDesc fontdescription; +#if FOXVERSION < FXVERSION(1,7,17) + font->getFontDesc(fontdescription); + fontdescription.size = font->getActualSize(); + fontdescription.weight = font->getActualWeight(); + fontdescription.setwidth = font->getActualSetWidth(); + fontdescription.slant = font->getActualSlant(); +#else + fontdescription = font->getActualFontDesc(); +#endif + strncpy(fontdescription.face,font->getActualName().text(),sizeof(fontdescription.face)); + dialog.setFontDesc(fontdescription); + if(dialog.execute(PLACEMENT_SCREEN)){ + fontdescription=dialog.getFontDesc(); + GMApp::instance()->setFont(fontdescription); + updateFonts(); + } + return 1; + } + +void GMPreferencesDialog::updateFonts() { + GMPlayerManager::instance()->getMainWindow()->layoutToolBarButtons(); + GMPlayerManager::instance()->getMainWindow()->getTrackView()->updateFont(); + redraw(); + FXint nw =getDefaultWidth(); + FXint nh = getDefaultHeight(); + resize(nw,nh); + } + + +static void fancyfontname(FXFont * font,FXString & name) { + name = font->getActualName().before('[').trimEnd(); + const FXchar *wgt=NULL,*slt=NULL,*wid=NULL; + const FXint size=font->getActualSize()/10; + + switch(font->getActualSetWidth()){ + case FXFont::UltraCondensed: wid="Ultra Condensed"; break; + case FXFont::ExtraCondensed: wid="Extra Condensed"; break; + case FXFont::Condensed: wid="Condensed"; break; + case FXFont::SemiCondensed: wid="Semi Condensed"; break; + case FXFont::NonExpanded: wid=NULL; break; + case FXFont::SemiExpanded: wid="Semi Expanded"; break; + case FXFont::Expanded: wid="Expanded"; break; + case FXFont::ExtraExpanded: wid="Extra Expanded"; break; + case FXFont::UltraExpanded: wid="Ultra Expanded"; break; + default: wid=NULL; break; + } + + switch(font->getActualWeight()){ + case FXFont::Thin : wgt=fxtr("Thin"); break; + case FXFont::ExtraLight: wgt=fxtr("Extra Light"); break; + case FXFont::Light : wgt=fxtr("Light"); break; + case FXFont::Normal : wgt=NULL; break; + case FXFont::Medium : wgt=fxtr("Medium"); break; + case FXFont::DemiBold : wgt=fxtr("Demibold"); break; + case FXFont::Bold : wgt=fxtr("Bold"); break; + case FXFont::ExtraBold : wgt=fxtr("Extra Bold"); break; + case FXFont::Black : wgt=fxtr("Heavy"); break; + default: wgt=NULL; break; + } + + switch(font->getActualSlant()){ + case FXFont::ReverseOblique: slt="Reverse Oblique"; break; + case FXFont::ReverseItalic: slt="Reverse Italic"; break; + case FXFont::Straight: slt=NULL; break; + case FXFont::Italic: slt="Italic"; break; + case FXFont::Oblique: slt="Oblique"; break; + default: slt=NULL; break; + } + + if (wgt && slt && wid) + name+=GMStringFormat(", %s %s %s, %d",wgt,wid,slt,size); + else if (wgt && slt) + name+=GMStringFormat(", %s %s, %d",wgt,slt,size); + else if (wgt && wid) + name+=GMStringFormat(", %s %s, %d",wgt,wid,size); + else if (wid && slt) + name+=GMStringFormat(", %s %s, %d",wid,slt,size); + else if (slt) + name+=GMStringFormat(", %s, %d",slt,size); + else if (wgt) + name+=GMStringFormat(", %s, %d",wgt,size); + else if (wid) + name+=GMStringFormat(", %s, %d",wid,size); + else + name+=GMStringFormat(", %d",size); + } + +long GMPreferencesDialog::onUpdFont(FXObject*sender,FXSelector,void*){ + FXString fontname; + FXFont * font = FXApp::instance()->getNormalFont(); + fancyfontname(font,fontname); + sender->tryHandle(this,FXSEL(SEL_COMMAND,FXWindow::ID_SETSTRINGVALUE),&fontname); + return 1; + } diff --git a/src/GMPreferencesDialog.h b/src/GMPreferencesDialog.h new file mode 100644 index 0000000..e70e5a2 --- /dev/null +++ b/src/GMPreferencesDialog.h @@ -0,0 +1,131 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#ifndef GMPREFERENCESDIALOG_H +#define GMPREFERENCESDIALOG_H + +class GMPreferencesDialog : public FXDialogBox { +FXDECLARE(GMPreferencesDialog) +protected: + FXDataTarget target_closeishide; + FXDataTarget target_keywords; + FXDataTarget target_close_audio; + FXDataTarget target_pause_close_device; + FXDataTarget target_gapless; + FXDataTarget target_replaygain; + FXDataTarget target_open_device_on_startup; + FXDataTarget target_show_playing_albumcover; + FXDataTarget target_show_albumcovers; +#ifdef HAVE_DBUS + FXDataTarget target_dbus_notify_daemon; +#endif + FXDataTarget target_gui_tray_icon; + FXDataTarget target_gui_show_playing_titlebar; + FXDataTarget target_gui_format_title; +protected: + FXString keywords; +public: + FXTextField * lastfm_username; + FXTextField * lastfm_password; + FXLabel * lastfm_password_label; + FXLabel * lastfm_username_label; + FXCheckButton * lastfm_scrobble; + FXButton * lastfm_join; + FXTextField * iconthemedir; + FXCheckButton * check_audio_normalization; + FXCheckButton * browser_icons; + FXCheckButton * statusbarbutton; + FXCheckButton * toolbar_bigicons; + FXCheckButton * toolbar_showlabels; + FXList * colorpresets; + GMListBox * toolbar_docktop; + GMListBox * driverlist; + GMListBox * themelist; + GMListBox * lastfm_service; + FXbool password_set; + FXFont * selectedfont; + FXint theme; + ColorTheme current; + ColorTheme selected; + +public: + enum { + ID_LASTFM_USERNAME= FXDialogBox::ID_LAST, + ID_LASTFM_PASSWORD, + ID_LASTFM_SCROBBLE, + ID_LASTFM_SERVICE, + ID_LASTFM_JOIN, + ID_BASE_COLOR, + ID_BACK_COLOR, + ID_FORE_COLOR, + ID_SHADOW_COLOR, + ID_HILITE_COLOR, + ID_SEL_BACK_COLOR, + ID_SEL_FORE_COLOR, + ID_MENU_BACK_COLOR, + ID_MENU_FORE_COLOR, + ID_TIP_BACK_COLOR, + ID_TIP_FORE_COLOR, + ID_SOURCE_BACK_COLOR, + ID_SOURCE_FORE_COLOR, + ID_PLAY_BACK_COLOR, + ID_PLAY_FORE_COLOR, + ID_ALTERNATIVE_BACK_COLOR, + ID_TRAY_COLOR, + ID_BORDER_COLOR, + ID_COLOR_THEME, + ID_FONT, + ID_CHANGE_FONT, + ID_AUDIO_DRIVER, + ID_REPLAY_GAIN, + ID_ICON_THEME, + }; +public: + long onCmdLastFMScrobble(FXObject*,FXSelector,void*); + long onCmdLastFMUserName(FXObject*,FXSelector,void*); + long onCmdLastFMPassWord(FXObject*,FXSelector,void*); + long onFocusLastFMPassWord(FXObject*,FXSelector,void*); + long onCmdLastFMService(FXObject*,FXSelector,void*); + long onCmdLastFMJoin(FXObject*,FXSelector,void*); + long onCmdElementColor(FXObject*,FXSelector,void*); + long onUpdElementColor(FXObject*,FXSelector,void*); + long onCmdColorTheme(FXObject*,FXSelector,void*); + long onUpdColorTheme(FXObject*,FXSelector,void*); + long onUpdFont(FXObject*,FXSelector,void*); + long onCmdChangeFont(FXObject*,FXSelector,void*); + long onCmdAccept(FXObject*,FXSelector,void*); + long onCmdAudioDriver(FXObject*,FXSelector,void*); + long onCmdReplayGain(FXObject*,FXSelector,void*); + long onCmdIconTheme(FXObject*,FXSelector,void*); +protected: + GMPreferencesDialog(){} +private: + GMPreferencesDialog(const GMPreferencesDialog&); + GMPreferencesDialog &operator=(const GMPreferencesDialog&); +public: + GMPreferencesDialog(FXWindow * p); + + void initColorThemes(); + void updateColorThemes(); + void updateColors(); + void updateFonts(); + void redraw(); + + virtual ~GMPreferencesDialog(); + }; +#endif diff --git a/src/GMQuery.cpp b/src/GMQuery.cpp new file mode 100644 index 0000000..b810efc --- /dev/null +++ b/src/GMQuery.cpp @@ -0,0 +1,335 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#include "gmdefs.h" + +// Index out of range +const FXchar FXExecuteException::exceptionName[]="Query Execute Failed"; +const FXchar FXCompileException::exceptionName[]="Query Compile Failed"; +const FXchar FXQueryException::exceptionName[]="Query Exception"; + +GMQuery::GMQuery() : statement(NULL) { + } + +GMQuery::GMQuery(GMDatabase * database,const FXString & q) : statement(NULL) { + compile(database,q); + } + +void GMQuery::compile(GMDatabase * database,const FXString & q) { + if (statement) { + sqlite3_finalize((sqlite3_stmt*)statement); + statement=NULL; + } + +#if SQLITE_VERSION_NUMBER > 3003011 + FXint result = sqlite3_prepare_v2(database->db,q.text(),-1,&statement,NULL); +#else + FXint result = sqlite3_prepare(database->db,q.text(),-1,&statement,NULL); +#endif + if (result!=SQLITE_OK){ +#ifdef DEBUG + fxmessage("GMQuery::compile failed (%d): %s\n",result,q.text()); + fxmessage("Reason: %s\n",sqlite3_errmsg(database->db)); +#endif + throw FXCompileException(); + } + } + + +void GMQuery::compile(GMDatabase & database,const FXString & q) { + if (statement) { + sqlite3_finalize((sqlite3_stmt*)statement); + statement=NULL; + } + +#if SQLITE_VERSION_NUMBER > 3003011 + FXint result = sqlite3_prepare_v2(database.db,q.text(),-1,&statement,NULL); +#else + FXint result = sqlite3_prepare(database.db,q.text(),-1,&statement,NULL); +#endif + if (result!=SQLITE_OK){ +#ifdef DEBUG + fxmessage("GMQuery::compile failed (%d): %s\n",result,q.text()); + fxmessage("Reason: %s\n",sqlite3_errmsg(database.db)); +#endif + throw FXCompileException(); + } + } + +FXbool GMQuery::execute() { + if (statement) { + FXint result = sqlite3_step(statement); + switch(result) { + case SQLITE_DONE: return false; break; + case SQLITE_ROW : return true; break; + default : reset(); throw FXExecuteException(); break; + } + return false; + } + return false; + } + +FXbool GMQuery::perform() { + if (statement) { + FXint result; + while(1) { + result = sqlite3_step(statement); + switch(result) { + case SQLITE_DONE: return true; break; + case SQLITE_ROW : continue; break; + default : reset(); throw FXExecuteException(); break; + } + } + } + return true; + } + + +void GMQuery::clear(){ + if (statement) { + sqlite3_finalize((sqlite3_stmt*)statement); + statement=NULL; + } + } + + +/// Return number of columns the query returns +FXint GMQuery::getNumFields() const { + if (statement) + return sqlite3_column_count((sqlite3_stmt*)statement); + else + return 0; + } + +FXint GMQuery::getNumData() const { + if (statement) + return sqlite3_data_count((sqlite3_stmt*)statement); + else + return 0; + } + + +/// Return the specified column name the query returned. +FXString GMQuery::getFieldName(FXint i){ + if (statement) + return sqlite3_column_name((sqlite3_stmt*)statement,i); + else + return FXString::null; + } + +/// Return the column type for the specified column +FXFieldType GMQuery::getFieldType(FXint i){ + const FXchar * type=NULL; + if (statement) { + type = sqlite3_column_decltype((sqlite3_stmt*)statement,i); + if (type==NULL) + return TYPE_NULL; + else if (comparecase("INTEGER",type)==0) + return TYPE_INTEGER; + else if (comparecase("FLOAT",type)==0) + return TYPE_FLOAT; + else if (comparecase("TEXT",type)==0) + return TYPE_TEXT; + else if (comparecase("BLOB",type)==0) + return TYPE_BLOB; + else + return TYPE_NULL; + } + else { + return TYPE_NULL; + } + } + + + +void GMQuery::setParameter(FXint p,FXint v){ + if (!statement) fxerror("GMQuery::setParameter called without compiled query\n"); + sqlite3_bind_int((sqlite3_stmt*)statement,(p+1),v); + } + +void GMQuery::setParameter(FXint p,FXuint v){ + if (!statement) fxerror("GMQuery::setParameter called without compiled query\n"); + sqlite3_bind_int((sqlite3_stmt*)statement,(p+1),(FXint)v); + } + +void GMQuery::setParameter(FXint p,FXlong v){ + if (!statement) fxerror("GMQuery::setParameter called without compiled query\n"); + sqlite3_bind_int64((sqlite3_stmt*)statement,(p+1),v); + } + +void GMQuery::setParameter(FXint p,FXdouble v){ + if (!statement) fxerror("GMQuery::setParameter called without compiled query\n"); + sqlite3_bind_double((sqlite3_stmt*)statement,(p+1),v); + } + +void GMQuery::setParameter(FXint p,FXfloat v){ + if (!statement) fxerror("GMQuery::setParameter called without compiled query\n"); + sqlite3_bind_double((sqlite3_stmt*)statement,(p+1),v); + } + +void GMQuery::setParameter(FXint p,const FXString & text){ + if (!statement) fxerror("GMQuery::setParameter called without compiled query\n"); + sqlite3_bind_text((sqlite3_stmt*)statement,(p+1),text.text(),-1,SQLITE_TRANSIENT); + } + +FXint GMQuery::getNumParameters() { + if (!statement) fxerror("GMQuery::getNumParameters called without compiled query\n"); + return sqlite3_bind_parameter_count((sqlite3_stmt*)statement); + } + +FXint GMQuery::getParameterIndex(const FXString & name){ + if (!statement) fxerror("GMQuery::getParameterIndex called without compiled query\n"); + FXint index = sqlite3_bind_parameter_index((sqlite3_stmt*)statement,name.text()); + if (index==0) return -1; + return (index-1); + } + +FXString GMQuery::getParameterName(FXint p){ + if (!statement) fxerror("GMQuery::getParameterName called without compiled query\n"); + + const FXchar * name = sqlite3_bind_parameter_name((sqlite3_stmt*)statement,p+1); + if (name==NULL) + return "?"; + else + return name; + } + + +void GMQuery::reset() { + if (statement) { + sqlite3_reset(statement); + } + } + +void GMQuery::getResult(FXint column,FXString & v){ + if (getNumData()) + v = (const FXchar *) sqlite3_column_text((sqlite3_stmt*)statement,column); + } + +void GMQuery::getResult(FXint column,FXint & v){ + if (getNumData()) + v = sqlite3_column_int((sqlite3_stmt*)statement,column); + } + +void GMQuery::getResult(FXint column,FXuint & v){ + if (getNumData()) + v = (FXuint) sqlite3_column_int((sqlite3_stmt*)statement,column); + } + +void GMQuery::getResult(FXint column,FXdouble & v){ + if (getNumData() && sqlite3_column_type((sqlite3_stmt*)statement,column)==SQLITE_FLOAT){ + v=sqlite3_column_double((sqlite3_stmt*)statement,column); + } + else { + v=NAN; + } + } + +void GMQuery::getResult(FXint column,FXfloat & v){ + if (getNumData() && sqlite3_column_type((sqlite3_stmt*)statement,column)==SQLITE_FLOAT){ + v=sqlite3_column_double((sqlite3_stmt*)statement,column); + } + else { + v=NAN; + } + } + + +void GMQuery::getResult(FXint column,FXlong & v){ + if (getNumData()) + v = sqlite3_column_int64((sqlite3_stmt*)statement,column); + } + + +const FXchar * GMQuery::getResult(FXint column){ + if (!getNumData()) return NULL; + return (const FXchar *) sqlite3_column_text((sqlite3_stmt*)statement,column); + } + +void GMQuery::getResult(FXint column,FXbool & v){ + if (getNumData()) { + FXint i; + i = sqlite3_column_int((sqlite3_stmt*)statement,column); + v = (i==0) ? FALSE : TRUE; + } + } + +GMQuery::~GMQuery(){ + clear(); + } + + +/* +void fillTable(GMQuery * query,FXTable * table){ + FXint integer; + FXfloat real; + FXString text; + FXint rows=0; + FXint ncols=0; + table.clearItems(); + + if (query.execute()) { + ncols=query->getNumFields(); + table->setTableSize(1,ncols); + for (FXint i=0;isetColumnText(i,query->getFieldName()); + switch(query->getFieldType()){ + case TYPE_INTEGER : + query->getResult(i,integer); + table->setItemTex(rows,i,FXStringVal(integer)); + break; + case TYPE_FLOAT : + query->getResult(i,real); + table->setItemText(rows,i,FXStringVal(real)); + break; + case TYPE_TEXT : + query->getResult(i,text); + table->setItemText(rows,i,text); + break; + } + } + rows++; + } + while(query.execute()){ + table->insertRows(rows,1); + for (FXint i=0;isetColumnText(i,query->getFieldName()); + switch(query->getFieldType()){ + case TYPE_INTEGER : + query->getResult(i,integer); + table->setItemTex(rows,i,FXStringVal(integer)); + break; + case TYPE_FLOAT : + query->getResult(i,real); + table->setItemText(rows,i,FXStringVal(real)); + break; + case TYPE_TEXT : + query->getResult(i,text); + table->setItemText(rows,i,text); + break; + } + } + rows++; + } + } +*/ + + + + + diff --git a/src/GMQuery.h b/src/GMQuery.h new file mode 100644 index 0000000..368b672 --- /dev/null +++ b/src/GMQuery.h @@ -0,0 +1,153 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#ifndef GMQUERY_H +#define GMQUERY_H + +class FXCompileException : public FXErrorException { +private: + static const FXchar exceptionName[]; +public: + FXCompileException():FXErrorException(FXCompileException::exceptionName){} + FXCompileException(const FXchar *msg):FXErrorException(msg){} + }; + + +class FXExecuteException : public FXErrorException { +private: + static const FXchar exceptionName[]; +public: + FXExecuteException():FXErrorException(FXExecuteException::exceptionName){} + FXExecuteException(const FXchar *msg):FXErrorException(msg){} + }; + +class FXQueryException : public FXErrorException { +private: + static const FXchar exceptionName[]; +public: + FXQueryException():FXErrorException(FXQueryException::exceptionName){} + FXQueryException(const FXchar *msg):FXErrorException(msg){} + }; + +enum FXFieldType { + TYPE_INTEGER=0, + TYPE_FLOAT, + TYPE_TEXT, + TYPE_BLOB, + TYPE_NULL, + }; + +class GMDatabase; + +class GMQuery{ +private: + GMDatabaseStatement * statement; +public: + /// Create a empty query + GMQuery(); + + /// Create and compile a query. Throws FXCompileException if query couldn't be compiled + GMQuery(GMDatabase * database,const FXString & query); + + /// Create a query. Delete Old Query. Throws FXCompileException if query couldn't be compiled + void compile(GMDatabase & database,const FXString & query); + + /// Create a query. Delete Old Query. Throws FXCompileException if query couldn't be compiled + void compile(GMDatabase * database,const FXString & query); + + /// Set Integer Parameter + void setParameter(FXint p,FXint v); + + /// Set Integer Parameter + void setParameter(FXint p,FXuint v); + + /// Set Double Parameter + void setParameter(FXint p,FXfloat v); + + /// Set Double Parameter + void setParameter(FXint p,FXdouble v); + + /// Set Long Parameter + void setParameter(FXint p,FXlong v); + + /// Set String Parameter + void setParameter(FXint p,const FXString & text); + + /// Get Num Parameters in Query + FXint getNumParameters(); + + /// Get Index for given parameter. Returns -1 if not found + FXint getParameterIndex(const FXString & name); + + /// Get Name of Parameter + FXString getParameterName(FXint p); + + /// Return number of fields the query returns + FXint getNumFields() const; + + /// Returns num columns of data returned by query. + FXint getNumData() const; + + /// Return the specified column name the query returned. + FXString getFieldName(FXint i); + + /// Return the column type for the specified column + FXFieldType getFieldType(FXint i); + + /// Get Text for Column + void getResult(FXint column,FXString & v); + + /// Get Integer for Column + void getResult(FXint column,FXint & v); + + /// Get Integer for Column + void getResult(FXint column,FXuint & v); + + /// Get Double for Column + void getResult(FXint column,FXdouble & v); + + /// Get Float for Column + void getResult(FXint column,FXfloat & v); + + /// Get Bool for Column + void getResult(FXint column,FXbool & v); + + /// Get Long for Column + void getResult(FXint column,FXlong & v); + + + const FXchar * getResult(FXint column); + + /// Execute Query. Returns TRUE if there are any results, FALSE when done and throws a FXExecuteException if there is an error. + FXbool execute(); + + FXbool perform(); + + /// Reset the Query + void reset(); + + /// Clear the Query + void clear(); + + /// Destructor + ~GMQuery(); + }; + + +#endif + diff --git a/src/GMRemote.cpp b/src/GMRemote.cpp new file mode 100644 index 0000000..e5e4685 --- /dev/null +++ b/src/GMRemote.cpp @@ -0,0 +1,262 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#include "icons.h" +#include "gmdefs.h" +#include +#include +#include "GMRemote.h" +#include "GMWindow.h" +#include "GMList.h" +#include "GMSource.h" +#include "GMPlayerManager.h" +#include "GMIconTheme.h" + + +// Map +FXDEFMAP(GMRemote) GMRemoteMap[]={ + FXMAPFUNC(SEL_UPDATE, GMRemote::ID_VOLUME_BUTTON, GMRemote::onUpdVolumeButton), + FXMAPFUNC(SEL_COMMAND, GMRemote::ID_VOLUME_SLIDER, GMRemote::onCmdVolume), + FXMAPFUNC(SEL_CHANGED, GMRemote::ID_VOLUME_SLIDER, GMRemote::onCmdVolume), + FXMAPFUNC(SEL_MOUSEWHEEL, GMRemote::ID_VOLUME_BUTTON, GMRemote::onCmdVolumeButton), + FXMAPFUNC(SEL_MOUSEWHEEL, 0, GMRemote::onCmdVolumeButton), + }; + +// Implementation +FXIMPLEMENT(GMRemote,FXMainWindow,GMRemoteMap,ARRAYNUMBER(GMRemoteMap)) + +GMRemote::GMRemote(FXApp* a,FXObject * tgt,FXSelector msg):FXMainWindow(a,"Goggles Music Manager",NULL,NULL,DECOR_BORDER|DECOR_TITLE|DECOR_CLOSE|DECOR_STRETCHABLE,0,0,0,0,3,3,3,3,3,3){ + flags|=FLAG_ENABLED; + setTarget(tgt); + setSelector(msg); + + setIcon(GMIconTheme::instance()->icon_applogo); + setMiniIcon(GMIconTheme::instance()->icon_applogo_small); + + GMIconTheme::instance()->loadSmall(icon_home,"go-home",FXApp::instance()->getBaseColor()); + +#if FOXVERSION < FXVERSION(1,7,17) + FXFontDesc fontdescription; + getApp()->getNormalFont()->getFontDesc(fontdescription); +#else + FXFontDesc fontdescription = getApp()->getNormalFont()->getFontDesc(); +#endif + fontdescription.weight = FXFont::Bold; + fontdescription.size += 10; + font_title = new FXFont(getApp(),fontdescription); + font_title->create(); + + img_default = new FXPNGImage(getApp(),about_png); + img_default->scale(64,64,1); + img_default->blend(getApp()->getBackColor()); + img_default->create(); + + cover_label = new FXImageFrame(this,img_default,LAYOUT_SIDE_LEFT|FRAME_SUNKEN|LAYOUT_FIX_WIDTH|JUSTIFY_CENTER_X|JUSTIFY_CENTER_Y|LAYOUT_FILL_Y,0,0,64,64); + cover_label->setBackColor(getApp()->getBackColor()); + + /// Popup Volume Menu + volumecontrol = new FXPopup(this,POPUP_VERTICAL|FRAME_RAISED|FRAME_THICK|POPUP_SHRINKWRAP); + volumeslider = new FXSlider(volumecontrol,this,GMRemote::ID_VOLUME_SLIDER,LAYOUT_FIX_HEIGHT|LAYOUT_FIX_WIDTH|SLIDER_VERTICAL|SLIDER_TICKS_RIGHT|SLIDER_TICKS_LEFT|SLIDER_INSIDE_BAR,0,0,20,100); + volumeslider->setTickDelta(10); + volumeslider->setRange(0,100); + volumeslider->setIncrement(10); + + FXHorizontalFrame * buttons = new FXHorizontalFrame(this,LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X,0,0,0,0,3,3,0,0); + new FXButton(buttons,tr("\tShow Browser\tShow Browser"),icon_home,GMPlayerManager::instance(),GMPlayerManager::ID_SHOW_MANAGER,BUTTON_TOOLBAR|FRAME_RAISED|ICON_ABOVE_TEXT); + new FXVerticalSeparator(buttons,LAYOUT_FILL_Y|SEPARATOR_GROOVE); + new FXButton(buttons,tr("\tStart Playback\tStart Playback"),GMIconTheme::instance()->icon_play,GMPlayerManager::instance()->getMainWindow(),GMWindow::ID_PLAYPAUSEMENU,BUTTON_TOOLBAR|FRAME_RAISED|ICON_ABOVE_TEXT); + new FXButton(buttons,tr("\tStop Playback\tStop Playback"),GMIconTheme::instance()->icon_stop,GMPlayerManager::instance()->getMainWindow(),GMWindow::ID_STOP,BUTTON_TOOLBAR|FRAME_RAISED|ICON_ABOVE_TEXT); + new FXVerticalSeparator(buttons,LAYOUT_FILL_Y|SEPARATOR_GROOVE); + new FXButton(buttons,tr("\tPlay Previous Track\tPlay previous track."),GMIconTheme::instance()->icon_prev,GMPlayerManager::instance()->getMainWindow(),GMWindow::ID_PREV,BUTTON_TOOLBAR|FRAME_RAISED|ICON_ABOVE_TEXT); + new FXButton(buttons,tr("\tPlay Next Track\tPlay next track."),GMIconTheme::instance()->icon_next,GMPlayerManager::instance()->getMainWindow(),GMWindow::ID_NEXT,BUTTON_TOOLBAR|FRAME_RAISED|ICON_ABOVE_TEXT); + new FXVerticalSeparator(buttons,LAYOUT_FILL_Y|SEPARATOR_GROOVE); + time_label =new FX7Segment(buttons,"--:--",SEVENSEGMENT_SHADOW|LAYOUT_CENTER_Y); + time_label->setCellWidth(10); + time_label->setCellHeight(15); + new FXVerticalSeparator(buttons,LAYOUT_FILL_Y|SEPARATOR_GROOVE); + volumebutton = new FXMenuButton(buttons,tr("\tAdjust Volume\tAdjust Volume"),NULL,volumecontrol,MENUBUTTON_NOARROWS|MENUBUTTON_ATTACH_LEFT|MENUBUTTON_UP|MENUBUTTON_TOOLBAR|FRAME_RAISED|LAYOUT_CENTER_Y); + volumebutton->setTarget(this); + volumebutton->setSelector(ID_VOLUME_BUTTON); + new FXSeparator(this,LAYOUT_SIDE_BOTTOM|SEPARATOR_GROOVE|LAYOUT_FILL_X); + + FXVerticalFrame * info = new FXVerticalFrame(this,LAYOUT_CENTER_Y|FRAME_NONE|LAYOUT_FILL_X,0,0,0,0,2,2,2,2,0,0); + title_label = new FXTextField(info,20,NULL,0,FRAME_NONE|TEXTFIELD_READONLY,0,0,0,0,0,0,0,0); + title_label->setBackColor(getApp()->getBaseColor()); + title_label->setFont(font_title); + title_label->setDefaultCursor(getApp()->getDefaultCursor(DEF_ARROW_CURSOR)); + title_label->setDragCursor(getApp()->getDefaultCursor(DEF_ARROW_CURSOR)); + title_label->disable(); + + artistalbum_label = new FXTextField(info,30,NULL,0,FRAME_NONE|TEXTFIELD_READONLY,0,0,0,0,0,0,0,0); + artistalbum_label->setBackColor(getApp()->getBaseColor()); + artistalbum_label->setDefaultCursor(getApp()->getDefaultCursor(DEF_ARROW_CURSOR)); + + getAccelTable()->addAccel(parseAccel("F11"),GMPlayerManager::instance(),FXSEL(SEL_COMMAND,GMPlayerManager::ID_SHOW_MANAGER)); + getAccelTable()->addAccel(parseAccel("Ctrl-M"),GMPlayerManager::instance(),FXSEL(SEL_COMMAND,GMPlayerManager::ID_SHOW_MANAGER)); + getAccelTable()->addAccel(parseAccel("Ctrl-W"),this,FXSEL(SEL_CLOSE,0)); + getAccelTable()->addAccel(parseAccel("Ctrl-Q"),GMPlayerManager::instance()->getMainWindow(),FXSEL(SEL_COMMAND,GMWindow::ID_QUIT)); + + getAccelTable()->addAccel(parseAccel("Ctrl-P"),GMPlayerManager::instance()->getMainWindow(),FXSEL(SEL_COMMAND,GMWindow::ID_PLAYPAUSEMENU)); + getAccelTable()->addAccel(parseAccel("Ctrl-\\"),GMPlayerManager::instance()->getMainWindow(),FXSEL(SEL_COMMAND,GMWindow::ID_STOP)); + getAccelTable()->addAccel(parseAccel("Ctrl-["),GMPlayerManager::instance()->getMainWindow(),FXSEL(SEL_COMMAND,GMWindow::ID_PREV)); + getAccelTable()->addAccel(parseAccel("Ctrl-]"),GMPlayerManager::instance()->getMainWindow(),FXSEL(SEL_COMMAND,GMWindow::ID_NEXT)); + + GMIconTheme::instance()->loadSmall(icon_volume_high,"audio-volume-high",FXApp::instance()->getBaseColor()); + GMIconTheme::instance()->loadSmall(icon_volume_medium,"audio-volume-medium",FXApp::instance()->getBaseColor()); + GMIconTheme::instance()->loadSmall(icon_volume_low,"audio-volume-low",FXApp::instance()->getBaseColor()); + GMIconTheme::instance()->loadSmall(icon_volume_muted,"audio-volume-muted",FXApp::instance()->getBaseColor()); + + + reset(); + + if (GMPlayerManager::instance()->getMainWindow()->getSmallCover()) + cover_label->setImage(GMPlayerManager::instance()->getMainWindow()->getSmallCover()); + + update_volume_display(GMPlayerManager::instance()->volume()); + } + +// Destroy main window +GMRemote::~GMRemote(){ + volumeslider->setTarget(NULL); + volumeslider->setSelector(0); + volumebutton->setMenu(NULL); + } + +void GMRemote::writeRegistry(){ + if (shown()) { + getApp()->reg().writeIntEntry("window","remote-x",getX()); + getApp()->reg().writeIntEntry("window","remote-y",getY()); + getApp()->reg().writeIntEntry("window","remote-width",getWidth()); + getApp()->reg().writeIntEntry("window","remote-height",getHeight()); + } + } + +void GMRemote::updateCover(FXImage * cover) { + if (cover==NULL) { + cover_label->setImage(img_default); + } + else { + cover_label->setImage(cover); + } + } + + +void GMRemote::display(const GMTrack & track){ + FXString tip = GMStringFormat("%s\n%s\n%s (%d)",track.title.text(),track.artist.text(),track.album.text(),track.year); + + title_label->setText(track.title); + title_label->setJustify(JUSTIFY_LEFT); + title_label->setCursorPos(0); + title_label->setAnchorPos(0); + title_label->makePositionVisible(0); + title_label->setTipText(tip); + + artistalbum_label->setText("by " + track.artist + " from " + track.album); + artistalbum_label->show(); + artistalbum_label->setCursorPos(0); + artistalbum_label->setAnchorPos(0); + artistalbum_label->makePositionVisible(0); + artistalbum_label->setTipText(tip); + recalc(); + } + +void GMRemote::reset(){ + title_label->setText("Goggles Music Manager"); + title_label->setJustify(JUSTIFY_CENTER_X); + title_label->setLayoutHints(LAYOUT_CENTER_Y|LAYOUT_FILL_X); + title_label->setTipText(FXString::null); + artistalbum_label->setText(FXString::null); + artistalbum_label->setTipText(FXString::null); + artistalbum_label->hide(); + time_label->setText("--:--"); + recalc(); + layout(); + } + +void GMRemote::elapsed_time(FXint hours,FXint minutes,FXint seconds,FXint,FXbool playing){ + if (playing) { + if (hours>0) + time_label->setText(GMStringFormat("%d:%.2d:%.2d",hours,minutes,seconds)); + else + time_label->setText(GMStringFormat("%.2d:%.2d",minutes,seconds)); + } + else { + time_label->setText("--:--"); + } + } + +void GMRemote::update_volume_display(FXint level) { + if (level<=0) + volumebutton->setIcon(icon_volume_muted); + else if (level<=33) + volumebutton->setIcon(icon_volume_low); + else if (level<=66) + volumebutton->setIcon(icon_volume_medium); + else + volumebutton->setIcon(icon_volume_high); + + volumeslider->setValue(level); + } + + +// Create and show window +void GMRemote::create(){ + FXMainWindow::create(); + if (getApp()->reg().readIntEntry("window","remote-x",-1)!=-1) { + FXint xx=getApp()->reg().readIntEntry("window","remote-x",getX()); + FXint yy=getApp()->reg().readIntEntry("window","remote-y",getY()); + if (getApp()->reg().readIntEntry("window","remote-width",-1)!=-1) { + FXint ww=getApp()->reg().readIntEntry("window","remote-width",getDefaultWidth()); + FXint hh=getApp()->reg().readIntEntry("window","remote-height",getDefaultHeight()); + position(xx,yy,ww,hh); + } + else { + move(xx,yy); + } + } + else { + place(PLACEMENT_SCREEN); + } + gm_set_application_icon(this); + } + +bool GMRemote::doesOverrideRedirect() const { return FALSE; } + + +long GMRemote::onCmdVolume(FXObject*,FXSelector,void*ptr){ + FXint level = (FXint)(FXival)ptr; + GMPlayerManager::instance()->volume(level); + GMPlayerManager::instance()->getMainWindow()->update_volume_display(level); + update_volume_display(level); + return 1; + } + +long GMRemote::onCmdVolumeButton(FXObject*,FXSelector sel,void*ptr){ + volumeslider->handle(this,FXSEL(FXSELTYPE(sel),0),ptr); + return 1; + } + +long GMRemote::onUpdVolumeButton(FXObject*sender,FXSelector,void*){ + if (GMPlayerManager::instance()->audio_device_opened()) + sender->handle(this,FXSEL(SEL_COMMAND,ID_ENABLE),NULL); + else + sender->handle(this,FXSEL(SEL_COMMAND,ID_DISABLE),NULL); + return 1; + } + + diff --git a/src/GMRemote.h b/src/GMRemote.h new file mode 100644 index 0000000..a175fe9 --- /dev/null +++ b/src/GMRemote.h @@ -0,0 +1,77 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#ifndef GMREMOTE_H +#define GMREMOTE_H + +class GMWindow; + +class GMRemote : public FXMainWindow { +FXDECLARE(GMRemote) +private: + FXTextField * title_label; + FXTextField * artistalbum_label; + FX7Segment * time_label; + FXImageFrame* cover_label; + FXPopupPtr volumecontrol; + FXMenuButton* volumebutton; + FXSlider * volumeslider; + FXFontPtr font_title; + FXImagePtr img_default; + FXIconPtr icon_volume_high; + FXIconPtr icon_volume_medium; + FXIconPtr icon_volume_low; + FXIconPtr icon_volume_muted; + FXIconPtr icon_home; +protected: + virtual bool doesOverrideRedirect() const; + GMRemote() {} +public: + long onCmdVolume(FXObject*,FXSelector,void*); + long onCmdVolumeButton(FXObject*,FXSelector,void*); + long onUpdVolumeButton(FXObject*,FXSelector,void*); +public: + enum { + ID_VOLUME_SLIDER = FXMainWindow::ID_LAST, + ID_VOLUME_BUTTON, + }; +public: + /// Construct Remote Window + GMRemote(FXApp* a,FXObject*,FXSelector); + + void updateCover(FXImage * img); + + // Update Display + void display(const GMTrack & track); + + void elapsed_time(FXint h,FXint m,FXint s,FXint p,FXbool playing); + + void update_volume_display(FXint l); + + void reset(); + + void writeRegistry(); + + /// Create + virtual void create(); + + /// Destroy calculator + virtual ~GMRemote(); + }; +#endif + diff --git a/src/GMSearch.cpp b/src/GMSearch.cpp new file mode 100644 index 0000000..6198cbf --- /dev/null +++ b/src/GMSearch.cpp @@ -0,0 +1,674 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#include + +#include +#include "gmdefs.h" +#include "GMThread.h" +#include "GMTrackDatabase.h" +#include "GMSearch.h" +#include "GMTag.h" +#include "GMFilename.h" +#include "GMIconTheme.h" +#include "GMAnimImage.h" +#include "icons.h" + +#if APPLICATION_BETA_DB > 0 +#define DATABASE_FILENAME "goggles_beta.db" +#else +#define DATABASE_FILENAME "goggles.db" +#endif + +struct GMThreadProgress { + GMThreadProgress(){} + virtual ~GMThreadProgress(){} + }; + +struct GMImportProgress : public GMThreadProgress { + FXint nfound; + FXint ntotal; + FXString dir; + FXString file; + GMImportProgress(); + GMImportProgress(const GMImportProgress&); + }; + + +class GMDatabaseThread : public GMThread { +protected: + GMImportOptions options_import; + GMTrackDatabase database; +public: + GMDatabaseThread(const GMImportOptions &,FXObject*); + ~GMDatabaseThread(); + + FXbool open(); + + void parse(const FXString & filename,FXint filenum,GMTrack &); + + void sendProgress(GMThreadProgress * p); + + void fixEmptyTags(GMTrack&,FXint dirnum=-1); + }; + + +class GMImportThread : public GMDatabaseThread { +protected: + GMImportProgress progress; + FXString topdir; + FXStringList files; + FXint playlist; + FXint lastid; +protected: + virtual FXint run(); + void import(); + void listDirectory(const FXString &); +public: + GMImportThread(const FXStringList & list,const GMImportOptions &,FXint playlist,FXObject*); + virtual ~GMImportThread(); + }; + + +class GMSyncThread : public GMImportThread { +protected: + GMSyncOptions options_sync; + FXint nchanged; + FXbool synctag; +protected: + virtual FXint run(); + void sync(); + void syncDirectory(const FXString &); +public: + GMSyncThread(const FXStringList & list,const GMImportOptions & io,const GMSyncOptions & so,FXint playlist,FXbool t,FXObject*); + virtual ~GMSyncThread(); + }; + + + + + + + + + + +GMDatabaseThread::GMDatabaseThread(const GMImportOptions & io,FXObject*tgt) : GMThread(tgt), options_import(io) { + if (options_import.default_field.trim().empty()) + options_import.default_field="Untitled"; + } + +GMDatabaseThread::~GMDatabaseThread(){ + } + + +void GMDatabaseThread::sendProgress(GMThreadProgress * p) { + feedback.message(target,FXSEL(SEL_COMMAND,GMThreadDialog::ID_THREAD_PROGRESS),&p,sizeof(GMThreadProgress*)); + } + +FXbool GMDatabaseThread::open() { + FXString dir = FXSystem::getHomeDirectory() + PATHSEPSTRING + ".goggles"; + FXString databasefilename = dir + PATHSEPSTRING + DATABASE_FILENAME; + if (!database.init(databasefilename)){ + const char * msg = fxtr("Unable to open the database"); + feedback.message(target,FXSEL(SEL_COMMAND,GMThreadDialog::ID_THREAD_ERROR),msg,sizeof(msg)); + running=false; + return false; + } + return true; + } + + +void GMDatabaseThread::parse(const FXString & filename,FXint filenum,GMTrack & info){ + info.mrl=filename; + switch(options_import.parse_method) { + case GMImportOptions::PARSE_TAG : info.loadTag(filename); + break; + case GMImportOptions::PARSE_FILENAME : GMFilename::parse(info,options_import.filename_template,(options_import.replace_underscores ? (GMFilename::OVERWRITE|GMFilename::REPLACE_UNDERSCORE) : (GMFilename::OVERWRITE))); + GMTag::length(info); + break; + case GMImportOptions::PARSE_BOTH : info.loadTag(filename); + if (info.title.empty() || + info.artist.empty() || + info.album.empty() || + info.album_artist.empty() || + info.genre.empty() ) { + GMFilename::parse(info,options_import.filename_template,(options_import.replace_underscores ? (GMFilename::REPLACE_UNDERSCORE) : (0))); + } + break; + } + fixEmptyTags(info,filenum); + } + + +void GMDatabaseThread::fixEmptyTags(GMTrack & track,FXint dirnum) { + + if (track.title.empty()) + track.title=options_import.default_field; + + if (track.album.empty()) + track.album=options_import.default_field; + + if (track.album_artist.empty()){ + if (track.artist.empty()) { + track.album_artist=options_import.default_field; + track.artist=options_import.default_field; + } + else { + track.album_artist=track.artist; + } + } + + if (track.artist.empty()) + track.artist=track.album_artist; + + if (track.genre.empty()) { + track.genre=options_import.default_field; + } + + if (options_import.track_from_filelist && dirnum>=0) + track.no=dirnum+1; + } + + + + +struct GMSyncProgress : public GMThreadProgress { + FXint current; + FXint total; + FXint ndeleted; + FXint nupdated; + + GMSyncProgress() : current(0),total(0), ndeleted(0), nupdated(0) {} + GMSyncProgress(FXint t) : current(0),total(t), ndeleted(0), nupdated(0) {} + GMSyncProgress(const GMSyncProgress& o) : GMThreadProgress(o), current(o.current), total(o.total), ndeleted(o.ndeleted), nupdated(o.nupdated) {} +/* + void load(FXStream & store){ + store >> current; + store >> total; + } + + void save(FXStream & store) const { + store << current; + store << total; + } +*/ + }; + + +GMSyncThread::GMSyncThread(const FXStringList & list,const GMImportOptions & io,const GMSyncOptions & so,FXint p, FXbool t,FXObject*tgt) : GMImportThread(list,io,p,tgt),options_sync(so),nchanged(0),synctag(t) { + } + +GMSyncThread::~GMSyncThread(){ + } + + +FXint GMSyncThread::run(){ + if (!open()) return -1; + + database.beginDelete(); + database.beginEdit(); + sync(); + if (options_sync.import_new && !options_sync.remove_all) + import(); + database.endEdit(false); + database.endDelete((nchanged>0)); + + if (running) + feedback.message(target,FXSEL(SEL_COMMAND,GMThreadDialog::ID_THREAD_DONE),NULL,0); + return 1; + } + +#if FOXVERSION < FXVERSION(1,7,0) +#define TO_NANO_SECONDS(x) ((FXlong)(1000000000LL*x)) +#else +#define TO_NANO_SECONDS(x) (x) +#endif + + +void GMSyncThread::syncDirectory(const FXString & dir) { + GMTrack info; + FXIntList ids; + FXLongList dates; + FXStringList filenames; + FXStat stat; + + if (!database.getTrackFilenames(ids,filenames,dates,dir)) { + const char * msg = fxtr("Database Error: Unable to retrieve all filenames."); + feedback.message(target,FXSEL(SEL_COMMAND,GMThreadDialog::ID_THREAD_ERROR),msg,sizeof(msg)); + running=false; + return; + } + + GMSyncProgress progress(ids.no()); + + if (options_sync.remove_all) { + for (FXint i=0;i dates[i])) { + parse(filenames[i],-1,info); + if (!database.updateTrack(ids[i],info)) { + const char * msg = fxtr("Unable to update track"); + feedback.message(target,FXSEL(SEL_COMMAND,GMThreadDialog::ID_THREAD_ERROR),msg,sizeof(msg)); + running=false; + } + progress.nupdated++; + nchanged++; + } + sendProgress(new GMSyncProgress(progress)); + } + } + + } + +void GMSyncThread::sync(){ + if (files.no()) { + for (FXint i=0;(ibegin(); + for (FXint i=0;(icommit(); + return; + } + + lastid=id; + if (playlist>=0) database.insertTrackInPlaylist(playlist,id); + } + } + } + } + database.database()->commit(); + } + + +FXint GMImportThread::run(){ + if (!open()) return 1; + import(); + if (running) + feedback.message(target,FXSEL(SEL_COMMAND,GMThreadDialog::ID_THREAD_DONE),NULL,0); + return 1; + } + + + +void GMImportThread::listDirectory(const FXString & dir) { + GMTrack info; +#if FOXVERSION < FXVERSION(1,7,20) + const FXuint matchflags=FILEMATCH_FILE_NAME|FILEMATCH_CASEFOLD|FILEMATCH_NOESCAPE; +#else + const FXuint matchflags=FXPath::PathName|FXPath::NoEscape|FXPath::CaseFold; +#endif + + + FXint id; + FXString * files=NULL; + + FXint no = FXDir::listFiles(files,dir,"*",FXDir::AllDirs|FXDir::NoParent|FXDir::NoFiles); + for (FXint i=0;(i=0) database.insertTrackInPlaylist(playlist,id); + } + } + delete [] files; + } + + + + +FXDEFMAP(GMThreadDialog) GMThreadDialogMap[]={ + FXMAPFUNC(SEL_CLOSE,0,GMThreadDialog::onCmdCancel), + FXMAPFUNC(SEL_COMMAND,FXDialogBox::ID_CANCEL,GMThreadDialog::onCmdCancel), + FXMAPFUNC(SEL_COMMAND,GMThreadDialog::ID_THREAD_DONE,GMThreadDialog::onThreadDone), + FXMAPFUNC(SEL_COMMAND,GMThreadDialog::ID_THREAD_ERROR,GMThreadDialog::onThreadError), + }; + +FXIMPLEMENT(GMThreadDialog,FXDialogBox,GMThreadDialogMap,ARRAYNUMBER(GMThreadDialogMap)) + +GMThreadDialog::GMThreadDialog() { + thread=NULL; + } + + +GMThreadDialog::GMThreadDialog(FXWindow* owner) : FXDialogBox(owner,"Goggles Music Manager",DECOR_TITLE|DECOR_BORDER,0,0,0,0,0,0,0,0,0,0), thread(NULL){ + FXIconSource source(getApp()); + animation_image = GMIconTheme::instance()->loadMedium("process-working.png"); + animation_size = GMIconTheme::instance()->getMediumSize(); + if (animation_image) { + gm_colorize_bitmap(animation_image,getApp()->getForeColor()); + animation_image->blend(getApp()->getBackColor()); + animation_image->create(); + } + } + +GMThreadDialog::~GMThreadDialog(){ + delete thread; + delete animation_image; + } + +/// Run modal invocation of the dialog +FXuint GMThreadDialog::execute(FXuint placement){ + FXASSERT(thread); + create(); + show(placement); + getApp()->refresh(); + thread->start(); + return getApp()->runModalFor(this); + } + +long GMThreadDialog::onThreadDone(FXObject*,FXSelector,void*){ + thread->join(); + getApp()->stopModal(this,FALSE); + hide(); + return 1; + } + +long GMThreadDialog::onThreadError(FXObject*,FXSelector,void*ptr){ + const FXchar * msg = (FXchar*)ptr; + thread->join(); + FXMessageBox::error(this,MBOX_OK,fxtr("Fatal Error"),fxtrformat("%s\nPlease contact support if this error keeps occuring."),msg); + getApp()->stopModal(this,false); + hide(); + return 1; + } + +long GMThreadDialog::onCmdCancel(FXObject*,FXSelector,void*){ + thread->dispose_and_join(); + getApp()->stopModal(this,FALSE); + hide(); + return 1; + } + + +FXDEFMAP(GMSyncDatabase) GMSyncDatabaseMap[]={ + FXMAPFUNC(SEL_COMMAND,GMSyncDatabase::ID_THREAD_PROGRESS,GMSyncDatabase::onThreadProgress), + }; + +FXIMPLEMENT(GMSyncDatabase,GMThreadDialog,GMSyncDatabaseMap,ARRAYNUMBER(GMSyncDatabaseMap)) + +GMSyncDatabase::GMSyncDatabase(){ + } + +GMSyncDatabase::GMSyncDatabase(FXWindow * owner,const FXStringList & files,const GMImportOptions & io,const GMSyncOptions & so,FXint playlist,FXbool synctags,FXFont * font) : GMThreadDialog(owner) { + thread=new GMSyncThread(files,io,so,playlist,synctags,this); + FXLabel*label; + FXHorizontalFrame * header = new FXHorizontalFrame(this,LAYOUT_FILL_X,0,0,0,0,0,20,0,0,0,0); + header->setBackColor(getApp()->getBackColor()); + FXVerticalFrame * frame = new FXVerticalFrame(header,LAYOUT_FILL_X,0,0,0,0,0,0,0,0,0,0); + label_title = new FXLabel(frame,tr("Updating Database..."),NULL,LAYOUT_FILL_X|JUSTIFY_LEFT|TEXT_AFTER_ICON,0,0,0,0,10,0,10,0); + label_title->setBackColor(getApp()->getBackColor()); + label_title->setFont(font); + label = new FXLabel(frame,tr("Please wait. This may take a while."),NULL,LAYOUT_FILL_X|JUSTIFY_LEFT|TEXT_AFTER_ICON,0,0,0,0,10+30,0,0,10); + label->setBackColor(getApp()->getBackColor()); + + if (animation_image) { + GMAnimImage *animation = new GMAnimImage(header,animation_image,animation_size,FRAME_NONE|LAYOUT_CENTER_Y); + animation->setBackColor(getApp()->getBackColor()); + } + + new FXSeparator(this,LAYOUT_FILL_X|SEPARATOR_GROOVE|LAYOUT_SIDE_TOP); + FXVerticalFrame * main = new FXVerticalFrame(this,LAYOUT_FILL_X|LAYOUT_FILL_Y,0,0,0,0,0,0,0,0,0,0); + contentswitcher = new FXSwitcher(main,LAYOUT_FILL_X|LAYOUT_FILL_Y); + + FXVerticalFrame * content = new FXVerticalFrame(contentswitcher,LAYOUT_FILL_X|LAYOUT_FILL_Y,0,0,0,0,15,15,15,15); + label_sync = new FXLabel(content,"0 Files Removed, 0 Files Updated"); + + progressbar = new FXProgressBar(content,NULL,0,LAYOUT_FILL_X|FRAME_LINE|PROGRESSBAR_PERCENTAGE); + + FXMatrix * matrix = new FXMatrix(contentswitcher,2,MATRIX_BY_COLUMNS|LAYOUT_FILL_X|LAYOUT_FILL_Y,0,0,0,0,15,15,15,15); + new FXLabel(matrix,tr("New Tracks:"),NULL,LABEL_NORMAL|LAYOUT_FILL_X|JUSTIFY_RIGHT|LAYOUT_CENTER_Y); + text_count = new FXTextField(matrix,10,NULL,0,TEXTFIELD_READONLY|TEXTFIELD_INTEGER|LAYOUT_FILL_X|JUSTIFY_LEFT); + text_count->disable(); + text_count->setTextColor(FXRGB(0,0,255)); + text_count->setText("0"); + + new FXLabel(matrix,tr("File:"),NULL,LABEL_NORMAL|LAYOUT_FILL_X|JUSTIFY_RIGHT|LAYOUT_CENTER_Y); + text_file = new FXTextField(matrix,40,NULL,0,TEXTFIELD_READONLY|LAYOUT_FILL_X); + text_file->disable(); + + new FXLabel(matrix,tr("Directory:"),NULL,LABEL_NORMAL|LAYOUT_FILL_X|JUSTIFY_RIGHT|LAYOUT_CENTER_Y); + text_dir = new FXTextField(matrix,40,NULL,0,TEXTFIELD_READONLY|LAYOUT_FILL_X); + text_dir->disable(); + + + new FXSeparator(main,SEPARATOR_GROOVE|LAYOUT_FILL_X); + FXHorizontalFrame *closebox=new FXHorizontalFrame(main,LAYOUT_BOTTOM|LAYOUT_FILL_X|PACK_UNIFORM_WIDTH,0,0,0,0); + new GMButton(closebox,tr("&Cancel"),NULL,this,FXDialogBox::ID_CANCEL,BUTTON_INITIAL|BUTTON_DEFAULT|LAYOUT_CENTER_X|FRAME_RAISED|FRAME_THICK,0,0,0,0, 20,20); + } + +GMSyncDatabase::~GMSyncDatabase(){ + } + +long GMSyncDatabase::onThreadProgress(FXObject*,FXSelector,void*ptr){ + GMThreadProgress * tp = reinterpret_cast(*((void**)ptr)); + if (tp) { + GMSyncProgress * sp = dynamic_cast(tp); + if (sp) { + if (contentswitcher->getCurrent()!=0) { + label_title->setText(fxtr("Updating Database...")); + contentswitcher->setCurrent(0); + } + label_sync->setText(GMStringFormat("%d File(s) Removed, %d Files(s) Updated",sp->ndeleted,sp->nupdated)); + progressbar->setProgress(sp->current); + progressbar->setTotal(sp->total); + } + else { + GMImportProgress * ip = dynamic_cast(tp); + if (ip) { + if (contentswitcher->getCurrent()!=1) { + label_title->setText(fxtr("Importing Files...")); + contentswitcher->setCurrent(1); + } + text_count->setText(GMStringFormat("%d / %d",ip->nfound,ip->ntotal)); + text_file->setText(ip->file); + text_dir->setText(ip->dir); + } + } + delete tp; + } + return 1; + } + + +FXDEFMAP(GMImportDatabase) GMImportDatabaseMap[]={ + FXMAPFUNC(SEL_COMMAND,GMImportDatabase::ID_THREAD_PROGRESS,GMImportDatabase::onThreadProgress), + }; + +FXIMPLEMENT(GMImportDatabase,GMThreadDialog,GMImportDatabaseMap,ARRAYNUMBER(GMImportDatabaseMap)) + +GMImportDatabase::GMImportDatabase(){ + } + +GMImportDatabase::GMImportDatabase(FXWindow * owner,const FXStringList & files,const GMImportOptions & io,FXint playlist,FXFont * font) : GMThreadDialog(owner) { + thread=new GMImportThread(files,io,playlist,this); + FXLabel*label; + FXHorizontalFrame * header = new FXHorizontalFrame(this,LAYOUT_FILL_X,0,0,0,0,0,20,0,0,0,0); + header->setBackColor(getApp()->getBackColor()); + FXVerticalFrame * frame = new FXVerticalFrame(header,LAYOUT_FILL_X,0,0,0,0,0,0,0,0,0,0); + label = new FXLabel(frame,tr("Importing Files..."),NULL,LAYOUT_FILL_X|JUSTIFY_LEFT|TEXT_AFTER_ICON,0,0,0,0,10,0,10,0); + label->setBackColor(getApp()->getBackColor()); + label->setFont(font); + label = new FXLabel(frame,tr("Please wait. This may take a while."),NULL,LAYOUT_FILL_X|JUSTIFY_LEFT|TEXT_AFTER_ICON,0,0,0,0,10+30,0,0,10); + label->setBackColor(getApp()->getBackColor()); + + if (animation_image) { + GMAnimImage *animation = new GMAnimImage(header,animation_image,animation_size,FRAME_NONE|LAYOUT_CENTER_Y); + animation->setBackColor(getApp()->getBackColor()); + } + new FXSeparator(this,LAYOUT_FILL_X|SEPARATOR_GROOVE|LAYOUT_SIDE_TOP); + + FXVerticalFrame * main = new FXVerticalFrame(this,LAYOUT_FILL_X|LAYOUT_FILL_Y,0,0,0,0,0,0,0,0,0,0); + FXMatrix * matrix = new FXMatrix(main,2,MATRIX_BY_COLUMNS|LAYOUT_FILL_X|LAYOUT_FILL_Y,0,0,0,0,15,15,15,15); + new FXLabel(matrix,tr("New Tracks:"),NULL,LABEL_NORMAL|LAYOUT_FILL_X|JUSTIFY_RIGHT|LAYOUT_CENTER_Y); + text_count = new FXTextField(matrix,10,NULL,0,TEXTFIELD_READONLY|TEXTFIELD_INTEGER|LAYOUT_FILL_X|JUSTIFY_LEFT); + text_count->disable(); + text_count->setTextColor(FXRGB(0,0,255)); + text_count->setText("0"); + + new FXLabel(matrix,tr("File:"),NULL,LABEL_NORMAL|LAYOUT_FILL_X|JUSTIFY_RIGHT|LAYOUT_CENTER_Y); + text_file = new FXTextField(matrix,40,NULL,0,TEXTFIELD_READONLY|LAYOUT_FILL_X); + text_file->disable(); + + new FXLabel(matrix,tr("Directory:"),NULL,LABEL_NORMAL|LAYOUT_FILL_X|JUSTIFY_RIGHT|LAYOUT_CENTER_Y); + text_dir = new FXTextField(matrix,40,NULL,0,TEXTFIELD_READONLY|LAYOUT_FILL_X); + text_dir->disable(); + new FXSeparator(main,SEPARATOR_GROOVE|LAYOUT_FILL_X); + FXHorizontalFrame *closebox=new FXHorizontalFrame(main,LAYOUT_BOTTOM|LAYOUT_FILL_X|PACK_UNIFORM_WIDTH,0,0,0,0); + new GMButton(closebox,tr("&Stop Import"),NULL,this,FXDialogBox::ID_CANCEL,BUTTON_INITIAL|BUTTON_DEFAULT|LAYOUT_CENTER_X|FRAME_RAISED|FRAME_THICK,0,0,0,0, 20,20); + } + +GMImportDatabase::~GMImportDatabase(){ + } + +long GMImportDatabase::onThreadProgress(FXObject*,FXSelector,void*ptr){ + GMImportProgress * p = reinterpret_cast(*((void**)ptr)); + if (p) { + text_count->setText(GMStringFormat("%d / %d",p->nfound,p->ntotal)); + text_file->setText(p->file); + text_dir->setText(p->dir); + delete p; + } + return 1; + } + + diff --git a/src/GMSearch.h b/src/GMSearch.h new file mode 100644 index 0000000..ee79045 --- /dev/null +++ b/src/GMSearch.h @@ -0,0 +1,98 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#ifndef GMSEARCHDIALOG_H +#define GMSEARCHDIALOG_H + +class GMTrackDatabase; +class GMThread; +class GMAnimImage; + +class GMThreadDialog : public FXDialogBox { + FXDECLARE(GMThreadDialog) +protected: + FXImage * animation_image; + FXint animation_size; + GMThread * thread; + GMThreadDialog(); +private: + GMThreadDialog(const GMThreadDialog&); + GMThreadDialog& operator=(const GMThreadDialog&); +public: + enum { + ID_THREAD_DONE = FXDialogBox::ID_LAST, + ID_THREAD_ERROR, + ID_THREAD_PROGRESS + }; +public: + long onCmdCancel(FXObject*,FXSelector,void*); + long onThreadDone(FXObject*,FXSelector,void*); + long onThreadError(FXObject*,FXSelector,void*); +public: + GMThreadDialog(FXWindow * owner); + + virtual FXuint execute(FXuint placement=PLACEMENT_CURSOR); + + virtual ~GMThreadDialog(); + }; + + + +class GMImportDatabase : public GMThreadDialog { + FXDECLARE(GMImportDatabase) +protected: + FXTextField * text_file; + FXTextField * text_dir; + FXTextField * text_count; +protected: + GMImportDatabase(); +private: + GMImportDatabase(const GMImportDatabase&); + GMImportDatabase& operator=(const GMImportDatabase&); +public: + long onThreadProgress(FXObject*,FXSelector,void*); +public: + GMImportDatabase(FXWindow * owner,const FXStringList & files,const GMImportOptions & pref,FXint playlist,FXFont * font); + virtual ~GMImportDatabase(); + }; + + + +class GMSyncDatabase : public GMThreadDialog { + FXDECLARE(GMSyncDatabase) +protected: + FXSwitcher * contentswitcher; + FXLabel * label_title; + FXLabel * label_sync; + FXProgressBar * progressbar; + FXTextField * text_file; + FXTextField * text_dir; + FXTextField * text_count; +protected: + GMSyncDatabase(); +private: + GMSyncDatabase(const GMSyncDatabase&); + GMSyncDatabase& operator=(const GMSyncDatabase&); +public: + long onThreadProgress(FXObject*,FXSelector,void*); +public: + GMSyncDatabase(FXWindow * owner,const FXStringList & files,const GMImportOptions & io,const GMSyncOptions & so,FXint playlist,FXbool tags,FXFont * font); + virtual ~GMSyncDatabase(); + }; + +#endif diff --git a/src/GMSettingsDaemon.cpp b/src/GMSettingsDaemon.cpp new file mode 100644 index 0000000..0782012 --- /dev/null +++ b/src/GMSettingsDaemon.cpp @@ -0,0 +1,74 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2010-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ + +#include "gmdefs.h" +#include "GMDBus.h" +#include "GMSettingsDaemon.h" + +#define GNOME_SETTINGS_DAEMON_PATH "/org/gnome/SettingsDaemon/MediaKeys" +#define GNOME_SETTINGS_DAEMON_INTERFACE "org.gnome.SettingsDaemon.MediaKeys" +#define GNOME_SETTINGS_DAEMON_NAME "org.gnome.SettingsDaemon" + +FXDEFMAP(GMSettingsDaemon) GMSettingsDaemonMap[]={ + FXMAPFUNC(SEL_SIGNAL,0,GMSettingsDaemon::onSignal) + }; + +FXIMPLEMENT(GMSettingsDaemon,GMDBusProxy,GMSettingsDaemonMap,ARRAYNUMBER(GMSettingsDaemonMap)) + +GMSettingsDaemon::GMSettingsDaemon(GMDBus*b,FXObject * tgt,FXSelector sel) : GMDBusProxy(b,GNOME_SETTINGS_DAEMON_NAME,GNOME_SETTINGS_DAEMON_PATH,GNOME_SETTINGS_DAEMON_INTERFACE) { + target=tgt; + message=sel; + } + +GMSettingsDaemon::~GMSettingsDaemon(){ + } + +void GMSettingsDaemon::GrabMediaPlayerKeys(const FXchar * pl,FXuint time) { + DBusMessage * msg = method("GrabMediaPlayerKeys"); + if (msg) { + dbus_message_append_args(msg,DBUS_TYPE_STRING,&pl,DBUS_TYPE_UINT32,&time,DBUS_TYPE_INVALID); + player=pl; + bus->send(msg); + } + } + +void GMSettingsDaemon::ReleaseMediaPlayerKeys(const FXchar * pl) { + DBusMessage * msg = method("ReleaseMediaPlayerKeys"); + if (msg) { + dbus_message_append_args(msg,DBUS_TYPE_STRING,&pl,DBUS_TYPE_INVALID); + player.clear(); + bus->send(msg); + } + } + + +long GMSettingsDaemon::onSignal(FXObject*,FXSelector,void*ptr){ + DBusMessage * msg = reinterpret_cast(ptr); + if (dbus_message_is_signal(msg,GNOME_SETTINGS_DAEMON_INTERFACE,"MediaPlayerKeyPressed")){ + const FXchar * pl=NULL; + const FXchar * cmd=NULL; + if (dbus_message_get_args(msg,NULL,DBUS_TYPE_STRING,&pl,DBUS_TYPE_STRING,&cmd,DBUS_TYPE_INVALID)) { + if (compare(pl,player)==0 && target && cmd) { + target->handle(this,FXSEL(SEL_KEYPRESS,message),(void*)cmd); + } + } + return 1; + } + return 0; + } diff --git a/src/GMSettingsDaemon.h b/src/GMSettingsDaemon.h new file mode 100644 index 0000000..3f5e02e --- /dev/null +++ b/src/GMSettingsDaemon.h @@ -0,0 +1,46 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2010-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#ifndef GMSETTINGSDAEMON_H +#define GMSETTINGSDAEMON_H + +class GMSettingsDaemon : public GMDBusProxy { +FXDECLARE(GMSettingsDaemon) +protected: + FXString player; +protected: + GMSettingsDaemon(){} +private: + GMSettingsDaemon(const GMSettingsDaemon&); + GMSettingsDaemon& operator=(const GMSettingsDaemon&); +public: + long onSignal(FXObject*,FXSelector,void*); +public: + GMSettingsDaemon(GMDBus*,FXObject * tgt,FXSelector sel); + + void GrabMediaPlayerKeys(const FXchar * player,FXuint time=0); + + void ReleaseMediaPlayerKeys(const FXchar * player); + + virtual ~GMSettingsDaemon(); + }; + +#endif + + + diff --git a/src/GMSource.cpp b/src/GMSource.cpp new file mode 100644 index 0000000..42ecc54 --- /dev/null +++ b/src/GMSource.cpp @@ -0,0 +1,115 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#include "gmdefs.h" +#include "GMList.h" +#include "GMDatabase.h" +#include "GMTrackDatabase.h" +#include "GMTrackList.h" +#include "GMTrackView.h" +#include "GMWindow.h" +#include "GMSource.h" +#include "GMPlayerManager.h" +#include "GMTag.h" +#include "GMIconTheme.h" +#include "GMClipboard.h" + + +FXIMPLEMENT(GMSource,FXObject,NULL,0); + +GMSource::GMSource() : current_track(-1), sort_browse(NULL) { + } + +GMSource::~GMSource() { + } + +FXbool GMSource::findCurrent(GMTrackList * list,GMSource * src) { + if (src->current_track==-1) return false; + for (FXint i=0;igetNumItems();i++){ + if (list->getItemId(i)==src->current_track) { + list->setActiveItem(i); + list->setCurrentItem(i); + return true; + } + } + return false; + } + +FXbool GMSource::findCurrentArtist(GMList *,GMSource *){ + return false; + } + +FXbool GMSource::findCurrentAlbum(GMList *,GMSource *){ + return false; + } + +void GMSource::markCurrent(GMTrackList * list,FXint item) { + current_track=-1; + if (list->getNumItems()) { + current_track = list->getItemId(item); + } + } + +FXint GMSource::getNumTracks() const{ + return 0; + } + +FXString GMSource::getTrackFilename(FXint) const{ + return FXString::null; + } + +FXbool GMSource::getTrack(GMTrack &) const{ + return false; + } + + +FXbool GMSource::listGenres(GMList *,FXIcon *){ + return false; + } + +FXbool GMSource::listArtists(GMList *,FXIcon *,const FXIntList&){ + return false; + } + +FXbool GMSource::listAlbums(GMList *,FXIcon *,const FXIntList &,const FXIntList&){ + return false; + } + +FXbool GMSource::listTracks(GMTrackList *,const FXIntList &,const FXIntList&) { + return false; + } + +FXbool GMSource::genre_context_menu(FXMenuPane *){ + return false; + } + +FXbool GMSource::artist_context_menu(FXMenuPane *){ + return false; + } + +FXbool GMSource::album_context_menu(FXMenuPane *){ + return false; + } + +FXbool GMSource::track_context_menu(FXMenuPane *){ + return false; + } + +FXbool GMSource::source_context_menu(FXMenuPane *){ + return false; + } diff --git a/src/GMSource.h b/src/GMSource.h new file mode 100644 index 0000000..1984dc2 --- /dev/null +++ b/src/GMSource.h @@ -0,0 +1,172 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#ifndef GMSOURCE_H +#define GMSOURCE_H + + +#ifndef GMTRACKLIST_H +#include "GMTrackList.h" +#endif + +class GMList; +/* +enum { + FLAG_CAN_AUTOPLAY =0x1, + FLAG_CAN_FILTER =0x2, + FLAG_CAN_BROWSE =0x4, + FLAG_DEFAULT_BROWSE=0x8 + }; +*/ + +enum { + FILTER_TRACK = 0x1, + FILTER_ARTIST = 0x2, + FILTER_ALBUM = 0x4, + FILTER_GENRE = 0x8, + FILTER_ALL = (FILTER_TRACK|FILTER_ARTIST|FILTER_ALBUM|FILTER_GENRE), + FILTER_DEFAULT = (FILTER_TRACK|FILTER_ARTIST|FILTER_ALBUM) + }; + + +/// Sort Function +class GMTrackItem; +typedef FXint (*GMTrackListSortFunc)(const GMTrackItem*,const GMTrackItem*); + + +class GMSource : public FXObject { +FXDECLARE(GMSource) +protected: + FXIntList genre_selection; + FXIntList artist_selection; + FXIntList album_selection; + FXint current_track; + GMTrackListSortFunc sort_browse; +private: + GMSource(const GMSource&); + GMSource& operator=(const GMSource&); +public: + enum { + ID_TRACK_PLAYED = 1, + ID_EDIT_GENRE, + ID_EDIT_ARTIST, + ID_EDIT_ALBUM, + ID_EDIT_TRACK, + ID_COPY_ARTIST, + ID_COPY_ALBUM, + ID_COPY_TRACK, + ID_DELETE_GENRE, + ID_DELETE_ARTIST, + ID_DELETE_ALBUM, + ID_DELETE_TRACK, + ID_PASTE, + ID_DROP, + ID_EXPORT, + ID_LAST + }; +public: + GMSource(); + + virtual void configure(GMColumnList&) const {} + + virtual void shuffle(GMTrackList*,FXuint) const{} + + virtual void orderChanged(GMTrackList*) const{} + + GMTrackListSortFunc getSortBrowse() const { return sort_browse; } + + virtual FXint getSortColumn(FXbool browse) const { if (browse) return HEADER_BROWSE; else return HEADER_ARTIST; } + + virtual FXIcon * getAlbumIcon(FXint,FXbool) {return NULL;} + + virtual FXIcon * getAlbumIcon() {return NULL;} + + void setCurrentTrack(FXint t) { current_track=t; } + + FXint getCurrentTrack() const { return current_track; } + + virtual FXbool hasCurrentTrack(GMSource * ) const { return false; } + + virtual FXbool hasTrack(const FXString &,FXint &) { return false; } + + virtual void resetCurrent() { current_track=-1; } + + virtual void markCurrent(GMTrackList * tracklist,FXint item); + + virtual FXbool findCurrent(GMTrackList * tracklist,GMSource * src); + + virtual FXbool findCurrentArtist(GMList * tracklist,GMSource * src); + + virtual FXbool findCurrentAlbum(GMList * tracklist,GMSource * src); + + virtual FXbool moveTrack(GMTrackList*,FXint,FXint) { return false;} + + virtual FXint getNumTracks() const; + + virtual FXString getTrackFilename(FXint id) const; + + virtual FXbool getTrack(GMTrack & info) const; + + virtual FXbool setTrack(GMTrack &) const { return false; } + + virtual FXint getType() const { return SOURCE_INVALID; } + + virtual FXbool getQueueColumn(FXbool) const { return false; } + + virtual FXbool canBrowse() const { return true; } + + virtual FXbool canFilter() const { return false; } + + virtual FXbool defaultBrowse() const { return true; } + + virtual FXbool autoPlay() const { return true; } + + virtual FXString getName() const { return FXString::null; } + + virtual FXString settingKey() const { return "nokey"; } + + virtual FXbool setFilter(const FXString&,FXuint) {return false;} + + virtual FXbool listGenres(GMList * genrelist,FXIcon * icon); + + virtual FXbool listArtists(GMList * artistlist,FXIcon * icon,const FXIntList & genrelist); + + virtual FXbool listAlbums(GMList * albumlist,FXIcon * icon,const FXIntList & artistlist,const FXIntList & genrelist); + + virtual FXbool listTracks(GMTrackList * tracklist,const FXIntList & albumlist,const FXIntList & genrelist); + + virtual FXbool genre_context_menu(FXMenuPane * pane); + + virtual FXbool artist_context_menu(FXMenuPane * pane); + + virtual FXbool album_context_menu(FXMenuPane * pane); + + virtual FXbool track_context_menu(FXMenuPane * pane); + + virtual FXbool source_context_menu(FXMenuPane * pane); + + virtual FXbool dnd_source_accepts(FXDragType*,FXuint) { return false; } + + virtual ~GMSource(); + }; + +typedef FXObjectListOf GMSourceList; + + + +#endif diff --git a/src/GMSourceView.cpp b/src/GMSourceView.cpp new file mode 100644 index 0000000..d4f86d7 --- /dev/null +++ b/src/GMSourceView.cpp @@ -0,0 +1,324 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2007-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#include "gmdefs.h" +#include +#include "GMApp.h" +#include "GMList.h" +#include "GMTrackList.h" +#include "GMSourceView.h" +#include "GMTrackView.h" +#include "GMWindow.h" +#include "GMSource.h" +#include "GMDatabaseSource.h" +#include "GMStreamSource.h" +#include "GMPlayerManager.h" +#include "GMIconTheme.h" + + + +FXDEFMAP(GMSourceView) GMSourceViewMap[]={ + FXMAPFUNC(SEL_COMMAND,GMSourceView::ID_SOURCE_LIST_HEADER,GMSourceView::onCmdSortSourceList), + FXMAPFUNC(SEL_RIGHTBUTTONRELEASE,GMSourceView::ID_SOURCE_LIST,GMSourceView::onSourceContextMenu), + FXMAPFUNC(SEL_COMMAND,GMSourceView::ID_SOURCE_LIST,GMSourceView::onCmdSourceSelected), + FXMAPFUNC(SEL_DND_MOTION,GMSourceView::ID_SOURCE_LIST,GMSourceView::onDndSourceMotion), + FXMAPFUNC(SEL_DND_DROP,GMSourceView::ID_SOURCE_LIST,GMSourceView::onDndSourceDrop), + FXMAPFUNC(SEL_COMMAND,GMSourceView::ID_NEW_PLAYLIST,GMSourceView::onCmdNewPlayList), + FXMAPFUNC(SEL_UPDATE,GMSourceView::ID_NEW_PLAYLIST,GMSourceView::onUpdNewPlayList), + FXMAPFUNC(SEL_COMMAND,GMSourceView::ID_NEW_STATION,GMSourceView::onCmdNewStation), + FXMAPFUNC(SEL_COMMAND,GMSourceView::ID_EXPORT,GMSourceView::onCmdExport), + FXMAPFUNC(SEL_UPDATE,GMSourceView::ID_EXPORT,GMSourceView::onUpdExport), + }; + +FXIMPLEMENT(GMSourceView,GMScrollFrame,GMSourceViewMap,ARRAYNUMBER(GMSourceViewMap)) + +GMSourceView::GMSourceView() : source(NULL) { + } + +GMSourceView::GMSourceView(FXComposite* p) : GMScrollFrame(p) , source(NULL) { + sourcelistheader = new GMHeaderButton(this,tr("Sources\tPress to change sorting order\tPress to change sorting order"),NULL,this,ID_SOURCE_LIST_HEADER,LAYOUT_FILL_X|FRAME_THICK|FRAME_RAISED|JUSTIFY_LEFT); + sourcelist = new GMTreeList(this,this,ID_SOURCE_LIST,LAYOUT_FILL_X|LAYOUT_FILL_Y|TREELIST_BROWSESELECT); + + sourcelist->dropEnable(); + sourcelist->setSortFunc(source_list_sort); + + sourcelistheader->setArrowState(ARROW_DOWN); + + updateColors(); + } + +GMSourceView::~GMSourceView(){ + } + +void GMSourceView::updateColors() { + sourcelist->setRowColor(GMPlayerManager::instance()->getPreferences().gui_row_color); + } + +void GMSourceView::updateSource(GMSource * src){ + FXTreeItem * item = sourcelist->getFirstItem(); + while(item) { + if (item->getData()==src) { + item->setText(tr(src->getName().text())); + break; + } + item=item->getNext(); + } + resort(); + } + + +void GMSourceView::setSource(GMSource * src,FXbool makecurrent/*=true*/){ + if (src!=source) { + source=src; + if (makecurrent) { + FXTreeItem * item = sourcelist->getFirstItem(); + while(item) { + if (item->getData()==src) { + sourcelist->setCurrentItem(item,false); + break; + } + item=item->getNext(); + } + } + GMPlayerManager::instance()->getTrackView()->setSource(source); + } + } + + +void GMSourceView::clear() { + sourcelist->clearItems(); + } + + +void GMSourceView::refresh() { + clear(); + listSources(); + } + + +void GMSourceView::init() { + loadSettings("window"); + clear(); + listsources(); + + + FXString key = getApp()->reg().readStringEntry("window","source-list-current",""); + if (!key.empty()){ + FXTreeItem * item = sourcelist->getFirstItem(); + while(item) { + GMSource * src = (GMSource*)item->getData(); + if (src->settingKey()==key) { + sourcelist->setCurrentItem(item); + break; + } + item=item->getNext(); + } + } + + if (sourcelist->getCurrentItem()==NULL && sourcelist->getFirstItem()) + sourcelist->setCurrentItem(sourcelist->getFirstItem()); + + source=(GMSource*)sourcelist->getItemData(sourcelist->getCurrentItem()); + GMPlayerManager::instance()->getTrackView()->init(source); + } + + +void GMSourceView::resort() { + sortSources(); + } + + +FXbool GMSourceView::listsources() { + GMTreeItem * item=NULL; + for (FXint i=0;igetNumSources();i++){ + GMSource * source = GMPlayerManager::instance()->getSource(i); + switch(source->getType()){ + case SOURCE_DATABASE : item = new GMTreeItem(tr(source->getName().text()),GMIconTheme::instance()->icon_source_library,GMIconTheme::instance()->icon_source_library,source); break; + case SOURCE_DATABASE_PLAYLIST: item = new GMTreeItem(tr(source->getName().text()),GMIconTheme::instance()->icon_source_playlist,GMIconTheme::instance()->icon_source_playlist,source); break; + case SOURCE_INTERNET_RADIO : item = new GMTreeItem(tr(source->getName().text()),GMIconTheme::instance()->icon_source_internetradio,GMIconTheme::instance()->icon_source_internetradio,source); break; + default : item = NULL; break; + } + if (item) { + sourcelist->appendItem(NULL,item); + } + } + sourcelist->sortItems(); + return true; + } + + +FXbool GMSourceView::listSources() { + listsources(); + setSource((GMSource*)sourcelist->getItemData(sourcelist->getCurrentItem()),false); + return true; + } + + +void GMSourceView::sortSources() const{ + sourcelist->sortItems(); + } + + +void GMSourceView::loadSettings(const FXString & key) { + FXbool sort_reverse,shown; + + sort_reverse = getApp()->reg().readBoolEntry(key.text(),"source-list-sort-reverse",false); + if (sort_reverse) + sourcelist->setSortFunc(source_list_sort_reverse); + else + sourcelist->setSortFunc(source_list_sort); + + shown = getApp()->reg().readBoolEntry(key.text(),"source-list",true); + if (shown) + getParent()->show(); + else + getParent()->hide(); + } + + +void GMSourceView::saveSettings(const FXString & key) const { + getApp()->reg().writeBoolEntry(key.text(),"source-list-sort-reverse",sourcelist->getSortFunc()==source_list_sort_reverse); + getApp()->reg().writeBoolEntry(key.text(),"source-list",getParent()->shown()); + } + + + +void GMSourceView::saveView() const { + saveSettings("window"); + if (source) { + getApp()->reg().writeStringEntry("window","source-list-current",source->settingKey().text()); + } + } + + +long GMSourceView::onCmdSourceSelected(FXObject*,FXSelector,void*){ + FXTreeItem * item = sourcelist->getCurrentItem(); + if (item) { + setSource((GMSource*)item->getData(),false); + } + return 1; + } + + +long GMSourceView::onCmdSortSourceList(FXObject*,FXSelector,void*){ + if (sourcelist->getSortFunc()==source_list_sort) { + sourcelist->setSortFunc(source_list_sort_reverse); + sourcelistheader->setArrowState(ARROW_UP); + } + else { + sourcelist->setSortFunc(source_list_sort); + sourcelistheader->setArrowState(ARROW_DOWN); + } + sortSources(); + return 1; + } + + +long GMSourceView::onSourceContextMenu(FXObject*,FXSelector,void*ptr){ + FXEvent * event = reinterpret_cast(ptr); + if (event->moved) return 0; + GMTreeItem * item = dynamic_cast(sourcelist->getItemAt(event->win_x,event->win_y)); + GMMenuPane pane(this); + if (item) { + GMSource * source = (GMSource*)item->getData(); + if (source && source->source_context_menu(&pane)) { + sourcelist->setCurrentItem(item); + onCmdSourceSelected(NULL,0,NULL); // Simulate SEL_COMMAND + } + } + else { + new GMMenuCommand(&pane,tr("New Playlist…\t\tCreate a new playlist"),GMIconTheme::instance()->icon_playlist,this,ID_NEW_PLAYLIST); + new GMMenuCommand(&pane,tr("Import Playlist…\t\tImport existing playlist"),GMIconTheme::instance()->icon_import,GMPlayerManager::instance()->getDatabaseSource(),GMDatabaseSource::ID_IMPORT_PLAYLIST); + new GMMenuCommand(&pane,tr("New Radio Station…\t\tCreate a new playlist"),NULL,this,GMSourceView::ID_NEW_STATION); + } + pane.create(); + ewmh_change_window_type(&pane,WINDOWTYPE_POPUP_MENU); + pane.popup(NULL,event->root_x,event->root_y); + getApp()->runPopup(&pane); + return 1; + } + + +long GMSourceView::onDndSourceMotion(FXObject*,FXSelector,void*ptr){ + FXEvent * event = reinterpret_cast(ptr); + GMTreeItem * item = dynamic_cast(sourcelist->getItemAt(event->win_x,event->win_y)); + if (item) { + GMSource * source = reinterpret_cast(item->getData()); + FXDragType*types; + FXuint ntypes; + if (sourcelist->inquireDNDTypes(FROM_DRAGNDROP,types,ntypes)){ + if (source->dnd_source_accepts(types,ntypes)){ + sourcedrop=source; + sourcelist->acceptDrop(DRAG_LINK); + freeElms(types); + return 1; + } + freeElms(types); + } + } + sourcedrop=NULL; + return 0; + } + + + +long GMSourceView::onDndSourceDrop(FXObject*sender,FXSelector,void*ptr){ + if (sourcedrop) { + long code = sourcedrop->handle(sender,FXSEL(SEL_DND_DROP,GMSource::ID_DROP),ptr); + sourcedrop=NULL; + return code; + } + return 0; + } + + +long GMSourceView::onCmdNewPlayList(FXObject*sender,FXSelector,void*ptr){ + if (GMPlayerManager::instance()->getSource(0)) + return GMPlayerManager::instance()->getSource(0)->handle(sender,FXSEL(SEL_COMMAND,GMDatabaseSource::ID_NEW_PLAYLIST),ptr); + return 0; + } + + +long GMSourceView::onUpdNewPlayList(FXObject*sender,FXSelector,void*ptr){ + if (GMPlayerManager::instance()->getSource(0)) + return GMPlayerManager::instance()->getSource(0)->handle(sender,FXSEL(SEL_UPDATE,GMDatabaseSource::ID_NEW_PLAYLIST),ptr); + return 0; + } + + +long GMSourceView::onCmdNewStation(FXObject*sender,FXSelector,void*ptr){ + for (FXint i=0;igetNumSources();i++){ + GMSource * source = GMPlayerManager::instance()->getSource(i); + if (source->getType()==SOURCE_INTERNET_RADIO) + return source->handle(sender,FXSEL(SEL_COMMAND,GMStreamSource::ID_NEW_STATION),ptr); + } + return 0; + } + +long GMSourceView::onCmdExport(FXObject*sender,FXSelector,void*ptr){ + if (source) + return source->handle(sender,FXSEL(SEL_COMMAND,GMSource::ID_EXPORT),ptr); + return 0; + } + + +long GMSourceView::onUpdExport(FXObject*sender,FXSelector,void*ptr){ + if (source) + return source->handle(sender,FXSEL(SEL_UPDATE,GMSource::ID_EXPORT),ptr); + return 0; + } diff --git a/src/GMSourceView.h b/src/GMSourceView.h new file mode 100644 index 0000000..a2382c6 --- /dev/null +++ b/src/GMSourceView.h @@ -0,0 +1,92 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2007-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#ifndef GMSOURCEVIEW_H +#define GMSOURCEVIEW_H + +class GMTreeList; +class GMSource; +class GMScrollFrame; + +class GMSourceView : public GMScrollFrame { +FXDECLARE(GMSourceView) +protected: + GMTreeList * sourcelist; + GMHeaderButton * sourcelistheader; +protected: + GMSource * source; + GMSource * sourcedrop; +protected: + GMSourceView(); + FXbool listsources(); +private: + GMSourceView(const GMSourceView&); + GMSourceView& operator=(const GMSourceView&); +public: + enum { + ID_SOURCE_LIST_HEADER = FXVerticalFrame::ID_LAST, + ID_SOURCE_LIST, + ID_NEW_PLAYLIST, + ID_NEW_STATION, + ID_EXPORT, + ID_LAST, + }; +public: + long onCmdSourceSelected(FXObject*,FXSelector,void*); + long onCmdSortSourceList(FXObject*,FXSelector,void*); + long onSourceContextMenu(FXObject*,FXSelector,void*); + long onDndSourceMotion(FXObject*,FXSelector,void*); + long onDndSourceDrop(FXObject*,FXSelector,void*); + long onCmdNewPlayList(FXObject*,FXSelector,void*); + long onUpdNewPlayList(FXObject*,FXSelector,void*); + long onCmdNewStation(FXObject*,FXSelector,void*); + long onCmdExport(FXObject*,FXSelector,void*); + long onUpdExport(FXObject*,FXSelector,void*); +public: + GMSourceView(FXComposite* p); + + void updateColors(); + + void updateSource(GMSource * src); + + void setSource(GMSource * src,FXbool makecurrent=true); + + GMSource * getSource() const { return source; } + + FXbool listSources(); + + void sortSources() const; + + void resort(); + + void init(); + + void refresh(); + + void clear(); + + void loadSettings(const FXString & key); + + void saveSettings(const FXString & key) const; + + void saveView() const; + + virtual ~GMSourceView(); + }; + +#endif diff --git a/src/GMStreamSource.cpp b/src/GMStreamSource.cpp new file mode 100644 index 0000000..e580554 --- /dev/null +++ b/src/GMStreamSource.cpp @@ -0,0 +1,296 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#include "gmdefs.h" +#include "GMList.h" +#include "GMDatabase.h" +#include "GMTrackDatabase.h" +#include "GMTrackList.h" +#include "GMTrackItem.h" +#include "GMTrackView.h" +#include "GMWindow.h" +#include "GMSource.h" +#include "GMSourceView.h" +#include "GMClipboard.h" +#include "GMStreamSource.h" +#include "GMPlayerManager.h" +#include "GMTag.h" +#include "GMIconTheme.h" +#include "GMFilename.h" +#include "GMThread.h" +#include "GMSearch.h" + + +FXDEFMAP(GMStreamSource) GMStreamSourceMap[]={ + FXMAPFUNC(SEL_COMMAND,GMStreamSource::ID_NEW_STATION,GMStreamSource::onCmdNewStation), + FXMAPFUNC(SEL_COMMAND,GMStreamSource::ID_EDIT_STATION,GMStreamSource::onCmdEditStation), + FXMAPFUNC(SEL_COMMAND,GMStreamSource::ID_DELETE_STATION,GMStreamSource::onCmdDeleteStation), + FXMAPFUNC(SEL_UPDATE,GMStreamSource::ID_EXPORT,GMStreamSource::onUpdExport) + + }; + +FXIMPLEMENT(GMStreamSource,GMSource,GMStreamSourceMap,ARRAYNUMBER(GMStreamSourceMap)); + + +GMStreamSource::GMStreamSource() : db(NULL) { + } + +GMStreamSource::GMStreamSource(GMTrackDatabase * database) : db(database) { + FXASSERT(db); + } + +GMStreamSource::~GMStreamSource(){ + } + + +void GMStreamSource::configure(GMColumnList& list) const{ + list.no(4); + list[0]=GMColumn(notr("No."),HEADER_TRACK,GMStreamTrackItem::ascendingTrack,GMStreamTrackItem::descendingTrack,60,true,true,0); + list[1]=GMColumn(notr("Station"),HEADER_TITLE,GMStreamTrackItem::ascendingTitle,GMStreamTrackItem::descendingTitle,200,true,true,1); + list[2]=GMColumn(notr("Bitrate"),HEADER_BITRATE,GMStreamTrackItem::ascendingTime,GMStreamTrackItem::descendingTime,80,true,true,2); + list[3]=GMColumn(notr("Genre"),HEADER_GENRE,GMStreamTrackItem::ascendingGenre,GMStreamTrackItem::descendingGenre,150,true,true,3); + } + + +FXbool GMStreamSource::hasCurrentTrack(GMSource * src) const { + if (src==this) return true; + return false; + } + +FXbool GMStreamSource::setTrack(GMTrack & track) const { + if (current_track>=0 && track.bitrate>0) { + db->setStreamBitrate(current_track,track.bitrate); + if (GMPlayerManager::instance()->getTrackView()->getSource()==this) + GMPlayerManager::instance()->getTrackView()->refresh(); + } + return true; + } + +FXbool GMStreamSource::getTrack(GMTrack & info) const { + info.clear(); + info.mrl=getTrackFilename(current_track); + return false; + } + +FXString GMStreamSource::getTrackFilename(FXint id) const{ + const char * url; + FXString query; + GMQuery q; + try { + query="SELECT url FROM streams WHERE id == " + GMStringVal(id) + ";"; + q.compile(db->database(),query); + q.execute(); + url = q.getResult(0); + return url; + } + catch(FXCompileException &){ + return FXString::null; + } + catch(FXExecuteException &){ + return FXString::null; + } + return url; + } + + +FXbool GMStreamSource::source_context_menu(FXMenuPane * pane){ + new GMMenuCommand(pane,fxtr("New Station…\t\t"),NULL,this,ID_NEW_STATION); + return true; + } + +FXbool GMStreamSource::track_context_menu(FXMenuPane * pane){ + new GMMenuCommand(pane,fxtr("Edit…\t\t"),GMIconTheme::instance()->icon_edit,this,ID_EDIT_STATION); + new GMMenuCommand(pane,fxtr("New Station…\t\t"),NULL,this,ID_NEW_STATION); + new GMMenuCommand(pane,fxtr("Remove\t\tRemove."),GMIconTheme::instance()->icon_delete,this,ID_DELETE_STATION); + return true; + } + +FXbool GMStreamSource::listTracks(GMTrackList * tracklist,const FXIntList &/* albumlist*/,const FXIntList & /*genre*/){ + GMQuery q; + FXString query; +// const FXchar * c_artist; + //const FXchar * c_albumname; + const FXchar * c_title; + const FXchar * c_genre; +// FXint time; +// FXint no; + FXint id; + FXint bitrate; + FXint queue=1; + try { + + query = "SELECT streams.id, streams.description, streams.bitrate, genres.name " + "FROM streams, genres " + "WHERE genres.id == streams.genre;"; + + q.compile(db->database(),query); + + while(q.execute()){ + q.getResult(0,id); + c_title = q.getResult(1); + q.getResult(2,bitrate); + c_genre = q.getResult(3); + GMStreamTrackItem * item = new GMStreamTrackItem(id,c_title,c_genre,queue++,bitrate); + tracklist->appendItem((GMTrackItem*)item); + } + GMStreamTrackItem::max_trackno = tracklist->getFont()->getTextWidth(FXString('8',GMDBTrackItem::max_digits(queue))); + } + catch(FXCompileException &){ + tracklist->clearItems(); + return false; + } + catch(FXExecuteException &){ + tracklist->clearItems(); + return false; + } + return true; + } + +long GMStreamSource::onCmdNewStation(FXObject*,FXSelector,void*){ + FXDialogBox dialog(GMPlayerManager::instance()->getMainWindow(),fxtr("New Internet Radio Station"),DECOR_TITLE|DECOR_BORDER|DECOR_RESIZE|DECOR_CLOSE,0,0,0,0,0,0,0,0,0,0); + GMPlayerManager::instance()->getMainWindow()->create_dialog_header(&dialog,fxtr("New Internet Radio Station"),fxtr("Specify url and description of new station"),NULL); + FXHorizontalFrame *closebox=new FXHorizontalFrame(&dialog,LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X|PACK_UNIFORM_WIDTH,0,0,0,0); + new GMButton(closebox,fxtr("C&reate"),NULL,&dialog,FXDialogBox::ID_ACCEPT,BUTTON_INITIAL|BUTTON_DEFAULT|LAYOUT_RIGHT|FRAME_RAISED|FRAME_THICK,0,0,0,0, 15,15); + new GMButton(closebox,fxtr("&Cancel"),NULL,&dialog,FXDialogBox::ID_CANCEL,BUTTON_DEFAULT|LAYOUT_RIGHT|FRAME_RAISED|FRAME_THICK,0,0,0,0, 15,15); + new FXSeparator(&dialog,SEPARATOR_GROOVE|LAYOUT_FILL_X|LAYOUT_SIDE_BOTTOM); + FXVerticalFrame * main = new FXVerticalFrame(&dialog,LAYOUT_FILL_X|LAYOUT_FILL_Y,0,0,0,0,10,5,10,10); + FXMatrix * matrix = new FXMatrix(main,2,LAYOUT_FILL_X|MATRIX_BY_COLUMNS); + new FXLabel(matrix,fxtr("Location"),NULL,LABEL_NORMAL|LAYOUT_RIGHT|LAYOUT_CENTER_Y); + GMTextField * location_field = new GMTextField(matrix,40,NULL,0,LAYOUT_FILL_X|LAYOUT_FILL_COLUMN|FRAME_SUNKEN|FRAME_THICK); + new FXLabel(matrix,fxtr("Description"),NULL,LABEL_NORMAL|LAYOUT_RIGHT|LAYOUT_CENTER_Y); + GMTextField * description_field = new GMTextField(matrix,30,NULL,0,LAYOUT_FILL_X|LAYOUT_FILL_COLUMN|FRAME_SUNKEN|FRAME_THICK); + new FXLabel(matrix,fxtr("Genre"),NULL,LABEL_NORMAL|LAYOUT_RIGHT|LAYOUT_CENTER_Y); + GMComboBox * genrebox = new GMComboBox(matrix,20,NULL,0,LAYOUT_FILL_X|FRAME_THICK|FRAME_SUNKEN); + db->listGenres(genrebox); + genrebox->setSortFunc(genre_list_sort); + genrebox->setNumVisible(FXMIN(10,genrebox->getNumItems())); + genrebox->sortItems(); + genrebox->setCurrentItem(-1); + if (dialog.execute()) { + FXString url=location_field->getText().trim(); + FXString name=description_field->getText().trim(); + FXString genre=genrebox->getText().trim(); + if (!url.empty()) { + if (genre.empty()) genre=fxtr("Untitled"); + db->insertStream(url,name,genre); + GMPlayerManager::instance()->getTrackView()->refresh(); + } + } + return 1; + } + + +long GMStreamSource::onCmdEditStation(FXObject*,FXSelector,void*){ + GMTextField * location_field=NULL; + FXIntList tracks; + GMPlayerManager::instance()->getTrackView()->getSelectedTracks(tracks); + FXDialogBox dialog(GMPlayerManager::instance()->getMainWindow(),fxtr("Edit Internet Radio Station"),DECOR_TITLE|DECOR_BORDER|DECOR_RESIZE|DECOR_CLOSE,0,0,0,0,0,0,0,0,0,0); + GMPlayerManager::instance()->getMainWindow()->create_dialog_header(&dialog,fxtr("Edit Internet Radio Station"),fxtr("Update url and description of station"),NULL); + FXHorizontalFrame *closebox=new FXHorizontalFrame(&dialog,LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X|PACK_UNIFORM_WIDTH,0,0,0,0); + new GMButton(closebox,fxtr("&Save"),NULL,&dialog,FXDialogBox::ID_ACCEPT,BUTTON_INITIAL|BUTTON_DEFAULT|LAYOUT_RIGHT|FRAME_RAISED|FRAME_THICK,0,0,0,0, 15,15); + new GMButton(closebox,fxtr("&Cancel"),NULL,&dialog,FXDialogBox::ID_CANCEL,BUTTON_DEFAULT|LAYOUT_RIGHT|FRAME_RAISED|FRAME_THICK,0,0,0,0, 15,15); + new FXSeparator(&dialog,SEPARATOR_GROOVE|LAYOUT_FILL_X|LAYOUT_SIDE_BOTTOM); + FXVerticalFrame * main = new FXVerticalFrame(&dialog,LAYOUT_FILL_X|LAYOUT_FILL_Y,0,0,0,0,10,5,10,10); + FXMatrix * matrix = new FXMatrix(main,2,LAYOUT_FILL_X|MATRIX_BY_COLUMNS); + if (tracks.no()==1) { + new FXLabel(matrix,fxtr("Location"),NULL,LABEL_NORMAL|LAYOUT_RIGHT|LAYOUT_CENTER_Y); + location_field = new GMTextField(matrix,40,NULL,0,LAYOUT_FILL_X|LAYOUT_FILL_COLUMN|FRAME_SUNKEN|FRAME_THICK); + } + new FXLabel(matrix,fxtr("Description"),NULL,LABEL_NORMAL|LAYOUT_RIGHT|LAYOUT_CENTER_Y); + GMTextField * description_field = new GMTextField(matrix,30,NULL,0,LAYOUT_FILL_X|LAYOUT_FILL_COLUMN|FRAME_SUNKEN|FRAME_THICK); + new FXLabel(matrix,fxtr("Genre"),NULL,LABEL_NORMAL|LAYOUT_RIGHT|LAYOUT_CENTER_Y); + GMComboBox * genrebox = new GMComboBox(matrix,20,NULL,0,LAYOUT_FILL_X|FRAME_THICK|FRAME_SUNKEN); + db->listGenres(genrebox); + genrebox->setSortFunc(genre_list_sort); + genrebox->setCurrentItem(-1); + genrebox->setNumVisible(FXMIN(10,genrebox->getNumItems())); + genrebox->sortItems(); + + GMStream info; + + if (tracks.no()==1) { + db->getStream(tracks[0],info); + location_field->setText(info.url); + description_field->setText(info.description); + genrebox->setCurrentItem(genrebox->findItem(info.genre)); + } + + if (dialog.execute()) { + FXbool changed=false; + + db->beginEdit(); + + if (tracks.no()==1 && location_field->getText()!=info.url && !location_field->getText().empty()) { + db->setStreamFilename(tracks[0],location_field->getText()); + changed=true; + } + + if (description_field->getText()!=info.description && !description_field->getText().empty()){ + for (FXint i=0;isetStreamDescription(tracks[i],description_field->getText()); + changed=true; + } + + if (genrebox->getText()!=info.genre && !genrebox->getText().empty()){ + for (FXint i=0;isetStreamGenre(tracks[i],genrebox->getText()); + changed=true; + } + + db->endEdit(); + + + if (changed) GMPlayerManager::instance()->getTrackView()->refresh(); + + } + return 1; + } + +long GMStreamSource::onCmdDeleteStation(FXObject*,FXSelector,void*){ + const FXString title=fxtr("Remove Internet Radio Station(s)?"); + const FXString subtitle=fxtr("Remove Internet Radio Station(s) from library?"); + FXDialogBox dialog(GMPlayerManager::instance()->getMainWindow(),title,DECOR_TITLE|DECOR_BORDER|DECOR_RESIZE|DECOR_CLOSE,0,0,0,0,0,0,0,0,0,0); + GMPlayerManager::instance()->getMainWindow()->create_dialog_header(&dialog,title,subtitle,NULL); + FXHorizontalFrame *closebox=new FXHorizontalFrame(&dialog,LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X|PACK_UNIFORM_WIDTH,0,0,0,0); + new FXButton(closebox,fxtr("&Remove"),NULL,&dialog,FXDialogBox::ID_ACCEPT,BUTTON_INITIAL|BUTTON_DEFAULT|LAYOUT_RIGHT|FRAME_RAISED|FRAME_THICK,0,0,0,0, 15,15); + new FXButton(closebox,fxtr("&Cancel"),NULL,&dialog,FXDialogBox::ID_CANCEL,BUTTON_DEFAULT|LAYOUT_RIGHT|FRAME_RAISED|FRAME_THICK,0,0,0,0, 15,15); +// new FXSeparator(&dialog,SEPARATOR_GROOVE|LAYOUT_FILL_X|LAYOUT_SIDE_BOTTOM); + if (dialog.execute()){ + FXIntList tracks; + GMPlayerManager::instance()->getTrackView()->getSelectedTracks(tracks); + db->beginDelete(); + + for (int i=0;iremoveStream(tracks[i])){ + FXMessageBox::error(GMPlayerManager::instance()->getMainWindow(),MBOX_OK,fxtr("Library Error"),fxtrformat("Unable to remove station from the library.")); + } + } + + db->endDelete(); + GMPlayerManager::instance()->getTrackView()->refresh(); + } + return 1; + } + + +long GMStreamSource::onUpdExport(FXObject*sender,FXSelector,void*){ + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_DISABLE),NULL); + return 1; + } + + diff --git a/src/GMStreamSource.h b/src/GMStreamSource.h new file mode 100644 index 0000000..9ebc406 --- /dev/null +++ b/src/GMStreamSource.h @@ -0,0 +1,80 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2007-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#ifndef GMSTREAMSOURCE_H +#define GMSTREAMSOURCE_H + +class GMSource; + +class GMStreamSource : public GMSource { +FXDECLARE(GMStreamSource) + GMTrackDatabase * db; +protected: + GMStreamSource(); +private: + GMStreamSource(const GMStreamSource&); + GMStreamSource& operator=(const GMStreamSource&); +public: + enum { + ID_NEW_STATION = GMSource::ID_LAST, + ID_EDIT_STATION, + ID_DELETE_STATION, + ID_LAST + }; +public: + long onCmdNewStation(FXObject*,FXSelector,void*); + long onCmdEditStation(FXObject*,FXSelector,void*); + long onCmdDeleteStation(FXObject*,FXSelector,void*); + long onUpdExport(FXObject*,FXSelector,void*); +public: + GMStreamSource(GMTrackDatabase * db); + + virtual void configure(GMColumnList&) const; + + FXbool hasCurrentTrack(GMSource * ) const; + + virtual FXbool getTrack(GMTrack & info) const; + + virtual FXbool setTrack(GMTrack & info) const; + + FXString getTrackFilename(FXint id) const; + + FXString getName() const { return notr("Internet Radio"); } + + FXint getType() const { return SOURCE_INTERNET_RADIO; } + + FXString settingKey() const { return "internet-radio"; } + + FXint getSortColumn(FXbool) const { return HEADER_TRACK; } + + FXbool canBrowse() const { return false; } + + FXbool defaultBrowse() const { return false; } + + FXbool autoPlay() const { return false; } + + FXbool source_context_menu(FXMenuPane * pane); + + FXbool track_context_menu(FXMenuPane * pane); + + FXbool listTracks(GMTrackList * tracklist,const FXIntList & albumlist,const FXIntList & genrelist); + + virtual ~GMStreamSource(); + }; + +#endif diff --git a/src/GMTag.cpp b/src/GMTag.cpp new file mode 100644 index 0000000..a241289 --- /dev/null +++ b/src/GMTag.cpp @@ -0,0 +1,1357 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2010 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#if defined(TAGLIB_WITH_MP4) && (TAGLIB_WITH_MP4==1) +#define TAGLIB_HAVE_MP4 1 +#endif + +#ifdef TAGLIB_HAVE_MP4 +#include "mp4file.h" +#include "mp4tag.h" +#include "mp4coverart.h" +#endif + + +#ifndef TAGLIB_MAJOR_VERSION +#error "missing taglib_major_version" +#endif +#define MKVERSION(major,minor,release) ((release)+(minor*1000)+(major*1000000)) +#define TAGLIB_VERSION MKVERSION(TAGLIB_MAJOR_VERSION,TAGLIB_MINOR_VERSION,TAGLIB_PATCH_VERSION) + +#include "gmdefs.h" +#include "gmutils.h" +#include "GMTag.h" + +#include "FXPNGImage.h" +#include "FXJPGImage.h" + + +static GMCover * id3v2_load_cover(TagLib::ID3v2::AttachedPictureFrame * frame,FXint scale) { + FXString mime = frame->mimeType().toCString(true); + /// Skip File Icon + if (frame->type()==TagLib::ID3v2::AttachedPictureFrame::FileIcon || + frame->type()==TagLib::ID3v2::AttachedPictureFrame::OtherFileIcon || + frame->type()==TagLib::ID3v2::AttachedPictureFrame::ColouredFish) { + return NULL; + } + FXImage * image = gm_load_image_from_data(frame->picture().data(),frame->picture().size(),mime,scale); + if (image) return new GMCover(image,frame->type()); + return NULL; + } + +static FXbool id3v2_is_front_cover(TagLib::ID3v2::AttachedPictureFrame * frame){ + if (frame->type()==TagLib::ID3v2::AttachedPictureFrame::FrontCover) + return true; + else + return false; + } + +/******************************************************************************/ +/* HELPER CLASS TO ACCESS ADDITIONAL TAGS FROM FILE */ +/******************************************************************************/ + +class GMFileTag { +public: + TagLib::File * file; + TagLib::Tag * tag; +#ifdef TAGLIB_HAVE_MP4 + TagLib::MP4::Tag * mp4; +#else + void * mp4; +#endif + TagLib::Ogg::XiphComment * xiph; + TagLib::ID3v2::Tag * id3v2; + TagLib::APE::Tag * ape; +protected: + void xiph_update_field(const FXchar * field,const FXString & value); + void xiph_update_field(const FXchar * field,const FXStringList & value); + void id3v2_update_field(const FXchar * field,const FXString & value); + void id3v2_update_field(const FXchar * field,const FXStringList & value); + void mp4_update_field(const FXchar * field,const FXString & value); + void mp4_update_field(const FXchar * field,const FXStringList & value); + void ape_update_field(const FXchar * field,const FXStringList & value); + void xiph_get_field(const FXchar * field,FXString &); + void xiph_get_field(const FXchar * field,FXStringList &); + void id3v2_get_field(const FXchar * field,FXString &); + void id3v2_get_field(const FXchar * field,FXStringList &); + void mp4_get_field(const FXchar * field,FXString &); + void mp4_get_field(const FXchar * field,FXStringList &); + void ape_get_field(const FXchar * field,FXString &); + void ape_get_field(const FXchar * field,FXStringList &); + void parse_tagids(FXStringList&); +public: + GMFileTag(const FXString & filename); + GMFileTag(TagLib::File*); + void setComposer(const FXString & value); + void setConductor(const FXString & value); + void setAlbumArtist(const FXString & value); + void setTags(const FXStringList & value); + void setDiscNumber(FXushort disc); + void getComposer(FXString &); + void getConductor(FXString &); + void getAlbumArtist(FXString &); + void getTags(FXStringList&); + void getTitle(FXString&); + FXushort getDiscNumber(); + void getGain(FXdouble & track_gain,FXdouble & track_peak,FXdouble & album_gain,FXdouble & album_peak); + }; + + +/******************************************************************************/ + +GMFileTag::GMFileTag(const FXString &) : + file(NULL), + tag(NULL), + mp4(NULL), + xiph(NULL), + id3v2(NULL), + ape(NULL) { + /// TODO + } + +GMFileTag::GMFileTag(TagLib::File * file) : + file(NULL), + tag(NULL), + mp4(NULL), + xiph(NULL), + id3v2(NULL), + ape(NULL) { + + TagLib::MPEG::File * mpgfile = NULL; + TagLib::Ogg::Vorbis::File * oggfile = NULL; + TagLib::FLAC::File * flacfile = NULL; +#ifdef TAGLIB_HAVE_MP4 + TagLib::MP4::File * mp4file = NULL; +#endif + + tag = file->tag(); + + if ((oggfile = dynamic_cast(file))) { + xiph=oggfile->tag(); + } + else if ((flacfile = dynamic_cast(file))){ + xiph=flacfile->xiphComment(); + id3v2=flacfile->ID3v2Tag(); + } + else if ((mpgfile = dynamic_cast(file))){ + id3v2=mpgfile->ID3v2Tag(); + ape=mpgfile->APETag(); + } +#ifdef TAGLIB_HAVE_MP4 + else if ((mp4file = dynamic_cast(file))){ + mp4=mp4file->tag(); + } +#endif + } + +void GMFileTag::xiph_update_field(const FXchar * field,const FXString & value) { + FXASSERT(field); + FXASSERT(xiph); + if (!value.empty()) + xiph->addField(field,TagLib::String(value.text(),TagLib::String::UTF8),true); + else + xiph->removeField(field); + } + + +void GMFileTag::xiph_update_field(const FXchar * field,const FXStringList & list) { + FXASSERT(field); + FXASSERT(xiph); + xiph->removeField(field); + for (FXint i=0;iaddField(field,TagLib::String(list[i].text(),TagLib::String::UTF8),false); + } + } + + +void GMFileTag::xiph_get_field(const FXchar * field,FXString & value) { + FXASSERT(field); + FXASSERT(xiph); + if (xiph->contains(field)) + value=xiph->fieldListMap()[field].front().toCString(true); + else + value.clear(); + } + +void GMFileTag::xiph_get_field(const FXchar * field,FXStringList & list) { + FXASSERT(field); + FXASSERT(xiph); + if (xiph->contains(field)) { + const TagLib::StringList & fieldlist = xiph->fieldListMap()[field]; + for(TagLib::StringList::ConstIterator it = fieldlist.begin(); it != fieldlist.end(); it++) { + list.append(it->toCString(true)); + } + } + else { + list.clear(); + } + } + +void GMFileTag::ape_get_field(const FXchar * field,FXString & value) { + FXASSERT(field); + FXASSERT(ape); + if (ape->itemListMap().contains(field) && !ape->itemListMap()[field].isEmpty()) + value=ape->itemListMap()[field].toString().toCString(true); + else + value.clear(); + } + +void GMFileTag::ape_get_field(const FXchar * field,FXStringList & list) { + FXASSERT(field); + FXASSERT(ape); + if (ape->itemListMap().contains(field)) { + TagLib::StringList fieldlist = ape->itemListMap()[field].toStringList(); + for(TagLib::StringList::ConstIterator it = fieldlist.begin(); it != fieldlist.end(); it++) { + list.append(it->toCString(true)); + } + } + else { + list.clear(); + } + } + +void GMFileTag::ape_update_field(const FXchar * field,const FXStringList & list) { + FXASSERT(field); + FXASSERT(ape); + ape->removeItem(field); + + TagLib::StringList values; + for (FXint i=0;isetItem(field,TagLib::APE::Item(field,values)); + } + + +void GMFileTag::id3v2_update_field(const FXchar * field,const FXString & value) { + FXASSERT(field); + FXASSERT(id3v2); + if (value.empty()) { + id3v2->removeFrames(field); + } + else if (id3v2->frameListMap().contains(field) && !id3v2->frameListMap()[field].isEmpty()) { + id3v2->frameListMap()[field].front()->setText(TagLib::String(value.text(),TagLib::String::UTF8)); + } + else { + TagLib::ID3v2::TextIdentificationFrame *frame = new TagLib::ID3v2::TextIdentificationFrame(field,TagLib::ID3v2::FrameFactory::instance()->defaultTextEncoding()); + frame->setText(TagLib::String(value.text(),TagLib::String::UTF8) ); + id3v2->addFrame(frame); + } + } + +void GMFileTag::id3v2_update_field(const FXchar * field,const FXStringList & list) { + FXASSERT(field); + FXASSERT(id3v2); + if (list.no()==0) { + id3v2->removeFrames(field); + } + else { + TagLib::ID3v2::TextIdentificationFrame * frame = NULL; + if (id3v2->frameListMap().contains(field) && !id3v2->frameListMap()[field].isEmpty()) { + frame = dynamic_cast(id3v2->frameListMap()[field].front()); + } + else { + frame = new TagLib::ID3v2::TextIdentificationFrame(field,TagLib::ID3v2::FrameFactory::instance()->defaultTextEncoding()); + id3v2->addFrame(frame); + } + FXASSERT(frame); + + TagLib::StringList values; + for (FXint i=0;isetText(values); + } + } + +void GMFileTag::id3v2_get_field(const FXchar * field,FXString & value) { + FXASSERT(field); + FXASSERT(id3v2); + if (id3v2->frameListMap().contains(field) && !id3v2->frameListMap()[field].isEmpty() ) + value=id3v2->frameListMap()[field].front()->toString().toCString(true); + else + value.clear(); + } + +void GMFileTag::id3v2_get_field(const FXchar * field,FXStringList & list) { + FXASSERT(field); + FXASSERT(id3v2); + if (id3v2->frameListMap().contains(field) && !id3v2->frameListMap()[field].isEmpty() ) { + TagLib::ID3v2::TextIdentificationFrame * frame = dynamic_cast(id3v2->frameListMap()[field].front()); + TagLib::StringList fieldlist = frame->fieldList(); + for(TagLib::StringList::ConstIterator it = fieldlist.begin(); it != fieldlist.end(); it++) { + list.append(it->toCString(true)); + } + } + else { + list.clear(); + } + } + + +void GMFileTag::mp4_update_field(const FXchar * field,const FXString & value) { +#ifdef TAGLIB_HAVE_MP4 + FXASSERT(field); + FXASSERT(mp4); + if (!value.empty()) + mp4->itemListMap()[field] = TagLib::StringList(TagLib::String(value.text(),TagLib::String::UTF8)); + else + mp4->itemListMap().erase(field); +#endif + } + + +void GMFileTag::mp4_update_field(const FXchar * field,const FXStringList & list) { +#ifdef TAGLIB_HAVE_MP4 + FXASSERT(field); + FXASSERT(mp4); + if (list.no()==0) { + mp4->itemListMap().erase(field); + } + else { + TagLib::StringList values; + for (FXint i=0;iitemListMap()[field]=values; + } +#endif + } + + +void GMFileTag::mp4_get_field(const FXchar * field,FXString & value) { +#ifdef TAGLIB_HAVE_MP4 + FXASSERT(field); + FXASSERT(mp4); + if (mp4->itemListMap().contains(field) && !mp4->itemListMap().isEmpty()) + value=mp4->itemListMap()[field].toStringList().toString(", ").toCString(true); + else + value.clear(); +#else + value.clear(); +#endif + } + + +void GMFileTag::mp4_get_field(const FXchar * field,FXStringList & list) { +#ifdef TAGLIB_HAVE_MP4 + FXASSERT(field); + FXASSERT(mp4); + if (mp4->itemListMap().contains(field) && !mp4->itemListMap().isEmpty()) { + TagLib::StringList fieldlist = mp4->itemListMap()[field].toStringList(); + for(TagLib::StringList::ConstIterator it = fieldlist.begin(); it != fieldlist.end(); it++) { + list.append(it->toCString(true)); + } + } + else + list.clear(); +#else + list.clear(); +#endif + } + + + +/******************************************************************************/ + +void GMFileTag::setComposer(const FXString & composer) { + if (xiph) + xiph_update_field("COMPOSER",composer); + else if (id3v2) + id3v2_update_field("TCOM",composer); + else if (mp4) + mp4_update_field("\251wrt",composer); + } + +void GMFileTag::getComposer(FXString & composer) { + if (xiph) + xiph_get_field("COMPOSER",composer); + else if (id3v2) + id3v2_get_field("TCOM",composer); + else if (mp4) + mp4_get_field("\251wrt",composer); + else + composer.clear(); + } + +void GMFileTag::setConductor(const FXString & conductor) { + if (xiph) + xiph_update_field("COMPOSER",conductor); + else if (id3v2) + id3v2_update_field("TPE3",conductor); + else if (mp4) + mp4_update_field("----:com.apple.iTunes:CONDUCTOR",conductor); + } + +void GMFileTag::getConductor(FXString & conductor) { + if (xiph) + xiph_get_field("COMPOSER",conductor); + else if (id3v2) + id3v2_get_field("TPE3",conductor); + else if (mp4) + mp4_get_field("----:com.apple.iTunes:CONDUCTOR",conductor); + else + conductor.clear(); + } + + +void GMFileTag::setAlbumArtist(const FXString & albumartist) { + if (xiph) + xiph_update_field("ALBUMARTIST",albumartist); + else if (id3v2) + id3v2_update_field("TPE2",albumartist); + else if (mp4) + mp4_update_field("aART",albumartist); + } + + +void GMFileTag::getAlbumArtist(FXString & albumartist) { + if (xiph) + xiph_get_field("ALBUMARTIST",albumartist); + else if (id3v2) + id3v2_get_field("TPE2",albumartist); + else if (mp4) + mp4_get_field("aART",albumartist); + else + albumartist.clear(); + } + +void GMFileTag::setTags(const FXStringList & tags){ + if (xiph) + xiph_update_field("GENRE",tags); + else if (id3v2) + id3v2_update_field("TCON",tags); + else if (mp4) + mp4_update_field("\251gen",tags); + else if (ape) + ape_update_field("GENRE",tags); + else { + if (tags.no()) + tag->setGenre(TagLib::String(tags[0].text(),TagLib::String::UTF8)); + else + tag->setGenre(TagLib::String("",TagLib::String::UTF8)); + } + } + +void GMFileTag::getTags(FXStringList & tags) { + if (xiph) + xiph_get_field("GENRE",tags); + else if (id3v2) { + id3v2_get_field("TCON",tags); + parse_tagids(tags); + } + else if (mp4) + mp4_get_field("\251gen",tags); + else if (ape) + ape_get_field("GENRE",tags); + else + tags.append(FXString(tag->genre().toCString(true))); + } + +void GMFileTag::getTitle(FXString & title){ + if (xiph) { + FXStringList titles; + xiph_get_field("TITLE",titles); + title.clear(); + if (titles.no()) { + for (FXint i=0;ititle().toCString(true); + title.trim(); + } + } + +static FXbool to_int(const FXString & str,FXint & val){ + char * endptr=NULL; + errno=0; + val=strtol(str.text(),&endptr,10); + if (errno==0) { + if (endptr==str.text()) + return false; + return true; + } + return false; + } + + +void GMFileTag::parse_tagids(FXStringList & tags){ + FXint id; + for (FXint i=tags.no()-1;i>=0;i--){ + if (to_int(tags[i],id)) { + tags[i]=TagLib::ID3v1::genre(id).toCString(true); + } + } + } + + +void GMFileTag::setDiscNumber(FXushort disc) { + if (xiph) { + if (disc>0) + xiph_update_field("DISCNUMBER",GMStringFormat("%d",disc)); + else + xiph_update_field("DISCNUMBER",FXString::null); + } + else if (id3v2) { + if (disc>0) + id3v2_update_field("TPOS",GMStringFormat("%d",disc)); + else + id3v2_update_field("TPOS",FXString::null); + } +#ifdef TAGLIB_HAVE_MP4 + else if (mp4) { + if (disc>0) + mp4->itemListMap()["disk"] = TagLib::MP4::Item(disc,0); + else + mp4->itemListMap().erase("disk"); + } +#endif + } + + +static FXushort string_to_disc_number(const FXString & disc) { + if (disc.empty()) + return 0; +#if FOXVERSION >= FXVERSION(1,7,12) + return FXMIN(disc.before('/').toUInt(),0xFFFF); +#else + return FXMIN(FXUIntVal(disc.before('/')),0xFFFF); +#endif + } + +FXushort GMFileTag::getDiscNumber() { + FXString disc; + if (xiph) { + xiph_get_field("DISCNUMBER",disc); + return string_to_disc_number(disc); + } + else if (id3v2) { + id3v2_get_field("TPOS",disc); + return string_to_disc_number(disc); + } +#ifdef TAGLIB_HAVE_MP4 + else if (mp4) { + if (mp4->itemListMap().contains("disk")) + return FXMIN(mp4->itemListMap()["disk"].toIntPair().first,0xFFFF); + } +#endif + return 0; + } + +void GMFileTag::getGain(FXdouble & track_gain,FXdouble & track_peak,FXdouble & album_gain,FXdouble & album_peak) { + track_gain=track_peak=album_gain=album_peak=NAN; + FXString tmp; + if (xiph) { + xiph_get_field("REPLAYGAIN_ALBUM_GAIN",tmp); + album_gain=gm_parse_number(tmp); + + xiph_get_field("REPLAYGAIN_ALBUM_PEAK",tmp); + album_peak=gm_parse_number(tmp); + + xiph_get_field("REPLAYGAIN_TRACK_GAIN",tmp); + track_gain=gm_parse_number(tmp); + + xiph_get_field("REPLAYGAIN_TRACK_PEAK",tmp); + track_peak=gm_parse_number(tmp); + + if (isnan(track_peak) && isnan(album_gain)) { + xiph_get_field("RG_RADIO",tmp); + track_gain=gm_parse_number(tmp); + + xiph_get_field("RG_PEAK",tmp); + track_peak=gm_parse_number(tmp); + + xiph_get_field("RG_AUDIOPHILE",tmp); + album_gain=gm_parse_number(tmp); + } + } + else if (ape) { + ape_get_field("REPLAYGAIN_ALBUM_GAIN",tmp); + album_gain=gm_parse_number(tmp); + + ape_get_field("REPLAYGAIN_ALBUM_PEAK",tmp); + album_peak=gm_parse_number(tmp); + + ape_get_field("REPLAYGAIN_TRACK_GAIN",tmp); + track_gain=gm_parse_number(tmp); + + ape_get_field("REPLAYGAIN_TRACK_PEAK",tmp); + track_peak=gm_parse_number(tmp); + } + } + + + +/******************************************************************************/ +/* GMTRACK IMPLEMENTATION */ +/******************************************************************************/ + +FXbool GMTrack::saveTag(const FXString & filename,FXuint/*opts=0*/) { + + if (!FXStat::isWritable(filename)) + return false; + + TagLib::FileRef file(filename.text(),false); + if (file.isNull() || !file.tag()) { + return false; + } + + TagLib::Tag * tag = file.tag(); + + tag->setTitle(TagLib::String(title.text(),TagLib::String::UTF8)); + tag->setArtist(TagLib::String(artist.text(),TagLib::String::UTF8)); + tag->setAlbum(TagLib::String(album.text(),TagLib::String::UTF8)); + tag->setYear(year); + tag->setTrack(getTrackNumber()); + tag->setGenre(TagLib::String(genre.text(),TagLib::String::UTF8)); + + GMFileTag filetags(file.file()); + + filetags.setDiscNumber(getDiscNumber()); + //filetags.setComposer(composer); + //filetags.setConductor(conductor); + //filetags.setTags(tags); + + if (album_artist!=artist && !album_artist.empty()) + filetags.setAlbumArtist(album_artist); + else + filetags.setAlbumArtist(FXString::null); + + return file.save(); + } + + +FXbool GMTrack::loadTag(const FXString & filename) { + + FXString disc,value; + + TagLib::FileRef file(filename.text()); + if (file.isNull() || !file.tag()) { + clear(); + return false; + } + + TagLib::Tag * tag = file.tag(); + TagLib::AudioProperties * properties = file.audioProperties(); + + mrl = filename; + artist = tag->artist().toCString(true); + album = tag->album().toCString(true); + genre = tag->genre().toCString(true); + year = tag->year(); + no = FXMIN(tag->track(),0xFFFF); + + artist.trim(); + album.trim(); + + if (properties) { + time = properties->length(); + bitrate = properties->bitrate(); + } + else { + bitrate = 0; + time = 0; + } + + GMFileTag filetags(file.file()); + + filetags.getAlbumArtist(album_artist); +// filetags.getComposer(composer); +// filetags.getConductor(conductor); + filetags.getGain(track_gain,track_peak,album_gain,album_peak); +// filetags.getTags(tags); + + filetags.getTitle(title); + + if (album_artist.empty()) + album_artist=artist; + + setDiscNumber(filetags.getDiscNumber()); +// GM_DEBUG_PRINT("gain = (%lf %lf) (%lf %lf) track: %d/%d\n",album_gain,album_peak,track_gain,track_peak,getTrackNumber(),getDiscNumber()); + return true; + } + + + + + + + + + + + + +/******************************************************************************/ +/* FLAC PICTURE LOADING */ +/******************************************************************************/ + +struct FlacPictureBlock{ + FXString mimetype; + FXString description; + FXuint type; + FXuint width; + FXuint height; + FXuint bps; + FXuint ncolors; + FXuint data_size; + FXuchar* data; + + FXuint size() const { + return 64 + description.length() + mimetype.length() + data_size; + } + }; + + +#if TAGLIB_VERSION < MKVERSION(1,7,0) + +#if FOX_BIGENDIAN == 0 +#define FLAC_LAST_BLOCK 0x80 +#define FLAC_BLOCK_TYPE_MASK 0x7f +#define FLAC_BLOCK_TYPE(h) (h&0x7f) +#define FLAC_BLOCK_SIZE(h) ( ((h&0xFF00)<<8) | ((h&0xFF0000)>>8) | ((h&0xFF000000)>>24) ) +#define FLAC_BLOCK_SET_TYPE(h,type) (h|=(type&FLAC_BLOCK_TYPE_MASK)) +#define FLAC_BLOCK_SET_SIZE(h,size) (h|=(((size&0xFF)<<24) | ((size&0xFF0000)>>16) | ((size&0xFF00)<<8))) +#else +#define FLAC_LAST_BLOCK 0x80000000 +#define FLAC_BLOCK_TYPE_MASK 0x7F000000 +#define FLAC_BLOCK_TYPE(h) ((h&0x7F000000)>>24) +#define FLAC_BLOCK_SIZE(h) (h&0xFFFFFF) +#define FLAC_BLOCK_SET_TYPE(h,type) (h|=((type<<24)&FLAC_BLOCK_TYPE_MASK)) +#define FLAC_BLOCK_SET_SIZE(h,size) (h|=(size&0xFFFFFF)) +#endif + + +enum { + FLAC_BLOCK_STREAMINFO = 0, + FLAC_BLOCK_PADDING = 1, + FLAC_BLOCK_VORBIS_COMMENT = 4, + FLAC_BLOCK_PICTURE = 6 + }; + + +static FXbool gm_read_uint32_be(FXIO & io,FXuint & v) { +#if FOX_BIGENDIAN == 0 + FXuchar block[4]; + if (io.readBlock(block,4)!=4) return false; + ((FXuchar*)&v)[3]=block[0]; + ((FXuchar*)&v)[2]=block[1]; + ((FXuchar*)&v)[1]=block[2]; + ((FXuchar*)&v)[0]=block[3]; +#else + if (io.readBlock(&v,4)!=4) return false; +#endif + return true; + } + + +static FXbool gm_read_string_be(FXIO & io,FXString & v) { + FXuint len=0; + gm_read_uint32_be(io,len); + if (len>0) { + v.length(len); + if (io.readBlock(&v[0],len)!=len) + return false; + } + return true; + } + +FXbool gm_flac_is_front_cover(FXIO & io) { + FlacPictureBlock picture; + gm_read_uint32_be(io,picture.type); + if (picture.type==GMCover::FrontCover) + return true; + else + return false; + } + +GMCover * gm_flac_parse_block_picture(FXIO & io,FXint scale) { + GMCover* cover=NULL; + FlacPictureBlock picture; + + gm_read_uint32_be(io,picture.type); + + /// Skip useless icons + if (picture.type==GMCover::FileIcon || picture.type==GMCover::OtherFileIcon || + picture.type==GMCover::Fish) { + return NULL; + } + + gm_read_string_be(io,picture.mimetype); + gm_read_string_be(io,picture.description); + gm_read_uint32_be(io,picture.width); + gm_read_uint32_be(io,picture.height); + gm_read_uint32_be(io,picture.bps); + gm_read_uint32_be(io,picture.ncolors); + gm_read_uint32_be(io,picture.data_size); + + if (picture.data_size>0) { + allocElms(picture.data,picture.data_size); + if (io.readBlock(picture.data,picture.data_size)==picture.data_size) { + FXImage * image = gm_load_image_from_data(picture.data,picture.data_size,picture.mimetype,scale); + if (image) { + cover = new GMCover(image,picture.type,picture.description); + } + } + freeElms(picture.data); + } + return cover; + } + + +static FXbool gm_flac_parse_header(FXIO & io,FXuint & header) { + FXchar marker[4]; + + if (io.readBlock(marker,4)!=4 || compare(marker,"fLaC",4)) + return false; + + if (io.readBlock(&header,4)!=4 || FLAC_BLOCK_TYPE(header)!=FLAC_BLOCK_STREAMINFO || FLAC_BLOCK_SIZE(header)!=34 || (header&FLAC_LAST_BLOCK)) + return false; + + /// Go to beginning of meta data + io.position(34,FXIO::Current); + return true; + } + +static FXbool gm_flac_next_block(FXIO & io,FXuint & header) { + if (!(header&FLAC_LAST_BLOCK) && (io.readBlock(&header,4)==4)) + return true; + else + return false; + } + +static FXint flac_load_covers(const FXString & mrl,GMCoverList & covers,FXint scale) { + FXuint header; + FXFile io; + + if (io.open(mrl,FXIO::Reading)) { + + if (!gm_flac_parse_header(io,header)) + return 0; + + while(gm_flac_next_block(io,header)) { + if (FLAC_BLOCK_TYPE(header)==FLAC_BLOCK_PICTURE) { + GMCover * cover = gm_flac_parse_block_picture(io,scale); + if (cover) covers.append(cover); + } + else if (!(header&FLAC_LAST_BLOCK)){ + io.position(FLAC_BLOCK_SIZE(header),FXIO::Current); + } + } + } + return covers.no(); + } + +static GMCover * flac_load_cover(const FXString & mrl,FXint scale) { + FXuint header; + FXlong pos; + FXFile io; + + if (io.open(mrl,FXIO::Reading)) { + + if (!gm_flac_parse_header(io,header)) + return 0; + + while(gm_flac_next_block(io,header)) { + if (FLAC_BLOCK_TYPE(header)==FLAC_BLOCK_PICTURE) { + pos=io.position(); + if (gm_flac_is_front_cover(io)) { + io.position(pos,FXIO::Begin); + GMCover * cover = gm_flac_parse_block_picture(io,scale); + if (cover) return cover; + } + } + else if (!(header&FLAC_LAST_BLOCK)){ + io.position(FLAC_BLOCK_SIZE(header),FXIO::Current); + } + } + } + return NULL; + } + +#endif + + + + +static FXbool gm_uint32_be(const FXuchar * block,FXuint & v) { +#if FOX_BIGENDIAN == 0 + ((FXuchar*)&v)[3]=block[0]; + ((FXuchar*)&v)[2]=block[1]; + ((FXuchar*)&v)[1]=block[2]; + ((FXuchar*)&v)[0]=block[3]; +#else + ((FXuchar*)&v)[3]=block[3]; + ((FXuchar*)&v)[2]=block[2]; + ((FXuchar*)&v)[1]=block[1]; + ((FXuchar*)&v)[0]=block[0]; +#endif + return true; + } + + +static GMCover * gm_flac_parse_block_picture(const FXuchar * buffer,FXint len,FXint scale) { + FlacPictureBlock picture; + const FXuchar * p = buffer; + FXuint sz; + gm_uint32_be(p,picture.type); + + /// Skip useless icons + if (picture.type==GMCover::FileIcon || picture.type==GMCover::OtherFileIcon || + picture.type==GMCover::Fish) { + return NULL; + } + p+=4; + + gm_uint32_be(p,sz); + picture.mimetype.length(sz); + picture.mimetype.assign((const FXchar*)p+4,sz); + + p+=(4+sz); + + gm_uint32_be(p,sz); + picture.description.length(sz); + picture.description.assign((const FXchar*)p+4,sz); + + p+=(4+sz); + + gm_uint32_be(p+0,picture.width); + gm_uint32_be(p+4,picture.height); + gm_uint32_be(p+8,picture.bps); + gm_uint32_be(p+12,picture.ncolors); + gm_uint32_be(p+16,picture.data_size); + + if (picture.data_size>0) { + picture.data = (FXuchar*) p+20; + if (picture.data+picture.data_size>buffer+len) + return NULL; + FXImage * image = gm_load_image_from_data(picture.data,picture.data_size,picture.mimetype,scale); + if (image) return new GMCover(image,picture.type,picture.description); + } + return NULL; + } + + + +static GMCover * xiph_load_cover(const TagLib::ByteVector & tbuf,FXint scale) { + GMCover * cover = NULL; + if (tbuf.size()) { + FXuchar * buffer=NULL; + FXint len=tbuf.size(); + + allocElms(buffer,len); + memcpy(buffer,tbuf.data(),len); + if (gm_decode_base64(buffer,len)) { + cover = gm_flac_parse_block_picture(buffer,len,scale); + } + freeElms(buffer); + } + return cover; + } + + +namespace GMTag { + +void init(){ + TagLib::ID3v2::FrameFactory::instance()->setDefaultTextEncoding(TagLib::String::UTF16); + } + + +FXbool length(GMTrack & info) { + TagLib::FileRef file(info.mrl.text()); + if (file.isNull()) + return false; + TagLib::AudioProperties *prop = file.audioProperties(); + if (prop) + info.time = prop->length(); + else + info.time = 0; + return true; + } + + +FXbool properties(const FXString & mrl,Properties & p) { + p.bitrate=-1; + p.samplerate=-1; + p.channels=-1; + + TagLib::FileRef file(mrl.text()); + if (file.isNull()) + return false; + + TagLib::AudioProperties *prop = file.audioProperties(); + + if (!prop) + return false; + + p.bitrate = prop->bitrate(); + p.samplerate = prop->sampleRate(); + p.channels = prop->channels(); + return true; + } +} + + + +GMCover::GMCover() : image(NULL), type(0) { + } + +GMCover::GMCover(FXImage * img,FXuint t,const FXString & label) : image(img),description(label),type(t) { + } + +GMCover::~GMCover() { + if (image) { + delete image; + image=NULL; + } + } + +#if TAGLIB_VERSION >= MKVERSION(1,7,0) +GMCover* flac_load_cover_from_taglib(const TagLib::FLAC::Picture * picture,FXint scale) { + GMCover * cover=NULL; + if (picture) { + if (picture->type()==TagLib::FLAC::Picture::FileIcon || + picture->type()==TagLib::FLAC::Picture::OtherFileIcon || + picture->type()==TagLib::FLAC::Picture::ColouredFish) { + return NULL; + } + + FXImage * image = gm_load_image_from_data(picture->data().data(),picture->data().size(),picture->mimeType().toCString(true),scale); + if (image) { + cover = new GMCover(image,picture->type(),picture->description().toCString(true)); + } + } + return cover; + } + +GMCover* flac_load_frontcover_from_taglib(const TagLib::FLAC::Picture * picture,FXint scale) { + GMCover * cover=NULL; + if (picture && picture->type()==TagLib::FLAC::Picture::FrontCover) { + FXImage * image = gm_load_image_from_data(picture->data().data(),picture->data().size(),picture->mimeType().toCString(true),scale); + if (image) { + cover = new GMCover(image,picture->type(),picture->description().toCString(true)); + } + } + return cover; + } +#endif + + + +FXint GMCover::fromTag(const FXString & mrl,GMCoverList & covers,FXint scale/*=0*/) { + FXString extension = FXPath::extension(mrl); + +#if TAGLIB_VERSION < MKVERSION(1,7,0) + if (comparecase(extension,"flac")==0){ + flac_load_covers(mrl,covers,scale); + if (covers.no()) return (covers.no()); + } +#endif + + TagLib::FileRef file(mrl.text(),false); + if (file.isNull() || !file.tag()) { + return 0; + } + +#if TAGLIB_VERSION >= MKVERSION(1,7,0) + TagLib::FLAC::File * flacfile = dynamic_cast(file.file()); + if (flacfile) { + const TagLib::List picturelist = flacfile->pictureList(); + for(TagLib::List::ConstIterator it = picturelist.begin(); it != picturelist.end(); it++) { + GMCover * cover = flac_load_cover_from_taglib((*it),scale); + if (cover) covers.append(cover); + } + if (covers.no()) { + return (covers.no()); + } + } +#endif + + GMFileTag tags(file.file()); + FXIconSource src(FXApp::instance()); + if (tags.xiph) { + if (tags.xiph->contains("METADATA_BLOCK_PICTURE")) { + const TagLib::StringList & coverlist = tags.xiph->fieldListMap()["METADATA_BLOCK_PICTURE"]; + for(TagLib::StringList::ConstIterator it = coverlist.begin(); it != coverlist.end(); it++) { + GMCover * cover = xiph_load_cover((*it).data(TagLib::String::UTF8),scale); + if (cover) covers.append(cover); + } + } + } + if (tags.id3v2) { + TagLib::ID3v2::FrameList framelist = tags.id3v2->frameListMap()["APIC"]; + if(!framelist.isEmpty()){ + for(TagLib::ID3v2::FrameList::Iterator it = framelist.begin(); it != framelist.end(); it++) { + TagLib::ID3v2::AttachedPictureFrame * frame = dynamic_cast(*it); + GMCover * cover = id3v2_load_cover(frame,scale); + if (cover) covers.append(cover); + } + } + } +#ifdef TAGLIB_HAVE_MP4 + else if (tags.mp4) { + if (tags.mp4->itemListMap().contains("covr")) { + TagLib::MP4::CoverArtList coverlist = tags.mp4->itemListMap()["covr"].toCoverArtList(); + for(TagLib::MP4::CoverArtList::Iterator it = coverlist.begin(); it != coverlist.end(); it++) { + FXImage * img = NULL; + + if (it->format()==TagLib::MP4::CoverArt::PNG) + img = gm_load_image_from_data(it->data().data(),it->data().size(),FXPNGImage::fileExt,scale); + else if (it->format()==TagLib::MP4::CoverArt::JPEG) + img = gm_load_image_from_data(it->data().data(),it->data().size(),FXJPGImage::fileExt,scale); + + if (img) + covers.append(new GMCover(img,0)); + } + } + } +#endif + return covers.no(); + } + +FXint GMCover::fromPath(const FXString & path,GMCoverList & list,FXint scale/*=0*/) { + FXString * files=NULL; + FXImage * image; + FXIconSource src(FXApp::instance()); + + FXint nfiles = FXDir::listFiles(files,path,"*.(png,jpg,jpeg,bmp,gif)",FXDir::NoDirs|FXDir::NoParent|FXDir::CaseFold|FXDir::HiddenFiles); + if (nfiles) { + for (FXint i=0;i= MKVERSION(1,7,0) + TagLib::FLAC::File * flacfile = dynamic_cast(file.file()); + if (flacfile) { + const TagLib::List picturelist = flacfile->pictureList(); + for(TagLib::List::ConstIterator it = picturelist.begin(); it != picturelist.end(); it++) { + GMCover * cover = flac_load_frontcover_from_taglib((*it),scale); + if (cover) { return cover;} + } + } +#endif + if (tags.id3v2) { + TagLib::ID3v2::FrameList framelist = tags.id3v2->frameListMap()["APIC"]; + if(!framelist.isEmpty()){ + /// First Try Front Cover + for(TagLib::ID3v2::FrameList::Iterator it = framelist.begin(); it != framelist.end(); it++) { + TagLib::ID3v2::AttachedPictureFrame * frame = dynamic_cast(*it); + FXASSERT(frame); + if (id3v2_is_front_cover(frame)) { + GMCover * cover = id3v2_load_cover(frame,scale); + if (cover) { + return cover;} + } + } + for(TagLib::ID3v2::FrameList::Iterator it = framelist.begin(); it != framelist.end(); it++) { + TagLib::ID3v2::AttachedPictureFrame * frame = dynamic_cast(*it); + FXASSERT(frame); + GMCover * cover = id3v2_load_cover(frame,scale); + if (cover) { return cover;} + } + } + } + else if (tags.xiph) { + if (tags.xiph->contains("METADATA_BLOCK_PICTURE")) { + const TagLib::StringList & coverlist = tags.xiph->fieldListMap()["METADATA_BLOCK_PICTURE"]; + for(TagLib::StringList::ConstIterator it = coverlist.begin(); it != coverlist.end(); it++) { + GMCover * cover = xiph_load_cover((*it).data(TagLib::String::UTF8),scale); + if (cover) { return cover;} + } + } + } +#ifdef TAGLIB_HAVE_MP4 + if (tags.mp4) { /// MP4 + if (tags.mp4->itemListMap().contains("covr")) { + TagLib::MP4::CoverArtList coverlist = tags.mp4->itemListMap()["covr"].toCoverArtList(); + for(TagLib::MP4::CoverArtList::Iterator it = coverlist.begin(); it != coverlist.end(); it++) { + FXImage * img = NULL; + if (it->format()==TagLib::MP4::CoverArt::PNG) + img = gm_load_image_from_data(it->data().data(),it->data().size(),FXPNGImage::fileExt,scale); + else if (it->format()==TagLib::MP4::CoverArt::JPEG) + img = gm_load_image_from_data(it->data().data(),it->data().size(),FXJPGImage::fileExt,scale); + if (img) { return new GMCover(img,0); } + } + } + } +#endif + return NULL; + } + + + +GMCover * GMCover::fromPath(const FXString & path,FXint scale/*=0*/) { + static const FXchar * covernames[]={"cover","album","front","albumart",".folder","folder",NULL}; + FXString * files=NULL; + FXString * names=NULL; + FXImage * image=NULL; + + FXIconSource src(FXApp::instance()); + + FXint nfiles = FXDir::listFiles(files,path,"*.(png,jpg,jpeg,bmp,gif)",FXDir::NoDirs|FXDir::NoParent|FXDir::CaseFold|FXDir::HiddenFiles); + if (nfiles) { + names = new FXString[nfiles]; + for (FXint i=0;iimage; + cover->image=NULL; + delete cover; + return image; + } + return NULL; + } + + +FXIcon * GMCover::toIcon(GMCover * cover) { + if (cover) { + FXImage * image = GMCover::toImage(cover); + if (image) { + FXIcon * icon = new FXIcon(FXApp::instance(),image->getData(),0,IMAGE_OWNED,image->getWidth(),image->getHeight()); + FXImageOwner::clear(image); + delete image; + return icon; + } + } + return NULL; + } + + +GMTrack::GMTrack() : + year(0), + no(0), + time(0), + bitrate(0), + album_gain(NAN), + album_peak(NAN), + track_gain(NAN), + track_peak(NAN){ + } + +void GMTrack::clear() { + path.clear(); + title.clear(); + artist.clear(); + album.clear(); + album_artist.clear(); + genre.clear(); + year=0; + no=0; + time=0; + bitrate=0; + album_gain=NAN; + album_peak=NAN; + track_gain=NAN; + track_peak=NAN; + } + + + diff --git a/src/GMTag.h b/src/GMTag.h new file mode 100644 index 0000000..3d2bed8 --- /dev/null +++ b/src/GMTag.h @@ -0,0 +1,148 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#ifndef GMTAG_H +#define GMTAG_H + + +/// Album number consists of a disc number + track number +#define GMALBUMNO(disc,track) ((((FX::FXuint)(track))&0xffff) | (((FX::FXuint)(disc))<<16)) + +/// Get the disc number from a album no. +#define GMDISCNO(s) ((FX::FXushort)(((s)>>16)&0xffff)) + +/// Get the track number from a album no. +#define GMTRACKNO(s) ((FX::FXushort)((s)&0xffff)) + +class GMTrack{ +public: + FXString path; + FXString mrl; + FXString title; + FXString artist; + FXString album; + FXString album_artist; + FXString genre; + FXint year; + FXint no; + FXint time; + FXint bitrate; + FXdouble album_gain; + FXdouble album_peak; + FXdouble track_gain; + FXdouble track_peak; +public: + GMTrack(); + + /// Get track number + FXushort getTrackNumber() const { return (FXushort)(no&0xffff); } + + /// Set Track Number + void setTrackNumber(FXushort track) { no|=((FXuint)track)&0xffff; } + + /// Set Disc Number + void setDiscNumber(FXushort disc) { no|=((FXuint)disc)<<16; } + + /// Get disc number + FXushort getDiscNumber() const { return (FXushort)(no>>16); } + + /// Load from tag in given filename. Note that mrl is not set + FXbool loadTag(const FXString & filename); + + /// Save to tag in given filename. Note that mrl is not set + FXbool saveTag(const FXString & filename,FXuint opts=0); + + FXbool loadPath(const FXString & mrl); + + static FXbool fromPath(const FXString & mrl,GMTrack & track); + + void clear(); + }; + + + + +class GMCover; +typedef FXArray GMCoverList; + +class GMCover { +public: + enum { + Other = 0, + FileIcon = 1, + OtherFileIcon = 2, + FrontCover = 3, + BackCover = 4, + Leaflet = 5, + Media = 6, + LeadArtist = 7, + Artist = 8, + Conductor = 9, + Band = 10, + Composer = 11, + Lyricist = 12, + RecordingLocation = 13, + DuringRecoding = 14, + DuringPerformance = 15, + ScreenCapture = 16, + Fish = 17, + Illustration = 18, + BandLogo = 19, + PublisherLogo = 20 + }; +public: + FXImage * image; + FXString description; + FXuint type; +public: + GMCover(); + GMCover(FXImage * img,FXuint t=GMCover::Other,const FXString & label=FXString::null); + ~GMCover(); +public: + static FXint fromTag(const FXString & mrl,GMCoverList & list,FXint scale=0); + + static FXint fromPath(const FXString & mrl,GMCoverList & list,FXint scale=0); + + static GMCover * fromTag(const FXString & mrl,FXint scale=0); + + static GMCover * fromPath(const FXString & mrl,FXint scale=0); + + static FXImage * toImage(GMCover*); + + static FXIcon * toIcon(GMCover*); + }; + +namespace GMTag { + + +struct Properties { + FXint bitrate; + FXint samplerate; + FXint channels; + }; + +void init(); + +FXbool properties(const FXString & mrl,Properties & p); + +FXbool length(GMTrack & info); +} + + + +#endif diff --git a/src/GMTaskManager.cpp b/src/GMTaskManager.cpp new file mode 100644 index 0000000..ec44163 --- /dev/null +++ b/src/GMTaskManager.cpp @@ -0,0 +1,121 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2010 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#include "gmdefs.h" +#include "gmutils.h" +#include "GMApp.h" +#include "GMTaskManager.h" + + +GMTask::GMTask(FXObject*tgt,FXSelector sel) : taskmanager(NULL),mc(NULL),processing(true),target(tgt),message(sel) { + } + +GMTask::~GMTask() { + } + +GMTaskManager::GMTaskManager(FXObject*tgt,FXSelector sel) : processing(false),started(false),active(NULL),target(tgt),message(sel),mc(FXApp::instance()) { + } + +GMTaskManager::~GMTaskManager() { + } + +void GMTaskManager::setStatus(const FXString & msg) { + if (target) mc.message(target,FXSEL(SEL_TASK_STATUS,message),msg.text(),msg.length()+1); + } + +void GMTaskManager::run(GMTask* task) { + mutex.lock(); + tasks.append(task); + task->mc = &mc; + task->taskmanager = this; + processing=true; + if (!started) { + started=true; + mutex.unlock(); + start(); + } + else { + condition_task.signal(); + mutex.unlock(); + } + } + +FXbool GMTaskManager::next() { + mutex.lock(); + if (tasks.no()) { + active = tasks[0]; + tasks.erase(0); + } + else { + active=NULL; + } + mutex.unlock(); + return ((active!=NULL) && processing); + } + +FXbool GMTaskManager::wait() { + if (processing) { + if (target) mc.message(target,FXSEL(SEL_TASK_IDLE,message),NULL,0); + mutex.lock(); + condition_task.wait(mutex); + mutex.unlock(); + } + return processing; + } + +FXint GMTaskManager::run() { + ap_set_thread_name("gm_taskmanager"); + do { + while(next()) { + if (target) mc.message(target,FXSEL(SEL_TASK_RUNNING,message),NULL,0); + FXint code = active->run(); + mutex.lock(); + if (target) { + + if (code) + mc.message(active->target,FXSEL(SEL_TASK_CANCELLED,active->message),&active,sizeof(GMTask*)); + else + mc.message(active->target,FXSEL(SEL_TASK_COMPLETED,active->message),&active,sizeof(GMTask*)); + + active=NULL; + } + else { + delete active; + active=NULL; + } + mutex.unlock(); + } + } + while(wait()); + return 0; + } + +void GMTaskManager::cancelTask() { + FXMutexLock lock(mutex); + if (active) active->processing=false; + } + +void GMTaskManager::shutdown() { + mutex.lock(); + if (active) active->processing=false; + processing=false; + condition_task.signal(); + mutex.unlock(); + join(); + started=false; + } diff --git a/src/GMTaskManager.h b/src/GMTaskManager.h new file mode 100644 index 0000000..ad6c469 --- /dev/null +++ b/src/GMTaskManager.h @@ -0,0 +1,83 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2010 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#ifndef GMTHREAD_H +#define GMTHREAD_H + +class GMTaskManager; + +class GMTask { +friend class GMTaskManager; +private: + GMTask(const GMTask&); + GMTask &operator=(const GMTask&); +protected: + GMTaskManager * taskmanager; + FXMessageChannel * mc; + volatile FXbool processing; +protected: + FXObject * target; + FXSelector message; +public: + GMTask(FXObject*tgt=NULL,FXSelector sel=0); + + void setTarget(FXObject * tgt) { target=tgt; } + + void setSelector(FXSelector sel) { message=sel; } + + virtual FXint run() = 0; + + virtual ~GMTask(); + }; + +typedef FXArray GMTaskList; + +class GMTaskManager : public FXThread { +protected: + FXMutex mutex; + FXCondition condition_task; + volatile FXbool processing; + FXbool started; +protected: + FXbool wait(); + FXbool next(); +protected: + GMTask * active; + FXObject* target; + FXSelector message; + FXMessageChannel mc; + GMTaskList tasks; +protected: + FXint run(); +public: + GMTaskManager(FXObject*tgt=NULL,FXSelector sel=0); + + void run(GMTask*); + void shutdown(); + void cancelTask(); + + void setStatus(const FXString &); + + virtual ~GMTaskManager(); + }; + + +#endif + + + diff --git a/src/GMThread.cpp b/src/GMThread.cpp new file mode 100644 index 0000000..3c5555d --- /dev/null +++ b/src/GMThread.cpp @@ -0,0 +1,35 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#include "gmdefs.h" +#include "GMThread.h" + +GMThread::GMThread(FXObject*tgt) : feedback(FXApp::instance()),target(tgt),running(TRUE){ + } + +GMThread::~GMThread(){ + } + +void GMThread::dispose_and_join(){ + dispose(); + join(); + } + +void GMThread::dispose() { + running=FALSE; + } diff --git a/src/GMThread.h b/src/GMThread.h new file mode 100644 index 0000000..7f19259 --- /dev/null +++ b/src/GMThread.h @@ -0,0 +1,44 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#ifndef GMTHREAD_H +#define GMTHREAD_H + +class GMThread : public FXThread { +public: + FXMessageChannel feedback; + FXObject * target; +protected: + volatile FXbool running; +protected: + virtual FXint run()=0; +public: + GMThread(FXObject*); + /// Cleanup + void dispose(); + + /// Cleanup + void dispose_and_join(); + + virtual ~GMThread(); + }; + +#endif + + + diff --git a/src/GMTrackDatabase.cpp b/src/GMTrackDatabase.cpp new file mode 100644 index 0000000..23ef5dd --- /dev/null +++ b/src/GMTrackDatabase.cpp @@ -0,0 +1,2834 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#include "GMTrackDatabase.h" +#include "GMTag.h" +#include "GMTrackList.h" +#include "GMList.h" +#include "GMSource.h" + +/// For listing default genres +#include + +#define GOGGLESMM_DATABASE_SCHEMA_VERSION 2009 +/* + +***************************************************** + Goggles Music Manager Database Schema v2009 +***************************************************** + + TABLE tracks + id INTEGER NOT NULL * + path INTEGER + mrl TEXT + title TEXT NOT NULL + time INTEGER + no INTEGER + year INTEGER + playcount INTEGER + playdate INTEGER + importdate INTEGER + album INTEGER NOT NULL + genre INTEGER + rating INTEGER + artist INTEGER NOT NULL + replay_gain REAL + replay_peak REAL + + TABLE genres + id INTEGER NOT NULL * + name TEXT NOT NULL UNIQUE + + TABLE artists + id INTEGER NOT NULL * + name TEXT NOT NULL UNIQUE + + TABLE albums + id INTEGER NOT NULL * + name TEXT + artists INTEGER NOT NULL + year INTEGER NOT NULL + replay_gain REAL + replay_peak REAL + + TABLE playlists + id INTEGER NOT NULL * + name TEXT + + TABLE playlist_tracks + playlist INTEGER + track INTEGER + queue INTEGER + + +*/ + +static FXString last_dir; +static FXint last_path=-1; + + + +const FXchar create_streams[]="CREATE TABLE streams ( id INTEGER NOT NULL, " + "url TEXT, " + "description TEXT, " + "genre INTEGER, " + "bitrate INTEGER, " + "rating INTEGER, " + "PRIMARY KEY (id) );"; + +const FXchar create_tracks[]="CREATE TABLE tracks ( id INTEGER NOT NULL, " + "path INTEGER, " + "mrl TEXT, " + "title TEXT NOT NULL, " + "time INTEGER, " + "no INTEGER, " + "year INTEGER, " + "playcount INTEGER, " + "playdate INTEGER, " + "importdate INTEGER, " + "album INTEGER NOT NULL, " + "genre INTEGER, " + "rating INTEGER, " + "artist INTEGER NOT NULL, " + "replay_gain REAL, " + "replay_peak REAL, " + "PRIMARY KEY (id) );"; + +const FXchar create_genres[]="CREATE TABLE genres ( id INTEGER NOT NULL, name TEXT NOT NULL UNIQUE, PRIMARY KEY (id) );"; +const FXchar create_albums[]="CREATE TABLE albums ( id INTEGER NOT NULL, " + "name TEXT NOT NULL, " + "artist INTEGER NOT NULL, " + "year INTEGER, " + "replay_gain REAL, " + "replay_peak REAL, " + "PRIMARY KEY (id) );"; +const FXchar create_artists[]="CREATE TABLE artists ( id INTEGER NOT NULL, name TEXT NOT NULL UNIQUE, PRIMARY KEY (id) );"; + + +const FXchar create_playlists[]="CREATE TABLE playlists ( id INTEGER NOT NULL, name TEXT, PRIMARY KEY (id) );"; +const FXchar create_playlist_tracks[]="CREATE TABLE playlist_tracks ( playlist INTEGER NOT NULL, track INTEGER NOT NULL, queue INTEGER );"; + +const FXchar create_directories[]="CREATE TABLE directories ( id INTEGER PRIMARY KEY,parent INTEGER,name TEXT,path TEXT);"; + + +const FXchar create_trigger_diradd[]="CREATE TRIGGER directories_insert AFTER INSERT ON directories " + "BEGIN " + "UPDATE directories SET path = " + "CASE WHEN parent ISNULL THEN '/' " + "ELSE (SELECT path FROM directories WHERE id = new.parent) || parent || '/' " + "END " + "WHERE id = new.id; " + "END;"; + +// Recursive Trigger not supported by SQLite +//const FXchar create_trigger_dirdel[]="CREATE TRIGGER directories_delete AFTER DELETE ON directories " +// "BEGIN " +// "DELETE FROM directories WHERE id IN " +// "(SELECT id FROM directories WHERE path LIKE old.path || old.id || '/%' ); " +// "END;"; + + +GMTrackDatabase::GMTrackDatabase() { + } + +GMTrackDatabase::~GMTrackDatabase() { + } + + +FXbool GMTrackDatabase::clearTracks(FXbool removeplaylists){ + + if (!db.execute("DELETE FROM tracks;")) + return false; + + if (!db.execute("DELETE FROM artists")) + return false; + + if (!db.execute("DELETE FROM albums;")) + return false; + + if (!db.execute("DELETE FROM playlist_tracks;")) + return false; + + if (removeplaylists) { + if (!db.execute("DELETE FROM playlists;")) + return false; + } + + if (!db.execute("DELETE FROM directories;")) + return false; + + last_path=-1; + + if (!db.execute("DELETE FROM genres WHERE id NOT IN (SELECT DISTINCT(genre) FROM tracks) AND id NOT IN (SELECT DISTINCT(genre) FROM streams);")) + return false; + + return true; + } + + +void GMTrackDatabase::clear(){ + db.clearTables(); + } + +FXbool GMTrackDatabase::init(const FXString & database,FXbool checkversion) { + if (!db.open(database)) + goto error; + + if (checkversion && db.getVersion()>GOGGLESMM_DATABASE_SCHEMA_VERSION) { + if (FXMessageBox::question(FXApp::instance(),MBOX_OK_CANCEL,fxtr("Database Error"),fxtr("An incompatible (future) version of the database was found.\nThis usually happens when you try to downgrade to a older version of GMM\nPress OK to continue and reset the database (all information will be lost!).\nPress Cancel to quit now and leave the database as is."))==MBOX_CLICKED_CANCEL) + return false; + } + + if (!setup_tables()) + goto error; + + if (!setup_queries()) + goto error; + + return true; +error: + FXMessageBox::error(FXApp::instance(),MBOX_OK,fxtr("Fatal Error"),fxtr("Goggles Music Manager was unable to open the database.\nThe database may have been corrupted. Please remove ~/.goggles/goggles.db to try again.\nif the error keeps occuring, please file an issue at http://code.google.com/p/gogglesmm")); + return false; + } + + + + + + + +void GMTrackDatabase::setup_insert_queries() { + } + + +void GMTrackDatabase::clear_insert_queries() { + } + + +void GMTrackDatabase::beginDelete() { + delete_artist.compile(db,"DELETE FROM artists WHERE id == ?;"); + delete_genre.compile(db,"DELETE FROM genres WHERE id == ?;"); + delete_album.compile(db,"DELETE FROM albums WHERE id == ?;"); + delete_track.compile(db,"DELETE FROM tracks WHERE id == ?;"); + delete_track_artist.compile(db,"DELETE FROM tracks WHERE artist == ? OR album IN ( SELECT id FROM albums WHERE artist == ?) ;"); + delete_track_album.compile(db,"DELETE FROM tracks WHERE album == ?;"); + delete_albums_artist.compile(db,"DELETE FROM albums WHERE artist == ?;"); + delete_track_from_playlist.compile(db,"DELETE FROM playlist_tracks WHERE track == ? AND playlist == ?;"); + delete_track_from_playlists.compile(db,"DELETE FROM playlist_tracks WHERE track == ?;"); + delete_artist_from_playlists.compile(db,"DELETE FROM playlist_tracks WHERE track IN ( SELECT id FROM tracks WHERE artist == ? OR album IN (SELECT album FROM albums WHERE artist == ?));"); + delete_album_from_playlists.compile(db,"DELETE FROM playlist_tracks WHERE track IN (SELECT id FROM tracks WHERE album == ?);"); + query_track_no.compile(db,"SELECT no FROM tracks WHERE id == ?;"); + } + +void GMTrackDatabase::endDelete(FXbool vac){ + if (vac) vacuum(); + delete_artist.clear(); + delete_genre.clear(); + delete_track.clear(); + delete_track_artist.clear(); + delete_track_album.clear(); + delete_album.clear(); + delete_albums_artist.clear(); + delete_artist_from_playlists.clear(); + delete_album_from_playlists.clear(); + delete_track_from_playlist.clear(); + delete_track_from_playlists.clear(); + query_track_no.clear(); + } + +void GMTrackDatabase::beginEdit() { + update_track_genre_id.compile(db,"UPDATE tracks SET genre = ? WHERE id == ?;"); + update_mrl.compile(db,"UPDATE tracks SET path = ?, mrl = ? WHERE id == ?;"); + update_artist_album_id.compile(db,"UPDATE albums SET artist = ? WHERE id == ?;"); + update_track_title.compile(db,"UPDATE tracks SET title = ? WHERE id == ?;"); + update_track_album_id.compile(db,"UPDATE tracks SET album = ? WHERE id == ?;"); + update_track_genre.compile(db,"UPDATE tracks SET genre = ? WHERE genre == ?;"); + update_track_artist.compile(db,"UPDATE tracks SET artist = ? WHERE id == ?;"); + update_track_no.compile(db,"UPDATE tracks SET no = ? WHERE id == ?;"); + update_track_disc.compile(db,"UPDATE tracks SET no = ((no&65535)|(?<<16)) WHERE id == ?;"); + update_track_track.compile(db,"UPDATE tracks SET no = ((no&1048560)|?) WHERE id == ?;"); + update_track_importdate.compile(db,"UPDATE tracks SET importdate = ? WHERE id == ?;"); + + + update_track_year.compile(db,"UPDATE tracks SET year = ? WHERE id == ?;"); + update_track.compile(db,"UPDATE tracks SET title = ?, time = ?, no = ?, year = ?, album = ?, genre = ?, artist = ?, replay_gain = ?, replay_peak = ?, importdate = ? WHERE id == ?;"); + update_album.compile(db,"UPDATE albums SET year = ?, replay_gain = ?, replay_peak = ? WHERE id == ?;"); + + query_track_no.compile(db,"SELECT no FROM tracks WHERE id == ?;"); + + query_album_existing.compile(db,"SELECT id FROM albums WHERE artist == ? AND name == (SELECT albums.name FROM albums WHERE albums.id == (SELECT album FROM tracks WHERE id == ? ));"); + insert_album_existing.compile(db,"INSERT INTO albums ( name, artist, year, replay_gain, replay_peak) SELECT ?, ?, year,replay_gain,replay_peak FROM albums WHERE id == (SELECT album FROM tracks WHERE id == ? ) ;"); + insert_album_existing_name.compile(db,"INSERT INTO albums ( name, artist, year, replay_gain, replay_peak) SELECT name, ?, year,replay_gain,replay_peak FROM albums WHERE id == (SELECT album FROM tracks WHERE id == ? ) ;"); + query_artist_album.compile(db,"SELECT artist FROM albums WHERE id == (SELECT album FROM tracks WHERE id == ?);"); + + + } + + +void GMTrackDatabase::endEdit(FXbool vac){ + if (vac) vacuum(); + update_track_genre_id.clear(); + update_mrl.clear(); + update_artist_album_id.clear(); + update_track_title.clear(); + update_track_album_id.clear(); + update_track_genre.clear(); + update_track_artist.clear(); + update_track_no.clear(); + update_track_disc.clear(); + update_track_track.clear(); + update_track_year.clear(); + update_track.clear(); + update_album.clear(); + update_track_importdate.clear(); + query_track_no.clear(); + query_album_existing.clear(); + insert_album_existing.clear(); + insert_album_existing_name.clear(); + query_artist_album.clear(); + } + + + + + + + +#include "GMPlayerManager.h" + +#ifdef DEBUG +namespace FX { + extern FXlong fxgetticks(); +} + +static FXlong total_ticks = 0; +static FXlong total_has_ticks = 0; +#endif + + +FXbool GMTrackDatabase::hasTrack(const FXString & dir,const FXString & filename,FXint & trackid) { + FXint pathid=-1; + trackid=-1; +#ifdef DEBUG + FXlong start=fxgetticks(); +#endif + if (last_dir==dir) { + if (last_path!=-1) { + query_filename.setParameter(0,last_path); + query_filename.setParameter(1,filename); + query_filename.execute(); + query_filename.getResult(0,trackid); + query_filename.reset(); + } + } + else if (findPath(dir,pathid)){ + query_filename.setParameter(0,pathid); + query_filename.setParameter(1,filename); + query_filename.execute(); + query_filename.getResult(0,trackid); + query_filename.reset(); + last_path=pathid; + last_dir=dir; + } + else { + last_path=-1; + last_dir=dir; + } +#ifdef DEBUG + FXlong end=fxgetticks(); + total_has_ticks+=(end-start); +#endif + if (trackid==-1) { + return false; + } + return true; + } + +FXbool GMTrackDatabase::hasTrack(const FXString & filename,FXint & trackid) { + return hasTrack(FXPath::directory(filename),FXPath::name(filename),trackid); + } + + +FXbool GMTrackDatabase::findPath(const FXString & path,FXint & id) { + if (!FXPath::isTopDirectory(path)) { + FXString parent = FXPath::upLevel(path); + if (!findPath(parent,id)) return false; + FXString name = FXPath::name(path); + FXint nid = -1; + query_path_component.setParameter(0,id); + query_path_component.setParameter(1,name); + query_path_component.execute(); + query_path_component.getResult(0,nid); + query_path_component.reset(); + if (nid==-1) return false; + id=nid; + } + else { + id=-1; + query_root_component.setParameter(0,path); + query_root_component.execute(); + query_root_component.getResult(0,id); + query_root_component.reset(); + if (id==-1) return false; + } + return true; + } + +void GMTrackDatabase::addPath(const FXString & path,FXint & id) { + if (!FXPath::isTopDirectory(path)) { + FXString parent = FXPath::upLevel(path); + FXString name = FXPath::name(path); + FXint nid = -1; + + addPath(parent,id); + + query_path_component.setParameter(0,id); + query_path_component.setParameter(1,name); + query_path_component.execute(); + query_path_component.getResult(0,nid); + query_path_component.reset(); + if (nid==-1) { + insert_path_component.setParameter(0,id); + insert_path_component.setParameter(1,name); + insert_path_component.execute(); + insert_path_component.reset(); + id = db.lastInsertRow(); + } + else { + id = nid; + } + } + else { + id=-1; + query_root_component.setParameter(0,path); + query_root_component.execute(); + query_root_component.getResult(0,id); + query_root_component.reset(); + if (id==-1) { + insert_root_component.setParameter(0,path); + insert_root_component.execute(); + insert_root_component.reset(); + id = db.lastInsertRow(); + } + } + } + + + +FXbool GMTrackDatabase::insertTrack(const GMTrack & track,FXint & result){ + FXint artist=-1; + FXint album=-1; + FXint genre=0; + FXint path=-1; + result=0; + +#ifdef DEBUG + FXlong start = fxgetticks(); +#endif + + if (!queryArtist(artist,track.album_artist,true)) return FALSE; + if (!queryAlbum(album,track.album,artist,track.year,track.album_gain,track.album_peak,true)) return FALSE; + if (!track.genre.empty() && !queryGenre(genre,track.genre,true)) return FALSE; + + /// Insert Track Artist if needed + if (!track.artist.empty() && track.artist!=track.album_artist && !queryArtist(artist,track.artist,true)) return false; + + + static int cached=0; + static int newentry=0; + + FXString dir=FXPath::directory(track.mrl); + + if (last_path>=0 && last_dir==dir){ + path=last_path; + cached++; + } + else { + addPath(dir,path); + last_path=path; + last_dir=dir; + newentry++; + } + + /// Needed for hasTrack + try { + insert_track.setParameter(0,path); + + if (path>=0) + insert_track.setParameter(1,FXPath::name(track.mrl)); + else + insert_track.setParameter(1,track.mrl); + + insert_track.setParameter(2,track.title); + insert_track.setParameter(3,track.time); + insert_track.setParameter(4,track.no); + insert_track.setParameter(5,track.year); + insert_track.setParameter(6,FXThread::time()); + insert_track.setParameter(7,album); + insert_track.setParameter(8,genre); + insert_track.setParameter(9,artist); + insert_track.setParameter(10,track.track_gain); + insert_track.setParameter(11,track.track_peak); + insert_track.execute(); + result = db.lastInsertRow(); +// if (result>0 && trackdict_updated) { +// trackdict.insert(track.mrl.text(),(void*)(FXival)result); +// } + insert_track.reset(); + } + catch (FXExecuteException & e){ + fxmessage("Exception: %s\n",e.what()); + result=0; + return FALSE; + } + +#ifdef DEBUG + FXlong end = fxgetticks(); + total_ticks += (end-start); + fxmessage("insert: %ld\n",end-start); +#endif + return true; + } + + +FXbool GMTrackDatabase::updateTrack(FXint id,const GMTrack & track){ + FXint album=-1; + FXint genre=-1; + FXint artist=-1; + FXuint no=track.no; + + if (!queryArtist(artist,track.album_artist,true)) return FALSE; + if (!updateAlbum(album,track,artist)) return false; + +// if (!queryAlbum(album,track.album,artist,track.year,track.album_gain,track.album_peak,true)) return FALSE; + if (!track.genre.empty() && !queryGenre(genre,track.genre,true)) return FALSE; + if (!track.artist.empty() && track.artist!=track.album_artist && !queryArtist(artist,track.artist,true)) return false; + + try { + if (track.no==0) { + query_track_no.setParameter(0,id); + query_track_no.execute(); + query_track_no.getResult(0,no); + query_track_no.reset(); + } + update_track.setParameter(0,track.title); + update_track.setParameter(1,track.time); + update_track.setParameter(2,no); + update_track.setParameter(3,track.year); + update_track.setParameter(4,album); + update_track.setParameter(5,genre); + update_track.setParameter(6,artist); + update_track.setParameter(7,track.track_gain); + update_track.setParameter(8,track.track_peak); + update_track.setParameter(9,FXThread::time()); + update_track.setParameter(10,id); + update_track.execute(); + update_track.reset(); + } + catch (FXExecuteException &){ + return false; + } + return true; + } + + + +FXbool GMTrackDatabase::insertStream(const FXString & url,const FXString & description,const FXString & genre){ + FXString query; + FXint genreid=0; + if (!genre.empty() && !queryGenre(genreid,genre,true)) return false; + query.format("INSERT INTO streams VALUES (NULL,\"%s\",\"%s\",%d,0,0);",url.text(),description.text(),genreid); + return db.execute(query); + } + + +/// Insert Playlist into database +FXbool GMTrackDatabase::insertPlaylist(const FXString & name,FXint & id) { + try { + GMQuery query(&db,"INSERT INTO playlists VALUES ( NULL, ? );"); + query.setParameter(0,name); + query.execute(); + id = db.lastInsertRow(); + query.reset(); + } + catch (FXCompileException &){ + return false; + } + catch (FXExecuteException &){ + return false; + } + return true; + } + + + +/// Insert Track in Playlist +FXbool GMTrackDatabase::insertTrackInPlaylist(FXint playlist,FXint & track) { + FXint queue=0; + try { + GMQuery queu_query(&db,"SELECT MAX(queue) FROM playlist_tracks WHERE playlist == ?;"); + queu_query.setParameter(0,playlist); + queu_query.execute(); + queu_query.getResult(0,queue); + queu_query.reset(); + + // Increment + queue++; + + GMQuery insert_query(&db,"INSERT INTO playlist_tracks VALUES ( ? , ? , ?);"); + insert_query.setParameter(0,playlist); + insert_query.setParameter(1,track); + insert_query.setParameter(2,queue); + insert_query.execute(); + + } + catch (FXCompileException & e){ + fxmessage("Compile Exception: %s\n",e.what()); + return false; + } + catch (FXExecuteException & e){ + fxmessage("Execute Exception: %s\n",e.what()); + return false; + } + return true; + } + + +/// Insert Track in Playlist +FXbool GMTrackDatabase::insertTrackInPlaylist(FXint playlist,FXIntList & tracks) { + FXint queue=0; + try { + query_playlist_queue.setParameter(0,playlist); + query_playlist_queue.execute(); + query_playlist_queue.getResult(0,queue); + query_playlist_queue.reset(); + + // Increment + queue++; + +#ifdef DEBUG + FXlong start = fxgetticks(); +#endif + + db.begin(); + for (int i=0;i=0) + update_mrl.setParameter(1,FXPath::name(filename)); + else + update_mrl.setParameter(1,filename); + update_mrl.setParameter(2,id); + update_mrl.execute(); + update_mrl.reset(); + } + catch (FXExecuteException &){ + } + } + +FXString GMTrackDatabase::getTrackFilename(FXint id){ + FXString filename; + FXString path; + FXint pathid; + try { + query_track_filename.setParameter(0,id); + query_track_filename.execute(); + query_track_filename.getResult(0,filename); + query_track_filename.getResult(1,pathid); + query_track_filename.reset(); + + query_path.setParameter(0,pathid); + while(query_path.execute()) { + if (!path.empty() && path!="/") path+=PATHSEPSTRING; + path+=query_path.getResult(0); + } + query_path.reset(); + } + catch (FXExecuteException &){ + return FXString::null; + } + + if (path.empty()) + return filename; + else + return path + PATHSEPSTRING + filename; + } + +/// Return list of filenames +void GMTrackDatabase::getTrackFilenames(const FXIntList & tracks,FXStringList & filenames){ + filenames.no(tracks.no()); + for (FXint i=0;i0); + } + +FXbool GMTrackDatabase::getAlbumTrack(FXint id,FXString & path) { + FXString filename; + FXint pathid; + try { + query_track_filename_by_album.setParameter(0,id); + query_track_filename_by_album.execute(); + query_track_filename_by_album.getResult(0,filename); + query_track_filename_by_album.getResult(1,pathid); + query_track_filename_by_album.reset(); + + query_path.setParameter(0,pathid); + while(query_path.execute()) { + if (!path.empty() && path!="/") path+=PATHSEPSTRING; + path+=query_path.getResult(0); + } + query_path.reset(); + } + catch (FXExecuteException &){ + return false; + } + if (path.empty()) + path=filename; + else + path+=PATHSEPSTRING+filename; + return true; + } + + + +FXint GMTrackDatabase::getAlbumPath(FXint id) { + FXint path; + try { + GMQuery query(&db,"SELECT path FROM tracks WHERE album == ? LIMIT(1);"); + query.setParameter(0,id); + query.execute(); + query.getResult(0,path); + query.reset(); + } + catch (FXCompileException &){ + return -1; + } + catch (FXExecuteException &){ + return -1; + } + return path; + } + +FXString GMTrackDatabase::getPathName(FXint id) { + FXString path; + try { + query_path.setParameter(0,id); + while(query_path.execute()) { + if (!path.empty() && path!="/") path+=PATHSEPSTRING; + path+=query_path.getResult(0); + } + query_path.reset(); + } + catch (FXExecuteException &){ + return FXString::null; + } + return path; + } + +FXString GMTrackDatabase::getAlbumName(FXint id) { + FXString result; + queryAlbum(result,id); + return result; + } + +FXString GMTrackDatabase::getAlbumArtist(FXint id) { + FXString result; + queryArtist(result,id); + return result; + } + +FXString GMTrackDatabase::getArtistName(FXint id){ + FXString result; + try { + query_artist_name.setParameter(0,id); + query_artist_name.execute(); + result = query_artist_name.getResult(0); + query_artist_name.reset(); + } + catch (FXExecuteException &){ + return FXString::null; + } + return result; + } + + + +FXString GMTrackDatabase::getGenreName(FXint id){ + FXString name; + try { + GMQuery query(&db,"SELECT name FROM genres WHERE id == ?;"); + query.setParameter(0,id); + query.execute(); + query.getResult(0,name); + query.reset(); + } + catch (FXCompileException &){ + return FXString::null; + } + catch (FXExecuteException &){ + return FXString::null; + } + return name; + } + +FXint GMTrackDatabase::getTrackAlbum(FXint id){ + FXint result; + FXint year; + queryAlbum(result,year,id); + return result; + } + +/* +FXint GMTrackDatabase::hasTrack(const FXString & mrl){ + FXString filename; + FXint id; + if (!trackdict_updated) { + trackdict.clear(); + try { + while(list_all_tracks.execute()){ + list_all_tracks.getResult(0,id); + list_all_tracks.getResult(1,filename); + trackdict.insert(filename.text(),(void*)(FXival)id); + } + list_all_tracks.reset(); + } + catch (FXExecuteException & e){ + return 0; + } + trackdict_updated=true; + } + void * data = trackdict.find(mrl.text()); + if (data==NULL) + return 0; + else + return (FXint)(FXival) data; + } +*/ + +FXbool GMTrackDatabase::getStream(FXint id,GMStream & stream){ + FXString query; + query.format("SELECT url,description,genres.name,bitrate FROM streams, genres WHERE genres.id == streams.genre AND streams.id == %d;",id); + GMQuery get_stream; + try { + get_stream.compile(db,query); + get_stream.execute(); + stream.url=get_stream.getResult(0); + stream.description=get_stream.getResult(1); + stream.genre=get_stream.getResult(2); + get_stream.getResult(3,stream.bitrate); + get_stream.reset(); + } + catch (FXCompileException &){ + return false; + } + catch (FXExecuteException &){ + return false; + } + return true; + } + + + +FXbool GMTrackDatabase::getTrack(FXint id,GMTrack & track){ + FXString path,filename,number; + FXint pathid; + try { + + query_track.setParameter(0,id); + query_track.execute(); + query_track.getResult(0,pathid); + query_track.getResult(1,filename); + query_track.getResult(2,track.album); + query_track.getResult(3,track.album_artist); + query_track.getResult(4,track.artist); + query_track.getResult(5,track.genre); + query_track.getResult(6,track.title); + query_track.getResult(7,track.time); + query_track.getResult(8,track.no); + query_track.getResult(9,track.year); + query_track.getResult(10,track.track_gain); + query_track.getResult(11,track.track_peak); + query_track.getResult(12,track.album_gain); + query_track.getResult(13,track.album_peak); + query_track.reset(); + + if (pathid>=0) { + query_path.setParameter(0,pathid); + while(query_path.execute()) { + if (!path.empty() && path!="/") path+=PATHSEPSTRING; + path+=query_path.getResult(0); + } + query_path.reset(); + } + if (!path.empty()) + track.mrl = path + PATHSEPSTRING + filename; + else + track.mrl = filename; + } + catch (FXExecuteException &){ + return FALSE; + } + return true; + } + +FXbool GMTrackDatabase::getTrackAssociation(FXint id,FXint & artist,FXint & album){ + try { + query_album_artists.setParameter(0,id); + query_album_artists.execute(); + query_album_artists.getResult(0,artist); + query_album_artists.getResult(1,album); + query_album_artists.reset(); + } + catch (FXCompileException &){ + return false; + } + catch (FXExecuteException &){ + return false; + } + return true; + } + + +FXbool GMTrackDatabase::removeGenre(FXint id,FXbool update_tracks) { + GMQuery remove_genre; + GMQuery remove_tracks; + GMQuery remove_albums; + GMQuery remove_artist; + try { + db.begin(); + remove_genre.compile((GMDatabase*)this,"DELETE FROM genres WHERE id == ?;"); + remove_tracks.compile((GMDatabase*)this,"DELETE FROM tracks WHERE genre == ?;"); + remove_albums.compile((GMDatabase*)this,"DELETE FROM albums WHERE id NOT IN ( SELECT album FROM tracks);"); + remove_artist.compile((GMDatabase*)this,"DELETE FROM artists WHERE id NOT IN ( SELECT DISTINCT(artist) FROM albums ) AND id NOT IN ( SELECT DISTINCT(artist) FROM tracks ) ; "); + remove_genre.setParameter(0,id); + remove_genre.perform(); + if (update_tracks) { + remove_tracks.setParameter(0,id); + remove_tracks.perform(); + remove_albums.perform(); + remove_artist.perform(); + remove_tracks.reset(); + remove_albums.reset(); + remove_artist.reset(); + } + remove_genre.reset(); + db.commit(); + } + catch (FXCompileException &){ + db.rollback(); + return false; + } + catch (FXExecuteException &){ + db.rollback(); + return false; + } + return true; + } + + + +FXbool GMTrackDatabase::removeArtist(FXint artist) { + try { + db.begin(); + + /// Remove tracks from artist from playlists. + delete_artist_from_playlists.setParameter(0,artist); + delete_artist_from_playlists.setParameter(1,artist); + delete_artist_from_playlists.execute(); + delete_artist_from_playlists.reset(); + + /// Remove Tracks + delete_track_artist.setParameter(0,artist); + delete_track_artist.setParameter(1,artist); + delete_track_artist.execute(); + delete_track_artist.reset(); + + /// Remove Album Entries + delete_albums_artist.setParameter(0,artist); + delete_albums_artist.execute(); + delete_albums_artist.reset(); + + /// Remove Artist itself + delete_artist.setParameter(0,artist); + delete_artist.execute(); + delete_artist.reset(); + db.commit(); + } + catch (FXExecuteException &){ + db.rollback(); + return false; + } + return true; + } + +FXbool GMTrackDatabase::removeAlbum(FXint album) { + try { + db.begin(); + delete_album_from_playlists.setParameter(0,album); + delete_album_from_playlists.execute(); + delete_album_from_playlists.reset(); + + delete_track_album.setParameter(0,album); + delete_track_album.execute(); + delete_track_album.reset(); + + delete_album.setParameter(0,album); + delete_album.execute(); + delete_album.reset(); + db.commit(); + } + catch (FXExecuteException &){ + db.rollback(); + return false; + } + return true; + } + + +FXbool GMTrackDatabase::removeTracks(const FXIntList & tracks) { + try { +#ifdef DEBUG + FXlong start = fxgetticks(); +#endif + db.begin(); + for (FXint i=0;i0 && playlists[i-1]!=playlists[i]) j=1; + insert_all.setParameter(0,playlists[i]); + insert_all.setParameter(1,tracks[i]); + insert_all.setParameter(2,j); + insert_all.execute(); + insert_all.reset(); + } + } + catch (FXCompileException &){ + return false; + } + catch (FXExecuteException &){ + return false; + } + return true; + } + + +FXbool GMTrackDatabase::removeTracksFromPlaylist(const FXIntList & queue,FXint playlist){ + FXString query; + + db.begin(); + + if (queue.no()==1) { + + query.format("DELETE FROM playlist_tracks WHERE playlist == %d AND queue == %d;",playlist,queue[0]); + if (!db.execute(query)) + goto error; + + // Renumber queue following removed queue + query.format("UPDATE playlist_tracks SET queue = queue - 1 WHERE playlist == %d AND queue > %d;",playlist,queue[0]); + if (!db.execute(query)) + goto error; + } + else { + query.format("DELETE FROM playlist_tracks WHERE playlist == %d AND queue IN ( %d",playlist,queue[0]); + for (FXint i=1;i " +GMStringVal(oldq) + " AND queue <= " + GMStringVal(newq) + ";"; + else + query = "UPDATE playlist_tracks SET queue = queue + 1 WHERE playlist == "+ GMStringVal(playlist) +" AND queue < " +GMStringVal(oldq) + " AND queue >= " + GMStringVal(newq) + ";"; + + if (!db.execute(query)) + return false; + + query = "UPDATE playlist_tracks SET queue = " + GMStringVal(newq) + " WHERE playlist == " + GMStringVal(playlist) + " AND ROWID == " + GMStringVal(row) + ";"; + if (!db.execute(query)) + return false; + + return true; + } + +/// Set the import date of a track +FXbool GMTrackDatabase::setTrackImported(FXint track,FXlong tm){ + try { + update_track_importdate.setParameter(0,tm); + update_track_importdate.setParameter(1,track); + update_track_importdate.execute(); + update_track_importdate.reset(); + } + catch (FXExecuteException & ){ + return false; + } + return true; + } + + +/// Set the name of a track +FXbool GMTrackDatabase::setTrackTitle(FXint track,const FXString & name){ + FXString filename; + try { + update_track_title.setParameter(0,name); + update_track_title.setParameter(1,track); + update_track_title.execute(); + update_track_title.reset(); + } + catch (FXExecuteException &){ + return FALSE; + } + return true; + } + + +FXbool GMTrackDatabase::playedTrack(FXint track,FXlong time) { + try { + update_played.setParameter(0,time); + update_played.setParameter(1,track); + update_played.execute(); + update_played.reset(); + } + catch (FXExecuteException &){ + return false; + } + return true; + } + + +FXbool GMTrackDatabase::setTrackYear(FXint track,FXuint year){ + FXString mrl; + try { + update_track_year.setParameter(0,(FXint)year); + update_track_year.setParameter(1,track); + update_track_year.execute(); + update_track_year.reset(); + } + catch (FXExecuteException &){ + return false; + } + return true; + } + + +FXbool GMTrackDatabase::setTrackYear(const FXIntList & tracks,FXuint year){ + FXString mrl; + try { + for (FXint i=0;iappendItem(TagLib::ID3v1::genre(i).toCString(true)); + } + while(list_genres.execute()) { + list_genres.getResult(0,id); + list_genres.getResult(1,name); + if (!genres.find(name.text())) + list->appendItem(name); + } + } + else { + while(list_genres.execute()) { + list_genres.getResult(0,id); + list_genres.getResult(1,name); + list->appendItem(name); + } + } + list_genres.reset(); + list->sortItems(); + } + catch(FXExecuteException &){ + list->clearItems(); + return FALSE; + } + return true; + } + + + + +FXbool GMTrackDatabase::listArtists(FXComboBox * list){ + FXString name; + FXint id; + try { + while(list_artists.execute()){ + list_artists.getResult(0,id); + list_artists.getResult(1,name); + list->appendItem(name); + } + list_artists.reset(); + list->sortItems(); + } + catch(FXExecuteException & ){ + list->clearItems(); + return FALSE; + } + return true; + } + +FXbool GMTrackDatabase::listAlbums(FXComboBox * list,FXint album){ + FXString name; + FXint id; + FXint artist; + try { + if (!queryArtist(artist,album)) return FALSE; + + list_albums_artist.setParameter(0,artist); + while(list_albums_artist.execute()){ + list_albums_artist.getResult(0,id); + list_albums_artist.getResult(1,name); + list->appendItem(name); + } + list_albums_artist.reset(); + list->sortItems(); + } + catch(FXExecuteException & ){ + list->clearItems(); + return FALSE; + } + return true; + } + + +FXbool GMTrackDatabase::listTracks(FXIntList & list,FXint album,FXint genre/*=-1*/,FXint playlist/*=-1*/){ + GMQuery query; + FXint id; + try { + if (genre==-1) { + if (playlist==-1) { + list_tracks_from_album.setParameter(0,album); + while(list_tracks_from_album.execute()){ + list_tracks_from_album.getResult(0,id); + list.append(id); + } + list_tracks_from_album.reset(); + } + else { + query.compile((GMDatabase*)this,"SELECT tracks.id FROM tracks,genres,playlist_tracks WHERE playlist_tracks.track == tracks.id AND tracks.album == ? AND playlist_tracks.playlist == ?;"); + query.setParameter(0,album); + query.setParameter(1,playlist); + while(query.execute()) { + query.getResult(0,id); + list.append(id); + } + query.reset(); + } + } + else { + if (playlist==-1) { + list_tracks_from_album_genre.setParameter(0,album); + list_tracks_from_album_genre.setParameter(1,genre); + while(list_tracks_from_album_genre.execute()){ + list_tracks_from_album_genre.getResult(0,id); + list.append(id); + } + list_tracks_from_album_genre.reset(); + } + else { + query.compile((GMDatabase*)this,"SELECT tracks.id FROM tracks,genres,playlist_tracks WHERE genres.id == tracks.genre AND playlist_tracks.track == tracks.id AND tracks.album == ? AND tracks.genre == ? AND playlist_tracks.playlist == ?;"); + query.setParameter(0,album); + query.setParameter(1,genre); + query.setParameter(2,playlist); + while(query.execute()) { + query.getResult(0,id); + list.append(id); + } + query.reset(); + } + } + } + catch(FXCompileException & e){ + fxmessage("compile exception: %s\n",e.what()); + list.clear(); + return false; + } + catch(FXExecuteException & e){ + fxmessage("execute exception: %s\n",e.what()); + list.clear(); + return false; + } + return true; + } + +FXbool GMTrackDatabase::listTracks(FXIntList & list,FXint genre){ + FXint id; + try { + list_tracks_genre_only.setParameter(0,genre); + while(list_tracks_genre_only.execute()){ + list_tracks_genre_only.getResult(0,id); + list.append(id); + } + list_tracks_genre_only.reset(); + } + catch(FXExecuteException & e){ + fxwarning("Exception: %s\n",e.what()); + list.clear(); + return false; + } + return true; + } + +FXbool GMTrackDatabase::listTracksArtists(FXIntList & list,FXint artist,FXint genre/*=-1*/,FXint playlist/*=-1*/){ + GMQuery query; + FXint id; + try { + if (genre==-1) { + if (playlist==-1) { + query.compile((GMDatabase*)this,"SELECT tracks.id FROM tracks,albums,artists WHERE tracks.album = albums.id AND artists.id == albums.artist AND artists.id == ?;"); + query.setParameter(0,artist); + while(query.execute()) { + query.getResult(0,id); + list.append(id); + } + query.reset(); + } + else { + query.compile((GMDatabase*)this,"SELECT tracks.id FROM tracks,albums,artists,playlist_tracks WHERE tracks.album == albums.id AND artists.id == albums.artist AND playlist_tracks.track == tracks.id AND artists.id == ? AND playlist_tracks.playlist == ? ;"); + query.setParameter(0,artist); + query.setParameter(1,playlist); + while(query.execute()) { + query.getResult(0,id); + list.append(id); + } + query.reset(); + } + } + else { + if (playlist==-1) { + query.compile((GMDatabase*)this,"SELECT tracks.id FROM tracks,albums,artists WHERE tracks.album = albums.id AND artists.id == albums.artist AND tracks.genre == ? AND artists.id == ?;"); + query.setParameter(0,genre); + query.setParameter(1,artist); + while(query.execute()) { + query.getResult(0,id); + list.append(id); + } + query.reset(); + } + else { + query.compile((GMDatabase*)this,"SELECT tracks.id FROM tracks,albums,artists,playlist_tracks WHERE tracks.album = albums.id AND artists.id == albums.artist AND playlist_tracks.track == tracks.id AND tracks.genre == ? AND artists.id == ? AND playlist_tracks.playlist == ?;"); + query.setParameter(0,genre); + query.setParameter(1,artist); + query.setParameter(2,playlist); + while(query.execute()) { + query.getResult(0,id); + list.append(id); + } + query.reset(); + } + } + } + catch(FXCompileException & e){ + fxmessage("compile: %s\n",e.what()); + list.clear(); + return false; + } + catch(FXExecuteException & e){ + fxmessage("execute: %s\n",e.what()); + list.clear(); + return false; + } + return true; + } + + +FXbool GMTrackDatabase::upgrade_to_2009() { + + if (!db.execute("ALTER TABLE albums RENAME TO albums_old;")) + return false; + + if (!db.execute(create_albums)) + return false; + + if (!db.execute("INSERT INTO albums (id, name, artist, year) SELECT albums_old.id, name, artist,year FROM albums_old,album_artist,tracks WHERE album_artist.album == albums_old.id AND tracks.album==albums_old.id GROUP BY albums_old.id;")) + return false; + + if (!db.execute("DROP TABLE albums_old;")) + return false; + + if (!db.execute("DROP TABLE album_artist;")) + return false; + + if (!db.execute("ALTER TABLE tracks ADD COLUMN artist INTEGER;")) + return false; + if (!db.execute("ALTER TABLE tracks ADD COLUMN replay_gain REAL;")) + return false; + if (!db.execute("ALTER TABLE tracks ADD COLUMN replay_peak REAL;")) + return false; + + if (!db.execute("UPDATE tracks SET artist = ( SELECT DISTINCT(albums.artist) FROM albums WHERE albums.id == tracks.album );")) + return false; + + GMQuery q; + q.compile(db,"UPDATE tracks SET replay_gain = ?, replay_peak = ?;"); + FXdouble v=NAN; + q.setParameter(0,v); + q.setParameter(1,v); + q.execute(); + q.reset(); + + + if (!db.hasTable("streams")) { + if (!db.execute(create_streams)) + return false; + } + + if (sqlite3_libversion_number()>=SQVERSION(3,3,0)) { + db.execute("CREATE INDEX IF NOT EXISTS tracks_album ON tracks(album);"); + db.execute("CREATE INDEX IF NOT EXISTS playlist_tracks_idx ON playlist_tracks(track);"); + } + else { + if (!db.hasIndex("tracks_album")) db.execute("CREATE INDEX tracks_album ON tracks(album);"); + if (!db.hasIndex("playlist_tracks_idx")) db.execute("CREATE INDEX playlist_tracks_idx ON playlist_tracks(track);"); + } + + db.setVersion(GOGGLESMM_DATABASE_SCHEMA_VERSION); + + /// Rebuild the database + db.execute("VACUUM;"); + + return true; + } + +void GMTrackDatabase::fix_genres() { + FXint total=0; + try { + GMQuery count_tracks(&db,"SELECT COUNT(id) FROM tracks WHERE genre == 0;"); + count_tracks.execute(); + count_tracks.getResult(0,total); + count_tracks.reset(); + if (total>0) { + FXint genre=0; + if (queryGenre(genre,"Untitled",true)) { + GMQuery fix; + fix.compile(db,"UPDATE tracks SET genre = ? WHERE genre == 0;"); + fix.setParameter(0,genre); + fix.execute(); + fix.reset(); + } + } + } + catch (FXCompileException &){ + } + catch (FXExecuteException &){ + } + } + +FXbool GMTrackDatabase::setup_tables() { + FXString sql; + FXint version = db.getVersion(); + GMQuery list_tables; + GMQuery list_index; + try { + + if (version==GOGGLESMM_DATABASE_SCHEMA_VERSION) { + return true; + } + + if (version==2008) { + if (upgrade_to_2009()) + return true; + } + + + db.clear(); + + /// Create Tables + if (!db.execute(create_tracks)) + return FALSE; + if (!db.execute(create_genres)) + return FALSE; + if (!db.execute(create_albums)) + return FALSE; + if (!db.execute(create_artists)) + return FALSE; + if (!db.execute(create_playlists)) + return FALSE; + if (!db.execute(create_playlist_tracks)) + return FALSE; + if (!db.execute(create_directories)) + return FALSE; + if (!db.execute(create_trigger_diradd)) + return FALSE; + if (!db.execute(create_streams)) + return FALSE; + + db.execute("CREATE INDEX tracks_album ON tracks(album);"); + db.execute("CREATE INDEX playlist_tracks_idx ON playlist_tracks(track);"); + db.setVersion(GOGGLESMM_DATABASE_SCHEMA_VERSION); + } + catch (FXCompileException &){ + return false; + } + catch (FXExecuteException &){ + return false; + } + return true; + } + +FXbool GMTrackDatabase::setup_queries(){ + try { + insert_root_component.compile(db,"INSERT INTO directories (id,parent,name) VALUES (NULL,NULL,?);"); + insert_path_component.compile(db,"INSERT INTO directories (id,parent,name) VALUES (NULL,?,?);"); + insert_track.compile(db,"INSERT INTO tracks VALUES ( NULL , ? , ? , ? , ? , ? , ? , 0 , 0 , ? , ? , ?, 0, ?, ?, ? );"); + insert_genre.compile(db,"INSERT INTO genres VALUES ( NULL , ? );"); + insert_artist.compile(db,"INSERT INTO artists VALUES ( NULL , ? );"); + insert_album.compile(db,"INSERT INTO albums VALUES ( NULL , ?, ?, ?, ?, ? );"); + query_filename.compile(db,"SELECT id FROM tracks WHERE path == ? AND mrl == ?;"); + + query_root_component.compile(db,"SELECT id FROM directories WHERE parent isnull AND name = ?;"); + query_path_component.compile(db,"SELECT id FROM directories WHERE parent = ? AND name = ?;"); + + + query_path.compile(db,"SELECT name FROM directories WHERE (select path || id || '/' from directories WHERE id = ? ) like path || id || '/%' order by path;"); + query_track.compile(db,"SELECT path, mrl, albums.name, a1.name, a2.name, genres.name, title, time, no, tracks.year, tracks.replay_gain, tracks.replay_peak, albums.replay_gain, albums.replay_peak " + "FROM tracks, albums, genres, artists AS a1, artists AS a2 " + "WHERE albums.id == tracks.album " + "ANd a1.id == albums.artist " + "AND genres.id == tracks.genre " + "AND a2.id == tracks.artist " + "AND tracks.id == ?;"); + + query_genre.compile(db,"SELECT id FROM genres WHERE name == ?;"); + query_artist.compile(db,"SELECT id FROM artists WHERE name == ?;"); + query_artist_name.compile(db,"SELECT name FROM artists WHERE id == ?;"); + query_album.compile(db,"SELECT id FROM albums WHERE name == ? AND artist == ?;"); + query_album_name.compile(db,"SELECT name FROM albums WHERE id == ?;"); + query_artist_with_album.compile(db,"SELECT artist FROM albums WHERE id == ?;"); + query_artist_name_album.compile(db,"SELECT name FROM artists WHERE id == ( SELECT artist FROM albums WHERE id == ? );"); + query_album_with_track.compile(db,"SELECT album,year FROM tracks WHERE id == ?;"); + query_track_filename.compile(db,"SELECT mrl,path FROM tracks WHERE tracks.id == ? ;"); + query_track_filename_by_album.compile(db,"SELECT mrl,path FROM tracks WHERE album == ? ORDER BY no LIMIT(1);"); + + query_track_genre_id.compile(db,"SELECT genre FROM tracks WHERE id == ?;"); + query_artist_year.compile(db,"SELECT albums.artist,tracks.year FROM tracks,albums WHERE albums.id ==tracks.album AND tracks.id == ?;"); + query_album_artists.compile(db,"SELECT albums.artist,album FROM tracks,albums WHERE albums.id ==tracks.album AND tracks.id == ?;"); + + update_played.compile(db,"UPDATE tracks SET playcount = playcount + 1, playdate = ? WHERE id == ?;"); + + list_genres.compile(db,"SELECT id, name FROM genres WHERE id IN (SELECT DISTINCT(genre) FROM tracks);"); + list_artists.compile(db,"SELECT id,name FROM artists;"); + + list_albums_artist.compile(db,"SELECT albums.id, albums.name FROM albums WHERE albums.artist == ? ;"); + + list_tracks_from_album.compile(db,"SELECT id FROM tracks WHERE album == ?"); + list_tracks_from_album_genre.compile(db,"SELECT id FROM tracks WHERE album == ? AND genre == ?;"); + list_tracks_genre_only.compile(db,"SELECT id FROM tracks WHERE genre == ?;"); + query_playlist_queue.compile(db,"SELECT MAX(queue) FROM playlist_tracks WHERE playlist == ?;"); + insert_track_playlist.compile(db,"INSERT INTO playlist_tracks VALUES ( ? , ? , ?);"); + + + fix_genres(); + } + catch (FXCompileException & e){ + fxmessage("Compile Exception: %s\n",e.what()); + return FALSE; + } + return true; + } + + +FXbool GMTrackDatabase::queryGenre(FXint & result,const FXString & name,FXbool insert){ + result=-1; + try { + query_genre.setParameter(0,name); + query_genre.execute(); + query_genre.getResult(0,result); + query_genre.reset(); + + if (result==-1 && insert) { + insert_genre.setParameter(0,name); + insert_genre.execute(); + result = db.lastInsertRow(); + insert_genre.reset(); + } + } + catch (FXExecuteException &){ + result=-1; + return FALSE; + } + return true; + } + +/* +FXbool GMTrackDatabase::queryGenre(FXString & result,FXint trackid){ + result=FXString::null; + try { + query_track_genre.setParameter(0,trackid); + query_track_genre.execute(); + query_track_genre.getResult(0,result); + query_track_genre.reset(); + } + catch (FXExecuteException & e){ + result=FXString::null; + return FALSE; + } + return true; + } +*/ + +FXbool GMTrackDatabase::getTrackGenre(FXint & genre,FXint trackid) { + genre=-1; + try { + query_track_genre_id.setParameter(0,trackid); + query_track_genre_id.execute(); + query_track_genre_id.getResult(0,genre); + query_track_genre_id.reset(); + } + catch (FXExecuteException &){ + genre=-1; + return FALSE; + } + return true; + } + + + +FXbool GMTrackDatabase::queryArtist(FXint & result,const FXString & name,FXbool insert){ + result=-1; + try { + query_artist.setParameter(0,name); + query_artist.execute(); + query_artist.getResult(0,result); + query_artist.reset(); + + if (result==-1 && insert) { + insert_artist.setParameter(0,name); + insert_artist.execute(); + result = db.lastInsertRow(); + insert_artist.reset(); + } + } + catch (FXExecuteException &){ + result=-1; + return FALSE; + } + return true; + } + + +FXbool GMTrackDatabase::queryArtist(FXint & result,FXint album){ + result=-1; + try { + query_artist_with_album.setParameter(0,album); + query_artist_with_album.execute(); + query_artist_with_album.getResult(0,result); + query_artist_with_album.reset(); + } + catch (FXExecuteException &){ + result=-1; + return FALSE; + } + return true; + } + + +FXbool GMTrackDatabase::queryArtist(FXString & result,FXint album){ + result=FXString::null; + try { + query_artist_name_album.setParameter(0,album); + query_artist_name_album.execute(); + query_artist_name_album.getResult(0,result); + query_artist_name_album.reset(); + } + catch (FXExecuteException &){ + result=FXString::null; + return FALSE; + } + return true; + } + + +FXbool GMTrackDatabase::queryAlbum(FXint & album,FXint & year,FXint track){ + album=-1; + year=0; + try { + query_album_with_track.setParameter(0,track); + query_album_with_track.execute(); + query_album_with_track.getResult(0,album); + query_album_with_track.getResult(1,year); + query_album_with_track.reset(); + } + catch (FXExecuteException &){ + album=-1; + year=0; + return false; + } + return true; + } + + +FXbool GMTrackDatabase::updateAlbum(FXint & result,const GMTrack & track,FXint artist){ + result=-1; + try { + query_album.setParameter(0,track.album); + query_album.setParameter(1,artist); + query_album.execute(); + query_album.getResult(0,result); + query_album.reset(); + + if (result==-1) { + insert_album.setParameter(0,track.album); + insert_album.setParameter(1,artist); + insert_album.setParameter(2,track.year); + insert_album.setParameter(3,track.album_gain); + insert_album.setParameter(4,track.album_peak); + insert_album.execute(); + result = db.lastInsertRow(); + insert_album.reset(); + } + else { + update_album.setParameter(0,track.year); + update_album.setParameter(1,track.album_gain); + update_album.setParameter(2,track.album_peak); + update_album.setParameter(3,result); + update_album.execute(); + update_album.reset(); + } + } + catch (FXExecuteException & e){ + fxmessage("Exception: %s\n",e.what()); + result=-1; + return FALSE; + } + return true; + } + + +FXbool GMTrackDatabase::queryAlbum(FXint & result,const FXString & name,FXint artist,FXint year,FXdouble gain,FXdouble peak,FXbool insert){ + result=-1; + try { + query_album.setParameter(0,name); + query_album.setParameter(1,artist); + query_album.execute(); + query_album.getResult(0,result); + query_album.reset(); + + if (result==-1 && insert) { + insert_album.setParameter(0,name); + insert_album.setParameter(1,artist); + insert_album.setParameter(2,year); + insert_album.setParameter(3,gain); + insert_album.setParameter(4,peak); + insert_album.execute(); + result = db.lastInsertRow(); + insert_album.reset(); + } + } + catch (FXExecuteException & e){ + fxmessage("Exception: %s\n",e.what()); + result=-1; + return FALSE; + } + return true; + } + + +FXbool GMTrackDatabase::queryAlbum(FXString & name,FXint album){ + name=FXString::null; + try { + query_album_name.setParameter(0,album); + query_album_name.execute(); + query_album_name.getResult(0,name); + query_album_name.reset(); + } + catch (FXExecuteException &){ + name=FXString::null; + return FALSE; + } + return true; + } + + + + + + +FXbool GMTrackDatabase::vacuum() { + +#ifdef DEBUG + FXlong end, start; + start = fxgetticks(); +#endif + + /// Remove empty playlists ? FIXME: may be not... + /// if (!db.execute("DELETE FROM playlists WHERE id NOT IN (SELECT DISTINCT(playlist) FROM playlist_tracks);")) + // return false; + db.begin(); + +#ifdef DEBUG + fxmessage("Reorder Playlists\n"); +#endif + + /// Reorder Playlists + if (!reorderPlaylists()) goto error; + +#ifdef DEBUG + fxmessage("Remove Empty Albums\n"); +#endif + + /// Remove empty albums + if (!db.execute("DELETE FROM albums WHERE id NOT IN (SELECT DISTINCT(album) FROM tracks);")) + goto error; + +#ifdef DEBUG + fxmessage("Remove unused artists\n"); +#endif + + /// Remove unused artists + if (!db.execute("DELETE FROM artists WHERE id NOT IN (SELECT DISTINCT(artist) FROM albums) ANd id NOT IN (SELECT DISTINCT(artist) FROM tracks);")) + goto error; + +#ifdef DEBUG + fxmessage("Remove unused genres\n"); +#endif + + /// Remove unused genres + if (!db.execute("DELETE FROM genres WHERE id NOT IN (SELECT DISTINCT(genre) FROM tracks) AND id NOT IN (SELECT DISTINCT(genre) FROM streams);")) + goto error; + + +#ifdef DEBUG + fxmessage("Remove empty leafs\n"); +#endif + + /// Remove empty leafs + do { + if (!db.execute("DELETE FROM directories WHERE id NOT IN (SELECT DISTINCT(parent) FROM directories) AND id NOT IN (SELECT DISTINCT(path) FROM tracks);")) + goto error; + } + while (db.changes()>0); + last_path=-1; + + /// commit changes + db.commit(); + +#ifdef DEBUG + end = fxgetticks(); + fxmessage("Vacuum Database in %ld ticks\n",end-start); +#endif + + return true; +error: + db.rollback(); + return false; + } + + +FXbool GMTrackDatabase::exportList(const FXString & filename,FXint playlist,FXuint filetype,FXuint opts){ + const FXchar * c_artist; + const FXchar * c_album; + const FXchar * c_title; + const FXchar * c_genre; + FXint time; + FXint no; + FXint id; + FXint cnt=1; + FXint year; + FILE * fp = NULL; + FXString query; + FXString title; + FXString file; + FXString basepath=FXPath::directory(filename); + GMQuery list; + try { + + query = "SELECT tracks.id,title,artists.name,albums.name,genres.name,no,time,tracks.year " + "FROM tracks, genres, albums, artists"; + + if (playlist>=0) + query+=", playlist_tracks"; + + query+=" WHERE genres.id == tracks.genre AND " + "albums.artist == artists.id AND " + "tracks.album == albums.id"; + + if (playlist>=0) { + query+=" AND playlist_tracks.track == tracks.id"; + query+=" AND playlist_tracks.playlist == "+GMStringVal(playlist); + query+=" ORDER BY playlist_tracks.queue; "; + title=getPlaylistName(playlist); + } + else { + query+=";"; + } + + list.compile(db,query); + + FILE * fp = fopen(filename.text(),"w"); + if (!fp) return false; + + if (filetype==PLAYLIST_XSPF) { + fprintf(fp,""); + fprintf(fp,"\n"); + if (!title.empty()) fprintf(fp,"\t%s\n",title.text()); + fprintf(fp,"\t\n"); + } + else if (filetype==PLAYLIST_M3U_EXTENDED) { + fprintf(fp,"#EXTM3U\n"); + } + else if (filetype==PLAYLIST_PLS) { + fprintf(fp,"[playlist]\n"); + } + else if (filetype==PLAYLIST_CSV) { + fprintf(fp,"No,Title,Album,Artist,Genre,Duration,Year,Filename\n"); + } + + while(list.execute()){ + list.getResult(0,id); + c_title = list.getResult(1); + c_artist = list.getResult(2); + c_album = list.getResult(3); + c_genre = list.getResult(4); + list.getResult(5,no); + list.getResult(6,time); + list.getResult(7,year); + file = getTrackFilename(id); + + if (opts&PLAYLIST_OPTIONS_RELATIVE) + file = FXPath::relative(basepath,file); + + if (filetype==PLAYLIST_XSPF) { + fprintf(fp,"\t\t\n"); + fprintf(fp,"\t\t\t%s\n",GMURL::fileToURL(gm_url_encode(file)).text()); + fprintf(fp,"\t\t\t%s\n",c_artist); + fprintf(fp,"\t\t\t%s\n",c_album); + fprintf(fp,"\t\t\t%s\n",c_title); + fprintf(fp,"\t\t\t%d\n",time*1000); + fprintf(fp,"\t\t\t%u\n",no); + fprintf(fp,"\t\t\n"); + } + else if (filetype==PLAYLIST_M3U_EXTENDED) { + fprintf(fp,"#EXTINF:%d,%s - %s\n",time,c_artist,c_title); + fprintf(fp,"%s\n",file.text()); + } + else if (filetype==PLAYLIST_M3U) { + fprintf(fp,"%s\n",file.text()); + } + else if (filetype==PLAYLIST_PLS) { + fprintf(fp,"File%d=%s\n",cnt,file.text()); + fprintf(fp,"Title%d=%s - %s\n",cnt,c_artist,c_title); + fprintf(fp,"Length%d=%d\n",cnt,time); + cnt++; + } + else if (filetype==PLAYLIST_CSV){ + fprintf(fp,"%d,\"%s\",\"%s\",\"%s\",\"%s\",%d,%d,\"%s\"\n",no,c_title,c_album,c_artist,c_genre,time,year,file.text()); + } + } + list.reset(); + if (filetype==PLAYLIST_XSPF) { + fprintf(fp,"\t\n"); + fprintf(fp,""); + } + else if (filetype==PLAYLIST_PLS){ + fprintf(fp,"Version=2\n"); + fprintf(fp,"NumberOfEntries=%d",cnt-1); + } + fclose(fp); + fp=NULL; + } + catch (FXCompileException &){ + if (fp) fclose(fp); + return false; + } + catch (FXExecuteException &){ + if (fp) fclose(fp); + return false; + } + return true; + } + diff --git a/src/GMTrackDatabase.h b/src/GMTrackDatabase.h new file mode 100644 index 0000000..b69b122 --- /dev/null +++ b/src/GMTrackDatabase.h @@ -0,0 +1,399 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#ifndef GMTRACKDATABASE_H +#define GMTRACKDATABASE_H + +#include "gmdefs.h" + +class GMTrackList; + + +enum { + PLAYLIST_M3U, + PLAYLIST_M3U_EXTENDED, + PLAYLIST_XSPF, + PLAYLIST_PLS, + PLAYLIST_CSV + }; + +enum { + PLAYLIST_OPTIONS_RELATIVE = 0x1 + }; + +class GMTrackDatabase { +private: + GMDatabase db; +public: + + /// Browse Queries + /// ------------------------------------------------------------------------ + GMQuery list_genres; + + /// Insert Queries + /// ------------------------------------------------------------------------ + GMQuery insert_root_component; + GMQuery insert_path_component; + GMQuery insert_track; /// Insert Track + GMQuery insert_genre; /// Insert Genre + GMQuery insert_artist; /// Insert Artist; + GMQuery insert_album; /// Insert Album; +// GMQuery insert_album_artist; /// Insert Album-Artist Assoc; + GMQuery insert_track_playlist; /// Insert Track into Playlist + GMQuery query_filename; /// Query filename + GMQuery query_root_component; + GMQuery query_path_component; + GMQuery query_path; + GMQuery query_genre; /// Query unique genre + GMQuery query_album; /// Query unique album from artist + GMQuery query_album_name; /// Query album name + GMQuery query_artist; /// Query unique artist + GMQuery query_artist_with_album;/// Query artist by album + GMQuery query_artist_name; /// Query Artist Name for id + GMQuery query_track; /// Query track + GMQuery query_track_genre; /// Query track genre + GMQuery query_playlist_queue; /// Get the max playlist queue + GMQuery query_track_filename; /// Query filename by track id + GMQuery query_track_filename_by_album; + GMQuery query_track_genre_id; /// Query Track Genre ID + GMQuery query_album_with_track; /// Query album by track + GMQuery query_artist_name_album;/// Query artist by album + GMQuery query_artist_year; /// Query artist, year for track + GMQuery query_album_artists; /// Query artist and album for track + + + GMQuery list_artists; + GMQuery list_tracks_genre_only; /// List Tracks that have a certain genre + GMQuery list_tracks_from_album; /// List Tracks from Album + GMQuery list_tracks_from_album_genre; /// List Tracks from Album and Genre + GMQuery list_albums_artist; + + + /// Delete Queries + /// ------------------------------------------------------------------------ + GMQuery delete_artist; /// Delete Artist + GMQuery delete_genre; /// Delete Genre + GMQuery delete_album; /// Delete Album + GMQuery delete_track; /// Delete Track + GMQuery delete_track_artist; /// Delete Tracks from Artist + GMQuery delete_track_album; /// Delete Tracks from Album + GMQuery delete_albums_artist; /// Delete Album from Artist in Album List + GMQuery delete_track_from_playlist; /// Delete track from specific playlist + GMQuery delete_track_from_playlists; /// Delete track from all playlists + GMQuery delete_album_from_playlists; /// Delete tracks from album from all playlists + GMQuery delete_artist_from_playlists; /// Delete tracks from artist from all playlists + + + GMQuery update_mrl; /// Update Name of Artist + GMQuery update_track; /// Update track + GMQuery update_album; /// Update Album + GMQuery update_track_title; /// Update track title + GMQuery update_track_year; /// Update Track Year + GMQuery update_played; /// Update Track as played + GMQuery update_track_genre; /// Update track genre + GMQuery update_track_genre_id; /// Update track genre + GMQuery update_track_no; /// Update track & disc no + GMQuery update_track_disc; // Update disc no + GMQuery update_track_track; /// Update track no + GMQuery update_track_artist; /// Update track artist + GMQuery update_track_importdate; /// Update track importdate; + GMQuery query_track_no; /// Update track no + GMQuery update_artist_album_id; /// Update artist in album_artist (with album id) + GMQuery update_track_album_id; /// Update track album + GMQuery insert_album_existing; /// Create new album entry based on existing + GMQuery insert_album_existing_name; /// Create new album entry based on existing + GMQuery query_album_existing; /// Create new album entry based on existing + GMQuery query_artist_album; /// Query Artist Name for id + + + +protected: + void setup_insert_queries(); + void clear_insert_queries(); + + FXbool upgrade_to_2009(); + + FXbool reorderPlaylists(); + +protected: + FXbool queryGenre(FXint & result,const FXString & name,FXbool insert); +// FXbool queryGenre(FXString & result,FXint trackid); + FXbool queryArtist(FXint & result,const FXString & name,FXbool insert); + FXbool queryArtist(FXint & result,FXint album); + FXbool queryArtist(FXString & result,FXint album); + FXbool queryAlbum(FXint & result,const FXString & name,FXint artist,FXint year,FXdouble gain,FXdouble peak,FXbool insert); + FXbool queryAlbum(FXString & name,FXint album); + FXbool queryAlbum(FXint & album,FXint & year, FXint track); + FXbool updateAlbum(FXint &album,const GMTrack&,FXint artist); + FXbool getTrackGenre(FXint & genre,FXint trackid); + + void addPath(const FXString & path,FXint & id); + FXbool findPath(const FXString & path,FXint & id); + + FXbool setup_tables(); + FXbool setup_queries(); + void fix_genres(); +public: + /// Constructor + GMTrackDatabase(); + + GMDatabase * database() { return &db; } + + FXbool clearTracks(FXbool removeplaylists); + + /// Clear Database + void clear(); + + /// Initialize the database. Return FALSE if failed else TRUE + FXbool init(const FXString & filename,FXbool checkversion=false); + + + ///======================================================================================= + /// QUERY ITEMS + ///======================================================================================= + + FXbool getStream(FXint id,GMStream & info); + + /// Return Track Info + FXbool getTrack(FXint id,GMTrack & info); + + + + /// Return artist, album id + FXbool getTrackAssociation(FXint id,FXint & artist,FXint & album); + + /// Return filename for track + FXString getTrackFilename(FXint id); + + /// Return a list of filenames + FXbool getTrackFilenames(FXIntList & ids,FXStringList & filenames,FXLongList & importdates ,const FXString & root=FXString::null); + + /// Return list of filenames + void getTrackFilenames(const FXIntList & tracks,FXStringList & filenames); + + FXString getPathName(FXint id); + + FXbool getAlbumTrack(FXint id,FXString & path); + + /// Return the album path + FXint getAlbumPath(FXint id); + + /// Return album id for track + FXint getTrackAlbum(FXint id); + + /// Return Name for given album + FXString getAlbumName(FXint id); + + /// Return Artist for given album + FXString getAlbumArtist(FXint id); + + /// Return Artist Name for given id + FXString getArtistName(FXint id); + + /// Return Genre Name for give id + FXString getGenreName(FXint id); + + /// Return Play list name for given id + FXString getPlaylistName(FXint id); + + /// Return number of tracks in database + FXint getNumTracks(); + + /// Return number of tracks in database + FXint getNumStreams(); + + /// Return number of albums in database + FXint getNumAlbums(); + + /// Return number of artists in database + FXint getNumArtists(); + + /// Return total time of all tracks + FXint getTotalTime(); + + /// Mark track as played + FXbool playedTrack(FXint id,FXlong t); + + /// Update Playlist Queue + FXbool updateTrackPlaylists(FXint playlist,FXIntList & tracks); + + FXbool trackInPlaylist(FXint track,FXint playlist); + + ///======================================================================================= + /// INSERTING ITEMS + ///======================================================================================= + + /// Check if we have a track + FXbool hasTrack(const FXString &,FXint & id); + + /// Check if we have file in directory + FXbool hasTrack(const FXString & directory,const FXString & filename,FXint & id); + + /// Insert Track into database + FXbool insertTrack(const GMTrack & info,FXint & id); + + /// Uodate Track in database + FXbool updateTrack(FXint id,const GMTrack & info); + + /// Insert Playlist into database + FXbool insertPlaylist(const FXString & name,FXint & id); + + /// Insert Track in Playlist + FXbool insertTrackInPlaylist(FXint playlist,FXint & track); + + /// Insert Track in Playlist + FXbool insertTrackInPlaylist(FXint playlist,FXIntList & tracks); + + /// Remove Queue numbers from Playlist + FXbool removeTracksFromPlaylist(const FXIntList & queue,FXint playlist); + + FXbool insertStream(const FXString & url,const FXString & description,const FXString & genre); + + ///======================================================================================= + /// DELETING ITEMS + ///======================================================================================= + + void beginDelete(); + + /// Remove Track from database + FXbool removeTrack(FXint id); + + /// Remove Track from database + FXbool removeTracks(const FXIntList &ids); + + /// Remove Album from database + FXbool removeAlbum(FXint id); + + /// Remove Artist from database + FXbool removeArtist(FXint id); + + /// Remove Genre from database + FXbool removeGenre(FXint id,FXbool update_tracks=true); + + /// Remove Playlist from database + FXbool removePlaylist(FXint id); + + /// Remove Stream + FXbool removeStream(FXint id); + + void endDelete(FXbool vacuum=true); + + /// Synchronize all tables and makes sure no empty entries are left behind. + FXbool vacuum(); + + ///======================================================================================= + /// EDITING ITEMS + ///======================================================================================= + + void beginEdit(); + + /// Set Track No + FXbool setTrackDiscNumber(FXint id,FXuint no); + + /// Set Track Disc + FXbool setTrackDisc(FXint id,FXushort disc); + FXbool setTrackDisc(const FXIntList & ids,FXushort disc); + + /// Set Track Number + FXbool setTrackNumber(FXint id,FXushort no); + + /// Set Track Title + FXbool setTrackTitle(FXint id,const FXString & name); + + /// Set Track Genre + FXbool setTrackGenre(FXint id,const FXString & name); + FXbool setTrackGenre(const FXIntList & ids,const FXString & name); + + /// Set Track Album + FXbool setTrackAlbum(FXint id,const FXString & name); + FXbool setTrackAlbum(const FXIntList & ids,const FXString & name,FXbool sameartist); + + /// Set Track Artist + FXbool setTrackAlbumArtist(FXint id,const FXString & name,const FXString & album_title); + FXbool setTrackAlbumArtist(const FXIntList & ids,const FXString & name,const FXString & album_title); + + /// Set Track Artist + FXbool setTrackAlbumArtist(FXint id,const FXString & name); + + /// Set Track Artist + FXbool setTrackArtist(FXint id,const FXString & name); + FXbool setTrackArtist(const FXIntList & ids,const FXString & name); + + /// Set Track Year + FXbool setTrackYear(FXint id,FXuint year); + FXbool setTrackYear(const FXIntList & ids,FXuint year); + + /// Change the filename of id + void setTrackFilename(FXint id,const FXString & filename); + + /// Set Playlist Name + FXbool setPlaylistName(FXint id,const FXString & name); + + /// Move oldq to newq + FXbool moveTrack(FXint playlist,FXint oldq,FXint newq); + + /// Mark track as imported + FXbool setTrackImported(FXint id,FXlong tm); + + void endEdit(FXbool vacuum=true); + + FXbool setStreamFilename(FXint id,const FXString & filename); + + FXbool setStreamDescription(FXint id,const FXString & description); + + FXbool setStreamGenre(FXint id,const FXString & genre); + + FXbool setStreamBitrate(FXint id,FXint rate); + + ///======================================================================================= + /// LISTING ITEMS + ///======================================================================================= + + + /// List Genres + FXbool listGenres(FXComboBox * list,FXbool insert_default=TRUE); + + /// List Artists with optional genre + FXbool listArtists(FXList * list,FXIcon * icon,FXint genre=-1,FXint playlist=-1); + + /// List Artists + FXbool listArtists(FXComboBox * list); + + /// List Albums from Artist + FXbool listAlbums(FXComboBox * list,FXint album); + + /// List Tracks from Album + FXbool listTracks(FXIntList & list,FXint album,FXint genre,FXint playlist=-1); + + /// List Tracks with genre + FXbool listTracks(FXIntList & list,FXint genre); + + FXbool listTracksArtists(FXIntList & list,FXint artist,FXint genre,FXint playlist=-1); + + /// List Playlists + FXbool listPlaylists(FXIntList & ids); + + ///======================================================================================= + /// EXPORT + ///======================================================================================= + FXbool exportList(const FXString & filename,FXint playlist,FXuint format,FXuint opts=0); + + /// Destructor + ~GMTrackDatabase(); + }; +#endif diff --git a/src/GMTrackItem.cpp b/src/GMTrackItem.cpp new file mode 100644 index 0000000..a44c0f5 --- /dev/null +++ b/src/GMTrackItem.cpp @@ -0,0 +1,553 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#include +#include "gmdefs.h" +#include "GMTrackList.h" +#include "GMTrackItem.h" +#include "GMList.h" +#include "GMSource.h" +#include "GMTrackView.h" +#include "GMPlayerManager.h" + +#define SIDE_SPACING 4 // Left or right spacing between items +#define DETAIL_TEXT_SPACING 2 // Spacing between text and icon in detail icon mode +#define MINI_TEXT_SPACING 2 // Spacing between text and icon in mini icon mode +#define BIG_LINE_SPACING 6 // Line spacing in big icon mode +#define BIG_TEXT_SPACING 2 // Spacing between text and icon in big icon mode +#define ITEM_SPACE 128 // Default space for item + +FXint GMDBTrackItem::max_time=0; +FXint GMDBTrackItem::max_trackno=0; +FXint GMDBTrackItem::max_queue=0; + + +FXint GMDBTrackItem::max_digits(FXint num){ + if (num>9) { + FXint n=0; + while(num>0) { ++n; num/=10; } + return n; + } + return 1; + } + + +GMDBTrackItem::GMDBTrackItem(FXint track_id,const FXchar * track_title,const FXchar * track_artist,const FXchar * track_album_artist,const FXchar * track_album,const FXchar * track_genre,FXint track_time,FXuint track_no,FXint track_queue,FXushort track_year,FXushort track_album_year) : + GMTrackItem(), title(track_title), + artist(track_artist), + album_artist(track_album_artist), + album(track_album),genre(track_genre), + time(track_time), + no(track_no), + queue(track_queue), + year(track_year), + album_year(track_album_year) { + id=track_id; + state|=GMTrackItem::DRAGGABLE; + } + +GMDBTrackItem::~GMDBTrackItem(){ + } + + +const FXString * GMDBTrackItem::getColumnData(FXint type,FXString &text,FXuint & justify,FXint & max) const{ + const FXString * textptr; + justify=COLUMN_JUSTIFY_NORMAL; + switch(type){ + case HEADER_QUEUE : text.format("%d",queue); + textptr=&text; + justify=COLUMN_JUSTIFY_LEFT_RIGHT_ALIGNED; + max=GMDBTrackItem::max_queue; + break; + + case HEADER_TRACK : if (GMTRACKNO((FXuint)(FXuval)no)>0) { + text.format("%d",GMTRACKNO((FXuint)(FXuval)no)); + textptr=&text; + justify=COLUMN_JUSTIFY_LEFT_RIGHT_ALIGNED; + max=GMDBTrackItem::max_trackno; + } + else { + textptr=NULL; + } + break; + + case HEADER_DISC : if (GMDISCNO((FXuint)(FXuval)no)>0) { + text.format("%d",GMDISCNO((FXuint)(FXuval)no)); + textptr=&text; + } + else { + textptr=NULL; + } + break; + + case HEADER_TITLE : textptr = &title; break; + case HEADER_ALBUM : textptr = &album; break; + case HEADER_ARTIST : textptr = &artist; break; + case HEADER_ALBUM_ARTIST : textptr = &album_artist; break; + case HEADER_GENRE : textptr = &genre; break; + case HEADER_YEAR : //justify=COLUMN_JUSTIFY_CENTER_RIGHT_ALIGNED; + //max=9999; + if (year>0) {text.format("%d",year); textptr=&text; } else textptr=NULL; break; + case HEADER_TIME : /*textptr = ×tring;*/ + text.format("%d:%.2d",time/60,time%60); + textptr=&text; + justify=COLUMN_JUSTIFY_CENTER_RIGHT_ALIGNED; + max=GMDBTrackItem::max_time; + break; + default : textptr=NULL; break; + } + return textptr; + } + + +static inline FXbool begins_with_keyword(const FXString & t){ + for (FXint i=0;igetPreferences().gui_sort_keywords.no();i++){ + if (comparecase(t,GMPlayerManager::instance()->getPreferences().gui_sort_keywords[i],GMPlayerManager::instance()->getPreferences().gui_sort_keywords[i].length())==0) return TRUE; + } + return FALSE; + } + + +FXint GMDBTrackItem::browseSort(const GMTrackItem * pa,const GMTrackItem * pb){ + const GMDBTrackItem * const ta = (GMDBTrackItem*)pa; + const GMDBTrackItem * const tb = (GMDBTrackItem*)pb; + register FXint a=0,b=0,x; + if (GMTrackView::album_by_year) { + if (ta->album_year > tb->album_year) + return (GMTrackView::reverse_album) ? -1 : 1; + else if (ta->album_year < tb->album_year) + return (GMTrackView::reverse_album) ? 1 : -1; + } + + if (begins_with_keyword(ta->album)) a=FXMIN(ta->album.length()-1,ta->album.find(' ')+1); + if (begins_with_keyword(tb->album)) b=FXMIN(tb->album.length()-1,tb->album.find(' ')+1); + x = comparecase(&ta->album[a],&tb->album[b]); + + if (x!=0) + return (GMTrackView::reverse_album) ? -x : x; + + a=b=0; + if (begins_with_keyword(ta->album_artist)) a=FXMIN(ta->album_artist.length()-1,ta->album_artist.find(' ')+1); + if (begins_with_keyword(tb->album_artist)) b=FXMIN(tb->album_artist.length()-1,tb->album_artist.find(' ')+1); + x = comparecase(&ta->album_artist[a],&tb->album_artist[b]); + + if (x!=0) + return (GMTrackView::reverse_artist) ? -x : x; + + /// Track & Disc + if (ta->no>tb->no) return 1; + else if (ta->nono) return -1; + return 0; + } + + +FXint GMDBTrackItem::ascendingTitle(const GMTrackItem* pa,const GMTrackItem* pb){ + const GMDBTrackItem * const ta = (GMDBTrackItem*)pa; + const GMDBTrackItem * const tb = (GMDBTrackItem*)pb; + register FXint a=0,b=0; + if (begins_with_keyword(ta->title)) a=FXMIN(ta->title.length()-1,ta->title.find(' ')+1); + if (begins_with_keyword(tb->title)) b=FXMIN(tb->title.length()-1,tb->title.find(' ')+1); + return comparecase(&ta->title[a],&tb->title[b]); + } + +FXint GMDBTrackItem::descendingTitle(const GMTrackItem* pa,const GMTrackItem* pb){ + const GMDBTrackItem * const ta = (GMDBTrackItem*)pa; + const GMDBTrackItem * const tb = (GMDBTrackItem*)pb; + register FXint a=0,b=0; + if (begins_with_keyword(ta->title)) a=FXMIN(ta->title.length()-1,ta->title.find(' ')+1); + if (begins_with_keyword(tb->title)) b=FXMIN(tb->title.length()-1,tb->title.find(' ')+1); + return -comparecase(&ta->title[a],&tb->title[b]); + } + +FXint GMDBTrackItem::ascendingTrack(const GMTrackItem* pa,const GMTrackItem* pb){ + register const FXuint a=GMTRACKNO((FXuint)(FXuval)((GMDBTrackItem*)pa)->no); + register const FXuint b=GMTRACKNO((FXuint)(FXuval)((GMDBTrackItem*)pb)->no); + if (a>b) return 1; + else if (ano); + register const FXint b=GMTRACKNO((FXuint)(FXuval)((GMDBTrackItem*)pb)->no); + if (a>b) return -1; + else if (ano); + register const FXuint b=GMDISCNO((FXuint)(FXuval)((GMDBTrackItem*)pb)->no); + if (a>b) return 1; + else if (ano); + register const FXint b=GMDISCNO((FXuint)(FXuval)((GMDBTrackItem*)pb)->no); + if (a>b) return -1; + else if (aqueue; + register const FXint b=(FXint)(FXival)((GMDBTrackItem*)pb)->queue; + if (a>b) return 1; + else if (aqueue; + register const FXint b=(FXint)(FXival)((GMDBTrackItem*)pb)->queue; + if (a>b) return -1; + else if (ayear; + register const FXushort b=(FXushort)(FXival)((GMDBTrackItem*)pb)->year; + if (a>b) return 1; + if (ayear; + register const FXushort b=(FXushort)(FXival)((GMDBTrackItem*)pb)->year; + if (a>b) return -1; + if (atime; + register const FXint b=((GMDBTrackItem*)pb)->time; + if (a>b) return 1; + if (atime; + register const FXint b=((GMDBTrackItem*)pb)->time; + if (a>b) return -1; + if (aalbum)) a=FXMIN(ta->album.length()-1,ta->album.find(' ')+1); + if (begins_with_keyword(tb->album)) b=FXMIN(tb->album.length()-1,tb->album.find(' ')+1); + x = comparecase(&ta->album[a],&tb->album[b]); + + if (x!=0) return x; + /// Track & Disc + if (ta->no>tb->no) return 1; + else if (ta->nono) return -1; + return 0; + } + +FXint GMDBTrackItem::descendingAlbum(const GMTrackItem* pa,const GMTrackItem* pb){ + const GMDBTrackItem * const ta = (GMDBTrackItem*)pa; + const GMDBTrackItem * const tb = (GMDBTrackItem*)pb; + + register FXint a=0,b=0,x; + if (begins_with_keyword(ta->album)) a=FXMIN(ta->album.length()-1,ta->album.find(' ')+1); + if (begins_with_keyword(tb->album)) b=FXMIN(tb->album.length()-1,tb->album.find(' ')+1); + x = comparecase(&tb->album[b],&ta->album[a]); + + if (x!=0) return x; + + /// Track & Disc + if (ta->no>tb->no) return 1; + else if (ta->nono) return -1; + return 0; + } + + +FXint GMDBTrackItem::ascendingArtist(const GMTrackItem* pa,const GMTrackItem* pb){ + const GMDBTrackItem * const ta = (GMDBTrackItem*)pa; + const GMDBTrackItem * const tb = (GMDBTrackItem*)pb; + + register FXint a=0,b=0,x; + + if (begins_with_keyword(ta->artist)) a=FXMIN(ta->artist.length()-1,ta->artist.find(' ')+1); + if (begins_with_keyword(tb->artist)) b=FXMIN(tb->artist.length()-1,tb->artist.find(' ')+1); + x = comparecase(&ta->artist[a],&tb->artist[b]); + if (x!=0) return x; + + a=b=0; + if (begins_with_keyword(ta->album)) a=FXMIN(ta->album.length()-1,ta->album.find(' ')+1); + if (begins_with_keyword(tb->album)) b=FXMIN(tb->album.length()-1,tb->album.find(' ')+1); + x = comparecase(&ta->album[a],&tb->album[b]); + + if (x!=0) return x; + + /// Track & Disc + if (ta->no>tb->no) return 1; + else if (ta->nono) return -1; + return 0; + } + + +FXint GMDBTrackItem::descendingArtist(const GMTrackItem* pa,const GMTrackItem* pb){ + const GMDBTrackItem * const ta = (GMDBTrackItem*)pa; + const GMDBTrackItem * const tb = (GMDBTrackItem*)pb; + + register FXint a=0,b=0,x; + + if (begins_with_keyword(ta->artist)) a=FXMIN(ta->artist.length()-1,ta->artist.find(' ')+1); + if (begins_with_keyword(tb->artist)) b=FXMIN(tb->artist.length()-1,tb->artist.find(' ')+1); + x = comparecase(&tb->artist[b],&ta->artist[a]); + if (x!=0) return x; + + a=b=0; + if (begins_with_keyword(ta->album)) a=FXMIN(ta->album.length()-1,ta->album.find(' ')+1); + if (begins_with_keyword(tb->album)) b=FXMIN(tb->album.length()-1,tb->album.find(' ')+1); + x = comparecase(&ta->album[a],&tb->album[b]); + + if (x!=0) return x; + + /// Track & Disc + if (ta->no>tb->no) return 1; + else if (ta->nono) return -1; + return 0; + } + +FXint GMDBTrackItem::ascendingAlbumArtist(const GMTrackItem* pa,const GMTrackItem* pb){ + const GMDBTrackItem * const ta = (GMDBTrackItem*)pa; + const GMDBTrackItem * const tb = (GMDBTrackItem*)pb; + + register FXint a=0,b=0,x; + + if (begins_with_keyword(ta->album_artist)) a=FXMIN(ta->album_artist.length()-1,ta->album_artist.find(' ')+1); + if (begins_with_keyword(tb->album_artist)) b=FXMIN(tb->album_artist.length()-1,tb->album_artist.find(' ')+1); + x = comparecase(&ta->album_artist[a],&tb->album_artist[b]); + if (x!=0) return x; + + a=b=0; + if (begins_with_keyword(ta->album)) a=FXMIN(ta->album.length()-1,ta->album.find(' ')+1); + if (begins_with_keyword(tb->album)) b=FXMIN(tb->album.length()-1,tb->album.find(' ')+1); + x = comparecase(&ta->album[a],&tb->album[b]); + + if (x!=0) return x; + + /// Track & Disc + if (ta->no>tb->no) return 1; + else if (ta->nono) return -1; + return 0; + } + + +FXint GMDBTrackItem::descendingAlbumArtist(const GMTrackItem* pa,const GMTrackItem* pb){ + const GMDBTrackItem * const ta = (GMDBTrackItem*)pa; + const GMDBTrackItem * const tb = (GMDBTrackItem*)pb; + + register FXint a=0,b=0,x; + + if (begins_with_keyword(ta->album_artist)) a=FXMIN(ta->album_artist.length()-1,ta->album_artist.find(' ')+1); + if (begins_with_keyword(tb->album_artist)) b=FXMIN(tb->album_artist.length()-1,tb->album_artist.find(' ')+1); + x = comparecase(&tb->album_artist[b],&ta->album_artist[a]); + if (x!=0) return x; + + a=b=0; + if (begins_with_keyword(ta->album)) a=FXMIN(ta->album.length()-1,ta->album.find(' ')+1); + if (begins_with_keyword(tb->album)) b=FXMIN(tb->album.length()-1,tb->album.find(' ')+1); + x = comparecase(&ta->album[a],&tb->album[b]); + + if (x!=0) return x; + + /// Track & Disc + if (ta->no>tb->no) return 1; + else if (ta->nono) return -1; + return 0; + } + + +FXint GMDBTrackItem::ascendingGenre(const GMTrackItem* pa,const GMTrackItem* pb){ + const GMDBTrackItem * const ta = (GMDBTrackItem*)pa; + const GMDBTrackItem * const tb = (GMDBTrackItem*)pb; + + register FXint a=0,b=0,x; + + x = comparecase(ta->genre,tb->genre); + if (x!=0) return x; + + if (begins_with_keyword(ta->artist)) a=FXMIN(ta->artist.length()-1,ta->artist.find(' ')+1); + if (begins_with_keyword(tb->artist)) b=FXMIN(tb->artist.length()-1,tb->artist.find(' ')+1); + x = comparecase(&ta->artist[a],&tb->artist[b]); + if (x!=0) return x; + + a=b=0; + if (begins_with_keyword(ta->album)) a=FXMIN(ta->album.length()-1,ta->album.find(' ')+1); + if (begins_with_keyword(tb->album)) b=FXMIN(tb->album.length()-1,tb->album.find(' ')+1); + x = comparecase(&ta->album[a],&tb->album[b]); + + if (x!=0) return x; + + /// Track & Disc + if (ta->no>tb->no) return 1; + else if (ta->nono) return -1; + return 0; + } + +FXint GMDBTrackItem::descendingGenre(const GMTrackItem* pa,const GMTrackItem* pb){ + const GMDBTrackItem * const ta = (GMDBTrackItem*)pa; + const GMDBTrackItem * const tb = (GMDBTrackItem*)pb; + + register FXint a=0,b=0,x; + + x = comparecase(tb->genre,ta->genre); + if (x!=0) return x; + + if (begins_with_keyword(ta->artist)) a=FXMIN(ta->artist.length()-1,ta->artist.find(' ')+1); + if (begins_with_keyword(tb->artist)) b=FXMIN(tb->artist.length()-1,tb->artist.find(' ')+1); + x = comparecase(&ta->artist[a],&tb->artist[b]); + if (x!=0) return x; + + a=b=0; + if (begins_with_keyword(ta->album)) a=FXMIN(ta->album.length()-1,ta->album.find(' ')+1); + if (begins_with_keyword(tb->album)) b=FXMIN(tb->album.length()-1,tb->album.find(' ')+1); + x = comparecase(&ta->album[a],&tb->album[b]); + + if (x!=0) return x; + + /// Track & Disc + if (ta->no>tb->no) return 1; + else if (ta->nono) return -1; + return 0; + } + + + + + + +FXint GMStreamTrackItem::max_trackno=0; + +FXint GMStreamTrackItem::max_digits(FXint num){ + if (num>9) { + FXint n=0; + while(num>0) { ++n; num/=10; } + return n; + } + return 1; + } + + +GMStreamTrackItem::GMStreamTrackItem(FXint track_id,const FXchar * track_title,const FXchar * track_genre,FXint track_no,FXint track_bitrate) : + GMTrackItem(), title(track_title),genre(track_genre),bitrate(track_bitrate),no(track_no) { + id=track_id; + } + + +const FXString * GMStreamTrackItem::getColumnData(FXint type,FXString &text,FXuint & justify,FXint & max) const{ + const FXString * textptr; + justify=COLUMN_JUSTIFY_NORMAL; + switch(type){ + case HEADER_TRACK : text.format("%d",GMTRACKNO((FXuint)(FXuval)no)); + textptr=&text; + justify=COLUMN_JUSTIFY_LEFT_RIGHT_ALIGNED; + max=GMStreamTrackItem::max_trackno; + break; + case HEADER_TITLE : textptr = &title; break; + case HEADER_GENRE : textptr = &genre; break; + case HEADER_BITRATE : text.format("%d",bitrate); + textptr=&text; + break; + default : textptr=NULL; break; + } + return textptr; + } + +FXint GMStreamTrackItem::ascendingTime(const GMTrackItem* pa,const GMTrackItem* pb){ + register const FXint a=(FXint)(FXival)((GMStreamTrackItem*)pa)->bitrate; + register const FXint b=(FXint)(FXival)((GMStreamTrackItem*)pb)->bitrate; + if (a>b) return 1; + if (abitrate; + register const FXint b=(FXint)(FXival)((GMStreamTrackItem*)pb)->bitrate; + if (a>b) return -1; + if (agenre,tb->genre); + if (x!=0) return x; + return ascendingTrack(pa,pb); + } + +FXint GMStreamTrackItem::descendingGenre(const GMTrackItem* pa,const GMTrackItem* pb){ + const GMStreamTrackItem * const ta = (GMStreamTrackItem*)pa; + const GMStreamTrackItem * const tb = (GMStreamTrackItem*)pb; + register FXint x; + x = comparecase(tb->genre,ta->genre); + if (x!=0) return x; + return ascendingTrack(pa,pb); + } + + +FXint GMStreamTrackItem::ascendingTrack(const GMTrackItem* pa,const GMTrackItem* pb){ + if (((GMStreamTrackItem*)pa)->no>((GMStreamTrackItem*)pb)->no) return 1; + else if (((GMStreamTrackItem*)pa)->no<((GMStreamTrackItem*)pb)->no) return -1; + return 0; + } + +FXint GMStreamTrackItem::descendingTrack(const GMTrackItem* pa,const GMTrackItem* pb){ + if (((GMStreamTrackItem*)pa)->no>((GMStreamTrackItem*)pb)->no) return -1; + else if (((GMStreamTrackItem*)pa)->no<((GMStreamTrackItem*)pb)->no) return 1; + return 0; + } + +FXint GMStreamTrackItem::ascendingTitle(const GMTrackItem* pa,const GMTrackItem* pb){ + const GMStreamTrackItem * const ta = (GMStreamTrackItem*)pa; + const GMStreamTrackItem * const tb = (GMStreamTrackItem*)pb; + register FXint a=0,b=0; + if (begins_with_keyword(ta->title)) a=FXMIN(ta->title.length()-1,ta->title.find(' ')+1); + if (begins_with_keyword(tb->title)) b=FXMIN(tb->title.length()-1,tb->title.find(' ')+1); + return comparecase(&ta->title[a],&tb->title[b]); + } + +FXint GMStreamTrackItem::descendingTitle(const GMTrackItem* pa,const GMTrackItem* pb){ + const GMStreamTrackItem * const ta = (GMStreamTrackItem*)pa; + const GMStreamTrackItem * const tb = (GMStreamTrackItem*)pb; + register FXint a=0,b=0; + if (begins_with_keyword(ta->title)) a=FXMIN(ta->title.length()-1,ta->title.find(' ')+1); + if (begins_with_keyword(tb->title)) b=FXMIN(tb->title.length()-1,tb->title.find(' ')+1); + return -comparecase(&ta->title[a],&tb->title[b]); + } + + diff --git a/src/GMTrackItem.h b/src/GMTrackItem.h new file mode 100644 index 0000000..67ae15f --- /dev/null +++ b/src/GMTrackItem.h @@ -0,0 +1,118 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#ifndef GMTRACKITEM_H +#define GMTRACKITEM_H + +class GMTrackItem; + +class GMDBTrackItem : public GMTrackItem { +public: + static FXint max_time; + static FXint max_queue; + static FXint max_trackno; + static FXint max_digits(FXint no); +protected: + FXString title; /* 4 - 8 */ + FXString artist; /* 4 - 8 */ + FXString album_artist; /* 4 - 8 */ + FXString album; /* 4 - 8 */ + FXString genre; /* 4 - 8 */ + FXint time; /* 4 - 4 */ + FXuint no; /* 4 - 4 */ + FXint queue; /* 4 - 4 */ + FXushort year; /* 2 - 2 */ + FXushort album_year; /* 2 - 2 */ + //FXuchar filetype; /* 1 - 1 */ +public: + static FXint browseSort(const GMTrackItem*,const GMTrackItem*); + static FXint ascendingTitle(const GMTrackItem*,const GMTrackItem*); + static FXint descendingTitle(const GMTrackItem*,const GMTrackItem*); + static FXint ascendingTrack(const GMTrackItem*,const GMTrackItem*); + static FXint descendingTrack(const GMTrackItem*,const GMTrackItem*); + static FXint ascendingDisc(const GMTrackItem*,const GMTrackItem*); + static FXint descendingDisc(const GMTrackItem*,const GMTrackItem*); + static FXint ascendingQueue(const GMTrackItem*,const GMTrackItem*); + static FXint descendingQueue(const GMTrackItem*,const GMTrackItem*); + static FXint ascendingTime(const GMTrackItem*,const GMTrackItem*); + static FXint descendingTime(const GMTrackItem*,const GMTrackItem*); + static FXint ascendingAlbum(const GMTrackItem*,const GMTrackItem*); + static FXint descendingAlbum(const GMTrackItem*,const GMTrackItem*); + static FXint ascendingArtist(const GMTrackItem*,const GMTrackItem*); + static FXint descendingArtist(const GMTrackItem*,const GMTrackItem*); + static FXint ascendingAlbumArtist(const GMTrackItem*,const GMTrackItem*); + static FXint descendingAlbumArtist(const GMTrackItem*,const GMTrackItem*); + static FXint ascendingGenre(const GMTrackItem*,const GMTrackItem*); + static FXint descendingGenre(const GMTrackItem*,const GMTrackItem*); + static FXint ascendingYear(const GMTrackItem*,const GMTrackItem*); + static FXint descendingYear(const GMTrackItem*,const GMTrackItem*); +protected: + GMDBTrackItem(){} +protected: + virtual const FXString * getColumnData(FXint i,FXString & t,FXuint &justify,FXint & max) const; +public: + GMDBTrackItem(FXint id,const FXchar * title,const FXchar * artist,const FXchar * album_artist,const FXchar * album,const FXchar * genre,FXint time,FXuint no,FXint queue,FXushort year,FXushort ayear); + + void setTrackQueue(FXint q) { queue=q; } + + FXint getTrackQueue() const { return queue; } + + /// Return Track Number + FXint getTrackNumber() const { return no; } + + /// Return Track Title + FXString getTrackTitle() const { return title; } + + virtual ~GMDBTrackItem(); + }; + +class GMStreamTrackItem : public GMTrackItem { +public: + static FXint max_trackno; + static FXint max_digits(FXint no); +protected: + FXString title; + FXString artist; + FXString genre; + FXint bitrate; + FXint no; +public: + static FXint ascendingTitle(const GMTrackItem*,const GMTrackItem*); + static FXint descendingTitle(const GMTrackItem*,const GMTrackItem*); + static FXint ascendingTrack(const GMTrackItem*,const GMTrackItem*); + static FXint descendingTrack(const GMTrackItem*,const GMTrackItem*); + static FXint ascendingGenre(const GMTrackItem*,const GMTrackItem*); + static FXint descendingGenre(const GMTrackItem*,const GMTrackItem*); + static FXint ascendingTime(const GMTrackItem*,const GMTrackItem*); + static FXint descendingTime(const GMTrackItem*,const GMTrackItem*); +protected: + GMStreamTrackItem(){} +protected: + virtual const FXString * getColumnData(FXint i,FXString & t,FXuint &justify,FXint & max) const; +public: + GMStreamTrackItem(FXint id,const FXchar * title,const FXchar * genre,FXint no,FXint bitrate); + + /// Return Track Number + FXint getTrackNumber() const { return no; } + + /// Return Track Title + FXString getTrackTitle() const { return title; } + + virtual ~GMStreamTrackItem() {} + }; +#endif diff --git a/src/GMTrackList.cpp b/src/GMTrackList.cpp new file mode 100644 index 0000000..03fc416 --- /dev/null +++ b/src/GMTrackList.cpp @@ -0,0 +1,1934 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Icon List Widget (under LGPL3) * +* Copyright (C) 1999,2010 by Jeroen van der Zijp. All Rights Reserved. * +* --- * +* Modifications * +* Copyright (C) 2006-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#include +#include "gmdefs.h" +#include +#include "GMTrackList.h" +#include "GMTrackItem.h" +#include "GMList.h" +#include "GMSource.h" +#include "GMTrackView.h" +#include "GMPlayerManager.h" +#include "GMIconTheme.h" +#include "GMTrackDatabase.h" +#include "GMDatabaseSource.h" +#include "GMPlayListSource.h" + +#define SIDE_SPACING 4 // Left or right spacing between items +#define DETAIL_TEXT_SPACING 2 // Spacing between text and icon in detail icon mode +#define MINI_TEXT_SPACING 2 // Spacing between text and icon in mini icon mode +#define BIG_LINE_SPACING 6 // Line spacing in big icon mode +#define BIG_TEXT_SPACING 2 // Spacing between text and icon in big icon mode +#define ITEM_SPACE 128 // Default space for item + +#define SELECT_MASK (TRACKLIST_EXTENDEDSELECT|TRACKLIST_SINGLESELECT|TRACKLIST_BROWSESELECT|TRACKLIST_MULTIPLESELECT) +#define TRACKLIST_MASK (SELECT_MASK) + +#define ICON_WIDTH 10 +#define ICON_HEIGHT 15 + + +// Set or kill focus +void GMTrackItem::setFocus(FXbool focus){ + if(focus) state|=FOCUS; else state&=~FOCUS; + } + +// Select or deselect item +void GMTrackItem::setSelected(FXbool selected){ + if(selected) state|=SELECTED; else state&=~SELECTED; + } + +// Icon is draggable +void GMTrackItem::setDraggable(FXbool draggable){ + if(draggable) state|=DRAGGABLE; else state&=~DRAGGABLE; + } + + +// Map +FXDEFMAP(GMTrackList) GMTrackListMap[]={ + FXMAPFUNC(SEL_PAINT,0,GMTrackList::onPaint), + FXMAPFUNC(SEL_MOTION,0,GMTrackList::onMotion), + FXMAPFUNC(SEL_LEFTBUTTONPRESS,0,GMTrackList::onLeftBtnPress), + FXMAPFUNC(SEL_LEFTBUTTONRELEASE,0,GMTrackList::onLeftBtnRelease), + FXMAPFUNC(SEL_RIGHTBUTTONPRESS,0,GMTrackList::onRightBtnPress), + FXMAPFUNC(SEL_RIGHTBUTTONRELEASE,0,GMTrackList::onRightBtnRelease), + FXMAPFUNC(SEL_TIMEOUT,GMTrackList::ID_AUTOSCROLL,GMTrackList::onAutoScroll), + FXMAPFUNC(SEL_TIMEOUT,GMTrackList::ID_TIPTIMER,GMTrackList::onTipTimer), + FXMAPFUNC(SEL_UNGRABBED,0,GMTrackList::onUngrabbed), + FXMAPFUNC(SEL_KEYPRESS,0,GMTrackList::onKeyPress), + FXMAPFUNC(SEL_KEYRELEASE,0,GMTrackList::onKeyRelease), + FXMAPFUNC(SEL_ENTER,0,GMTrackList::onEnter), + FXMAPFUNC(SEL_LEAVE,0,GMTrackList::onLeave), + FXMAPFUNC(SEL_FOCUSIN,0,GMTrackList::onFocusIn), + FXMAPFUNC(SEL_FOCUSOUT,0,GMTrackList::onFocusOut), + FXMAPFUNC(SEL_CLICKED,0,GMTrackList::onClicked), + FXMAPFUNC(SEL_DOUBLECLICKED,0,GMTrackList::onDoubleClicked), + FXMAPFUNC(SEL_TRIPLECLICKED,0,GMTrackList::onTripleClicked), + FXMAPFUNC(SEL_COMMAND,0,GMTrackList::onCommand), + FXMAPFUNC(SEL_QUERY_TIP,0,GMTrackList::onQueryTip), + FXMAPFUNC(SEL_QUERY_HELP,0,GMTrackList::onQueryHelp), + FXMAPFUNC(SEL_COMMAND,GMTrackList::ID_HEADER,GMTrackList::onCmdHeader), + FXMAPFUNC(SEL_UPDATE,GMTrackList::ID_HEADER,GMTrackList::onUpdHeader), + FXMAPFUNC(SEL_CHANGED,GMTrackList::ID_HEADER,GMTrackList::onChgHeader), + FXMAPFUNC(SEL_CLICKED,GMTrackList::ID_HEADER,GMTrackList::onClkHeader), + FXMAPFUNC(SEL_RIGHTBUTTONRELEASE,GMTrackList::ID_HEADER,GMTrackList::onHeaderRightBtnRelease), + FXMAPFUNC(SEL_COMMAND,GMTrackList::ID_SELECT_ALL,GMTrackList::onCmdSelectAll), + FXMAPFUNC(SEL_COMMAND,GMTrackList::ID_DESELECT_ALL,GMTrackList::onCmdDeselectAll), + FXMAPFUNC(SEL_COMMAND,GMTrackList::ID_SELECT_INVERSE,GMTrackList::onCmdSelectInverse), + FXMAPFUNC(SEL_COMMAND,FXWindow::ID_SETVALUE,GMTrackList::onCmdSetValue), + FXMAPFUNC(SEL_COMMAND,FXWindow::ID_SETINTVALUE,GMTrackList::onCmdSetIntValue), + FXMAPFUNC(SEL_COMMAND,FXWindow::ID_GETINTVALUE,GMTrackList::onCmdGetIntValue), + }; + + +// Object implementation +FXIMPLEMENT(GMTrackList,FXScrollArea,GMTrackListMap,ARRAYNUMBER(GMTrackListMap)) + + +/*******************************************************************************/ + + +// Serialization +GMTrackList::GMTrackList(){ + flags|=FLAG_ENABLED; + header=(FXHeader*)-1L; + anchor=-1; + current=-1; + extent=-1; + cursor=-1; + viewable=-1; + active=-1; + font=(FXFont*)-1L; + activeFont=(FXFont*)-1L; + sortfunc=NULL; + textColor=0; + selbackColor=0; + seltextColor=0; + rowColor=0; + activeColor=0; + activeTextColor=0; + lineHeight=1; + anchorx=0; + anchory=0; + currentx=0; + currenty=0; + grabx=0; + graby=0; + state=false; + sortMethod=HEADER_DEFAULT; + } + + +// Icon List +GMTrackList::GMTrackList(FXComposite *p,FXObject* tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h):FXScrollArea(p,opts,x,y,w,h){ + flags|=FLAG_ENABLED; + header=new GMHeader(this,this,GMTrackList::ID_HEADER,HEADER_TRACKING|HEADER_BUTTON|HEADER_RESIZE|FRAME_LINE); + target=tgt; + message=sel; + anchor=-1; + current=-1; + extent=-1; + cursor=-1; + viewable=-1; + active=-1; + font=getApp()->getNormalFont(); + activeFont=font; + sortfunc=NULL; + textColor=getApp()->getForeColor(); + selbackColor=getApp()->getSelbackColor(); + seltextColor=getApp()->getSelforeColor(); + rowColor=backColor; + activeColor=backColor; + activeTextColor=textColor; + lineHeight=1; + anchorx=0; + anchory=0; + currentx=0; + currenty=0; + grabx=0; + graby=0; + state=false; + sortMethod=HEADER_DEFAULT; + + + GMScrollArea::replaceScrollbars(this); + } + + +// Create window +void GMTrackList::create(){ + FXScrollArea::create(); + font->create(); + } + + +void GMTrackList::markUnsorted() { + sortMethod=HEADER_DEFAULT; + sortfunc=NULL; + } + +// Detach window +void GMTrackList::detach(){ + FXScrollArea::detach(); + font->detach(); + } + + +// If window can have focus +#if FOXVERSION < FXVERSION(1,7,0) +bool GMTrackList::canFocus() const { return true; } +#else +FXbool GMTrackList::canFocus() const { return true; } +#endif + +// Into focus chain +void GMTrackList::setFocus(){ + FXScrollArea::setFocus(); + setDefault(TRUE); + } + + +// Out of focus chain +void GMTrackList::killFocus(){ + FXScrollArea::killFocus(); + setDefault(MAYBE); + } + + +// Propagate size change +void GMTrackList::recalc(){ + FXScrollArea::recalc(); + flags|=FLAG_RECALC; + cursor=-1; + } + + +#if FOXVERSION < FXVERSION(1,7,0) +FXint GMTrackList::getViewportHeight(){ + return height-header->getDefaultHeight(); + } +#else + +// Return visible area y position +FXint GMTrackList::getVisibleY() const { + return header->getHeight(); + } + +// Return visible area height +FXint GMTrackList::getVisibleHeight() const { + return height-header->getHeight()-horizontal->getHeight(); + } + +#endif + + +// Move content +void GMTrackList::moveContents(FXint x,FXint y){ +#if FOXVERSION < FXVERSION(1,7,0) + FXint dx=x-pos_x; + FXint dy=y-pos_y; + FXint top=0; + pos_x=x; + pos_y=y; + top=header->getDefaultHeight(); + header->setPosition(x); + scroll(0,top,viewport_w,viewport_h,dx,dy); +#else + FXScrollArea::moveContents(x,y); + header->setPosition(x); +#endif + } + + +// Recompute interior +void GMTrackList::recompute(){ + lineHeight=FXMAX(GMIconTheme::instance()->getSmallSize(),(4+font->getFontHeight())); + flags&=~FLAG_RECALC; + } + + +// Determine content width of icon list +FXint GMTrackList::getContentWidth(){ + if(flags&FLAG_RECALC) recompute(); + return header->getTotalSize(); + } + + +// Determine content height of icon list +FXint GMTrackList::getContentHeight(){ + if(flags&FLAG_RECALC) recompute(); + return items.no()*lineHeight; + } + + +// Recalculate layout +void GMTrackList::layout(){ +#if FOXVERSION < FXVERSION(1,7,0) + // Update scroll bars + FXScrollArea::layout(); + + // In detail mode + header->position(0,0,viewport_w,header->getDefaultHeight()); + header->show(); + + // Set line size + vertical->setLine(lineHeight); + horizontal->setLine(header->getTotalSize()); + + // We were supposed to make this item viewable + if(0<=viewable){ + makeItemVisible(viewable); + } + + // Force repaint + update(); + + flags&=~FLAG_DIRTY; +#else + FXint hh=header->getDefaultHeight(); + + // Place scroll bars + placeScrollBars(width,height-hh); + + // Place header control + header->position(0,0,width,hh); + + // Set line size + vertical->setLine(lineHeight); + horizontal->setLine(header->getTotalSize()); + + // We were supposed to make this item viewable + if(0<=viewable){ + makeItemVisible(viewable); + } + + // Force repaint + update(); + + // Clean + flags&=~FLAG_DIRTY; +#endif + + } + + +// Header changed but content size didn't +long GMTrackList::onChgHeader(FXObject*,FXSelector,void*){ + return 1; + } + + +// Header subdivision resize has been requested; +// we want to set the width of the header column +// to that of the widest item. +long GMTrackList::onClkHeader(FXObject*,FXSelector,void* ptr){ + register FXint hi=(FXint)(FXival)ptr; + register FXint i,tw,w,nw=0,type; + FXuint justify; + FXint max; + const FXString * textptr; + FXString text; + + type=getHeaderData(hi)->type; + + for(i=0;igetColumnData(type,text,justify,max); +/* + if (justify) { + tw=font->getTextWidth(*textptr); + w=FXMAX(tw,max)+SIDE_SPACING+2; + } + + else */ if (textptr && !textptr->empty()){ + tw=font->getTextWidth(*textptr); + w=tw+SIDE_SPACING+2; + } + if(w>nw) nw=w; + } + + if (hi==0) { + nw+=ICON_WIDTH+DETAIL_TEXT_SPACING+SIDE_SPACING/2; + } + + // Set new header width + if(nw>0 && nw!=header->getItemSize(hi)){ + header->setItemSize(hi,nw); + flags&=~FLAG_RECALC; + } + return 1; + } + +long GMTrackList::onHeaderRightBtnRelease(FXObject*,FXSelector,void*ptr){ + if (target) target->handle(this,FXSEL(SEL_RIGHTBUTTONRELEASE,message+1),ptr); + return 1; + }; + +long GMTrackList::onCmdHeader(FXObject*,FXSelector,void*ptr){ + GMColumn * data = getHeaderData((FXuint)(FXival)ptr); + if (data) { + if (data->type==sortMethod) { + if (sortfunc==data->ascending) + sortfunc=data->descending; + else + sortfunc=data->ascending; + } + else { + sortMethod=data->type; + sortfunc=data->ascending; + } + if (sortfunc) + GMPlayerManager::instance()->getTrackView()->sortTracks(); + } + return 1; + } + +long GMTrackList::onUpdHeader(FXObject*,FXSelector,void*){ + GMColumn * data; + for (FXint i=0;igetNumItems();i++){ + data = getHeaderData(i); + if (data && sortMethod==data->type) { +#if FOXVERSION < FXVERSION(1,7,0) + if (sortfunc==data->ascending) + header->setArrowDir(i,FALSE); + else + header->setArrowDir(i,TRUE); + } + else { + header->setArrowDir(i,MAYBE); + } +#else + if (sortfunc==data->ascending) + header->setArrowDir(i,FXHeaderItem::ARROW_DOWN); + else + header->setArrowDir(i,FXHeaderItem::ARROW_UP); + } + else { + header->setArrowDir(i,FXHeaderItem::ARROW_NONE); + } +#endif + } + return 1; + } + + + +void GMTrackList::appendHeader(const FXString & label,FXint size,GMColumn * data){ + GMColumn * c; + for (FXint i=0;igetNumItems();i++){ + c = getHeaderData(i); + if (data->index < c->index){ + header->insertItem(i,label,NULL,size,data); + return; + } + } + header->appendItem(label,NULL,size,data); + } + + +// Remove header caption +void GMTrackList::removeHeader(FXint index){ + if(index<0 || header->getNumItems()<=index){ fxerror("%s::removeHeader: index out of range.\n",getClassName()); } + header->removeItem(index); + } + +// Return number of headers +FXint GMTrackList::getNumHeaders() const { + return header->getNumItems(); + } + +/// Remove all headers +void GMTrackList::clearHeaders() { + header->clearItems(); + } + +/// Save header configuration +void GMTrackList::saveHeaders() { + GMColumn * column = NULL; + for (FXint i=0;igetNumItems();i++){ + column = getHeaderData(i); + FXASSERT(column); + column->size = header->getItemSize(i); + //column->index = i; + } + } + + + +// True if item is selected +FXbool GMTrackList::isItemSelected(FXint index) const { + if(index<0 || items.no()<=index){ fxerror("%s::isItemSelected: index out of range.\n",getClassName()); } + return items[index]->isSelected(); + } + + +// True if item is current +FXbool GMTrackList::isItemCurrent(FXint index) const { + if(index<0 || items.no()<=index){ fxerror("%s::isItemCurrent: index out of range.\n",getClassName()); } + return index==current; + } + +// True if item (partially) visible +FXbool GMTrackList::isItemVisible(FXint index) const { + register FXbool vis=false; + register FXint y,hh; + if(index<0 || items.no()<=index){ fxerror("%s::isItemVisible: index out of range.\n",getClassName()); } + hh=header->getDefaultHeight(); + y=pos_y+hh+index*lineHeight; +#if FOXVERSION < FXVERSION(1,7,0) + if(hhgetDefaultHeight(); + y=hh+index*lineHeight; + if(py+y+lineHeight >= vh+hh) py=hh+vh-y-lineHeight; + if(py+y <= hh) py=hh-y; + + // Scroll into view + setPosition(px,py); + + // Done it + viewable=-1; + } + } + } + + +// Get item at position x,y +FXint GMTrackList::getItemAt(FXint x,FXint y) const { + register FXint index; + y-=pos_y; + x-=pos_x; + y-=header->getDefaultHeight(); + index=y/lineHeight; + if(index<0 || index>=items.no()) return -1; + return index; + } + + +// Did we hit the item, and which part of it did we hit +FXint GMTrackList::hitItem(FXint index,FXint /*x*/,FXint /*y*/,FXint /*ww*/,FXint /*hh*/) const { +#if 0 + FXint ix,iy,r,c,hit=0; + if(0<=index && indexgetDefaultHeight(); +// r=index; +// c=0; +// ix=header->getTotalSize()*c; +// iy=lineHeight*r; + hit=2; //FIXME items[index]->hitItem(this,x-ix,y-iy,ww,hh); + } + +#endif + FXint hit=0; + if(0<=index && indexgetDefaultHeight()+index*lineHeight,width,lineHeight); + } + } + + + +// Select one item +FXbool GMTrackList::selectItem(FXint index,FXbool notify){ + if(index<0 || items.no()<=index){ fxerror("%s::selectItem: index out of range.\n",getClassName()); } + if(!items[index]->isSelected()){ + switch(options&SELECT_MASK){ + case TRACKLIST_SINGLESELECT: + case TRACKLIST_BROWSESELECT: + killSelection(notify); + case TRACKLIST_EXTENDEDSELECT: + case TRACKLIST_MULTIPLESELECT: + items[index]->setSelected(true); + updateItem(index); + if(notify && target){target->tryHandle(this,FXSEL(SEL_SELECTED,message),(void*)(FXival)index);} + break; + } + return true; + } + return false; + } + + +// Deselect one item +FXbool GMTrackList::deselectItem(FXint index,FXbool notify){ + if(index<0 || items.no()<=index){ fxerror("%s::deselectItem: index out of range.\n",getClassName()); } + if(items[index]->isSelected()){ + switch(options&SELECT_MASK){ + case TRACKLIST_EXTENDEDSELECT: + case TRACKLIST_MULTIPLESELECT: + case TRACKLIST_SINGLESELECT: + items[index]->setSelected(false); + updateItem(index); + if(notify && target){target->tryHandle(this,FXSEL(SEL_DESELECTED,message),(void*)(FXival)index);} + break; + } + return true; + } + return false; + } + + +// Toggle one item +FXbool GMTrackList::toggleItem(FXint index,FXbool notify){ + if(index<0 || items.no()<=index){ fxerror("%s::toggleItem: index out of range.\n",getClassName()); } + switch(options&SELECT_MASK){ + case TRACKLIST_BROWSESELECT: + if(!items[index]->isSelected()){ + killSelection(notify); + items[index]->setSelected(true); + updateItem(index); + if(notify && target){target->tryHandle(this,FXSEL(SEL_SELECTED,message),(void*)(FXival)index);} + } + break; + case TRACKLIST_SINGLESELECT: + if(!items[index]->isSelected()){ + killSelection(notify); + items[index]->setSelected(true); + updateItem(index); + if(notify && target){target->tryHandle(this,FXSEL(SEL_SELECTED,message),(void*)(FXival)index);} + } + else{ + items[index]->setSelected(false); + updateItem(index); + if(notify && target){target->tryHandle(this,FXSEL(SEL_DESELECTED,message),(void*)(FXival)index);} + } + break; + case TRACKLIST_EXTENDEDSELECT: + case TRACKLIST_MULTIPLESELECT: + if(!items[index]->isSelected()){ + items[index]->setSelected(true); + updateItem(index); + if(notify && target){target->tryHandle(this,FXSEL(SEL_SELECTED,message),(void*)(FXival)index);} + } + else{ + items[index]->setSelected(false); + updateItem(index); + if(notify && target){target->tryHandle(this,FXSEL(SEL_DESELECTED,message),(void*)(FXival)index);} + } + break; + } + return true; + } + + +// Select items in rectangle +FXbool GMTrackList::selectInRectangle(FXint x,FXint y,FXint w,FXint h,FXbool notify){ + register FXint index; + register FXbool changed=false; + for(index=0; indexisSelected()){ + items[i]->setSelected(true); + updateItem(i); + changes=true; + if(notify && target){target->tryHandle(this,FXSEL(SEL_SELECTED,message),(void*)(FXival)i);} + } + } + + // extent===anchor---item + // extent===item-----anchor + else if(i1==extent){ + if(items[i]->isSelected()){ + items[i]->setSelected(false); + updateItem(i); + changes=true; + if(notify && target){target->tryHandle(this,FXSEL(SEL_DESELECTED,message),(void*)(FXival)i);} + } + } + } + + // Second segment + for(i=i2+1; i<=i3; i++){ + + // extent---anchor===item + // anchor---extent===item + if(i3==index){ + if(!items[i]->isSelected()){ + items[i]->setSelected(true); + updateItem(i); + changes=true; + if(notify && target){target->tryHandle(this,FXSEL(SEL_SELECTED,message),(void*)(FXival)i);} + } + } + + // item-----anchor===extent + // anchor---item=====extent + else if(i3==extent){ + if(items[i]->isSelected()){ + items[i]->setSelected(false); + updateItem(i); + changes=true; + if(notify && target){target->tryHandle(this,FXSEL(SEL_DESELECTED,message),(void*)(FXival)i);} + } + } + } + extent=index; + } + return changes; + } + + +// Kill selection +FXbool GMTrackList::killSelection(FXbool notify){ + register FXbool changes=false; + register FXint i; + for(i=0; iisSelected()){ + items[i]->setSelected(false); + updateItem(i); + changes=true; + if(notify && target){target->tryHandle(this,FXSEL(SEL_DESELECTED,message),(void*)(FXival)i);} + } + } + return changes; + } + +// Update value from a message +long GMTrackList::onCmdSetValue(FXObject*,FXSelector,void* ptr){ + setCurrentItem((FXint)(FXival)ptr); + return 1; + } + + +// Obtain value from list +long GMTrackList::onCmdGetIntValue(FXObject*,FXSelector,void* ptr){ + *((FXint*)ptr)=getCurrentItem(); + return 1; + } + + +// Update value from a message +long GMTrackList::onCmdSetIntValue(FXObject*,FXSelector,void* ptr){ + setCurrentItem(*((FXint*)ptr)); + return 1; + } + + +// Start motion timer while in this window +long GMTrackList::onEnter(FXObject* sender,FXSelector sel,void* ptr){ + FXScrollArea::onEnter(sender,sel,ptr); + getApp()->addTimeout(this,ID_TIPTIMER,getApp()->getMenuPause()); + cursor=-1; + return 1; + } + + +// Stop motion timer when leaving window +long GMTrackList::onLeave(FXObject* sender,FXSelector sel,void* ptr){ + FXScrollArea::onLeave(sender,sel,ptr); + getApp()->removeTimeout(this,ID_TIPTIMER); + cursor=-1; + return 1; + } + + +// We timed out, i.e. the user didn't move for a while +long GMTrackList::onTipTimer(FXObject*,FXSelector,void*){ + FXTRACE((250,"%s::onTipTimer %p\n",getClassName(),this)); + flags|=FLAG_TIP; + return 1; + } + + +// We were asked about tip text +long GMTrackList::onQueryTip(FXObject* sender,FXSelector sel,void* ptr){ + if(FXScrollArea::onQueryTip(sender,sel,ptr)) return 1; +/* +FIXME + if((flags&FLAG_TIP) && (0<=cursor)){ + FXString string=items[cursor]->getText().section('\t',0); + sender->handle(this,FXSEL(SEL_COMMAND,ID_SETSTRINGVALUE),(void*)&string); + return 1; + } +*/ + return 0; + } + + +// We were asked about status text +long GMTrackList::onQueryHelp(FXObject* sender,FXSelector sel,void* ptr){ + if(FXScrollArea::onQueryHelp(sender,sel,ptr)) return 1; + if((flags&FLAG_HELP) && !help.empty()){ + sender->handle(this,FXSEL(SEL_COMMAND,ID_SETSTRINGVALUE),(void*)&help); + return 1; + } + return 0; + } + + +// Gained focus +long GMTrackList::onFocusIn(FXObject* sender,FXSelector sel,void* ptr){ + FXScrollArea::onFocusIn(sender,sel,ptr); + if(0<=current){ + FXASSERT(currentsetFocus(true); + updateItem(current); + } + return 1; + } + + +// Lost focus +long GMTrackList::onFocusOut(FXObject* sender,FXSelector sel,void* ptr){ + FXScrollArea::onFocusOut(sender,sel,ptr); + if(0<=current){ + FXASSERT(currentsetFocus(false); + updateItem(current); + } + return 1; + } + + +// Draw item list +long GMTrackList::onPaint(FXObject*,FXSelector,void* ptr){ + register FXint rlo,rhi,dw,index,vw,y; + FXEvent* event=(FXEvent*)ptr; + FXDCWindow dc(this,event); + + // Draw nothing + if (header->getNumItems()==0) { + dc.setForeground(backColor); + dc.fillRectangle(0,0,width,height); + return 1; + } + + // Set font + dc.setFont(font); + + // Calculate stipple width + dw=font->getTextWidth(UTF8_ELLIPSIS,3); + + // Exposed rows + rlo=(event->rect.y-pos_y-header->getDefaultHeight())/lineHeight; + rhi=(event->rect.y+event->rect.h-pos_y-header->getDefaultHeight())/lineHeight; + if(rlo<0) rlo=0; + if(rhi>=items.no()) rhi=items.no()-1; + +#if FOXVERSION < FXVERSION(1,7,0) + vw = getViewportWidth(); +#else + vw = getVisibleWidth(); +#endif + + // Repaint the items + y=pos_y+rlo*lineHeight+header->getDefaultHeight(); + for(index=rlo; index<=rhi; index++,y+=lineHeight){ + if (active==index) { + dc.setForeground(activeColor); + dc.setFont(activeFont); + } + else if (index%2) + dc.setForeground(rowColor); + else + dc.setForeground(backColor); + + draw(dc,event,index,pos_x,y,vw,lineHeight,dw); + + if (active==index) + dc.setFont(font); + } + + // Background below + y=pos_y+(rhi+1)*lineHeight+header->getDefaultHeight(); + if(yrect.y+event->rect.h){ + dc.setForeground(backColor); + dc.fillRectangle(event->rect.x,y,event->rect.w,event->rect.y+event->rect.h-y); + } + return 1; + } + + +void GMTrackList::draw(FXDC& dc,FXEvent *,FXint index,FXint x,FXint y,FXint w,FXint h,FXint dw) const { + register FXint iw=0,ih=0,tw=0,th=0,yt,hi,drw,space,used,xx,type; + FXString text; + const FXString * textptr; + FXint max=50; + FXuint justify; + FXIcon * icon=NULL; + + /// Get Icon Size + iw=ih=GMIconTheme::instance()->getSmallSize(); + + if(header->getNumItems()==0) return; + +#if 0 + if (GMPlayerManager::instance()->getPlayQueue() && GMPlayerManager::instance()->getPlayQueue()->hasTrack(items[index]->getId())){ + icon = GMIconTheme::instance()->icon_playqueue; + icon->create(); + } +#endif + + /// Draw background + if(items[index]->isSelected()){ + if (active==index) icon = GMIconTheme::instance()->icon_play;//selectedIcon; + dc.setForeground(getSelBackColor()); + dc.fillRectangle(0,y,w,h); + } + else{ + if (active==index) icon = GMIconTheme::instance()->icon_play; //activeIcon; + dc.fillRectangle(0,y,w,h); + } + + /// Draw Focus + if(items[index]->hasFocus()){ + dc.drawFocusRectangle(x+1,y+1,getHeader()->getTotalSize()-2,h-2); + } + + /// Draw Icon + xx=x+SIDE_SPACING/2; + if(icon){ + dc.setClipRectangle(x,y,header->getItemSize(0),h); + dc.drawIcon(icon,xx,y+(h-ih)/2); + dc.clearClipRectangle(); + } + xx+=iw+DETAIL_TEXT_SPACING; + + /// Draw Text + th=dc.getFont()->getFontHeight(); + yt=y+(h-th-4)/2; + if(items[index]->isSelected()) + dc.setForeground(getSelTextColor()); + else if (active==index) + dc.setForeground(getActiveTextColor()); + else + dc.setForeground(getTextColor()); + + used=iw+DETAIL_TEXT_SPACING+SIDE_SPACING/2; +#if FOXVERSION < FXVERSION(1,7,0) + for(hi=0;higetNumItems()&& xx<=w; hi++){ +#else + for(hi=0;higetNumItems()&& xx<=w+getVisibleX(); hi++){ +#endif + space=header->getItemSize(hi)-used; +#if FOXVERSION < FXVERSION(1,7,0) + if (xx+space>=0){ +#else + if (xx+space>=getVisibleX()){ +#endif + type=getHeaderType(hi); + textptr=items[index]->getColumnData(type,text,justify,max); + if (textptr) { + drw=textptr->length(); + tw=dc.getFont()->getTextWidth(*textptr); + if(tw>space-4){ + while((tw=dc.getFont()->getTextWidth(textptr->text(),drw))+dw>space-4 && drw>1) drw=textptr->dec(drw); + dc.setClipRectangle(xx,y,space,h); + dc.drawText(xx+2,yt+dc.getFont()->getFontAscent()+2,textptr->text(),drw); + dc.drawText(xx+tw+2,yt+dc.getFont()->getFontAscent()+2,UTF8_ELLIPSIS,3); + dc.clearClipRectangle(); + } + else if (justify==0 || (justify && max>space)) { + dc.drawText(xx+2,yt+dc.getFont()->getFontAscent()+2,textptr->text(),drw); + } + else if (justify==COLUMN_JUSTIFY_LEFT_RIGHT_ALIGNED) { + dc.drawText(xx+2+max-tw,yt+dc.getFont()->getFontAscent()+2,text.text(),drw); + } + else if (justify==COLUMN_JUSTIFY_CENTER_RIGHT_ALIGNED){ + dc.drawText(xx+((space/2)-(max/2))+(max-tw),yt+dc.getFont()->getFontAscent()+2,textptr->text(),drw); + } + else if (justify==COLUMN_JUSTIFY_RIGHT){ + dc.drawText(xx+(space-tw)-2,yt+dc.getFont()->getFontAscent()+2,textptr->text(),drw); + } + else { + dc.drawText(xx+2,yt+dc.getFont()->getFontAscent()+2,textptr->text(),drw); + } + } + } + xx+=space; + used=0; + } + } + + +// Select all items +long GMTrackList::onCmdSelectAll(FXObject*,FXSelector,void*){ + for(int i=0; i0; h/=3){ + for(i=h+1;i<=items.no();i++){ + v=items[i-1]; + j=i; + while(j>h && sortfunc(items[j-h-1],v)>0){ + items[j-1]=items[j-h-1]; + exch=true; + j-=h; + } + items[j-1]=v; + } + } + if(0<=current){ + for(i=0; isetFocus(false); + updateItem(current); + } + } + + current=index; + + // Activate new item + if(0<=current){ + + // No visible change if it doen't have the focus + if(hasFocus()){ + items[current]->setFocus(true); + updateItem(current); + } + } + + // Notify item change + if(notify && target){target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)current);} + } + + // In browse selection mode, select item + if((options&SELECT_MASK)==TRACKLIST_BROWSESELECT && 0<=current ){ + selectItem(current,notify); + } + } + + +// Set anchor item +void GMTrackList::setAnchorItem(FXint index){ + if(index<-1 || items.no()<=index){ fxerror("%s::setAnchorItem: index out of range.\n",getClassName()); } + anchor=index; + extent=index; + } + + +// Set active item +void GMTrackList::setActiveItem(FXint i) { + active=i; + if (items.no()) { + if (i>=0) + makeItemVisible(i); + update(); + } + } + + +// Key Press +long GMTrackList::onKeyPress(FXObject*,FXSelector,void* ptr){ + FXEvent* event=(FXEvent*)ptr; + FXint index=current; + flags&=~FLAG_TIP; + if(!isEnabled()) return 0; + if(target && target->tryHandle(this,FXSEL(SEL_KEYPRESS,message),ptr)) return 1; + switch(event->code){ + case KEY_Control_L: + case KEY_Control_R: + case KEY_Shift_L: + case KEY_Shift_R: + case KEY_Alt_L: + case KEY_Alt_R: + if(flags&FLAG_DODRAG){handle(this,FXSEL(SEL_DRAGGED,0),ptr);} + return 1; + case KEY_Page_Up: + case KEY_KP_Page_Up: + setPosition(pos_x,pos_y+verticalScrollBar()->getPage()); + return 1; + case KEY_Page_Down: + case KEY_KP_Page_Down: + setPosition(pos_x,pos_y-verticalScrollBar()->getPage()); + return 1; + case KEY_Right: + case KEY_KP_Right: + setPosition(pos_x-10,pos_y); + return 1; + goto hop; + case KEY_Left: + case KEY_KP_Left: + setPosition(pos_x+10,pos_y); + return 1; + goto hop; + case KEY_Up: + case KEY_KP_Up: + index-=1; + goto hop; + case KEY_Down: + case KEY_KP_Down: + index+=1; + goto hop; + case KEY_Home: + case KEY_KP_Home: + index=0; + goto hop; + case KEY_End: + case KEY_KP_End: + index=items.no()-1; +hop: if(0<=index && indexstate&SHIFTMASK){ + if(0<=anchor){ + selectItem(anchor,true); + extendSelection(index,true); + } + else{ + selectItem(index,true); + } + } + else if(!(event->state&CONTROLMASK)){ + killSelection(true); + selectItem(index,true); + setAnchorItem(index); + } + } + } + handle(this,FXSEL(SEL_CLICKED,0),(void*)(FXival)current); + if(0<=current){ + handle(this,FXSEL(SEL_COMMAND,0),(void*)(FXival)current); + } + return 1; + case KEY_space: + case KEY_KP_Space: + if(0<=current){ + switch(options&SELECT_MASK){ + case TRACKLIST_EXTENDEDSELECT: + if(event->state&SHIFTMASK){ + if(0<=anchor){ + selectItem(anchor,true); + extendSelection(current,true); + } + else{ + selectItem(current,true); + } + } + else if(event->state&CONTROLMASK){ + toggleItem(current,true); + } + else{ + killSelection(true); + selectItem(current,true); + } + break; + case TRACKLIST_MULTIPLESELECT: + case TRACKLIST_SINGLESELECT: + toggleItem(current,true); + break; + } + setAnchorItem(current); + } + handle(this,FXSEL(SEL_CLICKED,0),(void*)(FXival)current); + if(0<=current){ + handle(this,FXSEL(SEL_COMMAND,0),(void*)(FXival)current); + } + return 1; + case KEY_Return: + case KEY_KP_Enter: + handle(this,FXSEL(SEL_DOUBLECLICKED,0),(void*)(FXival)current); + if(0<=current){ + handle(this,FXSEL(SEL_COMMAND,0),(void*)(FXival)current); + } + return 1; + default: break; + } + return 0; + } + + +// Key Release +long GMTrackList::onKeyRelease(FXObject*,FXSelector,void* ptr){ + FXEvent* event=(FXEvent*)ptr; + if(!isEnabled()) return 0; + if(target && target->tryHandle(this,FXSEL(SEL_KEYRELEASE,message),ptr)) return 1; + switch(event->code){ + case KEY_Shift_L: + case KEY_Shift_R: + case KEY_Control_L: + case KEY_Control_R: + case KEY_Alt_L: + case KEY_Alt_R: + if(flags&FLAG_DODRAG){handle(this,FXSEL(SEL_DRAGGED,0),ptr);} + return 1; + } + return 0; + } + + +// Autoscrolling timer +long GMTrackList::onAutoScroll(FXObject* sender,FXSelector sel,void* ptr){ + // Scroll the content + FXScrollArea::onAutoScroll(sender,sel,ptr); + + // Content scrolled, so perhaps something else under cursor + if(flags&FLAG_DODRAG){ + handle(this,FXSEL(SEL_DRAGGED,0),ptr); + return 1; + } + + return 0; + } + + +// Mouse moved +long GMTrackList::onMotion(FXObject*,FXSelector,void* ptr){ + FXEvent* event=(FXEvent*)ptr; + FXint oldcursor=cursor; + FXuint flg=flags; + + // Kill the tip + flags&=~FLAG_TIP; + + // Kill the tip timer + getApp()->removeTimeout(this,ID_TIPTIMER); + + // Right mouse scrolling + if(flags&FLAG_SCROLLING){ + setPosition(event->win_x-grabx,event->win_y-graby); + return 1; + } + + // Drag and drop mode + if(flags&FLAG_DODRAG){ + if(startAutoScroll(event,true)) return 1; + handle(this,FXSEL(SEL_DRAGGED,0),ptr); + return 1; + } + + // Tentative drag and drop + if(flags&FLAG_TRYDRAG){ + if(event->moved){ + flags&=~FLAG_TRYDRAG; + if(handle(this,FXSEL(SEL_BEGINDRAG,0),ptr)){ + flags|=FLAG_DODRAG; + } + } + return 1; + } + + // Reset tip timer if nothing's going on + getApp()->addTimeout(this,ID_TIPTIMER,getApp()->getMenuPause()); + + // Get item we're over + cursor=getItemAt(event->win_x,event->win_y); + + // Force GUI update only when needed + return (cursor!=oldcursor)||(flg&FLAG_TIP); + } + + +// Pressed a button +long GMTrackList::onLeftBtnPress(FXObject*,FXSelector,void* ptr){ + FXEvent* event=(FXEvent*)ptr; + FXint index; + flags&=~FLAG_TIP; + handle(this,FXSEL(SEL_FOCUS_SELF,0),ptr); + if(isEnabled()){ + grab(); + flags&=~FLAG_UPDATE; + + // First change callback + if(target && target->tryHandle(this,FXSEL(SEL_LEFTBUTTONPRESS,message),ptr)) return 1; + + // Locate item + index=getItemAt(event->win_x,event->win_y); + + // No item + if(index<0){ + return 1; + } + + // Previous selection state + state=items[index]->isSelected(); + + // Change current item + setCurrentItem(index,true); + + // Change item selection + switch(options&SELECT_MASK){ + case TRACKLIST_EXTENDEDSELECT: + if(event->state&SHIFTMASK){ + if(0<=anchor){ + selectItem(anchor,true); + extendSelection(index,true); + } + else{ + selectItem(index,true); + setAnchorItem(index); + } + } + else if(event->state&CONTROLMASK){ + if(!state) selectItem(index,true); + setAnchorItem(index); + } + else{ + if(!state){ killSelection(true); selectItem(index,true); } + setAnchorItem(index); + } + break; + case TRACKLIST_MULTIPLESELECT: + case TRACKLIST_SINGLESELECT: + if(!state) selectItem(index,true); + break; + } + + // Are we dragging? + if(state && items[index]->isSelected() && items[index]->isDraggable()){ + flags|=FLAG_TRYDRAG; + } + + flags|=FLAG_PRESSED; + return 1; + } + return 0; + } + + +// Released button +long GMTrackList::onLeftBtnRelease(FXObject*,FXSelector,void* ptr){ + FXEvent* event=(FXEvent*)ptr; + FXuint flg=flags; + if(isEnabled()){ + ungrab(); + stopAutoScroll(); + flags|=FLAG_UPDATE; + flags&=~(FLAG_PRESSED|FLAG_TRYDRAG|FLAG_LASSO|FLAG_DODRAG); + + // First chance callback + if(target && target->tryHandle(this,FXSEL(SEL_LEFTBUTTONRELEASE,message),ptr)) return 1; + + // Was dragging + if(flg&FLAG_DODRAG){ + handle(this,FXSEL(SEL_ENDDRAG,0),ptr); + return 1; + } + + // Must have pressed + if(flg&FLAG_PRESSED){ + + // Selection change + switch(options&SELECT_MASK){ + case TRACKLIST_EXTENDEDSELECT: + if(0<=current){ + if(event->state&CONTROLMASK){ + if(state) deselectItem(current,true); + } + else if(!(event->state&SHIFTMASK)){ + if(state){ killSelection(true); selectItem(current,true); } + } + } + break; + case TRACKLIST_MULTIPLESELECT: + case TRACKLIST_SINGLESELECT: + if(0<=current){ + if(state) deselectItem(current,true); + } + break; + } + + // Scroll to make item visibke + makeItemVisible(current); + + // Update anchor + setAnchorItem(current); + + // Generate clicked callbacks + if(event->click_count==1){ + handle(this,FXSEL(SEL_CLICKED,0),(void*)(FXival)current); + } + else if(event->click_count==2){ + handle(this,FXSEL(SEL_DOUBLECLICKED,0),(void*)(FXival)current); + } + else if(event->click_count==3){ + handle(this,FXSEL(SEL_TRIPLECLICKED,0),(void*)(FXival)current); + } + + // Command callback only when clicked on item + if(0<=current){ + handle(this,FXSEL(SEL_COMMAND,0),(void*)(FXival)current); + } + } + return 1; + } + return 0; + } + + +// Pressed right button +long GMTrackList::onRightBtnPress(FXObject*,FXSelector,void* ptr){ + FXEvent* event=(FXEvent*)ptr; + flags&=~FLAG_TIP; + handle(this,FXSEL(SEL_FOCUS_SELF,0),ptr); + if(isEnabled()){ + grab(); + flags&=~FLAG_UPDATE; + if(target && target->tryHandle(this,FXSEL(SEL_RIGHTBUTTONPRESS,message),ptr)) return 1; + flags|=FLAG_SCROLLING; + grabx=event->win_x-pos_x; + graby=event->win_y-pos_y; + return 1; + } + return 0; + } + + +// Released right button +long GMTrackList::onRightBtnRelease(FXObject*,FXSelector,void* ptr){ + if(isEnabled()){ + ungrab(); + flags&=~FLAG_SCROLLING; + flags|=FLAG_UPDATE; + if(target && target->tryHandle(this,FXSEL(SEL_RIGHTBUTTONRELEASE,message),ptr)) return 1; + return 1; + } + return 0; + } + + +// The widget lost the grab for some reason +long GMTrackList::onUngrabbed(FXObject* sender,FXSelector sel,void* ptr){ + FXScrollArea::onUngrabbed(sender,sel,ptr); + flags&=~(FLAG_DODRAG|FLAG_LASSO|FLAG_TRYDRAG|FLAG_PRESSED|FLAG_CHANGED|FLAG_SCROLLING); + flags|=FLAG_UPDATE; + stopAutoScroll(); + return 1; + } + + +// Command message +long GMTrackList::onCommand(FXObject*,FXSelector,void* ptr){ + return target && target->tryHandle(this,FXSEL(SEL_COMMAND,message),ptr); + } + + +// Clicked in list +long GMTrackList::onClicked(FXObject*,FXSelector,void* ptr){ + return target && target->tryHandle(this,FXSEL(SEL_CLICKED,message),ptr); + } + + +// Double Clicked in list; ptr may or may not point to an item +long GMTrackList::onDoubleClicked(FXObject*,FXSelector,void* ptr){ + return target && target->tryHandle(this,FXSEL(SEL_DOUBLECLICKED,message),ptr); + } + + +// Triple Clicked in list; ptr may or may not point to an item +long GMTrackList::onTripleClicked(FXObject*,FXSelector,void* ptr){ + return target && target->tryHandle(this,FXSEL(SEL_TRIPLECLICKED,message),ptr); + } + +// Retrieve item +GMTrackItem *GMTrackList::getItem(FXint index) const { + if(index<0 || items.no()<=index){ fxerror("%s::getItem: index out of range.\n",getClassName()); } + return items[index]; + } + + +// Replace item with another +FXint GMTrackList::setItem(FXint index,GMTrackItem* item,FXbool notify){ + + // Must have item + if(!item){ fxerror("%s::setItem: item is NULL.\n",getClassName()); } + + // Must be in range + if(index<0 || items.no()<=index){ fxerror("%s::setItem: index out of range.\n",getClassName()); } + + // Notify item will be replaced + if(notify && target){target->tryHandle(this,FXSEL(SEL_REPLACED,message),(void*)(FXival)index);} + + // Copy the state over + item->state=items[index]->state; + + // Delete old + delete items[index]; + + // Add new + items[index]=item; + + // Redo layout + recalc(); + return index; + } + +// Insert item +FXint GMTrackList::insertItem(FXint index,GMTrackItem* item,FXbool notify){ + register FXint old=current; + + // Must have item + if(!item){ fxerror("%s::insertItem: item is NULL.\n",getClassName()); } + + // Must be in range + if(index<0 || items.no()=index) anchor++; + if(extent>=index) extent++; + if(current>=index) current++; + if(viewable>=index) viewable++; + if(current<0 && items.no()==1) current=0; + + // Notify item has been inserted + if(notify && target){target->tryHandle(this,FXSEL(SEL_INSERTED,message),(void*)(FXival)index);} + + // Current item may have changed + if(old!=current){ + if(notify && target){target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)current);} + } + + // Was new item + if(0<=current && current==index){ + if(hasFocus()){ + items[current]->setFocus(true); + } + if((options&SELECT_MASK)==TRACKLIST_BROWSESELECT){ + selectItem(current,notify); + } + } + + // Redo layout + recalc(); + return index; + } + + +// Append item +FXint GMTrackList::appendItem(GMTrackItem* item,FXbool notify){ + return insertItem(items.no(),item,notify); + } + + +// Prepend item +FXint GMTrackList::prependItem(GMTrackItem* item,FXbool notify){ + return insertItem(0,item,notify); + } + + +// Move item from oldindex to newindex +FXint GMTrackList::moveItem(FXint newindex,FXint oldindex,FXbool notify){ + register FXint old=current; + GMTrackItem *item; + + // Must be in range + if(newindex<0 || oldindex<0 || items.no()<=newindex || items.no()<=oldindex){ fxerror("%s::moveItem: index out of range.\n",getClassName()); } + + // Did it change? + if(oldindex!=newindex){ + + // Move item + item=items[oldindex]; + items.erase(oldindex); + items.insert(newindex,item); + + // Move item down + if(newindextryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)current);} + } + + // Redo layout + recalc(); + } + return newindex; + } + + +// Extract node from list +GMTrackItem* GMTrackList::extractItem(FXint index,FXbool notify){ + register GMTrackItem *result; + register FXint old=current; + + // Must be in range + if(index<0 || items.no()<=index){ fxerror("%s::extractItem: index out of range.\n",getClassName()); } + + // Notify item will be deleted + if(notify && target){target->tryHandle(this,FXSEL(SEL_DELETED,message),(void*)(FXival)index);} + + // Extract item + result=items[index]; + + // Remove from list + items.erase(index); + + // Adjust indices + if(anchor>index || anchor>=items.no()) anchor--; + if(extent>index || extent>=items.no()) extent--; + if(current>index || current>=items.no()) current--; + if(viewable>index || viewable>=items.no()) viewable--; + + // Current item has changed + if(index<=old){ + if(notify && target){target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)current);} + } + + // Deleted current item + if(0<=current && index==old){ + if(hasFocus()){ + items[current]->setFocus(true); + } + if((options&SELECT_MASK)==TRACKLIST_BROWSESELECT){ + selectItem(current,notify); + } + } + + // Redo layout + recalc(); + + // Return item + return result; + } + + +// Remove node from list +void GMTrackList::removeItem(FXint index,FXbool notify){ + register FXint old=current; + + // Must be in range + if(index<0 || items.no()<=index){ fxerror("%s::removeItem: index out of range.\n",getClassName()); } + + // Notify item will be deleted + if(notify && target){target->tryHandle(this,FXSEL(SEL_DELETED,message),(void*)(FXival)index);} + + // Delete item + delete items[index]; + + // Remove from list + items.erase(index); + + // Adjust indices + if(anchor>index || anchor>=items.no()) anchor--; + if(extent>index || extent>=items.no()) extent--; + if(current>index || current>=items.no()) current--; + if(viewable>index || viewable>=items.no()) viewable--; + + // Current item has changed + if(index<=old){ + if(notify && target){target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)current);} + } + + // Deleted current item + if(0<=current && index==old){ + if(hasFocus()){ + items[current]->setFocus(true); + } + if((options&SELECT_MASK)==TRACKLIST_BROWSESELECT){ + selectItem(current,notify); + } + } + + // Redo layout + recalc(); + } + + +// Remove all items +void GMTrackList::clearItems(FXbool notify){ + register FXint old=current; + + // Delete items + for(FXint index=items.no()-1; 0<=index; index--){ + if(notify && target){target->tryHandle(this,FXSEL(SEL_DELETED,message),(void*)(FXival)index);} + delete items[index]; + } + + // Free array + items.clear(); + + // Adjust indices + current=-1; + anchor=-1; + extent=-1; + viewable=-1; + + // Current item has changed + if(old!=-1){ + if(notify && target){target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)-1);} + } + + // Redo layout + recalc(); + } + + +// Change the font +void GMTrackList::setFont(FXFont* fnt){ + if(!fnt){ fxerror("%s::setFont: NULL font specified.\n",getClassName()); } + if(font!=fnt){ + font=fnt; + recalc(); + update(); + } + } + +// Change the font +void GMTrackList::setActiveFont(FXFont* fnt){ + if(!fnt){ fxerror("%s::setFont: NULL font specified.\n",getClassName()); } + if(activeFont!=fnt){ + activeFont=fnt; + recalc(); + update(); + } + } + + + +// Set text color +void GMTrackList::setTextColor(FXColor clr){ + if(clr!=textColor){ + textColor=clr; + update(); + } + } + + +// Set select background color +void GMTrackList::setSelBackColor(FXColor clr){ + if(clr!=selbackColor){ + selbackColor=clr; + update(); + } + } + + +// Set selected text color +void GMTrackList::setSelTextColor(FXColor clr){ + if(clr!=seltextColor){ + seltextColor=clr; + update(); + } + } + +// Change the row color +void GMTrackList::setRowColor(FXColor clr){ + if(clr!=rowColor){ + rowColor=clr; + update(); + } + } + +// Change the active color +void GMTrackList::setActiveColor(FXColor clr){ + if(clr!=activeColor){ + activeColor=clr; + update(); + } + } + + +// Change the active text color +void GMTrackList::setActiveTextColor(FXColor clr){ + if(clr!=activeTextColor){ + activeTextColor=clr; + } + } + + +// Change list style +void GMTrackList::setListStyle(FXuint style){ + FXuint opts=(options&~TRACKLIST_MASK) | (style&TRACKLIST_MASK); + if(options!=opts){ + options=opts; + recalc(); + } + } + + +// Get list style +FXuint GMTrackList::getListStyle() const { + return (options&TRACKLIST_MASK); + } + + +// Change help text +void GMTrackList::setHelpText(const FXString& text){ + help=text; + } + + +// Save data +void GMTrackList::save(FXStream& store) const { + FXScrollArea::save(store); + store << header; + store << anchor; + store << current; + store << extent; + store << font; + store << textColor; + store << selbackColor; + store << seltextColor; + store << lineHeight; + store << help; + } + + +// Load data +void GMTrackList::load(FXStream& store){ + FXScrollArea::load(store); + store >> header; + store >> anchor; + store >> current; + store >> extent; + store >> font; + store >> textColor; + store >> selbackColor; + store >> seltextColor; + store >> lineHeight; + store >> help; + } + + +// Cleanup +GMTrackList::~GMTrackList(){ + getApp()->removeTimeout(this,ID_TIPTIMER); + clearItems(false); + header=(FXHeader*)-1L; + font=(FXFont*)-1L; + activeFont=(FXFont*)-1L; + } diff --git a/src/GMTrackList.h b/src/GMTrackList.h new file mode 100644 index 0000000..028b7d2 --- /dev/null +++ b/src/GMTrackList.h @@ -0,0 +1,476 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Icon List Widget (under LGPL3) * +* Copyright (C) 1999,2011 by Jeroen van der Zijp. All Rights Reserved. * +* --- * +* Modifications * +* Copyright (C) 2006-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#ifndef GMTRACKLIST_H +#define GMTRACKLIST_H + +enum { + COLUMN_JUSTIFY_NORMAL=0, + COLUMN_JUSTIFY_CENTER_RIGHT_ALIGNED, + COLUMN_JUSTIFY_LEFT_RIGHT_ALIGNED, + COLUMN_JUSTIFY_RIGHT, + }; + + +/// Icon list styles +enum { + TRACKLIST_EXTENDEDSELECT = 0, /// Extended selection mode + TRACKLIST_SINGLESELECT = 0x00100000, /// At most one selected item + TRACKLIST_BROWSESELECT = 0x00200000, /// Always exactly one selected item + TRACKLIST_MULTIPLESELECT = 0x00300000, /// Multiple selection mode + TRACKLIST_NORMAL = ICONLIST_EXTENDEDSELECT + }; + +class GMTrackList; +class GMColumn; + + +class GMTrackItem { + friend class GMTrackList; +protected: + FXint id; + FXuchar state; +protected: + virtual const FXString * getColumnData(FXint,FXString &,FXuint &,FXint &) const { return NULL; } + virtual FXIcon * getIcon() const { return NULL; } +public: + enum { + SELECTED = 1, /// Selected + FOCUS = 2, /// Focus + DRAGGABLE = 4, /// Draggable + }; +public: + GMTrackItem() : state(0) {} + GMTrackItem(FXint tid) : id(tid),state(0) {} + + /// Return track item id + FXint getId() const { return id; } + + /// Select item + virtual void setSelected(FXbool selected); + + /// Return true if this item is selected + FXbool isSelected() const { return (state&SELECTED)!=0; } + + /// Make item draw as focused + virtual void setFocus(FXbool focus); + + /// Return true if item has focus + FXbool hasFocus() const { return (state&FOCUS)!=0; } + + /// Make item draggable + virtual void setDraggable(FXbool draggable); + + /// Return true if this item is draggable + FXbool isDraggable() const { return (state&DRAGGABLE)!=0; } + + /// Destructor + virtual ~GMTrackItem() {} + }; + +class GMDBTrackItem; + + +/// Icon item collate function +typedef FXint (*GMTrackListSortFunc)(const GMTrackItem*,const GMTrackItem*); + +struct GMColumn { + FXString name; + FXint type; + FXint size; + FXint index; + GMTrackListSortFunc ascending; + GMTrackListSortFunc descending; + FXbool show; + FXbool default_show; + FXbool default_browser_show; + FXObject* target; + FXSelector message; + GMColumn() : size(60),index(0),show(false),target(NULL),message(0) {} + GMColumn(const FXchar * n,FXuint t,GMTrackListSortFunc a,GMTrackListSortFunc b,FXint sz=60,FXbool def_show=true,FXbool def_browser_show=true,FXint idx=0,FXObject* tgt=NULL,FXSelector sel=0) : name(n),type(t),size(sz),index(idx),ascending(a),descending(b),show(true),default_show(def_show),default_browser_show(def_browser_show),target(tgt),message(sel) {} + }; + +typedef FXArray GMColumnList; + +/// List of FXIconItem's +typedef FXArray GMTrackItemList; + + +class GMTrackList : public FXScrollArea { +FXDECLARE(GMTrackList) +friend class GMTrackItem; +protected: + FXHeader* header; // Header control + GMTrackItemList items; // Item List + FXint anchor; // Anchor item + FXint current; // Current item + FXint extent; // Extent item + FXint cursor; // Cursor item + FXint viewable; // Visible item + FXint active; + FXFont *font; // Font + FXFont *activeFont; + GMTrackListSortFunc sortfunc; // Item sort function + FXColor textColor; // Text color + FXColor selbackColor; // Selected back color + FXColor seltextColor; // Selected text color + FXColor rowColor; + FXColor activeColor; + FXColor activeTextColor; + FXint lineHeight; // Item height + FXint anchorx; // Rectangular selection + FXint anchory; + FXint currentx; + FXint currenty; + FXint ratingx; + FXint ratingy; + FXint ratingl; + FXint grabx; // Grab point x + FXint graby; // Grab point y + FXString help; // Help text + FXbool state; // State of item + FXint sortMethod; + FXString starset; + FXString starunset; +protected: + GMTrackList(); + void draw(FXDC& dc,FXEvent *event,FXint index,FXint x,FXint y,FXint w,FXint h,FXint dw) const; + void recompute(); + virtual void moveContents(FXint x,FXint y); + void clearRating(); +private: + GMTrackList(const GMTrackList&); + GMTrackList &operator=(const GMTrackList&); +public: + long onPaint(FXObject*,FXSelector,void*); + long onEnter(FXObject*,FXSelector,void*); + long onLeave(FXObject*,FXSelector,void*); + long onUngrabbed(FXObject*,FXSelector,void*); + long onKeyPress(FXObject*,FXSelector,void*); + long onKeyRelease(FXObject*,FXSelector,void*); + long onLeftBtnPress(FXObject*,FXSelector,void*); + long onLeftBtnRelease(FXObject*,FXSelector,void*); + long onRightBtnPress(FXObject*,FXSelector,void*); + long onRightBtnRelease(FXObject*,FXSelector,void*); + long onMotion(FXObject*,FXSelector,void*); + long onQueryTip(FXObject*,FXSelector,void*); + long onQueryHelp(FXObject*,FXSelector,void*); + long onTipTimer(FXObject*,FXSelector,void*); + long onCmdSelectAll(FXObject*,FXSelector,void*); + long onCmdDeselectAll(FXObject*,FXSelector,void*); + long onCmdSelectInverse(FXObject*,FXSelector,void*); + long onCmdArrangeByRows(FXObject*,FXSelector,void*); + long onUpdArrangeByRows(FXObject*,FXSelector,void*); + long onCmdArrangeByColumns(FXObject*,FXSelector,void*); + long onUpdArrangeByColumns(FXObject*,FXSelector,void*); + long onCmdShowDetails(FXObject*,FXSelector,void*); + long onUpdShowDetails(FXObject*,FXSelector,void*); + long onCmdShowBigIcons(FXObject*,FXSelector,void*); + long onUpdShowBigIcons(FXObject*,FXSelector,void*); + long onCmdShowMiniIcons(FXObject*,FXSelector,void*); + long onUpdShowMiniIcons(FXObject*,FXSelector,void*); + long onChgHeader(FXObject*,FXSelector,void*); + long onClkHeader(FXObject*,FXSelector,void*); + long onCmdHeader(FXObject*,FXSelector,void*); + long onUpdHeader(FXObject*,FXSelector,void*); + long onHeaderRightBtnRelease(FXObject*,FXSelector,void*); + long onFocusIn(FXObject*,FXSelector,void*); + long onFocusOut(FXObject*,FXSelector,void*); + long onClicked(FXObject*,FXSelector,void*); + long onDoubleClicked(FXObject*,FXSelector,void*); + long onTripleClicked(FXObject*,FXSelector,void*); + long onCommand(FXObject*,FXSelector,void*); + long onAutoScroll(FXObject*,FXSelector,void*); + long onLookupTimer(FXObject*,FXSelector,void*); + long onCmdSetValue(FXObject*,FXSelector,void*); + long onCmdGetIntValue(FXObject*,FXSelector,void*); + long onCmdSetIntValue(FXObject*,FXSelector,void*); + long onMouseLeave(FXObject*,FXSelector,void*); + long onWheelTimeout(FXObject*,FXSelector,void*); +public: + enum { + ID_HEADER=FXScrollArea::ID_LAST, + ID_SELECT_ALL, + ID_DESELECT_ALL, + ID_SELECT_INVERSE, + ID_WHEEL_TIMEOUT, + ID_LAST + }; +public: + /// Construct icon list with no items in it initially + GMTrackList(FXComposite *p,FXObject* tgt=NULL,FXSelector sel=0,FXuint opts=TRACKLIST_NORMAL,FXint x=0,FXint y=0,FXint w=0,FXint h=0); + + /// Get the unique item id + FXint getItemId(FXint index) const { return items[index]->id; } + + /// Set the sort method + void setSortMethod(FXint m) { sortMethod=m; } + + /// Get the sort method + FXint getSortMethod() const { return sortMethod; } + + /// Mark the list as unsorted + void markUnsorted(); + + /// Create server-side resources + virtual void create(); + + /// Detach server-side resources + virtual void detach(); + + /// Recalculate layout + virtual void recalc(); + + /// Perform layout + virtual void layout(); + +#if FOXVERSION < FXVERSION(1,7,0) + virtual FXint getViewportHeight(); +#else + /// Return visible area y position + virtual FXint getVisibleY() const; + + /// Return visible area height + virtual FXint getVisibleHeight() const; +#endif + + /// Compute and return content width + virtual FXint getContentWidth(); + + /// Return content height + virtual FXint getContentHeight(); + + /// Icon list can receive focus +#if FOXVERSION < FXVERSION(1,7,0) + virtual bool canFocus() const; +#else + virtual FXbool canFocus() const; +#endif + + /// Move the focus to this window + virtual void setFocus(); + + /// Remove the focus from this window + virtual void killFocus(); + + /// Return number of items + FXint getNumItems() const { return items.no(); } + + /// Return header control + FXHeader* getHeader() const { return header; } + + /// Return the header data. + GMColumn * getHeaderData(FXint i) const { return reinterpret_cast(header->getItemData(i)); } + + /// Return the header type + FXuint getHeaderType(FXint i) const { return ( (i>=0 && igetNumItems()) ? ((reinterpret_cast(header->getItemData(i)))->type) : -1); } + + /// Append header with given text, size and column data + void appendHeader(const FXString & label,FXint size,GMColumn * data); + + /// Remove header at index + void removeHeader(FXint index); + + /// Return number of headers + FXint getNumHeaders() const; + + /// Return index of given header type if displayed, otherwise -1 + FXint getHeaderByType(FXuint type) const; + + /// Remove All Headers + void clearHeaders(); + + /// Save Header Configuration + void saveHeaders(); + + /// Return the item at the given index + GMTrackItem *getItem(FXint index) const; + + /// Replace the item with a [possibly subclassed] item + FXint setItem(FXint index,GMTrackItem* item,FXbool notify=false); + + /// Insert a new [possibly subclassed] item at the give index + FXint insertItem(FXint index,GMTrackItem* item,FXbool notify=false); + + /// Append a [possibly subclassed] item to the end of the list + FXint appendItem(GMTrackItem* item,FXbool notify=false); + + /// Prepend a [possibly subclassed] item to the end of the list + FXint prependItem(GMTrackItem* item,FXbool notify=false); + + /// Move item from oldindex to newindex + FXint moveItem(FXint newindex,FXint oldindex,FXbool notify=false); + + /// Extract item from list + GMTrackItem* extractItem(FXint index,FXbool notify=false); + + /// Remove item from list + void removeItem(FXint index,FXbool notify=false); + + /// Remove all items from list + void clearItems(FXbool notify=false); + + /// Return item height + FXint getLineHeight() const { return lineHeight; } + + /// Return index of item at x,y, or -1 if none + virtual FXint getItemAt(FXint x,FXint y) const; + + /// Scroll to make item at index visible + virtual void makeItemVisible(FXint index); + + /// Return true if item at index is selected + FXbool isItemSelected(FXint index) const; + + /// Return true if item at index is current + FXbool isItemCurrent(FXint index) const; + + /// Return true if item at index is visible + FXbool isItemVisible(FXint index) const; + + /// Return true if item at index is enabled + FXbool isItemEnabled(FXint index) const; + + /// Return item hit code: 0 outside, 1 icon, 2 text + FXint hitItem(FXint index,FXint x,FXint y,FXint ww=1,FXint hh=1) const; + + /// Repaint item at index + void updateItem(FXint index) const; + + /// Select item at index + virtual FXbool selectItem(FXint index,FXbool notify=false); + + /// Deselect item at index + virtual FXbool deselectItem(FXint index,FXbool notify=false); + + /// Toggle item at index + virtual FXbool toggleItem(FXint index,FXbool notify=false); + + /// Select items in rectangle + virtual FXbool selectInRectangle(FXint x,FXint y,FXint w,FXint h,FXbool notify=false); + + /// Extend selection from anchor index to index + virtual FXbool extendSelection(FXint index,FXbool notify=false); + + /// Deselect all items + virtual FXbool killSelection(FXbool notify=false); + + /// Change current item index + virtual void setCurrentItem(FXint index,FXbool notify=false); + + /// Return current item index, or -1 if none + FXint getCurrentItem() const { return current; } + + /// Change anchor item index + void setAnchorItem(FXint index); + + /// Return anchor item index, or -1 if none + FXint getAnchorItem() const { return anchor; } + + /// Return index of item under cursor, or -1 if none + FXint getCursorItem() const { return cursor; } + + /// Change active item index + void setActiveItem(FXint index); + + /// Return active item index, or -1 if none + FXint getActiveItem() const { return (active==-1) ? current : active ; } + + /// Sort items + void sortItems(); + + /// Return sort function + GMTrackListSortFunc getSortFunc() const { return sortfunc; } + + /// Change sort function + void setSortFunc(GMTrackListSortFunc func){ sortfunc=func; } + + /// Change text font + void setFont(FXFont* fnt); + + /// Return text font + FXFont* getFont() const { return font; } + + /// Change active text font + void setActiveFont(FXFont* fnt); + + /// Return active text font + FXFont* getActiveFont() const { return activeFont; } + + /// Return normal text color + FXColor getTextColor() const { return textColor; } + + /// Change normal text color + void setTextColor(FXColor clr); + + /// Return selected text background + FXColor getSelBackColor() const { return selbackColor; } + + /// Change selected text background + void setSelBackColor(FXColor clr); + + /// Return selected text color + FXColor getSelTextColor() const { return seltextColor; } + + /// Change selected text color + void setSelTextColor(FXColor clr); + + /// Return active text color + FXColor getActiveTextColor() const { return activeTextColor; } + + /// Change active text color + void setActiveTextColor(FXColor clr); + + /// Return row color + FXColor getRowColor() const { return rowColor; } + + /// Change the row color + void setRowColor(FXColor clr); + + /// Return active color + FXColor getActiveColor() const { return activeColor; } + + /// Change the active color + void setActiveColor(FXColor clr); + + /// Get the current icon list style + FXuint getListStyle() const; + + /// Set the current icon list style. + void setListStyle(FXuint style); + + /// Set the status line help text for this widget + void setHelpText(const FXString& text); + + /// Get the status line help text for this widget + const FXString& getHelpText() const { return help; } + + /// Save list to a stream + virtual void save(FXStream& store) const; + + /// Load list from a stream + virtual void load(FXStream& store); + + /// Destructor + virtual ~GMTrackList(); + }; +#endif diff --git a/src/GMTrackView.cpp b/src/GMTrackView.cpp new file mode 100644 index 0000000..5648af7 --- /dev/null +++ b/src/GMTrackView.cpp @@ -0,0 +1,2249 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2007-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#include "gmdefs.h" +#include +#include "GMApp.h" +#include "GMList.h" +#include "GMTrackList.h" +#include "GMTrackView.h" +#include "GMWindow.h" +#include "GMSource.h" +#include "GMPlayerManager.h" +#include "GMIconTheme.h" +#include "GMClipboard.h" +#include "GMSourceView.h" +#include "GMColumnDialog.h" + + +#define HIDEBROWSER (FX4Splitter::ExpandBottomLeft) +#define SHOWBROWSER (FX4Splitter::ExpandTopLeft|FX4Splitter::ExpandTopRight|FX4Splitter::ExpandBottomLeft) + +static inline FXbool begins_with_keyword(const FXString & t){ + for (FXint i=0;igetPreferences().gui_sort_keywords.no();i++){ + if (comparecase(t,GMPlayerManager::instance()->getPreferences().gui_sort_keywords[i],GMPlayerManager::instance()->getPreferences().gui_sort_keywords[i].length())==0) return TRUE; + } + return FALSE; + } + +static FXint album_selectionchanged=-1; +static FXint artist_selectionchanged=-1; +static FXint genre_selectionchanged=-1; + +FXbool GMTrackView::reverse_artist=false; +FXbool GMTrackView::reverse_album=false; +FXbool GMTrackView::album_by_year=true; + +class GMStaticMenuCheck : public GMMenuCheck { +FXDECLARE(GMStaticMenuCheck) +protected: + GMStaticMenuCheck(); +private: + GMStaticMenuCheck(const GMStaticMenuCheck&); + GMStaticMenuCheck &operator=(const GMStaticMenuCheck&); +public: + long onButtonRelease(FXObject*,FXSelector,void*); + long onKeyRelease(FXObject*,FXSelector,void*); +public: + GMStaticMenuCheck(FXComposite* p,const FXString& text,FXObject* tgt=NULL,FXSelector sel=0,FXuint opts=0); + virtual ~GMStaticMenuCheck(); + }; + +FXDEFMAP(GMStaticMenuCheck) GMStaticMenuCheckMap[]={ + FXMAPFUNC(SEL_LEFTBUTTONRELEASE,0,GMStaticMenuCheck::onButtonRelease), + FXMAPFUNC(SEL_KEYRELEASE,0,GMStaticMenuCheck::onKeyRelease) + }; + +FXIMPLEMENT(GMStaticMenuCheck,GMMenuCheck,GMStaticMenuCheckMap,ARRAYNUMBER(GMStaticMenuCheckMap)) + +GMStaticMenuCheck::GMStaticMenuCheck(){ + } + +GMStaticMenuCheck::GMStaticMenuCheck(FXComposite* p,const FXString& text,FXObject* tgt,FXSelector sel,FXuint opts) : GMMenuCheck(p,text,tgt,sel,opts) { + } + +GMStaticMenuCheck::~GMStaticMenuCheck(){ + } + + +// Released button +long GMStaticMenuCheck::onButtonRelease(FXObject*,FXSelector,void*){ + FXbool active=isActive(); + if(!isEnabled()) return 0; + if(active){ + setCheck(!check); + if(target){ target->tryHandle(this,FXSEL(SEL_COMMAND,message),(void*)(FXuval)check); } + } + return 1; + } + +// Keyboard release +long GMStaticMenuCheck::onKeyRelease(FXObject*,FXSelector,void* ptr){ + FXEvent* event=(FXEvent*)ptr; + if(isEnabled() && (flags&FLAG_PRESSED)){ + FXTRACE((200,"%s::onKeyRelease %p keysym=0x%04x state=%04x\n",getClassName(),this,event->code,event->state)); + if(event->code==KEY_space || event->code==KEY_KP_Space || event->code==KEY_Return || event->code==KEY_KP_Enter){ + flags&=~FLAG_PRESSED; + setCheck(!check); + if(target) target->tryHandle(this,FXSEL(SEL_COMMAND,message),(void*)(FXuval)check); + return 1; + } + } + return 0; + } + + + + +FXDEFMAP(GMTrackView) GMTrackViewMap[]={ + FXMAPFUNC(SEL_UPDATE,GMTrackView::ID_TOGGLE_BROWSER,GMTrackView::onUpdToggleBrowser), + FXMAPFUNC(SEL_UPDATE,GMTrackView::ID_TOGGLE_GENRES,GMTrackView::onUpdToggleGenres), + FXMAPFUNC(SEL_UPDATE,GMTrackView::ID_TOGGLE_FILTER,GMTrackView::onUpdToggleFilter), + + FXMAPFUNC(SEL_UPDATE,GMTrackView::ID_COPY,GMTrackView::onUpdCopy), + FXMAPFUNC(SEL_UPDATE,GMTrackView::ID_PASTE,GMTrackView::onUpdPaste), + FXMAPFUNC(SEL_UPDATE,GMTrackView::ID_SHOW_CURRENT,GMTrackView::onUpdShowCurrent), + + FXMAPFUNCS(SEL_UPDATE,GMTrackView::ID_FILTER_TRACK,GMTrackView::ID_FILTER_LAST,GMTrackView::onUpdFilterMask), + FXMAPFUNCS(SEL_COMMAND,GMTrackView::ID_FILTER_TRACK,GMTrackView::ID_FILTER_LAST,GMTrackView::onCmdFilterMask), + + + FXMAPFUNC(SEL_COMMAND,GMTrackView::ID_ARTIST_LIST_HEADER,GMTrackView::onCmdSortArtistList), + FXMAPFUNC(SEL_COMMAND,GMTrackView::ID_ALBUM_LIST_HEADER,GMTrackView::onCmdSortAlbumList), + FXMAPFUNC(SEL_COMMAND,GMTrackView::ID_GENRE_LIST_HEADER,GMTrackView::onCmdSortGenreList), + FXMAPFUNC(SEL_COMMAND,GMTrackView::ID_TOGGLE_BROWSER,GMTrackView::onCmdToggleBrowser), + FXMAPFUNC(SEL_COMMAND,GMTrackView::ID_TOGGLE_GENRES,GMTrackView::onCmdToggleGenres), + FXMAPFUNC(SEL_COMMAND,GMTrackView::ID_TOGGLE_FILTER,GMTrackView::onCmdToggleFilter), + FXMAPFUNC(SEL_COMMAND,GMTrackView::ID_CLOSE_FILTER,GMTrackView::onCmdToggleFilter), + + FXMAPFUNC(SEL_COMMAND,GMTrackView::ID_COPY,GMTrackView::onCmdCopy), + FXMAPFUNC(SEL_COMMAND,GMTrackView::ID_PASTE,GMTrackView::onCmdPaste), + FXMAPFUNC(SEL_COMMAND,GMTrackView::ID_SHOW_CURRENT,GMTrackView::onCmdShowCurrent), + + FXMAPFUNC(SEL_COMMAND,GMTrackView::ID_FILTER,GMTrackView::onCmdFilter), + FXMAPFUNC(SEL_CHANGED,GMTrackView::ID_FILTER,GMTrackView::onCmdFilter), + FXMAPFUNC(SEL_TIMEOUT,GMTrackView::ID_FILTER,GMTrackView::onCmdFilter), + + FXMAPFUNC(SEL_COMMAND,GMTrackView::ID_FILTER_MODE,GMTrackView::onCmdFilter), + + + FXMAPFUNC(SEL_COMMAND,GMTrackView::ID_GENRE_LIST,GMTrackView::onCmdGenreSelected), + FXMAPFUNC(SEL_SELECTED,GMTrackView::ID_GENRE_LIST,GMTrackView::onCmdGenreSelected), + FXMAPFUNC(SEL_DESELECTED,GMTrackView::ID_GENRE_LIST,GMTrackView::onCmdGenreSelected), + FXMAPFUNC(SEL_COMMAND,GMTrackView::ID_ARTIST_LIST,GMTrackView::onCmdArtistSelected), + FXMAPFUNC(SEL_SELECTED,GMTrackView::ID_ARTIST_LIST,GMTrackView::onCmdArtistSelected), + FXMAPFUNC(SEL_DESELECTED,GMTrackView::ID_ARTIST_LIST,GMTrackView::onCmdArtistSelected), + FXMAPFUNC(SEL_DOUBLECLICKED,GMTrackView::ID_ARTIST_LIST,GMTrackView::onCmdArtistSelected), + FXMAPFUNC(SEL_COMMAND,GMTrackView::ID_ALBUM_LIST,GMTrackView::onCmdAlbumSelected), + FXMAPFUNC(SEL_SELECTED,GMTrackView::ID_ALBUM_LIST,GMTrackView::onCmdAlbumSelected), + FXMAPFUNC(SEL_DESELECTED,GMTrackView::ID_ALBUM_LIST,GMTrackView::onCmdAlbumSelected), + FXMAPFUNC(SEL_DOUBLECLICKED,GMTrackView::ID_ALBUM_LIST,GMTrackView::onCmdAlbumSelected), + + FXMAPFUNC(SEL_RIGHTBUTTONRELEASE,GMTrackView::ID_GENRE_LIST,GMTrackView::onGenreContextMenu), + FXMAPFUNC(SEL_RIGHTBUTTONRELEASE,GMTrackView::ID_ARTIST_LIST,GMTrackView::onArtistContextMenu), + FXMAPFUNC(SEL_RIGHTBUTTONRELEASE,GMTrackView::ID_ALBUM_LIST,GMTrackView::onAlbumContextMenu), + FXMAPFUNC(SEL_RIGHTBUTTONRELEASE,GMTrackView::ID_TRACK_LIST,GMTrackView::onTrackContextMenu), + FXMAPFUNC(SEL_RIGHTBUTTONRELEASE,GMTrackView::ID_TRACK_LIST_HEADER,GMTrackView::onTrackHeaderContextMenu), + FXMAPFUNC(SEL_RIGHTBUTTONRELEASE,GMTrackView::ID_ALBUM_LIST_HEADER,GMTrackView::onAlbumHeaderContextMenu), + + + FXMAPFUNC(SEL_KEYPRESS,GMTrackView::ID_GENRE_LIST,GMTrackView::onCmdGenreKeyPress), + FXMAPFUNC(SEL_KEYPRESS,GMTrackView::ID_ARTIST_LIST,GMTrackView::onCmdArtistKeyPress), + FXMAPFUNC(SEL_KEYPRESS,GMTrackView::ID_ALBUM_LIST,GMTrackView::onCmdAlbumKeyPress), + FXMAPFUNC(SEL_KEYPRESS,GMTrackView::ID_TRACK_LIST,GMTrackView::onCmdTrackKeyPress), + + + FXMAPFUNC(SEL_DOUBLECLICKED,GMTrackView::ID_TRACK_LIST,GMTrackView::onCmdPlayTrack), + + FXMAPFUNC(SEL_BEGINDRAG,GMTrackView::ID_ARTIST_LIST,GMTrackView::onCmdBeginDrag), + FXMAPFUNC(SEL_BEGINDRAG,GMTrackView::ID_ALBUM_LIST,GMTrackView::onCmdBeginDrag), + FXMAPFUNC(SEL_BEGINDRAG,GMTrackView::ID_TRACK_LIST,GMTrackView::onCmdBeginDrag), + + FXMAPFUNC(SEL_DRAGGED,GMTrackView::ID_ARTIST_LIST,GMTrackView::onCmdDragged), + FXMAPFUNC(SEL_DRAGGED,GMTrackView::ID_ALBUM_LIST,GMTrackView::onCmdDragged), + FXMAPFUNC(SEL_DRAGGED,GMTrackView::ID_TRACK_LIST,GMTrackView::onCmdDragged), + + FXMAPFUNC(SEL_ENDDRAG,GMTrackView::ID_ARTIST_LIST,GMTrackView::onCmdEndDrag), + FXMAPFUNC(SEL_ENDDRAG,GMTrackView::ID_ALBUM_LIST,GMTrackView::onCmdEndDrag), + FXMAPFUNC(SEL_ENDDRAG,GMTrackView::ID_TRACK_LIST,GMTrackView::onCmdEndDrag), + + FXMAPFUNC(SEL_DND_ENTER,GMTrackView::ID_TRACK_LIST,GMTrackView::onDndTrackEnter), + FXMAPFUNC(SEL_DND_LEAVE,GMTrackView::ID_TRACK_LIST,GMTrackView::onDndTrackLeave), + FXMAPFUNC(SEL_DND_MOTION,GMTrackView::ID_TRACK_LIST,GMTrackView::onDndTrackMotion), + FXMAPFUNC(SEL_DND_DROP,GMTrackView::ID_TRACK_LIST,GMTrackView::onDndTrackDrop), + + + FXMAPFUNC(SEL_DND_MOTION,GMTrackView::ID_GENRE_LIST,GMTrackView::onDndMotion), + FXMAPFUNC(SEL_DND_MOTION,GMTrackView::ID_ARTIST_LIST,GMTrackView::onDndMotion), + FXMAPFUNC(SEL_DND_MOTION,GMTrackView::ID_ALBUM_LIST,GMTrackView::onDndMotion), + FXMAPFUNC(SEL_DND_DROP,GMTrackView::ID_GENRE_LIST,GMTrackView::onDndDrop), + FXMAPFUNC(SEL_DND_DROP,GMTrackView::ID_ARTIST_LIST,GMTrackView::onDndDrop), + FXMAPFUNC(SEL_DND_DROP,GMTrackView::ID_ALBUM_LIST,GMTrackView::onDndDrop), + + FXMAPFUNC(SEL_DND_REQUEST,GMTrackView::ID_ARTIST_LIST,GMTrackView::onDndRequest), + FXMAPFUNC(SEL_DND_REQUEST,GMTrackView::ID_ALBUM_LIST,GMTrackView::onDndRequest), + FXMAPFUNC(SEL_DND_REQUEST,GMTrackView::ID_TRACK_LIST,GMTrackView::onDndRequest), + + FXMAPFUNCS(SEL_COMMAND,GMTrackView::ID_COLUMN_FIRST,GMTrackView::ID_COLUMN_LAST,GMTrackView::onCmdShowColumn), + FXMAPFUNCS(SEL_UPDATE,GMTrackView::ID_COLUMN_FIRST,GMTrackView::ID_COLUMN_LAST,GMTrackView::onUpdShowColumn), + FXMAPFUNCS(SEL_COMMAND,GMTrackView::ID_SORT_FIRST,GMTrackView::ID_SORT_LAST,GMTrackView::onCmdSort), + FXMAPFUNCS(SEL_UPDATE,GMTrackView::ID_SORT_FIRST,GMTrackView::ID_SORT_LAST,GMTrackView::onUpdSort), + FXMAPFUNC(SEL_UPDATE,GMTrackView::ID_SORT_REVERSE,GMTrackView::onUpdSortReverse), + FXMAPFUNC(SEL_UPDATE,GMTrackView::ID_SORT_BROWSE,GMTrackView::onUpdSortBrowse), + FXMAPFUNC(SEL_UPDATE,GMTrackView::ID_SORT_SHUFFLE,GMTrackView::onUpdSortShuffle), + + FXMAPFUNC(SEL_COMMAND,GMTrackView::ID_SORT_SHUFFLE,GMTrackView::onCmdSortShuffle), + FXMAPFUNC(SEL_COMMAND,GMTrackView::ID_SORT_BROWSE,GMTrackView::onCmdSortBrowse), + FXMAPFUNC(SEL_COMMAND,GMTrackView::ID_SORT_DEFAULT,GMTrackView::onCmdSortDefault), + + FXMAPFUNC(SEL_CHORE,GMTrackView::ID_LOAD_ALBUM_ICONS,GMTrackView::onCmdLoadAlbumIcons), + FXMAPFUNC(SEL_COMMAND,GMTrackView::ID_CONFIGURE_COLUMNS,GMTrackView::onCmdConfigureColumns), + }; + +FXIMPLEMENT(GMTrackView,FXPacker,GMTrackViewMap,ARRAYNUMBER(GMTrackViewMap)) + +GMTrackView::GMTrackView() { + source = NULL; + } + +GMTrackView::GMTrackView(FXComposite* p) : FXPacker(p,LAYOUT_FILL_X|LAYOUT_FILL_Y,0,0,0,0,0,0,0,0) , source(NULL) { + GMScrollFrame * sunkenframe; + +#if FOXVERSION < FXVERSION(1,7,0) + shuffle_seed = (FXuint)FXSystem::now(); +#else + shuffle_seed = (FXuint)FXThread::time(); +#endif + + filtermask=FILTER_DEFAULT; + + updateFont(); + + filtermenu = new GMMenuPane(getShell()); + new GMStaticMenuCheck(filtermenu,tr("Title"),this,ID_FILTER_TRACK); + new GMStaticMenuCheck(filtermenu,tr("Artist"),this,ID_FILTER_ARTIST); + new GMStaticMenuCheck(filtermenu,tr("Album"),this,ID_FILTER_ALBUM); + new GMStaticMenuCheck(filtermenu,tr("Genre"),this,ID_FILTER_GENRE); + + + filterframe = new FXHorizontalFrame(this,LAYOUT_SIDE_TOP|LAYOUT_FILL_X|PACK_UNIFORM_HEIGHT,0,0,0,0,0,0,0,0); + new GMButton(filterframe,tr("\tClose Filter\tClose Filter"),GMIconTheme::instance()->icon_close,this,ID_CLOSE_FILTER,BUTTON_TOOLBAR|FRAME_RAISED|LAYOUT_RIGHT); + new FXMenuButton(filterframe,tr("&Find"),GMIconTheme::instance()->icon_find,filtermenu,MENUBUTTON_NOARROWS|FRAME_RAISED|MENUBUTTON_TOOLBAR|ICON_BEFORE_TEXT|LAYOUT_CENTER_Y); + filterfield = new GMTextField(filterframe,20,this,ID_FILTER,LAYOUT_FILL_X|FRAME_LINE); + + browsersplit = new FX4Splitter(this,LAYOUT_FILL_X|LAYOUT_FILL_Y|FOURSPLITTER_TRACKING); + genresplit = new FX4Splitter(browsersplit,FOURSPLITTER_TRACKING); + genrelistframe = new GMScrollFrame(genresplit); + genrelistheader = new GMHeaderButton(genrelistframe,tr("Genres\tPress to change sorting order\tPress to change sorting order"),NULL,this,GMTrackView::ID_GENRE_LIST_HEADER,LAYOUT_FILL_X|FRAME_RAISED|JUSTIFY_LEFT); + genrelist = new GMList(genrelistframe,this,ID_GENRE_LIST,LAYOUT_FILL_X|LAYOUT_FILL_Y|LIST_EXTENDEDSELECT); + + sunkenframe = new GMScrollFrame(genresplit); + artistlistheader = new GMHeaderButton(sunkenframe,tr("Artists\tPress to change sorting order\tPress to change sorting order"),NULL,this,GMTrackView::ID_ARTIST_LIST_HEADER,LAYOUT_FILL_X|FRAME_RAISED|JUSTIFY_LEFT); + artistlist = new GMList(sunkenframe,this,ID_ARTIST_LIST,LAYOUT_FILL_X|LAYOUT_FILL_Y|LIST_EXTENDEDSELECT); + + sunkenframe = new GMScrollFrame(browsersplit); + albumlistheader = new GMHeaderButton(sunkenframe,tr("Albums\tPress to change sorting order\tPress to change sorting order"),NULL,this,GMTrackView::ID_ALBUM_LIST_HEADER,LAYOUT_FILL_X|FRAME_RAISED|JUSTIFY_LEFT); + albumlist = new GMList(sunkenframe,this,ID_ALBUM_LIST,LAYOUT_FILL_X|LAYOUT_FILL_Y|LIST_EXTENDEDSELECT); + + + sunkenframe = new GMScrollFrame(browsersplit); + tracklist = new GMTrackList(sunkenframe,this,ID_TRACK_LIST,LAYOUT_FILL_X|LAYOUT_FILL_Y|TRACKLIST_EXTENDEDSELECT); + + + + + + + + + + + + columnmenu = new GMMenuPane(getShell()); + + for (FXint i=ID_COLUMN_FIRST;igetDefaultCursor(DEF_ARROW_CURSOR)); + gm_set_window_cursor(columnmenu,getApp()->getDefaultCursor(DEF_ARROW_CURSOR)); + gm_set_window_cursor(filtermenu,getApp()->getDefaultCursor(DEF_ARROW_CURSOR)); + + genrelist->setNumVisible(9); + artistlist->setNumVisible(9); + albumlist->setNumVisible(9); + + genrelist->setThickFont(font_listhead); + artistlist->setThickFont(font_listhead); + albumlist->setThickFont(font_listhead); + tracklist->setActiveFont(font_listhead); + + genrelist->dropEnable(); + artistlist->dropEnable(); + albumlist->dropEnable(); + tracklist->dropEnable(); + + genrelist->setSortFunc(genre_list_sort); + artistlist->setSortFunc(artist_list_sort); + albumlist->setSortFunc(album_list_sort); + + genrelistheader->setArrowState(ARROW_DOWN); + artistlistheader->setArrowState(ARROW_DOWN); + albumlistheader->setArrowState(ARROW_DOWN); + + browsersplit->setBarSize(7); + genresplit->setBarSize(7); + + getShell()->getAccelTable()->addAccel(parseAccel("Ctrl-N"),this,FXSEL(SEL_COMMAND,ID_SORT_DEFAULT)); + updateColors(); + } + +GMTrackView::~GMTrackView(){ + getApp()->removeChore(this,ID_LOAD_ALBUM_ICONS); + getApp()->removeTimeout(this,ID_FILTER); + } + +void GMTrackView::updateFont() { +#if FOXVERSION < FXVERSION(1,7,17) + FXFontDesc fontdescription; + getApp()->getNormalFont()->getFontDesc(fontdescription); +#else + FXFontDesc fontdescription = getApp()->getNormalFont()->getFontDesc(); +#endif + fontdescription.slant = FXFont::Italic; + fontdescription.weight = FXFont::Bold; + fontdescription.size -= 10; + + if (font_listhead) { + font_listhead->destroy(); + font_listhead->setFontDesc(fontdescription); + font_listhead->create(); + } + else { + font_listhead = new FXFont(getApp(),fontdescription); + font_listhead->create(); + } + } + + +void GMTrackView::updateColors(){ + tracklist->setRowColor(GMPlayerManager::instance()->getPreferences().gui_row_color); + tracklist->setActiveColor(GMPlayerManager::instance()->getPreferences().gui_play_color); + tracklist->setActiveTextColor(GMPlayerManager::instance()->getPreferences().gui_playtext_color); + genrelist->setRowColor(GMPlayerManager::instance()->getPreferences().gui_row_color); + artistlist->setRowColor(GMPlayerManager::instance()->getPreferences().gui_row_color); + albumlist->setRowColor(GMPlayerManager::instance()->getPreferences().gui_row_color); + } + +void GMTrackView::updateIcons(){ + FXint i=0; + FXIcon * icon_genre = NULL; + FXIcon * icon_artist = NULL; + FXIcon * icon_album = NULL; + + if (GMPlayerManager::instance()->getPreferences().gui_show_browser_icons) { + icon_genre = GMIconTheme::instance()->icon_genre; + icon_artist = GMIconTheme::instance()->icon_artist; + icon_album = GMIconTheme::instance()->icon_album; + } + + for (i=0;igetNumItems();i++){ + genrelist->setItemIcon(i,icon_genre); + } + + for (i=0;igetNumItems();i++){ + artistlist->setItemIcon(i,icon_artist); + } + + for (i=0;igetNumItems();i++){ + albumlist->setItemIcon(i,icon_album); + } + + } + + +void GMTrackView::clear() { + genrelist->clearItems(); + artistlist->clearItems(); + albumlist->clearItems(); + tracklist->clearItems(); + tracklist->setActiveItem(-1); + } + + +void GMTrackView::mark(FXint item,FXbool show/*=true*/) { + if (source && item>=0){ + source->markCurrent(tracklist,item); + if (show) tracklist->setCurrentItem(item); + } + if (show) tracklist->setActiveItem(item); + } + +void GMTrackView::showCurrent() { + FXASSERT(source); + FXASSERT(GMPlayerManager::instance()->getSource()); + source->findCurrent(tracklist,GMPlayerManager::instance()->getSource()); + } + +FXint GMTrackView::getCurrent() const{ + /* + // Any Items? + if (tracklist->getNumItems()==0) + return -1; + + // First Selected + for (FXint i=0;igetNumItems();i++){ + if (tracklist->isItemSelected(i)) return i; + } + + /// First Item + return 0; +*/ + return tracklist->getCurrentItem(); + } + +//generates a psuedo-random integer between min and max +extern int randint(int min, int max,unsigned int * random_seed); + +FXint GMTrackView::getNext(FXbool wrap){ + if (tracklist->getNumItems()==0) { + return -1; + } + else if (tracklist->getNumItems()>2 && GMPlayerManager::instance()->getPreferences().play_shuffle) { + if (tracklist->getActiveItem()>=0){ + for (FXint i=0,track=0;i<10;i++) { + track = randint(0,tracklist->getNumItems()-1,&shuffle_seed); + if (track!=tracklist->getActiveItem()) return track; + } + } + return randint(0,tracklist->getNumItems()-1,&shuffle_seed); + } + else if (tracklist->getActiveItem()==tracklist->getNumItems()-1){ + if (GMPlayerManager::instance()->getPreferences().play_repeat==REPEAT_ALL || wrap) + return 0; + else { + if (tracklist->getCurrentItem()==tracklist->getNumItems()-1){ + tracklist->setCurrentItem(0); + } + return -1; + } + } + return tracklist->getActiveItem()+1; + } + +FXint GMTrackView::getPrevious(){ + if (tracklist->getNumItems()==0) { + return -1; + } + else if (tracklist->getNumItems()>2 && GMPlayerManager::instance()->getPreferences().play_shuffle) { + if (tracklist->getActiveItem()>=0){ + for (FXint i=0,track=0;i<10;i++) { + track = randint(0,tracklist->getNumItems()-1,&shuffle_seed); + if (track!=tracklist->getActiveItem()) return track; + } + } + return randint(0,tracklist->getNumItems()-1,&shuffle_seed); + } + else if (tracklist->getActiveItem()==0) { + return tracklist->getNumItems()-1; + } + else { + return tracklist->getActiveItem()-1; + } + } + + +FXString GMTrackView::getTrackFilename(FXint item) const { + FXint track = tracklist->getItemId(item); + if (source && track!=-1 ) + return source->getTrackFilename(track); + return FXString::null; + } + + +void GMTrackView::getTracks(FXIntList & tracks) const{ + for (FXint i=0;igetNumItems();i++){ + tracks.append(tracklist->getItemId(i)); + } + } + + +static int compareindex(const void *a,const void *b){ + register FXint * aa = (FXint*)a; + register FXint * bb = (FXint*)b; + if ((*aa) > (*bb)) return 1; + else if ((*aa) < (*bb)) return -1; + return 0; + } + +void GMTrackView::getSelectedGenres(FXIntList & genres) const{ + if (genrelist->getNumItems()==1) { + if (genrelist->isItemSelected(0) && ((FXint)(FXival)genrelist->getItemData(0))!=-1) + genres.append((FXint)(FXival)genrelist->getItemData(0)); + } + else { + for (FXint i=1;igetNumItems();i++){ + if (genrelist->isItemSelected(i)) { + genres.append((FXint)(FXival)genrelist->getItemData(i)); + } + } + } + qsort(genres.data(),genres.no(),sizeof(FXint),compareindex); + } + + +void GMTrackView::getSelectedArtists(FXIntList & artists) const{ + if (artistlist->getNumItems()==1) { + if (artistlist->isItemSelected(0) && ((FXint)(FXival)artistlist->getItemData(0))!=-1) + artists.append((FXint)(FXival)artistlist->getItemData(0)); + } + else { + for (FXint i=1;igetNumItems();i++){ + if (artistlist->isItemSelected(i)) { + artists.append((FXint)(FXival)artistlist->getItemData(i)); + } + } + } + qsort(artists.data(),artists.no(),sizeof(FXint),compareindex); + } + + +void GMTrackView::getSelectedAlbums(FXIntList & albums) const{ + register FXint i=0; + if (albumlist->getNumItems()) { + if (albumlist->getItemData(0)==(void*)(FXival)-1) { + if (albumlist->isItemSelected(0)){ + for (i=1;igetNumItems();i++){ + albums.append((FXint)(FXival)albumlist->getItemData(i)); + albums.append(((GMAlbumListItem*)albumlist->getItem(i))->albums); + } + } + else { + for (i=1;igetNumItems();i++){ + if (albumlist->isItemSelected(i)) { + albums.append((FXint)(FXival)albumlist->getItemData(i)); + albums.append(((GMAlbumListItem*)albumlist->getItem(i))->albums); + } + } + } + } + else { + for (i=0;igetNumItems();i++){ + if (albumlist->isItemSelected(i)) { + albums.append((FXint)(FXival)albumlist->getItemData(i)); + albums.append(((GMAlbumListItem*)albumlist->getItem(i))->albums); + } + } + } + } + qsort(albums.data(),albums.no(),sizeof(FXint),compareindex); + } + + + + +void GMTrackView::getSelectedTracks(FXIntList & tracks) const{ + for (FXint i=0;igetNumItems();i++){ + if (tracklist->isItemSelected(i)) { + tracks.append(tracklist->getItemId(i)); + } + } + } + + +FXint GMTrackView::numTrackSelected() const { + FXint num=0; + for (FXint i=0;igetNumItems();i++){ + if (tracklist->isItemSelected(i)) num++; + } + return num; + } + +FXbool GMTrackView::trackSelected() const { + for (FXint i=0;igetNumItems();i++){ + if (tracklist->isItemSelected(i)) return true; + } + return false; + } + + +FXbool GMTrackView::hasTracks() const { + return tracklist->getNumItems()>0; + } + +void GMTrackView::selectGenreItem(FXint item) { + genrelist->killSelection(true); + genrelist->selectItem(item,true); + genrelist->setCurrentItem(item,true); + handle(this,FXSEL(SEL_COMMAND,ID_GENRE_LIST),(void*)(FXival)item); + + } + + +void GMTrackView::selectArtistItem(FXint item) { + artistlist->killSelection(true); + artistlist->selectItem(item,true); + artistlist->setCurrentItem(item,true); + handle(this,FXSEL(SEL_COMMAND,ID_ARTIST_LIST),(void*)(FXival)item); + } + + +void GMTrackView::selectAlbumItem(FXint item) { + albumlist->killSelection(true); + albumlist->selectItem(item,true); + albumlist->setCurrentItem(item,true); + handle(this,FXSEL(SEL_COMMAND,ID_ALBUM_LIST),(void*)(FXival)item); + } + + +void GMTrackView::selectTrackItem(FXint item) { + tracklist->killSelection(); + tracklist->selectItem(item); + tracklist->setCurrentItem(item); + } + + +void GMTrackView::setSource(GMSource * src) { + if (source!=src) { + + if (source) { + saveSettings(source->settingKey()); + + if (browsersplit->getExpanded()==SHOWBROWSER){ + saveSelection(genrelist,"genre-list-selection",source->settingKey()); + saveSelection(artistlist,"artist-list-selection",source->settingKey()); + saveSelection(albumlist,"album-list-selection",source->settingKey()); + } + + } + + source=src; + + if (source) { + + columns.clear(); + source->configure(columns); + + loadSettings(source->settingKey()); + + clear(); + + if (browsersplit->getExpanded()==SHOWBROWSER) { + listGenres(); + initSelection(genrelist,"genre-list-selection",source->settingKey()); + listArtists(); + initSelection(artistlist,"artist-list-selection",source->settingKey()); + listAlbums(); + initSelection(albumlist,"album-list-selection",source->settingKey()); + listTracks(); + } + else { + listTracks(); + } + + tracklist->setPosition(tracklist_posx,tracklist_posy); + } + } + } + +void GMTrackView::saveSelection(GMList * list,const char * key,const FXString & section) const{ + FXString value; + for (FXint i=0;igetNumItems();i++){ + if (list->isItemSelected(i)) + value+=GMStringVal(i)+";"; + } + getApp()->reg().writeStringEntry(section.text(),key,value.text()); + } + + +void GMTrackView::initSelection(GMList * list,const FXchar * key,const FXString & section){ + FXint i=0,x=0,nselected=0; + FXString part; + FXString view = getApp()->reg().readStringEntry(section.text(),key,""); + if (!view.empty()){ + list->killSelection(false); + part=view.section(';',i); + while(!part.empty()){ +#if FOXVERSION >= FXVERSION(1,7,12) + x = part.toInt(); +#else + x = FXIntVal(part); +#endif + if (x>=0 && xgetNumItems()){ + nselected++; + list->selectItem(x); + if (i==0) list->makeItemVisible(x); + } + part=view.section(';',++i); + } + if (nselected==0 && list->getNumItems()){ + list->selectItem(0); + } + } + } + +void GMTrackView::init(GMSource * src) { + FXASSERT(source==NULL); + FXASSERT(src); + + source=src; + + + columns.clear(); + source->configure(columns); + + loadSettings(source->settingKey()); + + clear(); + + if (source) { + if (browsersplit->getExpanded()==SHOWBROWSER) { + listGenres(); + initSelection(genrelist,"genre-list-selection",source->settingKey()); + listArtists(); + initSelection(artistlist,"artist-list-selection",source->settingKey()); + listAlbums(); + initSelection(albumlist,"album-list-selection",source->settingKey()); + listTracks(); + } + else { + listTracks(); + } + + FXint active = getApp()->reg().readIntEntry("window","track-list-current",-1); + if (active>=0 && activegetNumItems()) { + tracklist->setCurrentItem(active); + tracklist->selectItem(active); + } + tracklist->setPosition(tracklist_posx,tracklist_posy); + } + } + + + +void GMTrackView::saveView() const { + if (source) + saveSettings(source->settingKey()); + + if (browsersplit->getExpanded()==SHOWBROWSER){ + saveSelection(genrelist,"genre-list-selection",source->settingKey()); + saveSelection(artistlist,"artist-list-selection",source->settingKey()); + saveSelection(albumlist,"album-list-selection",source->settingKey()); + } + getApp()->reg().writeIntEntry("window","track-list-current",tracklist->getActiveItem()); + } + + + +void GMTrackView::refresh() { + clear(); + + if (source) { + if (browsersplit->getExpanded()==SHOWBROWSER) { + listGenres(); + listArtists(); + listAlbums(); + listTracks(); + } + else { + listTracks(); + } + } + + } + + +void GMTrackView::resort() { + sortGenres(); + sortArtists(); + sortAlbums(); + sortTracks(); + } + + +FXbool GMTrackView::listGenres() { + if (source) { + + FXIcon * icon = NULL; + if (GMPlayerManager::instance()->getPreferences().gui_show_browser_icons) + icon = GMIconTheme::instance()->icon_genre; + + if (!source->listGenres(genrelist,icon)) + return false; + + genrelist->sortItems(); + if (genrelist->getNumItems()>1) + genrelist->prependItem(GMStringFormat(fxtrformat("All %d Genres"),genrelist->getNumItems()),icon,(void*)(FXival)-1); + else + genrelist->prependItem(tr("All Genres"),icon,(void*)(FXival)-1); + + genrelist->setCurrentItem(0,false); + genrelist->selectItem(0,false); + } + return true; + } + + +FXbool GMTrackView::listArtists(){ + if (source) { + + FXIcon * icon = NULL; + if (GMPlayerManager::instance()->getPreferences().gui_show_browser_icons) + icon = GMIconTheme::instance()->icon_artist; + + + FXIntList genreselection; + + getSelectedGenres(genreselection); + + if (!source->listArtists(artistlist,icon,genreselection)) + return false; + + artistlist->sortItems(); + if (artistlist->getNumItems()>1) { + artistlist->prependItem(GMStringFormat(fxtrformat("All %d Artists"),artistlist->getNumItems()),icon,(void*)(FXival)-1); + } + + if (GMPlayerManager::instance()->playing() && GMPlayerManager::instance()->getSource()) { + if (source->findCurrentArtist(artistlist,GMPlayerManager::instance()->getSource())) { + return true; + } + } + + if (artistlist->getNumItems()){ + artistlist->setCurrentItem(0,false); + artistlist->selectItem(0,false); + } + } + return true; + } + + +FXbool GMTrackView::listAlbums(){ + if (source) { + + FXIcon * icon = NULL; + if (GMPlayerManager::instance()->getPreferences().gui_show_browser_icons) { + if (GMPlayerManager::instance()->getPreferences().gui_show_albumcovers) + icon = GMIconTheme::instance()->icon_nocover; + else + icon = GMIconTheme::instance()->icon_album; + } + + FXIntList genreselection; + FXIntList artistselection; + + getSelectedGenres(genreselection); + getSelectedArtists(artistselection); + + if (!source->listAlbums(albumlist,icon,artistselection,genreselection)) + return false; + + albumlist->sortItems(); + if (albumlist->getNumItems()>1){ + albumlist->prependItem(GMStringFormat(fxtrformat("All %d Albums"),albumlist->getNumItems()),icon,(void*)(FXival)-1); + } + + if (albumlist->getNumItems() && GMPlayerManager::instance()->getPreferences().gui_show_albumcovers) + getApp()->addChore(this,ID_LOAD_ALBUM_ICONS,(void*)(FXival)0); + + if (GMPlayerManager::instance()->playing() && GMPlayerManager::instance()->getSource()) { + if (source->findCurrentAlbum(albumlist,GMPlayerManager::instance()->getSource())) { + return true; + } + } + + if (albumlist->getNumItems()) { + albumlist->setCurrentItem(0,false); + albumlist->selectItem(0,false); + } + } + return true; + } + + +FXbool GMTrackView::listTracks(){ + if (source) { + + tracklist->setActiveItem(-1); + + FXIntList genreselection; + FXIntList albumselection; + + if (browsersplit->getExpanded()==SHOWBROWSER){ + getSelectedGenres(genreselection); + getSelectedAlbums(albumselection); + if (albumselection.no()==0) + return false; + } + + if (!source->listTracks(tracklist,albumselection,genreselection)) + return false; + + layout(); + + tracklist->setCurrentItem(-1); + + if (tracklist->getNumItems()) { + sortTracks(); + if (tracklist->getCurrentItem()==-1) + tracklist->setCurrentItem(0); + } +/* + if (GMPlayerManager::instance()->playing() && GMPlayerManager::instance()->getSource()) + source->findCurrent(tracklist,GMPlayerManager::instance()->getSource()); +*/ + //fxmessage("getCurrentItem()==%d\n",tracklist->getCurrentItem()); + + } + return true; + } + + +void GMTrackView::sortGenres() const{ + genrelist->sortItems(); + + /// Make sure "All" is on top. + FXint data=-1; + FXint all = genrelist->findItemByData((void*)(FXival)(FXint)data); + if (all>0) { + genrelist->moveItem(0,all); + } + } + +void GMTrackView::sortArtists() const{ + artistlist->sortItems(); + + /// Make sure "All" is on top. + FXint data=-1; + FXint all = artistlist->findItemByData((void*)(FXival)(FXint)data); + if (all>0) { + artistlist->moveItem(0,all); + } + } + +void GMTrackView::sortAlbums() const { + albumlist->sortItems(); + + /// Make sure "All" is on top. + FXint data=-1; + FXint all = albumlist->findItemByData((void*)(FXival)(FXint)data); + if (all>0) { + albumlist->moveItem(0,all); + } + } + +void GMTrackView::sortTracks() const{ + if (tracklist->getSortMethod()==HEADER_SHUFFLE) + source->shuffle(tracklist,sort_seed); + else + tracklist->sortItems(); + + if (GMPlayerManager::instance()->playing() && GMPlayerManager::instance()->getSource()) + source->findCurrent(tracklist,GMPlayerManager::instance()->getSource()); + + } + +void GMTrackView::setSortMethod(FXint def,FXbool reverse) { + if (def==HEADER_BROWSE) { + tracklist->setSortMethod(HEADER_BROWSE); + tracklist->setSortFunc(source->getSortBrowse()); + } + else if (def==HEADER_SHUFFLE) { + tracklist->setSortMethod(HEADER_SHUFFLE); + tracklist->setSortFunc(NULL); + } + else { + for (FXint i=0;isetSortMethod(columns[i].type); + if (reverse) + tracklist->setSortFunc(columns[i].descending); + else + tracklist->setSortFunc(columns[i].ascending); + break; + } + } + } + } + +FXbool GMTrackView::getSortReverse() const { + for (FXint i=0;igetSortMethod()) { + if (columns[i].descending==tracklist->getSortFunc()) + return true; + break; + } + } + return false; + } + + + +void GMTrackView::loadSettings(const FXString & key) { + FXbool sort_reverse,shown; + FXint split; + + sort_reverse = getApp()->reg().readBoolEntry(key.text(),"genre-list-sort-reverse",false); + if (sort_reverse) { + genrelist->setSortFunc(genre_list_sort_reverse); + genrelistheader->setArrowState(ARROW_UP); + } + else { + genrelist->setSortFunc(genre_list_sort); + genrelistheader->setArrowState(ARROW_DOWN); + } + + reverse_artist = getApp()->reg().readBoolEntry(key.text(),"artist-list-sort-reverse",false); + if (reverse_artist) { + artistlist->setSortFunc(artist_list_sort_reverse); + artistlistheader->setArrowState(ARROW_UP); + } + else { + artistlist->setSortFunc(artist_list_sort); + artistlistheader->setArrowState(ARROW_DOWN); + } + + album_by_year = getApp()->reg().readBoolEntry(key.text(),"album-list-sort-by-year",false); + reverse_album = getApp()->reg().readBoolEntry(key.text(),"album-list-sort-reverse",false); + if (reverse_album) { + albumlist->setSortFunc(album_list_sort_reverse); + albumlistheader->setArrowState(ARROW_UP); + } + else { + albumlist->setSortFunc(album_list_sort); + albumlistheader->setArrowState(ARROW_DOWN); + } + + shown = getApp()->reg().readBoolEntry(key.text(),"genre-list",false); + if (shown) + genrelistframe->show(); + else + genrelistframe->hide(); + + shown = getApp()->reg().readBoolEntry(key.text(),"browser",source->defaultBrowse()); + if (shown && source->canBrowse()) + browsersplit->setExpanded(SHOWBROWSER); + else + browsersplit->setExpanded(HIDEBROWSER); + + + split = getApp()->reg().readIntEntry(key.text(),"browser-track-split",-1); + if (split!=-1) browsersplit->setVSplit(split); + + split = getApp()->reg().readIntEntry(key.text(),"artist-album-split",-1); + if (split!=-1) browsersplit->setHSplit(split); + + split = getApp()->reg().readIntEntry(key.text(),"genre-artist-split",-1); + if (split!=-1) genresplit->setHSplit(split); + + +// split = getApp()->reg().readIntEntry(key.text(),"browser-split",-1); +// if (split!=-1) setSplit(0,split); + + tracklist_posx = getApp()->reg().readIntEntry(key.text(),"track-list-posx",0); + tracklist_posy = getApp()->reg().readIntEntry(key.text(),"track-list-posy",0); + + shown = getApp()->reg().readBoolEntry(key.text(),"filter",true); + if (shown && source && source->canFilter()) + filterframe->show(); + else + filterframe->hide(); + + filterfield->setText(getApp()->reg().readStringEntry(key.text(),"filter-text","")); + if (getApp()->reg().existingEntry(key.text(),"filter-mode")) { + FXuint mode = getApp()->reg().readIntEntry(key.text(),"filter-mode",0); + switch(mode){ + case 0 : filtermask=FILTER_TRACK|FILTER_ALBUM|FILTER_ARTIST; break; + case 1 : filtermask=FILTER_ARTIST; break; + case 2 : filtermask=FILTER_ALBUM; break; + case 3 : filtermask=FILTER_TRACK; break; + default: filtermask=FILTER_DEFAULT; break; + } + getApp()->reg().deleteEntry(key.text(),"filter-mode"); +#if FOXVERSION < FXVERSION(1,7,0) + getApp()->reg().writeUnsignedEntry(key.text(),"filter-mask",filtermask); +#else + getApp()->reg().writeUIntEntry(key.text(),"filter-mask",filtermask); +#endif + } + else { +#if FOXVERSION < FXVERSION(1,7,0) + filtermask = getApp()->reg().readUnsignedEntry(key.text(),"filter-mask",FILTER_DEFAULT); +#else + filtermask = getApp()->reg().readUIntEntry(key.text(),"filter-mask",FILTER_DEFAULT); +#endif + } + + if (source) source->setFilter(filterfield->getText().trim().simplify(),filtermask); + + + loadTrackSettings(key); + } + + + +void GMTrackView::loadTrackSettings(const FXString & key) { + FXString browseprefix = (browsersplit->getExpanded()==SHOWBROWSER) ? "browse" : "list"; + FXString name; + tracklist->clearHeaders(); + for (FXint i=0;ireg().readBoolEntry(key.text(),FXString(browseprefix+"-showcolumn-"+name).text(),(browsersplit->getExpanded()==SHOWBROWSER) ? columns[i].default_browser_show : columns[i].default_show); + columns[i].size = getApp()->reg().readIntEntry(key.text(),FXString(browseprefix+"-columnwidth-"+name).text(),columns[i].size); + columns[i].index = getApp()->reg().readIntEntry(key.text(),FXString(browseprefix+"-columnindex-"+name).text(),columns[i].index); + if (columns[i].show) { + tracklist->appendHeader(fxtr(columns[i].name.text()),columns[i].size,&columns[i]); + } + } + FXint sort = getApp()->reg().readIntEntry(key.text(),FXString(browseprefix+"-sort-column").text(),source->getSortColumn(browsersplit->getExpanded()==SHOWBROWSER)); + FXbool reverse = getApp()->reg().readBoolEntry(key.text(),FXString(browseprefix+"-sort-reverse").text(),false); + +#if FOXVERSION < FXVERSION(1,7,0) + sort_seed = getApp()->reg().readUnsignedEntry(key.text(),FXString(browseprefix+"-sort-seed").text(),(FXuint)FXSystem::now()); +#else + sort_seed = getApp()->reg().readUIntEntry(key.text(),FXString(browseprefix+"-sort-seed").text(),(FXuint)FXThread::time()); +#endif + + setSortMethod(sort,reverse); + } + + +void GMTrackView::saveSettings(const FXString & key) const { + getApp()->reg().writeBoolEntry(key.text(),"genre-list-sort-reverse",genrelist->getSortFunc()==genre_list_sort_reverse); + getApp()->reg().writeBoolEntry(key.text(),"artist-list-sort-reverse",artistlist->getSortFunc()==artist_list_sort_reverse); + getApp()->reg().writeBoolEntry(key.text(),"album-list-sort-reverse",albumlist->getSortFunc()==album_list_sort_reverse); + getApp()->reg().writeBoolEntry(key.text(),"album-list-sort-by-year",album_by_year); + + getApp()->reg().writeBoolEntry(key.text(),"genre-list",genrelistframe->shown()); + getApp()->reg().writeBoolEntry(key.text(),"browser",browsersplit->getExpanded()==SHOWBROWSER); + getApp()->reg().writeIntEntry(key.text(),"browser-track-split",browsersplit->getVSplit()); + getApp()->reg().writeIntEntry(key.text(),"artist-album-split",browsersplit->getHSplit()); + getApp()->reg().writeIntEntry(key.text(),"genre-artist-split",genresplit->getHSplit()); +#if FOXVERSION < FXVERSION(1,7,0) + getApp()->reg().writeIntEntry(key.text(),"track-list-posx",tracklist->getXPosition()); + getApp()->reg().writeIntEntry(key.text(),"track-list-posy",tracklist->getYPosition()); +#else + getApp()->reg().writeIntEntry(key.text(),"track-list-posx",tracklist->getContentX()); + getApp()->reg().writeIntEntry(key.text(),"track-list-posy",tracklist->getContentY()); +#endif + + getApp()->reg().writeBoolEntry(key.text(),"filter",filterframe->shown()); + + if (filterframe->shown() && !filterfield->getText().empty()) { + getApp()->reg().writeStringEntry(key.text(),"filter-text",filterfield->getText().text()); +#if FOXVERSION < FXVERSION(1,7,0) + getApp()->reg().writeUnsignedEntry(key.text(),"filter-mask",filtermask); +#else + getApp()->reg().writeUIntEntry(key.text(),"filter-mask",filtermask); +#endif + } + else{ + getApp()->reg().writeStringEntry(key.text(),"filter-text",""); +#if FOXVERSION < FXVERSION(1,7,0) + getApp()->reg().writeUnsignedEntry(key.text(),"filter-mask",FILTER_DEFAULT); +#else + getApp()->reg().writeUIntEntry(key.text(),"filter-mask",FILTER_DEFAULT); +#endif + } + + saveTrackSettings(key); + } + + +void GMTrackView::saveTrackSettings(const FXString & key) const { + tracklist->saveHeaders(); + FXString browseprefix = (browsersplit->getExpanded()==SHOWBROWSER) ? "browse" : "list"; + FXString name; + for (FXint i=0;ireg().writeBoolEntry(key.text(),FXString(browseprefix+"-showcolumn-"+name.lower()).text(),columns[i].show); + getApp()->reg().writeIntEntry(key.text(),FXString(browseprefix+"-columnwidth-"+name.lower()).text(),columns[i].size); + getApp()->reg().writeIntEntry(key.text(),FXString(browseprefix+"-columnindex-"+name.lower()).text(),columns[i].index); + } + getApp()->reg().writeIntEntry(key.text(),FXString(browseprefix+"-sort-column").text(),tracklist->getSortMethod()); + getApp()->reg().writeBoolEntry(key.text(),FXString(browseprefix+"-sort-reverse").text(),getSortReverse()); +#if FOXVERSION < FXVERSION(1,7,0) + getApp()->reg().writeUnsignedEntry(key.text(),FXString(browseprefix+"-sort-seed").text(),sort_seed); +#else + getApp()->reg().writeUIntEntry(key.text(),FXString(browseprefix+"-sort-seed").text(),sort_seed); +#endif + } + + + +long GMTrackView::onCmdShowColumn(FXObject*,FXSelector sel,void*){ + FXint no=FXSELID(sel)-ID_COLUMN_FIRST; + + for (FXint i=0;iclearHeaders(); + for (FXint i=0;iappendHeader(fxtr(columns[i].name.text()),columns[i].size,&columns[i]); + } + } + return 1; + } + +long GMTrackView::onUpdShowColumn(FXObject*sender,FXSelector sel,void*){ + if (columns.no()) { + FXint no=FXSELID(sel)-ID_COLUMN_FIRST; + for (FXint i=0;ihandle(this,FXSEL(SEL_COMMAND,ID_SETSTRINGVALUE),&column); + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_SHOW),NULL); + if (columns[i].show) + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_CHECK),NULL); + else + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_UNCHECK),NULL); + return 1; + } + } + } + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_HIDE),NULL); + return 1; + } + + +long GMTrackView::onCmdSort(FXObject*,FXSelector sel,void*){ + FXint no=FXSELID(sel)-ID_SORT_FIRST; + tracklist->setSortMethod(columns[no].type); + tracklist->setSortFunc(columns[no].ascending); + sortTracks(); + return 1; + } + + +long GMTrackView::onUpdSort(FXObject*sender,FXSelector sel,void*){ + if (columns.no()) { + FXMenuCommand * cmd = dynamic_cast(sender); + FXASSERT(cmd); + FXint no=FXSELID(sel)-ID_SORT_FIRST; + if (nosetText(GMStringFormat(fxtrformat("By %s"),fxtr(columns[no].name.text()))); + if (columns[no].type==source->getSortColumn(browsersplit->getExpanded()==SHOWBROWSER)) + cmd->setAccelText("Ctrl-N"); + else + cmd->setAccelText(FXString::null); + + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_SHOW),NULL); + if (tracklist->getSortMethod()==columns[no].type) + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_CHECK),NULL); + else + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_UNCHECK),NULL); + } + else { + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_HIDE),NULL); + } + } + else + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_HIDE),NULL); + return 1; + } + + +long GMTrackView::onUpdSortReverse(FXObject*sender,FXSelector,void*){ + for (FXint i=0;igetSortMethod()==columns[i].type) { + sender->handle(this,FXSEL(SEL_COMMAND,ID_ENABLE),NULL); + if (tracklist->getSortFunc()==columns[i].descending) + sender->handle(this,FXSEL(SEL_COMMAND,ID_CHECK),NULL); + else + sender->handle(this,FXSEL(SEL_COMMAND,ID_UNCHECK),NULL); + return 1; + } + } + sender->handle(this,FXSEL(SEL_COMMAND,ID_DISABLE),NULL); + return 1; + } + +long GMTrackView::onCmdSortDefault(FXObject*,FXSelector,void*){ + FXint def = source->getSortColumn(browsersplit->getExpanded()==SHOWBROWSER); + setSortMethod(def); + sortTracks(); + return 1; + } + + + +long GMTrackView::onCmdSortBrowse(FXObject*,FXSelector,void*){ + setSortMethod(HEADER_BROWSE); + sortTracks(); + return 1; + } + + +long GMTrackView::onUpdSortBrowse(FXObject*sender,FXSelector,void*){ + if ((browsersplit->getExpanded()==SHOWBROWSER) && source && source->getSortBrowse()) { + sender->handle(this,FXSEL(SEL_COMMAND,ID_SHOW),NULL); + if (tracklist->getSortMethod()==HEADER_BROWSE) + sender->handle(this,FXSEL(SEL_COMMAND,ID_CHECK),NULL); + else + sender->handle(this,FXSEL(SEL_COMMAND,ID_UNCHECK),NULL); + + FXMenuCommand * cmd = dynamic_cast(sender); + FXASSERT(cmd); + if (HEADER_BROWSE==source->getSortColumn(browsersplit->getExpanded()==SHOWBROWSER)) + cmd->setAccelText("Ctrl-N"); + else + cmd->setAccelText(FXString::null); + + } + else + sender->handle(this,FXSEL(SEL_COMMAND,ID_HIDE),NULL); + return 1; + } + + + +long GMTrackView::onCmdSortShuffle(FXObject*,FXSelector,void*){ + tracklist->setSortMethod(HEADER_SHUFFLE); +#if FOXVERSION < FXVERSION(1,7,0) + sort_seed = (FXuint)FXSystem::now(); +#else + sort_seed = (FXuint)FXThread::time(); +#endif + sortTracks(); + return 1; + } + + +long GMTrackView::onUpdSortShuffle(FXObject*sender,FXSelector,void*){ + if (tracklist->getSortMethod()==HEADER_SHUFFLE) + sender->handle(this,FXSEL(SEL_COMMAND,ID_CHECK),NULL); + else + sender->handle(this,FXSEL(SEL_COMMAND,ID_UNCHECK),NULL); + return 1; + } + + +long GMTrackView::onCmdSortGenreList(FXObject*,FXSelector,void*){ + if (genrelist->getSortFunc()==genre_list_sort) { + genrelist->setSortFunc(genre_list_sort_reverse); + genrelistheader->setArrowState(ARROW_UP); + } + else { + genrelist->setSortFunc(genre_list_sort); + genrelistheader->setArrowState(ARROW_DOWN); + } + sortGenres(); + return 1; + } + +long GMTrackView::onCmdSortArtistList(FXObject*,FXSelector,void*){ + if (artistlist->getSortFunc()==artist_list_sort) { + artistlist->setSortFunc(artist_list_sort_reverse); + reverse_artist=true; + artistlistheader->setArrowState(ARROW_UP); + } + else { + artistlist->setSortFunc(artist_list_sort); + reverse_artist=false; + artistlistheader->setArrowState(ARROW_DOWN); + } + + sortArtists(); + + if (tracklist->getSortMethod()==HEADER_BROWSE) + sortTracks(); + return 1; + } + + +long GMTrackView::onCmdSortAlbumList(FXObject*,FXSelector,void*){ + if (albumlist->getSortFunc()==album_list_sort) { + albumlist->setSortFunc(album_list_sort_reverse); + reverse_album=true; + albumlistheader->setArrowState(ARROW_UP); + } + else { + albumlist->setSortFunc(album_list_sort); + reverse_album=false; + albumlistheader->setArrowState(ARROW_DOWN); + } + + sortAlbums(); + + if (tracklist->getSortMethod()==HEADER_BROWSE) + sortTracks(); + + if (getApp()->hasChore(this,ID_LOAD_ALBUM_ICONS)) + getApp()->addChore(this,ID_LOAD_ALBUM_ICONS,(void*)(FXival)0); + + return 1; + } + + +long GMTrackView::onCmdGenreSelected(FXObject*,FXSelector sel,void*ptr){ + if ( FXSELTYPE(sel)==SEL_COMMAND ) { + if (genre_selectionchanged>=0) { + artistlist->clearItems(); + albumlist->clearItems(); + tracklist->clearItems(); + listArtists(); + listAlbums(); + listTracks(); + genre_selectionchanged=-1; + } + } + else { + + /* + Make sure "All Genre" is not selected at the + same time as the other items and vice-versa. + */ + + if ( (FXSELTYPE(sel)==SEL_SELECTED) && genrelist->getNumItems()>1 ){ + if (((FXint)(FXival)ptr)==0) { + genrelist->killSelection(); + genrelist->selectItem(0); + } + else if (genrelist->isItemSelected(0)){ + genrelist->deselectItem(0); + } + } + + if ((FXSELTYPE(sel)==SEL_DESELECTED) && genrelist->getNumItems()==1){ + genrelist->selectItem(0); + return 1; + } + + if (genre_selectionchanged==(FXint)(FXival)ptr) + genre_selectionchanged=-1; + else + genre_selectionchanged=(FXint)(FXival)ptr; + } + return 1; + } + + +long GMTrackView::onCmdArtistSelected(FXObject*,FXSelector sel,void*ptr){ + if ( FXSELTYPE(sel)==SEL_DOUBLECLICKED) { + GMPlayerManager::instance()->play(); + } + else if ( FXSELTYPE(sel)==SEL_COMMAND ) { + if (artist_selectionchanged>=0) { + albumlist->clearItems(); + tracklist->clearItems(); + listAlbums(); + listTracks(); + artist_selectionchanged=-1; + } + } + else { + + /* + Make sure "All Artist" is not selected at the + same time as the other items and vice-versa. + */ + + if ( (FXSELTYPE(sel)==SEL_SELECTED) && artistlist->getNumItems()>1 ){ + if (((FXint)(FXival)ptr)==0) { + artistlist->killSelection(); + artistlist->selectItem(0); + } + else if (artistlist->isItemSelected(0)){ + artistlist->deselectItem(0); + } + } + + if ((FXSELTYPE(sel)==SEL_DESELECTED) && artistlist->getNumItems()==1){ + artistlist->selectItem(0); + return 1; + } + + + if (artist_selectionchanged==(FXint)(FXival)ptr) + artist_selectionchanged=-1; + else + artist_selectionchanged=(FXint)(FXival)ptr; + } + return 1; + } + + +long GMTrackView::onCmdAlbumSelected(FXObject*,FXSelector sel,void*ptr){ + if ( FXSELTYPE(sel)==SEL_DOUBLECLICKED) { + GMPlayerManager::instance()->play(); + } + else if ( FXSELTYPE(sel)==SEL_COMMAND ) { + if (album_selectionchanged>=0) { + tracklist->clearItems(); + listTracks(); + album_selectionchanged=-1; + } + } + else { + + /* + Make sure "All Albums" is not selected at the + same time as the other items and vice-versa. + */ + if ( (FXSELTYPE(sel)==SEL_SELECTED) && albumlist->getNumItems()>1 ){ + if (((FXint)(FXival)ptr)==0) { + albumlist->killSelection(); + albumlist->selectItem(0); + } + else if (albumlist->isItemSelected(0)){ + albumlist->deselectItem(0); + } + } + + if (album_selectionchanged==(FXint)(FXival)ptr) + album_selectionchanged=-1; + else + album_selectionchanged=(FXint)(FXival)ptr; + } + return 1; + } + + +long GMTrackView::onGenreContextMenu(FXObject*,FXSelector,void*ptr){ + FXEvent * event = reinterpret_cast(ptr); + if (source && !event->moved) { + FXint item = genrelist->getItemAt(event->win_x,event->win_y); + if (item>=0 && getGenre(item)!=-1) { + GMMenuPane pane(this); + if (source->genre_context_menu(&pane)) { + selectGenreItem(item); + pane.create(); + ewmh_change_window_type(&pane,WINDOWTYPE_POPUP_MENU); + pane.popup(NULL,event->root_x+3,event->root_y+3); + getApp()->runPopup(&pane); + } + } + return 1; + } + return 0; + } + +long GMTrackView::onArtistContextMenu(FXObject*,FXSelector,void*ptr){ + FXEvent * event = reinterpret_cast(ptr); + if (source && !event->moved) { + FXint item = artistlist->getItemAt(event->win_x,event->win_y); + if (item>=0 && getArtist(item)!=-1) { + GMMenuPane pane(this); + if (source->artist_context_menu(&pane)) { + selectArtistItem(item); + selectAlbumItem(0); + pane.create(); + ewmh_change_window_type(&pane,WINDOWTYPE_POPUP_MENU); + pane.popup(NULL,event->root_x+3,event->root_y+3); + getApp()->runPopup(&pane); + } + } + return 1; + } + return 0; + } + + + +long GMTrackView::onAlbumContextMenu(FXObject*,FXSelector,void*ptr){ + FXEvent * event = reinterpret_cast(ptr); + FXbool old = album_by_year; + FXDataTarget target_yearsort(album_by_year); + if (source && !event->moved) { + GMMenuPane pane(this); + FXint item = albumlist->getItemAt(event->win_x,event->win_y); + if (item>=0 && getAlbum(item)!=-1 && source->album_context_menu(&pane)) + selectAlbumItem(item); + new GMMenuCheck(&pane,fxtr("Sort by Album Year"),&target_yearsort,FXDataTarget::ID_VALUE); + pane.create(); + pane.forceRefresh(); + ewmh_change_window_type(&pane,WINDOWTYPE_POPUP_MENU); + pane.popup(NULL,event->root_x+3,event->root_y+3); + getApp()->runPopup(&pane); + + if (old!=album_by_year){ + sortAlbums(); + sortTracks(); + } + return 1; + } + return 0; + } + +long GMTrackView::onAlbumHeaderContextMenu(FXObject*,FXSelector,void*ptr){ + FXEvent * event = reinterpret_cast(ptr); + FXbool old = album_by_year; + FXDataTarget target_yearsort(album_by_year); + + if (source && !event->moved) { + GMMenuPane pane(this); + new GMMenuCheck(&pane,fxtr("Sort by Album Year"),&target_yearsort,FXDataTarget::ID_VALUE); + pane.create(); + pane.forceRefresh(); + ewmh_change_window_type(&pane,WINDOWTYPE_POPUP_MENU); + pane.popup(NULL,event->root_x+3,event->root_y+3); + getApp()->runPopup(&pane); + + if (old!=album_by_year){ + sortAlbums(); + sortTracks(); + } + return 1; + } + return 0; + } + + +long GMTrackView::onTrackContextMenu(FXObject*,FXSelector,void*ptr){ + FXEvent * event = reinterpret_cast(ptr); + if (source && !event->moved) { + GMMenuPane pane(this); + FXint item = tracklist->getItemAt(event->win_x,event->win_y); + if (item>=0 && !tracklist->isItemSelected(item)) + selectTrackItem(item); + + if (item>=0 && source->track_context_menu(&pane)) + new FXMenuSeparator(&pane); + + new GMMenuCascade(&pane,tr("&Columns\t\tChange Visible Columns."),NULL,columnmenu); + new GMMenuCascade(&pane,tr("&Sort\t\tChange Sorting."),GMIconTheme::instance()->icon_sort,sortmenu); + + pane.create(); + ewmh_change_window_type(columnmenu,WINDOWTYPE_DROPDOWN_MENU); + ewmh_change_window_type(sortmenu,WINDOWTYPE_DROPDOWN_MENU); + ewmh_change_window_type(&pane,WINDOWTYPE_POPUP_MENU); + pane.popup(NULL,event->root_x+3,event->root_y+3); + getApp()->runPopup(&pane); + return 1; + } + return 0; + } + + +long GMTrackView::onTrackHeaderContextMenu(FXObject*,FXSelector,void*ptr){ + FXEvent * event = reinterpret_cast(ptr); + if (source && !event->moved) { + columnmenu->create(); + ewmh_change_window_type(columnmenu,WINDOWTYPE_POPUP_MENU); + columnmenu->popup(NULL,event->root_x+3,event->root_y+3); + getApp()->runPopup(columnmenu); + return 1; + } + return 0; + } + + +long GMTrackView::onCmdGenreKeyPress(FXObject*,FXSelector,void*ptr){ + FXEvent* event=reinterpret_cast(ptr); + if (event->state&(CONTROLMASK) && (event->code==KEY_A || event->code==KEY_a)) { + if (genrelist->getNumItems()) { + selectGenreItem(0); + genrelist->makeItemVisible(0); + } + return 1; + } + else if (event->code==KEY_Delete || event->code==KEY_KP_Delete) { + if (source && genrelist->getNumItems()) { + source->handle(this,FXSEL(SEL_COMMAND,GMSource::ID_DELETE_GENRE),NULL); + } + return 1; + } +// else if (event->code==KEY_F2) { +// if (source && genrelist->getNumItems()) { +// source->handle(this,FXSEL(SEL_COMMAND,GMSource::ID_EDIT_GENRE),NULL); +// } +// return 1; +// } + return 0; + } + +long GMTrackView::onCmdArtistKeyPress(FXObject*,FXSelector,void*ptr){ + FXEvent* event=reinterpret_cast(ptr); + if (event->state&(CONTROLMASK) && (event->code==KEY_A || event->code==KEY_a)) { + if (artistlist->getNumItems()) { + selectArtistItem(0); + artistlist->makeItemVisible(0); + } + return 1; + } + else if (event->code==KEY_Delete || event->code==KEY_KP_Delete) { + if (source && artistlist->getNumItems()) { + source->handle(this,FXSEL(SEL_COMMAND,GMSource::ID_DELETE_ARTIST),NULL); + } + return 1; + } +// else if (event->code==KEY_F2) { +// if (source && artistlist->getNumItems()) { +// source->handle(this,FXSEL(SEL_COMMAND,GMSource::ID_EDIT_ARTIST),NULL); +// } +// return 1; +// } + else if (!(event->state&CONTROLMASK)){ + FXString text; + FXint a; + for (FXint i=0;igetNumItems();i++){ + text=artistlist->getItemText(i); + if (begins_with_keyword(text)) + a=FXMIN(text.length()-1,text.find(' ')+1); + else + a=0; + if (comparecase(&text[a],event->text,1)==0){ + selectArtistItem(i); + artistlist->makeItemVisible(i); + break; + } + } + } + return 0; + } + + +long GMTrackView::onCmdAlbumKeyPress(FXObject*,FXSelector,void*ptr){ + FXEvent* event=reinterpret_cast(ptr); + if (event->state&(CONTROLMASK) && (event->code==KEY_A || event->code==KEY_a)) { + if (albumlist->getNumItems()) { + selectAlbumItem(0); + albumlist->makeItemVisible(0); + } + return 1; + } + else if (event->code==KEY_Delete || event->code==KEY_KP_Delete) { + if (source && albumlist->getNumItems()) { + source->handle(this,FXSEL(SEL_COMMAND,GMSource::ID_DELETE_ALBUM),NULL); + } + return 1; + } +// else if (event->code==KEY_F2) { +// if (source && albumlist->getNumItems()) { +// source->handle(this,FXSEL(SEL_COMMAND,GMSource::ID_EDIT_ALBUM),NULL); +// } +// return 1; +// } + return 0; + } + + +long GMTrackView::onCmdTrackKeyPress(FXObject*,FXSelector,void*ptr){ + FXEvent* event=reinterpret_cast(ptr); + if (event->state&(CONTROLMASK) ) { + if (event->code==KEY_A || event->code==KEY_a) { + if (tracklist->getNumItems()){ + tracklist->setAnchorItem(0); + tracklist->selectItem(0); + tracklist->extendSelection(tracklist->getNumItems()-1); + } + return 1; + } + else if (event->code==KEY_C || event->code==KEY_c) { + if (source && numTrackSelected() ) { + source->handle(this,FXSEL(SEL_COMMAND,GMSource::ID_COPY_TRACK),NULL); + } + return 1; + } + } + else if (event->code==KEY_Delete || event->code==KEY_KP_Delete) { + if (source && numTrackSelected() ) { + source->handle(this,FXSEL(SEL_COMMAND,GMSource::ID_DELETE_TRACK),NULL); + } + return 1; + } + else if (event->code==KEY_F2) { + if (source && numTrackSelected()) { + source->handle(this,FXSEL(SEL_COMMAND,GMSource::ID_EDIT_TRACK),NULL); + } + return 1; + } + return 0; + } + + +long GMTrackView::onCmdPlayTrack(FXObject*,FXSelector,void*){ + GMPlayerManager::instance()->play(); + tracklist->deselectItem(tracklist->getCurrentItem()); + return 1; + } + +long GMTrackView::onCmdShowCurrent(FXObject*,FXSelector,void*){ + if (!source->findCurrent(tracklist,GMPlayerManager::instance()->getSource())){ + if (source->hasCurrentTrack(GMPlayerManager::instance()->getSource())){ + refresh(); + } + else { + GMPlayerManager::instance()->getSourceView()->setSource(GMPlayerManager::instance()->getSource()); + if (!source->findCurrent(tracklist,GMPlayerManager::instance()->getSource())){ + if (source->hasCurrentTrack(GMPlayerManager::instance()->getSource())){ + refresh(); + } + } + } + } + return 1; + } + +long GMTrackView::onUpdShowCurrent(FXObject*sender,FXSelector,void*){ + if (GMPlayerManager::instance()->playing() && GMPlayerManager::instance()->getSource()) + sender->handle(this,FXSEL(SEL_COMMAND,ID_ENABLE),NULL); + else + sender->handle(this,FXSEL(SEL_COMMAND,ID_DISABLE),NULL); + return 1; + } + + +long GMTrackView::onCmdPaste(FXObject*sender,FXSelector,void*ptr){ + return source && source->handle(sender,FXSEL(SEL_COMMAND,GMSource::ID_PASTE),ptr); + } + +long GMTrackView::onUpdPaste(FXObject*sender,FXSelector,void*ptr){ + return source && source->handle(sender,FXSEL(SEL_UPDATE,GMSource::ID_PASTE),ptr); + } + + +long GMTrackView::onCmdCopy(FXObject*sender,FXSelector,void*ptr){ + return source && source->handle(sender,FXSEL(SEL_COMMAND,GMSource::ID_COPY_TRACK),ptr); + } + +long GMTrackView::onUpdCopy(FXObject*sender,FXSelector,void*){ + if (trackSelected() && source) + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_ENABLE),NULL); + else + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_DISABLE),NULL); + return 1; + } + + +long GMTrackView::onCmdFilter(FXObject*,FXSelector sel,void*){ + if (FXSELID(sel)==ID_FILTER_MODE && filterfield->getText().empty()) + return 1; + if (FXSELTYPE(sel)==SEL_CHANGED) { + getApp()->addTimeout(this,ID_FILTER,TIME_MSEC(500)); + return 1; + } + if (FXSELTYPE(sel)==SEL_COMMAND){ + getApp()->removeTimeout(this,ID_FILTER); + } + if (source->setFilter(filterfield->getText().trim().simplify(),filtermask)) + refresh(); + return 1; + } + +long GMTrackView::onCmdFilterMask(FXObject*,FXSelector sel,void*){ + switch(FXSELID(sel)){ + case ID_FILTER_TRACK : filtermask ^= FILTER_TRACK; break; + case ID_FILTER_ALBUM : filtermask ^= FILTER_ALBUM; break; + case ID_FILTER_ARTIST : filtermask ^= FILTER_ARTIST; break; + case ID_FILTER_GENRE : filtermask ^= FILTER_GENRE; break; + } + getApp()->addTimeout(this,ID_FILTER,TIME_MSEC(500)); + return 1; + } + + +long GMTrackView::onUpdFilterMask(FXObject*sender,FXSelector sel,void*){ + FXbool check=false; + switch(FXSELID(sel)){ + case ID_FILTER_TRACK: check = (filtermask&FILTER_TRACK)!=0; break; + case ID_FILTER_ALBUM: check = (filtermask&FILTER_ALBUM)!=0; break; + case ID_FILTER_ARTIST: check = (filtermask&FILTER_ARTIST)!=0; break; + case ID_FILTER_GENRE: check = (filtermask&FILTER_GENRE)!=0; break; + } + if (check) + sender->handle(this,FXSEL(SEL_COMMAND,ID_CHECK),NULL); + else + sender->handle(this,FXSEL(SEL_COMMAND,ID_UNCHECK),NULL); + return 1; + } + + + +long GMTrackView::onCmdBeginDrag(FXObject*sender,FXSelector sel,void*){ + FXWindow * window = (FXWindow*)sender; + if (FXSELID(sel)==ID_ALBUM_LIST) { + + /* we don't seem to get a SEL_COMMAND message when we start dragging */ + if (album_selectionchanged>=0) { + tracklist->clearItems(); + listTracks(); + album_selectionchanged=-1; + } + + FXDragType types[]={GMClipboard::kdeclipboard,GMClipboard::urilistType,GMClipboard::alltracks}; + if (window->beginDrag(types,3)){ + } + } + else if (FXSELID(sel)==ID_TRACK_LIST){ + FXDragType types[]={GMClipboard::kdeclipboard,GMClipboard::urilistType,GMClipboard::selectedtracks}; + if (window->beginDrag(types,3)){ + } + } + else if (FXSELID(sel)==ID_ARTIST_LIST){ + + /* we don't seem to get a SEL_COMMAND message when we start dragging */ + if (artist_selectionchanged>=0) { + albumlist->clearItems(); + tracklist->clearItems(); + listAlbums(); + listTracks(); + artist_selectionchanged=-1; + } + FXDragType types[]={GMClipboard::kdeclipboard,GMClipboard::urilistType,GMClipboard::alltracks}; + if (window->beginDrag(types,3)){ + } + } + else { + return 0; + } + return 1; + } + +long GMTrackView::onCmdDragged(FXObject*sender,FXSelector,void*ptr){ + FXWindow * window = (FXWindow*)sender; + FXEvent* event=(FXEvent*)ptr; + FXDragAction action=DRAG_COPY; + if(event->state&ALTMASK) action=DRAG_LINK; + window->handleDrag(event->root_x,event->root_y,action); + action=window->didAccept(); + if (window->didAccept()!=DRAG_REJECT) { + if (action==DRAG_MOVE) + window->setDragCursor(getApp()->getDefaultCursor(DEF_DNDMOVE_CURSOR)); + else if (action==DRAG_LINK) + window->setDragCursor(getApp()->getDefaultCursor(DEF_DNDLINK_CURSOR)); + else + window->setDragCursor(getApp()->getDefaultCursor(DEF_DNDCOPY_CURSOR)); + } + else { + window->setDragCursor(getApp()->getDefaultCursor(DEF_DNDSTOP_CURSOR)); + } + return 1; + } + +long GMTrackView::onCmdEndDrag(FXObject*sender,FXSelector,void*){ + FXWindow * window = (FXWindow*)sender; + if (getApp()->getDragWindow()==tracklist) + window->endDrag(window->didAccept()!=DRAG_REJECT); + else + window->endDrag((window->didAccept()!=DRAG_MOVE && window->didAccept()!=DRAG_REJECT)); + window->setDragCursor(window->getDefaultCursor()); + return 1; + } + + +long GMTrackView::onDndTrackEnter(FXObject*,FXSelector,void*){ + tracklist_dragging=1; + tracklist_lastline=-1; + return 0; + } + + +long GMTrackView::onDndTrackLeave(FXObject*,FXSelector,void*){ + tracklist_dragging=0; + if (tracklist_lastline>=0) { + tracklist->update(0,tracklist_lastline,tracklist->getHeader()->getDefaultWidth(),1); + } + tracklist_lastline=-1; + return 0; + } + + +long GMTrackView::onDndTrackMotion(FXObject*,FXSelector,void*ptr){ + FXEvent* event=(FXEvent*)ptr; + if (getApp()->getDragWindow()==tracklist) { + if (tracklist->offeredDNDType(FROM_DRAGNDROP,GMClipboard::selectedtracks)){ + FXint pos_x,pos_y,item; + tracklist->getPosition(pos_x,pos_y); + + item = (event->win_y-pos_y-tracklist->getHeader()->getDefaultHeight())/tracklist->getLineHeight(); + + if (item<0) item=0; + if (item>=tracklist->getNumItems()) { + tracklist->acceptDrop(DRAG_REJECT); + return 1; + } + + FXint ds = pos_y+item*tracklist->getLineHeight()+tracklist->getHeader()->getDefaultHeight(); + FXint dm = ds + (tracklist->getLineHeight()/2); + FXint dl=0; + + tracklist_dropitem=item; + + if (event->win_y<=dm) { + dl = ds; + } + else { + dl = ds+tracklist->getLineHeight(); + tracklist_dropitem=FXMIN(item+1,tracklist->getNumItems()); + } + + FXDCWindow dc(tracklist); + dc.setForeground(FXRGB(0,0,0)); + dc.fillRectangle(0,dl,tracklist->getHeader()->getDefaultWidth(),1); + + if (tracklist_lastline>=0 && (tracklist_lastline!=dl || tracklist_lastposy!=pos_y)) { + tracklist->update(0,tracklist_lastline-(tracklist_lastposy-pos_y),tracklist->getHeader()->getDefaultWidth(),1); + } + + tracklist_lastline=dl; + tracklist_lastposy=pos_y; + + tracklist->acceptDrop(DRAG_MOVE); + return 1; + } + } + else if (getApp()->getDragWindow()==NULL) { + FXDragType * types; + FXuint ntypes; + if (tracklist->inquireDNDTypes(FROM_DRAGNDROP,types,ntypes)){ + if (source->dnd_source_accepts(types,ntypes)){ + tracklist->acceptDrop(DRAG_LINK); + freeElms(types); + return 1; + } + } + freeElms(types); + } + tracklist->acceptDrop(DRAG_REJECT); + return 1; + } + + + + +long GMTrackView::onDndTrackDrop(FXObject*sender,FXSelector,void*ptr){ + if (getApp()->getDragWindow()==tracklist) { + + if (!tracklist->offeredDNDType(FROM_DRAGNDROP,GMClipboard::selectedtracks)) return 1; + + if (tracklist_lastline>=0) { + tracklist->update(0,tracklist_lastline,tracklist->getHeader()->getDefaultWidth(),1); + } + + tracklist_dragging=1; + tracklist_lastline=-1; + + FXint i,tgt; + FXbool srcmoved=false; + for (i=tracklist->getNumItems()-1;i>=0;i--){ + if (tracklist->isItemSelected(i) && (i+1)!=tracklist_dropitem) { + if (imoveTrack(tracklist,i,tgt); + tracklist->moveItem(tgt,i); + tracklist->deselectItem(tgt); + if (iorderChanged(tracklist); + } + } + if (srcmoved==false) tracklist->markUnsorted(); + tracklist->dropFinished(DRAG_ACCEPT); + + if (GMPlayerManager::instance()->playing() && GMPlayerManager::instance()->getSource()) + source->findCurrent(tracklist,GMPlayerManager::instance()->getSource()); + + return 1; + } + else if (source && getApp()->getDragWindow()==NULL) { + return source->handle(sender,FXSEL(SEL_DND_DROP,GMSource::ID_DROP),ptr); + } + return 0; + } + + +long GMTrackView::onDndMotion(FXObject*,FXSelector,void*){ + if (getApp()->getDragWindow()==NULL && source) { + FXDragType * types; + FXuint ntypes; + if (tracklist->inquireDNDTypes(FROM_DRAGNDROP,types,ntypes)){ + if (source->dnd_source_accepts(types,ntypes)){ + tracklist->acceptDrop(DRAG_ACCEPT); + freeElms(types); + return 1; + } + } + freeElms(types); + } + return 0; + } + +long GMTrackView::onDndDrop(FXObject*sender,FXSelector,void*ptr){ + if (getApp()->getDragWindow()==NULL && source) { + return source->handle(sender,FXSEL(SEL_DND_DROP,GMSource::ID_DROP),ptr); + } + return 0; + } + +long GMTrackView::onDndRequest(FXObject*sender,FXSelector sel,void*ptr){ + if (source) { + switch(FXSELID(sel)){ + case ID_TRACK_LIST : return source->handle(sender,FXSEL(SEL_DND_REQUEST,GMSource::ID_COPY_TRACK),ptr); break; + case ID_ALBUM_LIST : return source->handle(sender,FXSEL(SEL_DND_REQUEST,GMSource::ID_COPY_ALBUM),ptr); break; + case ID_ARTIST_LIST: return source->handle(sender,FXSEL(SEL_DND_REQUEST,GMSource::ID_COPY_ARTIST),ptr); break; + default : break; + } + } + return 0; + } + + +long GMTrackView::onCmdToggleBrowser(FXObject*,FXSelector,void*){ + + if (source) + saveTrackSettings(source->settingKey()); + + if (browsersplit->getExpanded()==SHOWBROWSER) + browsersplit->setExpanded(HIDEBROWSER); + else + browsersplit->setExpanded(SHOWBROWSER); + + + if (source) + loadTrackSettings(source->settingKey()); + + refresh(); + return 1; + } + +long GMTrackView::onUpdToggleBrowser(FXObject*sender,FXSelector,void*){ + if (source && source->canBrowse()) { + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_ENABLE),NULL); + if (browsersplit->getExpanded()==SHOWBROWSER) + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_CHECK),NULL); + else + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_UNCHECK),NULL); + } + else { + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_UNCHECK),NULL); + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_DISABLE),NULL); + } + return 1; + } + + + +long GMTrackView::onCmdToggleGenres(FXObject*,FXSelector,void*){ + if (genrelistframe->shown()) { + genrelistframe->hide(); + genrelist->killSelection(false); + genrelist->selectItem(0,true); + genrelistframe->recalc(); + browsersplit->setHSplit(5000); + } + else { + genrelistframe->show(); + genrelistframe->recalc(); + genresplit->setHSplit(5000); + browsersplit->setHSplit(6666); + } + return 1; + } + +long GMTrackView::onUpdToggleGenres(FXObject*sender,FXSelector,void*){ + if (source && source->canBrowse() && browsersplit->getExpanded()==SHOWBROWSER) { + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_ENABLE),NULL); + if (genrelistframe->shown()) + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_CHECK),NULL); + else + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_UNCHECK),NULL); + } + else { + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_UNCHECK),NULL); + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_DISABLE),NULL); + } + return 1; + } + +long GMTrackView::onCmdToggleFilter(FXObject*,FXSelector sel,void*){ + if (filterframe->shown() && FXSELID(sel)==ID_CLOSE_FILTER) { + filterframe->hide(); + if (!filterfield->getText().empty()){ + source->setFilter(FXString::null,filtermask); + refresh(); + } + recalc(); + } + else { + filterframe->show(); + filterfield->setFocus(); + if (!filterfield->getText().empty()) + filterfield->setSelection(0,filterfield->getText().length()); + recalc(); + } + return 1; + } + +long GMTrackView::onUpdToggleFilter(FXObject*sender,FXSelector,void*){ + if (source && source->canFilter()) + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_ENABLE),NULL); + else + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_DISABLE),NULL); + return 1; + } + + +long GMTrackView::onCmdConfigureColumns(FXObject*,FXSelector,void*){ + GMColumnDialog dialog(getShell(),columns); + if (dialog.execute()) { + dialog.saveIndex(); + + tracklist->clearHeaders(); + for (FXint i=0;iappendHeader(fxtr(columns[i].name.text()),columns[i].size,&columns[i]); + } + } + + + } + return 1; + } + + +long GMTrackView::onCmdLoadAlbumIcons(FXObject*,FXSelector,void*ptr){ + FXint item=(FXint)(FXival)ptr; + if (item>=albumlist->getNumItems()) return 1; + while(itemgetNumItems()){ + if (albumlist->getItemIcon(item)!=GMIconTheme::instance()->icon_nocover) { + item++; + continue; + } + FXint album=(FXint)(FXival)albumlist->getItemData(item); + if (album==-1){ + item++; + continue; + } + FXIcon * icon = source->getAlbumIcon(album,false); + if (icon) albumlist->setItemIcon(item,icon); + break; + } + item++; + if (item>=albumlist->getNumItems()) return 1; + getApp()->addChore(this,ID_LOAD_ALBUM_ICONS,(void*)(FXival)item); + return 1; + } diff --git a/src/GMTrackView.h b/src/GMTrackView.h new file mode 100644 index 0000000..41b4397 --- /dev/null +++ b/src/GMTrackView.h @@ -0,0 +1,294 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#ifndef GMTRACKVIEW_H +#define GMTRACKVIEW_H + +class GMList; +class GMTrackList; +class GMSource; +class GMHeaderButton; + +class GMTrackView : public FXPacker { +FXDECLARE(GMTrackView) +protected: + FX4Splitter * browsersplit; + FX4Splitter * genresplit; + FXVerticalFrame * genrelistframe; + FXHorizontalFrame * filterframe; + FXTextField * filterfield; + FXListBox * filterlist; + GMTrackList * tracklist; + GMList * genrelist; + GMList * artistlist; + GMList * albumlist; + GMHeaderButton * genrelistheader; + GMHeaderButton * artistlistheader; + GMHeaderButton * albumlistheader; + FXMenuPtr columnmenu; + FXMenuPtr sortmenu; + FXMenuPtr filtermenu; +protected: + FXFontPtr font_listhead; +protected: + GMColumnList columns; + GMSource * source; + FXuint sort_seed; + FXuint shuffle_seed; + FXuint filtermask; +public: + static FXbool reverse_artist; + static FXbool reverse_album; + static FXbool album_by_year; +protected: + FXint tracklist_dragging; + FXint tracklist_lastline; + FXint tracklist_lastposy; + FXint tracklist_dropitem; + FXint tracklist_posx; + FXint tracklist_posy; +protected: + GMTrackView(); + void initSelection(GMList * list,const FXchar *,const FXString & section="window"); + void saveSelection(GMList * list,const FXchar *,const FXString & section="window") const; +private: + GMTrackView(const GMTrackView&); + GMTrackView& operator=(const GMTrackView&); +public: + enum { + ID_ARTIST_LIST_HEADER = FX4Splitter::ID_LAST, + ID_ALBUM_LIST_HEADER, + ID_GENRE_LIST_HEADER, + ID_ARTIST_LIST, + ID_ALBUM_LIST, + ID_GENRE_LIST, + ID_TRACK_LIST, + ID_TRACK_LIST_HEADER, + ID_FILTER, + ID_FILTER_MODE, + ID_TOGGLE_BROWSER, + ID_TOGGLE_GENRES, + ID_TOGGLE_FILTER, + ID_CLOSE_FILTER, + ID_COPY, + ID_CUT, + ID_PASTE, + ID_SHOW_CURRENT, + ID_COLUMN_FIRST, + ID_COLUMN_LAST=ID_COLUMN_FIRST+10, + ID_SORT_FIRST, + ID_SORT_LAST=ID_SORT_FIRST+10, + ID_SORT_SHUFFLE, + ID_SORT_BROWSE, + ID_SORT_REVERSE, + ID_SORT_DEFAULT, + ID_CONFIGURE_COLUMNS, + ID_LOAD_ALBUM_ICONS, + ID_FILTER_TRACK, + ID_FILTER_ALBUM, + ID_FILTER_ARTIST, + ID_FILTER_GENRE, + ID_FILTER_LAST = ID_FILTER_GENRE, + ID_LAST, + }; +public: + long onCmdSortGenreList(FXObject*,FXSelector,void*); + long onCmdSortArtistList(FXObject*,FXSelector,void*); + long onCmdSortAlbumList(FXObject*,FXSelector,void*); + + long onCmdGenreSelected(FXObject*,FXSelector,void*); + long onCmdArtistSelected(FXObject*,FXSelector,void*); + long onCmdAlbumSelected(FXObject*,FXSelector,void*); + + long onGenreContextMenu(FXObject*,FXSelector,void*); + long onArtistContextMenu(FXObject*,FXSelector,void*); + long onAlbumContextMenu(FXObject*,FXSelector,void*); + long onAlbumHeaderContextMenu(FXObject*,FXSelector,void*); + long onTrackContextMenu(FXObject*,FXSelector,void*); + long onTrackHeaderContextMenu(FXObject*,FXSelector,void*); + + long onCmdGenreKeyPress(FXObject*,FXSelector,void*); + long onCmdArtistKeyPress(FXObject*,FXSelector,void*); + long onCmdAlbumKeyPress(FXObject*,FXSelector,void*); + long onCmdTrackKeyPress(FXObject*,FXSelector,void*); + + long onCmdFilter(FXObject*,FXSelector,void*); + + long onCmdFilterMask(FXObject*,FXSelector,void*); + long onUpdFilterMask(FXObject*,FXSelector,void*); + + + long onCmdBeginDrag(FXObject*,FXSelector,void*); + long onCmdDragged(FXObject*,FXSelector,void*); + long onCmdEndDrag(FXObject*,FXSelector,void*); + + long onDndTrackEnter(FXObject*,FXSelector,void*); + long onDndTrackLeave(FXObject*,FXSelector,void*); + long onDndTrackMotion(FXObject*,FXSelector,void*); + long onDndTrackDrop(FXObject*,FXSelector,void*); + + long onDndMotion(FXObject*,FXSelector,void*); + long onDndDrop(FXObject*,FXSelector,void*); + long onDndRequest(FXObject*,FXSelector,void*); + + long onCmdPlayTrack(FXObject*,FXSelector,void*); + + long onCmdPaste(FXObject*,FXSelector,void*); + long onUpdPaste(FXObject*,FXSelector,void*); + + long onCmdCopy(FXObject*,FXSelector,void*); + long onUpdCopy(FXObject*,FXSelector,void*); + + long onCmdToggleBrowser(FXObject*,FXSelector,void*); + long onUpdToggleBrowser(FXObject*,FXSelector,void*); + + long onCmdToggleGenres(FXObject*,FXSelector,void*); + long onUpdToggleGenres(FXObject*,FXSelector,void*); + + long onCmdToggleFilter(FXObject*,FXSelector,void*); + long onUpdToggleFilter(FXObject*,FXSelector,void*); + + long onCmdShowCurrent(FXObject*,FXSelector,void*); + long onUpdShowCurrent(FXObject*,FXSelector,void*); + + long onCmdShowColumn(FXObject*,FXSelector,void*); + long onUpdShowColumn(FXObject*,FXSelector,void*); + + long onCmdSortDefault(FXObject*,FXSelector,void*); + long onCmdSort(FXObject*,FXSelector,void*); + long onUpdSort(FXObject*,FXSelector,void*); + long onUpdSortReverse(FXObject*,FXSelector,void*); + long onCmdSortBrowse(FXObject*,FXSelector,void*); + long onUpdSortBrowse(FXObject*,FXSelector,void*); + long onCmdSortShuffle(FXObject*,FXSelector,void*); + long onUpdSortShuffle(FXObject*,FXSelector,void*); + + long onCmdConfigureColumns(FXObject*,FXSelector,void*); + long onCmdLoadAlbumIcons(FXObject*,FXSelector,void*); + +public: + GMTrackView(FXComposite* p); + + void updateFont(); + + void updateColors(); + + void updateIcons(); + + FXMenuPane * getColumnMenu() const { return columnmenu; } + + FXMenuPane * getSortMenu() const { return sortmenu; } + + FXint numTrackSelected() const; + + FXbool trackSelected() const; + + FXbool hasTracks() const; + + void getSelectedGenres(FXIntList & genre) const; + + void getSelectedArtists(FXIntList & artists) const; + + void getSelectedAlbums(FXIntList & albums) const; + + void getSelectedTracks(FXIntList & tracks) const; + + void getTracks(FXIntList & tracks) const; + + GMTrackItem * getTrackItem(FXint i) const { return (GMTrackItem*)tracklist->getItem(i); } + + FXbool isTrackItemSelected(FXint i) const { return tracklist->isItemSelected(i); } + + FXint getNumTracks() const { return tracklist->getNumItems() ; } + + void selectGenreItem(FXint item); + + void selectAlbumItem(FXint item); + + void selectArtistItem(FXint item); + + void selectTrackItem(FXint item); + + inline FXint getGenre(FXint index) const { return (FXint)(FXival)genrelist->getItemData(index); } + + inline FXint getArtist(FXint index) const { return (FXint)(FXival)artistlist->getItemData(index); } + + inline FXint getAlbum(FXint index) const { return (FXint)(FXival)albumlist->getItemData(index); } + + inline FXint getTrack(FXint index) const { return (FXint)(FXival)tracklist->getItemId(index); } + + FXbool listGenres(); + + FXbool listArtists(); + + FXbool listAlbums(); + + FXbool listTracks(); + + FXint getCurrent() const; + + FXint getNext(FXbool wrap=false); + + FXint getPrevious(); + + void setSource(GMSource * src); + + GMSource * getSource() const { return source; } + + FXString getTrackFilename(FXint i) const; + + void sortGenres() const; + + void sortArtists() const; + + void sortAlbums() const; + + void sortTracks() const; + + void resort(); + + void init(GMSource * src); + + void refresh(); + + void clear(); + + void mark(FXint item,FXbool show=true); + + void showCurrent(); + + void setSortMethod(FXint hdr,FXbool reverse=false); + + FXbool getSortReverse() const; + + void loadSettings(const FXString & key); + + void saveSettings(const FXString & key) const; + + void loadTrackSettings(const FXString & key); + + void saveTrackSettings(const FXString & key) const; + + void saveView() const; + + virtual ~GMTrackView(); + }; + + + +#endif diff --git a/src/GMTrayIcon.cpp b/src/GMTrayIcon.cpp new file mode 100644 index 0000000..a2d54c9 --- /dev/null +++ b/src/GMTrayIcon.cpp @@ -0,0 +1,355 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#include "gmdefs.h" +#include "xincs.h" + +#include "icons.h" +#include "FXPNGIcon.h" + +#include "GMRemote.h" +#include "GMList.h" +#include "GMSource.h" +#include "GMPlayerManager.h" +#include "GMWindow.h" + +#include "GMApp.h" +#include "GMTrayIcon.h" +#include "GMIconTheme.h" + +enum { + SYSTEM_TRAY_REQUEST_DOCK =0, + SYSTEM_TRAY_BEGIN_MESSAGE =1, + SYSTEM_TRAY_CANCEL_MESSAGE=2, + }; + +enum { + SYSTEM_TRAY_HORIZONTAL = 0, + SYSTEM_TRAY_VERTICAL = 1, + SYSTEM_TRAY_UNKNOWN = 2 + }; + + +FXDEFMAP(GMTrayIcon) GMTrayIconMap[]={ + FXMAPFUNC(SEL_PAINT,0,GMTrayIcon::onPaint), + FXMAPFUNC(SEL_CONFIGURE,0,GMTrayIcon::onConfigure), + FXMAPFUNC(SEL_LEFTBUTTONPRESS,0,GMTrayIcon::onLeftBtnPress), + FXMAPFUNC(SEL_MIDDLEBUTTONPRESS,0,GMTrayIcon::onMiddleBtnPress), + FXMAPFUNC(SEL_RIGHTBUTTONRELEASE,0,GMTrayIcon::onRightBtnRelease), + FXMAPFUNC(SEL_MOUSEWHEEL,0,GMTrayIcon::onMouseWheel), + FXMAPFUNC(SEL_QUERY_TIP,0,GMTrayIcon::onQueryTip) + }; + +FXIMPLEMENT(GMTrayIcon,GMPlug,GMTrayIconMap,ARRAYNUMBER(GMTrayIconMap)); + +GMTrayIcon::GMTrayIcon(){ + icon=NULL; + flags|=FLAG_ENABLED; + } + +GMTrayIcon::GMTrayIcon(FXApp * app) : GMPlug(app), xtraywindow(0), xtrayopcode(0),icon(NULL), opaque(false) { + flags|=FLAG_ENABLED; + } + +GMTrayIcon::~GMTrayIcon(){ + if (icon) delete icon; + } + +void GMTrayIcon::display(const GMTrack &track){ + setToolTip(GMStringFormat("%s\n%s\n%s (%d)",track.title.text(),track.artist.text(),track.album.text(),track.year)); + } + +void GMTrayIcon::reset() { + setToolTip(FXString::null); + } + +void GMTrayIcon::updateIcon() { + if (icon) { + FXint size = icon->getWidth(); + + /// Delete the old + delete icon; + icon=NULL; + + /// Update + if (size<=16) { + icon = new FXPNGIcon(getApp(),gogglesmm_16_png,0,opaque ? IMAGE_OPAQUE : 0); + icon->setVisual(getVisual()); + if (size!=16) icon->scale(size,size,1); + } + else { + icon = new FXPNGIcon(getApp(),gogglesmm_32_png,0,opaque ? IMAGE_OPAQUE : 0); + icon->setVisual(getVisual()); + if (size!=32) icon->scale(size,size,1); + } + + icon->blend(GMPlayerManager::instance()->getPreferences().gui_tray_color); + icon->create(); + + // Mark Dirty + update(); + } + } + +FXbool GMTrayIcon::findSystemTray() { + FXString systemtray = GMStringFormat("_NET_SYSTEM_TRAY_S%d",DefaultScreen((Display*)getApp()->getDisplay())); + Atom xtrayselection = XInternAtom((Display*)getApp()->getDisplay(),systemtray.text(),0); + if (xtrayselection!=None) { + xtraywindow = (FXID)XGetSelectionOwner((Display*)getApp()->getDisplay(),xtrayselection); + } + return (xtraywindow!=0); + } + + +void GMTrayIcon::requestDock() { + if (xid && xtraywindow) { + XEvent ev; + memset(&ev,0,sizeof(ev)); + ev.xclient.type = ClientMessage; + ev.xclient.window = xtraywindow; + ev.xclient.message_type = xtrayopcode; + ev.xclient.format = 32; + ev.xclient.data.l[0] = CurrentTime; + ev.xclient.data.l[1] = SYSTEM_TRAY_REQUEST_DOCK; + ev.xclient.data.l[2] = xid; + XSendEvent((Display*)getApp()->getDisplay(),xtraywindow,False,NoEventMask,&ev); + XSync((Display*)getApp()->getDisplay(),0); + } + } + +FXuint GMTrayIcon::getTrayOrientation(){ + if (xtrayorientation || xtrayxfceorientation) { + FXuint orientation; + Atom returntype; + int returnformat; + unsigned long nitems; + unsigned long nbytes; + long * bytes=NULL; + + if (xtrayorientation && (XGetWindowProperty((Display*)getApp()->getDisplay(),(Atom)xtraywindow,(Atom)xtrayorientation,0,2,False,XA_CARDINAL,&returntype,&returnformat,&nitems,&nbytes,(unsigned char**)(void*)&bytes)==Success) && returntype==XA_CARDINAL){ + orientation=*(long*)bytes; + XFree(bytes); + return orientation; + } + + if (bytes!=NULL) { + XFree(bytes); + bytes=NULL; + } + + if (xtrayxfceorientation && (XGetWindowProperty((Display*)getApp()->getDisplay(),(Atom)xtraywindow,(Atom)xtrayxfceorientation,0,2,False,XA_CARDINAL,&returntype,&returnformat,&nitems,&nbytes,(unsigned char**)(void*)&bytes)==Success) && returntype==XA_CARDINAL){ + orientation=*(long*)bytes; + XFree(bytes); + return orientation; + } + + if (bytes!=NULL) { + XFree(bytes); + bytes=NULL; + } + } + return SYSTEM_TRAY_UNKNOWN; + } + + +FXuint GMTrayIcon::getTrayVisual(){ + if (xtrayvisual) { + FXuint visualid; + Atom returntype; + int returnformat; + unsigned long nitems; + unsigned long nbytes; + long * bytes=NULL; + + if (xtrayvisual && (XGetWindowProperty((Display*)getApp()->getDisplay(),xtraywindow,xtrayvisual,0,2,False,XA_VISUALID,&returntype,&returnformat,&nitems,&nbytes,(unsigned char**)(void*)&bytes)==Success) && returntype==XA_VISUALID){ + visualid=*(long*)bytes; + //fxmessage("visualid %d\n",visualid); + XFree(bytes); + return visualid; + } + + if (bytes!=NULL) { + XFree(bytes); + bytes=NULL; + } + } + return 0; + } + +void GMTrayIcon::create(){ + xtrayopcode = (FXID)XInternAtom((Display*)getApp()->getDisplay(),"_NET_SYSTEM_TRAY_OPCODE",0); + xtrayorientation = (FXID)XInternAtom((Display*)getApp()->getDisplay(),"_NET_SYSTEM_TRAY_ORIENTATION",0); + xtrayxfceorientation = (FXID)XInternAtom((Display*)getApp()->getDisplay(),"_NET_XFCE_TRAY_MANAGER_ORIENTATION",0); + xtrayvisual = (FXID)XInternAtom((Display*)getApp()->getDisplay(),"_NET_SYSTEM_TRAY_VISUAL",0); + + GMPlug::create(); + if (xid) { + + /// Set the size hints... + XSizeHints size; + size.flags=PMinSize|PMaxSize|PBaseSize|PAspect; + size.x=0; + size.y=0; + size.width=0; + size.height=0; + size.width_inc=0; + size.height_inc=0; + size.min_aspect.x=1; + size.min_aspect.y=1; + size.max_aspect.x=1; + size.max_aspect.y=1; + size.win_gravity=0; + size.win_gravity=0; + + size.min_width=8; + size.min_height=8; + size.max_width=64; + size.max_height=64; + size.base_width=32; + size.base_height=32; + XSetWMNormalHints((Display*)getApp()->getDisplay(),xid,&size); + } + } + +FXbool GMTrayIcon::dock() { + if (findSystemTray()){ + FXuint id = getTrayVisual(); + if (id) { + if (id!=XVisualIDFromVisual((Visual*)getVisual()->getVisual())) + opaque=true; + else + opaque=false; + } + + if (!opaque) { + /// Don't draw the background + XSetWindowAttributes sattr; + sattr.background_pixmap = ParentRelative; + XChangeWindowAttributes((Display*)getApp()->getDisplay(),xid,CWBackPixmap,&sattr); + } + + requestDock(); + return true; + } + return false; + } + +long GMTrayIcon::onConfigure(FXObject*,FXSelector,void*ptr){ + FXEvent * event = (FXEvent*)ptr; +// GMPlug::onConfigure(sender,sel,ptr); + //fxmessage("GMTrayIcon::onConfigure %d %d\n",event->rect.w,event->rect.h); + + + FXuint orientation = getTrayOrientation(); + FXint size=0; + switch(orientation){ + case SYSTEM_TRAY_HORIZONTAL: size=event->rect.h; break; + case SYSTEM_TRAY_VERTICAL : size=event->rect.w; break; + default : size=FXMAX(event->rect.h,event->rect.w); break; + }; + + resize(size,size); + + XSizeHints hint; + hint.flags=PMinSize|PMaxSize|PBaseSize; + hint.min_width = hint.max_width = hint.base_width = size; + hint.min_height = hint.max_height = hint.base_height = size; + XSetWMNormalHints((Display*)getApp()->getDisplay(),xid,&hint); + + + if (icon && icon->getWidth()!=size) { + delete icon; + icon=NULL; + } + + if (icon==NULL) { + if (size<=16) { + icon = new FXPNGIcon(getApp(),gogglesmm_16_png,0,opaque ? IMAGE_OPAQUE : 0); + icon->setVisual(getVisual()); + if (size!=16) icon->scale(size,size,1); + } + else { + icon = new FXPNGIcon(getApp(),gogglesmm_32_png,0,opaque ? IMAGE_OPAQUE : 0); + icon->setVisual(getVisual()); + if (size!=32) icon->scale(size,size,1); + } + icon->blend(GMPlayerManager::instance()->getPreferences().gui_tray_color); + icon->create(); + } + return 1; + } + + +long GMTrayIcon::onRightBtnRelease(FXObject*,FXSelector,void*ptr){ + FXEvent * event=(FXEvent*)ptr; + if (event->moved) return 0; + GMMenuPane pane(this,POPUP_SHRINKWRAP); + new GMMenuCommand(&pane,pane.tr("Play"),GMIconTheme::instance()->icon_play,GMPlayerManager::instance()->getMainWindow(),GMWindow::ID_PLAYPAUSEMENU); + new GMMenuCommand(&pane,pane.tr("Stop"),GMIconTheme::instance()->icon_stop,GMPlayerManager::instance()->getMainWindow(),GMWindow::ID_STOP); + new GMMenuCommand(&pane,pane.tr("Previous Track"),GMIconTheme::instance()->icon_prev,GMPlayerManager::instance()->getMainWindow(),GMWindow::ID_PREV); + new GMMenuCommand(&pane,pane.tr("Next Track"),GMIconTheme::instance()->icon_next,GMPlayerManager::instance()->getMainWindow(),GMWindow::ID_NEXT); + new FXMenuSeparator(&pane); + new GMMenuCommand(&pane,tr("Quit"),GMIconTheme::instance()->icon_exit,GMPlayerManager::instance()->getMainWindow(),GMWindow::ID_QUIT); + gm_set_window_cursor(&pane,getApp()->getDefaultCursor(DEF_ARROW_CURSOR)); + pane.create(); + pane.forceRefresh(); + ewmh_change_window_type(&pane,WINDOWTYPE_POPUP_MENU); + pane.popup(NULL,event->root_x+1,event->root_y+1); + getApp()->runModalWhileShown(&pane); + return 1; + } + + +long GMTrayIcon::onLeftBtnPress(FXObject*,FXSelector,void*){ + GMPlayerManager::instance()->cmd_toggle_shown(); + return 1; + } + +long GMTrayIcon::onMiddleBtnPress(FXObject*,FXSelector,void*){ + GMPlayerManager * p = GMPlayerManager::instance(); + if (p->can_pause()) + p->pause(); + else if (p->can_unpause()) + p->unpause(); + else if (p->can_play()) + p->play(); + return 1; + } + +long GMTrayIcon::onMouseWheel(FXObject*sender,FXSelector sel,void*ptr){ + GMPlayerManager::instance()->getMainWindow()->handle(sender,sel,ptr); + return 1; + } + + +long GMTrayIcon::onQueryTip(FXObject*sender,FXSelector sel,void* ptr){ + if(FXTopWindow::onQueryTip(sender,sel,ptr)) return 1; + if((flags&FLAG_TIP) && !tip.empty() && !grabbed() ){ + sender->handle(this,FXSEL(SEL_COMMAND,ID_SETSTRINGVALUE),(void*)&tip); + return 1; + } + return 0; + } + +long GMTrayIcon::onPaint(FXObject*,FXSelector,void*){ + if (icon) { + FXDCWindow dc(this); + dc.drawIcon(icon,0,0); + } + return 1; + } diff --git a/src/GMTrayIcon.h b/src/GMTrayIcon.h new file mode 100644 index 0000000..17ae15b --- /dev/null +++ b/src/GMTrayIcon.h @@ -0,0 +1,69 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#ifndef GMTRAYICON_H +#define GMTRAYICON_H + +class GMTrayIcon : public GMPlug { +FXDECLARE(GMTrayIcon) +protected: + FXID xtraywindow; + FXID xtrayopcode; + FXID xtrayorientation; + FXID xtrayxfceorientation; + FXID xtrayvisual; +protected: + FXIcon * icon; + FXbool opaque; + FXString tip; +private: + GMTrayIcon(const GMTrayIcon*); + GMTrayIcon& operator=(const GMTrayIcon&); +protected: + GMTrayIcon(); + FXbool findSystemTray(); + void requestDock(); + FXuint getTrayOrientation(); + FXuint getTrayVisual(); +public: + long onPaint(FXObject*,FXSelector,void*); + long onConfigure(FXObject*,FXSelector,void*); + long onLeftBtnPress(FXObject*,FXSelector,void*); + long onMiddleBtnPress(FXObject*,FXSelector,void*); + long onRightBtnRelease(FXObject*,FXSelector,void*); + long onMouseWheel(FXObject*,FXSelector,void*); + long onQueryTip(FXObject*,FXSelector,void*); +public: + GMTrayIcon(FXApp * app); + + void setToolTip(const FXString & t) { tip = t; } + + void display(const GMTrack&); + + void reset(); + + virtual void create(); + + FXbool dock(); + + void updateIcon(); + + virtual ~GMTrayIcon(); + }; + +#endif diff --git a/src/GMURL.cpp b/src/GMURL.cpp new file mode 100644 index 0000000..b8c6bcd --- /dev/null +++ b/src/GMURL.cpp @@ -0,0 +1,308 @@ +/******************************************************************************** +* * +* U R L M a n i p u l a t i o n * +* * +********************************************************************************* +* Copyright (C) 2000,2011 by Jeroen van der Zijp. All Rights Reserved. * +********************************************************************************* +* This library is free software; you can redistribute it and/or modify * +* it under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 3 of the License, or * +* (at your option) any later version. * +* * +* 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 program. If not, see * +********************************************************************************/ +#include "gmdefs.h" +#if FOXVERSION < FXVERSION(1,7,0) + +#define UNRESERVED "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~" // Unreserved characters +#define IPV6DIGITS "abcdefABCDEF0123456789:." // Stuff in IPv6 numbers +#define PCTENCODED "%0123456789abcdefABCDEF" // Percent encoded characters +#define GENDELIMS ":/?#[]@" // General delimiters +#define SUBDELIMS "!$&'()*+,;=" // Sub-delimiters +#define RESERVED ":/?#[]@!$&'()*+,;=" // Reserved characters (GENDELIMS + SUBDELIMS) +#define UNSAFE "<>#%{}|^~[]`\" " // Unsafe characters +#define ENCODE_THESE "<>#%{}|^~[]`\"?$&'*,;=" // Encode these for pathnames + +using namespace FX; + +/*******************************************************************************/ + +// URL parts +class URL { +public: + FXint prot[2]; + FXint user[2]; + FXint pass[2]; + FXint host[2]; + FXint port[2]; + FXint path[2]; + FXint quer[2]; + FXint frag[2]; +public: + URL(const FXString& string); + }; + + +// Parse string to url parts +URL::URL(const FXString& string){ + register FXint s=0; + + prot[0]=prot[1]=0; + + // Parse protocol + if(Ascii::isLetter(string[0])){ + s++; + + // Scan till end of scheme name + while(Ascii::isAlphaNumeric(string[s]) || string[s]=='+' || string[s]=='-' || string[s]=='.') s++; + + // Scheme end found + if(string[s]==':' && s>1){ + prot[1]=s++; + } + else{ + s=prot[0]; // Reset:- wasn't protocol after all since no ':' found + } + } + + user[0]=user[1]=s; + pass[0]=pass[1]=s; + host[0]=host[1]=s; + port[0]=port[1]=s; + + // Parse hier part + if(string[s]=='/' && string[s+1]=='/'){ + s+=2; + + // Parse username + user[0]=s; + while(string[s] && strchr(UNRESERVED SUBDELIMS "%",string[s])){ + s++; + } + + // Parse password + user[1]=pass[0]=s; + if(string[s]==':'){ + pass[0]=++s; + while(string[s] && strchr(UNRESERVED SUBDELIMS "%",string[s])){ + s++; + } + } + pass[1]=s; + + // Check for @ after user:pass + if(string[s]=='@'){ + s++; + } + else{ + s=pass[0]=pass[1]=user[1]=user[0]; // Reset:- wasn't user:pass after all since no '@' found + } + + // Parse hostname + host[0]=s; + while(string[s] && strchr(UNRESERVED SUBDELIMS "%",string[s])){ + s++; + } + + // Parse port number + host[1]=port[0]=s; + if(string[s]==':'){ + port[0]=++s; + while(Ascii::isDigit(string[s])) s++; + } + port[1]=s; + } + +#ifdef WIN32 + // Parse path, allowing for \ path delimiters (legacy urls) + path[0]=s; + while(string[s] && strchr(UNRESERVED SUBDELIMS "%:@/\\ ",string[s])){ + s++; + } +#else + // Parse path + path[0]=s; + while(string[s] && strchr(UNRESERVED SUBDELIMS "%:@/ ",string[s])){ + s++; + } +#endif + + // Parse query + path[1]=quer[0]=s; + if(string[s]=='?'){ + quer[0]=++s; + while(string[s] && strchr(UNRESERVED SUBDELIMS "%:@/?",string[s])){ + s++; + } + } + + // Parse fragment + quer[1]=frag[0]=s; + if(string[s]=='#'){ + frag[0]=++s; + while(string[s] && strchr(UNRESERVED SUBDELIMS "%:@/?",string[s])){ + s++; + } + } + frag[1]=s; + } + + +// Encode control characters and characters from set using %-encoding +FXString GMURL::encode(const FXString& url,const FXchar* set){ + FXString result; + if(!url.empty()){ + register FXint p,q,c; + for(p=q=0; p>4)&15]; + result[q++]=FXString::hex[c&15]; +// result[q++]=FXString::value2Digit[c>>4]; +// result[q++]=FXString::value2Digit[c&15]; + continue; + } + result[q++]=c; + } + } + return result; + } + + +// Decode string containing %-encoded characters +FXString GMURL::decode(const FXString& url){ + FXString result; + if(!url.empty()){ + register FXint p,q,c; + for(p=q=0; p * +********************************************************************************* +* $Id: FXURL.h,v 1.23 2008/06/02 14:19:51 fox Exp $ * +********************************************************************************/ +#ifndef GMURL_H +#define GMURL_H + +#if FOXVERSION < FXVERSION(1,7,0) +namespace GMURL { + +/// Encode control characters and characters from set using %-encoding +extern FXAPI FXString encode(const FXString& string,const FXchar* set=NULL); + +/// Decode string containing %-encoded characters +extern FXAPI FXString decode(const FXString& string); + +/// Parse scheme from string containing url +extern FXAPI FXString scheme(const FXString& string); + +/// Parse username from string containing url +extern FXAPI FXString username(const FXString& string); + +/// Parse password from string containing url +extern FXAPI FXString password(const FXString& string); + +/// Parse hostname from string containing url +extern FXAPI FXString host(const FXString& string); + +/// Parse port number from string containing url +extern FXAPI FXint port(const FXString& string); + +/// Parse path from string containing url +extern FXAPI FXString path(const FXString& string); + +/// Parse query from string containing url +extern FXAPI FXString query(const FXString& string); + +/// Parse fragment from string containing url +extern FXAPI FXString fragment(const FXString& string); + + +/// Return URL of filename +extern FXAPI FXString fileToURL(const FXString& string); + +/// Return filename from URL, empty if url is not a local file +extern FXAPI FXString fileFromURL(const FXString& string); +} +#else +#define GMURL FXURL +#endif +#else +#error GMURL already included +#endif diff --git a/src/GMWindow.cpp b/src/GMWindow.cpp new file mode 100644 index 0000000..3324b5a --- /dev/null +++ b/src/GMWindow.cpp @@ -0,0 +1,1541 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#include "gmdefs.h" +#include + +#include + +#include +#include "GMPlayer.h" + + +#include "GMApp.h" +#include "GMAbout.h" +#include "GMWindow.h" +#include "GMTrackList.h" +#include "GMList.h" +#include "GMRemote.h" +#include "GMTag.h" +#include "GMThread.h" +#include "GMSearch.h" +#include "GMSource.h" +#include "GMPlayerManager.h" +#include "GMDatabaseSource.h" +#include "GMTrackView.h" +#include "GMSourceView.h" +#include "GMAudioScrobbler.h" +#include "icons.h" +#include "GMIconTheme.h" +#include "GMImportDialog.h" +#include "GMPreferencesDialog.h" +#include "GMImageView.h" +#include "GMFilename.h" + +#include +#include +#include +#include +#include +#include + + +#if FOXVERSION > FXVERSION(1,7,21) +#define HIDESOURCES (FX4Splitter::ExpandTopRight) +#define SHOWSOURCES (FX4Splitter::ExpandTopLeft|FX4Splitter::ExpandTopRight) +#define SHOWSOURCES_COVER (FX4Splitter::ExpandTopLeft|FX4Splitter::ExpandTopRight|FX4Splitter::ExpandBottomLeft) +#endif + + +// Define Message Map +FXDEFMAP(GMWindow) GMWindowMap[]={ + //________Message_Type_____________________ID___________________________Message_Handler___ + FXMAPFUNC(SEL_UPDATE, GMWindow::ID_PLAYPAUSE, GMWindow::onUpdPlayPause), + FXMAPFUNC(SEL_UPDATE, GMWindow::ID_PLAYPAUSEMENU, GMWindow::onUpdPlayPauseMenu), + + FXMAPFUNC(SEL_UPDATE, GMWindow::ID_PAUSE, GMWindow::onUpdPause), + FXMAPFUNC(SEL_UPDATE, GMWindow::ID_STOP, GMWindow::onUpdStop), + FXMAPFUNC(SEL_UPDATE, GMWindow::ID_NEXT, GMWindow::onUpdNext), + FXMAPFUNC(SEL_UPDATE, GMWindow::ID_PREV, GMWindow::onUpdPrev), + FXMAPFUNC(SEL_UPDATE, GMWindow::ID_REPEAT_ALL, GMWindow::onUpdRepeatAll), + FXMAPFUNC(SEL_UPDATE, GMWindow::ID_REPEAT, GMWindow::onUpdRepeat), + FXMAPFUNC(SEL_UPDATE, GMWindow::ID_REPEAT_AB, GMWindow::onUpdRepeatAB), + FXMAPFUNC(SEL_UPDATE, GMWindow::ID_REPEAT_OFF, GMWindow::onUpdRepeatOff), + + FXMAPFUNC(SEL_UPDATE, GMWindow::ID_SHUFFLE, GMWindow::onUpdShuffle), + FXMAPFUNC(SEL_UPDATE, GMWindow::ID_SLEEP, GMWindow::onUpdSleepTimer), + FXMAPFUNC(SEL_UPDATE, GMWindow::ID_SHOW_TRACK, GMWindow::onUpdShowTrack), + FXMAPFUNC(SEL_UPDATE, GMWindow::ID_VOLUME_BUTTON, GMWindow::onUpdVolumeButton), + FXMAPFUNC(SEL_UPDATE, GMWindow::ID_SHOW_MINIPLAYER, GMWindow::onUpdShowMiniPlayer), + + FXMAPFUNC(SEL_COMMAND, GMWindow::ID_QUIT, GMWindow::onCmdQuit), + FXMAPFUNC(SEL_SIGNAL, GMWindow::ID_QUIT, GMWindow::onCmdQuit), + FXMAPFUNC(SEL_COMMAND, GMWindow::ID_IMPORT_DIRS, GMWindow::onCmdImport), + FXMAPFUNC(SEL_COMMAND, GMWindow::ID_IMPORT_FILES, GMWindow::onCmdImport), + FXMAPFUNC(SEL_COMMAND, GMWindow::ID_SYNC_DIRS, GMWindow::onCmdImport), + + FXMAPFUNC(SEL_COMMAND, GMWindow::ID_OPEN, GMWindow::onCmdOpen), + + FXMAPFUNC(SEL_COMMAND, GMWindow::ID_REPEAT_ALL, GMWindow::onCmdRepeatAll), + FXMAPFUNC(SEL_COMMAND, GMWindow::ID_REPEAT_AB, GMWindow::onCmdRepeatAB), + FXMAPFUNC(SEL_COMMAND, GMWindow::ID_REPEAT, GMWindow::onCmdRepeat), + FXMAPFUNC(SEL_COMMAND, GMWindow::ID_REPEAT_OFF, GMWindow::onCmdRepeatOff), + + FXMAPFUNC(SEL_COMMAND, GMWindow::ID_SHUFFLE, GMWindow::onCmdShuffle), + + FXMAPFUNC(SEL_COMMAND, GMWindow::ID_ABOUT, GMWindow::onCmdAbout), + + FXMAPFUNC(SEL_COMMAND, GMWindow::ID_PLAYPAUSE, GMWindow::onCmdPlayPause), + FXMAPFUNC(SEL_COMMAND, GMWindow::ID_PLAYPAUSEMENU, GMWindow::onCmdPlayPause), + + FXMAPFUNC(SEL_COMMAND, GMWindow::ID_PAUSE, GMWindow::onCmdPause), + FXMAPFUNC(SEL_COMMAND, GMWindow::ID_STOP, GMWindow::onCmdStop), + FXMAPFUNC(SEL_COMMAND, GMWindow::ID_NEXT, GMWindow::onCmdNext), + FXMAPFUNC(SEL_COMMAND, GMWindow::ID_PREV, GMWindow::onCmdPrev), + +#if FOXVERSION >= FXVERSION(1,7,11) + FXMAPFUNC(SEL_COMMAND, GMWindow::ID_SHOW_FULLSCREEN, GMWindow::onCmdShowFullScreen), +#endif + FXMAPFUNC(SEL_COMMAND, GMWindow::ID_SHOW_MINIPLAYER, GMWindow::onCmdShowMiniPlayer), + + FXMAPFUNC(SEL_COMMAND, GMWindow::ID_SHOW_TRACK, GMWindow::onCmdShowTrack), + + FXMAPFUNC(SEL_COMMAND, GMWindow::ID_PREFERENCES, GMWindow::onCmdPreferences), + + FXMAPFUNC(SEL_COMMAND, GMWindow::ID_RESET_COLORS, GMWindow::onCmdResetColors), + + FXMAPFUNC(SEL_COMMAND, GMWindow::ID_TIMESLIDER, GMWindow::onCmdTimeSlider), + + FXMAPFUNC(SEL_COMMAND, GMWindow::ID_VOLUME_SLIDER, GMWindow::onCmdVolume), + FXMAPFUNC(SEL_CHANGED, GMWindow::ID_VOLUME_SLIDER, GMWindow::onCmdVolume), + FXMAPFUNC(SEL_MOUSEWHEEL, GMWindow::ID_VOLUME_BUTTON, GMWindow::onCmdVolumeButton), + FXMAPFUNC(SEL_MOUSEWHEEL, 0, GMWindow::onCmdVolumeButton), + + + FXMAPFUNC(SEL_COMMAND, GMWindow::ID_HOMEPAGE, GMWindow::onCmdHomePage), + FXMAPFUNC(SEL_COMMAND, GMWindow::ID_REPORT_ISSUE, GMWindow::onCmdReportIssue), + + FXMAPFUNC(SEL_COMMAND, GMWindow::ID_JOIN_LASTFM, GMWindow::onCmdJoinLastFM), + FXMAPFUNC(SEL_COMMAND, GMWindow::ID_JOIN_GMM_LASTFM, GMWindow::onCmdJoinGMMLastFM), + + FXMAPFUNC(SEL_COMMAND, GMWindow::ID_SLEEP, GMWindow::onCmdSleepTimer), + + FXMAPFUNC(SEL_COMMAND, GMWindow::ID_CHANGE_COVERVIEW, GMWindow::onCmdChangeCoverView), + FXMAPFUNC(SEL_RIGHTBUTTONRELEASE, GMWindow::ID_COVERVIEW, GMWindow::onCmdCoverView), + +#if FOXVERSION > FXVERSION(1,7,21) + FXMAPFUNC(SEL_UPDATE, GMWindow::ID_SHOW_SOURCES, GMWindow::onUpdShowSources), + FXMAPFUNC(SEL_COMMAND, GMWindow::ID_SHOW_SOURCES, GMWindow::onCmdShowSources), +#endif + + FXMAPFUNCS(SEL_COMMAND, GMWindow::ID_COVERSIZE_SMALL,GMWindow::ID_COVERSIZE_EXTRALARGE, GMWindow::onCmdCoverSize), + FXMAPFUNCS(SEL_UPDATE, GMWindow::ID_COVERSIZE_SMALL,GMWindow::ID_COVERSIZE_EXTRALARGE, GMWindow::onUpdCoverSize), + + + }; + + +// Object implementation +FXIMPLEMENT(GMWindow,FXMainWindow,GMWindowMap,ARRAYNUMBER(GMWindowMap)) + + + + + +//---------------------------------------------------------------------------------- +// Constructor +//---------------------------------------------------------------------------------- +GMWindow::GMWindow(FXApp* a,FXObject*tgt,FXSelector msg) : FXMainWindow(a,"Goggles Music Manager",NULL,NULL,DECOR_ALL,5,5,700,580) { + flags|=FLAG_ENABLED; + + remote=NULL; + cover_small=NULL; + + icontheme = new GMIconTheme(getApp()); + icontheme->load(); + + createFonts(); + + setIcon(icontheme->icon_applogo); + setMiniIcon(icontheme->icon_applogo_small); + +#if APPLICATION_BETA > 0 + setTitle("Goggles Music Manager " APPLICATION_VERSION_STRING); +#endif + + /// Set myself as the target + setTarget(tgt); + setSelector(msg); + + /// Popup Volume Menu + volumecontrol = new FXPopup(this,POPUP_VERTICAL|FRAME_RAISED|FRAME_THICK|POPUP_SHRINKWRAP); + volumeslider = new FXSlider(volumecontrol,this,GMWindow::ID_VOLUME_SLIDER,LAYOUT_FIX_HEIGHT|LAYOUT_FIX_WIDTH|SLIDER_VERTICAL|SLIDER_TICKS_RIGHT|SLIDER_TICKS_LEFT|SLIDER_INSIDE_BAR,0,0,20,100); + volumeslider->setTickDelta(10); + volumeslider->setRange(0,100); + volumeslider->setIncrement(10); + + // Make menu bar + FXMenuBar * menubar=new FXMenuBar(this,LAYOUT_SIDE_TOP|LAYOUT_FILL_X); + menubar->setDragCursor(getApp()->getDefaultCursor(DEF_ARROW_CURSOR)); + + statusbar = new FXStatusBar(this,LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X|STATUSBAR_WITH_DRAGCORNER,0,0,0,0,3,3,2,2,3); + statusbar->getStatusLine()->setFrameStyle(FRAME_NONE); + + new FXSeparator(this,LAYOUT_FILL_X|SEPARATOR_GROOVE|LAYOUT_SIDE_TOP); +// controlstatusseparator = new FXSeparator(this,LAYOUT_FILL_X|SEPARATOR_GROOVE|LAYOUT_SIDE_BOTTOM); +// controlstatusseparator->hide(); + + controlseparator = new FXSeparator(this,LAYOUT_FILL_X|SEPARATOR_GROOVE|LAYOUT_SIDE_BOTTOM); + + controlframe = new FXHorizontalFrame(this,LAYOUT_SIDE_TOP|LAYOUT_FILL_X,0,0,0,0,3,3,3,3); + playpausebutton = new FXToggleButton(controlframe,tr("Play\tStart Playback\tStart Playback"),tr("Pause\tPause\tPause Playback"),icontheme->icon_play,icontheme->icon_pause,this,ID_PLAYPAUSE,BUTTON_TOOLBAR|FRAME_RAISED|ICON_ABOVE_TEXT); + stopbutton = new FXButton(controlframe,tr("Stop\tStop Playback\tStop Playback"),icontheme->icon_stop,this,ID_STOP,BUTTON_TOOLBAR|FRAME_RAISED|ICON_ABOVE_TEXT); + new FXVerticalSeparator(controlframe,LAYOUT_FILL_Y|SEPARATOR_GROOVE); + prevbutton = new FXButton(controlframe,tr("Previous\tPlay Previous Track\tPlay previous track."),icontheme->icon_prev,this,ID_PREV,BUTTON_TOOLBAR|FRAME_RAISED|ICON_ABOVE_TEXT); + nextbutton = new FXButton(controlframe,tr("Next\tPlay Next Track\tPlay next track."),icontheme->icon_next,this,ID_NEXT,BUTTON_TOOLBAR|FRAME_RAISED|ICON_ABOVE_TEXT); + new FXVerticalSeparator(controlframe,LAYOUT_FILL_Y|SEPARATOR_GROOVE); + + timeslider = new FXSlider(controlframe,this,ID_TIMESLIDER,LAYOUT_FILL_X|LAYOUT_CENTER_Y); + timeslider->setRange(0,65535); + timeslider->setTickDelta(13107); + timeslider->setIncrement(5000); + timeslider->disable(); + timelabel =new FX7Segment(controlframe,"--:--",SEVENSEGMENT_SHADOW|LAYOUT_CENTER_Y); + timelabel->setCellWidth(10); + timelabel->setCellHeight(15); + + new FXVerticalSeparator(controlframe,LAYOUT_FILL_Y|SEPARATOR_GROOVE); + volumebutton = new FXMenuButton(controlframe,tr("\tAdjust Volume\tAdjust Volume"),NULL,volumecontrol,MENUBUTTON_NOARROWS|MENUBUTTON_ATTACH_LEFT|MENUBUTTON_UP|MENUBUTTON_TOOLBAR|FRAME_RAISED|LAYOUT_CENTER_Y); + volumebutton->setTarget(this); + volumebutton->setSelector(ID_VOLUME_BUTTON); + controldragcorner = new FXDragCorner(controlframe); + + FXVerticalFrame * mainframe = new FXVerticalFrame(this,LAYOUT_FILL_X|LAYOUT_FILL_Y); + + +#if FOXVERSION > FXVERSION(1,7,21) + mainsplitter = new FX4Splitter(mainframe,LAYOUT_FILL_X|LAYOUT_FILL_Y|FOURSPLITTER_VERTICAL|FOURSPLITTER_TRACKING); + sourceview = new GMSourceView(mainsplitter); + trackview = new GMTrackView(mainsplitter); + coverframe = new GMCoverFrame(mainsplitter); +#else + mainsplitter = new FXSplitter(mainframe,LAYOUT_FILL_X|LAYOUT_FILL_Y|SPLITTER_HORIZONTAL|SPLITTER_TRACKING); + sourcesplitter = new FXSplitter(mainsplitter,LAYOUT_FILL_X|LAYOUT_FILL_Y|SPLITTER_VERTICAL|SPLITTER_TRACKING|SPLITTER_REVERSED); + sourceview = new GMSourceView(sourcesplitter); + coverframe = new GMCoverFrame(sourcesplitter); + trackview = new GMTrackView(mainsplitter); + sourcesplitter->setBarSize(7); +#endif + mainsplitter->setBarSize(7); + + coverframe->setBackColor(getApp()->getBackColor()); + coverframe->setBorderColor(getApp()->getShadowColor()); + coverframe->hide(); + coverview_x11=NULL; + coverview_gl=NULL; + glvisual=NULL; + updateCoverView(); + + // File Menu + filemenu=new GMMenuPane(this); + new GMMenuTitle(menubar,tr("&Music"),NULL,filemenu); + new GMMenuCommand(filemenu,tr("Import Folder…\tCtrl-O\tImport Music from folder into Library"),icontheme->icon_import,this,GMWindow::ID_IMPORT_DIRS); + new GMMenuCommand(filemenu,tr("Sync Folder…\t\tSynchronize Folder with Music in Library"),icontheme->icon_sync,this,GMWindow::ID_SYNC_DIRS); + new FXMenuSeparator(filemenu); + new GMMenuCommand(filemenu,tr("Play File or Stream…\t\tPlay File or Stream"),NULL,this,GMWindow::ID_OPEN); + new FXMenuSeparator(filemenu); + new GMMenuCommand(filemenu,tr("New Playlist…\t\tCreate a new playlist"),icontheme->icon_playlist,sourceview,GMSourceView::ID_NEW_PLAYLIST); + new GMMenuCommand(filemenu,tr("Import Playlist…\t\tImport existing playlist"),icontheme->icon_import,GMPlayerManager::instance()->getDatabaseSource(),GMDatabaseSource::ID_IMPORT_PLAYLIST); + new GMMenuCommand(filemenu,tr("New Radio Station…\t\tCreate a new playlist"),NULL,sourceview,GMSourceView::ID_NEW_STATION); + + new FXMenuSeparator(filemenu); + new GMMenuCommand(filemenu,tr("&Quit\tCtrl-Q\tQuit the application."),icontheme->icon_exit,this,GMWindow::ID_QUIT); + gm_set_window_cursor(filemenu,getApp()->getDefaultCursor(DEF_ARROW_CURSOR)); + + // Help Menu + editmenu=new GMMenuPane(this); + new GMMenuTitle(menubar,tr("&Edit"),NULL,editmenu); + new GMMenuCommand(editmenu,tr("&Copy\tCtrl-C\tCopy Selected Tracks"),icontheme->icon_copy,trackview,GMTrackView::ID_COPY,MENU_AUTOGRAY); + new GMMenuCommand(editmenu,tr("&Cut\tCtrl-X\tCut Selected Tracks"),icontheme->icon_cut,trackview,GMTrackView::ID_CUT,MENU_AUTOGRAY); + new GMMenuCommand(editmenu,tr("&Paste\tCtrl-V\tPaste Clipboard Selection"),icontheme->icon_paste,trackview,GMTrackView::ID_PASTE,MENU_AUTOGRAY); + new FXMenuSeparator(editmenu); + new GMMenuCommand(editmenu,tr("Find…\tCtrl-F\tShow search filter."),GMIconTheme::instance()->icon_find,trackview,GMTrackView::ID_TOGGLE_FILTER); + new FXMenuSeparator(editmenu); + new GMMenuCommand(editmenu,tr("Preferences…"),icontheme->icon_settings,this,GMWindow::ID_PREFERENCES); + gm_set_window_cursor(editmenu,getApp()->getDefaultCursor(DEF_ARROW_CURSOR)); + + viewmenu=new GMMenuPane(this); + new GMMenuTitle(menubar,tr("&View"),NULL,viewmenu); + new GMMenuCheck(viewmenu,tr("&Browse\tCtrl-B\tShow genre artist and album browser."),trackview,GMTrackView::ID_TOGGLE_BROWSER); + new GMMenuCheck(viewmenu,tr("Show &Genres\tCtrl-G\tShow genre browser."),trackview,GMTrackView::ID_TOGGLE_GENRES); + new FXMenuSeparator(viewmenu); +#if FOXVERSION > FXVERSION(1,7,21) + new GMMenuCheck(viewmenu,tr("Show &Sources\tCtrl-S\tShow source browser "),this,ID_SHOW_SOURCES); +#else + new GMMenuCheck(viewmenu,tr("Show &Sources\tCtrl-S\tShow source browser "),sourcesplitter,FXWindow::ID_TOGGLESHOWN); +#endif + + new FXMenuSeparator(viewmenu); +#if FOXVERSION >= FXVERSION(1,7,11) + fullscreencheck = new GMMenuCheck(viewmenu,tr("Show Full Screen\tF12\tToggle fullscreen mode."),this,ID_SHOW_FULLSCREEN); +#endif + new GMMenuCheck(viewmenu,tr("Show Mini Player\tCtrl-M\tToggle Mini Player."),this,ID_SHOW_MINIPLAYER); + + new FXMenuSeparator(viewmenu); + new GMMenuCommand(viewmenu,tr("&Configure Columns…"),NULL,trackview,GMTrackView::ID_CONFIGURE_COLUMNS); + new GMMenuCascade(viewmenu,tr("&Sort"),icontheme->icon_sort,trackview->getSortMenu()); + new FXMenuSeparator(viewmenu); + new GMMenuCommand(viewmenu,tr("&Jump to Current Track\tCtrl-J\tShow current playing track."),NULL,trackview,GMTrackView::ID_SHOW_CURRENT); + gm_set_window_cursor(viewmenu,getApp()->getDefaultCursor(DEF_ARROW_CURSOR)); + + playmenu=new GMMenuPane(this); + new GMMenuTitle(menubar,tr("&Control"),NULL,playmenu); + new GMMenuCommand(playmenu,tr("Play\tCtrl-P\tStart playback."),icontheme->icon_play,this,ID_PLAYPAUSEMENU); + new GMMenuCommand(playmenu,tr("Stop\tCtrl-\\\tStop playback."),icontheme->icon_stop,this,ID_STOP); + new GMMenuCommand(playmenu,tr("Previous Track\tCtrl-[\tPlay next track."),icontheme->icon_prev,this,ID_PREV); + new GMMenuCommand(playmenu,tr("Next Track\tCtrl-]\tPlay previous track."),icontheme->icon_next,this,ID_NEXT); + new FXMenuSeparator(playmenu); + new GMMenuRadio(playmenu,tr("Repeat Off\tCtrl-,\tRepeat current track."),this,ID_REPEAT_OFF); + new GMMenuRadio(playmenu,tr("Repeat Track\tCtrl-.\tRepeat current track."),this,ID_REPEAT); + new GMMenuRadio(playmenu,tr("Repeat All Tracks\tCtrl-/\tRepeat all tracks."),this,ID_REPEAT_ALL); + new GMMenuCheck(playmenu,tr("Repeat A-B\tCtrl-T\tRepeat section of track."),this,ID_REPEAT_AB); + new GMMenuCheck(playmenu,tr("Shuffle Mode\tAlt-R\tPlay tracks in random order."),this,ID_SHUFFLE); + new FXMenuSeparator(playmenu); + new GMMenuCommand(playmenu,tr("Equalizer\t\t"),NULL,GMPlayerManager::instance(),GMPlayerManager::ID_EQUALIZER); + new GMMenuCheck(playmenu,tr("Sleep Timer\t\tSetup sleeptimer."),this,ID_SLEEP); + gm_set_window_cursor(playmenu,getApp()->getDefaultCursor(DEF_ARROW_CURSOR)); + + helpmenu=new GMMenuPane(this); + new GMMenuTitle(menubar,tr("&Help"),NULL,helpmenu,LAYOUT_RIGHT); + new GMMenuCommand(helpmenu,tr("&Homepage"),icontheme->icon_homepage,this,GMWindow::ID_HOMEPAGE); + new GMMenuCommand(helpmenu,tr("&Report Issue…"),NULL,this,GMWindow::ID_REPORT_ISSUE); + new FXMenuSeparator(helpmenu); + new GMMenuCommand(helpmenu,tr("&Sign up for last.fm…"),NULL,this,GMWindow::ID_JOIN_LASTFM); + new GMMenuCommand(helpmenu,tr("&Join GMM on last.fm…\t\tJoin the Goggles Music Manager group on last.fm…"),NULL,this,GMWindow::ID_JOIN_GMM_LASTFM); + new FXMenuSeparator(helpmenu); + new GMMenuCommand(helpmenu,tr("&About…"),icontheme->icon_info,this,GMWindow::ID_ABOUT); + gm_set_window_cursor(helpmenu,getApp()->getDefaultCursor(DEF_ARROW_CURSOR)); + + getAccelTable()->addAccel(parseAccel("F11"),this,FXSEL(SEL_COMMAND,ID_SHOW_MINIPLAYER)); + getAccelTable()->addAccel(parseAccel("Ctrl-W"),this,FXSEL(SEL_CLOSE,0)); + getAccelTable()->addAccel(parseAccel("/"),trackview,FXSEL(SEL_COMMAND,GMTrackView::ID_TOGGLE_FILTER)); + } + + +void GMWindow::init(FXuint mode) { + sourceview->init(); + + if (mode==SHOW_NORMAL || mode==SHOW_TRAY) { + if (getApp()->reg().readBoolEntry("window","remote",false)){ + if (!remote) { + remote = new GMRemote(getApp(),target,message); + remote->create(); + } + } + } + + switch(mode) { + case SHOW_TRAY : break; + case SHOW_WIZARD: toggleShown(); + handle(this,FXSEL(SEL_COMMAND,GMWindow::ID_IMPORT_DIRS),NULL); + break; + default : if (getApp()->reg().readBoolEntry("window","window-show",true)){ + toggleShown(); + } + break; + }; + } + +//---------------------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------------------- +GMWindow::~GMWindow(){ + if (coverview_gl) { + coverview_gl->setImage(NULL); + delete coverview_gl; + delete glvisual; + } + delete icontheme; + } + + +//---------------------------------------------------------------------------------- +// Create Fonts +//---------------------------------------------------------------------------------- +void GMWindow::createFonts(){ +#if FOXVERSION < FXVERSION(1,7,17) + FXFontDesc fontdescription; + getApp()->getNormalFont()->getFontDesc(fontdescription); +#else + FXFontDesc fontdescription = getApp()->getNormalFont()->getFontDesc(); +#endif + fontdescription.weight = FXFont::Bold; + font_thick = new FXFont(getApp(),fontdescription); + font_thick->create(); + } + + +//---------------------------------------------------------------------------------- +// Create +//---------------------------------------------------------------------------------- +void GMWindow::create(){ + +#if FOXVERSION > FXVERSION(1,7,21) + mainsplitter->setHSplit(getApp()->reg().readIntEntry("window","source-list-hsplit",2500)); +#else + mainsplitter->setSplit(0,getApp()->reg().readIntEntry("window","source-list-split",200)); + sourcesplitter->setSplit(1,getApp()->reg().readIntEntry("window","cover-list-split",256+10+2)); +#endif + + configureStatusbar(GMPlayerManager::instance()->getPreferences().gui_show_status_bar); + + /// Set initial Toolbar Configuration + configureToolbar(GMPlayerManager::instance()->getPreferences().gui_toolbar_docktop, + GMPlayerManager::instance()->getPreferences().gui_toolbar_showlabels, + GMPlayerManager::instance()->getPreferences().gui_toolbar_bigicons, + true); + + FXMainWindow::create(); + fix_wm_properties(this); + + /// Initialize the window size & position + if (getApp()->reg().readIntEntry("window","x",-1)!=-1) { + FXint xx=getApp()->reg().readIntEntry("window","x",getX()); + FXint yy=getApp()->reg().readIntEntry("window","y",getY()); + FXint ww=getApp()->reg().readIntEntry("window","width",500); + FXint hh=getApp()->reg().readIntEntry("window","height",550); + position(xx,yy,ww,hh); + } + else { + place(PLACEMENT_SCREEN); + } + + /// Set Window Type to Normal + ewmh_change_window_type(this,WINDOWTYPE_NORMAL); + + + /// Set extended window manager hints on the menus for compositing window managers. + ewmh_change_window_type(filemenu,WINDOWTYPE_DROPDOWN_MENU); + ewmh_change_window_type(editmenu,WINDOWTYPE_DROPDOWN_MENU); + ewmh_change_window_type(viewmenu,WINDOWTYPE_DROPDOWN_MENU); + ewmh_change_window_type(playmenu,WINDOWTYPE_DROPDOWN_MENU); + ewmh_change_window_type(helpmenu,WINDOWTYPE_DROPDOWN_MENU); + + gm_set_application_icon(this); + } + + +#if FOXVERSION >= FXVERSION(1,7,11) +void GMWindow::setFullScreen(FXbool show){ + if (show) { + if (isMaximized()) restore(); + getApp()->reg().writeIntEntry("window","x",getX()); + getApp()->reg().writeIntEntry("window","y",getY()); + getApp()->reg().writeIntEntry("window","width",getWidth()); + getApp()->reg().writeIntEntry("window","height",getHeight()); + fullScreen(); + statusbar->setCornerStyle(false); + controldragcorner->hide(); + fullscreencheck->setCheck(true); + } + else { + restore(); + if (!statusbar->shown() && GMPlayerManager::instance()->getPreferences().gui_toolbar_docktop==false) + controldragcorner->show(); + statusbar->setCornerStyle(true); + fullscreencheck->setCheck(false); + } + } +#endif + + +void GMWindow::hide() { +#if FOXVERSION >= FXVERSION(1,7,11) + getApp()->reg().writeBoolEntry("window","fullscreen",isFullScreen()); +#endif + getApp()->reg().writeBoolEntry("window","maximized",isMaximized()); +#if FOXVERSION >= FXVERSION(1,7,11) + if (isFullScreen() || isMaximized() || isMinimized() ) +#else + if (isMaximized() || isMinimized() ) +#endif + restore(); + FXMainWindow::hide(); + } + +void GMWindow::toggleShown() { + if (remote) { + if (remote->shown()) + remote->hide(); + else + remote->show(); + } + else { + if (shown()) + hide(); + else + show(); + } + } + +void GMWindow::show(){ + FXMainWindow::show(); +#if FOXVERSION >= FXVERSION(1,7,11) + if (getApp()->reg().readBoolEntry("window","fullscreen",false)){ + getApp()->reg().writeBoolEntry("window","fullscreen",false); + getApp()->reg().writeBoolEntry("window","maximized",false); + + if (getApp()->reg().readIntEntry("window","x",-1)!=-1) { + FXint xx=getApp()->reg().readIntEntry("window","x",getX()); + FXint yy=getApp()->reg().readIntEntry("window","y",getY()); + FXint ww=getApp()->reg().readIntEntry("window","width",500); + FXint hh=getApp()->reg().readIntEntry("window","height",550); + position(xx,yy,ww,hh); + } + else { + place(PLACEMENT_SCREEN); + } + setFullScreen(true); + } + else +#endif + if (getApp()->reg().readBoolEntry("window","maximized",false)){ + getApp()->reg().writeBoolEntry("window","fullscreen",false); + getApp()->reg().writeBoolEntry("window","maximized",false); + maximize(); + } + } + + + +FXbool GMWindow::question(const FXString & title,const FXString & labeltext,const FXString & accept,const FXString & cancel){ + FXDialogBox dialog(this,title,DECOR_TITLE|DECOR_BORDER|DECOR_RESIZE,0,0,400,0,0,0,0,0,0,0); + create_dialog_header(&dialog,title,labeltext,NULL); + FXHorizontalFrame *closebox=new FXHorizontalFrame(&dialog,LAYOUT_BOTTOM|LAYOUT_FILL_X,0,0,0,0); + new GMButton(closebox,accept,NULL,&dialog,FXDialogBox::ID_ACCEPT,BUTTON_INITIAL|BUTTON_DEFAULT|LAYOUT_RIGHT|FRAME_RAISED|FRAME_THICK,0,0,0,0, 20,20); + new GMButton(closebox,cancel,NULL,&dialog,FXDialogBox::ID_CANCEL,BUTTON_DEFAULT|LAYOUT_RIGHT|FRAME_RAISED|FRAME_THICK,0,0,0,0, 20,20); + if (dialog.execute()) { + return TRUE; + } + return FALSE; + } + + +void GMWindow::showRemote(){ + if (!remote) { + remote = new GMRemote(getApp(),target,message); + remote->update_volume_display(GMPlayerManager::instance()->volume()); + remote->create(); + if (GMPlayerManager::instance()->playing()){ + GMTrack info; + GMPlayerManager::instance()->getTrackInformation(info); + remote->updateCover(cover_small); + remote->display(info); + } + remote->show(); + } + } + +void GMWindow::hideRemote(){ + if (remote) { + remote->writeRegistry(); + delete remote; + remote = NULL; + } + show(); + } + + +void GMWindow::reset() { + if (remote) remote->reset(); + + /// Reset Status Text + statusbar->getStatusLine()->setNormalText("Ready."); + + /// Clear Cover + loadCover(FXString::null); + +#if APPLICATION_BETA > 0 + setTitle("Goggles Music Manager " APPLICATION_VERSION_STRING); +#else + setTitle("Goggles Music Manager"); +#endif + } + +void GMWindow::display(const GMTrack& info){ + FXUTF8Codec codec; + FXString title = GMFilename::format_track(info,GMPlayerManager::instance()->getPreferences().gui_format_title,FXString::null,0,&codec); + statusbar->getStatusLine()->setNormalText(title); + + if (GMPlayerManager::instance()->getPreferences().gui_show_playing_titlebar){ + +#if APPLICATION_BETA > 0 + setTitle(GMStringFormat("%s ~ Goggles Music Manager " APPLICATION_VERSION_STRING,title.text())); +#else + setTitle(GMStringFormat("%s ~ Goggles Music Manager",title.text())); +#endif + } + else { +#if APPLICATION_BETA > 0 + setTitle("Goggles Music Manager " APPLICATION_VERSION_STRING); +#else + setTitle("Goggles Music Manager"); +#endif + } + + if (remote) remote->display(info); + } + +void GMWindow::update_volume_display(FXint level) { + if (level<=0) + volumebutton->setIcon(icontheme->icon_audio_volume_muted); + else if (level<=33) + volumebutton->setIcon(icontheme->icon_audio_volume_low); + else if (level<=66) + volumebutton->setIcon(icontheme->icon_audio_volume_medium); + else + volumebutton->setIcon(icontheme->icon_audio_volume_high); + + volumeslider->setValue(level); + + if (remote) remote->update_volume_display(level); + } + +void GMWindow::update_elapsed_time(FXint hours,FXint minutes,FXint seconds,FXint position,FXbool playing,FXbool seekable) { + if (playing) { + if (hours>0) + timelabel->setText(GMStringFormat("%d:%.2d:%.2d",hours,minutes,seconds)); + else + timelabel->setText(GMStringFormat("%.2d:%.2d",minutes,seconds)); + + if (seekable) { + if (!timeslider->grabbed()){ + timeslider->setValue(position); + timeslider->enable(); + } + } + else { + timeslider->disable(); + } + } + else { + timelabel->setText("--:--"); + timeslider->disable(); + } + if (remote) remote->elapsed_time(hours,minutes,seconds,position,playing); + } + +long GMWindow::onCmdQuit(FXObject *,FXSelector,void*){ + + + sourceview->saveView(); + trackview->saveView(); + + /// Save the Window State + getApp()->reg().writeBoolEntry("window","remote",(remote==NULL) ? false : true); + if (remote) + getApp()->reg().writeBoolEntry("window","window-show",remote->shown()); + else + getApp()->reg().writeBoolEntry("window","window-show",shown()); + + +#if FOXVERSION >= FXVERSION(1,7,11) + if (!isFullScreen() && !isMaximized() && !isMinimized() &&shown()) { +#else + if (!isMaximized() && !isMinimized() && shown()) { +#endif + getApp()->reg().writeIntEntry("window","x",getX()); + getApp()->reg().writeIntEntry("window","y",getY()); + getApp()->reg().writeIntEntry("window","width",getWidth()); + getApp()->reg().writeIntEntry("window","height",getHeight()); + } +#if FOXVERSION >= FXVERSION(1,7,11) + getApp()->reg().writeBoolEntry("window","fullscreen",isFullScreen()); +#endif + getApp()->reg().writeBoolEntry("window","maximized",isMaximized()); + + +#if FOXVERSION > FXVERSION(1,7,21) + getApp()->reg().writeIntEntry("window","source-list-hsplit",mainsplitter->getHSplit()); +#else + getApp()->reg().writeIntEntry("window","source-list-split",mainsplitter->getSplit(0)); + getApp()->reg().writeIntEntry("window","cover-list-split",sourcesplitter->getSplit(1)); +#endif + + /// Get rid of remote + if (remote) { + remote->writeRegistry(); + delete remote; + remote = NULL; + } + + volumeslider->setTarget(NULL); + volumeslider->setSelector(0); + volumebutton->setMenu(NULL); + delete volumecontrol; + + GMPlayerManager::instance()->exit(); + return 1; + } + + + +long GMWindow::onCmdAbout(FXObject *,FXSelector,void*){ + GMAboutDialog dialog(this); + dialog.execute(PLACEMENT_SCREEN); + return 1; + } + + +#if FOXVERSION >= FXVERSION(1,7,11) +long GMWindow::onCmdShowFullScreen(FXObject*,FXSelector,void*){ + if (isFullScreen()) { + setFullScreen(false); + } + else { + setFullScreen(true); + } + return 1; + } +#endif + + +long GMWindow::onCmdShowMiniPlayer(FXObject*,FXSelector,void*){ + showRemote(); + hide(); + return 1; + } + +long GMWindow::onUpdShowMiniPlayer(FXObject*sender,FXSelector,void*){ + if (remote) + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_CHECK),NULL); + else + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_UNCHECK),NULL); + return 1; + } + + +long GMWindow::onCmdShowTrack(FXObject *,FXSelector,void*){ + return 1; + } + +long GMWindow::onUpdShowTrack(FXObject *sender,FXSelector,void*){ + if (!GMPlayerManager::instance()->playing()) + sender->handle(this,FXSEL(SEL_COMMAND,ID_DISABLE),NULL); + else + sender->handle(this,FXSEL(SEL_COMMAND,ID_ENABLE),NULL); + return 1; + } + +long GMWindow::onCmdResetColors(FXObject*,FXSelector,void*){ + GMPlayerManager::instance()->getPreferences().resetColors(); + return 1; + } + +long GMWindow::onCmdPreferences(FXObject *,FXSelector,void*){ + GMPreferencesDialog dialog(this); + dialog.execute(); + return 1; + } + + + +void GMWindow::configureStatusbar(FXbool show){ + if (show) { + statusbar->show(); + //controlstatusseparator->show(); + controldragcorner->hide(); + } + else { + statusbar->hide(); + //controlstatusseparator->hide(); + if (!GMPlayerManager::instance()->getPreferences().gui_toolbar_docktop){ + controldragcorner->show(); + controlframe->recalc(); + } + } + GMPlayerManager::instance()->getPreferences().gui_show_status_bar=show; + recalc(); + controlframe->recalc(); + } + + +void GMWindow::layoutToolBarButtons() { + if (GMPlayerManager::instance()->getPreferences().gui_toolbar_showlabels) { + FXint mw = playpausebutton->getDefaultWidth(); + mw = FXMAX(mw,stopbutton->getDefaultWidth()); + mw = FXMAX(mw,prevbutton->getDefaultWidth()); + mw = FXMAX(mw,nextbutton->getDefaultWidth()); + + playpausebutton->setLayoutHints(LAYOUT_FIX_WIDTH); + playpausebutton->setWidth(mw); + + stopbutton->setLayoutHints(LAYOUT_FIX_WIDTH); + stopbutton->setWidth(mw); + + prevbutton->setLayoutHints(LAYOUT_FIX_WIDTH); + prevbutton->setWidth(mw); + + nextbutton->setLayoutHints(LAYOUT_FIX_WIDTH); + nextbutton->setWidth(mw); + } + else { + playpausebutton->setLayoutHints(0); + stopbutton->setLayoutHints(0); + prevbutton->setLayoutHints(0); + nextbutton->setLayoutHints(0); + } + } + + +void GMWindow::configureToolbar(FXbool docktop,FXbool showlabels,FXbool bigicons,FXbool init/*=false*/){ + +// if ((docktop != GMPlayerManager::instance()->getPreferences().gui_toolbar_docktop) || init) { + if (docktop) { + controlframe->setLayoutHints(LAYOUT_FILL_X|LAYOUT_SIDE_TOP); + //controlseparator->setLayoutHints(LAYOUT_FILL_X|LAYOUT_SIDE_TOP); + controldragcorner->hide(); + controlseparator->hide(); + } + else { + controlframe->setLayoutHints(LAYOUT_FILL_X|LAYOUT_SIDE_BOTTOM); + //controlseparator->setLayoutHints(LAYOUT_FILL_X|LAYOUT_SIDE_BOTTOM); + if (!statusbar->shown()) controldragcorner->show(); + if (statusbar->shown()) + controlseparator->show(); + else + controlseparator->hide(); + } +// } + + if ((bigicons != GMPlayerManager::instance()->getPreferences().gui_toolbar_bigicons) || init) { + + if (bigicons) { + playpausebutton->setIcon(icontheme->icon_play_toolbar); + playpausebutton->setAltIcon(icontheme->icon_pause_toolbar); + stopbutton->setIcon(icontheme->icon_stop_toolbar); + prevbutton->setIcon(icontheme->icon_prev_toolbar); + nextbutton->setIcon(icontheme->icon_next_toolbar); + } + else { + playpausebutton->setIcon(icontheme->icon_play); + playpausebutton->setAltIcon(icontheme->icon_pause); + stopbutton->setIcon(icontheme->icon_stop); + prevbutton->setIcon(icontheme->icon_prev); + nextbutton->setIcon(icontheme->icon_next); + } + update_volume_display(GMPlayerManager::instance()->volume()); + } + + if (showlabels != GMPlayerManager::instance()->getPreferences().gui_toolbar_showlabels || init) { + if (showlabels) { + playpausebutton->setText(tr("Play")); + playpausebutton->setAltText(tr("Pause")); + stopbutton->setText(tr("Stop")); + prevbutton->setText(tr("Previous")); + nextbutton->setText(tr("Next")); + } + else { + playpausebutton->setText(FXString::null); + playpausebutton->setAltText(FXString::null); + stopbutton->setText(FXString::null); + prevbutton->setText(FXString::null); + nextbutton->setText(FXString::null); + } + GMPlayerManager::instance()->getPreferences().gui_toolbar_showlabels=showlabels; + layoutToolBarButtons(); + } + GMPlayerManager::instance()->getPreferences().gui_toolbar_docktop=docktop; + GMPlayerManager::instance()->getPreferences().gui_toolbar_bigicons=bigicons; + } + + +long GMWindow::onCmdImport(FXObject *,FXSelector sel,void*){ + FXuint mode=(FXSELID(sel)==ID_IMPORT_DIRS || FXSELID(sel)==ID_SYNC_DIRS) ? IMPORT_FROMDIR : IMPORT_FROMFILE; + if (FXSELID(sel)==ID_SYNC_DIRS) mode|=IMPORT_SYNC; + + GMImportDialog dialog(this,mode); + if (dialog.execute()) { + FXStringList files; + dialog.getSelectedFiles(files); + GMPlayerManager::instance()->stop(); + + if (FXSELID(sel)==ID_SYNC_DIRS) { + GMSyncDatabase searchdialog(this,files,GMPlayerManager::instance()->getPreferences().import,GMPlayerManager::instance()->getPreferences().sync,-1,true,font_thick); + searchdialog.execute(); + } + else { + GMImportDatabase searchdialog(this,files,GMPlayerManager::instance()->getPreferences().import,-1,font_thick); + searchdialog.execute(); + } + if (sourceview->getSource()==GMPlayerManager::instance()->getSource(0)) + trackview->refresh(); + } + return 1; + } + + +long GMWindow::onCmdPlayPause(FXObject*,FXSelector,void*){ + if (GMPlayerManager::instance()->can_pause()) + GMPlayerManager::instance()->pause(); + else if (GMPlayerManager::instance()->can_unpause()) + GMPlayerManager::instance()->unpause(); + else + GMPlayerManager::instance()->play(); + return 1; + } + +long GMWindow::onUpdPlayPause(FXObject*sender,FXSelector,void*){ + if ( GMPlayerManager::instance()->can_play() || GMPlayerManager::instance()->can_unpause() || GMPlayerManager::instance()->can_pause()){ + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_ENABLE),NULL); + if (GMPlayerManager::instance()->can_play() || GMPlayerManager::instance()->can_unpause()) + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_UNCHECK),NULL); + else + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_CHECK),NULL); + } + else { + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_DISABLE),NULL); + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_UNCHECK),NULL); + } + return 1; + } + +long GMWindow::onUpdPlayPauseMenu(FXObject*sender,FXSelector,void*){ + FXMenuCommand * menucommand=dynamic_cast(sender); + FXButton * button=dynamic_cast(sender); + + if ( GMPlayerManager::instance()->can_play() || GMPlayerManager::instance()->can_unpause() || GMPlayerManager::instance()->can_pause()){ + if (GMPlayerManager::instance()->can_play() || GMPlayerManager::instance()->can_unpause()){ + if (button) { + button->enable(); + button->setHelpText(tr("Start playback.")); + button->setIcon(GMIconTheme::instance()->icon_play); + } + else { + menucommand->enable(); + menucommand->setText(tr("Play")); + menucommand->setHelpText(tr("Start playback")); + menucommand->setTipText(tr("Start playback")); + menucommand->setIcon(GMIconTheme::instance()->icon_play); + } + } + else { + if (button) { + button->enable(); + button->setHelpText(tr("Pause playback.")); + button->setTipText(tr("Pause playback")); + button->setIcon(GMIconTheme::instance()->icon_pause); + } + else { + menucommand->enable(); + menucommand->setText(tr("Pause")); + menucommand->setHelpText(tr("Pause playback.")); + menucommand->setIcon(GMIconTheme::instance()->icon_pause); + } + } + } + else { + if (button) { + button->setHelpText(tr("Start playback.")); + button->setIcon(GMIconTheme::instance()->icon_play); + button->disable(); + } + else { + menucommand->setText(tr("Play")); + menucommand->setHelpText(tr("Start playback.")); + menucommand->setIcon(GMIconTheme::instance()->icon_play); + menucommand->disable(); + } + } + return 1; + } + + + + +long GMWindow::onCmdPause(FXObject*,FXSelector,void*){ + GMPlayerManager::instance()->pause(); + return 1; + } + +long GMWindow::onUpdPause(FXObject*sender,FXSelector,void*){ + if (GMPlayerManager::instance()->can_pause()) + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_SHOW),NULL); + else + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_HIDE),NULL); + return 1; + } + +long GMWindow::onCmdStop(FXObject*,FXSelector,void*){ + GMPlayerManager::instance()->stop(); + return 1; + } + +long GMWindow::onUpdStop(FXObject*sender,FXSelector,void*){ + if (GMPlayerManager::instance()->can_stop()) + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_ENABLE),NULL); + else + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_DISABLE),NULL); + return 1; + } + +long GMWindow::onCmdNext(FXObject*,FXSelector,void*){ + GMPlayerManager::instance()->next(); + return 1; + } + +long GMWindow::onUpdNext(FXObject*sender,FXSelector,void*){ + if (GMPlayerManager::instance()->can_next()) + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_ENABLE),NULL); + else + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_DISABLE),NULL); + return 1; + } + +long GMWindow::onCmdPrev(FXObject*,FXSelector,void*){ + GMPlayerManager::instance()->prev(); + return 1; + } + +long GMWindow::onUpdPrev(FXObject*sender,FXSelector,void*){ + if (GMPlayerManager::instance()->can_prev()) + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_ENABLE),NULL); + else + sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_DISABLE),NULL); + return 1; + } + + +long GMWindow::onCmdTimeSlider(FXObject*,FXSelector,void*ptr){ + FXint pos=(FXint)(FXival)ptr; + GMPlayerManager::instance()->seek(pos); + return 1; + } + +long GMWindow::onCmdVolume(FXObject*,FXSelector,void*ptr){ + FXint level = (FXint)(FXival)ptr; + GMPlayerManager::instance()->volume(level); + update_volume_display(level); + return 1; + } + +long GMWindow::onCmdVolumeButton(FXObject*,FXSelector sel,void*ptr){ + volumeslider->handle(this,FXSEL(FXSELTYPE(sel),0),ptr); + return 1; + } + +long GMWindow::onUpdVolumeButton(FXObject*sender,FXSelector,void*){ + if (GMPlayerManager::instance()->getPlayer()->opened()) + sender->handle(this,FXSEL(SEL_COMMAND,ID_ENABLE),NULL); + else + sender->handle(this,FXSEL(SEL_COMMAND,ID_DISABLE),NULL); + return 1; + } + +long GMWindow::onCmdRepeatAB(FXObject*,FXSelector,void*){ + GMPlayerManager::instance()->getPlayer()->setRepeatAB(); + return 1; + } + +long GMWindow::onUpdRepeatAB(FXObject*sender,FXSelector,void*){ + if (GMPlayerManager::instance()->getPlayer()->playing() && GMPlayerManager::instance()->getPlayer()->seekable()) { + const FXString state_off = tr("Repeat A-B"); + const FXString state_a = tr("Repeat A"); + const FXString state_b = tr("Repeat A-B"); + sender->handle(this,FXSEL(SEL_COMMAND,ID_ENABLE),NULL); + switch(GMPlayerManager::instance()->getPlayer()->getRepeatAB()){ + case REPEAT_AB_OFF: sender->handle(this,FXSEL(SEL_COMMAND,ID_UNCHECK),NULL); + sender->handle(this,FXSEL(SEL_COMMAND,ID_SETSTRINGVALUE),(void*)&state_off); + break; + case REPEAT_AB_A : sender->handle(this,FXSEL(SEL_COMMAND,ID_CHECK),NULL); + sender->handle(this,FXSEL(SEL_COMMAND,ID_SETSTRINGVALUE),(void*)&state_a); + break; + case REPEAT_AB_B : sender->handle(this,FXSEL(SEL_COMMAND,ID_CHECK),NULL); + sender->handle(this,FXSEL(SEL_COMMAND,ID_SETSTRINGVALUE),(void*)&state_b); + break; + } + } + else { + sender->handle(this,FXSEL(SEL_COMMAND,ID_DISABLE),NULL); + } + return 1; + } + + + +long GMWindow::onCmdRepeatOff(FXObject*,FXSelector,void*){ + GMPlayerManager::instance()->getPreferences().play_repeat=REPEAT_OFF; + return 1; + } + +long GMWindow::onUpdRepeatOff(FXObject*sender,FXSelector,void*){ + if (GMPlayerManager::instance()->getPreferences().play_repeat==REPEAT_OFF) + sender->handle(this,FXSEL(SEL_COMMAND,ID_CHECK),NULL); + else + sender->handle(this,FXSEL(SEL_COMMAND,ID_UNCHECK),NULL); + return 1; + } + +long GMWindow::onCmdRepeat(FXObject*,FXSelector,void*){ + GMPlayerManager::instance()->getPreferences().play_repeat=REPEAT_TRACK; + return 1; + } + +long GMWindow::onUpdRepeat(FXObject*sender,FXSelector,void*){ + if (GMPlayerManager::instance()->getPreferences().play_repeat==REPEAT_TRACK) + sender->handle(this,FXSEL(SEL_COMMAND,ID_CHECK),NULL); + else + sender->handle(this,FXSEL(SEL_COMMAND,ID_UNCHECK),NULL); + return 1; + } + + +long GMWindow::onCmdRepeatAll(FXObject*,FXSelector,void*){ + GMPlayerManager::instance()->getPreferences().play_repeat=REPEAT_ALL; + return 1; + } + +long GMWindow::onUpdRepeatAll(FXObject*sender,FXSelector,void*){ + if (GMPlayerManager::instance()->getPreferences().play_repeat==REPEAT_ALL) + sender->handle(this,FXSEL(SEL_COMMAND,ID_CHECK),NULL); + else + sender->handle(this,FXSEL(SEL_COMMAND,ID_UNCHECK),NULL); + return 1; + } + + +long GMWindow::onCmdShuffle(FXObject*,FXSelector,void*ptr){ + GMPlayerManager::instance()->getPreferences().play_shuffle = (FXbool)(FXival)(ptr); + return 1; + } + +long GMWindow::onUpdShuffle(FXObject*sender,FXSelector,void*){ + if (GMPlayerManager::instance()->getPreferences().play_shuffle) + sender->handle(this,FXSEL(SEL_COMMAND,ID_CHECK),NULL); + else + sender->handle(this,FXSEL(SEL_COMMAND,ID_UNCHECK),NULL); + return 1; + } + + +long GMWindow::onCmdHomePage(FXObject*,FXSelector,void*){ + if (!gm_open_browser("http://code.google.com/p/gogglesmm")){ + FXMessageBox::error(this,MBOX_OK,tr("Unable to launch webbrowser"),"Goggles Music Manager was unable to launch a webbrowser.\nPlease visit http://code.google.com/p/gogglesmm for the official homepage."); + } + return 1; + } + +long GMWindow::onCmdReportIssue(FXObject*,FXSelector,void*){ + if (!gm_open_browser("http://code.google.com/p/gogglesmm/issues/list")){ + FXMessageBox::error(this,MBOX_OK,tr("Unable to launch webbrowser"),"Goggles Music Manager was unable to launch a webbrowser.\nPlease visit http://code.google.com/p/gogglesmm/issues/list to report an issue."); + } + return 1; + } + +long GMWindow::onCmdJoinLastFM(FXObject*,FXSelector,void*){ + if (!gm_open_browser("https://www.last.fm/join/")){ + FXMessageBox::error(this,MBOX_OK,tr("Unable to launch webbrowser"),"Goggles Music Manager was unable to launch a webbrowser.\nPlease visit https://www.last.fm/join/"); + } + return 1; + } + +long GMWindow::onCmdJoinGMMLastFM(FXObject*,FXSelector,void*){ + if (!gm_open_browser("http://www.last.fm/group/Goggles+Music+Manager")){ + FXMessageBox::error(this,MBOX_OK,tr("Unable to launch webbrowser"),"Goggles Music Manager was unable to launch a webbrowser.\nPlease visit http://www.last.fm/group/Goggles+Music+Manager"); + } + return 1; + } + + + + +extern const FXchar gmfilepatterns[]; + +class GMOpenDialog : public FXDialogBox { +FXDECLARE(GMOpenDialog) +protected: + GMTextField * input; +protected: + GMOpenDialog(){} +private: + GMOpenDialog(const GMOpenDialog&); + GMOpenDialog &operator=(const GMOpenDialog&); +public: + enum { + ID_BROWSE = FXDialogBox::ID_LAST, + ID_LAST + }; +public: + long onCmdBrowse(FXObject*,FXSelector,void*); + long onCmdAccept(FXObject*,FXSelector,void*); +public: + GMOpenDialog(FXWindow*); + + FXString getFilename() const; + }; + +FXDEFMAP(GMOpenDialog) GMOpenDialogMap[]={ + FXMAPFUNC(SEL_COMMAND,GMOpenDialog::ID_BROWSE,GMOpenDialog::onCmdBrowse), + FXMAPFUNC(SEL_COMMAND,GMOpenDialog::ID_ACCEPT,GMOpenDialog::onCmdAccept) + }; + +FXIMPLEMENT(GMOpenDialog,FXDialogBox,GMOpenDialogMap,ARRAYNUMBER(GMOpenDialogMap)); + + +GMOpenDialog::GMOpenDialog(FXWindow*p) : FXDialogBox(p,fxtr("Play File or Stream"),DECOR_TITLE|DECOR_BORDER,0,0,0,0,4,4,4,4) { + FXHorizontalFrame *closebox=new FXHorizontalFrame(this,LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X|PACK_UNIFORM_WIDTH,0,0,0,0,0,0,0,0); + new GMButton(closebox,tr("&Play"),NULL,this,FXDialogBox::ID_ACCEPT,BUTTON_INITIAL|BUTTON_DEFAULT|LAYOUT_RIGHT|FRAME_RAISED|FRAME_THICK,0,0,0,0, 15,15); + new GMButton(closebox,tr("&Cancel"),NULL,this,FXDialogBox::ID_CANCEL,BUTTON_DEFAULT|LAYOUT_RIGHT|FRAME_RAISED|FRAME_THICK,0,0,0,0, 15,15); + new FXSeparator(this,SEPARATOR_GROOVE|LAYOUT_FILL_X|LAYOUT_SIDE_BOTTOM); + + FXVerticalFrame * vframe = new FXVerticalFrame(this,LAYOUT_FILL_X|LAYOUT_FILL_Y); + new FXLabel(vframe,tr("Please specify a file or url to play:"),NULL); + + FXHorizontalFrame *inputframe=new FXHorizontalFrame(vframe,LAYOUT_FILL_X,0,0,0,0,0,0,0,0); + input = new GMTextField(inputframe,40,NULL,0,TEXTFIELD_NORMAL|LAYOUT_FILL_X|LAYOUT_CENTER_Y); + new GMButton(inputframe,tr("…"),NULL,this,ID_BROWSE); + input->setText(getApp()->reg().readStringEntry("GMOpenDialog","url",NULL)); + } + + +long GMOpenDialog::onCmdBrowse(FXObject*,FXSelector,void*){ + GMFileDialog filedialog(this,tr("Select File")); + if (!input->getText().empty()) + filedialog.setFilename(input->getText()); + filedialog.setPatternList(gmfilepatterns); + if (filedialog.execute()) + input->setText(filedialog.getFilename()); + return 1; + } + +long GMOpenDialog::onCmdAccept(FXObject*sender,FXSelector sel,void*ptr){ + getApp()->reg().writeStringEntry("GMOpenDialog","url",input->getText().text()); + return FXDialogBox::onCmdAccept(sender,sel,ptr); + } + +FXString GMOpenDialog::getFilename() const { + return input->getText(); + } + + +long GMWindow::onCmdOpen(FXObject*,FXSelector,void*){ + GMOpenDialog dialog(this); + if (dialog.execute()) { + FXString filename = dialog.getFilename(); + GMPlayerManager::instance()->open(filename); + } + return 1; + } + + +long GMWindow::onCmdSleepTimer(FXObject*,FXSelector,void*ptr){ + if (((FXint)(FXival)ptr)==1) { + FXDialogBox dialog(this,tr("Sleep Timer"),DECOR_TITLE|DECOR_BORDER,0,0,0,0,0,0,0,0,0,0); + create_dialog_header(&dialog,tr("Setup sleep timer"),tr("Stop playback within a certain time"),NULL); + FXHorizontalFrame *closebox=new FXHorizontalFrame(&dialog,LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X,0,0,0,0); + new GMButton(closebox,tr("&Start Timer"),NULL,&dialog,FXDialogBox::ID_ACCEPT,BUTTON_INITIAL|BUTTON_DEFAULT|LAYOUT_RIGHT|FRAME_RAISED|FRAME_THICK,0,0,0,0, 15,15); + new GMButton(closebox,tr("&Cancel"),NULL,&dialog,FXDialogBox::ID_CANCEL,BUTTON_DEFAULT|LAYOUT_RIGHT|FRAME_RAISED|FRAME_THICK,0,0,0,0, 15,15); + new FXSeparator(&dialog,SEPARATOR_GROOVE|LAYOUT_FILL_X|LAYOUT_SIDE_BOTTOM); + + FXHorizontalFrame * main = new FXHorizontalFrame(&dialog,LAYOUT_FILL_X,0,0,0,0,40,40,10,10); + new FXLabel(main,tr("Sleep in"),NULL,LABEL_NORMAL|LAYOUT_CENTER_Y); + GMSpinner* hours = new GMSpinner(main,2,NULL,0,FRAME_SUNKEN|FRAME_THICK); + new FXLabel(main,tr("hours and"),NULL,LABEL_NORMAL|LAYOUT_CENTER_Y); + GMSpinner* minutes = new GMSpinner(main,2,NULL,0,FRAME_SUNKEN|FRAME_THICK); + new FXLabel(main,tr("minutes."),NULL,LABEL_NORMAL|LAYOUT_CENTER_Y); + minutes->setRange(0,59); + hours->setRange(0,24); + + minutes->setValue(FXCLAMP(0,getApp()->reg().readIntEntry("player","sleeptimer-minutes",0),59)); + hours->setValue(FXCLAMP(0,getApp()->reg().readIntEntry("player","sleeptimer-hours",2),24)); + + if (dialog.execute()) { + GMPlayerManager::instance()->setSleepTimer(TIME_HOUR(hours->getValue())+TIME_MIN(minutes->getValue())); + getApp()->reg().writeIntEntry("player","sleeptimer-minutes",minutes->getValue()); + getApp()->reg().writeIntEntry("player","sleeptimer-hours",hours->getValue()); + } + } + else { + GMPlayerManager::instance()->setSleepTimer(0); + } + return 1; + } + +long GMWindow::onUpdSleepTimer(FXObject*sender,FXSelector,void*){ + if (GMPlayerManager::instance()->hasSleepTimer()) + sender->handle(this,FXSEL(SEL_COMMAND,ID_CHECK),NULL); + else + sender->handle(this,FXSEL(SEL_COMMAND,ID_UNCHECK),NULL); + return 1; + } + + + +extern const char *albumartfiles[]; + + + +void GMWindow::loadCover(const FXString & filename) { + FXIconSource src(getApp()); + FXImage * cover=NULL; + FXint coverdisplaysize; + FXString dirname=filename; + + const FXint smallcoversize=64; + + /// Whether to scale the image when loading or not + if (coverview_gl) + coverdisplaysize=0; + else + coverdisplaysize=FXCLAMP(64,GMPlayerManager::instance()->getPreferences().gui_coverdisplay_size,500); + + /// Check if we've already loaded it. + if (filename==coverfile) { + return; + } + + /// If empty skip cover searching + if (!filename.empty()) { + cover = GMCover::toImage(GMCover::fromTag(filename,coverdisplaysize)); + if (cover==NULL) { + dirname = FXPath::directory(filename); + if (!dirname.empty()) { + if (dirname==coverfile) + return; + cover= GMCover::toImage(GMCover::fromPath(dirname,coverdisplaysize)); + } + } + } + + /// Delete current cover + if (remote) + remote->updateCover(NULL); + + if (cover_small) { + FXImage * img = cover_small.release(); + delete img; + } + + if (coverview_x11) { + if (coverview_x11->getImage()){ + delete coverview_x11->getImage(); + coverview_x11->setImage(NULL); + } + } + + /// Set new cover to coverview + if (coverview_gl) + coverview_gl->setImage(cover); + else + coverview_x11->setImage(cover); + + if (cover) { + coverfile = dirname; + + /// Retrieve Image Data + FXColor * imagedata = cover->getData(); + + /// Disown image data + FXImageOwner::clear(cover); + + /// Make sure to create cover for x11 + if (coverview_x11) + cover->create(); + + /// Make small cover the owner of the data + cover_small = new FXImage(getApp(),imagedata,IMAGE_SHMI|IMAGE_SHMP|IMAGE_KEEP|IMAGE_OWNED,cover->getWidth(),cover->getHeight()); + if (cover_small->getWidth()>smallcoversize || cover_small->getHeight()>smallcoversize) { + if (cover_small->getWidth()>cover_small->getHeight()) + cover_small->scale(smallcoversize,(smallcoversize*cover_small->getHeight())/cover_small->getWidth(),1); + else + cover_small->scale((smallcoversize*cover_small->getWidth())/cover_small->getHeight(),smallcoversize,1); + } + + /// delete the cover for gl. + if (coverview_gl) + delete cover; + + cover_small->create(); + + if (remote) + remote->updateCover(cover_small); + + if (!coverframe->shown()) { + coverframe->show(); + coverframe->recalc(); + } + } + else { + coverfile = FXString::null; + if (coverframe->shown()) { + coverframe->hide(); + coverframe->recalc(); + } + } + } + + + +void GMWindow::create_dialog_header(FXDialogBox * dialog,const FXString & title,const FXString & subtitle,FXIcon * icon) { + FXHorizontalFrame * header = new FXHorizontalFrame(dialog,LAYOUT_FILL_X,0,0,0,0,0,0,0,0,0,0); + header->setBackColor(getApp()->getBackColor()); + FXLabel * label = new FXLabel(header,FXString::null,icon,LABEL_NORMAL|LAYOUT_CENTER_Y,0,0,0,0,10,0,0,0); + label->setBackColor(getApp()->getBackColor()); + FXVerticalFrame * frame = new FXVerticalFrame(header,LAYOUT_FILL_X,0,0,0,0,0,0,0,0,0,0); + label = new FXLabel(frame,title,NULL,LAYOUT_FILL_X|JUSTIFY_LEFT|TEXT_AFTER_ICON,0,0,0,0,10,10,10,0); + label->setBackColor(getApp()->getBackColor()); + label->setFont(font_thick); + label = new FXLabel(frame,subtitle,NULL,LAYOUT_FILL_X|JUSTIFY_LEFT|TEXT_AFTER_ICON,0,0,0,0,10+30,10,0,10); + label->setBackColor(getApp()->getBackColor()); + new FXSeparator(dialog,LAYOUT_FILL_X|SEPARATOR_GROOVE|LAYOUT_SIDE_TOP); + } + +void GMWindow::updateCoverView() { + + if (GMPlayerManager::instance()->getPreferences().gui_show_opengl_coverview && gm_has_opengl() ) { + + if (coverview_x11) { + loadCover(FXString::null); + delete coverview_x11; + coverview_x11=NULL; + } + + if (!coverview_gl) { +#if FOXVERSION < FXVERSION(1,7,15) + glvisual = new FXGLVisual(getApp(),VISUAL_DOUBLEBUFFER); +#else + glvisual = new FXGLVisual(getApp(),VISUAL_DOUBLE_BUFFER); +#endif + coverview_gl = new GMImageView(coverframe,glvisual,LAYOUT_FILL_X|LAYOUT_FILL_Y); + coverview_gl->setTarget(this); + coverview_gl->setSelector(ID_COVERVIEW); + coverview_gl->enable(); + if (coverframe->id()) coverview_gl->create(); + } + + } + else { + + if (coverview_gl) { + loadCover(FXString::null); + delete coverview_gl; + coverview_gl=NULL; + delete glvisual; + glvisual=NULL; + } + + if (!coverview_x11) { + coverview_x11 = new FXImageFrame(coverframe,NULL,FRAME_NONE|JUSTIFY_CENTER_X|JUSTIFY_CENTER_Y|LAYOUT_FILL_X|LAYOUT_FILL_Y,0,0,0,0,0,0,0,0); + coverview_x11->setBackColor(getApp()->getBackColor()); + coverview_x11->setTarget(this); + coverview_x11->setSelector(ID_COVERVIEW); + coverview_x11->enable(); + if (coverframe->id()) coverview_x11->create(); + } + + } + } + + +long GMWindow::onCmdCoverView(FXObject*,FXSelector,void*ptr){ + FXEvent * event = reinterpret_cast(ptr); + if (!event->moved) { + GMMenuPane pane(this); + if (coverview_x11) { + if (gm_has_opengl()) { + new GMMenuCheck(&pane,"Use OpenGL viewer",this,ID_CHANGE_COVERVIEW); + new FXMenuSeparator(&pane); + } + new GMMenuRadio(&pane,"Small Cover",this,ID_COVERSIZE_SMALL); + new GMMenuRadio(&pane,"Medium Cover",this,ID_COVERSIZE_MEDIUM); + new GMMenuRadio(&pane,"Large Cover",this,ID_COVERSIZE_LARGE); + new GMMenuRadio(&pane,"Extra Large Cover",this,ID_COVERSIZE_EXTRALARGE); + } + else { + new GMMenuCheck(&pane,"Use X11 viewer",this,ID_CHANGE_COVERVIEW); + } + gm_set_window_cursor(&pane,getApp()->getDefaultCursor(DEF_ARROW_CURSOR)); + pane.create(); + + ewmh_change_window_type(&pane,WINDOWTYPE_POPUP_MENU); + pane.popup(NULL,event->root_x+3,event->root_y+3); + getApp()->runPopup(&pane); + return 1; + } + return 0; + } + +long GMWindow::onCmdChangeCoverView(FXObject*,FXSelector,void*){ + GMPlayerManager::instance()->getPreferences().gui_show_opengl_coverview=!GMPlayerManager::instance()->getPreferences().gui_show_opengl_coverview; + updateCoverView(); + if (GMPlayerManager::instance()->playing()) + GMPlayerManager::instance()->update_cover_display(); + return 1; + } + +long GMWindow::onCmdCoverSize(FXObject*,FXSelector sel,void*){ + switch(FXSELID(sel)){ + case ID_COVERSIZE_SMALL : GMPlayerManager::instance()->getPreferences().gui_coverdisplay_size=128; break; + case ID_COVERSIZE_MEDIUM : GMPlayerManager::instance()->getPreferences().gui_coverdisplay_size=256; break; + case ID_COVERSIZE_LARGE : GMPlayerManager::instance()->getPreferences().gui_coverdisplay_size=384; break; + case ID_COVERSIZE_EXTRALARGE : GMPlayerManager::instance()->getPreferences().gui_coverdisplay_size=512; break; + } + loadCover(FXString::null); + if (GMPlayerManager::instance()->playing()) + GMPlayerManager::instance()->update_cover_display(); + return 1; + } + +long GMWindow::onUpdCoverSize(FXObject*sender,FXSelector sel,void*){ + FXbool check=false; + switch(FXSELID(sel)){ + case ID_COVERSIZE_SMALL : check = (GMPlayerManager::instance()->getPreferences().gui_coverdisplay_size<=128); break; + case ID_COVERSIZE_MEDIUM : check = ((GMPlayerManager::instance()->getPreferences().gui_coverdisplay_size>128) && (GMPlayerManager::instance()->getPreferences().gui_coverdisplay_size<384)); break; + case ID_COVERSIZE_LARGE : check = ((GMPlayerManager::instance()->getPreferences().gui_coverdisplay_size>=384) && (GMPlayerManager::instance()->getPreferences().gui_coverdisplay_size<512)); break; + case ID_COVERSIZE_EXTRALARGE : check = (GMPlayerManager::instance()->getPreferences().gui_coverdisplay_size>=512); break; + } + if (check) + sender->handle(this,FXSEL(SEL_COMMAND,ID_CHECK),NULL); + else + sender->handle(this,FXSEL(SEL_COMMAND,ID_UNCHECK),NULL); + return 1; + } + +#if FOXVERSION > FXVERSION(1,7,21) +long GMWindow::onCmdShowSources(FXObject*,FXSelector,void*){ + if (mainsplitter->getExpanded()==HIDESOURCES) { + if (!coverfile.empty()) + mainsplitter->setExpanded(SHOWSOURCES_COVER); + else + mainsplitter->setExpanded(SHOWSOURCES); + } + else + mainsplitter->setExpanded(HIDESOURCES); + return 1; + } + +long GMWindow::onUpdShowSources(FXObject*sender,FXSelector,void*){ + FXuint exp = mainsplitter->getExpanded(); + if (exp==SHOWSOURCES || exp==SHOWSOURCES_COVER) + sender->handle(this,FXSEL(SEL_COMMAND,ID_CHECK),NULL); + else + sender->handle(this,FXSEL(SEL_COMMAND,ID_UNCHECK),NULL); + return 1; + } +#endif + diff --git a/src/GMWindow.h b/src/GMWindow.h new file mode 100644 index 0000000..4937d96 --- /dev/null +++ b/src/GMWindow.h @@ -0,0 +1,285 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#ifndef GMWINDOW_H +#define GMWINDOW_H + +class GMRemote; +class GMTrackView; +class GMSourceView; +class GMIconTheme; +class GMPreferencesDialog; +class GMImageView; +class GMCoverFrame; + +enum { + DISPLAYMODE_UNDEFINED=-1, + DISPLAYMODE_NORMAL=0, + DISPLAYMODE_LIST=1, + }; + + +enum { + SHOW_NORMAL, + SHOW_WIZARD, + SHOW_TRAY + }; + +class GMWindow : public FXMainWindow { + FXDECLARE(GMWindow) +friend class GMRemote; +friend class GMPlayerManager; +friend class GMTrackView; +friend class GMPreferencesDialog; +private: + FXMenuPtr filemenu; + FXMenuPtr editmenu; + FXMenuPtr viewmenu; + FXMenuPtr playmenu; + FXMenuPtr helpmenu; + FXMenuButton *volumebutton; + FXPopup *volumecontrol; + FXSlider *volumeslider; +private: + GMIconTheme * icontheme; + GMTrackView * trackview; + GMSourceView * sourceview; + FXHorizontalFrame * controlframe; + FXHorizontalFrame * statusframe; + GMCoverFrame * coverframe; + GMImageView * coverview_gl; + FXImageFrame * coverview_x11; + FXGLVisual * glvisual; + FXToggleButton * playpausebutton; + FXButton * stopbutton; + FXButton * nextbutton; + FXButton * prevbutton; +#if FOXVERSION > FXVERSION(1,7,11) + FXMenuCheck * fullscreencheck; +#endif + FXSeparator * controlseparator; + FXDragCorner * controldragcorner; + FXSeparator * controlstatusseparator; + FX7Segment * timelabel; + FXSlider * timeslider; +#if FOXVERSION > FXVERSION(1,7,21) + FX4Splitter * mainsplitter; +#else + FXSplitter * mainsplitter; + FXSplitter * sourcesplitter; +#endif + FXStatusBar * statusbar; + FXTextField * nowplaying; + GMRemote * remote; +private: + FXString coverfile; +private: + FXFontPtr font_thick; + FXImagePtr cover_small; +private: + void createFonts(); + void configureToolbar(FXbool docktop,FXbool showlabels,FXbool bigicons,FXbool init=false); + void configureStatusbar(FXbool show); +#if FOXVERSION > FXVERSION(1,7,11) + void setFullScreen(FXbool show); +#endif +private: + GMWindow(){} + GMWindow(const GMWindow&); + GMWindow& operator=(const GMWindow&); +public: /// Message Handlers + long onCmdAbout(FXObject*,FXSelector,void*); + long onCmdQuit(FXObject*,FXSelector,void*); + long onCmdPreferences(FXObject*,FXSelector,void*); + + long onCmdTimeSlider(FXObject*,FXSelector,void*); + long onCmdVolume(FXObject*,FXSelector,void*); + long onCmdVolumeButton(FXObject*,FXSelector,void*); + long onUpdVolumeButton(FXObject*,FXSelector,void*); + + long onCmdOpen(FXObject*,FXSelector,void*); + + long onCmdImport(FXObject*,FXSelector,void*); + long onCmdImportFiles(FXObject*,FXSelector,void*); + long onCmdShowTrack(FXObject*,FXSelector,void*); + long onUpdShowTrack(FXObject*,FXSelector,void*); + +#if FOXVERSION >= FXVERSION(1,7,11) + long onCmdShowFullScreen(FXObject*,FXSelector,void*); +#endif + +#if FOXVERSION > FXVERSION(1,7,21) + long onCmdShowSources(FXObject*,FXSelector,void*); + long onUpdShowSources(FXObject*,FXSelector,void*); +#endif + + long onCmdShowMiniPlayer(FXObject*,FXSelector,void*); + long onUpdShowMiniPlayer(FXObject*,FXSelector,void*); + + long onCmdPlayPause(FXObject*,FXSelector,void*); + long onUpdPlayPause(FXObject*,FXSelector,void*); + long onCmdPlayPauseMenu(FXObject*,FXSelector,void*); + long onUpdPlayPauseMenu(FXObject*,FXSelector,void*); + long onCmdPause(FXObject*,FXSelector,void*); + long onUpdPause(FXObject*,FXSelector,void*); + long onCmdStop(FXObject*,FXSelector,void*); + long onUpdStop(FXObject*,FXSelector,void*); + long onCmdNext(FXObject*,FXSelector,void*); + long onUpdNext(FXObject*,FXSelector,void*); + long onCmdPrev(FXObject*,FXSelector,void*); + long onUpdPrev(FXObject*,FXSelector,void*); + long onCmdRepeatAll(FXObject*,FXSelector,void*); + long onUpdRepeatAll(FXObject*,FXSelector,void*); + long onCmdRepeatAB(FXObject*,FXSelector,void*); + long onUpdRepeatAB(FXObject*,FXSelector,void*); + long onCmdRepeatOff(FXObject*,FXSelector,void*); + long onUpdRepeatOff(FXObject*,FXSelector,void*); + long onCmdRepeat(FXObject*,FXSelector,void*); + long onUpdRepeat(FXObject*,FXSelector,void*); + long onCmdSleepTimer(FXObject*,FXSelector,void*); + long onUpdSleepTimer(FXObject*,FXSelector,void*); + long onCmdShuffle(FXObject*,FXSelector,void*); + long onUpdShuffle(FXObject*,FXSelector,void*); + long onCmdHomePage(FXObject*,FXSelector,void*); + long onCmdReportIssue(FXObject*,FXSelector,void*); + long onCmdJoinLastFM(FXObject*,FXSelector,void*); + long onCmdJoinGMMLastFM(FXObject*,FXSelector,void*); + + long onCmdResetColors(FXObject*,FXSelector,void*); + long onCmdChangeCoverView(FXObject*,FXSelector,void*); + long onCmdCoverView(FXObject*,FXSelector,void*); + long onCmdCoverSize(FXObject*,FXSelector,void*); + long onUpdCoverSize(FXObject*,FXSelector,void*); + +public: + enum{ + ID_ABOUT=FXMainWindow::ID_LAST, + ID_QUIT, + + ID_OPEN, + + ID_HOMEPAGE, + ID_REPORT_ISSUE, + ID_JOIN_LASTFM, + ID_JOIN_GMM_LASTFM, + + ID_PAUSE, + ID_PLAYPAUSE, + ID_PLAYPAUSEMENU, + ID_STOP, + ID_NEXT, + ID_PREV, + + ID_REPEAT, + ID_REPEAT_ALL, + ID_REPEAT_AB, + ID_REPEAT_OFF, + ID_SHUFFLE, + + ID_TIMESLIDER, + ID_VOLUME_BUTTON, + ID_VOLUME_SLIDER, + + ID_DISPLAYMODE, + + ID_DATABASE_CLEAR, + + ID_IMPORT_DIRS, + ID_IMPORT_FILES, + ID_SYNC_DIRS, + + + ID_PREFERENCES, + ID_SHOW_TRACK, +#if FOXVERSION >= FXVERSION(1,7,11) + ID_SHOW_FULLSCREEN, + ID_SHOW_SOURCES, +#endif + ID_SHOW_MINIPLAYER, + + ID_OPEN_DIR, + ID_RESET_COLORS, + + ID_DDE_MESSAGE, + ID_SLEEP, + + ID_COVERVIEW, + ID_CHANGE_COVERVIEW, + ID_COVERSIZE_SMALL, + ID_COVERSIZE_MEDIUM, + ID_COVERSIZE_LARGE, + ID_COVERSIZE_EXTRALARGE, + + ID_LAST + }; +public: + GMWindow(FXApp* a,FXObject*tgt,FXSelector sel); + + void showRemote(); + + void hideRemote(); + + GMRemote * getRemote() const { return remote; } + + void updateCoverView(); + + + void create_dialog_header(FXDialogBox * dialog,const FXString & title,const FXString & label,FXIcon * icon=NULL); + + FXbool question(const FXString & title,const FXString & label,const FXString & accept,const FXString & cancel); + + + void reset(); + void display(const GMTrack&); + + + void init(FXuint); + + + void toggleShown(); + + /// Create window + virtual void create(); + + virtual void show(); + + virtual void hide(); + + GMTrackView * getTrackView() const { return trackview; } + GMSourceView * getSourceView() const {return sourceview;} + + + void loadCover(const FXString & filename); + + + void update_elapsed_time(FXint hours,FXint minutes,FXint seconds,FXint position,FXbool playing,FXbool seekable); + + void update_volume_display(FXint level); + + + FXFont * getThickFont() const { return font_thick; } + + FXImage * getSmallCover() const { return cover_small; } + + void layoutToolBarButtons(); + + /// Destructor + virtual ~GMWindow(); + }; + +#endif diff --git a/src/ap_buffer.cpp b/src/ap_buffer.cpp new file mode 100644 index 0000000..214dddf --- /dev/null +++ b/src/ap_buffer.cpp @@ -0,0 +1,168 @@ +#include "gmdefs.h" +#include "ap_buffer.h" + +#define ROUNDVAL 16 +#define ROUNDUP(n) (((n)+ROUNDVAL-1)&-ROUNDVAL) + +namespace ap { + +MemoryBuffer::MemoryBuffer(FXival cap) : + buffer(NULL), + buffersize(0), + rdptr(NULL), + wrptr(NULL) { + reserve(cap); + } + +MemoryBuffer::MemoryBuffer(const MemoryBuffer & other) : + buffer(NULL), + buffersize(0), + rdptr(NULL), + wrptr(NULL) { + reserve(other.capacity()); + append(other.data(),other.size()); + } + +MemoryBuffer::~MemoryBuffer() { + freeElms(buffer); + buffersize=0; + wrptr=rdptr=NULL; + } + +// Assignment operator +MemoryBuffer& MemoryBuffer::operator=(const MemoryBuffer& other) { + clear(); + reserve(other.capacity()); + append(other.data(),other.size()); + return *this; + } + +// Append operator +MemoryBuffer& MemoryBuffer::operator+=(const MemoryBuffer& other) { + reserve(other.capacity()); + append(other.data(),other.size()); + return *this; + } + + +void MemoryBuffer::adopt(MemoryBuffer & other) { + + // Take over + buffer=other.buffer; + buffersize=other.buffersize; + rdptr=other.rdptr; + wrptr=other.wrptr; + + // Reset Other + other.buffer=other.rdptr=other.wrptr=NULL; + other.buffersize=0; + } + + +// Clear +void MemoryBuffer::clear() { + wrptr=rdptr=buffer; + } + +// Clear and reset +void MemoryBuffer::reset(FXival nbytes) { + clear(); + if (buffersize!=nbytes) { + buffersize=nbytes; + resizeElms(buffer,buffersize); + wrptr=rdptr=buffer; + } + } + + +// Make room for needed bytes +void MemoryBuffer::reserve(FXival needed) { + if (needed>0) { + if (buffer) { + FXival avail = space(); + + /// Check if we can move back rdptr/wrptr + if (availbuffer) { + if (wrptr>rdptr) { + memmove(buffer,rdptr,wrptr-rdptr); + wrptr-=(rdptr-buffer); + rdptr = buffer; + avail = space(); + } + else { + wrptr=rdptr=buffer; + avail=buffersize; + } + } + } + + /// Still not enough space, resize buffer + if (avail=1); + reserve(nbytes); + while(nbytes--) *wrptr++=c; + } + + +FXival MemoryBuffer::read(void * b, FXival nbytes) { + //FXASSERT(nbytes(wrptr); } + FXchar * s8() { return reinterpret_cast(wrptr); } + FXshort * s16() { return reinterpret_cast(wrptr); } + FXint * s32() { return reinterpret_cast(wrptr); } + + /// Return pointer to buffer + FXuchar* data() { return (FXuchar*)rdptr; } + const FXuchar* data() const { return (const FXuchar*)rdptr; } + + void setReadPosition(const FXuchar *p) { rdptr=(FXchar*)p; } + + // Destructor + ~MemoryBuffer(); + }; +} + +#endif + diff --git a/src/ap_http.cpp b/src/ap_http.cpp new file mode 100644 index 0000000..66e0619 --- /dev/null +++ b/src/ap_http.cpp @@ -0,0 +1,1016 @@ +#include "gmdefs.h" +#include "ap_buffer.h" +#include "ap_http.h" + + +#ifndef WIN32 +#include // for close() +#include +#include +#include +#include +#include // for getaddrinfo() +#endif + +#ifndef MSG_NOSIGNAL +#define MSG_NOSIGNAL 0 +#endif + +#if FOXVERSION < FXVERSION(1,7,0) +#define APIntVal(x) FXIntVal((x)) +#define APStringVal FXStringVal +#else +#define APIntVal(x) ((x).toInt()) +#define APStringVal FXString::value +#endif + +#ifndef BadHandle +#ifdef WIN32 +#define BadHandle INVALID_HANDLE_VALUE +#else +#define BadHandle -1 +#endif +#endif + + +using namespace ap; + +namespace ap { + + +FXint HttpStatus::type() const { + if (code>=100 && code<200) + return HTTP_RESPONSE_INFORMATIONAL; + else if (code>=200 && code<300) + return HTTP_RESPONSE_SUCCESS; + else if (code>=300 && code<400) + return HTTP_RESPONSE_REDIRECT; + else if (code>=400 && code<500) + return HTTP_RESPONSE_CLIENT_ERROR; + else if (code>=500 && code<600) + return HTTP_RESPONSE_SERVER_ERROR; + else + return HTTP_RESPONSE_FAILED; + } + + +HttpResponse::HttpResponse() : + flags(0), + content_length(-1), + chunk_remaining(-1) { + } + +HttpResponse::~HttpResponse() { + clear(); + } + +void HttpResponse::clear() { + flags=0; + content_length=-1; + chunk_remaining=-1; + clear_headers(); + } + + +// Read (at most) nbytes from buffer or source +FXival HttpResponse::read(FXchar*ptr,FXival nbytes) { + FXival nread=0; + while(nbytes) { + if (buffer.size()) { + FXival n = buffer.read(ptr,nbytes); + nread+=n; + nbytes-=n; + ptr+=n; + } + else { + FXival n = readBlock(ptr,nbytes); + if (n<=0) return (nread>0) ? nread : n; + nread+=n; + nbytes-=n; + ptr+=n; + } + } + return nread; + } + +// Fill buffer with (at most) nbytes +FXival HttpResponse::fill(FXival nbytes) { + buffer.reserve(nbytes); + FXival nread = readBlock((FXchar*)buffer.ptr(),nbytes); + if (nread>0) { + //fxmessage("buf: \"%s\"\n",buffer.data()); + buffer.wroteBytes(nread); + } + return nread; + } + + +// Try parsing one HTTP header from buffer. If succesfull, returns +// headers. Mode can either be HEADER_SINGLE_LINE or HEADER_MULTIPLE_LINES, +// depending on whether headers may span multiple lines or not. +FXbool HttpResponse::parse_header(FXString & line,FXuint mode) { + FXint len=0,i,j,l; + for (i=0;i0) line.assign((FXchar*)buffer.data(),i); + buffer.readBytes(i+2); + return true; + } + + // check if header continues on the next line + if (len>0) { + + // need more bytes + if ((i+2)>=buffer.size()) + return false; + + /// header continues on next line + if (buffer[i+2]==' ' || buffer[i+2]=='\t') + continue; + } + + /// copy string + line.length(len); + for (j=0,l=0;ladopt(value); + headers.replace(key.text(),v); + } + } + +void HttpResponse::clear_headers() { + for (FXint pos=headers.first();pos<=headers.last();pos=headers.next(pos)) { + FXString * field = (FXString*) headers.data(pos); + delete field; + } + headers.clear(); + } + +void HttpResponse::check_headers() { + FXString * field = NULL; + + field = (FXString*) headers.find("content-length"); + if (field) + content_length = APIntVal(*field); + + field = (FXString*) headers.find("transfer-encoding"); + if (field && field->contains("chunked") ) + flags|=ChunkedResponse; + + field = (FXString*) headers.find("connection"); + if (field && comparecase(*field,"close")==0) + flags|=ConnectionClose; + +#ifdef DEBUG + for (FXint pos=headers.first();pos<=headers.last();pos=headers.next(pos)) { + fxmessage("%s: %s\n",headers.key(pos),((FXString*)headers.data(pos))->text()); + } +#endif + } + + + + +// Read a chunk header and set the chunksize +FXbool HttpResponse::read_chunk_header(FXint & chunksize) { + FXString header; + FXchar clrf[2]; + + // We've read a previous chunk + if (chunksize==0) { + if (read(clrf,2)!=2 || clrf[0]!='\r' || clrf[1]!='\n') { + fxwarning("http: missing line feed: %c%c\n",clrf[0],clrf[1]); + return false; + } + } + + if (read_header(header,HEADER_SINGLE_LINE)) { + if (header.scan("%x",&chunksize)==1) { + return true; + } + } + return false; + } + + +// Read status line and set status. +FXbool HttpResponse::read_status() { + FXString header; + if (read_header(header,HEADER_SINGLE_LINE)) { + if (header.scan("HTTP/%d.%d %d",&status.major,&status.minor,&status.code)==3 || + header.scan("ICY %d",&status.code)==1){ + //fxmessage("Code: %d \nVersion: %d.%d\n",status.code,status.major,status.minor); + return true; + } + } + return false; + } + +// Read header +FXbool HttpResponse::read_header(FXString & header,FXuint mode) { + while(!parse_header(header,mode)) { + if (fill()==-1) + return false; + } + return true; + } + + + + +// Read the full message body non-chunked +FXString HttpResponse::read_body() { + FXString body; + if (content_length==0) { + return FXString::null; + } + else if (content_length>0) { + body.length(content_length); + FXival n = read(&body[0],content_length); + if (n0) pos+=n; + } + while(n==BLOCK); + + if (pos>0) + body.length(pos); + } + return body; + } + +// Read the full message body chunked +FXString HttpResponse::read_body_chunked() { + FXString header; + FXString body; + FXint chunksize=-1; + + if (read_chunk_header(chunksize)) { + + while(chunksize) { + + // Resize buffer + body.length(body.length()+chunksize); + + // Anything less than chunksize is an error + if (read(&body[body.length()-chunksize],chunksize)0) { + allocElms(body,content_length); + FXival n = read(body,content_length); + if (n!=content_length) { + freeElms(body); + body=NULL; + return -1; + } + return content_length; + } + else if (content_length<0) { + const FXint BLOCK=4096; + FXint size = 0; + FXival len = 0; + FXival n; + do { + resizeElms(body,size+BLOCK); + size+=BLOCK; + n = read(body+len,BLOCK); + if (n>0) len+=n; + } + while(n==BLOCK); + return len; + } + else { + return 0; + } + } + + +FXival HttpResponse::read_body_chunked(FXchar *& body) { + FXString header; + FXint size=0; + FXint chunksize=-1; + + body = NULL; + + if (read_chunk_header(chunksize)) { + + while(chunksize) { + + // Resize buffer + size+=chunksize; + resizeElms(body,size); + + // Anything less than chunksize is an error + if (read(body+size-chunksize,chunksize)= 0) { + FXchar * data = (FXchar*)ptr; + FXival n = read(data,FXMIN(content_length,len)); + if (n>0) content_length-=n; + return n; + } + else { + return read((FXchar*)ptr,len); + } + } + + +FXival HttpResponse::read_body_chunked(void * ptr,FXival len) { + FXchar * data = (FXchar*)ptr; + FXString header; + FXival nbytes=0; + while(len) { + + if (chunk_remaining<=0) { + + if (!read_chunk_header(chunk_remaining)) + return -1; + + if (chunk_remaining==0) { + + // Trailing Headers + while(read_header(header,HEADER_MULTIPLE_LINES)) { + + // empty header indicates end of headers + if (header.empty()) + break; + + insert_header(header); + } + + return nbytes; + } + } + + FXival n = read(data+nbytes,FXMIN(len,chunk_remaining)); + if (n<=0) return nbytes; + chunk_remaining-=n; + nbytes+=n; + len-=n; + } + return nbytes; + } + + + + + + + + + + + + + + + + + +// Parse the response status and headers. Returns true if succesful +FXint HttpResponse::parse() { + FXString header; + if (read_status()) { + while(read_header(header,HEADER_MULTIPLE_LINES)) { + // empty header indicates end of headers + if (header.empty()) { + check_headers(); + return status.type(); + } + insert_header(header); + } + } + return HTTP_RESPONSE_FAILED; + } + +// Read the full message body into string +FXString HttpResponse::body() { + if (flags&HeadRequest) + return FXString::null; + else if (flags&ChunkedResponse) + return read_body_chunked(); + else + return read_body(); + } + +// Read the full message body into buffer +FXival HttpResponse::body(FXchar *& out) { + if (flags&HeadRequest) + return 0; + else if (flags&ChunkedResponse) + return read_body_chunked(out); + else + return read_body(out); + } + + +FXival HttpResponse::readBody(void * ptr,FXival len) { + if (flags&HeadRequest) + return 0; + else if (flags&ChunkedResponse) + return read_body_chunked(ptr,len); + else + return read_body(ptr,len); + } + + +void HttpResponse::discard() { + if (!(flags&ConnectionClose) && !(flags&HeadRequest)) { + FXchar buffer[1024]; + while(readBody(buffer,1024)==1024) ; + } + } + +FXString HttpResponse::getHeader(const FXString & key) const { + FXString * value = (FXString*)headers.find(key.text()); + if (value) + return (*value); + else + return FXString::null; + } + + +FXint HttpResponse::getContentLength() const { + return content_length; + } + + + + + + + + + + +HttpHost::HttpHost() : port(0) { + } + +HttpHost::HttpHost(const FXString & url) { + set(url); + } + +void HttpHost::clear() { + name.clear(); + port=0; + } + +FXbool HttpHost::set(const FXString & url) { + FXString nn = GMURL::host(url); + FXint np = GMURL::port(url); + if (np==0) np=80; + + if (name!=nn || port!=np) { + name.adopt(nn); + port=np; + return true; + } + return false; + } + + + + +HttpClient::HttpClient() : device(BadHandle) { + } + + +HttpClient::~HttpClient() { + close(); + } + +void HttpClient::close() { + if (device!=BadHandle) { + shutdown(device,SHUT_RDWR); + ::close(device); + device=BadHandle; + } + } + +void HttpClient::discard() { + if (flags&ConnectionClose) { + close(); + } + else { + HttpResponse::discard(); + } + } + +#ifndef SOCK_NONBLOCK +static FXbool ap_set_nonblocking(FXint fd) { + FXint flags; + + flags = fcntl(fd, F_GETFL); + if (flags==-1) return false; + + flags |= O_NONBLOCK; + + if (fcntl(fd,F_SETFL,flags)==-1) + return false; + + return true; + } +#endif + +#ifndef SOCK_CLOEXEC +static FXbool ap_set_closeonexec(FXint fd) { + FXint flags; + + flags = fcntl(fd, F_GETFD); + if (flags==-1) return false; + + flags |= FD_CLOEXEC; + + if (fcntl(fd,F_SETFD,flags)==-1) + return false; + + return true; + } +#endif + + +#if defined(SO_NOSIGPIPE) +static FXbool ap_set_nosignal(FXint fd) { + int nosignal=1; + socklen_t len=sizeof(nosignal); + if (setsockopt(fd,SOL_SOCKET,SO_NOSIGPIPE,&nosignal,len)==0) + return true; + else + return false; + } +#else +static FXbool ap_set_nosignal(FXint) { + // will try to use MSG_NOSIGNAL instead... + return true; + } +#endif + +static FXbool ap_set_timeout(FXInputHandle handle,FXint timeout) { + struct timeval tv; + + memset(&tv,0,sizeof(struct timeval)); + + tv.tv_sec = timeout; + + // Receiving + if (setsockopt(handle,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(struct timeval))) + return false; + + // Sending + if (setsockopt(handle,SOL_SOCKET,SO_SNDTIMEO,&tv,sizeof(struct timeval))) + return false; + + return true; + } + + + + + + + + + + + + + + +static FXInputHandle ap_create_socket(FXint domain, FXint type, FXint protocol,FXbool nonblocking,FXuint timeout=10) { + FXInputHandle device = BadHandle; + + // On linux 2.6.27 we can pass additional socket options + int opts=0; + +#ifdef SOCK_CLOEXEC + opts|=SOCK_CLOEXEC; +#endif + +#ifdef SOCK_NONBLOCK + if (nonblocking) + opts|=SOCK_NONBLOCK; +#endif + + device = socket(domain,type|opts,protocol); + if (device==BadHandle) + return BadHandle; + +#ifndef SOCK_CLOEXEC + if (!ap_set_closeonexec(device)){ + ::close(device); + return BadHandle; + } +#endif + +#ifndef SOCK_NONBLOCK + if (nonblocking && !ap_set_nonblocking(device)){ + ::close(device); + return BadHandle; + } +#endif + + // Don't want signals + if (!ap_set_nosignal(device)) { + ::close(device); + return BadHandle; + } + + // In case of blocking sockets, set a timeout + if (!nonblocking && !ap_set_timeout(device,timeout)) { + ::close(device); + return BadHandle; + } + + return device; + } + + +FXbool HttpClient::open_connection() { + struct addrinfo hints; + struct addrinfo * list=NULL; + struct addrinfo * item=NULL; + FXint result; + + + memset(&hints,0,sizeof(struct addrinfo)); + hints.ai_family=AF_UNSPEC; + hints.ai_socktype=SOCK_STREAM; + hints.ai_flags|=(AI_NUMERICSERV|AI_ADDRCONFIG); + + + if (flags&UseProxy) + result=getaddrinfo(proxy.name.text(),APStringVal(proxy.port).text(),&hints,&list); + else + result=getaddrinfo(server.name.text(),APStringVal(server.port).text(),&hints,&list); + + if (result) + return false; + + for (item=list;item;item=item->ai_next){ + + device = ap_create_socket(item->ai_family,item->ai_socktype,item->ai_protocol,(flags&UseNonBlock)); + if (device == BadHandle) + continue; + + if (connect(device,item->ai_addr,item->ai_addrlen)==0){ + freeaddrinfo(list); + return true; + } + + // In case of non-blocking we need to wait for the socket to become ready to write. + if (errno==EINPROGRESS || errno==EINTR || errno==EWOULDBLOCK) { + if (wait_write(device)) { + int socket_error=0; + socklen_t socket_length=sizeof(socket_error); + if (getsockopt(device,SOL_SOCKET,SO_ERROR,&socket_error,&socket_length)==0 && socket_error==0){ + freeaddrinfo(list); + return true; + } + } + else { + ::close(device); + device=BadHandle; + freeaddrinfo(list); + return false; + } + } + + // Failed, try some other one. + ::close(device); + device=BadHandle; + } + + if (list) + freeaddrinfo(list); + return false; + } + + +FXival HttpClient::writeBlock(const void * data,FXival count) { + FXival nwritten=-1; + do{ + nwritten=::write(device,data,count); + } + while(nwritten<0 && errno==EINTR); + return nwritten; + } + +FXival HttpClient::readBlock(void * data,FXival count) { + FXival nread=-1; + do{ + nread=::read(device,data,count); + } + while(nread<0 && errno==EINTR); + return nread; + } + + +FXbool HttpClient::send(const FXchar * data,FXint len) { + do { + FXival n = writeBlock(data,len); + if (n<=0) return false; + data += n; + len -= n; + } + while(len); + return true; + } + + + +void HttpClient::reset(FXbool forceclose){ + if (forceclose) + close(); + else + discard(); + + clear(); + } + + +FXbool HttpClient::request(const FXchar * method,const FXString & url,const FXString & headers,const FXString & body) { + FXString command,path,query; + + // Set Server Host + FXbool host_changed = server.set(url); + if (flags&UseProxy) + host_changed = false; + + // Reset Client + reset(host_changed); + + if (compare(method,"HEAD")==0) { + flags|=HeadRequest; + } + + // Open connection if necessary + if (device==BadHandle && !open_connection()){ + server.clear(); + return false; + } + + // Extract path and query + path = GMURL::path(url); + if (path.empty()) + path = "/"; + + query = GMURL::query(url); + if (!query.empty()) + path += "?" + query; + + +// fxmessage("path: %s\n",path.text()); + + // Method + Path +// if (flags&HttpProxy) +// command = method + url + "\r\n" + //else + command = method; + command += " " + path + " HTTP/1.1\r\n"; + + // Add Host + command += "Host: " + server.name + "\r\n"; + + // Add Content Length + if (body.length()) + command += "Content-Length: " + APStringVal(body.length()) + "\r\n"; + + // Additional headers + command+=headers; + + // End of headers + command += "\r\n"; + + // Add body + if (body.length()) + command += body; + + // Send Command + return send(command.text(),command.length()); + } + + + +static FXString ap_encode_base64(const FXString & source) { + const FXchar base64[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + const FXuchar * in = (const FXuchar*)source.text(); + FXint remaining = source.length() % 3; + FXint length = source.length() - remaining; + FXint n=0; + FXString out; + + out.length(4*(source.length()/3)); + + for (int i=0;i>2)]; + out[n++]=base64[((in[i]&0x3)<<4)|(in[i+1]>>4)]; + out[n++]=base64[((in[i+1]&0xf)<<2)|(in[i+2]>>6)]; + out[n++]=base64[(in[i+2]&0x3f)]; + } + + if (remaining) { + out[n++]=base64[(in[length]>>2)]; + if (remaining>1) { + out[n++]=base64[((in[length]&0x3)<<4)|(in[length+1]>>4)]; + out[n++]=base64[((in[length+1]&0xf)<<2)|in[length+2]>>6]; + out[n++]='='; + } + else { + out[n++]=base64[((in[length]&0x3)<<4)]; + out[n++]='='; + out[n++]='='; + } + } + return out; + } + + + + +FXbool HttpClient::basic(const FXchar* method, + FXString url, + const FXString & headers, + const FXString & body) { + + if (request(method,url,headers,body)) { + do { + switch(parse()) { + case HTTP_RESPONSE_INFORMATIONAL: + { + if (status.code==HTTP_CONTINUE) { + continue; + } + break; + } + case HTTP_RESPONSE_REDIRECT : + { + // 304 - Document not changed. We're done + // 305 - Need to use a proxy. Currently not handled + // 306 - Unused + if (status.code==HTTP_NOT_MODIFIED || status.code==HTTP_USE_PROXY || status.code==306) + return true; + + url = getHeader("location"); + + // No url given, done here + if (url.empty()) + return false; + + // Don't do automatic redirections for non GET/HEAD requests + if (comparecase(method,"GET") && comparecase(method,"HEAD")) + return true; + + if (!request(method,url,headers,body)) { + return false; + } + continue; + break; + } + case HTTP_RESPONSE_CLIENT_ERROR : + { + if (status.code==HTTP_UNAUTHORIZED) { + + FXString user = GMURL::username(url); + FXString password = GMURL::password(url); + + if (user.empty() || password.empty()) + return true; + + FXString challenge = getHeader("www-authenticate"); + + if (comparecase(challenge,"basic",5)==0) { + FXString auth = "Authorization: Basic " + ap_encode_base64(user+":"+password) + "\r\n"; + + if (!request(method,url,headers+auth,body)) { + return false; + } + continue; + } +// else if (comparecase(challenge,"digest",6)==0){ +// FXASSERT(0); +// } + } + } break; + + case HTTP_RESPONSE_FAILED: /* something went wrong */ + { +#ifdef DEBUG + fxmessage("HttpClient::basic() - Response Failed:\n"); + FXString b((const FXchar*)buffer->data(),buffer->size()); + fxmessage("%s\n",b.text()); +#endif + return false; + break; + } + + + default: break; + } + return true; + } + while(1); + } + return false; + } + + + + +} + + + + + diff --git a/src/ap_http.h b/src/ap_http.h new file mode 100644 index 0000000..5c2d588 --- /dev/null +++ b/src/ap_http.h @@ -0,0 +1,285 @@ +#ifndef AP_HTTP_H +#define AP_HTTP_H + +#ifndef GMAPI +#define GMAPI +#endif + +namespace ap { + +enum { + // HTTP status types + HTTP_RESPONSE_FAILED = 0, + HTTP_RESPONSE_INFORMATIONAL = 1, + HTTP_RESPONSE_SUCCESS = 2, + HTTP_RESPONSE_REDIRECT = 3, + HTTP_RESPONSE_CLIENT_ERROR = 4, + HTTP_RESPONSE_SERVER_ERROR = 5, + + + // Informational 1xx + HTTP_CONTINUE = 100, + HTTP_SWITCHING_PROTOCOLS = 101, + HTTP_PROCESSING = 102, // RFC2518 - WebDAV + + // Succesful 2xx + HTTP_OK = 200, + HTTP_CREATED = 201, + HTTP_ACCEPTED = 202, + HTTP_NONAUTHORITATIVE_INFORMATION = 203, + HTTP_NO_CONTENT = 204, + HTTP_RESET_CONTENT = 205, + HTTP_PARTIAL_CONTENT = 206, + HTTP_MULTI_STATUS = 207, //WEBDAV + HTTP_IM_USED = 226, //RFC3229 + + // Redirect 3xx + HTTP_MULTIPLE_CHOICES = 300, + HTTP_MOVED_PERMANENTLY = 301, + HTTP_FOUND = 302, + HTTP_SEE_OTHER = 303, + HTTP_NOT_MODIFIED = 304, + HTTP_USE_PROXY = 305, + HTTP_TEMPORARY_REDIRECT = 307, + + // Client Error 4xx + HTTP_BADREQUEST = 400, + HTTP_UNAUTHORIZED = 401, + HTTP_PAYMENTREQUIRED = 402, + HTTP_FORBIDDEN = 403, + HTTP_NOT_FOUND = 404, + HTTP_METHOD_NOT_ALLOWED = 405, + HTTP_NOT_ACCEPTABLE = 406, + HTTP_PROXY_AUTHENTICATION_REQUIRED = 407, + HTTP_REQUEST_TIMEOUT = 408, + HTTP_CONFLICT = 409, + HTTP_GONE = 410, + HTTP_LENGTH_REQUIRED = 411, + HTTP_PRECONDITION_FAILED = 412, + HTTP_REQUESTENTITY_TOO_LARGE = 413, + HTTP_REQUESTURI_TOO_LONG = 414, + HTTP_UNSUPPORTED_MEDIATYPE = 415, + HTTP_REQUESTED_RANGE_NOT_SATISFIABLE = 416, + HTTP_EXPECTATION_FAILED = 417, + HTTP_UNPROCESSABLE_ENTITY = 422, + HTTP_LOCKED = 423, + HTTP_FAILED_DEPENDENCY = 424, //RFC4918 - WEBDAV + HTTP_NO_CODE = 425, + HTTP_UPGRADE_REQUIRED = 426, + + // Server Error 5xx + HTTP_INTERNAL_SERVER_ERROR = 500, + HTTP_NOT_IMPLEMENTED = 501, + HTTP_BAD_GATEWAY = 502, + HTTP_SERVICE_UNAVAILABLE = 503, + HTTP_GATEWAY_TIMEOUT = 504, + HTTP_VERSION_NOTSUPPORTED = 505, + HTTP_INSUFFICIENT_STORAGE = 507, //RFC4918 - WEBDAV + HTTP_BANDWITH_LIMIT_EXCEEDED = 509, + HTTP_NOT_EXTENDED = 510 //RFC 2774 + }; + + +enum { + HEADER_MULTIPLE_LINES = 0, + HEADER_SINGLE_LINE = 1 + }; + + +/* Http Status */ +struct GMAPI HttpStatus { + FXint major; + FXint minor; + FXint code; + FXint type() const; + }; + +/* Http Response Parser */ +class GMAPI HttpResponse { +private: + MemoryBuffer buffer; // Internal Buffer +protected: + FXuint flags; // Options flags used by parser + FXint content_length; // Content Length from header + FXint content_remaining; // Content left to read + FXint chunk_remaining; // Remaining bytes left to read in chunk +public: + HttpStatus status; // Response Status. Valid after response() returns true + FXDict headers; // Dictionary of all http headers +protected: + + // Internal Parser Flags + enum { + ChunkedResponse = 0x1, // Chunked Response + ConnectionClose = 0x2, // Connection Closes + ResponseComplete = 0x4, + HeadRequest = 0x8, + Last = 0x10 + }; + +protected: + + /// read nbytes from source. + virtual FXival readBlock(void * ptr,FXival nbytes)=0; + +private: + + /// read nbytes + FXival read(FXchar*ptr,FXival nbytes); + + /// read (at most) nbytes into buffer + FXival fill(FXival nbytes=128); + +private: + + // Try reading complete header from buffer + FXbool parse_header(FXString & header,FXuint span); + + // Add header to dictionary + void insert_header(const FXString &); + + // Check all headers we're interested in + void check_headers(); + + // Clear Headers + void clear_headers(); + +private: + + // Reads chunk header and gives back size. + FXbool read_chunk_header(FXint & chunksize); + + // Read regular + FXbool read_header(FXString & header,FXuint span); + + // Read status line + FXbool read_status(); + +private: + + // Read body using normal transfer. Returns a string + FXString read_body(); + + // Read body using chunked transfer. Returns a string + FXString read_body_chunked(); + + + // Read body using normal transfer. Returns size of message body or -1. + FXival read_body(FXchar*&); + + // Read body using chunked transfer. Returns size of message body or -1. + FXival read_body_chunked(FXchar*&); + + + // Read body using chunked transfer + FXival read_body_chunked(void * ptr,FXival len); + + // Read body using normal transfer + FXival read_body(void * ptr,FXival len); + +protected: + + // Constructor + HttpResponse(); + + // Clear Response + void clear(); + +public: + + // Completes reading response + virtual void discard(); + + // Read response status and headers. + FXint parse(); + + // Return the complete message body as string + FXString body(); + + // Return the complete message body as buffer. Returns body size or -1 on error. + // Buffer needs to be freed with freeElms() + FXival body(FXchar *& out); + + + /// Read partial body + FXival readBody(void*ptr,FXival len); + + + // Return header for given key + FXString getHeader(const FXString & key) const; + + // Return Content Length if known or -1 + FXint getContentLength() const; + + // Destructor + virtual ~HttpResponse(); + }; + + + +struct HttpHost { + FXString name; + FXint port; + + HttpHost(); + HttpHost(const FXString & url); + + // Set from url. returns true if changed + FXbool set(const FXString & url); + + // Clear + void clear(); + }; + + + + + + + +class GMAPI HttpClient : public HttpResponse { +protected: + FXInputHandle device; +protected: + HttpHost server; + HttpHost proxy; +protected: + enum { + UseProxy = 0x10, // Keep in Sync with HttpResponse + UseNonBlock = 0x20, // Non Blocking Sockets + }; +protected: + virtual FXival readBlock(void*,FXival); + virtual FXival writeBlock(const void*,FXival); + + // Subclass for non-blocking IO + virtual FXbool wait_write(FXInputHandle) { return false; } +protected: + FXbool send(const FXchar *,FXint len); + FXbool open_connection(); + + // Reset Response + void reset(FXbool forceclose); +public: + HttpClient(); + + void close(); + + // Discard response + virtual void discard(); + + FXbool request(const FXchar * method,const FXString & url,const FXString & headers=FXString::null,const FXString & message=FXString::null); + + + // Perform basic request and handles some basic HTTP features + FXbool basic(const FXchar * method, + FXString url, + const FXString & headers=FXString::null, + const FXString & body=FXString::null); + + ~HttpClient(); + }; + +} + +#endif diff --git a/src/ap_xml_parser.cpp b/src/ap_xml_parser.cpp new file mode 100644 index 0000000..1db9e86 --- /dev/null +++ b/src/ap_xml_parser.cpp @@ -0,0 +1,67 @@ +#include "gmdefs.h" +#include "ap_xml_parser.h" + +#include + +namespace ap { + +XMLStream::XMLStream() : parser(NULL), depth(1),skip(0) { + parser = XML_ParserCreate(NULL); + XML_SetUserData((XML_Parser)parser,this); + XML_SetElementHandler((XML_Parser)parser,xml_element_start,xml_element_end); + XML_SetCharacterDataHandler((XML_Parser)parser,xml_element_data); + } + +XMLStream::~XMLStream() { + XML_ParserFree((XML_Parser)parser); + } + + +void XMLStream::xml_print_error() { + fxmessage("Parse Error (line %ld, column %ld): %s\n",XML_GetCurrentLineNumber((XML_Parser)parser),XML_GetCurrentColumnNumber((XML_Parser)parser),XML_ErrorString(XML_GetErrorCode((XML_Parser)parser))); + } + +FXbool XMLStream::parse(const FXchar * buffer,FXint length) { + XML_Status code = XML_Parse((XML_Parser)parser,buffer,length,1); + if (code==XML_STATUS_ERROR) { + xml_print_error(); + return false; + } + return true; + } + +FXbool XMLStream::parse(const FXString & buffer) { + return parse(buffer.text(),buffer.length()); + } + +void XMLStream::xml_element_start(void*ptr,const FXchar * element,const FXchar ** attributes) { + XMLStream * stream = reinterpret_cast(ptr); + if (!stream->skip) { + if (!stream->begin(element,attributes)) { + stream->skip=stream->depth; + } + } + stream->depth++; + } + +void XMLStream::xml_element_end(void*ptr,const FXchar * element) { + XMLStream * stream = reinterpret_cast(ptr); + + if (!stream->skip) + stream->end(element); + + stream->depth--; + + // turn off skip + if (stream->skip==stream->depth) + stream->skip=0; + } + +void XMLStream::xml_element_data(void*ptr,const FXchar * data,FXint len) { + XMLStream * stream = reinterpret_cast(ptr); + stream->data(data,len); + } + + + +} diff --git a/src/ap_xml_parser.h b/src/ap_xml_parser.h new file mode 100644 index 0000000..3b4f1a9 --- /dev/null +++ b/src/ap_xml_parser.h @@ -0,0 +1,29 @@ +#ifndef AP_XML_PARSER_H +#define AP_XML_PARSER_H + +namespace ap { + +class XMLStream { +protected: + void * parser; + FXint depth; + FXint skip; +private: + static void xml_element_start(void*,const FXchar*,const FXchar**); + static void xml_element_end(void*,const FXchar*); + static void xml_element_data(void*,const FXchar*,FXint); +protected: + virtual FXint begin(const FXchar *,const FXchar**) { return 1;} + virtual void data(const FXchar *,FXint) {} + virtual void end(const FXchar *){} + void xml_print_error(); +public: + XMLStream(); + FXbool parse(const FXchar * buffer,FXint length); + FXbool parse(const FXString & buffer); + virtual ~XMLStream(); + }; + +} +#endif + diff --git a/src/fxext.cpp b/src/fxext.cpp new file mode 100644 index 0000000..e7ecf0e --- /dev/null +++ b/src/fxext.cpp @@ -0,0 +1,2265 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2009-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#include "gmdefs.h" +#include "fxext.h" +#include "GMPlayerManager.h" +#include "GMPreferences.h" + +#if FOXVERSION < FXVERSION(1,7,0) +#define gm_make_hilite_color makeHiliteColor +#define gm_make_shadow_color makeShadowColor +#else + +// Get highlight color +static FXColor gm_make_hilite_color(FXColor clr){ + FXuint r,g,b; + r=FXREDVAL(clr); + g=FXGREENVAL(clr); + b=FXBLUEVAL(clr); + r=FXMAX(31,r); + g=FXMAX(31,g); + b=FXMAX(31,b); + r=(133*r)/100; + g=(133*g)/100; + b=(133*b)/100; + r=FXMIN(255,r); + g=FXMIN(255,g); + b=FXMIN(255,b); + return FXRGB(r,g,b); + } + +// Get shadow color +static FXColor gm_make_shadow_color(FXColor clr){ + FXuint r,g,b; + r=FXREDVAL(clr); + g=FXGREENVAL(clr); + b=FXBLUEVAL(clr); + r=(66*r)/100; + g=(66*g)/100; + b=(66*b)/100; + return FXRGB(r,g,b); + } + +#endif + + + + +// Fill vertical gradient rectangle +void fillVerticalGradient(FXDCWindow & dc,FXint x,FXint y,FXint w,FXint h,FXColor top,FXColor bottom){ + register FXint rr,gg,bb,dr,dg,db,r1,g1,b1,r2,g2,b2,yl,yh,yy,dy,n,t; + if(0n) n=t; + if((t=FXABS(db))>n) n=t; + n++; + if(n>h) n=h; + if(n>128) n=128; + rr=(r1<<16)+32767; + gg=(g1<<16)+32767; + bb=(b1<<16)+32767; + yy=32767; + dr=(dr<<16)/n; + dg=(dg<<16)/n; + db=(db<<16)/n; + dy=(h<<16)/n; + do{ + yl=yy>>16; + yy+=dy; + yh=yy>>16; + dc.setForeground(FXRGB(rr>>16,gg>>16,bb>>16)); + dc.fillRectangle(x,y+yl,w,yh-yl); + rr+=dr; + gg+=dg; + bb+=db; + } + while(yhn) n=t; + if((t=FXABS(db))>n) n=t; + n++; + if(n>w) n=w; + if(n>128) n=128; + rr=(r1<<16)+32767; + gg=(g1<<16)+32767; + bb=(b1<<16)+32767; + xx=32767; + dr=(dr<<16)/n; + dg=(dg<<16)/n; + db=(db<<16)/n; + dx=(w<<16)/n; + do{ + xl=xx>>16; + xx+=dx; + xh=xx>>16; + dc.setForeground(FXRGB(rr>>16,gg>>16,bb>>16)); + dc.fillRectangle(x+xl,y,xh-xl,h); + rr+=dr; + gg+=dg; + bb+=db; + } + while(xhgetShadowColor(); + } + + +FXIMPLEMENT(GMScrollHFrame,FXHorizontalFrame,NULL,0) + +GMScrollHFrame::GMScrollHFrame(){ + } + +GMScrollHFrame::GMScrollHFrame(FXComposite*p):FXHorizontalFrame(p,LAYOUT_FILL_X|LAYOUT_FILL_Y|FRAME_LINE,0,0,0,0,0,0,0,0,0,0){ + borderColor=getApp()->getShadowColor(); + } + + +FXIMPLEMENT(GMTabFrame,FXVerticalFrame,NULL,0) + +GMTabFrame::GMTabFrame(){ + } + +GMTabFrame::GMTabFrame(FXComposite*p):FXVerticalFrame(p,LAYOUT_FILL_X|LAYOUT_FILL_Y|FRAME_LINE,0,0,0,0){ + borderColor=getApp()->getShadowColor(); + } + + +FXIMPLEMENT(GMHeaderItem,FXHeaderItem,NULL,0) + + +// Map +FXDEFMAP(GMHeader) GMHeaderMap[]={ + FXMAPFUNC(SEL_PAINT,0,GMHeader::onPaint), + }; + + +// Object implementation +FXIMPLEMENT(GMHeader,FXHeader,GMHeaderMap,ARRAYNUMBER(GMHeaderMap)) + + +// Make a Header +GMHeader::GMHeader(){ + } + +// Make a Header +GMHeader::GMHeader(FXComposite* p,FXObject* tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h,FXint pl,FXint pr,FXint pt,FXint pb): + FXHeader(p,tgt,sel,opts,x,y,w,h,pl,pr,pt,pb){ + } + +// Handle repaint +long GMHeader::onPaint(FXObject*,FXSelector,void* ptr){ + FXEvent *ev=(FXEvent*)ptr; + FXDCWindow dc(this,ev); + register FXint x,y,w,h,i,ilo,ihi; + + // Set font + dc.setFont(font); + + // Paint background + + fillVerticalGradient(dc,ev->rect.x,0,ev->rect.w,height-1,gm_make_hilite_color(backColor),gm_make_hilite_color(gm_make_shadow_color(backColor))); + dc.setForeground(shadowColor); + dc.fillRectangle(ev->rect.x,height-1,ev->rect.w,1); + + // dc.fillRectangle(ev->rect.x,ev->rect.y,ev->rect.w,ev->rect.h); + + // Vertical + if(options&HEADER_VERTICAL){ + + // Determine affected items + ilo=getItemAt(ev->rect.y); + ihi=getItemAt(ev->rect.y+ev->rect.h); + + // Fragment below first item + if(ilo<0){ + y=pos; + if(0getPos(); + } + if(0=items.no()){ + y=pos; + if(0getPos()+items[items.no()-1]->getSize(); + } + if(ygetPos(); + h=items[i]->getSize(); + if(items[i]->isPressed()){ + if(options&FRAME_THICK) + drawDoubleSunkenRectangle(dc,0,y,width,h); + else if(options&FRAME_RAISED) + drawSunkenRectangle(dc,0,y,width,h); + } + else{ + if(options&FRAME_THICK) + drawDoubleRaisedRectangle(dc,0,y,width,h); + else if(options&FRAME_RAISED) + drawRaisedRectangle(dc,0,y,width,h); + } + ((GMHeaderItem*)items[i])->draw(this,dc,0,y,width,h); + } + } + + // Horizontal + else{ + + // Determine affected items + ilo=getItemAt(ev->rect.x); + ihi=getItemAt(ev->rect.x+ev->rect.w); + + // Fragment below first item + if(ilo<0){ + x=pos; + if(0getPos(); + } +/* + if(0=items.no()){ + x=pos; + if(0getPos()+items[items.no()-1]->getSize(); + } + if(xgetPos(); + w=items[i]->getSize(); + if(items[i]->isPressed()){ + + if (x>0) { + dc.setForeground(hiliteColor); + dc.fillRectangle(x,0,1,height); + } + + dc.setForeground(shadowColor); + dc.fillRectangle(x+w-1,0,1,height); + + + /* + if(options&FRAME_THICK) + drawDoubleSunkenRectangle(dc,x,0,w,height); + else if(options&FRAME_RAISED) + drawSunkenRectangle(dc,x,0,w,height); + + */ + } + else{ + if (x>0) { + + dc.setForeground(hiliteColor); + dc.fillRectangle(x,0,1,height); + } + + dc.setForeground(shadowColor); + dc.fillRectangle(x+w-1,0,1,height); + /* + if(options&FRAME_THICK) + drawDoubleRaisedRectangle(dc,x,0,w,height); + else if(options&FRAME_RAISED) + drawRaisedRectangle(dc,x,0,w,height); + + */ + } + ((GMHeaderItem*)items[i])->draw(this,dc,x,0,w,height); + } + } + return 1; + } + + + +FXIMPLEMENT(GMMenuCommand,FXMenuCommand,NULL,0) + +GMMenuCommand::GMMenuCommand(FXComposite* p,const FXString& text,FXIcon* ic,FXObject* tgt,FXSelector sel,FXuint opts) : FXMenuCommand(p,text,ic,tgt,sel,opts){ + backColor=getApp()->getBackColor(); + } + + +FXIMPLEMENT(GMMenuCheck,FXMenuCheck,NULL,0) + +GMMenuCheck::GMMenuCheck(FXComposite* p,const FXString& text,FXObject* tgt,FXSelector sel,FXuint opts) : FXMenuCheck(p,text,tgt,sel,opts){ + backColor=getApp()->getBackColor(); + } + + +FXIMPLEMENT(GMMenuRadio,FXMenuRadio,NULL,0) + +GMMenuRadio::GMMenuRadio(FXComposite* p,const FXString& text,FXObject* tgt,FXSelector sel,FXuint opts) : FXMenuRadio(p,text,tgt,sel,opts){ + backColor=getApp()->getBackColor(); + } + + +FXIMPLEMENT(GMMenuCascade,FXMenuCascade,NULL,0) + +GMMenuCascade::GMMenuCascade(FXComposite* p,const FXString& text,FXIcon* ic,FXPopup* pup,FXuint opts) : FXMenuCascade(p,text,ic,pup,opts){ + backColor=getApp()->getBackColor(); + } + +FXIMPLEMENT(GMMenuPane,FXMenuPane,NULL,0) + +GMMenuPane::GMMenuPane(FXWindow* owner,FXuint opts) : FXMenuPane(owner,opts) { + borderColor=getApp()->getShadowColor(); + setFrameStyle(FRAME_LINE); + } + + +FXDEFMAP(GMTextField) GMTextFieldMap[]={ + FXMAPFUNC(SEL_LEFTBUTTONPRESS,0,GMTextField::onLeftBtnPress) + }; + +FXIMPLEMENT(GMTextField,FXTextField,GMTextFieldMap,ARRAYNUMBER(GMTextFieldMap)) + +GMTextField::GMTextField(FXComposite* p,FXint ncols,FXObject* tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h,FXint pl,FXint pr,FXint pt,FXint pb) : FXTextField(p,ncols,tgt,sel,opts,x,y,w,h,pl,pr,pt,pb){ + borderColor=getApp()->getShadowColor(); + setFrameStyle(FRAME_LINE); + } + +FXbool GMTextField::extendWordSelection(FXint pos,FXbool /*notify*/) { + register FXint sp,ep; + pos=contents.validate(FXCLAMP(0,pos,contents.length())); + if(pos<=anchor){ + sp=wordStart(pos); + ep=wordEnd(anchor); + } + else{ + sp=wordStart(anchor); + ep=wordEnd(pos); + } + return setSelection(sp,ep-sp); + } + + + + + + + +// Pressed left button +long GMTextField::onLeftBtnPress(FXObject*,FXSelector,void* ptr){ + FXEvent* ev=(FXEvent*)ptr; + flags&=~FLAG_TIP; + handle(this,FXSEL(SEL_FOCUS_SELF,0),ptr); + if(isEnabled()){ + grab(); + if(target && target->tryHandle(this,FXSEL(SEL_LEFTBUTTONPRESS,message),ptr)) return 1; + flags&=~FLAG_UPDATE; + if(ev->click_count==1){ + setCursorPos(index(ev->win_x)); + if(ev->state&SHIFTMASK){ + extendSelection(cursor); + } + else{ + killSelection(); + setAnchorPos(cursor); + } + makePositionVisible(cursor); + flags|=FLAG_PRESSED; + } + else if (ev->click_count==2) { + setAnchorPos(cursor); + extendWordSelection(cursor,true); + } + else{ + setAnchorPos(0); + setCursorPos(contents.length()); + extendSelection(contents.length()); + makePositionVisible(cursor); + } + return 1; + } + return 0; + } + + +FXIMPLEMENT(GMSpinner,FXSpinner,NULL,0) + +GMSpinner::GMSpinner(FXComposite* p,FXint ncols,FXObject* tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h,FXint pl,FXint pr,FXint pt,FXint pb) : FXSpinner(p,ncols,tgt,sel,opts,x,y,w,h,pl,pr,pt,pb){ + borderColor=getApp()->getShadowColor(); + setFrameStyle(FRAME_LINE); + upButton->setFrameStyle(FRAME_NONE); + downButton->setFrameStyle(FRAME_NONE); + + upButton->setPadLeft(1); + upButton->setPadRight(1); + upButton->setPadTop(2); + upButton->setPadBottom(2); + downButton->setPadLeft(1); + downButton->setPadRight(1); + downButton->setPadTop(2); + downButton->setPadBottom(2); + } + + +FXDEFMAP(GMMenuTitle) GMMenuTitleMap[]={ + FXMAPFUNC(SEL_PAINT,0,GMMenuTitle::onPaint), + FXMAPFUNC(SEL_COMMAND,FXWindow::ID_POST,GMMenuTitle::onCmdPost), + }; + +FXIMPLEMENT(GMMenuTitle,FXMenuTitle,GMMenuTitleMap,ARRAYNUMBER(GMMenuTitleMap)) + +GMMenuTitle::GMMenuTitle(FXComposite* p,const FXString& text,FXIcon* ic,FXPopup* pup,FXuint opts) : FXMenuTitle(p,text,ic,pup,opts){ + selbackColor=getApp()->getSelbackColor(); + seltextColor=getApp()->getSelforeColor(); + } + + +// Handle repaint +long GMMenuTitle::onPaint(FXObject*,FXSelector,void* ptr){ + FXEvent *ev=(FXEvent*)ptr; + FXDCWindow dc(this,ev); + FXint xx,yy; + dc.setFont(font); + xx=6; + yy=0; + if(isEnabled()){ + if(isActive()){ + dc.setForeground(gm_make_shadow_color(selbackColor)); + +// dc.fillRectangle(0,height-1,width,1); + dc.fillRectangle(width-1,0,1,height); + dc.fillRectangle(0,0,width,1); + dc.fillRectangle(0,0,1,height); + + dc.setForeground(selbackColor); + dc.fillRectangle(1,1,width-2,height-1); + xx++; + yy++; + } + else if(underCursor()){ + dc.setForeground(backColor); + dc.fillRectangle(0,0,width,height); + } + else{ + dc.setForeground(backColor); + dc.fillRectangle(0,0,width,height); + } + if(icon){ + dc.drawIcon(icon,xx,yy+(height-icon->getHeight())/2); + xx+=5+icon->getWidth(); + } + if(!label.empty()){ + yy+=font->getFontAscent()+(height-font->getFontHeight())/2; + dc.setForeground(isActive() ? seltextColor : textColor); + dc.drawText(xx,yy,label); + if(0<=hotoff){ + dc.fillRectangle(xx+font->getTextWidth(&label[0],hotoff),yy+1,font->getTextWidth(&label[hotoff],wclen(&label[hotoff])),1); + } + } + } + else{ + dc.setForeground(backColor); + dc.fillRectangle(0,0,width,height); + if(icon){ + dc.drawIconSunken(icon,xx,yy+(height-icon->getHeight())/2); + xx+=5+icon->getWidth(); + } + if(!label.empty()){ + yy+=font->getFontAscent()+(height-font->getFontHeight())/2; + dc.setForeground(hiliteColor); + dc.drawText(xx+1,yy+1,label); + if(0<=hotoff){ + dc.fillRectangle(xx+font->getTextWidth(&label[0],hotoff),yy+1,font->getTextWidth(&label[hotoff],wclen(&label[hotoff])),1); + } + dc.setForeground(shadowColor); + dc.drawText(xx,yy,label); + if(0<=hotoff){ + dc.fillRectangle(xx+font->getTextWidth(&label[0],hotoff),yy+1,font->getTextWidth(&label[hotoff],wclen(&label[hotoff])),1); + } + } + } + return 1; + } + +// Post the menu +long GMMenuTitle::onCmdPost(FXObject*,FXSelector,void*){ + FXint x,y,side; + if(pane && !pane->shown()){ + translateCoordinatesTo(x,y,getRoot(),0,0); + side=getParent()->getLayoutHints(); + if(side&LAYOUT_SIDE_LEFT){ // Vertical + //y-=1; + if(side&LAYOUT_SIDE_BOTTOM){ // On right + x-=pane->getDefaultWidth(); + } + else{ // On left + x+=width; + } + } + else{ // Horizontal + //x-=1; + if(side&LAYOUT_SIDE_BOTTOM){ // On bottom + y-=pane->getDefaultHeight(); + } + else{ // On top + y+=height; + } + } + pane->popup(getParent(),x,y); + if(!getParent()->grabbed()) getParent()->grab(); + } + flags&=~FLAG_UPDATE; + flags|=FLAG_ACTIVE; + update(); + return 1; + } + + +FXDEFMAP(GMButton) GMButtonMap[]={ + FXMAPFUNC(SEL_PAINT,0,GMButton::onPaint) + }; + +FXIMPLEMENT(GMButton,FXButton,GMButtonMap,ARRAYNUMBER(GMButtonMap)) + + +GMButton::GMButton(){ + } + +GMButton::GMButton(FXComposite* p,const FXString& text,FXIcon* ic,FXObject* tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h,FXint pl,FXint pr,FXint pt,FXint pb) : FXButton(p,text,ic,tgt,sel,opts,x,y,w,h,pl,pr,pt,pb) { + } + +// Handle repaint +long GMButton::onPaint(FXObject*,FXSelector,void* ptr){ + FXint tw=0,th=0,iw=0,ih=0,tx,ty,ix,iy; + FXEvent*ev=(FXEvent*)ptr; + FXDCWindow dc(this,ev); + +// shadowColor = gm_make_shadow_color(baseColor); + FXColor top = gm_make_hilite_color(backColor); + FXColor bottom = gm_make_hilite_color(shadowColor); + FXColor shade = gm_make_hilite_color(shadowColor); + FXColor bordercolor = shadowColor; + + FXPoint basebackground[4]={FXPoint(0,0),FXPoint(width-1,0),FXPoint(0,height-1),FXPoint(width-1,height-1)}; + + FXPoint bordershade[16]={FXPoint(0,1),FXPoint(1,0),FXPoint(1,2),FXPoint(2,1), + FXPoint(width-2,0),FXPoint(width-1,1),FXPoint(width-3,1),FXPoint(width-2,2), + FXPoint(0,height-2),FXPoint(1,height-1),FXPoint(1,height-3),FXPoint(2,height-2), + FXPoint(width-1,height-2),FXPoint(width-2,height-1),FXPoint(width-2,height-3),FXPoint(width-3,height-2) + }; + FXPoint bordercorners[4]={FXPoint(1,1),FXPoint(1,height-2),FXPoint(width-2,1),FXPoint(width-2,height-2)}; + + if (options&BUTTON_TOOLBAR && (!underCursor() || !isEnabled())) { + dc.setForeground(baseColor); + dc.fillRectangle(0,0,width,height); + } + else if (state==STATE_UP && ((options&BUTTON_TOOLBAR)==0 || (options&BUTTON_TOOLBAR && underCursor()))) { + + /// Outside Background + dc.setForeground(baseColor); + dc.drawPoints(basebackground,4); + + /// Border + dc.setForeground(bordercolor); + dc.drawRectangle(2,0,width-5,0); + dc.drawRectangle(2,height-1,width-5,height-1); + dc.drawRectangle(0,2,0,height-5); + dc.drawRectangle(width-1,2,0,height-5); + dc.drawPoints(bordercorners,4); + dc.setForeground(shade); + dc.drawPoints(bordershade,16); + + fillVerticalGradient(dc,2,1,width-4,height-2,top,bottom); + dc.setForeground(top); + dc.drawRectangle(1,3,0,height-7); + dc.setForeground(bottom); + dc.drawRectangle(width-2,3,0,height-7); + } + else { + /// Outside Background + dc.setForeground(baseColor); + dc.drawPoints(basebackground,4); + + /// Border + dc.setForeground(bordercolor); + dc.drawRectangle(2,0,width-5,0); + dc.drawRectangle(2,height-1,width-5,height-1); + dc.drawRectangle(0,2,0,height-5); + dc.drawRectangle(width-1,2,0,height-5); + dc.drawPoints(bordercorners,4); + dc.setForeground(shade); + dc.drawPoints(bordershade,16); + + dc.setForeground(baseColor); + dc.fillRectangle(2,1,width-4,height-2); + + + //dc.setForeground(FXRGB(0xdc,0xd4,0xc9)); + //dc.fillRectangle(2,1,width-4,height-2); + } + + // Place text & icon + if(!label.empty()){ + tw=labelWidth(label); + th=labelHeight(label); + } + if(icon){ + iw=icon->getWidth(); + ih=icon->getHeight(); + } + + just_x(tx,ix,tw,iw); + just_y(ty,iy,th,ih); + + // Shift a bit when pressed + if(state && (options&(FRAME_RAISED|FRAME_SUNKEN))){ ++tx; ++ty; ++ix; ++iy; } + + // Draw enabled state + if(isEnabled()){ + if(icon){ + dc.drawIcon(icon,ix,iy); + } + if(!label.empty()){ + dc.setFont(font); + dc.setForeground(textColor); + drawLabel(dc,label,hotoff,tx,ty,tw,th); + } + if(hasFocus()){ + dc.drawFocusRectangle(border+1,border+1,width-2*border-2,height-2*border-2); + } + } + + // Draw grayed-out state + else{ + if(icon){ + dc.drawIconSunken(icon,ix,iy); + } + if(!label.empty()){ + dc.setFont(font); + dc.setForeground(hiliteColor); + drawLabel(dc,label,hotoff,tx+1,ty+1,tw,th); + dc.setForeground(shadowColor); + drawLabel(dc,label,hotoff,tx,ty,tw,th); + } + } + return 1; + } + + + + + + + + + + + + + + + +FXDEFMAP(GMToggleButton) GMToggleButtonMap[]={ + FXMAPFUNC(SEL_PAINT,0,GMToggleButton::onPaint) + }; + +FXIMPLEMENT(GMToggleButton,FXToggleButton,GMToggleButtonMap,ARRAYNUMBER(GMToggleButtonMap)) + + +GMToggleButton::GMToggleButton(){ + } + +GMToggleButton::GMToggleButton(FXComposite* p,const FXString& text1,const FXString& text2,FXIcon* ic1,FXIcon* ic2,FXObject* tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h,FXint pl,FXint pr,FXint pt,FXint pb) : FXToggleButton(p,text1,text2,ic1,ic2,tgt,sel,opts,x,y,w,h,pl,pr,pt,pb) { + } + +// Handle repaint +long GMToggleButton::onPaint(FXObject*,FXSelector,void* ptr){ + FXint tw=0,th=0,iw=0,ih=0,tx,ty,ix,iy; + FXEvent *ev=(FXEvent*)ptr; + FXDCWindow dc(this,ev); + + + FXColor top = gm_make_hilite_color(backColor); + FXColor bottom = gm_make_hilite_color(shadowColor); + FXColor shade = gm_make_hilite_color(shadowColor); + FXColor bordercolor = shadowColor; + + FXPoint basebackground[4]={FXPoint(0,0),FXPoint(width-1,0),FXPoint(0,height-1),FXPoint(width-1,height-1)}; + + FXPoint bordershade[16]={FXPoint(0,1),FXPoint(1,0),FXPoint(1,2),FXPoint(2,1), + FXPoint(width-2,0),FXPoint(width-1,1),FXPoint(width-3,1),FXPoint(width-2,2), + FXPoint(0,height-2),FXPoint(1,height-1),FXPoint(1,height-3),FXPoint(2,height-2), + FXPoint(width-1,height-2),FXPoint(width-2,height-1),FXPoint(width-2,height-3),FXPoint(width-3,height-2) + }; + FXPoint bordercorners[4]={FXPoint(1,1),FXPoint(1,height-2),FXPoint(width-2,1),FXPoint(width-2,height-2)}; + + + // Got a border at all? + if(options&(FRAME_RAISED|FRAME_SUNKEN)){ + + // Toolbar style + if(options&TOGGLEBUTTON_TOOLBAR){ + + // Enabled and cursor inside and down + if(down || ((options&TOGGLEBUTTON_KEEPSTATE) && state)){ + + + /// Outside Background + dc.setForeground(baseColor); + dc.drawPoints(basebackground,4); + + /// Border + dc.setForeground(bordercolor); + dc.drawRectangle(2,0,width-5,0); + dc.drawRectangle(2,height-1,width-5,height-1); + dc.drawRectangle(0,2,0,height-5); + dc.drawRectangle(width-1,2,0,height-5); + dc.drawPoints(bordercorners,4); + dc.setForeground(shade); + dc.drawPoints(bordershade,16); + + dc.setForeground(baseColor); + dc.fillRectangle(2,1,width-4,height-2); + } + + // Enabled and cursor inside, and up + else if(isEnabled() && underCursor()){ + /// Outside Background + dc.setForeground(baseColor); + dc.drawPoints(basebackground,4); + + /// Border + dc.setForeground(bordercolor); + dc.drawRectangle(2,0,width-5,0); + dc.drawRectangle(2,height-1,width-5,height-1); + dc.drawRectangle(0,2,0,height-5); + dc.drawRectangle(width-1,2,0,height-5); + dc.drawPoints(bordercorners,4); + dc.setForeground(shade); + dc.drawPoints(bordershade,16); + + fillVerticalGradient(dc,2,1,width-4,height-2,top,bottom); + dc.setForeground(top); + dc.drawRectangle(1,3,0,height-7); + dc.setForeground(bottom); + dc.drawRectangle(width-2,3,0,height-7); + } + + // Disabled or unchecked or not under cursor + else{ + dc.setForeground(backColor); + dc.fillRectangle(0,0,width,height); + } + } + + // Normal style + else{ + + // Draw sunken if pressed + if(down || ((options&TOGGLEBUTTON_KEEPSTATE) && state)){ + dc.setForeground(hiliteColor); + dc.fillRectangle(border,border,width-border*2,height-border*2); + if(options&FRAME_THICK) drawDoubleSunkenRectangle(dc,0,0,width,height); + else drawSunkenRectangle(dc,0,0,width,height); + } + + // Draw raised if not currently pressed down + else{ + dc.setForeground(backColor); + dc.fillRectangle(border,border,width-border*2,height-border*2); + if(options&FRAME_THICK) drawDoubleRaisedRectangle(dc,0,0,width,height); + else drawRaisedRectangle(dc,0,0,width,height); + } + + } + } + + // No borders + else{ + dc.setForeground(backColor); + dc.fillRectangle(0,0,width,height); + } + + // Place text & icon + if(state && !altlabel.empty()){ + tw=labelWidth(altlabel); + th=labelHeight(altlabel); + } + else if(!label.empty()){ + tw=labelWidth(label); + th=labelHeight(label); + } + if(state && alticon){ + iw=alticon->getWidth(); + ih=alticon->getHeight(); + } + else if(icon){ + iw=icon->getWidth(); + ih=icon->getHeight(); + } + + just_x(tx,ix,tw,iw); + just_y(ty,iy,th,ih); + + // Shift a bit when pressed + if((down || ((options&TOGGLEBUTTON_KEEPSTATE) && state)) && (options&(FRAME_RAISED|FRAME_SUNKEN))){ ++tx; ++ty; ++ix; ++iy; } + + // Draw enabled state + if(isEnabled()){ + if(state && alticon){ + dc.drawIcon(alticon,ix,iy); + } + else if(icon){ + dc.drawIcon(icon,ix,iy); + } + if(state && !altlabel.empty()){ + dc.setFont(font); + dc.setForeground(textColor); + drawLabel(dc,altlabel,althotoff,tx,ty,tw,th); + } + else if(!label.empty()){ + dc.setFont(font); + dc.setForeground(textColor); + drawLabel(dc,label,hotoff,tx,ty,tw,th); + } + if(hasFocus()){ + dc.drawFocusRectangle(border+1,border+1,width-2*border-2,height-2*border-2); + } + } + + // Draw grayed-out state + else{ + if(state && alticon){ + dc.drawIconSunken(alticon,ix,iy); + } + else if(icon){ + dc.drawIconSunken(icon,ix,iy); + } + if(state && !altlabel.empty()){ + dc.setFont(font); + dc.setForeground(hiliteColor); + drawLabel(dc,altlabel,althotoff,tx+1,ty+1,tw,th); + dc.setForeground(shadowColor); + drawLabel(dc,altlabel,althotoff,tx,ty,tw,th); + } + else if(!label.empty()){ + dc.setFont(font); + dc.setForeground(hiliteColor); + drawLabel(dc,label,hotoff,tx+1,ty+1,tw,th); + dc.setForeground(shadowColor); + drawLabel(dc,label,hotoff,tx,ty,tw,th); + } + } + return 1; + } + + + +FXIMPLEMENT(GMRadioButton,FXRadioButton,NULL,0) + + +GMRadioButton::GMRadioButton(){ + } + +GMRadioButton::GMRadioButton(FXComposite* p,const FXString& text,FXObject* tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h,FXint pl,FXint pr,FXint pt,FXint pb) : FXRadioButton(p,text,tgt,sel,opts,x,y,w,h,pl,pr,pt,pb) { + // borderColor=getApp()->getShadowColor(); + + borderColor=getApp()->getBackColor(); + hiliteColor=getApp()->getShadowColor(); + baseColor=getApp()->getBackColor(); + + + } + +FXDEFMAP(GMCheckButton) GMCheckButtonMap[]={ + FXMAPFUNC(SEL_PAINT,0,GMCheckButton::onPaint) + }; + +FXIMPLEMENT(GMCheckButton,FXCheckButton,GMCheckButtonMap,ARRAYNUMBER(GMCheckButtonMap)) + + +GMCheckButton::GMCheckButton(){ + } + +GMCheckButton::GMCheckButton(FXComposite* p,const FXString& text,FXObject* tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h,FXint pl,FXint pr,FXint pt,FXint pb) : FXCheckButton(p,text,tgt,sel,opts,x,y,w,h,pl,pr,pt,pb) { + } + +long GMCheckButton::onPaint(FXObject*,FXSelector,void* ptr){ + FXEvent *ev=(FXEvent*)ptr; + FXint tw=0,th=0,tx,ty,ix,iy; + FXDCWindow dc(this,ev); + + // Figure text size + if(!label.empty()){ + tw=labelWidth(label); + th=labelHeight(label); + } + + // Placement + just_x(tx,ix,tw,13); + just_y(ty,iy,th,13); + + // Widget background + dc.setForeground(backColor); + dc.fillRectangle(ev->rect.x,ev->rect.y,ev->rect.w,ev->rect.h); + + // Check background + if(check==MAYBE || !isEnabled()) + dc.setForeground(baseColor); + else + dc.setForeground(boxColor); + dc.fillRectangle(ix+2,iy+2,9,9); + + // Check border + if(options&CHECKBUTTON_PLUS){ + dc.setForeground(textColor); + dc.drawRectangle(ix+2,iy+2,8,8); + } + else{ + dc.setForeground(shadowColor); + dc.drawRectangle(ix+1,iy+1,10,10); + } + + // Check color + if(check==MAYBE || !isEnabled()) + dc.setForeground(shadowColor); + else + dc.setForeground(checkColor); + + // Show as + + if(options&CHECKBUTTON_PLUS){ + if(check!=TRUE){ + dc.fillRectangle(ix+6,iy+4,1,5); + } + dc.fillRectangle(ix+4,iy+6,5,1); + } + + // Show as v + else{ + if(check!=FALSE){ + FXSegment seg[6]; +#ifndef WIN32 + seg[0].x1=3+ix; seg[0].y1=5+iy; seg[0].x2=5+ix; seg[0].y2=7+iy; + seg[1].x1=3+ix; seg[1].y1=6+iy; seg[1].x2=5+ix; seg[1].y2=8+iy; + seg[2].x1=3+ix; seg[2].y1=7+iy; seg[2].x2=5+ix; seg[2].y2=9+iy; + seg[3].x1=5+ix; seg[3].y1=7+iy; seg[3].x2=9+ix; seg[3].y2=3+iy; + seg[4].x1=5+ix; seg[4].y1=8+iy; seg[4].x2=9+ix; seg[4].y2=4+iy; + seg[5].x1=5+ix; seg[5].y1=9+iy; seg[5].x2=9+ix; seg[5].y2=5+iy; +#else + seg[0].x1=3+ix; seg[0].y1=5+iy; seg[0].x2=5+ix; seg[0].y2=7+iy; + seg[1].x1=3+ix; seg[1].y1=6+iy; seg[1].x2=5+ix; seg[1].y2=8+iy; + seg[2].x1=3+ix; seg[2].y1=7+iy; seg[2].x2=5+ix; seg[2].y2=9+iy; + seg[3].x1=5+ix; seg[3].y1=7+iy; seg[3].x2=10+ix; seg[3].y2=2+iy; + seg[4].x1=5+ix; seg[4].y1=8+iy; seg[4].x2=10+ix; seg[4].y2=3+iy; + seg[5].x1=5+ix; seg[5].y1=9+iy; seg[5].x2=10+ix; seg[5].y2=4+iy; +#endif + dc.drawLineSegments(seg,6); + } + } + + // Text + if(!label.empty()){ + dc.setFont(font); + if(isEnabled()){ + dc.setForeground(textColor); + drawLabel(dc,label,hotoff,tx,ty,tw,th); + if(hasFocus()){ + dc.drawFocusRectangle(tx-1,ty-1,tw+2,th+2); + } + } + else{ + dc.setForeground(hiliteColor); + drawLabel(dc,label,hotoff,tx+1,ty+1,tw,th); + dc.setForeground(shadowColor); + drawLabel(dc,label,hotoff,tx,ty,tw,th); + } + } + + // Frame + drawFrame(dc,0,0,width,height); + + return 1; + } + + + + + + + + + + + + + + +#define MENUBUTTONARROW_WIDTH 11 +#define MENUBUTTONARROW_HEIGHT 5 + +#define MENUBUTTON_MASK (MENUBUTTON_AUTOGRAY|MENUBUTTON_AUTOHIDE|MENUBUTTON_TOOLBAR|MENUBUTTON_NOARROWS) +#define POPUP_MASK (MENUBUTTON_UP|MENUBUTTON_LEFT) +#define ATTACH_MASK (MENUBUTTON_ATTACH_RIGHT|MENUBUTTON_ATTACH_CENTER) + + + +FXDEFMAP(GMMenuButton) GMMenuButtonMap[]={ + FXMAPFUNC(SEL_PAINT,0,GMMenuButton::onPaint) + }; + +FXIMPLEMENT(GMMenuButton,FXMenuButton,GMMenuButtonMap,ARRAYNUMBER(GMMenuButtonMap)) + +GMMenuButton::GMMenuButton(){ + } + +GMMenuButton::GMMenuButton(FXComposite* p,const FXString& text,FXIcon* ic,FXPopup* pup,FXuint opts,FXint x,FXint y,FXint w,FXint h,FXint pl,FXint pr,FXint pt,FXint pb) : FXMenuButton(p,text,ic,pup,opts,x,y,w,h,pl,pr,pt,pb) { + } + + +// Handle repaint +long GMMenuButton::onPaint(FXObject*,FXSelector,void* ptr){ + FXint tw=0,th=0,iw=0,ih=0,tx,ty,ix,iy; + FXEvent *ev=(FXEvent*)ptr; + FXPoint points[3]; + FXDCWindow dc(this,ev); + + // Got a border at all? + if(options&(FRAME_RAISED|FRAME_SUNKEN)){ + + // Toolbar style + if(options&MENUBUTTON_TOOLBAR){ + + // Enabled and cursor inside, and not popped up + if(isEnabled() && underCursor() && !state){ + dc.setForeground(backColor); + dc.fillRectangle(border,border,width-border*2,height-border*2); + if(options&FRAME_THICK) drawDoubleRaisedRectangle(dc,0,0,width,height); + else drawRaisedRectangle(dc,0,0,width,height); + } + + // Enabled and popped up + else if(isEnabled() && state){ + dc.setForeground(hiliteColor); + dc.fillRectangle(border,border,width-border*2,height-border*2); + if(options&FRAME_THICK) drawDoubleSunkenRectangle(dc,0,0,width,height); + else drawSunkenRectangle(dc,0,0,width,height); + } + + // Disabled or unchecked or not under cursor + else{ + dc.setForeground(backColor); + dc.fillRectangle(0,0,width,height); + } + } + + // Normal style + else{ + + // Draw in up state if disabled or up + if(!isEnabled() || !state){ + dc.setForeground(backColor); + dc.fillRectangle(border,border,width-border*2,height-border*2); + if(options&FRAME_THICK) drawDoubleRaisedRectangle(dc,0,0,width,height); + else drawRaisedRectangle(dc,0,0,width,height); + } + + // Draw sunken if enabled and either checked or pressed + else{ + dc.setForeground(hiliteColor); + dc.fillRectangle(border,border,width-border*2,height-border*2); + if(options&FRAME_THICK) drawDoubleSunkenRectangle(dc,0,0,width,height); + else drawSunkenRectangle(dc,0,0,width,height); + } + } + } + + // No borders + else{ + if(isEnabled() && state){ + dc.setForeground(hiliteColor); + dc.fillRectangle(0,0,width,height); + } + else{ + dc.setForeground(backColor); + dc.fillRectangle(0,0,width,height); + } + } + + // Position text & icon +// if(!label.empty()){ +// tw=labelWidth(label); +// th=labelHeight(label); +// } + + // Icon? + if(icon){ + tw=icon->getWidth(); + th=icon->getHeight(); + } + + // Arrows? + if(!(options&MENUBUTTON_NOARROWS)){ + iw=MENUBUTTONARROW_WIDTH; + ih=MENUBUTTONARROW_HEIGHT; + } + + // Keep some room for the arrow! + just_x(tx,ix,tw,iw); + just_y(ty,iy,th,ih); + + // Move a bit when pressed + if(state){ ++tx; ++ty; ++ix; ++iy; } + + // Draw icon + if(icon){ + if(isEnabled()) + dc.drawIcon(icon,tx,ty); + else + dc.drawIconSunken(icon,tx,ty); + } + + // Draw arrows + if(!(options&MENUBUTTON_NOARROWS)){ + + // Right arrow + if((options&MENUBUTTON_RIGHT)==MENUBUTTON_RIGHT){ + if(isEnabled()) + dc.setForeground(textColor); + else + dc.setForeground(shadowColor); + points[0].x=ix; + points[0].y=iy; + points[1].x=ix; + points[1].y=iy+MENUBUTTONARROW_WIDTH-1; + points[2].x=ix+MENUBUTTONARROW_HEIGHT; + points[2].y=(FXshort)(iy+(MENUBUTTONARROW_WIDTH>>1)); + dc.fillPolygon(points,3); + } + + // Left arrow + else if(options&MENUBUTTON_LEFT){ + if(isEnabled()) + dc.setForeground(textColor); + else + dc.setForeground(shadowColor); + points[0].x=ix+MENUBUTTONARROW_HEIGHT; + points[0].y=iy; + points[1].x=ix+MENUBUTTONARROW_HEIGHT; + points[1].y=iy+MENUBUTTONARROW_WIDTH-1; + points[2].x=ix; + points[2].y=(FXshort)(iy+(MENUBUTTONARROW_WIDTH>>1)); + dc.fillPolygon(points,3); + } + + // Up arrow + else if(options&MENUBUTTON_UP){ + if(isEnabled()) + dc.setForeground(textColor); + else + dc.setForeground(shadowColor); + points[0].x=(FXshort)(ix+(MENUBUTTONARROW_WIDTH>>1)); + points[0].y=iy-1; + points[1].x=ix; + points[1].y=iy+MENUBUTTONARROW_HEIGHT; + points[2].x=ix+MENUBUTTONARROW_WIDTH; + points[2].y=iy+MENUBUTTONARROW_HEIGHT; + dc.fillPolygon(points,3); + } + + // Down arrow + else{ + if(isEnabled()) + dc.setForeground(textColor); + else + dc.setForeground(shadowColor); + points[0].x=ix+1; + points[0].y=iy; + points[2].x=ix+MENUBUTTONARROW_WIDTH-1; + points[2].y=iy; + points[1].x=(FXshort)(ix+(MENUBUTTONARROW_WIDTH>>1)); + points[1].y=iy+MENUBUTTONARROW_HEIGHT; + dc.fillPolygon(points,3); + } + } + + // Draw text + if(!label.empty()){ + dc.setFont(font); + if(isEnabled()){ + dc.setForeground(textColor); + drawLabel(dc,label,hotoff,tx,ty,tw,th); + } + else{ + dc.setForeground(hiliteColor); + drawLabel(dc,label,hotoff,tx+1,ty+1,tw,th); + dc.setForeground(shadowColor); + drawLabel(dc,label,hotoff,tx,ty,tw,th); + } + } + + // Draw focus + if(hasFocus()){ + if(isEnabled()){ + dc.drawFocusRectangle(border+1,border+1,width-2*border-2,height-2*border-2); + } + } + return 1; + } + + + +// Get default width +FXint GMMenuButton::getDefaultWidth(){ + FXint tw=0,iw=0,s=4,w,pw; +// if(!label.empty()){ tw=labelWidth(label); s=4; } + if(!(options&MENUBUTTON_NOARROWS)){ + if(options&MENUBUTTON_LEFT) iw=MENUBUTTONARROW_HEIGHT; else iw=MENUBUTTONARROW_WIDTH; + } + if(icon) tw=icon->getWidth(); + if(!(options&(ICON_AFTER_TEXT|ICON_BEFORE_TEXT))) w=FXMAX(tw,iw); else w=tw+iw+s; + w=padleft+padright+(border<<1)+w; + if(!(options&MENUBUTTON_LEFT) && (options&MENUBUTTON_ATTACH_RIGHT) && (options&MENUBUTTON_ATTACH_CENTER)){ + if(pane){ pw=pane->getDefaultWidth(); if(pw>w) w=pw; } + } + return w; + } + + +// Get default height +FXint GMMenuButton::getDefaultHeight(){ + FXint th=0,ih=0,h,ph; +// if(!label.empty()){ th=labelHeight(label); } + if(!(options&MENUBUTTON_NOARROWS)){ + if(options&MENUBUTTON_LEFT) ih=MENUBUTTONARROW_WIDTH; else ih=MENUBUTTONARROW_HEIGHT; + } + if(icon) th=icon->getHeight(); + if(!(options&(ICON_ABOVE_TEXT|ICON_BELOW_TEXT))) h=FXMAX(th,ih); else h=th+ih; + h=padtop+padbottom+(border<<1)+h; + if((options&MENUBUTTON_LEFT) && (options&MENUBUTTON_ATTACH_BOTTOM) && (options&MENUBUTTON_ATTACH_CENTER)){ + if(pane){ ph=pane->getDefaultHeight(); if(ph>h) h=ph; } + } + return h; + } + + + + + + + + + + + + + + + + + + + + + + + + + +FXDEFMAP(GMHeaderButton) GMHeaderButtonMap[]={ + FXMAPFUNC(SEL_PAINT,0,GMHeaderButton::onPaint) + }; + +FXIMPLEMENT(GMHeaderButton,FXButton,GMHeaderButtonMap,ARRAYNUMBER(GMHeaderButtonMap)) + + +GMHeaderButton::GMHeaderButton(){ + arrowstate=ARROW_UP; + } + +GMHeaderButton::GMHeaderButton(FXComposite* p,const FXString& text,FXIcon* ic,FXObject* tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h,FXint pl,FXint pr,FXint pt,FXint pb) : FXButton(p,text,ic,tgt,sel,opts,x,y,w,h,pl,pr,pt,pb) { + arrowstate=ARROW_UP; + } + +void GMHeaderButton::setArrowState(FXuint s) { + arrowstate=s; + } + +FXuint GMHeaderButton::getArrowState() const { + return arrowstate; + } + + + +long GMHeaderButton::onPaint(FXObject*,FXSelector,void*ptr){ + FXint tw=0,th=0,iw=0,ih=0,tx,ty,ix,iy; + FXEvent *ev=(FXEvent*)ptr; + + // Start drawing + FXDCWindow dc(this,ev); + + // Got a border at all? + if(options&(FRAME_RAISED|FRAME_SUNKEN)){ + + // Toolbar style + if(options&BUTTON_TOOLBAR){ + + // Enabled and cursor inside, and up + if(isEnabled() && underCursor() && (state==STATE_UP)){ + dc.setForeground(backColor); + dc.fillRectangle(border,border,width-border*2,height-border*2); + if(options&FRAME_THICK) drawDoubleRaisedRectangle(dc,0,0,width,height); + else drawRaisedRectangle(dc,0,0,width,height); + } + + // Enabled and cursor inside and down + else if(isEnabled() && underCursor() && (state==STATE_DOWN)){ + dc.setForeground(backColor); + dc.fillRectangle(border,border,width-border*2,height-border*2); + if(options&FRAME_THICK) drawDoubleSunkenRectangle(dc,0,0,width,height); + else drawSunkenRectangle(dc,0,0,width,height); + } + + // Enabled and checked + else if(isEnabled() && (state==STATE_ENGAGED)){ + dc.setForeground(hiliteColor); + dc.fillRectangle(border,border,width-border*2,height-border*2); + if(options&FRAME_THICK) drawDoubleSunkenRectangle(dc,0,0,width,height); + else drawSunkenRectangle(dc,0,0,width,height); + } + + // Disabled or unchecked or not under cursor + else{ + dc.setForeground(backColor); + dc.fillRectangle(0,0,width,height); + } + } + + // Normal style + else{ + + // Default + if(isDefault()){ + + // Draw in up state if disabled or up + if(!isEnabled() || (state==STATE_UP)){ + dc.setForeground(backColor); + dc.fillRectangle(border+1,border+1,width-border*2-1,height-border*2-1); + dc.setForeground(FXRGB(255,255,255)); + dc.fillRectangle(width-2,1,1,height-1); + + // if(options&FRAME_THICK) drawDoubleRaisedRectangle(dc,1,1,width-1,height-1); + // else drawRaisedRectangle(dc,1,1,width-3,height-1); + } + + // Draw sunken if enabled and either checked or pressed + else{ + if(state==STATE_ENGAGED) dc.setForeground(hiliteColor); else dc.setForeground(backColor); + dc.fillRectangle(border,border,width-border*2-1,height-border*2-1); + if(options&FRAME_THICK) drawDoubleSunkenRectangle(dc,0,0,width-1,height-1); + else drawSunkenRectangle(dc,0,0,width-1,height-1); + } + + // Black default border + drawBorderRectangle(dc,0,0,width,height); + } + + // Non-Default + else{ + + // Draw in up state if disabled or up + if(!isEnabled() || (state==STATE_UP)){ + dc.setForeground(backColor); + //dc.fillRectangle(border,border,width-border*2,height-border*2); + + //fillVerticalGradient(dc,border,border,width-border*2,height-border*2,makeHiliteColor(makeHiliteColor(makeShadowColor(backColor))),makeHiliteColor(makeShadowColor(backColor))); +// fillVerticalGradient(dc,0,0,width,height-1,makeHiliteColor(makeHiliteColor(makeShadowColor(backColor))),makeHiliteColor(makeShadowColor(backColor))); + + fillVerticalGradient(dc,ev->rect.x,0,ev->rect.w,height-1,gm_make_hilite_color(backColor),gm_make_hilite_color(gm_make_shadow_color(backColor))); + + + dc.setForeground(shadowColor); + dc.fillRectangle(0,height-1,width,1); + + + + + +// if(options&FRAME_THICK) drawDoubleRaisedRectangle(dc,0,0,width,height); +/// else drawRaisedRectangle(dc,0,0,width-1,height); + +// dc.setForeground(backColor); + +// dc.fillRectangle(width-2,0,2,height-1); + + } + + // Draw sunken if enabled and either checked or pressed + else{ + if(state==STATE_ENGAGED) dc.setForeground(hiliteColor); else dc.setForeground(backColor); + dc.fillRectangle(border,border,width-border*2,height-border*2); + if(options&FRAME_THICK) drawDoubleSunkenRectangle(dc,0,0,width,height); + else drawSunkenRectangle(dc,0,0,width,height); + } + } + } + } + + // No borders + else{ + if(isEnabled() && (state==STATE_ENGAGED)){ + dc.setForeground(hiliteColor); + dc.fillRectangle(0,0,width,height); + } + else{ + dc.setForeground(backColor); + dc.fillRectangle(0,0,width,height); + } + } + + // Place text & icon + if(!label.empty()){ + tw=labelWidth(label); + th=labelHeight(label); + } + if(icon){ + iw=icon->getWidth(); + ih=icon->getHeight(); + } + + just_x(tx,ix,tw,iw); + just_y(ty,iy,th,ih); + + // Shift a bit when pressed + if(state && (options&(FRAME_RAISED|FRAME_SUNKEN))){ ++tx; ++ty; ++ix; ++iy; } + + // Draw enabled state + if(isEnabled()){ + if(icon){ + dc.drawIcon(icon,ix,iy); + } + if(!label.empty()){ + dc.setFont(font); + dc.setForeground(textColor); + drawLabel(dc,label,hotoff,tx,ty,tw,th); + } +// if(hasFocus()){ +// dc.drawFocusRectangle(border+1,border+1,width-2*border-2,height-2*border-2); +// } + } + + // Draw grayed-out state + else{ + if(icon){ + dc.drawIconSunken(icon,ix,iy); + } + if(!label.empty()){ + dc.setFont(font); + dc.setForeground(hiliteColor); + drawLabel(dc,label,hotoff,tx+1,ty+1,tw,th); + dc.setForeground(shadowColor); + drawLabel(dc,label,hotoff,tx,ty,tw,th); + } + } + + // Draw arrows + if(arrowstate&(ARROW_UP|ARROW_DOWN)){ + FXint aa=(font->getFontHeight()-5)|1; + FXint ay=(height-aa)/2; + FXint ax=width-aa-border-border-2; + if(arrowstate&ARROW_UP){ + dc.setForeground(hiliteColor); + dc.drawLine(ax+aa/2,ay,ax+aa-1,ay+aa); + dc.drawLine(ax,ay+aa,ax+aa,ay+aa); + dc.setForeground(shadowColor); + dc.drawLine(ax+aa/2,ay,ax,ay+aa); + } + else{ + dc.setForeground(hiliteColor); + dc.drawLine(ax+aa/2,ay+aa,ax+aa-1,ay); + dc.setForeground(shadowColor); + dc.drawLine(ax+aa/2,ay+aa,ax,ay); + dc.drawLine(ax,ay,ax+aa,ay); + } + } + return 1; + } + + +FXIMPLEMENT(GMScrollArea,FXScrollArea,NULL,0) + +void GMScrollArea::replaceScrollbars(FXScrollArea *fs) { + GMScrollArea * s = (GMScrollArea*)(fs); + delete s->vertical; + delete s->horizontal; + delete s->corner; + s->vertical=new GMScrollBar(fs,fs,GMScrollArea::ID_VSCROLLED,SCROLLBAR_VERTICAL); + s->horizontal=new GMScrollBar(fs,fs,GMScrollArea::ID_HSCROLLED,SCROLLBAR_HORIZONTAL); + s->corner=new GMScrollCorner(fs); + } + + + +FXIMPLEMENT(GMTreeListBox,FXTreeListBox,NULL,0) + +void GMTreeListBox::replace(FXTreeListBox *fs) { + GMTreeListBox * s = (GMTreeListBox*)(fs); + GMScrollArea::replaceScrollbars(s->tree); + + s->borderColor=s->getApp()->getShadowColor(); + s->setFrameStyle(FRAME_LINE); + s->pane->setBorderColor(s->borderColor); + + s->button->setFrameStyle(FRAME_NONE); + s->button->setPadLeft(2); + s->button->setPadRight(2); + s->button->setYOffset(0); + s->button->setXOffset(1); + } + + + +// Map +FXDEFMAP(GMScrollBar) GMScrollBarMap[]={ + FXMAPFUNC(SEL_PAINT,0,GMScrollBar::onPaint), + }; + + +// Object implementation +FXIMPLEMENT(GMScrollBar,FXScrollBar,GMScrollBarMap,ARRAYNUMBER(GMScrollBarMap)) + + +// For deserialization +GMScrollBar::GMScrollBar(){ + } + +// Make a scrollbar +GMScrollBar::GMScrollBar(FXComposite* p,FXObject* tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h): FXScrollBar(p,tgt,sel,opts,x,y,w,h) { +//shadowColor=gm_make_shadow_color(getApp()->getBaseColor()); + } + +void GMScrollBar::drawThumb(FXDCWindow& dc,FXint x,FXint y,FXint w,FXint h){ + if(options&SCROLLBAR_HORIZONTAL){ + fillVerticalGradient(dc,x+2,y,w-4,h-1,gm_make_hilite_color(FXApp::instance()->getSelbackColor()),gm_make_hilite_color(gm_make_shadow_color(FXApp::instance()->getSelbackColor()))); + dc.setForeground(shadowColor); + dc.fillRectangle(x+w-1,y,1,h); + dc.fillRectangle(x,y,1,h); + dc.setForeground(gm_make_hilite_color(getApp()->getSelbackColor())); + dc.fillRectangle(x+1,y+h-1,w-2,1); + dc.fillRectangle(x+w-2,y,1,h-1); + dc.fillRectangle(x+1,y,1,h-1); + if (w>20) { + dc.setForeground(gm_make_shadow_color(getApp()->getSelbackColor())); + dc.fillRectangle(thumbpos+thumbsize / 2,(height/2) - 2,1,6); + dc.fillRectangle((thumbpos+thumbsize / 2) - 3,(height/2) - 2,1,6); + dc.fillRectangle((thumbpos+thumbsize / 2) + 3,(height/2) - 2,1,6); + } + } + else { + fillHorizontalGradient(dc,x,y+2,w-1,h-4,gm_make_hilite_color(getApp()->getSelbackColor()),gm_make_hilite_color(gm_make_shadow_color(getApp()->getSelbackColor()))); + dc.setForeground(shadowColor); + dc.fillRectangle(x,y+h-1,w,1); + dc.fillRectangle(x,y,w,1); + + dc.setForeground(gm_make_hilite_color(getApp()->getSelbackColor())); + dc.fillRectangle(x+w-1,y+1,1,h-2); + dc.fillRectangle(x,y+1,w-1,1); + dc.fillRectangle(x,y+h-2,w-1,1); + + if (h>20) { + dc.setForeground(gm_make_shadow_color(getApp()->getSelbackColor())); + dc.fillRectangle((width/2) - 2,thumbpos+thumbsize / 2,6,1); + dc.fillRectangle((width/2) - 2,(thumbpos+thumbsize / 2) - 3,6,1); + dc.fillRectangle((width/2) - 2,(thumbpos+thumbsize / 2) + 3,6,1); + } + } + } + +// Handle repaint +long GMScrollBar::onPaint(FXObject*,FXSelector,void* ptr){ + register FXEvent *ev=(FXEvent*)ptr; + register int total; + FXDCWindow dc(this,ev); + if(options&SCROLLBAR_HORIZONTAL){ + total=width-height-height; + dc.setForeground(shadowColor); + dc.fillRectangle(0,0,width,1); + if(thumbsize0) { + dc.setForeground(shadowColor); + dc.fillRectangle(height,1,1,height-1); + dc.setForeground(gm_make_hilite_color(shadowColor)); + dc.fillRectangle(height+1,1,thumbpos-height-1,height-1); + } + + if (width-height-thumbpos-thumbsize>0) { + dc.setForeground(shadowColor); + dc.fillRectangle(width-height-1,1,1,height-1); + dc.setForeground(gm_make_hilite_color(shadowColor)); + dc.fillRectangle(thumbpos+thumbsize,1,width-height-thumbpos-thumbsize-1,height); + } + } + else{ // Non-scrollable + dc.fillRectangle(height,1,1,height-1); + dc.fillRectangle(width-height-1,1,1,height-1); + dc.setForeground(gm_make_hilite_color(shadowColor)); + dc.fillRectangle(height+1,1,total-2,height-1); + } + dc.setFillStyle(FILL_SOLID); + dc.setForeground(backColor); + dc.fillRectangle(width-height,1,height,height-1); + drawRightArrow(dc,width-height,0,height,height,(mode==MODE_INC)); + dc.setForeground(backColor); + dc.fillRectangle(0,1,height,height-1); + drawLeftArrow(dc,0,0,height,height,(mode==MODE_DEC)); + } + else{ + total=height-width-width; + dc.setForeground(shadowColor); + dc.fillRectangle(0,0,1,height); + if(thumbsize0) { + dc.setForeground(shadowColor); + dc.fillRectangle(1,width,width-1,1); + dc.setForeground(gm_make_hilite_color(shadowColor)); + dc.fillRectangle(1,width+1,width-1,thumbpos-width-1); + } + if (height-width-thumbpos-thumbsize>0){ + dc.setForeground(shadowColor); + dc.fillRectangle(1,height-width-1,width-1,1); + dc.setForeground(gm_make_hilite_color(shadowColor)); + dc.fillRectangle(1,thumbpos+thumbsize,width-1,height-width-thumbpos-thumbsize-1); + } + } + else{ // Non-scrollable + dc.fillRectangle(1,width,width-1,1); + dc.fillRectangle(1,height-width-1,width-1,1); + dc.setForeground(gm_make_hilite_color(shadowColor)); + dc.fillRectangle(1,width+1,width-1,total-2); + } + dc.setFillStyle(FILL_SOLID); + dc.setForeground(backColor); + dc.fillRectangle(1,height-width,width-1,width); + drawDownArrow(dc,0,height-width,width,width,(mode==MODE_INC)); + dc.setForeground(backColor); + dc.fillRectangle(1,0,width-1,width); + drawUpArrow(dc,0,0,width,width,(mode==MODE_DEC)); + } + return 1; + } + +// Map +FXDEFMAP(GMScrollCorner) GMScrollCornerMap[]={ + FXMAPFUNC(SEL_PAINT,0,GMScrollCorner::onPaint), + }; + +FXIMPLEMENT(GMScrollCorner,FXScrollCorner,GMScrollCornerMap,ARRAYNUMBER(GMScrollCornerMap)) + +GMScrollCorner::GMScrollCorner(FXComposite*p):FXScrollCorner(p){ + shadowColor=getApp()->getShadowColor(); + } + +// Slightly different from Frame border +long GMScrollCorner::onPaint(FXObject*,FXSelector,void* ptr){ + FXEvent *ev=(FXEvent*)ptr; + FXDCWindow dc(this,ev); + dc.setForeground(backColor); + dc.fillRectangle(ev->rect.x,ev->rect.y,ev->rect.w,ev->rect.h); + dc.setForeground(shadowColor); + dc.fillRectangle(ev->rect.x,0,ev->rect.w,1); + dc.fillRectangle(0,ev->rect.y,1,ev->rect.h); + return 1; + } + + + +FXIMPLEMENT(GMTabBook,FXTabBook,NULL,0) + +GMTabBook::GMTabBook(FXComposite* p,FXObject* tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h,FXint pl,FXint pr,FXint pt,FXint pb) : + FXTabBook(p,tgt,sel,opts,x,y,w,h,pl,pr,pt,pb) { + } + + +/// Make sure old tab gets repainted +void GMTabBook::setCurrent(FXint panel,FXbool notify) { + if (panel!=current) { + FXint old = current; + FXTabBook::setCurrent(panel,notify); + FXWindow * window = childAtIndex(old<<1); + if (window) { window->update(); } + } + } + + + +// Map +FXDEFMAP(GMTabItem) GMTabItemMap[]={ + FXMAPFUNC(SEL_PAINT,0,GMTabItem::onPaint), + }; + +FXIMPLEMENT(GMTabItem,FXTabItem,GMTabItemMap,ARRAYNUMBER(GMTabItemMap)) + +GMTabItem::GMTabItem(FXTabBar* p,const FXString& text,FXIcon* ic,FXuint opts,FXint x,FXint y,FXint w,FXint h,FXint pl,FXint pr,FXint /*pt*/,FXint /*pb*/) : FXTabItem(p,text,ic,opts,x,y,w,h,pl,pr,5,5) { + } + +//void GMTabItem::raise() { +// FXTabItem::raise(); +// update(); +// recalc(); +// } + +long GMTabItem::onPaint(FXObject*,FXSelector,void*){ + FXTabBar * bar = (FXTabBar*)getParent(); + FXint tab = bar->indexOfChild(this)/2; + FXint ntabs = (bar->numChildren()/2); + FXint ctab = bar->getCurrent(); + + + + FXDCWindow dc(this); + + FXint tw=0,th=0,iw=0,ih=0,tx,ty,ix,iy; + + dc.setForeground(shadowColor); + dc.fillRectangle(0,0,width,1); + if (tab==ctab && tab==0) + dc.fillRectangle(0,0,1,height); + else + dc.fillRectangle(0,0,1,height-1); + + /// last one or active one + if ((tab == (ntabs-1)) || tab==ctab) { + dc.fillRectangle(width-1,0,1,height-1); + if (tab!=ctab) + fillVerticalGradient(dc,1,1,width-2,height-2,gm_make_hilite_color(shadowColor),gm_make_hilite_color(shadowColor)); + else { +/* + dc.setForeground(makeShadowColor(getApp()->getSelbackColor())); + dc.fillRectangle(0,0,width,1); + dc.fillRectangle(width-1,0,1,4); + dc.fillRectangle(0,0,1,4); + dc.fillRectangle(1,3,width-2,1); +*/ +/* + + dc.setForeground(getApp()->getSelbackColor()); + dc.fillRectangle(1,1,width-2,2); +*/ +// dc.setForeground(backColor); +// dc.fillRectangle(1,4,width-2,height-4); + + dc.setForeground(getApp()->getSelbackColor()); +// dc.fillRectangle(1,1,width-2,height-2); + fillVerticalGradient(dc,1,1,width-2,height-2,gm_make_hilite_color(backColor),backColor); + + dc.setForeground(backColor); + if (tab==0) + dc.fillRectangle(1,height-1,width-1,height-1); + else + dc.fillRectangle(0,height-1,width,height-1); + } + } + else { + fillVerticalGradient(dc,1,1,width-1,height-2,gm_make_hilite_color(shadowColor),gm_make_hilite_color(shadowColor)); + } + + if(!label.empty()){ + tw=labelWidth(label); + th=labelHeight(label); + } + if(icon){ + iw=icon->getWidth(); + ih=icon->getHeight(); + } + just_x(tx,ix,tw,iw); + just_y(ty,iy,th,ih); + if(icon){ + if(isEnabled()) + dc.drawIcon(icon,ix,iy); + else + dc.drawIconSunken(icon,ix,iy); + } + if(!label.empty()){ + dc.setFont(font); + if(isEnabled()){ + dc.setForeground(textColor); + drawLabel(dc,label,hotoff,tx,ty,tw,th); + if(hasFocus()){ + dc.drawFocusRectangle(border+1,border+1,width-2*border-2,height-2*border-2); + } + } + else{ + dc.setForeground(hiliteColor); + drawLabel(dc,label,hotoff,tx+1,ty+1,tw,th); + dc.setForeground(shadowColor); + drawLabel(dc,label,hotoff,tx,ty,tw,th); + } + } + return 1; + } + + + +FXIMPLEMENT(GMListBox,FXListBox,NULL,0); + +GMListBox::GMListBox(){ + } + +GMListBox::GMListBox(FXComposite*p,FXObject*tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h,FXint pl,FXint pr,FXint pt,FXint pb) + : FXListBox(p,tgt,sel,opts,x,y,w,h,pl,pr,pt,pb) { + borderColor=getApp()->getShadowColor(); + setFrameStyle(FRAME_LINE); + + GMScrollArea::replaceScrollbars(list); + + pane->setBorderColor(borderColor); + button->setFrameStyle(FRAME_NONE); + button->setPadLeft(2); + button->setPadRight(2); + button->setYOffset(0); + button->setXOffset(1); + } + + +void GMListBox::create(){ + FXListBox::create(); + ewmh_change_window_type(pane,WINDOWTYPE_COMBO); + } + + + +FXIMPLEMENT(GMComboBox,FXComboBox,NULL,0); + +GMComboBox::GMComboBox(){ + } + +GMComboBox::GMComboBox(FXComposite *p,FXint cols,FXObject* tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h,FXint pl,FXint pr,FXint pt,FXint pb) + :FXComboBox(p,cols,tgt,sel,opts,x,y,w,h,pl,pr,pt,pb){ + borderColor=getApp()->getShadowColor(); + setFrameStyle(FRAME_LINE); + pane->setBorderColor(borderColor); + + GMScrollArea::replaceScrollbars(list); + button->setFrameStyle(FRAME_NONE); + button->setPadLeft(2); + button->setPadRight(2); + button->setYOffset(0); + button->setXOffset(1); + } + +void GMComboBox::create(){ + FXComboBox::create(); + ewmh_change_window_type(pane,WINDOWTYPE_COMBO); + } + + +FXIMPLEMENT(GMImageFrame,FXImageFrame,NULL,0); + +GMImageFrame::GMImageFrame(){ + } + +GMImageFrame::GMImageFrame(FXComposite *p,FXImage * img,FXuint opts,FXint x,FXint y,FXint w,FXint h,FXint pl,FXint pr,FXint pt,FXint pb) + :FXImageFrame(p,img,opts,x,y,w,h,pl,pr,pt,pb){ + borderColor=getApp()->getShadowColor(); + backColor=getApp()->getBackColor(); + } + + + +FXIMPLEMENT(GMCoverFrame,FXVerticalFrame,NULL,0); + +GMCoverFrame::GMCoverFrame(){ + } + +GMCoverFrame::GMCoverFrame(FXComposite *p) + :FXVerticalFrame(p,LAYOUT_FILL_X|LAYOUT_FILL_Y|FRAME_LINE,0,0,0,0,5,5,5,5){ + borderColor=getApp()->getShadowColor(); + backColor=getApp()->getBackColor(); + } + + +FXIMPLEMENT(GMProgressBar,FXProgressBar,NULL,0); + +GMProgressBar::GMProgressBar(){ + } + +GMProgressBar::GMProgressBar(FXComposite *p,FXObject* tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h,FXint pl,FXint pr,FXint pt,FXint pb) + :FXProgressBar(p,tgt,sel,opts,x,y,w,h,pl,pr,pt,pb){ + borderColor=getApp()->getShadowColor(); + barColor=getApp()->getSelbackColor(); + textAltColor=getApp()->getSelforeColor();//FXRGB(255,255,255); + setFrameStyle(FRAME_LINE); + } + +#if 0 +FXDEFMAP(GMTrackProgressBar) GMTrackProgressBarMap[]={ + FXMAPFUNC(SEL_MOTION,0,GMTrackProgressBar::onMotion), + FXMAPFUNC(SEL_LEFTBUTTONPRESS,0,GMTrackProgressBar::onLeftBtnPress), + FXMAPFUNC(SEL_LEFTBUTTONRELEASE,0,GMTrackProgressBar::onLeftBtnRelease) + }; + +FXIMPLEMENT(GMTrackProgressBar,FXProgressBar,GMTrackProgressBarMap,ARRAYNUMBER(GMTrackProgressBarMap)); + +GMTrackProgressBar::GMTrackProgressBar(){ + flags|=FLAG_ENABLED; + } + +GMTrackProgressBar::GMTrackProgressBar(FXComposite *p,FXObject* tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h,FXint pl,FXint pr,FXint pt,FXint pb) + :FXProgressBar(p,tgt,sel,opts,x,y,w,h,pl,pr,pt,pb){ + flags|=FLAG_ENABLED; + borderColor=getApp()->getShadowColor(); + barColor=getApp()->getSelbackColor(); + textAltColor=getApp()->getSelforeColor(); + setFrameStyle(FRAME_LINE); + } + + + +// Moving +long GMTrackProgressBar::onMotion(FXObject*,FXSelector,void* ptr){ + register FXEvent *event=(FXEvent*)ptr; + register FXint xx,yy,ww,hh,lo,hi,p,h,travel; + if(!isEnabled()) return 0; + if(flags&FLAG_PRESSED){ + yy=border+padtop; + xx=border+padleft; + hh=height-(border<<1)-padtop-padbottom; + ww=width-(border<<1)-padleft-padright; +/* + if(options&PROGRESSBAR_VERTICAL){ + h=event->win_y-dragpoint; + travel=hh-headsize; + if(hyy+travel) h=yy+travel; + if(h!=headpos){ + FXMINMAX(lo,hi,headpos,h); + headpos=h; + update(border,lo-1,width-(border<<1),hi+headsize+2-lo); + } + if(travel>0) + p=range[0]+((range[1]-range[0])*(yy+travel-h)+travel/2)/travel; // Use rounding!! + else + p=range[0]; + } + else{ + h=event->win_x-dragpoint; + travel=ww-headsize; + if(hxx+travel) h=xx+travel; + if(h!=headpos){ + FXMINMAX(lo,hi,headpos,h); + headpos=h; + update(lo-1,border,hi+headsize+2-lo,height-(border<<1)); + } + if(travel>0) + p=range[0]+((range[1]-range[0])*(h-xx)+travel/2)/travel; // Use rounding!! + else + p=range[0]; + } + if(prange[1]) p=range[1]; + if(pos!=p){ + pos=p; + flags|=FLAG_CHANGED; + if(target) target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)pos); + } +*/ + if(progress!=p){ + progress=p; + flags|=FLAG_CHANGED; + if(target) target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)progress); + } + return 1; + } + return 0; + } + +// Pressed LEFT button +long GMTrackProgressBar::onLeftBtnPress(FXObject*,FXSelector,void* ptr){ + fxmessage("press\n"); + register FXEvent *event=(FXEvent*)ptr; + register FXint p=progress; + flags&=~FLAG_TIP; + handle(this,FXSEL(SEL_FOCUS_SELF,0),ptr); + if(isEnabled()){ + grab(); + if(target && target->tryHandle(this,FXSEL(SEL_LEFTBUTTONPRESS,message),ptr)) return 1; + flags&=~FLAG_UPDATE; +/* + if(options&PROGRESSBAR_VERTICAL){ + if(event->win_ywin_y>(headpos+headsize)){ + p=progress-incr; + } + else{ + dragpoint=event->win_y-headpos; + flags|=FLAG_PRESSED; + } + } + else{ + + if(event->win_xwin_x>(headpos+headsize)){ + p=progress+incr; + } + else{ + dragpoint=event->win_x-headpos; + flags|=FLAG_PRESSED; + } + + } +*/ +// if(prange[1]) p=range[1]; + if(p!=progress){ + setProgress(p); + flags|=FLAG_CHANGED; + if(target) target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)progress); + } + return 1; + } + return 0; + } + + +// Released Left button +long GMTrackProgressBar::onLeftBtnRelease(FXObject*,FXSelector,void* ptr){ + register FXEvent *event=(FXEvent*)ptr; + fxmessage("release\n"); + register FXuint flgs=flags; + if(isEnabled()){ + ungrab(); + + FXint xx=border+padleft; + FXint ww=width-(border<<1)-padleft-padright; + + +// setProgress(progress); // Hop to exact position + flags&=~FLAG_PRESSED; + flags&=~FLAG_CHANGED; + flags|=FLAG_UPDATE; + if(target && target->tryHandle(this,FXSEL(SEL_LEFTBUTTONRELEASE,message),ptr)) return 1; + + if (event->click_x > xx && event->click_x < xx+ww) { + FXdouble pos = (event->click_x - xx) / (FXdouble)ww; + if(target) target->tryHandle(this,FXSEL(SEL_COMMAND,message),&pos); + } + return 1; + } + return 0; + } + +#endif + + + + + + + + + + + + + +FXIMPLEMENT(GMSettings,FXSettings,NULL,0); + +#if FOXVERSION < FXVERSION(1,7,0) +// Parse filename +FXbool GMSettings::parseFile(const FXString& filename,FXbool mrk){ + FXFile file(filename,FXIO::Reading); + if(file.isOpen()){ + + // Prepare buffer string + FXString string('\0',file.size()); + + // Load file + if(file.readBlock((void*)string.text(),string.length())==string.length()){ + FXStringDict *group=NULL; + FXint lineno=1,p=0,b,e; + FXString name; + FXString value; + + // Skip BOM, if any + if(string[p]=='\xef' && string[p+1]=='\xbb' && string[p+2]=='\xbf') p+=3; + + // Parse one line at a time + while(string[p]){ + + // Skip leading blanks + while(Ascii::isBlank(string[p])) p++; + + // Non-comment + if(string[p] && string[p]!='\n' && string[p]!='\r' && string[p]!='#' && string[p]!=';'){ + + // Parse section name + if(string[p]=='['){ + + b=++p; + + // Scan section name + while(string[p] && string[p]!=']' && string[p]!='\n' && string[p]!='\r' && !Ascii::isControl(string[p])) p++; + + // Check errors + if(string[p]!=']'){ fxwarning("%s:%d: illegal section name.\n",filename.text(),lineno); goto next; } + + e=p++; + + // Grab name + name=string.mid(b,e-b); + + // Add new section dict + group=insert(name.text()); + } + + // Parse name-value pair + else{ + + // Should have seen section prior to this + if(!group){ fxwarning("%s:%d: settings entry should follow a section.\n",filename.text(),lineno); goto next; } + + b=p; + + // Scan key name + while(string[p] && string[p]!='=' && string[p]!='\n' && string[p]!='\r' && !Ascii::isControl(string[p])) p++; + + // Check errors + if(string[p]!='='){ fxwarning("%s:%d: expected '=' to follow key.\n",filename.text(),lineno); goto next; } + + e=p++; + + // Remove trailing spaces after name + while(breplace(name.text(),value.text(),mrk); + } + } + + // Skip to end of line +next: while(string[p] && string[p]!='\n') p++; + + // End of line + if(string[p]=='\n'){ + lineno++; + p++; + } + } + return true; + } + } + return false; + } +#endif + diff --git a/src/fxext.h b/src/fxext.h new file mode 100644 index 0000000..557e692 --- /dev/null +++ b/src/fxext.h @@ -0,0 +1,488 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2009-2010 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#ifndef FXEXT_H +#define FXEXT_H + +enum { + WINDOWTYPE_NORMAL, + WINDOWTYPE_DIALOG, + WINDOWTYPE_COMBO, + WINDOWTYPE_POPUP_MENU, + WINDOWTYPE_DROPDOWN_MENU, + WINDOWTYPE_TOOLTIP, + }; + +extern void ewmh_change_window_type(const FXWindow *,FXuint); +extern void ewmh_set_window_icon(const FXWindow *,FXImage *); +extern void ewmh_activate_window(const FXWindow*); + +extern void fix_wm_properties(const FXWindow*); + + +class FXIconThreshold : public FXIcon { +public: +#if FOXVERSION >= FXVERSION(1,7,22) + static void set(FXIcon*ic) { + FXIconThreshold * ih = (FXIconThreshold*) ic; ih->setThresholdValue(ih->guessthresh()); + } +#else + static void set(FXIcon*) {} +#endif + }; + + +/// We need this to allow reading ini files with long lines. +class GMSettings : public FXSettings { + FXDECLARE(GMSettings) +#if FOXVERSION < FXVERSION(1,7,0) +public: + FXbool parseFile(const FXString & filename,FXbool mark); +#endif + }; + +class GMListBox : public FXListBox { + FXDECLARE(GMListBox) +protected: + GMListBox(); +private: + GMListBox(const GMListBox&); + GMListBox& operator=(const GMListBox&); +public: + GMListBox(FXComposite*,FXObject*tgt=NULL,FXSelector sel=0,FXuint opts=FRAME_SUNKEN|FRAME_THICK|LISTBOX_NORMAL,FXint x=0,FXint y=0,FXint w=0,FXint h=0,FXint pl=DEFAULT_PAD,FXint pr=DEFAULT_PAD,FXint pt=DEFAULT_PAD,FXint pb=DEFAULT_PAD); + virtual void create(); + }; + +class GMComboBox : public FXComboBox { + FXDECLARE(GMComboBox) +protected: + GMComboBox(); +private: + GMComboBox(const GMComboBox&); + GMComboBox& operator=(const GMComboBox&); +public: + GMComboBox(FXComposite *p,FXint cols,FXObject* tgt=NULL,FXSelector sel=0,FXuint opts=COMBOBOX_NORMAL,FXint x=0,FXint y=0,FXint w=0,FXint h=0,FXint pl=DEFAULT_PAD,FXint pr=DEFAULT_PAD,FXint pt=DEFAULT_PAD,FXint pb=DEFAULT_PAD); + virtual void create(); + }; + +class GMScrollArea : public FXScrollArea { + FXDECLARE(GMScrollArea) +protected: + GMScrollArea(){} +private: + GMScrollArea(const GMScrollArea&); + GMScrollArea& operator=(const GMScrollArea&); +public: + static void replaceScrollbars(FXScrollArea*); + }; + + + +class GMTreeListBox : public FXTreeListBox { + FXDECLARE(GMTreeListBox) +protected: + GMTreeListBox(){} +private: + GMTreeListBox(const GMTreeListBox&); + GMTreeListBox& operator=(const GMTreeListBox&); +public: + static void replace(FXTreeListBox*); + }; + + +class GMTabBook : public FXTabBook { + FXDECLARE(GMTabBook) +protected: + GMTabBook(){} +private: + GMTabBook(const GMTabBook&); + GMTabBook& operator=(const GMTabBook&); +public: + GMTabBook(FXComposite* p,FXObject* tgt=NULL,FXSelector sel=0,FXuint opts=TABBOOK_NORMAL,FXint x=0,FXint y=0,FXint w=0,FXint h=0,FXint pl=DEFAULT_SPACING,FXint pr=DEFAULT_SPACING,FXint pt=DEFAULT_SPACING,FXint pb=DEFAULT_SPACING); + virtual void setCurrent(FXint panel,FXbool notify=false); + }; + + +class FXAPI GMTabItem : public FXTabItem { + FXDECLARE(GMTabItem) +protected: + GMTabItem(){} +private: + GMTabItem(const GMTabItem&); + GMTabItem& operator=(const GMTabItem&); +public: + long onPaint(FXObject*,FXSelector,void*); +public: + /// Construct a tab item + GMTabItem(FXTabBar* p,const FXString& text,FXIcon* ic=0,FXuint opts=TAB_TOP_NORMAL,FXint x=0,FXint y=0,FXint w=0,FXint h=0,FXint pl=DEFAULT_PAD,FXint pr=DEFAULT_PAD,FXint pt=DEFAULT_PAD,FXint pb=DEFAULT_PAD); + +// virtual void raise(); + }; + + + +class GMHeaderItem : public FXHeaderItem { + FXDECLARE(GMHeaderItem) + friend class GMHeader; +protected: + GMHeaderItem(){} +public: + /// Construct new item with given text, icon, size, and user-data + GMHeaderItem(const FXString& text,FXIcon* ic=NULL,FXint s=0,void* ptr=NULL): FXHeaderItem(text,ic,s,ptr) {} + }; + +class FXAPI GMHeader : public FXHeader { + FXDECLARE(GMHeader) +protected: + GMHeader(); +private: + GMHeader(const GMHeader&); + GMHeader &operator=(const GMHeader&); +public: + long onPaint(FXObject*,FXSelector,void*); +public: + GMHeader(FXComposite* p,FXObject* tgt=NULL,FXSelector sel=0,FXuint opts=HEADER_NORMAL,FXint x=0,FXint y=0,FXint w=0,FXint h=0,FXint pl=DEFAULT_PAD,FXint pr=DEFAULT_PAD,FXint pt=DEFAULT_PAD,FXint pb=DEFAULT_PAD); + }; + + +class GMHeaderButton : public FXButton { +FXDECLARE(GMHeaderButton) +protected: + FXuint arrowstate; +protected: + GMHeaderButton(); +private: + GMHeaderButton(const GMHeaderButton&); + GMHeaderButton& operator=(const GMHeaderButton&); +public: + long onPaint(FXObject*,FXSelector,void*); +public: + /// Construct button with text and icon + GMHeaderButton(FXComposite* p,const FXString& text,FXIcon* ic=NULL,FXObject* tgt=NULL,FXSelector sel=0,FXuint opts=BUTTON_NORMAL,FXint x=0,FXint y=0,FXint w=0,FXint h=0,FXint pl=DEFAULT_PAD,FXint pr=DEFAULT_PAD,FXint pt=DEFAULT_PAD,FXint pb=DEFAULT_PAD); + + void setArrowState(FXuint s); + + FXuint getArrowState() const; + + }; + + + +class GMMenuCommand : public FXMenuCommand { +FXDECLARE(GMMenuCommand) +protected: + GMMenuCommand(){} +private: + GMMenuCommand(const GMMenuCommand&); + GMMenuCommand& operator=(const GMMenuCommand&); +public: + GMMenuCommand(FXComposite* p,const FXString& text,FXIcon* ic=NULL,FXObject* tgt=NULL,FXSelector sel=0,FXuint opts=0); + }; + + +class GMMenuCheck : public FXMenuCheck { +FXDECLARE(GMMenuCheck) +protected: + GMMenuCheck(){} +private: + GMMenuCheck(const GMMenuCheck&); + GMMenuCheck& operator=(const GMMenuCheck&); +public: + GMMenuCheck(FXComposite* p,const FXString& text,FXObject* tgt=NULL,FXSelector sel=0,FXuint opts=0); + }; + + +class GMMenuRadio : public FXMenuRadio { +FXDECLARE(GMMenuRadio) +protected: + GMMenuRadio(){} +private: + GMMenuRadio(const GMMenuRadio&); + GMMenuRadio& operator=(const GMMenuRadio&); +public: + GMMenuRadio(FXComposite* p,const FXString& text,FXObject* tgt=NULL,FXSelector sel=0,FXuint opts=0); + }; + + +class GMMenuCascade : public FXMenuCascade { +FXDECLARE(GMMenuCascade) +protected: + GMMenuCascade(){} +private: + GMMenuCascade(const GMMenuCascade&); + GMMenuCascade& operator=(const GMMenuCascade&); +public: + GMMenuCascade(FXComposite* p,const FXString& text,FXIcon* ic=NULL,FXPopup* pup=NULL,FXuint opts=0); + }; + + + +class GMMenuTitle : public FXMenuTitle { +FXDECLARE(GMMenuTitle) +protected: + GMMenuTitle(){} +private: + GMMenuTitle(const GMMenuTitle&); + GMMenuTitle& operator=(const GMMenuTitle&); +public: + long onPaint(FXObject*,FXSelector,void*); + long onCmdPost(FXObject*,FXSelector,void*); +public: + GMMenuTitle(FXComposite* p,const FXString& text,FXIcon* ic=NULL,FXPopup* pup=NULL,FXuint opts=0); + }; + + +/// Popup menu pane +class GMMenuPane : public FXMenuPane { + FXDECLARE(GMMenuPane) +protected: + GMMenuPane(){} +private: + GMMenuPane(const GMMenuPane&); + GMMenuPane &operator=(const GMMenuPane&); +public: + /// Construct menu pane + GMMenuPane(FXWindow* owner,FXuint opts=0); + }; + + +class GMButton : public FXButton { +FXDECLARE(GMButton) +protected: + GMButton(); +private: + GMButton(const GMButton&); + GMButton& operator=(const GMButton&); +public: + long onPaint(FXObject*,FXSelector,void*); +public: + GMButton(FXComposite* p,const FXString& text,FXIcon* ic=NULL,FXObject* tgt=NULL,FXSelector sel=0,FXuint opts=BUTTON_NORMAL,FXint x=0,FXint y=0,FXint w=0,FXint h=0,FXint pl=DEFAULT_PAD,FXint pr=DEFAULT_PAD,FXint pt=DEFAULT_PAD,FXint pb=DEFAULT_PAD); + }; + + +class GMToggleButton : public FXToggleButton { +FXDECLARE(GMToggleButton) +protected: + GMToggleButton(); +private: + GMToggleButton(const GMToggleButton&); + GMToggleButton& operator=(const GMToggleButton&); +public: + long onPaint(FXObject*,FXSelector,void*); +public: + GMToggleButton(FXComposite* p,const FXString& text1,const FXString& text2,FXIcon* icon1=NULL,FXIcon* icon2=NULL,FXObject* tgt=NULL,FXSelector sel=0,FXuint opts=TOGGLEBUTTON_NORMAL,FXint x=0,FXint y=0,FXint w=0,FXint h=0,FXint pl=DEFAULT_PAD,FXint pr=DEFAULT_PAD,FXint pt=DEFAULT_PAD,FXint pb=DEFAULT_PAD); + }; + +class GMRadioButton : public FXRadioButton { +FXDECLARE(GMRadioButton) +protected: + GMRadioButton(); +private: + GMRadioButton(const GMRadioButton&); + GMRadioButton& operator=(const GMRadioButton&); +public: + GMRadioButton(FXComposite* p,const FXString& text,FXObject* tgt=NULL,FXSelector sel=0,FXuint opts=RADIOBUTTON_NORMAL,FXint x=0,FXint y=0,FXint w=0,FXint h=0,FXint pl=DEFAULT_PAD,FXint pr=DEFAULT_PAD,FXint pt=DEFAULT_PAD,FXint pb=DEFAULT_PAD); + }; + +class GMCheckButton : public FXCheckButton { +FXDECLARE(GMCheckButton) +protected: + GMCheckButton(); +private: + GMCheckButton(const GMCheckButton&); + GMCheckButton& operator=(const GMCheckButton&); +public: + long onPaint(FXObject*,FXSelector,void*); +public: + GMCheckButton(FXComposite* p,const FXString& text,FXObject* tgt=NULL,FXSelector sel=0,FXuint opts=CHECKBUTTON_NORMAL,FXint x=0,FXint y=0,FXint w=0,FXint h=0,FXint pl=DEFAULT_PAD,FXint pr=DEFAULT_PAD,FXint pt=DEFAULT_PAD,FXint pb=DEFAULT_PAD); + }; + +class GMMenuButton : public FXMenuButton { +FXDECLARE(GMMenuButton) +protected: + GMMenuButton(); +private: + GMMenuButton(const GMMenuButton&); + GMMenuButton& operator=(const GMMenuButton&); +public: + long onPaint(FXObject*,FXSelector,void*); +public: + GMMenuButton(FXComposite* p,const FXString& text,FXIcon* ic=NULL,FXPopup* pup=NULL,FXuint opts=JUSTIFY_NORMAL|ICON_BEFORE_TEXT|MENUBUTTON_DOWN,FXint x=0,FXint y=0,FXint w=0,FXint h=0,FXint pl=DEFAULT_PAD,FXint pr=DEFAULT_PAD,FXint pt=DEFAULT_PAD,FXint pb=DEFAULT_PAD); + + virtual FXint getDefaultWidth(); + virtual FXint getDefaultHeight(); + }; + + +class GMTextField : public FXTextField { + FXDECLARE(GMTextField) +protected: + GMTextField(){} +private: + GMTextField(const GMTextField&); + GMTextField& operator=(const GMTextField&); +public: + long onLeftBtnPress(FXObject*,FXSelector,void*); +public: + GMTextField(FXComposite* p,FXint ncols,FXObject* tgt=NULL,FXSelector sel=0,FXuint opts=TEXTFIELD_NORMAL,FXint x=0,FXint y=0,FXint w=0,FXint h=0,FXint pl=DEFAULT_PAD,FXint pr=DEFAULT_PAD,FXint pt=DEFAULT_PAD,FXint pb=DEFAULT_PAD); + + FXbool extendWordSelection(FXint p,FXbool notify=false); + }; + + + +class GMScrollFrame : public FXVerticalFrame { +FXDECLARE(GMScrollFrame) +protected: + GMScrollFrame(); +private: + GMScrollFrame(const GMScrollFrame&); + GMScrollFrame& operator=(const GMScrollFrame&); +public: + GMScrollFrame(FXComposite*p); + }; + +class GMCoverFrame : public FXVerticalFrame { +FXDECLARE(GMCoverFrame) +protected: + GMCoverFrame(); +private: + GMCoverFrame(const GMCoverFrame&); + GMCoverFrame& operator=(const GMCoverFrame&); +public: + GMCoverFrame(FXComposite*p); + }; + + +class GMScrollHFrame : public FXHorizontalFrame { +FXDECLARE(GMScrollHFrame) +protected: + GMScrollHFrame(); +private: + GMScrollHFrame(const GMScrollHFrame&); + GMScrollHFrame& operator=(const GMScrollHFrame&); +public: + GMScrollHFrame(FXComposite*p); + }; + + +class GMTabFrame : public FXVerticalFrame { +FXDECLARE(GMTabFrame) +protected: + GMTabFrame(); +private: + GMTabFrame(const GMTabFrame&); + GMTabFrame& operator=(const GMTabFrame&); +public: + GMTabFrame(FXComposite*p); + }; + + +class GMImageFrame : public FXImageFrame { +FXDECLARE(GMImageFrame) +protected: + GMImageFrame(); +private: + GMImageFrame(const GMImageFrame&); + GMImageFrame& operator=(const GMImageFrame&); +public: + GMImageFrame(FXComposite*p,FXImage *img,FXuint opts=FRAME_SUNKEN|FRAME_THICK,FXint x=0,FXint y=0,FXint w=0,FXint h=0,FXint pl=0,FXint pr=0,FXint pt=0,FXint pb=0); + }; + + +class GMScrollBar : public FXScrollBar { + FXDECLARE(GMScrollBar) +protected: + GMScrollBar(); + void drawThumb(FXDCWindow& dc,FXint x,FXint y,FXint w,FXint h); +private: + GMScrollBar(const GMScrollBar&); + GMScrollBar &operator=(const GMScrollBar&); +public: + long onPaint(FXObject*,FXSelector,void*); +public: + /// Construct scroll bar + GMScrollBar(FXComposite* p,FXObject* tgt=NULL,FXSelector sel=0,FXuint opts=SCROLLBAR_VERTICAL,FXint x=0,FXint y=0,FXint w=0,FXint h=0); + }; + +class GMScrollCorner : public FXScrollCorner { + FXDECLARE(GMScrollCorner) +protected: + FXColor shadowColor; +protected: + GMScrollCorner() {} +private: + GMScrollCorner(const GMScrollCorner&); + GMScrollCorner &operator=(const GMScrollCorner&); +public: + long onPaint(FXObject*,FXSelector,void*); +public: + GMScrollCorner(FXComposite* p); + }; + +class GMSpinner : public FXSpinner { + FXDECLARE(GMSpinner) +protected: + GMSpinner(){} +private: + GMSpinner(const GMSpinner&); + GMSpinner& operator=(const GMSpinner&); +public: + GMSpinner(FXComposite* p,FXint ncols,FXObject* tgt=NULL,FXSelector sel=0,FXuint opts=SPIN_NORMAL,FXint x=0,FXint y=0,FXint w=0,FXint h=0,FXint pl=DEFAULT_PAD,FXint pr=DEFAULT_PAD,FXint pt=DEFAULT_PAD,FXint pb=DEFAULT_PAD); + }; + + +class GMProgressBar : public FXProgressBar { + FXDECLARE(GMProgressBar) +protected: + GMProgressBar(); +private: + GMProgressBar(const GMProgressBar&); + GMProgressBar &operator=(const GMProgressBar&); +public: + /// Construct progress bar + GMProgressBar(FXComposite* p,FXObject* target=NULL,FXSelector sel=0,FXuint opts=PROGRESSBAR_NORMAL,FXint x=0,FXint y=0,FXint w=0,FXint h=0,FXint pl=DEFAULT_PAD,FXint pr=DEFAULT_PAD,FXint pt=DEFAULT_PAD,FXint pb=DEFAULT_PAD); + }; + + +class GMTrackProgressBar : public FXProgressBar { + FXDECLARE(GMTrackProgressBar) +protected: + GMTrackProgressBar(); +private: + GMTrackProgressBar(const GMTrackProgressBar&); + GMTrackProgressBar &operator=(const GMTrackProgressBar&); +public: + long onMotion(FXObject*,FXSelector,void*); + long onLeftBtnPress(FXObject*,FXSelector,void*); + long onLeftBtnRelease(FXObject*,FXSelector,void*); +public: + /// Construct progress bar + GMTrackProgressBar(FXComposite* p,FXObject* target=NULL,FXSelector sel=0,FXuint opts=PROGRESSBAR_NORMAL,FXint x=0,FXint y=0,FXint w=0,FXint h=0,FXint pl=DEFAULT_PAD,FXint pr=DEFAULT_PAD,FXint pt=DEFAULT_PAD,FXint pb=DEFAULT_PAD); + }; + + +class FXImageOwner : public FXImage { +public: + FXImageOwner(); + static void clear(FXImage * img) { FXImageOwner* owner = (FXImageOwner*) img; owner->options&=~IMAGE_OWNED; } + }; + + + +#endif diff --git a/src/gmdefs.h b/src/gmdefs.h new file mode 100644 index 0000000..d6d8e6b --- /dev/null +++ b/src/gmdefs.h @@ -0,0 +1,165 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#ifndef GMDB_H +#define GMDB_H + +#include "gmconfig.h" + +#include +#include +#include +#include +#include +#include + +#define FOXVERSION ((FOX_LEVEL) + (FOX_MINOR*1000) + (FOX_MAJOR*100000)) +#define FXVERSION(major,minor,release) ((release)+(minor*1000)+(major*100000)) +#define SQVERSION(major,minor,release) ((release)+(minor*1000)+(major*1000000)) + +#include "fxext.h" + +#if FOXVERSION < FXVERSION(1,7,0) +#define TIME_NSEC(ns) ((FXuint)(ns/1000000LL)) +#define TIME_MSEC(ms) (ms) +#define TIME_SEC(s) (1000*s) +#define TIME_MIN(m) TIME_SEC(60*m) +#define TIME_HOUR(h) TIME_MIN(60*h) +#else +#define TIME_NSEC(ns) (ns) +#define TIME_SEC(s) (1000000000LL*s) +#define TIME_MSEC(ms) (1000000LL*ms) +#define TIME_MIN(m) TIME_SEC(60*m) +#define TIME_HOUR(h) TIME_MIN(60*h) +#endif + +#if FOXVERSION >= FXVERSION(1,7,12) +#define GMStringVal(str) FXString::value(str) +#define GMStringFormat FXString::value +#define GMFloatVal(str) str.toFloat() +#define GMIntVal(str) (str).toInt() +#else +#define GMStringVal(str) FXStringVal(str) +#define GMStringFormat FXStringFormat +#define GMFloatVal(str) FXFloatVal(str) +#define GMIntVal(str) FXIntVal(str) +#endif + + +#if FOXVERSION >= FXVERSION(1,7,0) +#include +#endif + +#include "GMURL.h" +#include "GMAutoPtr.h" +#if FOXVERSION < FXVERSION(1,7,0) +#include "GMMessageChannel.h" +#endif + + +#ifdef DEBUG +namespace FX { +extern FXlong fxgetticks(); +} +#define GM_DEBUG_PRINT(format, args...) fxmessage (format , ##args) +#else +#define GM_DEBUG_PRINT(arguments, args...) ((void)0) +#endif + +#define UTF8_COPYRIGHT_SIGN "\xc2\xa9" +#define UTF8_ELLIPSIS "…" + +#include "GMTag.h" + +typedef sqlite3 GMDatabaseEngine; +typedef sqlite3_stmt GMDatabaseStatement; +typedef FXArray FXStringList; +typedef FXArray FXIntList; +typedef FXArray FXLongList; +typedef FXAutoPtr FXCursorPtr; +typedef FXAutoPtr FXIconPtr; +typedef FXAutoPtr FXImagePtr; +typedef FXAutoPtr FXMenuPtr; +typedef FXAutoPtr FXFontPtr; +typedef FXAutoPtr FXPopupPtr; + +enum { + REPEAT_OFF = 0, + REPEAT_TRACK, + REPEAT_ALL, + }; + +enum { + SOURCE_UNKNOWN = -1, + SOURCE_INVALID = -1, + SOURCE_DATABASE, + SOURCE_DATABASE_PLAYLIST, + SOURCE_INTERNET_RADIO, + SOURCE_ALBUM, + SOURCE_ARTIST, + SOURCE_AUDIOCD, + }; + +enum { + HEADER_DEFAULT=0, + HEADER_SHUFFLE=1, + HEADER_BROWSE, + HEADER_QUEUE, + HEADER_TRACK, + HEADER_TITLE, + HEADER_ALBUM, + HEADER_ARTIST, + HEADER_TIME, + HEADER_GENRE, + HEADER_BITRATE, + HEADER_RATING, + HEADER_YEAR, + HEADER_DISC, + HEADER_ALBUM_ARTIST, + HEADER_NONE + }; + + + +struct GMStream{ + FXString url; + FXString description; + FXString genre; + FXint bitrate; + }; + +class GMTrackItem; + +extern const FXchar * fxtr(const FXchar *) __attribute__ ((format_arg(1))); + +#define notr(x) x +#define fxtrformat(x) fxtr(x) + + +#define INVALID_TRACK -1 + +#include "GMQuery.h" +#include "GMDatabase.h" + +#include "GMPreferences.h" + +#include "gmutils.h" +#include + +#endif + diff --git a/src/gmutils.cpp b/src/gmutils.cpp new file mode 100644 index 0000000..b95bf53 --- /dev/null +++ b/src/gmutils.cpp @@ -0,0 +1,609 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2009-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#include "gmdefs.h" +#include "GMApp.h" + +#include "FXPNGImage.h" +#include "FXBMPImage.h" +#include "FXJPGImage.h" +#include "FXGIFImage.h" + +#include + +#include +#include +#include +#include +#include +#include + +/******************************************************************************/ + +#include "ap_xml_parser.h" + +#include + +using namespace ap; + +class XSPFParser : public XMLStream{ +public: + FXStringList files; + FXString title; +protected: + FXint elem; +protected: + FXint begin(const FXchar *,const FXchar**); + void data(const FXchar *,FXint len); + void end(const FXchar *); +public: + enum { + Elem_None, + Elem_Playlist, + Elem_Playlist_Title, + Elem_Playlist_TrackList, + Elem_Playlist_TrackList_Track, + Elem_Playlist_TrackList_Track_Location, + }; +public: + XSPFParser(); + ~XSPFParser(); + }; + +XSPFParser::XSPFParser() : elem(Elem_None) { + } + +XSPFParser::~XSPFParser(){ + } + +FXint XSPFParser::begin(const FXchar * element,const FXchar **/* attributes*/){ + switch(elem) { + case Elem_None: + { + if (compare(element,"playlist")==0) { + elem=Elem_Playlist; + return 1; + } + } break; + case Elem_Playlist: + { + if (compare(element,"title")==0) { + elem=Elem_Playlist_Title; + return 1; + } + else if (compare(element,"trackList")==0) { + elem=Elem_Playlist_TrackList; + return 1; + } + } break; + case Elem_Playlist_TrackList: + { + if (compare(element,"track")==0) { + elem=Elem_Playlist_TrackList_Track; + return 1; + } + } break; + case Elem_Playlist_TrackList_Track: + { + if (compare(element,"location")==0) { + elem=Elem_Playlist_TrackList_Track_Location; + return 1; + } + } break; + default: return 0; // skip + } + return 0; + } + + +void XSPFParser::data(const FXchar* str,FXint len){ + if (elem==Elem_Playlist_Title) { + title.assign(str,len); + } + else if (elem==Elem_Playlist_TrackList_Track_Location) { + FXString url(str,len); + files.append(url); + } + } + +void XSPFParser::end(const FXchar*) { + switch(elem){ + case Elem_Playlist_TrackList_Track_Location: elem=Elem_Playlist_TrackList_Track; break; + case Elem_Playlist_TrackList_Track : elem=Elem_Playlist_TrackList; break; + case Elem_Playlist_TrackList : + case Elem_Playlist_Title : elem=Elem_Playlist; break; + case Elem_Playlist : elem=Elem_None; break; + } + } + + +void gm_parse_xspf(const FXString & data,FXStringList & mrl,FXString & title) { + XSPFParser xspf; + if (xspf.parse(data)) { + mrl=xspf.files; + title=xspf.title; + } + } + +/******************************************************************************/ + + + +#define URL_UNSAFE "#$-_.+!*'><()\\,%\"" // Always Encode +#define URL_RESERVED ";/?:@=&" // Only encode if not used as reserved by scheme + + +// Encode url string +FXString gm_url_encode(const FXString& url){ + register FXint p=0; + register FXint c; + FXString result; + while(p='{') || strchr(URL_UNSAFE URL_RESERVED,c))){ + if (!Ascii::isAlphaNumeric(c)){ + result.append('%'); +#if FOXVERSION < FXVERSION(1,7,0) + result.append(FXString::HEX[(c>>4)&15]); + result.append(FXString::HEX[c&15]); +#else + result.append(FXString::value2Digit[(c>>4)&15]); + result.append(FXString::value2Digit[c&15]); +#endif + continue; + } + result.append(c); + } + return result; + } + + +FXString gm_make_url(const FXString & in) { + if (in[0]=='/') + return GMURL::fileToURL(in); + else + return in; + } + +FXdouble gm_parse_number(const FXString & str) { + if (str.empty()) + return NAN; + +/// FOX 1.7 has its own scanf and always uses C locale for number conversions. +#if FOXVERSION > FXVERSION(1,7,0) + FXdouble value=NAN; + if (str.scan("%lg",&value)==1) + return value; + else + return NAN; +#else + errno=0; + FXfloat value = strtod(str.text(),NULL); + if (errno) return NAN; + return value; +#endif + } + + +FXbool gm_buffer_file(const FXString & filename,FXString & buffer) { + FXFile file(filename,FXIO::Reading); + if (file.isOpen()) { + + buffer.assign('\0',file.size()); + + return (file.readBlock((void*)buffer.text(),buffer.length())==buffer.length()); + } + return false; + } + + + +void gm_parse_m3u(FXString & data,FXStringList & mrl) { + FXint start=0,end=0,next; + + for (FXint i=0;istart && Ascii::isSpace(data[end])) end--; + + /// Parse the actual line. + if ((end-start)) { + if (data[start]!='#') { + mrl.append(data.mid(start,1+end-start)); + } + } + start=next; + } + } + } + + +void gm_parse_pls(FXString & data,FXStringList & mrl) { + FXint start=0,end=0,pos,next; + for (FXint i=0;istart && Ascii::isSpace(data[end])) end--; + + /// Parse the actual line. + if ((end-start)>6) { + if (compare(&data[start],"File",4)==0) { + pos = data.find('=',start+4); + if (pos==-1) continue; + pos++; + if (end-pos>0) { + mrl.append(data.mid(pos,1+end-pos)); + } + } + } + start=next; + } + } + } + + + +FXbool gm_has_opengl() { +#if FOXVERSION < FXVERSION(1,7,15) + int glminor,glmajor; + return FXGLVisual::supported(GMApp::instance(),glmajor,glminor); +#else + return FXGLVisual::hasOpenGL(GMApp::instance()); +#endif + } + + + +void gm_focus_and_select(FXTextField * textfield) { + FXASSERT(textfield->id()); + textfield->setFocus(); + if (!textfield->getText().empty()) + textfield->setSelection(0,textfield->getText().length()); + } + +void gm_run_popup_menu(FXMenuPane*pane,FXint rx,FXint ry) { + pane->create(); + pane->forceRefresh(); + pane->show(); + pane->grabKeyboard(); + pane->popup(NULL,rx,ry); + FXApp::instance()->runPopup(pane); + pane->ungrabKeyboard(); + } + +void gm_set_window_cursor(FXWindow * window,FXCursor * cur) { + window->setDefaultCursor(cur); + window->setDragCursor(cur); + FXWindow * child=window->getFirst(); + while(child) { + child->setDefaultCursor(cur); + child->setDragCursor(cur); + child=child->getNext(); + } + } + + + +FXbool gm_is_local_file(const FXString & filename) { + if (filename[0]=='/') return true; + FXString scheme = GMURL::scheme(filename); + if (scheme.empty() || (comparecase(scheme,"file")==0)) + return true; + else + return false; + } + + +FXString gm_parse_uri(const FXString & in) { +#ifndef WIN32 + FXString out=in; + + // non-absolute path or some url + if(out[0]!='/') { + FXString scheme = GMURL::scheme(out); + if (comparecase(scheme,"file")==0){ + out = GMURL::fileFromURL(out); + } + else if (!scheme.empty()) { + return out; + } + } + + /// Make sure we have an absolute path + if (!FXPath::isAbsolute(out)) + out=FXPath::absolute(out); + + return out; +#else +#error "not yet implemented" +#endif + } + + + +void gm_convert_filenames_to_uri(const FXStringList & filenames,FXString & uri){ + if (filenames.no()) { + uri=GMURL::fileToURL(filenames[0]); + for (FXint i=1;i= 3) { + close(i); + } + execlp("/bin/sh", "sh", "-c",exec.text(),(char *)0); + exit(EXIT_FAILURE); + } + else { /// Parent Process + return true; + } + return true; + } + +FXbool gm_open_browser(const FXString & url) { + static const char * const programs[]={"xdg-open","chromium","firefox","konqueror","opera","netscape",NULL}; + return gm_launch_program(programs,url); + } + +FXbool gm_open_folder(const FXString & folder) { + static const char * const programs[]={"xdg-open","thunar","dolphin","konqueror","nautilus",NULL}; + return gm_launch_program(programs,folder); + } + +/******************************************************************************/ + + + +FXImage * gm_load_image_from_data(const void * data,FXuval size,const FXString & mime,FXint scale) { + FXImage * image = NULL; +// GM_DEBUG_PRINT("%s: loading image with mimetype \"%s\"\n",__func__,mime.text()); + if ((comparecase(mime,"image/jpg")==0) || (comparecase(mime,"image/jpeg")==0) || (comparecase(mime,"JPG")==0)) { + image=new FXJPGImage(FXApp::instance()); + } + else if (comparecase(mime,FXPNGImage::mimeType)==0) { + image=new FXPNGImage(FXApp::instance()); + } + else if ((comparecase(mime,"image/bmp")==0) || (comparecase(mime,"image/x-bmp")==0) ) { + image=new FXBMPImage(FXApp::instance()); + } + else if ((comparecase(mime,FXGIFImage::mimeType)==0)) { + image=new FXGIFImage(FXApp::instance()); + } + else { + GM_DEBUG_PRINT("%s: Mimetype \"%s\" not handled\n",__func__,mime.text()); + } + + if (image) { + FXMemoryStream store; +#if FOXVERSION < FXVERSION(1,7,18) + store.open(FXStreamLoad,size,(FXuchar*)data); +#else + store.open(FXStreamLoad,(FXuchar*)data,size); +#endif + if (image->loadPixels(store)) { + if (scale) { + if ((image->getWidth()>scale) || (image->getHeight()>scale)) { + if (image->getWidth()>image->getHeight()) + image->scale(scale,(scale*image->getHeight())/image->getWidth(),1); + else + image->scale((scale*image->getWidth())/image->getHeight(),scale,1); + } + } + store.close(); + return image; + } + store.close(); + delete image; + } + return NULL; + } + + + + +FXbool gm_make_path(const FXString & path,FXuint perm) { +#if FOXVERSION < FXVERSION(1,7,0) + if(!path.empty()){ + if(FXStat::isDirectory(path)) return true; + if(gm_make_path(FXPath::upLevel(path),perm)){ + if(FXDir::create(path,perm)) return true; + } + } + return false; +#else + return FXDir::createDirectories(path,perm); +#endif + } + + +FXbool gm_decode_base64(FXuchar * buffer,FXint & len){ + static const FXuchar base64[256]={ + 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, + 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, + 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x3e,0x80,0x80,0x80,0x3f, + 0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x80,0x80,0x80,0x80,0x80,0x80, + 0x80,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e, + 0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x80,0x80,0x80,0x80,0x80, + 0x80,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28, + 0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x30,0x31,0x32,0x33,0x80,0x80,0x80,0x80,0x80, + 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, + 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, + 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, + 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, + 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, + 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, + 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, + 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}; + + FXuint pos=0; + FXuchar v; + for (FXint i=0,b=0;i>4); + buffer[pos]=(v<<4); + b++; + break; + case 2: buffer[pos++]|=(v>>2); + buffer[pos]=(v<<6); + b++; + break; + case 3: buffer[pos++]|=v; + b=0; + break; + } + } + else { + if (buffer[i]=='=' && b>1) { + len=pos; + return true; + } + else { + return false; + } + } + } + len=pos; + return true; + } + + +void gm_colorize_bitmap(FXImage * icon,FXColor nc) { + FXColor color; + for (FXint y=0;ygetHeight();y++){ + for (FXint x=0;xgetWidth();x++){ + color=icon->getPixel(x,y); + if (FXALPHAVAL(color)>0) { + icon->setPixel(x,y,FXRGBA(FXREDVAL(nc),FXGREENVAL(nc),FXBLUEVAL(nc),FXALPHAVAL(color))); + } + } + } + } + +void gm_bgra_to_rgba(FXColor * inbuf,FXColor * outbuf, FXint len) { + FXuchar * in = reinterpret_cast(inbuf); + FXuchar * out = reinterpret_cast(outbuf); + for (FXint i=0;i<(len*4);i+=4) { + out[i+0]=in[i+2]; // r + out[i+1]=in[i+1]; // g + out[i+2]=in[i+0]; // b + out[i+3]=in[i+3]; // a + } + } + + diff --git a/src/gmutils.h b/src/gmutils.h new file mode 100644 index 0000000..4e90096 --- /dev/null +++ b/src/gmutils.h @@ -0,0 +1,71 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2009-2011 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#ifndef GMUTILS_H +#define GMUTILS_H + +extern FXbool gm_has_opengl(); + +extern FXbool gm_open_folder(const FXString & folder); + +extern FXbool gm_open_browser(const FXString & folder); + +extern FXString gm_url_encode(const FXString& url); + +extern FXString gm_make_url(const FXString& in); + +extern FXdouble gm_parse_number(const FXString &); + +extern FXbool gm_buffer_file(const FXString & filename,FXString & buffer); + +extern void gm_make_absolute_path(const FXString & path,FXStringList & urls); + +extern void gm_parse_pls(FXString & data,FXStringList & mrl); + +extern void gm_parse_m3u(FXString & data,FXStringList & mrl); + +extern void gm_parse_xspf(const FXString & data,FXStringList & mrl,FXString & title); + +extern void gm_set_window_cursor(FXWindow *,FXCursor*); + +extern void gm_focus_and_select(FXTextField*); + +extern void gm_run_popup_menu(FXMenuPane*,FXint rx,FXint ry); + +extern FXbool gm_is_local_file(const FXString & filename); + +extern FXString gm_parse_uri(const FXString& uri); + +extern void gm_convert_filenames_to_uri(const FXStringList & filenames,FXString & uri); + +extern void gm_convert_filenames_to_gnomeclipboard(const FXStringList & filenames,FXString & clipboard); + +extern void gm_convert_gnomeclipboard_to_filenames(FXString & clipboard,FXStringList & filenames); + +extern void gm_convert_uri_to_filenames(FXString & uri,FXStringList & filenames); + +extern FXImage * gm_load_image_from_data(const void * data,FXuval size,const FXString & mime,FXint scale); + +extern FXbool gm_make_path(const FXString & path,FXuint perm=FXIO::OwnerFull|FXIO::GroupFull|FXIO::OtherFull); + +extern FXbool gm_decode_base64(FXuchar * buffer,FXint & len); + +extern void gm_bgra_to_rgba(FXColor * in,FXColor * out,FXint len); + +extern void gm_colorize_bitmap(FXImage*,FXColor); +#endif diff --git a/src/gogglesmm.xml b/src/gogglesmm.xml new file mode 100644 index 0000000..9d5afa6 --- /dev/null +++ b/src/gogglesmm.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..ec4d768 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,102 @@ +/******************************************************************************* +* Goggles Music Manager * +******************************************************************************** +* Copyright (C) 2006-2010 by Sander Jansen. All Rights Reserved * +* --- * +* This program is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program. If not, see http://www.gnu.org/licenses. * +********************************************************************************/ +#include "gmdefs.h" +#include "GMList.h" +#include "GMSource.h" +#include "GMPlayerManager.h" + +/* + Display Help/Version + returns true if application may exit + TODO expand to all possible options +*/ +static bool gm_display_help(int argc,char * argv[]) { + for (int i=1;i0) + fxmessage("Usage: %s [options]\n\n",FXPath::name(argv[0]).text()); + else + fxmessage("Usage: gogglesmm [options]\n\n"); + + fxmessage("General options:\n" + " -h, --help Display this help page\n" + " -v, --version Display version information\n" + " --tray Start minimized to tray\n" + " --disable-opengl Disables opengl based features\n" + "\n" + "Control running music manager:\n" + " --play Start playback\n" + " --play-pause Toggle pause / playback.\n" + " --pause Pause playback\n" + " --previous Play previous track\n" + " --next Play next track\n" + " --stop Stop playback\n" + " --raise Try to raise the main window\n" + " --toggle-shown Show or Hide the main window\n" + " --now-playing Show now playing notification\n" + "\n" + "Debugging options:\n" + " --xine-debug Output xine debug info\n" + ); + return true; + } + else if ( (comparecase(argv[i],"--version")==0) || (comparecase(argv[i],"-v")==0) ) { + fxmessage("Goggles Music Manager %s\n",APPLICATION_VERSION_STRING); + return true; + } + } + return false; + } + + +int main(int argc,char *argv[]){ + + /// Check and make sure we're linked correctly to FOX + /// If we're not linked correctly, we cannot obviously popup a dialog... + if (fxversion[0]==1 && (fxversion[1]==6) ) { /// Test for Stable Version of FOX 1.6 + if (FOX_MAJOR!=fxversion[0] || FOX_MINOR!=fxversion[1]){ + fxwarning("FOX Header (v%d.%d.%d) / Library (v%d.%d.%d) mismatch! -\n",FOX_MAJOR,FOX_MINOR,FOX_LEVEL,fxversion[0],fxversion[1],fxversion[2]); + return 1; + } + } + else if (fxversion[0]==1 && ( fxversion[1]==7)) { /// Test for Development version of FOX 1.7 + if (FOX_MAJOR!=fxversion[0] || FOX_MINOR!=fxversion[1] || FOX_LEVEL!=fxversion[2]) { + fxwarning("FOX Header (v%d.%d.%d) / Library (v%d.%d.%d) mismatch! -\n",FOX_MAJOR,FOX_MINOR,FOX_LEVEL,fxversion[0],fxversion[1],fxversion[2]); + return 1; + } + } + else { + fxwarning("Goggles Music Manager linked to a unknown/unsupported version of the FOX Library (v%d.%d.%d)",fxversion[0],fxversion[1],fxversion[2]); + return 1; + } + + /// Display Help + if (gm_display_help(argc,argv)) + return 0; + + /// Main Application + GMPlayerManager gogglesmm; + return gogglesmm.run(argc,argv); + } + + + + + + diff --git a/src/md5.cpp b/src/md5.cpp new file mode 100644 index 0000000..c35d96c --- /dev/null +++ b/src/md5.cpp @@ -0,0 +1,381 @@ +/* + Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost@aladdin.com + + */ +/* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321, whose + text is available at + http://www.ietf.org/rfc/rfc1321.txt + The code is derived from the text of the RFC, including the test suite + (section A.5) but excluding the rest of Appendix A. It does not include + any code or documentation that is identified in the RFC as being + copyrighted. + + The original and principal author of md5.c is L. Peter Deutsch + . Other authors are noted in the change history + that follows (in reverse chronological order): + + 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order + either statically or dynamically; added missing #include + in library. + 2002-03-11 lpd Corrected argument list for main(), and added int return + type, in test program and T value program. + 2002-02-21 lpd Added missing #include in test program. + 2000-07-03 lpd Patched to eliminate warnings about "constant is + unsigned in ANSI C, signed in traditional"; made test program + self-checking. + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). + 1999-05-03 lpd Original version. + */ + +#include "md5.h" +#include + +#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */ +#ifdef ARCH_IS_BIG_ENDIAN +# define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1) +#else +# define BYTE_ORDER 0 +#endif + +#define T_MASK ((md5_word_t)~0) +#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87) +#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9) +#define T3 0x242070db +#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111) +#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050) +#define T6 0x4787c62a +#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec) +#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe) +#define T9 0x698098d8 +#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850) +#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e) +#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841) +#define T13 0x6b901122 +#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c) +#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71) +#define T16 0x49b40821 +#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d) +#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf) +#define T19 0x265e5a51 +#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855) +#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2) +#define T22 0x02441453 +#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e) +#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437) +#define T25 0x21e1cde6 +#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829) +#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278) +#define T28 0x455a14ed +#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa) +#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07) +#define T31 0x676f02d9 +#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375) +#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd) +#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e) +#define T35 0x6d9d6122 +#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3) +#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb) +#define T38 0x4bdecfa9 +#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f) +#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f) +#define T41 0x289b7ec6 +#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805) +#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a) +#define T44 0x04881d05 +#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6) +#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a) +#define T47 0x1fa27cf8 +#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a) +#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb) +#define T50 0x432aff97 +#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58) +#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6) +#define T53 0x655b59c3 +#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d) +#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82) +#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e) +#define T57 0x6fa87e4f +#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f) +#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb) +#define T60 0x4e0811a1 +#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d) +#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca) +#define T63 0x2ad7d2bb +#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e) + + +static void +md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) +{ + md5_word_t + a = pms->abcd[0], b = pms->abcd[1], + c = pms->abcd[2], d = pms->abcd[3]; + md5_word_t t; +#if BYTE_ORDER > 0 + /* Define storage only for big-endian CPUs. */ + md5_word_t X[16]; +#else + /* Define storage for little-endian or both types of CPUs. */ + md5_word_t xbuf[16]; + const md5_word_t *X; +#endif + + { +#if BYTE_ORDER == 0 + /* + * Determine dynamically whether this is a big-endian or + * little-endian machine, since we can use a more efficient + * algorithm on the latter. + */ + static const int w = 1; + + if (*((const md5_byte_t *)&w)) /* dynamic little-endian */ +#endif +#if BYTE_ORDER <= 0 /* little-endian */ + { + /* + * On little-endian machines, we can process properly aligned + * data without copying it. + */ + if (!((data - (const md5_byte_t *)0) & 3)) { + /* data are properly aligned */ + X = (const md5_word_t *)data; + } else { + /* not aligned */ + memcpy(xbuf, data, 64); + X = xbuf; + } + } +#endif +#if BYTE_ORDER == 0 + else /* dynamic big-endian */ +#endif +#if BYTE_ORDER >= 0 /* big-endian */ + { + /* + * On big-endian machines, we must arrange the bytes in the + * right order. + */ + const md5_byte_t *xp = data; + int i; + +# if BYTE_ORDER == 0 + X = xbuf; /* (dynamic only) */ +# else +# define xbuf X /* (static only) */ +# endif + for (i = 0; i < 16; ++i, xp += 4) + xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); + } +#endif + } + +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) + + /* Round 1. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ +#define F(x, y, z) (((x) & (y)) | (~(x) & (z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + F(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 7, T1); + SET(d, a, b, c, 1, 12, T2); + SET(c, d, a, b, 2, 17, T3); + SET(b, c, d, a, 3, 22, T4); + SET(a, b, c, d, 4, 7, T5); + SET(d, a, b, c, 5, 12, T6); + SET(c, d, a, b, 6, 17, T7); + SET(b, c, d, a, 7, 22, T8); + SET(a, b, c, d, 8, 7, T9); + SET(d, a, b, c, 9, 12, T10); + SET(c, d, a, b, 10, 17, T11); + SET(b, c, d, a, 11, 22, T12); + SET(a, b, c, d, 12, 7, T13); + SET(d, a, b, c, 13, 12, T14); + SET(c, d, a, b, 14, 17, T15); + SET(b, c, d, a, 15, 22, T16); +#undef SET + + /* Round 2. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ +#define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + G(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 1, 5, T17); + SET(d, a, b, c, 6, 9, T18); + SET(c, d, a, b, 11, 14, T19); + SET(b, c, d, a, 0, 20, T20); + SET(a, b, c, d, 5, 5, T21); + SET(d, a, b, c, 10, 9, T22); + SET(c, d, a, b, 15, 14, T23); + SET(b, c, d, a, 4, 20, T24); + SET(a, b, c, d, 9, 5, T25); + SET(d, a, b, c, 14, 9, T26); + SET(c, d, a, b, 3, 14, T27); + SET(b, c, d, a, 8, 20, T28); + SET(a, b, c, d, 13, 5, T29); + SET(d, a, b, c, 2, 9, T30); + SET(c, d, a, b, 7, 14, T31); + SET(b, c, d, a, 12, 20, T32); +#undef SET + + /* Round 3. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + H(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 5, 4, T33); + SET(d, a, b, c, 8, 11, T34); + SET(c, d, a, b, 11, 16, T35); + SET(b, c, d, a, 14, 23, T36); + SET(a, b, c, d, 1, 4, T37); + SET(d, a, b, c, 4, 11, T38); + SET(c, d, a, b, 7, 16, T39); + SET(b, c, d, a, 10, 23, T40); + SET(a, b, c, d, 13, 4, T41); + SET(d, a, b, c, 0, 11, T42); + SET(c, d, a, b, 3, 16, T43); + SET(b, c, d, a, 6, 23, T44); + SET(a, b, c, d, 9, 4, T45); + SET(d, a, b, c, 12, 11, T46); + SET(c, d, a, b, 15, 16, T47); + SET(b, c, d, a, 2, 23, T48); +#undef SET + + /* Round 4. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ +#define I(x, y, z) ((y) ^ ((x) | ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + I(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 6, T49); + SET(d, a, b, c, 7, 10, T50); + SET(c, d, a, b, 14, 15, T51); + SET(b, c, d, a, 5, 21, T52); + SET(a, b, c, d, 12, 6, T53); + SET(d, a, b, c, 3, 10, T54); + SET(c, d, a, b, 10, 15, T55); + SET(b, c, d, a, 1, 21, T56); + SET(a, b, c, d, 8, 6, T57); + SET(d, a, b, c, 15, 10, T58); + SET(c, d, a, b, 6, 15, T59); + SET(b, c, d, a, 13, 21, T60); + SET(a, b, c, d, 4, 6, T61); + SET(d, a, b, c, 11, 10, T62); + SET(c, d, a, b, 2, 15, T63); + SET(b, c, d, a, 9, 21, T64); +#undef SET + + /* Then perform the following additions. (That is increment each + of the four registers by the value it had before this block + was started.) */ + pms->abcd[0] += a; + pms->abcd[1] += b; + pms->abcd[2] += c; + pms->abcd[3] += d; +} + +void +md5_init(md5_state_t *pms) +{ + pms->count[0] = pms->count[1] = 0; + pms->abcd[0] = 0x67452301; + pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476; + pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301; + pms->abcd[3] = 0x10325476; +} + +void +md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes) +{ + const md5_byte_t *p = data; + int left = nbytes; + int offset = (pms->count[0] >> 3) & 63; + md5_word_t nbits = (md5_word_t)(nbytes << 3); + + if (nbytes <= 0) + return; + + /* Update the message length. */ + pms->count[1] += nbytes >> 29; + pms->count[0] += nbits; + if (pms->count[0] < nbits) + pms->count[1]++; + + /* Process an initial partial block. */ + if (offset) { + int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); + + memcpy(pms->buf + offset, p, copy); + if (offset + copy < 64) + return; + p += copy; + left -= copy; + md5_process(pms, pms->buf); + } + + /* Process full blocks. */ + for (; left >= 64; p += 64, left -= 64) + md5_process(pms, p); + + /* Process a final partial block. */ + if (left) + memcpy(pms->buf, p, left); +} + +void +md5_finish(md5_state_t *pms, md5_byte_t digest[16]) +{ + static const md5_byte_t pad[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + md5_byte_t data[8]; + int i; + + /* Save the length before padding. */ + for (i = 0; i < 8; ++i) + data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); + /* Pad to 56 bytes mod 64. */ + md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); + /* Append the length. */ + md5_append(pms, data, 8); + for (i = 0; i < 16; ++i) + digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); +} diff --git a/src/md5.h b/src/md5.h new file mode 100644 index 0000000..698c995 --- /dev/null +++ b/src/md5.h @@ -0,0 +1,91 @@ +/* + Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost@aladdin.com + + */ +/* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321, whose + text is available at + http://www.ietf.org/rfc/rfc1321.txt + The code is derived from the text of the RFC, including the test suite + (section A.5) but excluding the rest of Appendix A. It does not include + any code or documentation that is identified in the RFC as being + copyrighted. + + The original and principal author of md5.h is L. Peter Deutsch + . Other authors are noted in the change history + that follows (in reverse chronological order): + + 2002-04-13 lpd Removed support for non-ANSI compilers; removed + references to Ghostscript; clarified derivation from RFC 1321; + now handles byte order either statically or dynamically. + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); + added conditionalization for C++ compilation from Martin + Purschke . + 1999-05-03 lpd Original version. + */ + +#ifndef md5_INCLUDED +# define md5_INCLUDED + +/* + * This package supports both compile-time and run-time determination of CPU + * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be + * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is + * defined as non-zero, the code will be compiled to run only on big-endian + * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to + * run on either big- or little-endian CPUs, but will run slightly less + * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined. + */ + +typedef unsigned char md5_byte_t; /* 8-bit byte */ +typedef unsigned int md5_word_t; /* 32-bit word */ + +/* Define the state of the MD5 Algorithm. */ +typedef struct md5_state_s { + md5_word_t count[2]; /* message length in bits, lsw first */ + md5_word_t abcd[4]; /* digest buffer */ + md5_byte_t buf[64]; /* accumulate block */ +} md5_state_t; + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* Initialize the algorithm. */ +void md5_init(md5_state_t *pms); + +/* Append a string to the message. */ +void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); + +/* Finish the message and return the digest. */ +void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); + +#ifdef __cplusplus +} /* end extern "C" */ +#endif + +#endif /* md5_INCLUDED */ diff --git a/src/mpris_player.xml b/src/mpris_player.xml new file mode 100644 index 0000000..dac68be --- /dev/null +++ b/src/mpris_player.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/mpris_root.xml b/src/mpris_root.xml new file mode 100644 index 0000000..a284169 --- /dev/null +++ b/src/mpris_root.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + diff --git a/src/mpris_tracklist.xml b/src/mpris_tracklist.xml new file mode 100644 index 0000000..b7622ef --- /dev/null +++ b/src/mpris_tracklist.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -- 2.30.2