]> git.sur5r.net Git - minitube/commitdiff
Imported Upstream version 1.0 upstream/1.0
authorJakob Haufe <sur5r@sur5r.net>
Mon, 6 Aug 2012 20:31:33 +0000 (22:31 +0200)
committerJakob Haufe <sur5r@sur5r.net>
Mon, 6 Aug 2012 20:31:33 +0000 (22:31 +0200)
129 files changed:
.gitignore [new file with mode: 0644]
AUTHORS [new file with mode: 0644]
CHANGES [new file with mode: 0644]
COPYING [new file with mode: 0644]
INSTALL [new file with mode: 0644]
Info.plist [new file with mode: 0644]
LICENSE.LGPL [new file with mode: 0644]
TODO [new file with mode: 0644]
data/128x128/minitube.png [new file with mode: 0644]
data/16x16/minitube.png [new file with mode: 0644]
data/256x256/minitube.png [new file with mode: 0644]
data/32x32/minitube.png [new file with mode: 0644]
data/48x48/minitube.png [new file with mode: 0644]
data/64x64/minitube.png [new file with mode: 0644]
data/minitube.svg [new file with mode: 0644]
images/app.png [new file with mode: 0644]
images/media-playback-pause.png [new file with mode: 0644]
images/media-playback-start.png [new file with mode: 0644]
images/media-playback-stop.png [new file with mode: 0644]
images/media-skip-forward.png [new file with mode: 0644]
images/view-fullscreen.png [new file with mode: 0644]
locale/ar.ts [new file with mode: 0644]
locale/bg_BG.ts [new file with mode: 0644]
locale/calculate_completion.sh [new file with mode: 0755]
locale/cs_CZ.ts [new file with mode: 0644]
locale/de_DE.ts [new file with mode: 0644]
locale/el_GR.ts [new file with mode: 0644]
locale/es.ts [new file with mode: 0644]
locale/es_AR.ts [new file with mode: 0644]
locale/es_ES.ts [new file with mode: 0644]
locale/fi_FI.ts [new file with mode: 0644]
locale/fr_FR.ts [new file with mode: 0644]
locale/gl.ts [new file with mode: 0644]
locale/he_IL.ts [new file with mode: 0644]
locale/hr_HR.ts [new file with mode: 0644]
locale/hu_HU.ts [new file with mode: 0644]
locale/it_IT.ts [new file with mode: 0644]
locale/ja_JP.ts [new file with mode: 0644]
locale/lat.ts [new file with mode: 0644]
locale/locale.pri [new file with mode: 0644]
locale/lupdate.sh [new file with mode: 0755]
locale/nb_NO.ts [new file with mode: 0644]
locale/nl_NL.ts [new file with mode: 0644]
locale/pl_PL.ts [new file with mode: 0644]
locale/pt_BR.ts [new file with mode: 0644]
locale/pt_PT.ts [new file with mode: 0644]
locale/ro_RO.ts [new file with mode: 0644]
locale/ru_RU.ts [new file with mode: 0644]
locale/tr_TR.ts [new file with mode: 0644]
locale/uk.ts [new file with mode: 0644]
minitube.desktop [new file with mode: 0644]
minitube.icns [new file with mode: 0644]
minitube.ico [new file with mode: 0644]
minitube.pro [new file with mode: 0755]
minitube.rc [new file with mode: 0644]
resources.qrc [new file with mode: 0755]
src/AboutView.cpp [new file with mode: 0644]
src/AboutView.h [new file with mode: 0644]
src/Constants.h [new file with mode: 0755]
src/ListModel.cpp [new file with mode: 0755]
src/ListModel.h [new file with mode: 0755]
src/MainWindow.cpp [new file with mode: 0755]
src/MainWindow.h [new file with mode: 0755]
src/MediaView.cpp [new file with mode: 0644]
src/MediaView.h [new file with mode: 0644]
src/SearchView.cpp [new file with mode: 0644]
src/SearchView.h [new file with mode: 0644]
src/View.h [new file with mode: 0644]
src/faderwidget/FaderWidget.cpp [new file with mode: 0644]
src/faderwidget/FaderWidget.h [new file with mode: 0644]
src/flickcharm.cpp [new file with mode: 0644]
src/flickcharm.h [new file with mode: 0644]
src/global.h [new file with mode: 0644]
src/googlesuggest.cpp [new file with mode: 0644]
src/googlesuggest.h [new file with mode: 0644]
src/iconloader/qticonloader.cpp [new file with mode: 0644]
src/iconloader/qticonloader.h [new file with mode: 0644]
src/loadingwidget.cpp [new file with mode: 0644]
src/loadingwidget.h [new file with mode: 0644]
src/main.cpp [new file with mode: 0755]
src/minisplitter.cpp [new file with mode: 0644]
src/minisplitter.h [new file with mode: 0644]
src/networkaccess.cpp [new file with mode: 0644]
src/networkaccess.h [new file with mode: 0644]
src/playlist/PrettyItemDelegate.cpp [new file with mode: 0644]
src/playlist/PrettyItemDelegate.h [new file with mode: 0644]
src/playlistwidget.cpp [new file with mode: 0644]
src/playlistwidget.h [new file with mode: 0644]
src/qtsingleapplication/QtLockedFile [new file with mode: 0644]
src/qtsingleapplication/QtSingleApplication [new file with mode: 0644]
src/qtsingleapplication/qtlocalpeer.cpp [new file with mode: 0644]
src/qtsingleapplication/qtlocalpeer.h [new file with mode: 0644]
src/qtsingleapplication/qtlockedfile.cpp [new file with mode: 0644]
src/qtsingleapplication/qtlockedfile.h [new file with mode: 0644]
src/qtsingleapplication/qtlockedfile_unix.cpp [new file with mode: 0644]
src/qtsingleapplication/qtlockedfile_win.cpp [new file with mode: 0644]
src/qtsingleapplication/qtsingleapplication.cpp [new file with mode: 0644]
src/qtsingleapplication/qtsingleapplication.h [new file with mode: 0644]
src/qtsingleapplication/qtsingleapplication.pri [new file with mode: 0644]
src/qtsingleapplication/qtsinglecoreapplication.cpp [new file with mode: 0644]
src/qtsingleapplication/qtsinglecoreapplication.h [new file with mode: 0644]
src/qtsingleapplication/qtsinglecoreapplication.pri [new file with mode: 0644]
src/searchlineedit.cpp [new file with mode: 0644]
src/searchlineedit.h [new file with mode: 0644]
src/searchparams.cpp [new file with mode: 0644]
src/searchparams.h [new file with mode: 0644]
src/spacer.cpp [new file with mode: 0644]
src/spacer.h [new file with mode: 0644]
src/thlibrary/thblackbar.cpp [new file with mode: 0644]
src/thlibrary/thblackbar.h [new file with mode: 0644]
src/thlibrary/thlibrary.pri [new file with mode: 0644]
src/updatechecker.cpp [new file with mode: 0644]
src/updatechecker.h [new file with mode: 0644]
src/urllineedit.cpp [new file with mode: 0644]
src/urllineedit.h [new file with mode: 0644]
src/video.cpp [new file with mode: 0644]
src/video.h [new file with mode: 0644]
src/videoareawidget.cpp [new file with mode: 0644]
src/videoareawidget.h [new file with mode: 0644]
src/videodefinition.cpp [new file with mode: 0644]
src/videodefinition.h [new file with mode: 0644]
src/videomimedata.cpp [new file with mode: 0644]
src/videomimedata.h [new file with mode: 0644]
src/videowidget.cpp [new file with mode: 0644]
src/videowidget.h [new file with mode: 0644]
src/youtubesearch.cpp [new file with mode: 0644]
src/youtubesearch.h [new file with mode: 0644]
src/youtubestreamreader.cpp [new file with mode: 0644]
src/youtubestreamreader.h [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..a2f6691
--- /dev/null
@@ -0,0 +1,7 @@
+build/
+Makefile*
+minitube.pro.user
+.settings/
+.DS_Store
+.cproject
+.project
diff --git a/AUTHORS b/AUTHORS
new file mode 100644 (file)
index 0000000..b553224
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1 @@
+Flavio Tordini flavio.tordini@gmail.com
diff --git a/CHANGES b/CHANGES
new file mode 100644 (file)
index 0000000..77d1e71
--- /dev/null
+++ b/CHANGES
@@ -0,0 +1,113 @@
+1.0 - April ?, 2010
+- Ability to play Full HD (1080p) videos
+- Ability to copy the YouTube link and the video stream URL to the clipboard
+- Fixed videos failing to play
+- Fixed missing caret in the search box
+- Better toolbar and icon theme integration on Linux with Qt >= 4.6
+- Completely removed tooltips
+- Romanian translation by Ovidiu Niţan
+- Greek translation by Giorgos Skettos
+- Dutch translation by Brian Keetman
+- Arabic translation by Sderawi
+- Portuguese translation by Daniel Rodrigues
+- Finnish translation by Jesse Jaara
+- Bulgarian translation by Tsvyatko Makazchiev
+
+0.9 - January 22, 2010
+- Ability to clear recent keywords (by popular demand!)
+- Show the toolbar when mouse hits the top of the screen in Fullscreen mode (Linux only)
+- Show the playlist when mouse hits the left side of the screen in Fullscreen mode (Linux only)
+- Fixed toolbar search suggestions working just once (Thanks to Salvatore Benedetto)
+- Fixed toobar search incorrect handling of keys that also represent a shortcut (like Escape or Space)
+- Fixed time formatting bug with videos longer than an hour (Thanks to Rene Bogusch)
+- Fixed long queries in the recent keywords list (Thanks to Vadim P.)
+- Norwegian translation by Jan W. Skjoldal
+
+0.8.1 - November 20, 2009
+- Fix showstopper bug: Normal quality videos won't play when HD mode is enabled
+- Turkish translation by Ali E. İmrek
+
+0.8 - November 16, 2009
+- HD video support
+- Volume level and mute is restored accross sessions
+- No icons in menus on Linux (GNOME 2.28 style)
+- Select search box text on Ctrl+F
+- Handle HTTP_PROXY variable with trailing slash (Fix by Eduardo Suarez-Santana)
+- Croatian translation by Srecko Belaic
+- Latvian translation by Inga Muste
+- Galician and Neutral Spanish translations by Miguel Anxo Bouzada
+- Hungarian translation by Krisztián Horváth
+- French translation by Guillaume Betous
+
+0.7 - October 12, 2009
+- Fixed "embedding disabled by request" message. All videos now play.
+- Slightly faster playlist painting
+- Fixed overflowing text in the playlist
+- Fixed view crossfades on the Mac
+- Fixed FreeDesktop icons on Linux
+
+0.6.1 - September 15, 2009
+- Fixed showstopper bug caused by a change in the YouTube web service
+  Thanks to Guillaume Girard for reporting it
+
+0.6 - September 7, 2009
+- New search view look
+- Hide idle mouse cursor when above the video (Linux only)
+- Volume keyboard shortcuts
+- Keyboard shortcuts appear in the status bar
+- Better error reporting
+- Playlist width is saved accross restarts
+- HTTP proxy support contributed by Kiwamu Okabe
+- Fixed Space key not pausing videos in fullscreen mode (Mac only)
+- Fixed window losing focus after exiting fullscreen (Mac only)
+- Spanish translation by Rafa
+- Japanese translation by Kiwamu Okabe
+- Czech translation by Dan Vrátil
+- Hebrew translation by Yaron Shahrabani
+
+0.5 - July 31, 2009
+- Search autocomplete
+- Skip to the next when a video cannot be loaded
+- Playlist items can now be dropped onto the video area
+- Fixed messages in the playlist not updating
+- Disabled view crossfade on Linux
+- Save keywords only when there are results
+- Fixed fullscreen keyboard shortcurt not working
+- Polish translation by Grzegorz Gibas
+
+0.4 - July 2, 2009
+- Finally fixed playback start problem on Mac OSX
+- Display title and description while video is loading
+- Compact mode, contributed by Stefan Brück
+- Current video position and total time in the status bar
+- Keyboard shortcuts now work when in full screen mode
+- Fixed crash occurring when pressing stop during a search
+- Argentin spanish translation by Sergio Tocalini Joerg
+- German translation by Stefan Brück
+
+0.3 - June 15, 2009
+- Can sort videos by relevance, date and popularity
+- Doubleclick on video goes full screen
+- Video context menu
+- Can remove videos using the Backspace key, Mac laptops lack a Delete key
+- Keyboard shortcut to give focus to the search box
+- Load thumbnails asynchronously
+- Fixed wrong (absurdly high) number views on some videos
+- Now Minitube is ready to be translated. Italian, Russian and Portuguese translations available
+- Cosmetics
+
+0.2.1 - June 1, 2009
+- Fixed showstopper bug on Linux: Minitube fails to automatically play the next video
+
+0.2 - May 29, 2009
+- Faster playlist results
+- Ability to (re)move selected playlist items
+- Drag'n'drop playlist items
+- Uses less memory
+- Basic fullscreen mode now works
+- Show the total number of views of a video
+- Video duration is now overlayed on the thumb
+- Update notifier
+
+0.1 - May 15, 2009
+First release
diff --git a/COPYING b/COPYING
new file mode 100644 (file)
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. <http://fsf.org/>
+ 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.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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/>.
+
+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:
+
+    <program>  Copyright (C) <year>  <name of author>
+    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
+<http://www.gnu.org/licenses/>.
+
+  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
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/INSTALL b/INSTALL
new file mode 100644 (file)
index 0000000..a6db61f
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,48 @@
+# Build instructions
+
+## Prerequisites
+
+To compile Minitube you need Qt >= 4.5 installed.
+
+On a Debian or Ubuntu system just type:
+    sudo apt-get install build-essential qt4-dev-tools libphonon-dev
+
+Windows and Mac users can get the Qt libraries from:
+http://qt.nokia.com/downloads
+
+## Compiling
+
+Compiling on Linux is fairly easy. Just run:
+    qmake
+and then
+    make
+
+Beware of the Qt3 version of qmake! If things go wrong try running qmake-qt4 instead.
+
+## Running
+
+Just type:
+    ./build/target/minitube
+
+## The visual way
+
+There is also a visual way to build and run Qt apps.
+Download the Qt SDK from: http://qt.nokia.com/downloads
+Open `minitube.pro` with Qt Creator and use the Run (big green "play" icon) command.
+
+## Legal Stuff
+
+Copyright (C) 2009  Flavio Tordini
+
+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/>.
diff --git a/Info.plist b/Info.plist
new file mode 100644 (file)
index 0000000..27b03c3
--- /dev/null
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+       <key>CFBundleName</key>
+       <string>Minitube</string>
+       <key>CFBundleIconFile</key>
+       <string>minitube.icns</string>
+       <key>CFBundlePackageType</key>
+       <string>APPL</string>
+       <key>CFBundleGetInfoString</key>
+       <string>Copyright 2009 Flavio Tordini</string>
+       <key>CFBundleExecutable</key>
+       <string>minitube</string>
+       <key>CFBundleIdentifier</key>
+       <string>org.tordini.flavio.minitube</string>
+</dict>
+</plist>
diff --git a/LICENSE.LGPL b/LICENSE.LGPL
new file mode 100644 (file)
index 0000000..5ab7695
--- /dev/null
@@ -0,0 +1,504 @@
+                 GNU LESSER GENERAL PUBLIC LICENSE
+                      Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+\f
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+\f
+                 GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+\f
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+\f
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+\f
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+\f
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+\f
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser 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 Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+\f
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                           NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "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
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY 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
+LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..69d5ab5
--- /dev/null
+++ b/TODO
@@ -0,0 +1,51 @@
+# Minitube TODO list
+
+## Known Bugs
+- Wrong item positions when moving more than one item down or up
+
+## Killer features
+
+- Video download
+    Playlist items should have a download icon that appears on mouse hover.
+    Once the icon is clicked a mini-progressbar appears and the download icon remains visible
+    even on "mouseout", maybe with a different color.
+    Videos should be downloaded in HD format, if available.
+    Videos should be downloaded directly on the Desktop without asking for a location.
+
+- YouTube related videos
+    List of related videos identical to the playlist.
+    When a related video is clicked Minitube will keep playing the next related videos.
+    Don't know where the list should appear, maybe on the right, but when activated it should replace the playlist.
+    Maybe the playlist should disappear, leaving only the panel on the right.
+
+- Accept YouTube URLs in the search box and as CLI args
+    ./minitube [url]
+    When Minitube has a single video to play, it will continue playing related videos.
+    This feature depends on the related videos above.
+    We could also accept any URL and scrape web pages searching for YouTube videos.
+
+- Windows build
+    Stefan Brueck has compiled on Windows but there are problems with Phonon's directX backend
+    Marco di Antonio tried with the Mplayer backend, but it is very unstable
+
+- Subtitles, see http://google2srt.sourceforge.net/
+
+## Minor Features
+- Safe search levels (in the status bar beside HD)
+- Show buffering progress (when Phonon backends will work)
+- Show more thumbs on hover with animated crossfade
+
+## Unsure features
+- Restore status when a recent keyword is clicked: skipped videos, sortBar and play time
+- Add menu entries for Most relevant, Most recent, Most viewed (Playlist => ...)
+- Saved playlists
+- Starred videos
+
+## Phonon bugs (Come on Qt and KDE devs, do your job!)
+- Mac: playback sometimes does not start (got a workaround!)
+- Mac: Phonon freezes the GUI when loading a video
+- Phonon::MediaSource does not work with QIODevices
+- Mac: Cannot seek beyond the buffered part of the video
+- Xine: Seek does not work at all. https://bugs.kde.org/show_bug.cgi?id=197927
+- Phonon does not report the buffering percent correctly. https://bugs.kde.org/show_bug.cgi?id=210336
+- Linux: The Phonon volume slider mute button does not change: https://bugs.kde.org/show_bug.cgi?id=214543
diff --git a/data/128x128/minitube.png b/data/128x128/minitube.png
new file mode 100644 (file)
index 0000000..18961b3
Binary files /dev/null and b/data/128x128/minitube.png differ
diff --git a/data/16x16/minitube.png b/data/16x16/minitube.png
new file mode 100644 (file)
index 0000000..c785c2b
Binary files /dev/null and b/data/16x16/minitube.png differ
diff --git a/data/256x256/minitube.png b/data/256x256/minitube.png
new file mode 100644 (file)
index 0000000..5a2a186
Binary files /dev/null and b/data/256x256/minitube.png differ
diff --git a/data/32x32/minitube.png b/data/32x32/minitube.png
new file mode 100644 (file)
index 0000000..7d8d63a
Binary files /dev/null and b/data/32x32/minitube.png differ
diff --git a/data/48x48/minitube.png b/data/48x48/minitube.png
new file mode 100644 (file)
index 0000000..aeb6f11
Binary files /dev/null and b/data/48x48/minitube.png differ
diff --git a/data/64x64/minitube.png b/data/64x64/minitube.png
new file mode 100644 (file)
index 0000000..674a03d
Binary files /dev/null and b/data/64x64/minitube.png differ
diff --git a/data/minitube.svg b/data/minitube.svg
new file mode 100644 (file)
index 0000000..5d2670d
--- /dev/null
@@ -0,0 +1,382 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="32"
+   height="32"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.46"
+   sodipodi:docbase="/home/needcoffee/Templates"
+   sodipodi:docname="tv.svg"
+   version="1.0"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   inkscape:export-filename="/home/flavio/projects/minitube/images/app.png"
+   inkscape:export-xdpi="360"
+   inkscape:export-ydpi="360">
+  <defs
+     id="defs4">
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 16 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="32 : 16 : 1"
+       inkscape:persp3d-origin="16 : 10.666667 : 1"
+       id="perspective65" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient8228">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop8230" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0;"
+         offset="1"
+         id="stop8232" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient7815">
+      <stop
+         style="stop-color:#000000;stop-opacity:0;"
+         offset="0"
+         id="stop7817" />
+      <stop
+         id="stop7823"
+         offset="0.5"
+         style="stop-color:#000000;stop-opacity:1;" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0;"
+         offset="1"
+         id="stop7819" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient7422">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop7424" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0;"
+         offset="1"
+         id="stop7426" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient6646">
+      <stop
+         style="stop-color:#eeeeec;stop-opacity:1"
+         offset="0"
+         id="stop6648" />
+      <stop
+         style="stop-color:#555753;stop-opacity:1"
+         offset="1"
+         id="stop6650" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient6257">
+      <stop
+         style="stop-color:#555753;stop-opacity:1;"
+         offset="0"
+         id="stop6259" />
+      <stop
+         style="stop-color:#2e3436;stop-opacity:1"
+         offset="1"
+         id="stop6261" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient5863">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop5865" />
+      <stop
+         style="stop-color:#2e3436;stop-opacity:1"
+         offset="1"
+         id="stop5867" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient5088">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0.54347825;"
+         offset="0"
+         id="stop5090" />
+      <stop
+         style="stop-color:#729fcf;stop-opacity:1;"
+         offset="1"
+         id="stop5092" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4701"
+       inkscape:collect="always">
+      <stop
+         id="stop4703"
+         offset="0"
+         style="stop-color:#729fcf;stop-opacity:1" />
+      <stop
+         id="stop4705"
+         offset="1"
+         style="stop-color:#3465a4;stop-opacity:1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient4301">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop4303" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop4305" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient3145">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop3147" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop3149" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3145"
+       id="linearGradient3151"
+       x1="7"
+       y1="-1"
+       x2="12"
+       y2="16"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6666666,0,0,1.0000001,-2.7699557e-8,-3)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4301"
+       id="linearGradient4307"
+       x1="-5"
+       y1="-7"
+       x2="39"
+       y2="30"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.5555555,0,0,0.5692092,2.6666665,1.1694899)" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4701"
+       id="radialGradient4699"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.0025642,3.1703514e-8,-1.117005e-8,0.4159999,-8.0615381,4.9319989)"
+       cx="24"
+       cy="39.923077"
+       fx="24"
+       fy="39.923077"
+       r="20" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5088"
+       id="linearGradient5097"
+       gradientUnits="userSpaceOnUse"
+       x1="2"
+       y1="-4"
+       x2="19"
+       y2="30"
+       gradientTransform="matrix(0.5675677,0,0,0.6086956,2.3783783,0.5000008)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5863"
+       id="linearGradient5872"
+       gradientUnits="userSpaceOnUse"
+       x1="8"
+       y1="37"
+       x2="8"
+       y2="41"
+       gradientTransform="matrix(3.1249998,0,0,0.9999976,-10.562498,-13.999915)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient6257"
+       id="linearGradient6263"
+       x1="12.283331"
+       y1="41.250008"
+       x2="12.283331"
+       y2="36.750004"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.5405406,0,0,-0.3333337,1.5270266,38.000017)" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient6646"
+       id="radialGradient6652"
+       cx="12"
+       cy="8.4459467"
+       fx="12"
+       fy="8.4459467"
+       r="22"
+       gradientTransform="matrix(0.3514481,0.895542,-1.0274841,0.4545455,12.925816,-8.0855683)"
+       gradientUnits="userSpaceOnUse" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient7422"
+       id="radialGradient7831"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,1.2500003,0,-10.875016)"
+       cx="2"
+       cy="43.5"
+       fx="2"
+       fy="43.5"
+       r="2" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient7422"
+       id="radialGradient7833"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,1.2500003,44,-10.875014)"
+       cx="2"
+       cy="43.5"
+       fx="2"
+       fy="43.5"
+       r="2" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient7815"
+       id="linearGradient7835"
+       gradientUnits="userSpaceOnUse"
+       x1="6"
+       y1="41"
+       x2="6"
+       y2="46" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient8228"
+       id="linearGradient8234"
+       x1="10"
+       y1="40.999996"
+       x2="10"
+       y2="35.999996"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6756757,0,0,0.7499986,-0.2162159,-3.8749457)" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="13.62465"
+     inkscape:cx="28.332311"
+     inkscape:cy="12.08918"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     inkscape:window-width="1280"
+     inkscape:window-height="772"
+     inkscape:window-x="0"
+     inkscape:window-y="28"
+     inkscape:showpageshadow="false"
+     width="32px"
+     height="32px"
+     borderlayer="true"
+     gridtolerance="10000"
+     showgrid="false">
+    <inkscape:grid
+       type="xygrid"
+       id="grid2173" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Ebene 1"
+     inkscape:groupmode="layer"
+     id="layer1">
+    <g
+       id="g7826"
+       style="opacity:0.46000001"
+       transform="matrix(0.6666666,0,0,1.1999995,0,-24.199975)">
+      <path
+         sodipodi:nodetypes="cscc"
+         id="path6654"
+         d="M 2,45.999998 C 0.896,45.999998 0,44.879998 0,43.499998 C 0,42.119997 0.896,40.999997 2,40.999997 C 2,40.999997 2,45.999998 2,45.999998 z"
+         style="color:#000000;fill:url(#radialGradient7831);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+      <path
+         sodipodi:nodetypes="cccc"
+         id="path7037"
+         d="M 48,43.5 C 48,44.88 47.104,46 46,46 C 46,46 46,40.999999 46,40.999999 C 47.104,40.999999 48,42.119999 48,43.5 z"
+         style="color:#000000;fill:url(#radialGradient7833);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+      <path
+         id="rect7432"
+         d="M 2,41 L 46,41 L 46,46 L 2,46 L 2,41 z"
+         style="fill:url(#linearGradient7835);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+    </g>
+    <path
+       style="fill:#2e3436;fill-opacity:1;stroke:#000000;stroke-width:1.00000012;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1"
+       d="M 4.2388889,2.5000001 L 1.5,3.8684212 L 1.5,28.500002 L 30.499999,28.500002 L 30.499999,3.8684212 L 27.76111,2.5000001 L 4.2388889,2.5000001 z"
+       id="rect2174"
+       sodipodi:nodetypes="ccccccc" />
+    <path
+       style="fill:url(#linearGradient3151);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1"
+       d="M 4.6666667,3 L 27.333332,3 L 30,5 L 2,5 L 4.6666667,3 z"
+       id="rect2761"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       sodipodi:nodetypes="ccccccc"
+       id="path2757"
+       d="M 4.7696847,3.5000001 L 2.5000002,4.5250628 L 2.5000002,27.500001 L 29.500001,27.500001 L 29.500001,4.5250628 L 27.230316,3.5000001 L 4.7696847,3.5000001 z"
+       style="fill:none;fill-opacity:1;stroke:url(#radialGradient6652);stroke-width:1.00000024;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+    <path
+       style="fill:url(#radialGradient4699);fill-opacity:1;stroke:#204a87;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1"
+       d="M 4.5,6.5000002 L 27.5,6.5000002 L 27.5,22.500001 L 4.5,22.500001 L 4.5,6.5000002 z"
+       id="rect2759" />
+    <path
+       style="opacity:0.4;fill:url(#linearGradient4307);fill-opacity:1;stroke:none;stroke-width:0.99999994;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1"
+       d="M 6,8 L 26,8 L 26,11.415255 C 26,11.415255 24.888889,9.7076275 18.777778,13.692092 C 12.666667,17.676555 6,14.898987 6,14.898987 L 6,8 z"
+       id="rect3536"
+       sodipodi:nodetypes="ccczcc" />
+    <path
+       style="fill:none;fill-opacity:1;stroke:url(#linearGradient5097);stroke-width:1.00000012;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1"
+       d="M 5.5000001,7.4999998 L 26.500001,7.4999998 L 26.500001,21.499999 L 5.5000001,21.499999 L 5.5000001,7.4999998 z"
+       id="rect3534" />
+    <path
+       style="fill:url(#linearGradient5872);fill-opacity:1;stroke:url(#linearGradient8234);stroke-width:1.00000036;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1"
+       d="M 3.5000001,23.500001 L 28.5,23.500001 L 28.5,26.499995 L 3.5000001,26.499995 L 3.5000001,23.500001 z"
+       id="rect5099" />
+    <path
+       id="path5874"
+       d="M 4.4999997,25.500001 L 24.500001,25.500001 L 24.500001,24.5 L 4.4999997,24.5 L 4.4999997,25.500001 z"
+       style="fill:none;fill-opacity:1;stroke:url(#linearGradient6263);stroke-width:0.99999988;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+    <path
+       style="fill:#2e3436;fill-opacity:1;stroke:#555753;stroke-width:0.99999952;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1"
+       d="M 25.5,24.499999 L 27.499999,24.499999 L 27.499999,25.5 L 25.5,25.5 L 25.5,24.499999 z"
+       id="path7839" />
+    <rect
+       style="opacity:1;fill:#4e9a06;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       id="rect7843"
+       width="1"
+       height="1"
+       x="27"
+       y="25" />
+    <rect
+       y="25"
+       x="26"
+       height="1"
+       width="1"
+       id="rect7845"
+       style="opacity:1;fill:#8ae234;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+  </g>
+</svg>
diff --git a/images/app.png b/images/app.png
new file mode 100644 (file)
index 0000000..18961b3
Binary files /dev/null and b/images/app.png differ
diff --git a/images/media-playback-pause.png b/images/media-playback-pause.png
new file mode 100644 (file)
index 0000000..2faccc2
Binary files /dev/null and b/images/media-playback-pause.png differ
diff --git a/images/media-playback-start.png b/images/media-playback-start.png
new file mode 100644 (file)
index 0000000..c933f11
Binary files /dev/null and b/images/media-playback-start.png differ
diff --git a/images/media-playback-stop.png b/images/media-playback-stop.png
new file mode 100644 (file)
index 0000000..e4fb4bd
Binary files /dev/null and b/images/media-playback-stop.png differ
diff --git a/images/media-skip-forward.png b/images/media-skip-forward.png
new file mode 100644 (file)
index 0000000..704ca17
Binary files /dev/null and b/images/media-skip-forward.png differ
diff --git a/images/view-fullscreen.png b/images/view-fullscreen.png
new file mode 100644 (file)
index 0000000..c859ce4
Binary files /dev/null and b/images/view-fullscreen.png differ
diff --git a/locale/ar.ts b/locale/ar.ts
new file mode 100644 (file)
index 0000000..d97b57e
--- /dev/null
@@ -0,0 +1,394 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.0" language="ar_SA">
+<defaultcodec>UTF-8</defaultcodec>
+<context>
+    <name>AboutView</name>
+    <message>
+        <source>There&apos;s life outside the browser!</source>
+        <translation>هناك حياة خارج المتصفح!</translation>
+    </message>
+    <message>
+        <source>Version %1</source>
+        <translation>النسخة %1</translation>
+    </message>
+    <message>
+        <source>%1 is Free Software but its development takes precious time.</source>
+        <translation>%1 هو برنامج مجاني و لكن برمجته تستهلك وقتا ثمينا.</translation>
+    </message>
+    <message>
+        <source>Please &lt;a href=&apos;%1&apos;&gt;donate&lt;/a&gt; to support the continued development of %2.</source>
+        <translation>‎الرجاء &lt;a href=&apos;%1&apos;&gt;التبرع&lt;/a&gt; لمتابعة تطوير  %2.</translation>
+    </message>
+    <message>
+        <source>Report bugs and send in your ideas to %1</source>
+        <translation>ارسل الاخطاء و الاراء الى %1</translation>
+    </message>
+    <message>
+        <source>Icon designed by %1.</source>
+        <translation>صمم الايقونة %1.</translation>
+    </message>
+    <message>
+        <source>Compact mode contributed by %1.</source>
+        <translation>صمم الواجهة الخفيفة %1.</translation>
+    </message>
+    <message>
+        <source>HTTP proxy support contributed by %1.</source>
+        <translation>صمم بروكسي HTTP %1.</translation>
+    </message>
+    <message>
+        <source>Translated by %1</source>
+        <translation>تمت الترجمة بواسطة %1</translation>
+    </message>
+    <message>
+        <source>Released under the &lt;a href=&apos;%1&apos;&gt;GNU General Public License&lt;/a&gt;</source>
+        <translation>نشر هذا البرنامج تحت رخصة  &lt;a href=&apos;%1&apos;&gt;GNU General Public License&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>&amp;Close</source>
+        <translation>&amp;اقفل</translation>
+    </message>
+    <message>
+        <source>About</source>
+        <translation>عن البرنامج</translation>
+    </message>
+    <message>
+        <source>What you always wanted to know about %1 and never dared to ask</source>
+        <translation>ما اردت معرفته عن %1 و لم تستطع ان تسأل</translation>
+    </message>
+</context>
+<context>
+    <name>ClearButton</name>
+    <message>
+        <source>Clear</source>
+        <translation>مسح</translation>
+    </message>
+</context>
+<context>
+    <name>ListModel</name>
+    <message>
+        <source>Searching...</source>
+        <translation>جاري البحث...</translation>
+    </message>
+    <message>
+        <source>Show %1 More</source>
+        <translation>اظهر %1 المزيد</translation>
+    </message>
+    <message>
+        <source>No videos</source>
+        <translation>لا توجد فيديوات</translation>
+    </message>
+    <message>
+        <source>No more videos</source>
+        <translation> لا توجد فيديوات اخرى</translation>
+    </message>
+</context>
+<context>
+    <name>LoadingWidget</name>
+    <message>
+        <source>Error</source>
+        <translation>خطأ</translation>
+    </message>
+</context>
+<context>
+    <name>MainWindow</name>
+    <message>
+        <source>&amp;Stop</source>
+        <translation>&amp;قف</translation>
+    </message>
+    <message>
+        <source>Stop playback and go back to the search view</source>
+        <translation>وقف التشغيل و العودة لوضع البحث</translation>
+    </message>
+    <message>
+        <source>S&amp;kip</source>
+        <translation>&amp;القفز</translation>
+    </message>
+    <message>
+        <source>Skip to the next video</source>
+        <translation>القفز للفيديو التالي</translation>
+    </message>
+    <message>
+        <source>&amp;Pause</source>
+        <translation>&amp;ايقاف مؤقت</translation>
+    </message>
+    <message>
+        <source>Pause playback</source>
+        <translation>ايقاف التشغيل</translation>
+    </message>
+    <message>
+        <source>&amp;Full Screen</source>
+        <translation>&amp;شاشة كاملة</translation>
+    </message>
+    <message>
+        <source>Go full screen</source>
+        <translation>تشغيل شاشة كاملة</translation>
+    </message>
+    <message>
+        <source>&amp;Compact mode</source>
+        <translation>&amp;الواجهة الخفيفة</translation>
+    </message>
+    <message>
+        <source>Hide the playlist and the toolbar</source>
+        <translation>اخفاء القائمة و شريط المهام</translation>
+    </message>
+    <message>
+        <source>Open the &amp;YouTube page</source>
+        <translation>فتح &amp;صفحة YouTube</translation>
+    </message>
+    <message>
+        <source>Go to the YouTube video page and pause playback</source>
+        <translation>اذهب الى صفحة فيديو YouTube و وقف التشغيل</translation>
+    </message>
+    <message>
+        <source>Copy the YouTube &amp;link</source>
+        <translation>نسخ &amp;رابط YouTube</translation>
+    </message>
+    <message>
+        <source>Copy the current video YouTube link to the clipboard</source>
+        <translation>نسخ رابط YouTube الى clipboard</translation>
+    </message>
+    <message>
+        <source>Copy the video stream &amp;URL</source>
+        <translation>نسخ الفيديو و &amp;الرابط</translation>
+    </message>
+    <message>
+        <source>Copy the current video stream URL to the clipboard</source>
+        <translation>نسخ رابط الفيديو الى clipboard</translation>
+    </message>
+    <message>
+        <source>&amp;Remove</source>
+        <translation>&amp;إزالة</translation>
+    </message>
+    <message>
+        <source>Remove the selected videos from the playlist</source>
+        <translation>إزالة الفيديو من القائمة</translation>
+    </message>
+    <message>
+        <source>Move &amp;Up</source>
+        <translation>الرفع الى &amp;اعلى</translation>
+    </message>
+    <message>
+        <source>Move up the selected videos in the playlist</source>
+        <translation>رفع المقاطع المختارة الى فوق في القائمة</translation>
+    </message>
+    <message>
+        <source>Move &amp;Down</source>
+        <translation>الانزال الى &amp;تحت</translation>
+    </message>
+    <message>
+        <source>Move down the selected videos in the playlist</source>
+        <translation>انزال المقاطع المختارة الى تحت في القائمة</translation>
+    </message>
+    <message>
+        <source>&amp;Clear recent keywords</source>
+        <translation>&amp;مسح كلمات البحث السابقة</translation>
+    </message>
+    <message>
+        <source>Clear the search history. Cannot be undone.</source>
+        <translation>مسح تاريخ البحث. لا يمكن استعادته.</translation>
+    </message>
+    <message>
+        <source>&amp;Quit</source>
+        <translation>&amp;الانتهاء</translation>
+    </message>
+    <message>
+        <source>Ctrl+Q</source>
+        <translation>Ctrl+Q</translation>
+    </message>
+    <message>
+        <source>Bye</source>
+        <translation>مع السلامة</translation>
+    </message>
+    <message>
+        <source>&amp;Website</source>
+        <translation>&amp;الموقع</translation>
+    </message>
+    <message>
+        <source>%1 on the Web</source>
+        <translation>%1 على الشبكة</translation>
+    </message>
+    <message>
+        <source>Make a &amp;donation</source>
+        <translation>&amp;التبرع</translation>
+    </message>
+    <message>
+        <source>Please support the continued development of %1</source>
+        <translation>‎الرجاء التبرع لمتابعة تطوير  %1</translation>
+    </message>
+    <message>
+        <source>&amp;About</source>
+        <translation>&amp;عن البرنامج</translation>
+    </message>
+    <message>
+        <source>Info about %1</source>
+        <translation>معلومات عن %1</translation>
+    </message>
+    <message>
+        <source>Search</source>
+        <translation>البحث</translation>
+    </message>
+    <message>
+        <source>Mute volume</source>
+        <translation>كتم الصوت</translation>
+    </message>
+    <message>
+        <source>Ctrl+M</source>
+        <translation>Ctrl+M</translation>
+    </message>
+    <message>
+        <source>&amp;Application</source>
+        <translation>&amp;البرنامج</translation>
+    </message>
+    <message>
+        <source>&amp;Playlist</source>
+        <translation>&amp;القائمة</translation>
+    </message>
+    <message>
+        <source>&amp;Video</source>
+        <translation>&amp;الفيديو</translation>
+    </message>
+    <message>
+        <source>&amp;Help</source>
+        <translation>&amp;المساعدة</translation>
+    </message>
+    <message>
+        <source>Press %1 to raise the volume, %2 to lower it</source>
+        <translation>اضغط %1 لرفع الصوت و %2 لخفضه</translation>
+    </message>
+    <message>
+        <source>Opening %1</source>
+        <translation>جاري فتح %1</translation>
+    </message>
+    <message>
+        <source>Fatal error: %1</source>
+        <translation>خطأ قاتل: %1</translation>
+    </message>
+    <message>
+        <source>Error: %1</source>
+        <translation>خطأ: %1</translation>
+    </message>
+    <message>
+        <source>&amp;Play</source>
+        <translation>&amp;التشغيل</translation>
+    </message>
+    <message>
+        <source>Resume playback</source>
+        <translation>اكمال التشغيل</translation>
+    </message>
+    <message>
+        <source>Exit &amp;Full Screen</source>
+        <translation>الخروج من &amp;الشاشة الكاملة</translation>
+    </message>
+    <message>
+        <source>Remaining time: %1</source>
+        <translation>الوقت المتبقي: %1</translation>
+    </message>
+    <message>
+        <source>Volume at %1%</source>
+        <translation>%1%الصوت عند </translation>
+    </message>
+    <message>
+        <source>Volume is muted</source>
+        <translation>الصوت مكتوم</translation>
+    </message>
+    <message>
+        <source>Volume is unmuted</source>
+        <translation>الصوت غير مكتوم</translation>
+    </message>
+    <message>
+        <source>Maximum video definition set to %1</source>
+        <translation>الوضوح الاقصى %1</translation>
+    </message>
+    <message>
+        <source>Your privacy is now safe</source>
+        <translation>خصوصيتك امنة</translation>
+    </message>
+</context>
+<context>
+    <name>MediaView</name>
+    <message>
+        <source>Most relevant</source>
+        <translation>اقرب النتائج</translation>
+    </message>
+    <message>
+        <source>Most recent</source>
+        <translation>الاقرب زمنا</translation>
+    </message>
+    <message>
+        <source>Most viewed</source>
+        <translation>الاكثر عرضا</translation>
+    </message>
+    <message>
+        <source>You can now paste the YouTube link into another application</source>
+        <translation>يمكنك لصق رابط YouTube في برنامج اخر</translation>
+    </message>
+    <message>
+        <source>You can now paste the video stream URL into another application</source>
+        <translation>يمكنك لصق رابط الفيديو في برنامج اخر</translation>
+    </message>
+    <message>
+        <source>The link will be valid only for a limited time.</source>
+        <translation>الرابط سيكون صالحا لمدة محدودة.</translation>
+    </message>
+    <message>
+        <source>You&apos;re watching &quot;%1&quot;</source>
+        <translation>انت تشاهد &quot;%1&quot;</translation>
+    </message>
+</context>
+<context>
+    <name>NetworkAccess</name>
+    <message>
+        <source>Network error: %1</source>
+        <translation>خطأ شبكة:%1</translation>
+    </message>
+</context>
+<context>
+    <name>PrettyItemDelegate</name>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 مشاهدات</translation>
+    </message>
+</context>
+<context>
+    <name>SearchLineEdit</name>
+    <message>
+        <source>Search</source>
+        <translation>البحث</translation>
+    </message>
+</context>
+<context>
+    <name>SearchView</name>
+    <message>
+        <source>Welcome to &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</source>
+        <translation>مرحبا بك في &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
+    </message>
+    <message>
+        <source>Enter a keyword to start watching videos.</source>
+        <translation>اختر كلمة بحث للبدء في مشاهدة المقاطع.</translation>
+    </message>
+    <message>
+        <source>Watch</source>
+        <translation>شاهد</translation>
+    </message>
+    <message>
+        <source>Recent keywords</source>
+        <translation>كلمات بحث سابقة</translation>
+    </message>
+    <message>
+        <source>A new version of %1 is available. Please &lt;a href=&apos;%2&apos;&gt;update to version %3&lt;/a&gt;</source>
+        <translation>تم ايجاد نسخة جديدة من %1 الرجاء &lt;a href=&apos;%2&apos;&gt;الترقية الى نسخة  %3&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>Make yourself comfortable</source>
+        <translation>اجعل نفسك مرتاحا</translation>
+    </message>
+</context>
+<context>
+    <name>Video</name>
+    <message>
+        <source>Network error: %1 for %2</source>
+        <translation>خطأ شبكة:%1 الى %2</translation>
+    </message>
+</context>
+</TS>
diff --git a/locale/bg_BG.ts b/locale/bg_BG.ts
new file mode 100644 (file)
index 0000000..f507621
--- /dev/null
@@ -0,0 +1,392 @@
+<!DOCTYPE TS><TS>
+<defaultcodec>UTF-8</defaultcodec>
+<context>
+    <name>AboutView</name>
+    <message>
+        <source>There&apos;s life outside the browser!</source>
+        <translation>Има живот и извън браузера!</translation>
+    </message>
+    <message>
+        <source>Version %1</source>
+        <translation>Версия %1</translation>
+    </message>
+    <message>
+        <source>%1 is Free Software but its development takes precious time.</source>
+        <translation>%1 е безплатен софтуер, но разработването му отнема безценно време.</translation>
+    </message>
+    <message>
+        <source>Please &lt;a href=&apos;%1&apos;&gt;donate&lt;/a&gt; to support the continued development of %2.</source>
+        <translation>Моля &lt;a href=&apos;%1&apos;&gt;дарете&lt;/a&gt; за да подкрепите бъдещите разработки, на %2.</translation>
+    </message>
+    <message>
+        <source>Report bugs and send in your ideas to %1</source>
+        <translation>Докладвайте за бъгове и изпращайте вашите идей на %1</translation>
+    </message>
+    <message>
+        <source>Icon designed by %1.</source>
+        <translation>Иконите са изработени от %1.</translation>
+    </message>
+    <message>
+        <source>Compact mode contributed by %1.</source>
+        <translation>Компактният режим е допринесен от %1.</translation>
+    </message>
+    <message>
+        <source>HTTP proxy support contributed by %1.</source>
+        <translation>HTTP proxy поддръжката е допринесена от %1.</translation>
+    </message>
+    <message>
+        <source>Translated by %1</source>
+        <translation>Преведено е от %1</translation>
+    </message>
+    <message>
+        <source>Released under the &lt;a href=&apos;%1&apos;&gt;GNU General Public License&lt;/a&gt;</source>
+        <translation>Издадено е под &lt;a href=&apos;%1&apos;&gt;GNU General Public License&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>&amp;Close</source>
+        <translation>&amp;Затваряне</translation>
+    </message>
+    <message>
+        <source>About</source>
+        <translation>Относно</translation>
+    </message>
+    <message>
+        <source>What you always wanted to know about %1 and never dared to ask</source>
+        <translation>Какво винаги сте искали да знаете за %1 , но никога не сте посмявали да попитате</translation>
+    </message>
+</context>
+<context>
+    <name>ClearButton</name>
+    <message>
+        <source>Clear</source>
+        <translation>Изчисти</translation>
+    </message>
+</context>
+<context>
+    <name>ListModel</name>
+    <message>
+        <source>Searching...</source>
+        <translation>Търся...</translation>
+    </message>
+    <message>
+        <source>Show %1 More</source>
+        <translation>Покажи %1 повече</translation>
+    </message>
+    <message>
+        <source>No videos</source>
+        <translation>Няма видеа</translation>
+    </message>
+    <message>
+        <source>No more videos</source>
+        <translation>Няма повече видеа</translation>
+    </message>
+</context>
+<context>
+    <name>LoadingWidget</name>
+    <message>
+        <source>Error</source>
+        <translation>Грешка</translation>
+    </message>
+</context>
+<context>
+    <name>MainWindow</name>
+    <message>
+        <source>&amp;Stop</source>
+        <translation>&amp;Спри</translation>
+    </message>
+    <message>
+        <source>Stop playback and go back to the search view</source>
+        <translation>Спри изпълнението и се върни в търсачката</translation>
+    </message>
+    <message>
+        <source>S&amp;kip</source>
+        <translation>П&amp;ропусни</translation>
+    </message>
+    <message>
+        <source>Skip to the next video</source>
+        <translation>Следващо видео</translation>
+    </message>
+    <message>
+        <source>&amp;Pause</source>
+        <translation>&amp;Пауза</translation>
+    </message>
+    <message>
+        <source>Pause playback</source>
+        <translation>Пауза на възпроизвеждането</translation>
+    </message>
+    <message>
+        <source>&amp;Full Screen</source>
+        <translation>&amp;Цял екран</translation>
+    </message>
+    <message>
+        <source>Go full screen</source>
+        <translation>Отвори в цял екран</translation>
+    </message>
+    <message>
+        <source>&amp;Compact mode</source>
+        <translation>&amp;Компактен режим</translation>
+    </message>
+    <message>
+        <source>Hide the playlist and the toolbar</source>
+        <translation>Скрии прейлистата и туулбара</translation>
+    </message>
+    <message>
+        <source>Open the &amp;YouTube page</source>
+        <translation>Отвори в &amp;YouTube страницата</translation>
+    </message>
+    <message>
+        <source>Go to the YouTube video page and pause playback</source>
+        <translation>Отиди в YouTube видео страницата и сложи на пауза</translation>
+    </message>
+    <message>
+        <source>Copy the YouTube &amp;link</source>
+        <translation>Копирай YouTube &amp;връзката</translation>
+    </message>
+    <message>
+        <source>Copy the current video YouTube link to the clipboard</source>
+        <translation>Постави връзка към видеото в YouTube в клипборда</translation>
+    </message>
+    <message>
+        <source>Copy the video stream &amp;URL</source>
+        <translation>Копирай &amp;URL на видео излъчването</translation>
+    </message>
+    <message>
+        <source>Copy the current video stream URL to the clipboard</source>
+        <translation>Копирай URL на гледаното видео в клипборда</translation>
+    </message>
+    <message>
+        <source>&amp;Remove</source>
+        <translation>&amp;Премахни</translation>
+    </message>
+    <message>
+        <source>Remove the selected videos from the playlist</source>
+        <translation>Премахни избраните виде от плейлистата</translation>
+    </message>
+    <message>
+        <source>Move &amp;Up</source>
+        <translation>Премести &amp;нагоре</translation>
+    </message>
+    <message>
+        <source>Move up the selected videos in the playlist</source>
+        <translation>Премести избраните видеа в плейлиста</translation>
+    </message>
+    <message>
+        <source>Move &amp;Down</source>
+        <translation>Премести &amp;надолу</translation>
+    </message>
+    <message>
+        <source>Move down the selected videos in the playlist</source>
+        <translation>Премести надолу в плейлиста избраните видеа</translation>
+    </message>
+    <message>
+        <source>&amp;Clear recent keywords</source>
+        <translation>&amp;Изчисти скорошните ключови думи</translation>
+    </message>
+    <message>
+        <source>Clear the search history. Cannot be undone.</source>
+        <translation>Изчисти историята на търсене. Не е обратимо.</translation>
+    </message>
+    <message>
+        <source>&amp;Quit</source>
+        <translation>&amp;Изход</translation>
+    </message>
+    <message>
+        <source>Ctrl+Q</source>
+        <translation>Ctrl+Q</translation>
+    </message>
+    <message>
+        <source>Bye</source>
+        <translation>Чао</translation>
+    </message>
+    <message>
+        <source>&amp;Website</source>
+        <translation>&amp;Уебсайт</translation>
+    </message>
+    <message>
+        <source>%1 on the Web</source>
+        <translation>%1 в мрежата</translation>
+    </message>
+    <message>
+        <source>Make a &amp;donation</source>
+        <translation>Направи &amp;дарение</translation>
+    </message>
+    <message>
+        <source>Please support the continued development of %1</source>
+        <translation>Моля подкрепете, бъдещите разработки на %1</translation>
+    </message>
+    <message>
+        <source>&amp;About</source>
+        <translation>&amp;Относно</translation>
+    </message>
+    <message>
+        <source>Info about %1</source>
+        <translation>Информация за %1</translation>
+    </message>
+    <message>
+        <source>Search</source>
+        <translation>Търси</translation>
+    </message>
+    <message>
+        <source>Mute volume</source>
+        <translation>Заглуши звука</translation>
+    </message>
+    <message>
+        <source>Ctrl+M</source>
+        <translation>Ctrl+M</translation>
+    </message>
+    <message>
+        <source>&amp;Application</source>
+        <translation>&amp;Апликация</translation>
+    </message>
+    <message>
+        <source>&amp;Playlist</source>
+        <translation>&amp;Плейлиста</translation>
+    </message>
+    <message>
+        <source>&amp;Video</source>
+        <translation>&amp;Видео</translation>
+    </message>
+    <message>
+        <source>&amp;Help</source>
+        <translation>&amp;Помощ</translation>
+    </message>
+    <message>
+        <source>Press %1 to raise the volume, %2 to lower it</source>
+        <translation>Натисни %1 за да увеличите звука, %2 да го намалите</translation>
+    </message>
+    <message>
+        <source>Opening %1</source>
+        <translation>Отваряне %1</translation>
+    </message>
+    <message>
+        <source>Fatal error: %1</source>
+        <translation>Фатална грешка: %1</translation>
+    </message>
+    <message>
+        <source>Error: %1</source>
+        <translation>Грешка: %1</translation>
+    </message>
+    <message>
+        <source>&amp;Play</source>
+        <translation>&amp;Възпроизвеждане</translation>
+    </message>
+    <message>
+        <source>Resume playback</source>
+        <translation>Възстанови възпроизвеждането</translation>
+    </message>
+    <message>
+        <source>Exit &amp;Full Screen</source>
+        <translation>Изход от &amp;Цял екран</translation>
+    </message>
+    <message>
+        <source>Remaining time: %1</source>
+        <translation>Оставащо време: %1</translation>
+    </message>
+    <message>
+        <source>Volume at %1%</source>
+        <translation>Звук на %1%</translation>
+    </message>
+    <message>
+        <source>Volume is muted</source>
+        <translation>Звука е заглушен</translation>
+    </message>
+    <message>
+        <source>Volume is unmuted</source>
+        <translation>Звука е отглушен</translation>
+    </message>
+    <message>
+        <source>Maximum video definition set to %1</source>
+        <translation>Максимално качество на видеото зададено на %1</translation>
+    </message>
+    <message>
+        <source>Your privacy is now safe</source>
+        <translation>Вашата интимност вече е обезопасена</translation>
+    </message>
+</context>
+<context>
+    <name>MediaView</name>
+    <message>
+        <source>Most relevant</source>
+        <translation>Най-уместни</translation>
+    </message>
+    <message>
+        <source>Most recent</source>
+        <translation>Най-скорошни</translation>
+    </message>
+    <message>
+        <source>Most viewed</source>
+        <translation>Най-гледани</translation>
+    </message>
+    <message>
+        <source>You can now paste the YouTube link into another application</source>
+        <translation>Вече можете да вмъкнете YouTube линка в друго приложение</translation>
+    </message>
+    <message>
+        <source>You can now paste the video stream URL into another application</source>
+        <translation>Вече можете да вмъкнете URL адреса на излъчването в друго приложение</translation>
+    </message>
+    <message>
+        <source>The link will be valid only for a limited time.</source>
+        <translation>Линка ще е валиден само за определено време.</translation>
+    </message>
+    <message>
+        <source>You&apos;re watching &quot;%1&quot;</source>
+        <translation>Вие гледате &quot;%1&quot;</translation>
+    </message>
+</context>
+<context>
+    <name>NetworkAccess</name>
+    <message>
+        <source>Network error: %1</source>
+        <translation>Мрежова грешка: %1</translation>
+    </message>
+</context>
+<context>
+    <name>PrettyItemDelegate</name>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 гледания</translation>
+    </message>
+</context>
+<context>
+    <name>SearchLineEdit</name>
+    <message>
+        <source>Search</source>
+        <translation>Търси</translation>
+    </message>
+</context>
+<context>
+    <name>SearchView</name>
+    <message>
+        <source>Welcome to &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</source>
+        <translation>Добре дошли в &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
+    </message>
+    <message>
+        <source>Enter a keyword to start watching videos.</source>
+        <translation>Въведете ключова дума за да почнете да гледате видео клипчета.</translation>
+    </message>
+    <message>
+        <source>Watch</source>
+        <translation>Гледай</translation>
+    </message>
+    <message>
+        <source>Recent keywords</source>
+        <translation>Скорошни ключови думи</translation>
+    </message>
+    <message>
+        <source>A new version of %1 is available. Please &lt;a href=&apos;%2&apos;&gt;update to version %3&lt;/a&gt;</source>
+        <translation>Нова версия на %1 е достъпна. Моля &lt;a href=&apos;%2&apos;&gt;ъпдейтнете до версия %3&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>Make yourself comfortable</source>
+        <translation>Отпуснете се</translation>
+    </message>
+</context>
+<context>
+    <name>Video</name>
+    <message>
+        <source>Network error: %1 for %2</source>
+        <translation>Мрежова грешка: %1 за %2</translation>
+    </message>
+</context>
+</TS>
diff --git a/locale/calculate_completion.sh b/locale/calculate_completion.sh
new file mode 100755 (executable)
index 0000000..1f1a4e0
--- /dev/null
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# This script was written to get some data on how far the various translations are
+# compared to each other
+#
+# This script is donated to the public domain
+#
+# Klaas van Gend, 2008
+
+printf "\n   translation file  %%ready   (unfinished/(total-obsolete))\n"
+printf '=============================================================\n'
+for I in `ls -1 *.ts`;
+do
+       UNFINISHED=`grep 'type="unfinished"' $I | wc -l`;
+       OBSOLETE=`grep 'obsolete' $I | wc -l`;
+       MSGLINES=`grep '</message>' $I | wc -l`;
+       let "REALLINES=$MSGLINES-$OBSOLETE";
+       let "PERCENT=(100*$UNFINISHED)/$REALLINES";
+       let "FINISHED=100-$PERCENT";
+       printf "% 18s : % 4d%%    %d/(%d-%d)\n" $I $FINISHED $UNFINISHED $MSGLINES $OBSOLETE ;
+done
+printf "\n"
+
diff --git a/locale/cs_CZ.ts b/locale/cs_CZ.ts
new file mode 100644 (file)
index 0000000..5e20b53
--- /dev/null
@@ -0,0 +1,562 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.0" language="cs_CZ" sourcelanguage="en_US">
+<defaultcodec>UTF-8</defaultcodec>
+<context>
+    <name>AboutView</name>
+    <message>
+        <location filename="../src/AboutView.cpp" line="20"/>
+        <source>There&apos;s life outside the browser!</source>
+        <translation>Život existuje i mimo prohlížeč!</translation>
+    </message>
+    <message>
+        <location filename="../src/AboutView.cpp" line="21"/>
+        <source>Version %1</source>
+        <translation>Verze %1</translation>
+    </message>
+    <message>
+        <source>This is a &quot;Technology Preview&quot; release, do not expect it to be perfect.</source>
+        <translation type="obsolete">Toto je vývojová verze, neočekávejte plnou funkčnost.</translation>
+    </message>
+    <message>
+        <location filename="../src/AboutView.cpp" line="28"/>
+        <source>Report bugs and send in your ideas to %1</source>
+        <translation>Hlaste chyby a posílejte své nápady na %1</translation>
+    </message>
+    <message>
+        <location filename="../src/AboutView.cpp" line="24"/>
+        <source>%1 is Free Software but its development takes precious time.</source>
+        <translation>%1 je svobodný software, ale jeho vývoj stojí drahocenný čas.</translation>
+    </message>
+    <message>
+        <source>Please &lt;a href=&apos;%1&apos;&gt;donate via PayPal&lt;/a&gt; to support the continued development of %2.</source>
+        <translation type="obsolete">Prosím &lt;a href=&apos;%1&apos;&gt;přispějte přes PayPal&lt;/a&gt; a podpořte další vývoj aplikace %2.</translation>
+    </message>
+    <message>
+        <location filename="../src/AboutView.cpp" line="25"/>
+        <source>Please &lt;a href=&apos;%1&apos;&gt;donate&lt;/a&gt; to support the continued development of %2.</source>
+        <translation>Prosím &lt;a href=&apos;%1&apos;&gt;přispějte&lt;/a&gt; na další vývoj %2.</translation>
+    </message>
+    <message>
+        <location filename="../src/AboutView.cpp" line="32"/>
+        <source>Icon designed by %1.</source>
+        <translation>Autor ikony: %1.</translation>
+    </message>
+    <message>
+        <location filename="../src/AboutView.cpp" line="33"/>
+        <source>Compact mode contributed by %1.</source>
+        <translation>Autor kompaktního módu: %1.</translation>
+    </message>
+    <message>
+        <location filename="../src/AboutView.cpp" line="34"/>
+        <source>HTTP proxy support contributed by %1.</source>
+        <translation>Autor podpory HTTP proxy: %1.</translation>
+    </message>
+    <message>
+        <source>Windows version built by %1</source>
+        <translation type="obsolete">Verzi pro Windows sestavil: %1</translation>
+    </message>
+    <message>
+        <location filename="../src/AboutView.cpp" line="37"/>
+        <source>Translated by %1</source>
+        <translation>Přeložili: %1</translation>
+    </message>
+    <message>
+        <location filename="../src/AboutView.cpp" line="58"/>
+        <source>Released under the &lt;a href=&apos;%1&apos;&gt;GNU General Public License&lt;/a&gt;</source>
+        <translation>Vydáno pod &lt;a href=&apos;%1&apos;&gt;licencí GNU General Public License&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <location filename="../src/AboutView.cpp" line="69"/>
+        <source>&amp;Close</source>
+        <translation>&amp;Zavřít</translation>
+    </message>
+    <message>
+        <location filename="../src/AboutView.h" line="18"/>
+        <source>About</source>
+        <translation>O aplikaci</translation>
+    </message>
+    <message>
+        <location filename="../src/AboutView.h" line="20"/>
+        <source>What you always wanted to know about %1 and never dared to ask</source>
+        <translation>Co jste vždy chtěli vědět o aplikaci %1 ale nikdy jste se neodvážili zeptat</translation>
+    </message>
+</context>
+<context>
+    <name>ClearButton</name>
+    <message>
+        <location filename="../src/searchlineedit.cpp" line="56"/>
+        <source>Clear</source>
+        <translation>Odstranit vše</translation>
+    </message>
+</context>
+<context>
+    <name>ListModel</name>
+    <message>
+        <location filename="../src/ListModel.cpp" line="46"/>
+        <source>Searching...</source>
+        <translation>Hledám...</translation>
+    </message>
+    <message>
+        <location filename="../src/ListModel.cpp" line="47"/>
+        <source>Show %1 More</source>
+        <translation>Zobrazit dalších %1</translation>
+    </message>
+    <message>
+        <location filename="../src/ListModel.cpp" line="48"/>
+        <source>No videos</source>
+        <translation>Žádná videa</translation>
+    </message>
+    <message>
+        <location filename="../src/ListModel.cpp" line="49"/>
+        <source>No more videos</source>
+        <translation>Žádná další videa</translation>
+    </message>
+</context>
+<context>
+    <name>LoadingWidget</name>
+    <message>
+        <location filename="../src/loadingwidget.cpp" line="66"/>
+        <source>Error</source>
+        <translation>Chyba</translation>
+    </message>
+</context>
+<context>
+    <name>MainWindow</name>
+    <message>
+        <source>&amp;Back</source>
+        <translation type="obsolete">&amp;Zpět</translation>
+    </message>
+    <message>
+        <source>Go to the previous view</source>
+        <translation type="obsolete">Vrátit se k předchozí obrazovce</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="80"/>
+        <source>&amp;Stop</source>
+        <translation>&amp;Stop</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="81"/>
+        <source>Stop playback and go back to the search view</source>
+        <translation>Zastaví přehrávání a vrátí se zpět na vyhledávání</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="86"/>
+        <source>S&amp;kip</source>
+        <translation>Př&amp;eskočit</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="87"/>
+        <source>Skip to the next video</source>
+        <translation>Přeskočí na další video</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="93"/>
+        <location filename="../src/MainWindow.cpp" line="526"/>
+        <source>&amp;Pause</source>
+        <translation>&amp;Pauza</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="94"/>
+        <location filename="../src/MainWindow.cpp" line="527"/>
+        <source>Pause playback</source>
+        <translation>Pozastaví přehrávání</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="100"/>
+        <location filename="../src/MainWindow.cpp" line="605"/>
+        <source>&amp;Full Screen</source>
+        <translation>&amp;Celá obrazovka</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="101"/>
+        <source>Go full screen</source>
+        <translation>Přepne na celou obrazovku</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="107"/>
+        <source>&amp;Compact mode</source>
+        <translation>&amp;Kompaktní mód</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="108"/>
+        <source>Hide the playlist and the toolbar</source>
+        <translation>Skryje playlist a toolbar</translation>
+    </message>
+    <message>
+        <source>&amp;YouTube</source>
+        <translation type="obsolete">&amp;YouTube</translation>
+    </message>
+    <message>
+        <source>Open the YouTube video page</source>
+        <translation type="obsolete">Otevřít video na YouTube</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="116"/>
+        <source>Open the &amp;YouTube page</source>
+        <translation>Otevřít stránku &amp;YouTube</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="117"/>
+        <source>Go to the YouTube video page and pause playback</source>
+        <translation>Otevře video na webu YouTube a pozastaví přehrávání</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="123"/>
+        <source>Copy the YouTube &amp;link</source>
+        <translation>Zkopírovat &amp;odkaz na YouTube</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="124"/>
+        <source>Copy the current video YouTube link to the clipboard</source>
+        <translation>Zkopíruje adresu videa na YouTube do schránky</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="130"/>
+        <source>Copy the video stream &amp;URL</source>
+        <translation>Zkopírovat adresu &amp;video streamu</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="131"/>
+        <source>Copy the current video stream URL to the clipboard</source>
+        <translation>Zkopíruje do schránky adresu video streamu</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="137"/>
+        <source>&amp;Remove</source>
+        <translation>&amp;Odstranit</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="138"/>
+        <source>Remove the selected videos from the playlist</source>
+        <translation>Odstraní vybraná videa z playlistu</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="144"/>
+        <source>Move &amp;Up</source>
+        <translation>&amp;Nahoru</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="145"/>
+        <source>Move up the selected videos in the playlist</source>
+        <translation>Posune vybraná videa výš v playlistu</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="151"/>
+        <source>Move &amp;Down</source>
+        <translation>&amp;Dolů</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="152"/>
+        <source>Move down the selected videos in the playlist</source>
+        <translation>Posune vybraná videa níž v playlistu</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="158"/>
+        <source>&amp;Clear recent keywords</source>
+        <translation>&amp;Vymazat hledané výrazy</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="163"/>
+        <source>Clear the search history. Cannot be undone.</source>
+        <translation>Vyprázdní historii vyhledávání. Akci nelze vrátit.</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="168"/>
+        <source>&amp;Quit</source>
+        <translation>&amp;Zavřít</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="170"/>
+        <source>Ctrl+Q</source>
+        <translation>Ctrl+Q</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="171"/>
+        <source>Bye</source>
+        <translation>Sbohem</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="175"/>
+        <source>&amp;Website</source>
+        <translation>&amp;Homepage</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="177"/>
+        <source>%1 on the Web</source>
+        <translation>%1 na Webu</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="181"/>
+        <source>Make a &amp;donation</source>
+        <translation>&amp;Podpořit</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="760"/>
+        <source>Maximum video definition set to %1</source>
+        <translation>Maximální rozlišení videa je %1</translation>
+    </message>
+    <message>
+        <source>&amp;Donate via PayPal</source>
+        <translation type="obsolete">&amp;Podpořte přes PayPal</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="182"/>
+        <source>Please support the continued development of %1</source>
+        <translation>Prosím přispějte na další vývoj aplikace %1</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="186"/>
+        <source>&amp;About</source>
+        <translation>&amp;O aplikaci</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="188"/>
+        <source>Info about %1</source>
+        <translation>Info o aplikaci %1</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="196"/>
+        <source>Search</source>
+        <translation>Hledat</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="214"/>
+        <source>Mute volume</source>
+        <translation>Ztlumit</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="215"/>
+        <source>Ctrl+M</source>
+        <translation>Ctrl+M</translation>
+    </message>
+    <message>
+        <source>High Definition video is enabled</source>
+        <translation type="obsolete">HD video je povolené</translation>
+    </message>
+    <message>
+        <source>High Definition video is not enabled</source>
+        <translation type="obsolete">HD video není povolené</translation>
+    </message>
+    <message>
+        <source>The current video is in High Definition</source>
+        <translation type="obsolete">Aktuální video je v HD</translation>
+    </message>
+    <message>
+        <source>The current video is not in High Definition</source>
+        <translation type="obsolete">Aktuální video není v HD</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="800"/>
+        <source>Your privacy is now safe</source>
+        <translation>Vaše soukromí je nyní v bezpečí</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="264"/>
+        <source>&amp;Application</source>
+        <translation>&amp;Aplikace</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="272"/>
+        <source>&amp;Playlist</source>
+        <translation>&amp;Playlist</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="279"/>
+        <source>&amp;Video</source>
+        <translation>&amp;Video</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="292"/>
+        <source>&amp;Help</source>
+        <translation>&amp;Nápověda</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="337"/>
+        <source>Press %1 to raise the volume, %2 to lower it</source>
+        <translation>Stiskněte %1 pro zvýšení hlasitosti; %2 pro snížení</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="467"/>
+        <location filename="../src/MainWindow.cpp" line="473"/>
+        <source>Opening %1</source>
+        <translation>Otevírám %1</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="517"/>
+        <source>Fatal error: %1</source>
+        <translation>Chyba: %1</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="519"/>
+        <source>Error: %1</source>
+        <translation>Chyba: %1</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="540"/>
+        <source>&amp;Play</source>
+        <translation>&amp;Play</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="541"/>
+        <source>Resume playback</source>
+        <translation>Pokračovat v přehrávání</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="618"/>
+        <source>Exit &amp;Full Screen</source>
+        <translation>Vypnout &amp;fullscreen</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="701"/>
+        <source>Remaining time: %1</source>
+        <translation>Zbývající čas: %1</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="747"/>
+        <source>Volume at %1%</source>
+        <translation>Hlasitost na %1%</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="752"/>
+        <source>Volume is muted</source>
+        <translation>Zvuk je ztlumen</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="754"/>
+        <source>Volume is unmuted</source>
+        <translation>Zvuk je zapnut</translation>
+    </message>
+</context>
+<context>
+    <name>MediaView</name>
+    <message>
+        <location filename="../src/MediaView.cpp" line="25"/>
+        <source>Most relevant</source>
+        <translation>Nalezená videa</translation>
+    </message>
+    <message>
+        <location filename="../src/MediaView.cpp" line="32"/>
+        <source>Most recent</source>
+        <translation>Nejnovější</translation>
+    </message>
+    <message>
+        <location filename="../src/MediaView.cpp" line="39"/>
+        <source>Most viewed</source>
+        <translation>Nejsledovanější</translation>
+    </message>
+    <message>
+        <location filename="../src/MediaView.cpp" line="339"/>
+        <source>You can now paste the YouTube link into another application</source>
+        <translation>Nyní můžete vložit odkaz na video na YouTube do jiného programu</translation>
+    </message>
+    <message>
+        <location filename="../src/MediaView.cpp" line="347"/>
+        <source>You can now paste the video stream URL into another application</source>
+        <translation>Nyní můžete vložit odkaz na video stream do jiné aplikace</translation>
+    </message>
+    <message>
+        <location filename="../src/MediaView.cpp" line="348"/>
+        <source>The link will be valid only for a limited time.</source>
+        <translation>Tento odkaz platí jen po omezenou dobu.</translation>
+    </message>
+    <message>
+        <location filename="../src/MediaView.h" line="31"/>
+        <source>You&apos;re watching &quot;%1&quot;</source>
+        <translation>Sledujete &quot;%1&quot;</translation>
+    </message>
+</context>
+<context>
+    <name>NetworkAccess</name>
+    <message>
+        <location filename="../src/networkaccess.cpp" line="188"/>
+        <source>Network error: %1</source>
+        <translation>Chyba připojení: %1</translation>
+    </message>
+</context>
+<context>
+    <name>PrettyItemDelegate</name>
+    <message>
+        <location filename="../src/playlist/PrettyItemDelegate.cpp" line="145"/>
+        <source>%1 views</source>
+        <translation>Shlédnuto %1x</translation>
+    </message>
+</context>
+<context>
+    <name>SearchLineEdit</name>
+    <message>
+        <location filename="../src/searchlineedit.cpp" line="177"/>
+        <source>Search</source>
+        <translation>Hledat</translation>
+    </message>
+</context>
+<context>
+    <name>SearchView</name>
+    <message>
+        <location filename="../src/SearchView.cpp" line="46"/>
+        <source>Welcome to &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</source>
+        <translation>Vítejte v &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <location filename="../src/SearchView.cpp" line="55"/>
+        <source>Enter a keyword to start watching videos.</source>
+        <translation>Zadejte klíčové slovo pro vyhledávání videí.</translation>
+    </message>
+    <message>
+        <location filename="../src/SearchView.cpp" line="75"/>
+        <source>Watch</source>
+        <translation>Sledovat</translation>
+    </message>
+    <message>
+        <location filename="../src/SearchView.cpp" line="91"/>
+        <source>Recent keywords</source>
+        <translation>Poslední klíčová slova</translation>
+    </message>
+    <message>
+        <location filename="../src/SearchView.cpp" line="186"/>
+        <source>A new version of %1 is available. Please &lt;a href=&apos;%2&apos;&gt;update to version %3&lt;/a&gt;</source>
+        <translation>Nová verze aplikace %1 je dostupná. Prosím &lt;a href=&apos;%2&apos;&gt;aktualizujte na verzi %3&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <location filename="../src/SearchView.h" line="29"/>
+        <source>Make yourself comfortable</source>
+        <translation>Udělejte si pohodlí</translation>
+    </message>
+</context>
+<context>
+    <name>SettingsView</name>
+    <message>
+        <source>Preferences</source>
+        <translation type="obsolete">Nastavení</translation>
+    </message>
+    <message>
+        <source>&amp;Video options</source>
+        <translation type="obsolete">&amp;Nastavení videa</translation>
+    </message>
+    <message>
+        <source>Use high quality video when available</source>
+        <translation type="obsolete">Zobrazit video ve vysoké kvalitě pokud je dostupná</translation>
+    </message>
+    <message>
+        <source>&amp;Saved recent keywords</source>
+        <translation type="obsolete">&amp;Uložit hledané výrazy</translation>
+    </message>
+    <message>
+        <source>&amp;Clear recent keywords</source>
+        <translation type="obsolete">&amp;Vymazat hledané výrazy</translation>
+    </message>
+    <message>
+        <source>&amp;Close</source>
+        <translation type="obsolete">&amp;Zavřít</translation>
+    </message>
+</context>
+<context>
+    <name>Video</name>
+    <message>
+        <location filename="../src/video.cpp" line="121"/>
+        <source>Network error: %1 for %2</source>
+        <translation>Chyba přípojení: %1 pro %2</translation>
+    </message>
+</context>
+</TS>
diff --git a/locale/de_DE.ts b/locale/de_DE.ts
new file mode 100644 (file)
index 0000000..121483d
--- /dev/null
@@ -0,0 +1,606 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.0" language="de_DE" sourcelanguage="en_US">
+<defaultcodec>UTF-8</defaultcodec>
+<context>
+    <name>AboutView</name>
+    <message>
+        <location filename="../src/AboutView.cpp" line="20"/>
+        <source>There&apos;s life outside the browser!</source>
+        <translation>Es existiert Leben neben dem Browser !</translation>
+    </message>
+    <message>
+        <location filename="../src/AboutView.cpp" line="21"/>
+        <source>Version %1</source>
+        <translation>Version %1</translation>
+    </message>
+    <message>
+        <source>This is a &quot;Technology Preview&quot; release, do not expect it to be perfect.</source>
+        <translation type="obsolete">Dies ist eine &quot;Technology Vorschau&quot;, erwarte nicht, dass sie perfekt ist.</translation>
+    </message>
+    <message>
+        <location filename="../src/AboutView.cpp" line="28"/>
+        <source>Report bugs and send in your ideas to %1</source>
+        <translation>Bitte berichte Fehler und sende Deine Ideen an %1</translation>
+    </message>
+    <message>
+        <location filename="../src/AboutView.cpp" line="24"/>
+        <source>%1 is Free Software but its development takes precious time.</source>
+        <translation>%1 ist freie Software, aber die Entwicklung kostet wertvolle Zeit.</translation>
+    </message>
+    <message>
+        <source>Please &lt;a href=&apos;%1&apos;&gt;donate via PayPal&lt;/a&gt; to support the continued development of %2.</source>
+        <translation type="obsolete">Bitte &lt;a href=&apos;%1&apos;&gt;spende via PayPal&lt;/a&gt; um die dauerhafte Entwicklung von %2 zu unterstützen.</translation>
+    </message>
+    <message>
+        <location filename="../src/AboutView.cpp" line="25"/>
+        <source>Please &lt;a href=&apos;%1&apos;&gt;donate&lt;/a&gt; to support the continued development of %2.</source>
+        <translation>Bitte &lt;a href=&apos;%1&apos;&gt;Spenden&lt;/a&gt; um die ständige Entwicklung von %2 zu unterstützen.</translation>
+    </message>
+    <message>
+        <location filename="../src/AboutView.cpp" line="32"/>
+        <source>Icon designed by %1.</source>
+        <translation>Icon Entwurf durch %1.</translation>
+    </message>
+    <message>
+        <location filename="../src/AboutView.cpp" line="33"/>
+        <source>Compact mode contributed by %1.</source>
+        <translation>Kompakt Modus beigetragen von %1.</translation>
+    </message>
+    <message>
+        <location filename="../src/AboutView.cpp" line="34"/>
+        <source>HTTP proxy support contributed by %1.</source>
+        <translation>HTTP Proxy unterstützung beigetragen von %1.</translation>
+    </message>
+    <message>
+        <source>Windows version built by %1</source>
+        <translation type="obsolete">Windows Version erstellt durch %1</translation>
+    </message>
+    <message>
+        <location filename="../src/AboutView.cpp" line="37"/>
+        <source>Translated by %1</source>
+        <translation>Übersetzung durch %1</translation>
+    </message>
+    <message>
+        <location filename="../src/AboutView.cpp" line="58"/>
+        <source>Released under the &lt;a href=&apos;%1&apos;&gt;GNU General Public License&lt;/a&gt;</source>
+        <translation>Veröffentlicht unter der &lt;a href=&apos;%1&apos;&gt;GNU General Public License&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <location filename="../src/AboutView.cpp" line="69"/>
+        <source>&amp;Close</source>
+        <translation>S&amp;chließen</translation>
+    </message>
+    <message>
+        <location filename="../src/AboutView.h" line="18"/>
+        <source>About</source>
+        <translation>Über</translation>
+    </message>
+    <message>
+        <location filename="../src/AboutView.h" line="20"/>
+        <source>What you always wanted to know about %1 and never dared to ask</source>
+        <translation>Was Du schon immer über %1 wissen wolltest, aber nie zu fragen wagtest</translation>
+    </message>
+</context>
+<context>
+    <name>ClearButton</name>
+    <message>
+        <location filename="../src/searchlineedit.cpp" line="56"/>
+        <source>Clear</source>
+        <translation>Säubern</translation>
+    </message>
+</context>
+<context>
+    <name>ListModel</name>
+    <message>
+        <location filename="../src/ListModel.cpp" line="46"/>
+        <source>Searching...</source>
+        <translation>Suche...</translation>
+    </message>
+    <message>
+        <location filename="../src/ListModel.cpp" line="47"/>
+        <source>Show %1 More</source>
+        <translation>Zeige %1 weitere</translation>
+    </message>
+    <message>
+        <location filename="../src/ListModel.cpp" line="48"/>
+        <source>No videos</source>
+        <translation>Keine Videos</translation>
+    </message>
+    <message>
+        <location filename="../src/ListModel.cpp" line="49"/>
+        <source>No more videos</source>
+        <translation>Keine weiteren Videos</translation>
+    </message>
+</context>
+<context>
+    <name>LoadingWidget</name>
+    <message>
+        <location filename="../src/loadingwidget.cpp" line="66"/>
+        <source>Error</source>
+        <translation>Fehler</translation>
+    </message>
+</context>
+<context>
+    <name>MainWindow</name>
+    <message>
+        <source>&amp;Back</source>
+        <translation type="obsolete">&amp;Zurück</translation>
+    </message>
+    <message>
+        <source>Go to the previous view</source>
+        <translation type="obsolete">Gehe zur vorherigen Ansicht</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="80"/>
+        <source>&amp;Stop</source>
+        <translation>&amp;Halt</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="81"/>
+        <source>Stop playback and go back to the search view</source>
+        <translation>Wiedergabe anhalten und zurück zur Suchansicht</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="86"/>
+        <source>S&amp;kip</source>
+        <translation>Ü&amp;berspringen</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="87"/>
+        <source>Skip to the next video</source>
+        <translation>Überspringe zum nächsten Video</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="93"/>
+        <location filename="../src/MainWindow.cpp" line="526"/>
+        <source>&amp;Pause</source>
+        <translation>&amp;Pause</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="94"/>
+        <location filename="../src/MainWindow.cpp" line="527"/>
+        <source>Pause playback</source>
+        <translation>Wiedergabe pausieren</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="100"/>
+        <location filename="../src/MainWindow.cpp" line="605"/>
+        <source>&amp;Full Screen</source>
+        <translation>&amp;Vollbildmodus</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="101"/>
+        <source>Go full screen</source>
+        <translation>Vollbildmodus aktivieren</translation>
+    </message>
+    <message>
+        <source>&amp;Compact View</source>
+        <translation type="obsolete">&amp;Kompakt Ansicht</translation>
+    </message>
+    <message>
+        <source>Go compact view</source>
+        <translation type="obsolete">Kompakt Ansicht aktivieren</translation>
+    </message>
+    <message>
+        <source>&amp;YouTube</source>
+        <translation type="obsolete">&amp;YouTube</translation>
+    </message>
+    <message>
+        <source>Open the YouTube video page</source>
+        <translation type="obsolete">Öffne die YouTube Video Seite</translation>
+    </message>
+    <message>
+        <source>Ctrl+Y</source>
+        <translation type="obsolete">Ctrl+Y</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="107"/>
+        <source>&amp;Compact mode</source>
+        <translation>&amp;Kompakt-Ansicht</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="108"/>
+        <source>Hide the playlist and the toolbar</source>
+        <translation>Verstecke Abspielliste und Werkzeugleiste</translation>
+    </message>
+    <message>
+        <source>&amp;Download</source>
+        <translation type="obsolete">&amp;Herunterladen</translation>
+    </message>
+    <message>
+        <source>Download this video</source>
+        <translation type="obsolete">Dieses Video herunterladen</translation>
+    </message>
+    <message>
+        <source>Ctrl+S</source>
+        <translation type="obsolete">Strg+S</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="116"/>
+        <source>Open the &amp;YouTube page</source>
+        <translation>Öffne die &amp;YouTube Seite</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="117"/>
+        <source>Go to the YouTube video page and pause playback</source>
+        <translation>Gehe zur YouTube Video Seite und pausiere die Wiedergabe</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="123"/>
+        <source>Copy the YouTube &amp;link</source>
+        <translation>YouTube &amp;Link kopieren</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="124"/>
+        <source>Copy the current video YouTube link to the clipboard</source>
+        <translation>YouTube Link in die Zwischenablage kopieren</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="130"/>
+        <source>Copy the video stream &amp;URL</source>
+        <translation>Video &amp;URL kopieren</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="131"/>
+        <source>Copy the current video stream URL to the clipboard</source>
+        <translation>Video URL in die Zwischenablage kopieren</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="137"/>
+        <source>&amp;Remove</source>
+        <translation>Entfe&amp;rnen</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="138"/>
+        <source>Remove the selected videos from the playlist</source>
+        <translation>Entferne das ausgewählte Video aus der Abspielliste</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="144"/>
+        <source>Move &amp;Up</source>
+        <translation>Bewege &amp;hinauf</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="145"/>
+        <source>Move up the selected videos in the playlist</source>
+        <translation>Bewerge das ausgewählte Video in der Abspielliste hinauf</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="151"/>
+        <source>Move &amp;Down</source>
+        <translation>Bewege hin&amp;ab</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="152"/>
+        <source>Move down the selected videos in the playlist</source>
+        <translation>Bewege das ausgewählte Video in der Abspielliste hinunter</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="158"/>
+        <source>&amp;Clear recent keywords</source>
+        <translation>Kürzlich genutzte S&amp;chlüsselwörter entfernen</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="163"/>
+        <source>Clear the search history. Cannot be undone.</source>
+        <translation>Such Historie leeren. Kann nicht rückgängig gemacht werden.</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="168"/>
+        <source>&amp;Quit</source>
+        <translation>&amp;Verlassen</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="170"/>
+        <source>Ctrl+Q</source>
+        <translation>Ctrl+Q</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="171"/>
+        <source>Bye</source>
+        <translation>Tschüss</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="175"/>
+        <source>&amp;Website</source>
+        <translation>&amp;Webseite</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="177"/>
+        <source>%1 on the Web</source>
+        <translation>%1 im Internet</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="181"/>
+        <source>Make a &amp;donation</source>
+        <translation>Eine Spen&amp;den machen</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="760"/>
+        <source>Maximum video definition set to %1</source>
+        <translation>Maximale video Definition wurde auf %1 gesetzt</translation>
+    </message>
+    <message>
+        <source>&amp;Donate via PayPal</source>
+        <translation type="obsolete">Spen&amp;den via PayPal</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="182"/>
+        <source>Please support the continued development of %1</source>
+        <translation>Bitte unterstüte die fortwährende Entwicklung von %1</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="186"/>
+        <source>&amp;About</source>
+        <translation>&amp;Über</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="188"/>
+        <source>Info about %1</source>
+        <translation>Informationen über %1</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="196"/>
+        <source>Search</source>
+        <translation>Suche</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="214"/>
+        <source>Mute volume</source>
+        <translation>Audio ausschalten</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="215"/>
+        <source>Ctrl+M</source>
+        <translation>Strg+M</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="337"/>
+        <source>Press %1 to raise the volume, %2 to lower it</source>
+        <translation>%1 drücken um die Lautstärke zu erhöhen, %2 um sie zu verringern</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="701"/>
+        <source>Remaining time: %1</source>
+        <translation>Verbleibende Zeit: %1</translation>
+    </message>
+    <message>
+        <source>High Definition video is enabled</source>
+        <translation type="obsolete">High Definition Video ist eingeschaltet</translation>
+    </message>
+    <message>
+        <source>High Definition video is not enabled</source>
+        <translation type="obsolete">High Definition Video ist nicht eingeschaltet</translation>
+    </message>
+    <message>
+        <source>The current video is in High Definition</source>
+        <translation type="obsolete">Das derzeitige Video ist in High Definition</translation>
+    </message>
+    <message>
+        <source>The current video is not in High Definition</source>
+        <translation type="obsolete">Das derzeitige Video ist nicht in High Definition</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="800"/>
+        <source>Your privacy is now safe</source>
+        <translation>Deine Privatsphäre ist nun geschützt</translation>
+    </message>
+    <message>
+        <source>No Video playing</source>
+        <translation type="obsolete">Es wird kein Video abgespielt</translation>
+    </message>
+    <message>
+        <source>You must first play the video you intent to download !</source>
+        <translation type="obsolete">Du musst das Video erst abspielen !</translation>
+    </message>
+    <message>
+        <source>Save video as...</source>
+        <translation type="obsolete">Video speichern unter...</translation>
+    </message>
+    <message>
+        <source>minitube video.mp4</source>
+        <translation type="obsolete">minitube video.mp4</translation>
+    </message>
+    <message>
+        <source>Downloading: </source>
+        <translation type="obsolete">Herunterladen:</translation>
+    </message>
+    <message>
+        <source>Abort Download</source>
+        <translation type="obsolete">Herunterladen abbrechen</translation>
+    </message>
+    <message>
+        <source>File creation failed</source>
+        <translation type="obsolete">Das anlegen der Datei ist fehlgeschlagen</translation>
+    </message>
+    <message>
+        <source>Download failed</source>
+        <translation type="obsolete">Herunterladen fehlgeschlagen</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="747"/>
+        <source>Volume at %1%</source>
+        <translation>Lautstärke %1%</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="752"/>
+        <source>Volume is muted</source>
+        <translation>Audio ist ausgeschaltet</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="754"/>
+        <source>Volume is unmuted</source>
+        <translation>Audio eingeschaltet</translation>
+    </message>
+    <message>
+        <source>&amp;Search</source>
+        <translation type="obsolete">&amp;Suche</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="264"/>
+        <source>&amp;Application</source>
+        <translation>&amp;Anwendung</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="272"/>
+        <source>&amp;Playlist</source>
+        <translation>&amp;Abspielliste</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="279"/>
+        <source>&amp;Video</source>
+        <translation>&amp;Video</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="292"/>
+        <source>&amp;Help</source>
+        <translation>&amp;Hilfe</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="467"/>
+        <location filename="../src/MainWindow.cpp" line="473"/>
+        <source>Opening %1</source>
+        <translation>Öffne %1</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="517"/>
+        <source>Fatal error: %1</source>
+        <translation>Schwerer Fehler: %1</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="519"/>
+        <source>Error: %1</source>
+        <translation>Fehler: %1</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="540"/>
+        <source>&amp;Play</source>
+        <translation>&amp;Abspielen</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="541"/>
+        <source>Resume playback</source>
+        <translation>Wiedergabe fortsetzen</translation>
+    </message>
+    <message>
+        <location filename="../src/MainWindow.cpp" line="618"/>
+        <source>Exit &amp;Full Screen</source>
+        <translation>Vollbildmodus &amp;verlassen</translation>
+    </message>
+</context>
+<context>
+    <name>MediaView</name>
+    <message>
+        <location filename="../src/MediaView.cpp" line="25"/>
+        <source>Most relevant</source>
+        <translation>Die bedeutsamsten</translation>
+    </message>
+    <message>
+        <location filename="../src/MediaView.cpp" line="32"/>
+        <source>Most recent</source>
+        <translation>Die neusten</translation>
+    </message>
+    <message>
+        <location filename="../src/MediaView.cpp" line="39"/>
+        <source>Most viewed</source>
+        <translation>Meist gesehen</translation>
+    </message>
+    <message>
+        <location filename="../src/MediaView.cpp" line="339"/>
+        <source>You can now paste the YouTube link into another application</source>
+        <translation>Du kann den YouTube Link nun in einer anderen Anwendung einfügen</translation>
+    </message>
+    <message>
+        <location filename="../src/MediaView.cpp" line="347"/>
+        <source>You can now paste the video stream URL into another application</source>
+        <translation>Die kannst die Video URL nun in einer anderen Anwendung einfügen</translation>
+    </message>
+    <message>
+        <location filename="../src/MediaView.cpp" line="348"/>
+        <source>The link will be valid only for a limited time.</source>
+        <translation>Der Link wird nur eine beschränkte Zeit gültig sein.</translation>
+    </message>
+    <message>
+        <location filename="../src/MediaView.h" line="31"/>
+        <source>You&apos;re watching &quot;%1&quot;</source>
+        <translation>Du betrachtest &quot;%1&quot;</translation>
+    </message>
+</context>
+<context>
+    <name>NetworkAccess</name>
+    <message>
+        <location filename="../src/networkaccess.cpp" line="188"/>
+        <source>Network error: %1</source>
+        <translation>Netzwerk Fehler: %1</translation>
+    </message>
+</context>
+<context>
+    <name>PrettyItemDelegate</name>
+    <message>
+        <location filename="../src/playlist/PrettyItemDelegate.cpp" line="145"/>
+        <source>%1 views</source>
+        <translation>%1 mal betrachtet</translation>
+    </message>
+</context>
+<context>
+    <name>SearchLineEdit</name>
+    <message>
+        <location filename="../src/searchlineedit.cpp" line="177"/>
+        <source>Search</source>
+        <translation>Suche</translation>
+    </message>
+</context>
+<context>
+    <name>SearchView</name>
+    <message>
+        <location filename="../src/SearchView.cpp" line="46"/>
+        <source>Welcome to &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</source>
+        <translation>Willkommen bei &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
+    </message>
+    <message>
+        <location filename="../src/SearchView.cpp" line="55"/>
+        <source>Enter a keyword to start watching videos.</source>
+        <translation>Schlüsselwort eingeben um die Wiedergabe zu starten.</translation>
+    </message>
+    <message>
+        <location filename="../src/SearchView.cpp" line="75"/>
+        <source>Watch</source>
+        <translation>Anschauen</translation>
+    </message>
+    <message>
+        <location filename="../src/SearchView.cpp" line="91"/>
+        <source>Recent keywords</source>
+        <translation>Aktuelle Schlüsselwörter</translation>
+    </message>
+    <message>
+        <location filename="../src/SearchView.cpp" line="186"/>
+        <source>A new version of %1 is available. Please &lt;a href=&apos;%2&apos;&gt;update to version %3&lt;/a&gt;</source>
+        <translation>Eine neue Version von %1 ist verfügbar. Bitte &lt;a href=&apos;%2&apos;&gt;auf Version %3 aktuallisieren&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <location filename="../src/SearchView.h" line="29"/>
+        <source>Make yourself comfortable</source>
+        <translation>Mach es Dir gemütlich</translation>
+    </message>
+</context>
+<context>
+    <name>SettingsView</name>
+    <message>
+        <source>Preferences</source>
+        <translation type="obsolete">Einstellungen</translation>
+    </message>
+    <message>
+        <source>&amp;Close</source>
+        <translation type="obsolete">S&amp;chließen</translation>
+    </message>
+</context>
+<context>
+    <name>Video</name>
+    <message>
+        <location filename="../src/video.cpp" line="121"/>
+        <source>Network error: %1 for %2</source>
+        <translation>Netzwerk Fehler: %1 für %2</translation>
+    </message>
+</context>
+</TS>
diff --git a/locale/el_GR.ts b/locale/el_GR.ts
new file mode 100644 (file)
index 0000000..6e630b2
--- /dev/null
@@ -0,0 +1,511 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.0" language="el_GR">
+<defaultcodec>UTF-8</defaultcodec>
+<context>
+    <name>AboutView</name>
+    <message>
+        <source>There&apos;s life outside the browser!</source>
+        <translatorcomment>Πλοηγός = browser, though many people call it just &quot;browser&quot;</translatorcomment>
+        <translation>Υπάρχει ζωή έξω απο τον πλοηγό!</translation>
+    </message>
+    <message>
+        <source>Version %1</source>
+        <translation>Έκδοση %1</translation>
+    </message>
+    <message>
+        <source>This is a &quot;Technology Preview&quot; release, do not expect it to be perfect.</source>
+        <translation type="obsolete">Αυτή είναι μια έκδοση «Επίδειξης Τεχνολογίας », μην περιμένετε να είναι τέλεια.</translation>
+    </message>
+    <message>
+        <source>Report bugs and send in your ideas to %1</source>
+        <translation>Αναφέρετε προβλήματα και στείλτε τις ιδέες σας στην διεύθυνση %1</translation>
+    </message>
+    <message>
+        <source>%1 is Free Software but its development takes precious time.</source>
+        <translation>Το %1 είναι Ελεύθερο Λογισμικό αλλά η ανάπτυξη του παίρνει πολύτιμο χρόνο.</translation>
+    </message>
+    <message>
+        <source>Please &lt;a href=&apos;%1&apos;&gt;donate via PayPal&lt;/a&gt; to support the continued development of %2.</source>
+        <translation type="obsolete">Παρακαλούμε &lt;a href=&apos;%1&apos;&gt;δωρίστε μέσω PayPal&lt;/a&gt; για να υποστηρίξετε την συνεχόμενη ανάπτυξη του %2.</translation>
+    </message>
+    <message>
+        <source>Icon designed by %1.</source>
+        <translation>Σχεδιασμός εικονιδίου από %1.</translation>
+    </message>
+    <message>
+        <source>Translated by %1</source>
+        <translation>Μετάφραση από %1</translation>
+    </message>
+    <message>
+        <source>Released under the &lt;a href=&apos;%1&apos;&gt;GNU General Public License&lt;/a&gt;</source>
+        <translation>Έκδοση κάτω απο τους όρους της &lt;a href=&apos;%1&apos;&gt;Γενικής Άδειας Χρήσης GNU&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>&amp;Close</source>
+        <translation>&amp;Κλείσιμο</translation>
+    </message>
+    <message>
+        <source>About</source>
+        <translation>Σχετικά</translation>
+    </message>
+    <message>
+        <source>What you always wanted to know about %1 and never dared to ask</source>
+        <translation>Ότι θέλατε να μάθετε σχετικά με το %1 και δεν τολμούσατε να ρωτήσετε</translation>
+    </message>
+    <message>
+        <source>Compact mode contributed by %1.</source>
+        <translation>Συνεισφορά της συμπαγούς εμφάνισης (Compact Mode) από %1.</translation>
+    </message>
+    <message>
+        <source>HTTP proxy support contributed by %1.</source>
+        <translation>Συνεισφορά υποστήριξης μεσολαβητή HTTP (proxy) από %1.</translation>
+    </message>
+    <message>
+        <source>Windows version built by %1</source>
+        <translation type="obsolete">Версія для Windows %1</translation>
+    </message>
+    <message>
+        <source>Please &lt;a href=&apos;%1&apos;&gt;donate&lt;/a&gt; to support the continued development of %2.</source>
+        <translation>Παρακαλούμε &lt;a href=&apos;%1%&apos;&gt;δωρίστε&lt;/a&gt; για να υποστηρίξετε την συνεχόμενη ανάπτυξη του %2.</translation>
+    </message>
+</context>
+<context>
+    <name>ClearButton</name>
+    <message>
+        <source>Clear</source>
+        <translation>Καθαρισμός</translation>
+    </message>
+</context>
+<context>
+    <name>ListModel</name>
+    <message>
+        <source>Searching...</source>
+        <translation>Αναζήτηση...</translation>
+    </message>
+    <message>
+        <source>Show %1 More</source>
+        <translation>Εμφάνιση %1 ακόμα</translation>
+    </message>
+    <message>
+        <source>No videos</source>
+        <translation>Κανένα βίντεο</translation>
+    </message>
+    <message>
+        <source>No more videos</source>
+        <translation>Δεν υπάρχουν άλλα βίντεο</translation>
+    </message>
+</context>
+<context>
+    <name>LoadingWidget</name>
+    <message>
+        <source>Error</source>
+        <translation>Σφάλμα</translation>
+    </message>
+</context>
+<context>
+    <name>MainWindow</name>
+    <message>
+        <source>&amp;Back</source>
+        <translation type="obsolete">&amp;Πίσω</translation>
+    </message>
+    <message>
+        <source>Alt+Left</source>
+        <translation type="obsolete">Alt+Вліво</translation>
+    </message>
+    <message>
+        <source>Go to the previous view</source>
+        <translation type="obsolete">Προς την προηγούμενη όψη (view)</translation>
+    </message>
+    <message>
+        <source>&amp;Stop</source>
+        <translation>&amp;Στοπ</translation>
+    </message>
+    <message>
+        <source>Stop playback and go back to the search view</source>
+        <translation>Στοπ αναπαραγωγής και επιστροφή στην όψη αναζήτησης</translation>
+    </message>
+    <message>
+        <source>Esc</source>
+        <translation type="obsolete">Esc</translation>
+    </message>
+    <message>
+        <source>S&amp;kip</source>
+        <translation>&amp;Παράλειψη</translation>
+    </message>
+    <message>
+        <source>Skip to the next video</source>
+        <translation>Παράλειψη προς το επόμενο βίντεο</translation>
+    </message>
+    <message>
+        <source>Ctrl+Right</source>
+        <translation type="obsolete">Ctrl+Вправо</translation>
+    </message>
+    <message>
+        <source>&amp;Pause</source>
+        <translation>&amp;Διακοπή</translation>
+    </message>
+    <message>
+        <source>Pause playback</source>
+        <translation>Διακοπή αναπαραγωγής</translation>
+    </message>
+    <message>
+        <source>Space</source>
+        <translation type="obsolete">Клавіша пробілу</translation>
+    </message>
+    <message>
+        <source>&amp;Full Screen</source>
+        <translation>&amp;Πλήρης Οθόνη</translation>
+    </message>
+    <message>
+        <source>Go full screen</source>
+        <translation>Προβολή σε Πλήρη Οθόνη</translation>
+    </message>
+    <message>
+        <source>Alt+Return</source>
+        <translation type="obsolete">Alt+Enter</translation>
+    </message>
+    <message>
+        <source>&amp;YouTube</source>
+        <translation type="obsolete">&amp;YouTube</translation>
+    </message>
+    <message>
+        <source>Open the YouTube video page</source>
+        <translation type="obsolete">Άνοιγμα σελίδας YouTube</translation>
+    </message>
+    <message>
+        <source>Ctrl+Y</source>
+        <translation type="obsolete">Ctrl+Y</translation>
+    </message>
+    <message>
+        <source>&amp;Remove</source>
+        <translation>&amp;Αφαίρεση</translation>
+    </message>
+    <message>
+        <source>Remove the selected videos from the playlist</source>
+        <translation>Αφαίρεση επιλεγμένων βίντεο απο την λίστα αναπαραγωγής</translation>
+    </message>
+    <message>
+        <source>Move &amp;Up</source>
+        <translation>Μετακίνηση προς τα &amp;πάνω</translation>
+    </message>
+    <message>
+        <source>Move up the selected videos in the playlist</source>
+        <translation>Μετακίνηση επιλεγμένων βίντεο προς τα πάνω</translation>
+    </message>
+    <message>
+        <source>Ctrl+Up</source>
+        <translation type="obsolete">Ctrl+Вгору</translation>
+    </message>
+    <message>
+        <source>Move &amp;Down</source>
+        <translation>Μετακίνηση προς τα &amp;κάτω</translation>
+    </message>
+    <message>
+        <source>Move down the selected videos in the playlist</source>
+        <translation>Μετακίνηση επιλεγμένων βίντεο προς τα κάτω</translation>
+    </message>
+    <message>
+        <source>Ctrl+Down</source>
+        <translation type="obsolete">Ctrl+Вниз</translation>
+    </message>
+    <message>
+        <source>&amp;Quit</source>
+        <translation>Έ&amp;ξοδος</translation>
+    </message>
+    <message>
+        <source>Ctrl+Q</source>
+        <translation>Ctrl+Q</translation>
+    </message>
+    <message>
+        <source>Bye</source>
+        <translation>Γεια</translation>
+    </message>
+    <message>
+        <source>&amp;Website</source>
+        <translation>&amp;Ιστοχώρος</translation>
+    </message>
+    <message>
+        <source>Minitube on the Web</source>
+        <translation type="obsolete">Minitube в мережі</translation>
+    </message>
+    <message>
+        <source>%1 on the Web</source>
+        <translation>Το %1 στο διαδίκτυο</translation>
+    </message>
+    <message>
+        <source>&amp;Donate via PayPal</source>
+        <translation type="obsolete">Δωρεά μέσω Pa&amp;yPal</translation>
+    </message>
+    <message>
+        <source>Please support the continued development of %1</source>
+        <translation>Παρακαλούμε υποστηρίξτε την συνεχόμενη ανάπτυξη του %1</translation>
+    </message>
+    <message>
+        <source>&amp;About</source>
+        <translation>&amp;Σχετικά</translation>
+    </message>
+    <message>
+        <source>Info about %1</source>
+        <translation>Πληροφορίες για %1</translation>
+    </message>
+    <message>
+        <source>&amp;Search</source>
+        <translation type="obsolete">Пош&amp;ук</translation>
+    </message>
+    <message>
+        <source>&amp;Application</source>
+        <translation>&amp;Εφαρμογή</translation>
+    </message>
+    <message>
+        <source>&amp;Playlist</source>
+        <translation>Λ&amp;ίστα αναπαραγωγής</translation>
+    </message>
+    <message>
+        <source>&amp;Video</source>
+        <translation>Βίν&amp;τεο</translation>
+    </message>
+    <message>
+        <source>&amp;Help</source>
+        <translation>&amp;Βοήθεια</translation>
+    </message>
+    <message>
+        <source>Opening %1</source>
+        <translation>Άνοιγμα %1</translation>
+    </message>
+    <message>
+        <source>&amp;Play</source>
+        <translation>Α&amp;ναπαραγωγή</translation>
+    </message>
+    <message>
+        <source>Resume playback</source>
+        <translation>Συνέχεια αναπαραγωγής</translation>
+    </message>
+    <message>
+        <source>Exit &amp;Full Screen</source>
+        <translation>Έξο&amp;δος απο Πλήρη Οθόνη</translation>
+    </message>
+    <message>
+        <source>&amp;Compact mode</source>
+        <translation>&amp;Συμπαγής εμφάνιση</translation>
+    </message>
+    <message>
+        <source>Hide the playlist and the toolbar</source>
+        <translation>Απόκρυψη λίστας αναπαραγωγής και εργαλειοθήκης</translation>
+    </message>
+    <message>
+        <source>Fatal error: %1</source>
+        <translation>Θανάσιμο σφάλμα : %1</translation>
+    </message>
+    <message>
+        <source>Error: %1</source>
+        <translation>Σφάλμα: %1</translation>
+    </message>
+    <message>
+        <source>Ctrl+M</source>
+        <translation>Ctrl+M</translation>
+    </message>
+    <message>
+        <source>Volume at %1%</source>
+        <translation>Ένταση στο %1%</translation>
+    </message>
+    <message>
+        <source>Volume is muted</source>
+        <translation>Σίγαση</translation>
+    </message>
+    <message>
+        <source>Volume is unmuted</source>
+        <translation>Η ένταση αποκαταστάθηκε</translation>
+    </message>
+    <message>
+        <source>Search</source>
+        <translation>Αναζήτηση</translation>
+    </message>
+    <message>
+        <source>Mute volume</source>
+        <translation>Σίγαση</translation>
+    </message>
+    <message>
+        <source>Press %1 to raise the volume, %2 to lower it</source>
+        <translation>Πατήστε %1 για να αυξήσετε την ένταση, %2 για να την χαμηλώσετε</translation>
+    </message>
+    <message>
+        <source>Remaining time: %1</source>
+        <translation>Υπολειπόμενος χρόνος: %1</translation>
+    </message>
+    <message>
+        <source>High Definition video is enabled</source>
+        <translation type="obsolete">Βίντεο Υψηλής Ανάλυσης ενεργό</translation>
+    </message>
+    <message>
+        <source>High Definition video is not enabled</source>
+        <translation type="obsolete">Βίντεο Υψηλής Ανάλυσης ανενεργό</translation>
+    </message>
+    <message>
+        <source>The current video is in High Definition</source>
+        <translation type="obsolete">Το τρέχων βίντεο είναι σε Υψηλή Ανάλυση</translation>
+    </message>
+    <message>
+        <source>The current video is not in High Definition</source>
+        <translation type="obsolete">Το τρέχων βίντεο δεν είναι σε Υψηλή Ανάλυση</translation>
+    </message>
+    <message>
+        <source>&amp;Clear recent keywords</source>
+        <translation>&amp;Καθαρισμός πρόσφατων αναζητήσεων</translation>
+    </message>
+    <message>
+        <source>Clear the search history. Cannot be undone.</source>
+        <translation>Καθαρισμός του ιστορικού αναζήτησης. Δεν μπορεί να γίνει επαναφορά.</translation>
+    </message>
+    <message>
+        <source>Your privacy is now safe</source>
+        <translation>Η ιδιωτικότητα σας είναι τώρα ασφαλής</translation>
+    </message>
+    <message>
+        <source>Open the &amp;YouTube page</source>
+        <translation>Ανοίξτε την ιστοσελίδα του &amp;YouTube</translation>
+    </message>
+    <message>
+        <source>Go to the YouTube video page and pause playback</source>
+        <translation>Πλοήγηση στην σελίδα βίντεο του YouTube και παύση αναπαραγωγής</translation>
+    </message>
+    <message>
+        <source>Copy the YouTube &amp;link</source>
+        <translatorcomment>link=σύνδεσμος, however link is more commonly used</translatorcomment>
+        <translation>Αντιγραφή του YouTube &amp;link</translation>
+    </message>
+    <message>
+        <source>Copy the current video YouTube link to the clipboard</source>
+        <translation>Αντιγραφή του link του τρέχοντος YouTube βίντεο στην μνήμη</translation>
+    </message>
+    <message>
+        <source>Copy the video stream &amp;URL</source>
+        <translation>Αντιγραφή του &amp;URL της ροής βίντεο</translation>
+    </message>
+    <message>
+        <source>Copy the current video stream URL to the clipboard</source>
+        <translation>Αντιγραφή του URL της τρέχουσας ροής βίντεο στην μνήμη</translation>
+    </message>
+    <message>
+        <source>Make a &amp;donation</source>
+        <translation>Κάντε μια &amp;δωρεά</translation>
+    </message>
+    <message>
+        <source>Maximum video definition set to %1</source>
+        <translation>H μέγιστη ανάλυση βίντεο τέθηκε σε %1</translation>
+    </message>
+</context>
+<context>
+    <name>MediaView</name>
+    <message>
+        <source>Most relevant</source>
+        <translation>Πιο σχετικά</translation>
+    </message>
+    <message>
+        <source>Most recent</source>
+        <translation>Πιο πρόσφατα</translation>
+    </message>
+    <message>
+        <source>Most viewed</source>
+        <translation>Πιο προβεβλημένα</translation>
+    </message>
+    <message>
+        <source>You&apos;re watching &quot;%1&quot;</source>
+        <translation>Βλέπετε «%1»</translation>
+    </message>
+    <message>
+        <source>You can now paste the YouTube link into another application</source>
+        <translation>Τώρα μπορείτε να επικολλήσετε το YouTube link σε κάποια άλλη εφαρμογή</translation>
+    </message>
+    <message>
+        <source>You can now paste the video stream URL into another application</source>
+        <translation>Τώρα μπορείτε να επικολλήσετε το URL της ροής βίντεο σε κάποια άλλη εφαρμογή</translation>
+    </message>
+    <message>
+        <source>The link will be valid only for a limited time.</source>
+        <translation>Ο σύνδεμος θα είναι έγκυρος μόνο για περιορισμένο χρονικό διάστημα.</translation>
+    </message>
+</context>
+<context>
+    <name>NetworkAccess</name>
+    <message>
+        <source>Network error: %1</source>
+        <translation>Σφάλμα δικτύου: %1</translation>
+    </message>
+</context>
+<context>
+    <name>PrettyItemDelegate</name>
+    <message>
+        <source>%1 views</source>
+        <translation>Προβολές %1</translation>
+    </message>
+</context>
+<context>
+    <name>SearchLineEdit</name>
+    <message>
+        <source>Search</source>
+        <translation>Αναζήτηση</translation>
+    </message>
+</context>
+<context>
+    <name>SearchView</name>
+    <message>
+        <source>Welcome to &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</source>
+        <translation>Καλωσορίσατε στο &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
+    </message>
+    <message>
+        <source>Enter a keyword to start watching videos.</source>
+        <translation>Εισάγετε μια λέξη-κλειδί για να αρχίσετε να βλέπετε βίντεο.</translation>
+    </message>
+    <message>
+        <source>Watch</source>
+        <translation>Δείτε</translation>
+    </message>
+    <message>
+        <source>Recent keywords</source>
+        <translation>Πρόσφατες λέξεις-κλειδιά</translation>
+    </message>
+    <message>
+        <source>A new version of %1 is available. Please &lt;a href=&apos;%2&apos;&gt;update to version %3&lt;/a&gt;</source>
+        <translation>Μια νέα έκδοση του %1 είναι διαθέσιμη. Παρακαλούμε&lt;a href=&apos;%2&apos;&gt; αναβαθμίστε στην έκδοση %3&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>Make yourself comfortable</source>
+        <translation>Βολευτείτε</translation>
+    </message>
+</context>
+<context>
+    <name>SettingsView</name>
+    <message>
+        <source>Preferences</source>
+        <translation type="obsolete">Επιλογές</translation>
+    </message>
+    <message>
+        <source>&amp;Video options</source>
+        <translation type="obsolete">Відео &amp;можливості</translation>
+    </message>
+    <message>
+        <source>Use high quality video when available</source>
+        <translation type="obsolete">Відтворювати відео високої якості при наявності</translation>
+    </message>
+    <message>
+        <source>&amp;Saved recent keywords</source>
+        <translation type="obsolete">&amp;Збережені недавні терміни пошуку</translation>
+    </message>
+    <message>
+        <source>&amp;Clear recent keywords</source>
+        <translation type="obsolete">Очистити недавн&amp;і терміни пошуку</translation>
+    </message>
+    <message>
+        <source>&amp;Close</source>
+        <translation type="obsolete">За&amp;крити</translation>
+    </message>
+</context>
+<context>
+    <name>Video</name>
+    <message>
+        <source>Network error: %1 for %2</source>
+        <translation>Σφάλμα δικτύου: %1 για %2</translation>
+    </message>
+</context>
+</TS>
diff --git a/locale/es.ts b/locale/es.ts
new file mode 100644 (file)
index 0000000..9441085
--- /dev/null
@@ -0,0 +1,488 @@
+<!DOCTYPE TS><TS>
+<defaultcodec>UTF-8</defaultcodec>
+<context>
+    <name>AboutView</name>
+    <message>
+        <source>There&apos;s life outside the browser!</source>
+        <translation>¡Hay vida más allá del navegador!</translation>
+    </message>
+    <message>
+        <source>Version %1</source>
+        <translation>Versión %1</translation>
+    </message>
+    <message>
+        <source>This is a &quot;Technology Preview&quot; release, do not expect it to be perfect.</source>
+        <translation type="obsolete">Esta es una edición «Prueba conceptual», no espere que sea perfecta.</translation>
+    </message>
+    <message>
+        <source>Report bugs and send in your ideas to %1</source>
+        <translation>Envie sus informes de errores o sus ideas a %1</translation>
+    </message>
+    <message>
+        <source>%1 is Free Software but its development takes precious time.</source>
+        <translation>%1 es Software Libre pero su desarrollo supone un tiempo muy valioso.</translation>
+    </message>
+    <message>
+        <source>Please &lt;a href=&apos;%1&apos;&gt;donate via PayPal&lt;/a&gt; to support the continued development of %2.</source>
+        <translation type="obsolete">Por favor &lt;a href=&apos;%1&apos;&gt;haga una donación a través de PayPal&lt;/a&gt; para colaborar con el desarrollo de %2.</translation>
+    </message>
+    <message>
+        <source>Icon designed by %1.</source>
+        <translation>Icono diseñado por %1.</translation>
+    </message>
+    <message>
+        <source>Compact mode contributed by %1.</source>
+        <translation>Modo compacto aportado por %1.</translation>
+    </message>
+    <message>
+        <source>HTTP proxy support contributed by %1.</source>
+        <translation>Compatibilidad con proxy HTTP aportado por %1.</translation>
+    </message>
+    <message>
+        <source>Translated by %1</source>
+        <translation>Traducido por %1</translation>
+    </message>
+    <message>
+        <source>Released under the &lt;a href=&apos;%1&apos;&gt;GNU General Public License&lt;/a&gt;</source>
+        <translation>Publicado bajo la &lt;a href=&apos;%1&apos;&gt;Licencia Publica General GNU&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>&amp;Close</source>
+        <translation>&amp;Cerrar</translation>
+    </message>
+    <message>
+        <source>About</source>
+        <translation>Acerca de</translation>
+    </message>
+    <message>
+        <source>What you always wanted to know about %1 and never dared to ask</source>
+        <translation>Que es lo que siempre quiso saber acerca de %1 y nunca se atrevió a preguntar</translation>
+    </message>
+    <message>
+        <source>Windows version built by %1</source>
+        <translation type="obsolete">Versión Windows construida por %1</translation>
+    </message>
+    <message>
+        <source>Please &lt;a href=&apos;%1&apos;&gt;donate&lt;/a&gt; to support the continued development of %2.</source>
+        <translation>Haga una &lt;a href=&apos;%1&apos;&gt;donación&lt;/a&gt; para ayudar en la continuación del desarrollo de %2.</translation>
+    </message>
+</context>
+<context>
+    <name>ClearButton</name>
+    <message>
+        <source>Clear</source>
+        <translation>Limpiar</translation>
+    </message>
+</context>
+<context>
+    <name>ListModel</name>
+    <message>
+        <source>Searching...</source>
+        <translation>Buscando...</translation>
+    </message>
+    <message>
+        <source>Show %1 More</source>
+        <translation>Mostrar %1 más</translation>
+    </message>
+    <message>
+        <source>No videos</source>
+        <translation>No hay videos</translation>
+    </message>
+    <message>
+        <source>No more videos</source>
+        <translation>No hay más videos</translation>
+    </message>
+</context>
+<context>
+    <name>LoadingWidget</name>
+    <message>
+        <source>Error</source>
+        <translation>Error</translation>
+    </message>
+</context>
+<context>
+    <name>MainWindow</name>
+    <message>
+        <source>&amp;Back</source>
+        <translation type="obsolete">&amp;Atrás</translation>
+    </message>
+    <message>
+        <source>Alt+Left</source>
+        <translation type="obsolete">Retroceder</translation>
+    </message>
+    <message>
+        <source>Go to the previous view</source>
+        <translation type="obsolete">Ir a la vista anterior</translation>
+    </message>
+    <message>
+        <source>&amp;Stop</source>
+        <translation>&amp;Detener</translation>
+    </message>
+    <message>
+        <source>Stop playback and go back to the search view</source>
+        <translation>Detener la reproducción y volver a la vista de busqueda</translation>
+    </message>
+    <message>
+        <source>Esc</source>
+        <translation type="obsolete">Escape</translation>
+    </message>
+    <message>
+        <source>S&amp;kip</source>
+        <translation>&amp;Saltar</translation>
+    </message>
+    <message>
+        <source>Skip to the next video</source>
+        <translation>Saltar al siguiente video</translation>
+    </message>
+    <message>
+        <source>Ctrl+Right</source>
+        <translation type="obsolete">Avanzar</translation>
+    </message>
+    <message>
+        <source>&amp;Pause</source>
+        <translation>&amp;Pausar</translation>
+    </message>
+    <message>
+        <source>Pause playback</source>
+        <translation>Pausar la reproducción</translation>
+    </message>
+    <message>
+        <source>Space</source>
+        <translation type="obsolete">Espacio</translation>
+    </message>
+    <message>
+        <source>&amp;Full Screen</source>
+        <translation>&amp;Pantalla completa</translation>
+    </message>
+    <message>
+        <source>Go full screen</source>
+        <translation>Ir a pantalla completa</translation>
+    </message>
+    <message>
+        <source>Alt+Return</source>
+        <translation type="obsolete">Pantalla completa</translation>
+    </message>
+    <message>
+        <source>&amp;YouTube</source>
+        <translation type="obsolete">&amp;YouTube</translation>
+    </message>
+    <message>
+        <source>Open the YouTube video page</source>
+        <translation type="obsolete">Abrir la pagina del video en YouTube</translation>
+    </message>
+    <message>
+        <source>&amp;Remove</source>
+        <translation>&amp;Eliminar</translation>
+    </message>
+    <message>
+        <source>Remove the selected videos from the playlist</source>
+        <translation>Eliminarlos videos seleccionados de la lista de reproducción</translation>
+    </message>
+    <message>
+        <source>Move &amp;Up</source>
+        <translation>Mover A&amp;rriba</translation>
+    </message>
+    <message>
+        <source>Move up the selected videos in the playlist</source>
+        <translation>Mover hacia arriba, en la lista de reproducción, los videos seleccionados</translation>
+    </message>
+    <message>
+        <source>Ctrl+Up</source>
+        <translation type="obsolete">Arriba</translation>
+    </message>
+    <message>
+        <source>Move &amp;Down</source>
+        <translation>Mover Aba&amp;jo</translation>
+    </message>
+    <message>
+        <source>Move down the selected videos in the playlist</source>
+        <translation>Mover hacia abajo, en la lista de reproducción, los videos seleccionados</translation>
+    </message>
+    <message>
+        <source>Ctrl+Down</source>
+        <translation type="obsolete">Arriba</translation>
+    </message>
+    <message>
+        <source>&amp;Quit</source>
+        <translation>&amp;Salir</translation>
+    </message>
+    <message>
+        <source>Ctrl+Q</source>
+        <translation>Ctrl+Q</translation>
+    </message>
+    <message>
+        <source>Bye</source>
+        <translation>Hasta luego</translation>
+    </message>
+    <message>
+        <source>&amp;Website</source>
+        <translation>Sitio &amp;web</translation>
+    </message>
+    <message>
+        <source>Minitube on the Web</source>
+        <translation type="obsolete">Minitube en internet</translation>
+    </message>
+    <message>
+        <source>&amp;Compact mode</source>
+        <translation>Modo &amp;compacto</translation>
+    </message>
+    <message>
+        <source>Hide the playlist and the toolbar</source>
+        <translation>Ocultar la lista de reproducción y la barra de herramientas</translation>
+    </message>
+    <message>
+        <source>%1 on the Web</source>
+        <translation>%1 en la web</translation>
+    </message>
+    <message>
+        <source>&amp;Donate via PayPal</source>
+        <translation type="obsolete">D&amp;onar a través de PayPal</translation>
+    </message>
+    <message>
+        <source>Please support the continued development of %1</source>
+        <translation>Por favor apoye el desarrollo continuo de %1</translation>
+    </message>
+    <message>
+        <source>&amp;About</source>
+        <translation>&amp;Acerca de</translation>
+    </message>
+    <message>
+        <source>Info about %1</source>
+        <translation>Información acerca de %1</translation>
+    </message>
+    <message>
+        <source>&amp;Search</source>
+        <translation type="obsolete">&amp;Buscar</translation>
+    </message>
+    <message>
+        <source>Search</source>
+        <translation>Buscar</translation>
+    </message>
+    <message>
+        <source>Mute volume</source>
+        <translation>Silenciar el volumen</translation>
+    </message>
+    <message>
+        <source>Ctrl+M</source>
+        <translation></translation>
+    </message>
+    <message>
+        <source>&amp;Application</source>
+        <translation>&amp;Aplicación</translation>
+    </message>
+    <message>
+        <source>&amp;Playlist</source>
+        <translation>&amp;Lista de reproducción</translation>
+    </message>
+    <message>
+        <source>&amp;Video</source>
+        <translation>&amp;Video</translation>
+    </message>
+    <message>
+        <source>&amp;Help</source>
+        <translation>Ay&amp;uda</translation>
+    </message>
+    <message>
+        <source>Press %1 to raise the volume, %2 to lower it</source>
+        <translation>Presione %1 para aumentar el volumen, %2 para reducirlo</translation>
+    </message>
+    <message>
+        <source>Opening %1</source>
+        <translation>Abriendo %1</translation>
+    </message>
+    <message>
+        <source>Fatal error: %1</source>
+        <translation>Error fatal:%1</translation>
+    </message>
+    <message>
+        <source>Error: %1</source>
+        <translation>Error: %1</translation>
+    </message>
+    <message>
+        <source>&amp;Play</source>
+        <translation>&amp;Reproducir</translation>
+    </message>
+    <message>
+        <source>Resume playback</source>
+        <translation>Continuar la reproducción</translation>
+    </message>
+    <message>
+        <source>Exit &amp;Full Screen</source>
+        <translation>Salir de &amp;pantalla completa</translation>
+    </message>
+    <message>
+        <source>Remaining time: %1</source>
+        <translation>Tiempo restante: %1</translation>
+    </message>
+    <message>
+        <source>Volume at %1%</source>
+        <translation>Volumen al %1%</translation>
+    </message>
+    <message>
+        <source>Volume is muted</source>
+        <translation>El volumen está silenciado</translation>
+    </message>
+    <message>
+        <source>Volume is unmuted</source>
+        <translation>El volumen no está silenciado</translation>
+    </message>
+    <message>
+        <source>High Definition video is enabled</source>
+        <translation type="obsolete">El video en alta definición está activado</translation>
+    </message>
+    <message>
+        <source>High Definition video is not enabled</source>
+        <translation type="obsolete">El video en alta definición está desactivado</translation>
+    </message>
+    <message>
+        <source>The current video is in High Definition</source>
+        <translation type="obsolete">El vídeo actual es de alta definición</translation>
+    </message>
+    <message>
+        <source>The current video is not in High Definition</source>
+        <translation type="obsolete">El vídeo actual no es de alta definición</translation>
+    </message>
+    <message>
+        <source>&amp;Clear recent keywords</source>
+        <translation>Limpiar palabras &amp;clave recientes</translation>
+    </message>
+    <message>
+        <source>Clear the search history. Cannot be undone.</source>
+        <translation>Limpiar el historial de búsquedas. No se puede deshacer.</translation>
+    </message>
+    <message>
+        <source>Your privacy is now safe</source>
+        <translation>Su intimidad ahora está segura
+</translation>
+    </message>
+    <message>
+        <source>Open the &amp;YouTube page</source>
+        <translation>Abrir la página de &amp;YouTube</translation>
+    </message>
+    <message>
+        <source>Go to the YouTube video page and pause playback</source>
+        <translation>Ir a la página de videos YouTube y pausar la reproducción</translation>
+    </message>
+    <message>
+        <source>Copy the YouTube &amp;link</source>
+        <translation>Copiar el en&amp;lace de YouTube</translation>
+    </message>
+    <message>
+        <source>Copy the current video YouTube link to the clipboard</source>
+        <translation>Copiar el enlace actual de video de YouTube en el portapapeles</translation>
+    </message>
+    <message>
+        <source>Copy the video stream &amp;URL</source>
+        <translation>Copiar el &amp;URL de flujo de video</translation>
+    </message>
+    <message>
+        <source>Copy the current video stream URL to the clipboard</source>
+        <translation>Copiar el URL actual del flujo de video en el portapapeles</translation>
+    </message>
+    <message>
+        <source>Make a &amp;donation</source>
+        <translation>Hacer una &amp;donación</translation>
+    </message>
+    <message>
+        <source>Maximum video definition set to %1</source>
+        <translation>Resolución máxima de video fijada en %1</translation>
+    </message>
+</context>
+<context>
+    <name>MediaView</name>
+    <message>
+        <source>Most relevant</source>
+        <translation>Más destacados</translation>
+    </message>
+    <message>
+        <source>Most recent</source>
+        <translation>Más recientes</translation>
+    </message>
+    <message>
+        <source>Most viewed</source>
+        <translation>Más vistos</translation>
+    </message>
+    <message>
+        <source>You&apos;re watching &quot;%1&quot;</source>
+        <translation>Está viendo %1</translation>
+    </message>
+    <message>
+        <source>You can now paste the YouTube link into another application</source>
+        <translation>Ahora puede pegar el enlace de YouTube en otra aplicación</translation>
+    </message>
+    <message>
+        <source>You can now paste the video stream URL into another application</source>
+        <translation>Ahora puede pegar el URl del flujo de video en otra aplicación</translation>
+    </message>
+    <message>
+        <source>The link will be valid only for a limited time.</source>
+        <translation>El enlace es válido sólo por un tiempo limitado.</translation>
+    </message>
+</context>
+<context>
+    <name>NetworkAccess</name>
+    <message>
+        <source>Network error: %1</source>
+        <translation>Error de red: %1</translation>
+    </message>
+</context>
+<context>
+    <name>PrettyItemDelegate</name>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 visualizaciones</translation>
+    </message>
+</context>
+<context>
+    <name>SearchLineEdit</name>
+    <message>
+        <source>Search</source>
+        <translation>Buscar</translation>
+    </message>
+</context>
+<context>
+    <name>SearchView</name>
+    <message>
+        <source>Welcome to &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</source>
+        <translation>Bienvenido a &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>Enter a keyword to start watching videos.</source>
+        <translation>Introduzca una palabra clave para empezar a ver videos.</translation>
+    </message>
+    <message>
+        <source>Watch</source>
+        <translation>Ver</translation>
+    </message>
+    <message>
+        <source>Recent keywords</source>
+        <translation>Palabra clave reciente</translation>
+    </message>
+    <message>
+        <source>A new version of %1 is available. Please &lt;a href=&apos;%2&apos;&gt;update to version %3&lt;/a&gt;</source>
+        <translation>Está disponible una nueva versión de %1. Por favor &lt;a href=&apos;%2&apos;&gt;actualice a la version %3&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>Make yourself comfortable</source>
+        <translation>Sientase cómodo</translation>
+    </message>
+</context>
+<context>
+    <name>SettingsView</name>
+    <message>
+        <source>Preferences</source>
+        <translation type="obsolete">Preferencias</translation>
+    </message>
+    <message>
+        <source>&amp;Close</source>
+        <translation type="obsolete">&amp;Cerrar</translation>
+    </message>
+</context>
+<context>
+    <name>Video</name>
+    <message>
+        <source>Network error: %1 for %2</source>
+        <translation>Error de red: %1 por %2</translation>
+    </message>
+</context>
+</TS>
diff --git a/locale/es_AR.ts b/locale/es_AR.ts
new file mode 100644 (file)
index 0000000..fb8955f
--- /dev/null
@@ -0,0 +1,465 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.0" language="es_AR" sourcelanguage="en_US">
+<defaultcodec>UTF-8</defaultcodec>
+<context>
+    <name>AboutView</name>
+    <message>
+        <source>There&apos;s life outside the browser!</source>
+        <translation>Hay vida fuera del navegador!</translation>
+    </message>
+    <message>
+        <source>Version %1</source>
+        <translation>Versión-%1</translation>
+    </message>
+    <message>
+        <source>This is a &quot;Technology Preview&quot; release, do not expect it to be perfect.</source>
+        <translation type="obsolete">Esta es a edicion &quot;Technology Preview&quot;. no espere que sea perfecta.</translation>
+    </message>
+    <message>
+        <source>Report bugs and send in your ideas to %1</source>
+        <translation>Reportar errores y envia tus idea a %1</translation>
+    </message>
+    <message>
+        <source>%1 is Free Software but its development takes precious time.</source>
+        <translation>%1 es Software Libre pero su desarrollo lleva tiempo.</translation>
+    </message>
+    <message>
+        <source>Please &lt;a href=&apos;%1&apos;&gt;donate via PayPal&lt;/a&gt; to support the continued development of %2.</source>
+        <translation type="obsolete">Por favor &lt;a href=&apos;%1&apos;&gt;donar a través de PayPal para colaborar con el desarrollo de %2.</translation>
+    </message>
+    <message>
+        <source>Icon designed by %1.</source>
+        <translation>Iconos diseñados por %1.</translation>
+    </message>
+    <message>
+        <source>Compact mode contributed by %1.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>HTTP proxy support contributed by %1.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Translated by %1</source>
+        <translation>Traducido por %1</translation>
+    </message>
+    <message>
+        <source>Released under the &lt;a href=&apos;%1&apos;&gt;GNU General Public License&lt;/a&gt;</source>
+        <translation>Desarrollado bajo &lt;a href=&apos;%1&apos;&gt;GNU General Public License&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>&amp;Close</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>About</source>
+        <translation>Acerca</translation>
+    </message>
+    <message>
+        <source>What you always wanted to know about %1 and never dared to ask</source>
+        <translation>Que es lo que siempre quisiste saber acerca de %1 y nunca lo preguntaste</translation>
+    </message>
+    <message>
+        <source>Please &lt;a href=&apos;%1&apos;&gt;donate&lt;/a&gt; to support the continued development of %2.</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>ClearButton</name>
+    <message>
+        <source>Clear</source>
+        <translation>Limpiar</translation>
+    </message>
+</context>
+<context>
+    <name>ListModel</name>
+    <message>
+        <source>Searching...</source>
+        <translation>Buscando...</translation>
+    </message>
+    <message>
+        <source>Show %1 More</source>
+        <translation>Mostrar %1 más</translation>
+    </message>
+    <message>
+        <source>No videos</source>
+        <translation>No videos</translation>
+    </message>
+    <message>
+        <source>No more videos</source>
+        <translation>No más videos</translation>
+    </message>
+</context>
+<context>
+    <name>LoadingWidget</name>
+    <message>
+        <source>Error</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>MainWindow</name>
+    <message>
+        <source>&amp;Back</source>
+        <translation type="obsolete">&amp;Atrás</translation>
+    </message>
+    <message>
+        <source>Alt+Left</source>
+        <translation type="obsolete">Retroceder</translation>
+    </message>
+    <message>
+        <source>Go to the previous view</source>
+        <translation type="obsolete">Atrás</translation>
+    </message>
+    <message>
+        <source>&amp;Stop</source>
+        <translation>&amp;Parar</translation>
+    </message>
+    <message>
+        <source>Stop playback and go back to the search view</source>
+        <translation>Parar</translation>
+    </message>
+    <message>
+        <source>Esc</source>
+        <translation type="obsolete">Escape</translation>
+    </message>
+    <message>
+        <source>S&amp;kip</source>
+        <translation>&amp;Saltar</translation>
+    </message>
+    <message>
+        <source>Skip to the next video</source>
+        <translation>Siguiente</translation>
+    </message>
+    <message>
+        <source>Ctrl+Right</source>
+        <translation type="obsolete">Avanzar</translation>
+    </message>
+    <message>
+        <source>&amp;Pause</source>
+        <translation>&amp;Pausar</translation>
+    </message>
+    <message>
+        <source>Pause playback</source>
+        <translation>Pausar</translation>
+    </message>
+    <message>
+        <source>Space</source>
+        <translation type="obsolete">Espacio</translation>
+    </message>
+    <message>
+        <source>&amp;Full Screen</source>
+        <translation>&amp;Pantalla completa</translation>
+    </message>
+    <message>
+        <source>Go full screen</source>
+        <translation>Pantalla completa</translation>
+    </message>
+    <message>
+        <source>Alt+Return</source>
+        <translation type="obsolete">Pantalla completa</translation>
+    </message>
+    <message>
+        <source>&amp;YouTube</source>
+        <translation type="obsolete">&amp;YouTube</translation>
+    </message>
+    <message>
+        <source>Open the YouTube video page</source>
+        <translation type="obsolete">Abrir YouTube</translation>
+    </message>
+    <message>
+        <source>&amp;Remove</source>
+        <translation>&amp;Borrar</translation>
+    </message>
+    <message>
+        <source>Remove the selected videos from the playlist</source>
+        <translation>Borrar</translation>
+    </message>
+    <message>
+        <source>Move &amp;Up</source>
+        <translation>&amp;Arriba</translation>
+    </message>
+    <message>
+        <source>Move up the selected videos in the playlist</source>
+        <translation>Arriba</translation>
+    </message>
+    <message>
+        <source>Ctrl+Up</source>
+        <translation type="obsolete">Arriba</translation>
+    </message>
+    <message>
+        <source>Move &amp;Down</source>
+        <translation>&amp;Abajo</translation>
+    </message>
+    <message>
+        <source>Move down the selected videos in the playlist</source>
+        <translation>Abajo</translation>
+    </message>
+    <message>
+        <source>Ctrl+Down</source>
+        <translation type="obsolete">Arriba</translation>
+    </message>
+    <message>
+        <source>&amp;Quit</source>
+        <translation>&amp;Salir</translation>
+    </message>
+    <message>
+        <source>Ctrl+Q</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Bye</source>
+        <translation>Adiós</translation>
+    </message>
+    <message>
+        <source>&amp;Website</source>
+        <translation>&amp;Sitio web</translation>
+    </message>
+    <message>
+        <source>Minitube on the Web</source>
+        <translation type="obsolete">Minitube en internet</translation>
+    </message>
+    <message>
+        <source>&amp;Compact mode</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Hide the playlist and the toolbar</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>%1 on the Web</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>&amp;Donate via PayPal</source>
+        <translation type="obsolete">&amp;Donar-a-través de PayPal</translation>
+    </message>
+    <message>
+        <source>Please support the continued development of %1</source>
+        <translation>Por favor apoyo el desarrollo continuo de-%1</translation>
+    </message>
+    <message>
+        <source>&amp;About</source>
+        <translation>&amp;Acerca</translation>
+    </message>
+    <message>
+        <source>Info about %1</source>
+        <translation>Acercá de-%1</translation>
+    </message>
+    <message>
+        <source>&amp;Search</source>
+        <translation type="obsolete">&amp;Buscar</translation>
+    </message>
+    <message>
+        <source>Search</source>
+        <translation type="unfinished">Buscar</translation>
+    </message>
+    <message>
+        <source>Mute volume</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Ctrl+M</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>&amp;Application</source>
+        <translation type="unfinished">&amp;Aplicación</translation>
+    </message>
+    <message>
+        <source>&amp;Playlist</source>
+        <translation>&amp;Lista de reproducción</translation>
+    </message>
+    <message>
+        <source>&amp;Video</source>
+        <translation>&amp;Video</translation>
+    </message>
+    <message>
+        <source>&amp;Help</source>
+        <translation>&amp;Ayuda</translation>
+    </message>
+    <message>
+        <source>Press %1 to raise the volume, %2 to lower it</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Opening %1</source>
+        <translation>Abriendo-%1</translation>
+    </message>
+    <message>
+        <source>Fatal error: %1</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Error: %1</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>&amp;Play</source>
+        <translation>&amp;Reproducir</translation>
+    </message>
+    <message>
+        <source>Resume playback</source>
+        <translation>Reproducir</translation>
+    </message>
+    <message>
+        <source>Exit &amp;Full Screen</source>
+        <translation>&amp;Salir de pantalla completa</translation>
+    </message>
+    <message>
+        <source>Remaining time: %1</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Volume at %1%</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Volume is muted</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Volume is unmuted</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>&amp;Clear recent keywords</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Clear the search history. Cannot be undone.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Your privacy is now safe</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Open the &amp;YouTube page</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Go to the YouTube video page and pause playback</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Copy the YouTube &amp;link</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Copy the current video YouTube link to the clipboard</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Copy the video stream &amp;URL</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Copy the current video stream URL to the clipboard</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Make a &amp;donation</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Maximum video definition set to %1</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>MediaView</name>
+    <message>
+        <source>Most relevant</source>
+        <translation>Más relevantes</translation>
+    </message>
+    <message>
+        <source>Most recent</source>
+        <translation>Más recientes</translation>
+    </message>
+    <message>
+        <source>Most viewed</source>
+        <translation>Más visitadas</translation>
+    </message>
+    <message>
+        <source>You&apos;re watching &quot;%1&quot;</source>
+        <translation>Estás viendo %1</translation>
+    </message>
+    <message>
+        <source>You can now paste the YouTube link into another application</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>You can now paste the video stream URL into another application</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>The link will be valid only for a limited time.</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>NetworkAccess</name>
+    <message>
+        <source>Network error: %1</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>PrettyItemDelegate</name>
+    <message>
+        <source>%1 views</source>
+        <translation>%1-visitas</translation>
+    </message>
+</context>
+<context>
+    <name>SearchLineEdit</name>
+    <message>
+        <source>Search</source>
+        <translation>Buscar</translation>
+    </message>
+</context>
+<context>
+    <name>SearchView</name>
+    <message>
+        <source>Welcome to &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</source>
+        <translation>Bienvenidos a &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>Enter a keyword to start watching videos.</source>
+        <translation>Ingrese palabra a buscar.</translation>
+    </message>
+    <message>
+        <source>Watch</source>
+        <translation>Viendo</translation>
+    </message>
+    <message>
+        <source>Recent keywords</source>
+        <translation>Búsquedas recientes</translation>
+    </message>
+    <message>
+        <source>A new version of %1 is available. Please &lt;a href=&apos;%2&apos;&gt;update to version %3&lt;/a&gt;</source>
+        <translation>Una nueva versión de %1 esta disponible. Por favor &lt;a href=&apos;%2&apos;&gt;actualice a la version %3&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>Make yourself comfortable</source>
+        <translation>Personalizar</translation>
+    </message>
+</context>
+<context>
+    <name>SettingsView</name>
+    <message>
+        <source>Preferences</source>
+        <translation type="obsolete">Preferencias</translation>
+    </message>
+</context>
+<context>
+    <name>Video</name>
+    <message>
+        <source>Network error: %1 for %2</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+</TS>
diff --git a/locale/es_ES.ts b/locale/es_ES.ts
new file mode 100644 (file)
index 0000000..f0626ea
--- /dev/null
@@ -0,0 +1,489 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.0" language="es_AR" sourcelanguage="en_US">
+<defaultcodec>UTF-8</defaultcodec>
+<context>
+    <name>AboutView</name>
+    <message>
+        <source>There&apos;s life outside the browser!</source>
+        <translation>¡Hay vida más allá del navegador!</translation>
+    </message>
+    <message>
+        <source>Version %1</source>
+        <translation>Versión %1</translation>
+    </message>
+    <message>
+        <source>This is a &quot;Technology Preview&quot; release, do not expect it to be perfect.</source>
+        <translation type="obsolete">Esta es una edición &quot;Technology Preview&quot;, no espere que sea perfecta.</translation>
+    </message>
+    <message>
+        <source>Report bugs and send in your ideas to %1</source>
+        <translation>Notifique errores y envie sus ideas a %1</translation>
+    </message>
+    <message>
+        <source>%1 is Free Software but its development takes precious time.</source>
+        <translation>%1 es Software Libre pero su desarrollo supone valioso tiempo.</translation>
+    </message>
+    <message>
+        <source>Please &lt;a href=&apos;%1&apos;&gt;donate via PayPal&lt;/a&gt; to support the continued development of %2.</source>
+        <translation type="obsolete">Por favor &lt;a href=&apos;%1&apos;&gt;done a través de PayPal&lt;/a&gt; para colaborar con el desarrollo de %2.</translation>
+    </message>
+    <message>
+        <source>Icon designed by %1.</source>
+        <translation>Icono diseñado por %1.</translation>
+    </message>
+    <message>
+        <source>Compact mode contributed by %1.</source>
+        <translation>Modo compacto contribuido por %1.</translation>
+    </message>
+    <message>
+        <source>HTTP proxy support contributed by %1.</source>
+        <translation>Soporte de proxy HTTP contribuido por %1</translation>
+    </message>
+    <message>
+        <source>Windows version built by %1</source>
+        <translation type="obsolete">Versión Windows compilada por %1</translation>
+    </message>
+    <message>
+        <source>Translated by %1</source>
+        <translation>Traducido por %1</translation>
+    </message>
+    <message>
+        <source>Released under the &lt;a href=&apos;%1&apos;&gt;GNU General Public License&lt;/a&gt;</source>
+        <translation>Desarrollado bajo la &lt;a href=&apos;%1&apos;&gt;Licencia publica general GNU&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>&amp;Close</source>
+        <translation>&amp;Cerrar</translation>
+    </message>
+    <message>
+        <source>About</source>
+        <translation>Acerca de</translation>
+    </message>
+    <message>
+        <source>What you always wanted to know about %1 and never dared to ask</source>
+        <translation>Que es lo que siempre quiso saber acerca de %1 y nunca osó preguntar</translation>
+    </message>
+    <message>
+        <source>Please &lt;a href=&apos;%1&apos;&gt;donate&lt;/a&gt; to support the continued development of %2.</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>ClearButton</name>
+    <message>
+        <source>Clear</source>
+        <translation>Limpiar</translation>
+    </message>
+</context>
+<context>
+    <name>ListModel</name>
+    <message>
+        <source>Searching...</source>
+        <translation>Buscando...</translation>
+    </message>
+    <message>
+        <source>Show %1 More</source>
+        <translation>Mostrar %1 más</translation>
+    </message>
+    <message>
+        <source>No videos</source>
+        <translation>No videos</translation>
+    </message>
+    <message>
+        <source>No more videos</source>
+        <translation>No más videos</translation>
+    </message>
+</context>
+<context>
+    <name>LoadingWidget</name>
+    <message>
+        <source>Error</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>MainWindow</name>
+    <message>
+        <source>&amp;Back</source>
+        <translation type="obsolete">&amp;Atrás</translation>
+    </message>
+    <message>
+        <source>Alt+Left</source>
+        <translation type="obsolete">Retroceder</translation>
+    </message>
+    <message>
+        <source>Go to the previous view</source>
+        <translation type="obsolete">Ir a vista previa</translation>
+    </message>
+    <message>
+        <source>&amp;Stop</source>
+        <translation>&amp;Detener</translation>
+    </message>
+    <message>
+        <source>Stop playback and go back to the search view</source>
+        <translation>Detener reproducción y volver a la busqueda</translation>
+    </message>
+    <message>
+        <source>Esc</source>
+        <translation type="obsolete">Escape</translation>
+    </message>
+    <message>
+        <source>S&amp;kip</source>
+        <translation>&amp;Saltar</translation>
+    </message>
+    <message>
+        <source>Skip to the next video</source>
+        <translation>Saltar al siguiente video</translation>
+    </message>
+    <message>
+        <source>Ctrl+Right</source>
+        <translation type="obsolete">Avanzar</translation>
+    </message>
+    <message>
+        <source>&amp;Pause</source>
+        <translation>&amp;Pausar</translation>
+    </message>
+    <message>
+        <source>Pause playback</source>
+        <translation>Pausar reproducción</translation>
+    </message>
+    <message>
+        <source>Space</source>
+        <translation type="obsolete">Espacio</translation>
+    </message>
+    <message>
+        <source>&amp;Full Screen</source>
+        <translation>Pantalla &amp;completa</translation>
+    </message>
+    <message>
+        <source>Go full screen</source>
+        <translation>Ir a pantalla completa</translation>
+    </message>
+    <message>
+        <source>Alt+Return</source>
+        <translation type="obsolete">Pantalla completa</translation>
+    </message>
+    <message>
+        <source>&amp;YouTube</source>
+        <translation type="obsolete">&amp;YouTube</translation>
+    </message>
+    <message>
+        <source>Open the YouTube video page</source>
+        <translation type="obsolete">Abrir pagina del video en YouTube</translation>
+    </message>
+    <message>
+        <source>&amp;Remove</source>
+        <translation>&amp;Borrar</translation>
+    </message>
+    <message>
+        <source>Remove the selected videos from the playlist</source>
+        <translation>Borrar los videos seleccionados de la lista de reproducción</translation>
+    </message>
+    <message>
+        <source>Move &amp;Up</source>
+        <translation>Mover A&amp;rriba</translation>
+    </message>
+    <message>
+        <source>Move up the selected videos in the playlist</source>
+        <translation>Mover arriba los videos seleccionados en la lista de reproducción</translation>
+    </message>
+    <message>
+        <source>Ctrl+Up</source>
+        <translation type="obsolete">Arriba</translation>
+    </message>
+    <message>
+        <source>Move &amp;Down</source>
+        <translation>Mover Aba&amp;jo</translation>
+    </message>
+    <message>
+        <source>Move down the selected videos in the playlist</source>
+        <translation>Mover Abajo los videos seleccionados en la lista de reproducción</translation>
+    </message>
+    <message>
+        <source>Ctrl+Down</source>
+        <translation type="obsolete">Arriba</translation>
+    </message>
+    <message>
+        <source>&amp;Quit</source>
+        <translation>&amp;Salir</translation>
+    </message>
+    <message>
+        <source>Ctrl+Q</source>
+        <translation>Ctrl+Q</translation>
+    </message>
+    <message>
+        <source>Bye</source>
+        <translation>Adiós</translation>
+    </message>
+    <message>
+        <source>&amp;Website</source>
+        <translation>Sitio &amp;web</translation>
+    </message>
+    <message>
+        <source>Minitube on the Web</source>
+        <translation type="obsolete">Minitube en internet</translation>
+    </message>
+    <message>
+        <source>&amp;Compact mode</source>
+        <translation>Modo &amp;Compacto</translation>
+    </message>
+    <message>
+        <source>Hide the playlist and the toolbar</source>
+        <translation>Ocultar la lista de reproducción y la barra de herramientas</translation>
+    </message>
+    <message>
+        <source>%1 on the Web</source>
+        <translation>%1 en la Web</translation>
+    </message>
+    <message>
+        <source>&amp;Donate via PayPal</source>
+        <translation type="obsolete">D&amp;onar a través de PayPal</translation>
+    </message>
+    <message>
+        <source>Please support the continued development of %1</source>
+        <translation>Por favor sustente el desarrollo continuo de %1</translation>
+    </message>
+    <message>
+        <source>&amp;About</source>
+        <translation>&amp;Acerca de</translation>
+    </message>
+    <message>
+        <source>Info about %1</source>
+        <translation>Información de %1</translation>
+    </message>
+    <message>
+        <source>&amp;Search</source>
+        <translation type="obsolete">&amp;Buscar</translation>
+    </message>
+    <message>
+        <source>Search</source>
+        <translation>Buscar</translation>
+    </message>
+    <message>
+        <source>Mute volume</source>
+        <translation>Silenciar</translation>
+    </message>
+    <message>
+        <source>Ctrl+M</source>
+        <translation></translation>
+    </message>
+    <message>
+        <source>&amp;Application</source>
+        <translation type="unfinished">&amp;Aplicación</translation>
+    </message>
+    <message>
+        <source>&amp;Playlist</source>
+        <translation>&amp;Lista de reproducción</translation>
+    </message>
+    <message>
+        <source>&amp;Video</source>
+        <translation>&amp;Video</translation>
+    </message>
+    <message>
+        <source>&amp;Help</source>
+        <translation>&amp;Ayuda</translation>
+    </message>
+    <message>
+        <source>Press %1 to raise the volume, %2 to lower it</source>
+        <translation>Presione %1 para aumentar el volumen, %2 para reducirlo</translation>
+    </message>
+    <message>
+        <source>Opening %1</source>
+        <translation>Abriendo-%1</translation>
+    </message>
+    <message>
+        <source>Fatal error: %1</source>
+        <translation>Error fatal:%1</translation>
+    </message>
+    <message>
+        <source>Error: %1</source>
+        <translation></translation>
+    </message>
+    <message>
+        <source>&amp;Play</source>
+        <translation>&amp;Reproducir</translation>
+    </message>
+    <message>
+        <source>Resume playback</source>
+        <translation>Continuar Reproducción</translation>
+    </message>
+    <message>
+        <source>Exit &amp;Full Screen</source>
+        <translation>&amp;Salir de pantalla completa</translation>
+    </message>
+    <message>
+        <source>Remaining time: %1</source>
+        <translation>Tiempo restante: %1</translation>
+    </message>
+    <message>
+        <source>Volume at %1%</source>
+        <translation>Volumen al %1%</translation>
+    </message>
+    <message>
+        <source>Volume is muted</source>
+        <translation>Volumen silenciado</translation>
+    </message>
+    <message>
+        <source>Volume is unmuted</source>
+        <translation>Volumen activado</translation>
+    </message>
+    <message>
+        <source>High Definition video is enabled</source>
+        <translation type="obsolete">Video de alta definición activado</translation>
+    </message>
+    <message>
+        <source>High Definition video is not enabled</source>
+        <translation type="obsolete">Video de alta definición no activado</translation>
+    </message>
+    <message>
+        <source>The current video is in High Definition</source>
+        <translation type="obsolete">El video actual es de alta definición</translation>
+    </message>
+    <message>
+        <source>The current video is not in High Definition</source>
+        <translation type="obsolete">El video actual no es de alta definición</translation>
+    </message>
+    <message>
+        <source>&amp;Clear recent keywords</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Clear the search history. Cannot be undone.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Your privacy is now safe</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Open the &amp;YouTube page</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Go to the YouTube video page and pause playback</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Copy the YouTube &amp;link</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Copy the current video YouTube link to the clipboard</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Copy the video stream &amp;URL</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Copy the current video stream URL to the clipboard</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Make a &amp;donation</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Maximum video definition set to %1</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>MediaView</name>
+    <message>
+        <source>Most relevant</source>
+        <translation>Más relevantes</translation>
+    </message>
+    <message>
+        <source>Most recent</source>
+        <translation>Más recientes</translation>
+    </message>
+    <message>
+        <source>Most viewed</source>
+        <translation>Más vistos</translation>
+    </message>
+    <message>
+        <source>You&apos;re watching &quot;%1&quot;</source>
+        <translation>Está viendo %1</translation>
+    </message>
+    <message>
+        <source>You can now paste the YouTube link into another application</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>You can now paste the video stream URL into another application</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>The link will be valid only for a limited time.</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>NetworkAccess</name>
+    <message>
+        <source>Network error: %1</source>
+        <translation>Error de red: %1</translation>
+    </message>
+</context>
+<context>
+    <name>PrettyItemDelegate</name>
+    <message>
+        <source>%1 views</source>
+        <translation>%1-visualizaciones</translation>
+    </message>
+</context>
+<context>
+    <name>SearchLineEdit</name>
+    <message>
+        <source>Search</source>
+        <translation>Buscar</translation>
+    </message>
+</context>
+<context>
+    <name>SearchView</name>
+    <message>
+        <source>Welcome to &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</source>
+        <translation>Bienvenido a &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>Enter a keyword to start watching videos.</source>
+        <translation>Introduzca una palabra para empezar a ver videos.</translation>
+    </message>
+    <message>
+        <source>Watch</source>
+        <translation>Ver</translation>
+    </message>
+    <message>
+        <source>Recent keywords</source>
+        <translation>Búsquedas recientes</translation>
+    </message>
+    <message>
+        <source>A new version of %1 is available. Please &lt;a href=&apos;%2&apos;&gt;update to version %3&lt;/a&gt;</source>
+        <translation>Una nueva versión de %1 esta disponible. Por favor &lt;a href=&apos;%2&apos;&gt;actualice a la version %3&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>Make yourself comfortable</source>
+        <translation>Personalizar</translation>
+    </message>
+</context>
+<context>
+    <name>SettingsView</name>
+    <message>
+        <source>Preferences</source>
+        <translation type="obsolete">Preferencias</translation>
+    </message>
+    <message>
+        <source>&amp;Close</source>
+        <translation type="obsolete">&amp;Cerrar</translation>
+    </message>
+</context>
+<context>
+    <name>Video</name>
+    <message>
+        <source>Network error: %1 for %2</source>
+        <translation>Error de red: %1 por %2</translation>
+    </message>
+</context>
+</TS>
diff --git a/locale/fi_FI.ts b/locale/fi_FI.ts
new file mode 100644 (file)
index 0000000..787cf9d
--- /dev/null
@@ -0,0 +1,394 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.0" language="fi_FI">
+<defaultcodec>UTF-8</defaultcodec>
+<context>
+    <name>AboutView</name>
+    <message>
+        <source>There&apos;s life outside the browser!</source>
+        <translation>Elämää on muuallakin kuin selaimessa!</translation>
+    </message>
+    <message>
+        <source>Version %1</source>
+        <translation>Versio numero: %1</translation>
+    </message>
+    <message>
+        <source>%1 is Free Software but its development takes precious time.</source>
+        <translation>%1 on ilmainen ohjelma,mutta sen kehittäminen vie kallista aika.</translation>
+    </message>
+    <message>
+        <source>Please &lt;a href=&apos;%1&apos;&gt;donate&lt;/a&gt; to support the continued development of %2.</source>
+        <translation>Olisi mukaavaa jos tukisit %2 kehitystä tekemällä &lt;a href=&apos;%1&apos;&gt;lahjoituksen&lt;/a&gt;.</translation>
+    </message>
+    <message>
+        <source>Report bugs and send in your ideas to %1</source>
+        <translation>Kerro virheistä/ongelmista ja jaa ideoitasi osoitteeseen %1</translation>
+    </message>
+    <message>
+        <source>Icon designed by %1.</source>
+        <translation>Kuvakkeen suunnitteli %1.</translation>
+    </message>
+    <message>
+        <source>Compact mode contributed by %1.</source>
+        <translation>Kompaktin tilan teki %1.</translation>
+    </message>
+    <message>
+        <source>HTTP proxy support contributed by %1.</source>
+        <translation>HTTP välityspalvelin tuen teki %1.</translation>
+    </message>
+    <message>
+        <source>Translated by %1</source>
+        <translation>Käännöksen teki %1</translation>
+    </message>
+    <message>
+        <source>Released under the &lt;a href=&apos;%1&apos;&gt;GNU General Public License&lt;/a&gt;</source>
+        <translation>Julkaistu &lt;a href=&apos;%1&apos;&gt;GNU General Public License&lt;/a&gt; lisenssin alla</translation>
+    </message>
+    <message>
+        <source>&amp;Close</source>
+        <translation>&amp;Sulje</translation>
+    </message>
+    <message>
+        <source>About</source>
+        <translation>Tietoja</translation>
+    </message>
+    <message>
+        <source>What you always wanted to know about %1 and never dared to ask</source>
+        <translation>Mitä olet aina halunnut tietää %1sta muttet ole koskaan kehdannut kysyä</translation>
+    </message>
+</context>
+<context>
+    <name>ClearButton</name>
+    <message>
+        <source>Clear</source>
+        <translation>Tyhjennä</translation>
+    </message>
+</context>
+<context>
+    <name>ListModel</name>
+    <message>
+        <source>Searching...</source>
+        <translation>Etsitään...</translation>
+    </message>
+    <message>
+        <source>Show %1 More</source>
+        <translation>Näytä %1 lisää</translation>
+    </message>
+    <message>
+        <source>No videos</source>
+        <translation>Ei videoita</translation>
+    </message>
+    <message>
+        <source>No more videos</source>
+        <translation>Ei enempää videoita</translation>
+    </message>
+</context>
+<context>
+    <name>LoadingWidget</name>
+    <message>
+        <source>Error</source>
+        <translation>Virhe</translation>
+    </message>
+</context>
+<context>
+    <name>MainWindow</name>
+    <message>
+        <source>&amp;Stop</source>
+        <translation>&amp;Pysäytä</translation>
+    </message>
+    <message>
+        <source>Stop playback and go back to the search view</source>
+        <translation>Pysäytä taista ja palaa hakuruuttun</translation>
+    </message>
+    <message>
+        <source>S&amp;kip</source>
+        <translation>&amp;Hyppää yli</translation>
+    </message>
+    <message>
+        <source>Skip to the next video</source>
+        <translation>Siirry seuraavaan videoon</translation>
+    </message>
+    <message>
+        <source>&amp;Pause</source>
+        <translation>&amp;Stoppaa</translation>
+    </message>
+    <message>
+        <source>Pause playback</source>
+        <translation>Pysäytä toistaminen</translation>
+    </message>
+    <message>
+        <source>&amp;Full Screen</source>
+        <translation>&amp;Kokoruutu</translation>
+    </message>
+    <message>
+        <source>Go full screen</source>
+        <translation>Vaihda kokoruutu tilaan</translation>
+    </message>
+    <message>
+        <source>&amp;Compact mode</source>
+        <translation>&amp;Kompakti tila</translation>
+    </message>
+    <message>
+        <source>Hide the playlist and the toolbar</source>
+        <translation>Piilota toistolista sekä työkalupalkki</translation>
+    </message>
+    <message>
+        <source>Open the &amp;YouTube page</source>
+        <translation>Avaa &amp;Youtube sivusto</translation>
+    </message>
+    <message>
+        <source>Go to the YouTube video page and pause playback</source>
+        <translation>Pysäytä toisto ja mene YouTube sivulle</translation>
+    </message>
+    <message>
+        <source>Copy the YouTube &amp;link</source>
+        <translation>Kopio YouTube &amp;linkki</translation>
+    </message>
+    <message>
+        <source>Copy the current video YouTube link to the clipboard</source>
+        <translation>Kopio nykyisen videon YouTube linkki</translation>
+    </message>
+    <message>
+        <source>Copy the video stream &amp;URL</source>
+        <translation>Kopio videovirran osoite (&amp;URL)</translation>
+    </message>
+    <message>
+        <source>Copy the current video stream URL to the clipboard</source>
+        <translation>Kopio nykyisen videvirran osoite (URL)</translation>
+    </message>
+    <message>
+        <source>&amp;Remove</source>
+        <translation>&amp;Poista</translation>
+    </message>
+    <message>
+        <source>Remove the selected videos from the playlist</source>
+        <translation>Poista valittu video listalta</translation>
+    </message>
+    <message>
+        <source>Move &amp;Up</source>
+        <translation>Siirrä &amp;Ylös päin</translation>
+    </message>
+    <message>
+        <source>Move up the selected videos in the playlist</source>
+        <translation>Siirrä valittu video ylemmäksi listalla</translation>
+    </message>
+    <message>
+        <source>Move &amp;Down</source>
+        <translation>Sirrä &amp;Alas päin</translation>
+    </message>
+    <message>
+        <source>Move down the selected videos in the playlist</source>
+        <translation>Siirrä valittu video alemmaksi listalla</translation>
+    </message>
+    <message>
+        <source>&amp;Clear recent keywords</source>
+        <translation>&amp;Poista viimeisimmät haut</translation>
+    </message>
+    <message>
+        <source>Clear the search history. Cannot be undone.</source>
+        <translation>Tyhjennä haku historia. Tätä toimintoa ei voi kumota.</translation>
+    </message>
+    <message>
+        <source>&amp;Quit</source>
+        <translation>&amp;Poistu</translation>
+    </message>
+    <message>
+        <source>Ctrl+Q</source>
+        <translation>Ctrl+Q</translation>
+    </message>
+    <message>
+        <source>Bye</source>
+        <translation>Heippa</translation>
+    </message>
+    <message>
+        <source>&amp;Website</source>
+        <translation>&amp;Nettisivu</translation>
+    </message>
+    <message>
+        <source>%1 on the Web</source>
+        <translation>%1 netissä</translation>
+    </message>
+    <message>
+        <source>Make a &amp;donation</source>
+        <translation>Tee &amp;lahjoitus</translation>
+    </message>
+    <message>
+        <source>Please support the continued development of %1</source>
+        <translation>Tue %1n jatkuvaa kehitystä</translation>
+    </message>
+    <message>
+        <source>&amp;About</source>
+        <translation>&amp;Tietoja</translation>
+    </message>
+    <message>
+        <source>Info about %1</source>
+        <translation>Tiotoja %1sta</translation>
+    </message>
+    <message>
+        <source>Search</source>
+        <translation>Etsi</translation>
+    </message>
+    <message>
+        <source>Mute volume</source>
+        <translation>Mykistä ääni</translation>
+    </message>
+    <message>
+        <source>Ctrl+M</source>
+        <translation>Ctrl+M</translation>
+    </message>
+    <message>
+        <source>&amp;Application</source>
+        <translation>&amp;Ohjelma</translation>
+    </message>
+    <message>
+        <source>&amp;Playlist</source>
+        <translation>&amp;Soittolista</translation>
+    </message>
+    <message>
+        <source>&amp;Video</source>
+        <translation>&amp;Video</translation>
+    </message>
+    <message>
+        <source>&amp;Help</source>
+        <translation>&amp;Apua</translation>
+    </message>
+    <message>
+        <source>Press %1 to raise the volume, %2 to lower it</source>
+        <translation>Paina %1ta nostaaksesi tai %2ta laskeaksesi äänen voimakkuutta</translation>
+    </message>
+    <message>
+        <source>Opening %1</source>
+        <translation>Avataan %1ta/tä</translation>
+    </message>
+    <message>
+        <source>Fatal error: %1</source>
+        <translation>Kauhia Virhe: %1</translation>
+    </message>
+    <message>
+        <source>Error: %1</source>
+        <translation>Virhe: %1</translation>
+    </message>
+    <message>
+        <source>&amp;Play</source>
+        <translation>&amp;Toista</translation>
+    </message>
+    <message>
+        <source>Resume playback</source>
+        <translation>Jatka toistoa</translation>
+    </message>
+    <message>
+        <source>Exit &amp;Full Screen</source>
+        <translation>Poistu &amp;kokoruudun tilasta</translation>
+    </message>
+    <message>
+        <source>Remaining time: %1</source>
+        <translation>Aikaa jäljellä: %1</translation>
+    </message>
+    <message>
+        <source>Volume at %1%</source>
+        <translation>Äänen tasa %1%</translation>
+    </message>
+    <message>
+        <source>Volume is muted</source>
+        <translation>Ääni on mykistetty</translation>
+    </message>
+    <message>
+        <source>Volume is unmuted</source>
+        <translation>Ääni ei ole mykistetty</translation>
+    </message>
+    <message>
+        <source>Maximum video definition set to %1</source>
+        <translation>Suurin videon laatu on rajoiteetu %1aan/ään</translation>
+    </message>
+    <message>
+        <source>Your privacy is now safe</source>
+        <translation>Yksityisyytesi on nyt turvattu</translation>
+    </message>
+</context>
+<context>
+    <name>MediaView</name>
+    <message>
+        <source>Most relevant</source>
+        <translation>Olennaisin</translation>
+    </message>
+    <message>
+        <source>Most recent</source>
+        <translation>Uusin</translation>
+    </message>
+    <message>
+        <source>Most viewed</source>
+        <translation>Katsotuin</translation>
+    </message>
+    <message>
+        <source>You can now paste the YouTube link into another application</source>
+        <translation>Voit nyt liittää YouTube linkin johonkin toiseen ohjelmaan</translation>
+    </message>
+    <message>
+        <source>You can now paste the video stream URL into another application</source>
+        <translation>Voit nyt liittää videovirran osoitteen(URL) johonkin toiseen ohjelmaan</translation>
+    </message>
+    <message>
+        <source>The link will be valid only for a limited time.</source>
+        <translation>Osoite on käytössä vain rajoitetun ajan.</translation>
+    </message>
+    <message>
+        <source>You&apos;re watching &quot;%1&quot;</source>
+        <translation>Nyt pyörii &quot;%1&quot;</translation>
+    </message>
+</context>
+<context>
+    <name>NetworkAccess</name>
+    <message>
+        <source>Network error: %1</source>
+        <translation>Verkko virhe: %1</translation>
+    </message>
+</context>
+<context>
+    <name>PrettyItemDelegate</name>
+    <message>
+        <source>%1 views</source>
+        <translation>Katsottu %1 kertaa</translation>
+    </message>
+</context>
+<context>
+    <name>SearchLineEdit</name>
+    <message>
+        <source>Search</source>
+        <translation>Etsi</translation>
+    </message>
+</context>
+<context>
+    <name>SearchView</name>
+    <message>
+        <source>Welcome to &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</source>
+        <translation>&lt;a href=&apos;%1&apos;&gt;%2en&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>Enter a keyword to start watching videos.</source>
+        <translation>Anna hakusana alkaaksesi katsella videoita.</translation>
+    </message>
+    <message>
+        <source>Watch</source>
+        <translation>Katso</translation>
+    </message>
+    <message>
+        <source>Recent keywords</source>
+        <translation>Viimeisimmät hakusanat</translation>
+    </message>
+    <message>
+        <source>A new version of %1 is available. Please &lt;a href=&apos;%2&apos;&gt;update to version %3&lt;/a&gt;</source>
+        <translation>Uusi versio %1sta on saatavilla. &lt;a href=&apos;%2&apos;&gt;Päivitäthän versioon %3&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>Make yourself comfortable</source>
+        <translation>Tee olosi kotoisaksi</translation>
+    </message>
+</context>
+<context>
+    <name>Video</name>
+    <message>
+        <source>Network error: %1 for %2</source>
+        <translation>Verkko virhe: &quot;%1&quot; &quot;%2&quot;lle</translation>
+    </message>
+</context>
+</TS>
diff --git a/locale/fr_FR.ts b/locale/fr_FR.ts
new file mode 100644 (file)
index 0000000..6ca78de
--- /dev/null
@@ -0,0 +1,447 @@
+<!DOCTYPE TS><TS>
+<defaultcodec>UTF-8</defaultcodec>
+<context>
+    <name>AboutView</name>
+    <message>
+        <source>There&apos;s life outside the browser!</source>
+        <translation>Il y a une vie après le browser !</translation>
+    </message>
+    <message>
+        <source>Version %1</source>
+        <translation>Version %1</translation>
+    </message>
+    <message>
+        <source>This is a &quot;Technology Preview&quot; release, do not expect it to be perfect.</source>
+        <translation type="obsolete">Ceci est une &quot;Preview Technologique&quot;, ne vous attendez pas à ce que ce soit parfait.</translation>
+    </message>
+    <message>
+        <source>Report bugs and send in your ideas to %1</source>
+        <translation>Rapportez les bugs et envoyez vos idées à %1</translation>
+    </message>
+    <message>
+        <source>%1 is Free Software but its development takes precious time.</source>
+        <translation>%1 est un Logiciel Libre, mais son développement prend un temps précieux.</translation>
+    </message>
+    <message>
+        <source>Please &lt;a href=&apos;%1&apos;&gt;donate via PayPal&lt;/a&gt; to support the continued development of %2.</source>
+        <translation type="obsolete">Merci &lt;a href=&apos;%1&apos;&gt;d&apos;envoyer vos dons via PayPal&lt;/a&gt; pour aider à poursuivre le développement de %2.</translation>
+    </message>
+    <message>
+        <source>Icon designed by %1.</source>
+        <translation>Icône déssinée par %1.</translation>
+    </message>
+    <message>
+        <source>Compact mode contributed by %1.</source>
+        <translation>Mode compact développé par %1.</translation>
+    </message>
+    <message>
+        <source>HTTP proxy support contributed by %1.</source>
+        <translation>Support du proxy HTTP apporté par %1.</translation>
+    </message>
+    <message>
+        <source>Windows version built by %1</source>
+        <translation type="obsolete">Version Windows faite par %1</translation>
+    </message>
+    <message>
+        <source>Translated by %1</source>
+        <translation>Traduction par %1</translation>
+    </message>
+    <message>
+        <source>Released under the &lt;a href=&apos;%1&apos;&gt;GNU General Public License&lt;/a&gt;</source>
+        <translation>Proposé sous licence &lt;a href=&apos;%1&apos;&gt;GNU General Public License&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>&amp;Close</source>
+        <translation>&amp;Fermer</translation>
+    </message>
+    <message>
+        <source>About</source>
+        <translation>A propos</translation>
+    </message>
+    <message>
+        <source>What you always wanted to know about %1 and never dared to ask</source>
+        <translation>Ce que vous avez toujours voulu savoir à propos de %1 et que vous n&apos;avez jamais osé le demander</translation>
+    </message>
+    <message>
+        <source>Please &lt;a href=&apos;%1&apos;&gt;donate&lt;/a&gt; to support the continued development of %2.</source>
+        <translation>Merci &lt;a href=&apos;%1&apos;&gt;d&apos;envoyer vos dons&lt;/a&gt; pour aider à poursuivre le développement de %2.</translation>
+    </message>
+</context>
+<context>
+    <name>ClearButton</name>
+    <message>
+        <source>Clear</source>
+        <translation>Effacer</translation>
+    </message>
+</context>
+<context>
+    <name>ListModel</name>
+    <message>
+        <source>Searching...</source>
+        <translation>Recherche...</translation>
+    </message>
+    <message>
+        <source>Show %1 More</source>
+        <translation>Afficher %1 de plus</translation>
+    </message>
+    <message>
+        <source>No videos</source>
+        <translation>Pas de vidéos</translation>
+    </message>
+    <message>
+        <source>No more videos</source>
+        <translation>Plus de vidéos</translation>
+    </message>
+</context>
+<context>
+    <name>LoadingWidget</name>
+    <message>
+        <source>Error</source>
+        <translation>Erreur</translation>
+    </message>
+</context>
+<context>
+    <name>MainWindow</name>
+    <message>
+        <source>&amp;Back</source>
+        <translation type="obsolete">&amp;Retour</translation>
+    </message>
+    <message>
+        <source>Go to the previous view</source>
+        <translation type="obsolete">Aller à la vue précédente</translation>
+    </message>
+    <message>
+        <source>&amp;Stop</source>
+        <translation>&amp;Stop</translation>
+    </message>
+    <message>
+        <source>Stop playback and go back to the search view</source>
+        <translation>Arrêter et aller à la page de recherche</translation>
+    </message>
+    <message>
+        <source>S&amp;kip</source>
+        <translation>&amp;Sauter</translation>
+    </message>
+    <message>
+        <source>Skip to the next video</source>
+        <translation>Passer à la vidéo suivante</translation>
+    </message>
+    <message>
+        <source>&amp;Pause</source>
+        <translation>&amp;Pause</translation>
+    </message>
+    <message>
+        <source>Pause playback</source>
+        <translation>Mettre en pause</translation>
+    </message>
+    <message>
+        <source>&amp;Full Screen</source>
+        <translation>&amp;Plein écran</translation>
+    </message>
+    <message>
+        <source>Go full screen</source>
+        <translation>Mettre en plein écran</translation>
+    </message>
+    <message>
+        <source>&amp;Compact mode</source>
+        <translation>Mode &amp;compact</translation>
+    </message>
+    <message>
+        <source>Hide the playlist and the toolbar</source>
+        <translation>Cacher la playlist</translation>
+    </message>
+    <message>
+        <source>&amp;YouTube</source>
+        <translation type="obsolete">&amp;Youtube</translation>
+    </message>
+    <message>
+        <source>Open the YouTube video page</source>
+        <translation type="obsolete">Ouvrir la page Youtube</translation>
+    </message>
+    <message>
+        <source>&amp;Remove</source>
+        <translation>&amp;Supprimer</translation>
+    </message>
+    <message>
+        <source>Remove the selected videos from the playlist</source>
+        <translation>Supprimer la vidéo sélectionnée de la liste de lecture</translation>
+    </message>
+    <message>
+        <source>Move &amp;Up</source>
+        <translation>Déplacer vers les &amp;haut</translation>
+    </message>
+    <message>
+        <source>Move up the selected videos in the playlist</source>
+        <translation>Déplacer la vidéo sélectionnée vers le haut dans la liste de lecture</translation>
+    </message>
+    <message>
+        <source>Move &amp;Down</source>
+        <translation>Déplacer vers les &amp;bas</translation>
+    </message>
+    <message>
+        <source>Move down the selected videos in the playlist</source>
+        <translation>Déplacer la vidéo sélectionnée vers le bas dans la liste de lecture</translation>
+    </message>
+    <message>
+        <source>&amp;Quit</source>
+        <translation>&amp;Quitter</translation>
+    </message>
+    <message>
+        <source>Ctrl+Q</source>
+        <translation>Ctrl+Q</translation>
+    </message>
+    <message>
+        <source>Bye</source>
+        <translation>Au revoir</translation>
+    </message>
+    <message>
+        <source>&amp;Website</source>
+        <translation>Site &amp;Web</translation>
+    </message>
+    <message>
+        <source>%1 on the Web</source>
+        <translation>%1 sur le Web</translation>
+    </message>
+    <message>
+        <source>&amp;Donate via PayPal</source>
+        <translation type="obsolete">Faire un &amp;don via PayPal</translation>
+    </message>
+    <message>
+        <source>Please support the continued development of %1</source>
+        <translation>Merci d&apos;aider à poursuivre le développement de %1</translation>
+    </message>
+    <message>
+        <source>&amp;About</source>
+        <translation>&amp;A propos</translation>
+    </message>
+    <message>
+        <source>Info about %1</source>
+        <translation>A propos de %1</translation>
+    </message>
+    <message>
+        <source>Search</source>
+        <translation>Chercher</translation>
+    </message>
+    <message>
+        <source>Mute volume</source>
+        <translation>Couper le son</translation>
+    </message>
+    <message>
+        <source>Ctrl+M</source>
+        <translation>Ctrl+M</translation>
+    </message>
+    <message>
+        <source>&amp;Playlist</source>
+        <translation>&amp;Liste de lecture</translation>
+    </message>
+    <message>
+        <source>&amp;Video</source>
+        <translation>&amp;Video</translation>
+    </message>
+    <message>
+        <source>&amp;Help</source>
+        <translation>&amp;Aide</translation>
+    </message>
+    <message>
+        <source>Press %1 to raise the volume, %2 to lower it</source>
+        <translation>Appuyer sur %1 pour augmenter le volume, sur %2 pour le baisser</translation>
+    </message>
+    <message>
+        <source>Opening %1</source>
+        <translation>Ouverture de %1</translation>
+    </message>
+    <message>
+        <source>Fatal error: %1</source>
+        <translation>Erreur fatale : %1</translation>
+    </message>
+    <message>
+        <source>Error: %1</source>
+        <translation>Erreur : %1</translation>
+    </message>
+    <message>
+        <source>&amp;Play</source>
+        <translation>&amp;Jouer</translation>
+    </message>
+    <message>
+        <source>Resume playback</source>
+        <translation>Relancer la lecture</translation>
+    </message>
+    <message>
+        <source>Exit &amp;Full Screen</source>
+        <translation>Quitter le &amp;plein écran</translation>
+    </message>
+    <message>
+        <source>Remaining time: %1</source>
+        <translation>Temps restant : %1</translation>
+    </message>
+    <message>
+        <source>Volume at %1%</source>
+        <translation>Volume à %1</translation>
+    </message>
+    <message>
+        <source>Volume is muted</source>
+        <translation>Volume OFF</translation>
+    </message>
+    <message>
+        <source>Volume is unmuted</source>
+        <translation>Volume ON</translation>
+    </message>
+    <message>
+        <source>High Definition video is enabled</source>
+        <translation type="obsolete">Haute définition activée</translation>
+    </message>
+    <message>
+        <source>High Definition video is not enabled</source>
+        <translation type="obsolete">Haute définition désactivée</translation>
+    </message>
+    <message>
+        <source>The current video is in High Definition</source>
+        <translation type="obsolete">La vidéo actuelle est en Haute Définition</translation>
+    </message>
+    <message>
+        <source>The current video is not in High Definition</source>
+        <translation type="obsolete">La vidéo actuelle n&apos;est pas en Haute Définition</translation>
+    </message>
+    <message>
+        <source>&amp;Clear recent keywords</source>
+        <translation>&amp;Effacer les derniers mots-clés</translation>
+    </message>
+    <message>
+        <source>&amp;Application</source>
+        <translation>&amp;Application</translation>
+    </message>
+    <message>
+        <source>Clear the search history. Cannot be undone.</source>
+        <translation>Effacer l&apos;historique de recherche. Opération irréversible.</translation>
+    </message>
+    <message>
+        <source>Your privacy is now safe</source>
+        <translation>Votre confidentialité est maintenant respectée</translation>
+    </message>
+    <message>
+        <source>Open the &amp;YouTube page</source>
+        <translation>Ouvrir la page &amp;Youtube</translation>
+    </message>
+    <message>
+        <source>Go to the YouTube video page and pause playback</source>
+        <translation>Aller à la page de la vidéo Youtube et mettre en pause</translation>
+    </message>
+    <message>
+        <source>Copy the YouTube &amp;link</source>
+        <translation>Copier le &amp;lien Youtube</translation>
+    </message>
+    <message>
+        <source>Copy the current video YouTube link to the clipboard</source>
+        <translation>Copier l&apos;adresse de la vidéo YouTube courante dans le presse-papier</translation>
+    </message>
+    <message>
+        <source>Copy the video stream &amp;URL</source>
+        <translation>Copier l&apos;adresse du flux &amp;vidéo</translation>
+    </message>
+    <message>
+        <source>Copy the current video stream URL to the clipboard</source>
+        <translation>Copier l&apos;adresse du flux vidéo courant dans le presse-papier</translation>
+    </message>
+    <message>
+        <source>Make a &amp;donation</source>
+        <translation>Faire un &amp;don</translation>
+    </message>
+    <message>
+        <source>Maximum video definition set to %1</source>
+        <translation>Résolution vidéo maximale fixée à %1</translation>
+    </message>
+</context>
+<context>
+    <name>MediaView</name>
+    <message>
+        <source>Most relevant</source>
+        <translation>Plus pertinent</translation>
+    </message>
+    <message>
+        <source>Most recent</source>
+        <translation>Plus récent</translation>
+    </message>
+    <message>
+        <source>Most viewed</source>
+        <translation>Plus vus</translation>
+    </message>
+    <message>
+        <source>You&apos;re watching &quot;%1&quot;</source>
+        <translation>Vous regardez %1</translation>
+    </message>
+    <message>
+        <source>You can now paste the YouTube link into another application</source>
+        <translation>Vous pouvez maintenant copier l&apos;adresse Youtube dans une autre application</translation>
+    </message>
+    <message>
+        <source>You can now paste the video stream URL into another application</source>
+        <translation>Vous pouvez maintenant coller l&apos;adresse du flux vidéo dans une autre application</translation>
+    </message>
+    <message>
+        <source>The link will be valid only for a limited time.</source>
+        <translation>Le lien ne sera valide que pour un temps limité.</translation>
+    </message>
+</context>
+<context>
+    <name>NetworkAccess</name>
+    <message>
+        <source>Network error: %1</source>
+        <translation>Erreur réseau : %1</translation>
+    </message>
+</context>
+<context>
+    <name>PrettyItemDelegate</name>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 vues</translation>
+    </message>
+</context>
+<context>
+    <name>SearchLineEdit</name>
+    <message>
+        <source>Search</source>
+        <translation>Chercher</translation>
+    </message>
+</context>
+<context>
+    <name>SearchView</name>
+    <message>
+        <source>Welcome to &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</source>
+        <translation>Bienvenue sur  &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
+    </message>
+    <message>
+        <source>Enter a keyword to start watching videos.</source>
+        <translation>Entre un mot-clé pour commencer à regarder des vidéos.</translation>
+    </message>
+    <message>
+        <source>Watch</source>
+        <translation>Regarder</translation>
+    </message>
+    <message>
+        <source>Recent keywords</source>
+        <translation>Derniers mots-clés</translation>
+    </message>
+    <message>
+        <source>A new version of %1 is available. Please &lt;a href=&apos;%2&apos;&gt;update to version %3&lt;/a&gt;</source>
+        <translation>Une nouvelle version de %1 est disponible. Merci &lt;a href=&apos;%2&apos;&gt;de mettre à jour pour la version %3&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>Make yourself comfortable</source>
+        <translation>Installez-vous confortablement</translation>
+    </message>
+</context>
+<context>
+    <name>SettingsView</name>
+    <message>
+        <source>Preferences</source>
+        <translation type="obsolete">Préférences</translation>
+    </message>
+</context>
+<context>
+    <name>Video</name>
+    <message>
+        <source>Network error: %1 for %2</source>
+        <translation>Erreur réseau : %1 pour %2</translation>
+    </message>
+</context>
+</TS>
diff --git a/locale/gl.ts b/locale/gl.ts
new file mode 100644 (file)
index 0000000..27319cf
--- /dev/null
@@ -0,0 +1,507 @@
+<!DOCTYPE TS><TS>
+<defaultcodec>UTF-8</defaultcodec>
+<context>
+    <name>AboutView</name>
+    <message>
+        <source>There&apos;s life outside the browser!</source>
+        <translation>Existe vida máis aló do navegador!</translation>
+    </message>
+    <message>
+        <source>Version %1</source>
+        <translation>Versión %1</translation>
+    </message>
+    <message>
+        <source>This is a &quot;Technology Preview&quot; release, do not expect it to be perfect.</source>
+        <translation type="obsolete">Esta é unha edición «Proba conceptual», non agarde que sexa perfecta.</translation>
+    </message>
+    <message>
+        <source>Report bugs and send in your ideas to %1</source>
+        <translation>Envie os seus informes de erros ou as súas ideas a %1</translation>
+    </message>
+    <message>
+        <source>%1 is Free Software but its development takes precious time.</source>
+        <translation>%1 é Software libre, mais o seu desenvolvemento leva un tempo precioso.</translation>
+    </message>
+    <message>
+        <source>Please &lt;a href=&apos;%1&apos;&gt;donate via PayPal&lt;/a&gt; to support the continued development of %2.</source>
+        <translation type="obsolete">Por favor, &lt;a href=&apos;%1&apos;&gt;faga unha donación a través de PayPal&lt;/a&gt; para apoiar o desenvolvemento continuo de %2.</translation>
+    </message>
+    <message>
+        <source>Icon designed by %1.</source>
+        <translation>Icona deseñada por %1.</translation>
+    </message>
+    <message>
+        <source>Translated by %1</source>
+        <translation>Traducido por %1</translation>
+    </message>
+    <message>
+        <source>Released under the &lt;a href=&apos;%1&apos;&gt;GNU General Public License&lt;/a&gt;</source>
+        <translation>Publicado baixo a &lt;a href=&apos;%1&apos;&gt;Licenza pública Xeral GNU&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>&amp;Close</source>
+        <translation>&amp;Pechar</translation>
+    </message>
+    <message>
+        <source>About</source>
+        <translation>Acerca de</translation>
+    </message>
+    <message>
+        <source>What you always wanted to know about %1 and never dared to ask</source>
+        <translation>O que vostede sempre quixo saber acerca de %1 e nunca se atreveu a preguntar</translation>
+    </message>
+    <message>
+        <source>Compact mode contributed by %1.</source>
+        <translation>Modo compacto aportado por %1.</translation>
+    </message>
+    <message>
+        <source>HTTP proxy support contributed by %1.</source>
+        <translation>Compatibilidade con proxy HTTP aportado por %1.</translation>
+    </message>
+    <message>
+        <source>Windows version built by %1</source>
+        <translation type="obsolete">A versión Windows foi construida por %1</translation>
+    </message>
+    <message>
+        <source>Please &lt;a href=&apos;%1&apos;&gt;donate&lt;/a&gt; to support the continued development of %2.</source>
+        <translation>Faga unha &lt;a href=&apos;%1&apos;&gt;donación&lt;/a&gt; para axudar na continuación do desenvolvemento de %2.</translation>
+    </message>
+</context>
+<context>
+    <name>ClearButton</name>
+    <message>
+        <source>Clear</source>
+        <translation>Limpar</translation>
+    </message>
+</context>
+<context>
+    <name>ListModel</name>
+    <message>
+        <source>Searching...</source>
+        <translation>Buscando...</translation>
+    </message>
+    <message>
+        <source>Show %1 More</source>
+        <translation>Mostrar %1 máis</translation>
+    </message>
+    <message>
+        <source>No videos</source>
+        <translation>Non hai vídeos</translation>
+    </message>
+    <message>
+        <source>No more videos</source>
+        <translation>Non hai máis vídeos</translation>
+    </message>
+</context>
+<context>
+    <name>LoadingWidget</name>
+    <message>
+        <source>Error</source>
+        <translation>Erro</translation>
+    </message>
+</context>
+<context>
+    <name>MainWindow</name>
+    <message>
+        <source>&amp;Back</source>
+        <translation type="obsolete">&amp;Atrás</translation>
+    </message>
+    <message>
+        <source>Alt+Left</source>
+        <translation type="obsolete">Alt+Left</translation>
+    </message>
+    <message>
+        <source>Go to the previous view</source>
+        <translation type="obsolete">Ir para a vista anterior</translation>
+    </message>
+    <message>
+        <source>&amp;Stop</source>
+        <translation>&amp;Deter</translation>
+    </message>
+    <message>
+        <source>Stop playback and go back to the search view</source>
+        <translation>Deter a reprodución e volver á vista de busca</translation>
+    </message>
+    <message>
+        <source>Esc</source>
+        <translation type="obsolete">Esc</translation>
+    </message>
+    <message>
+        <source>S&amp;kip</source>
+        <translation>&amp;Saltar</translation>
+    </message>
+    <message>
+        <source>Skip to the next video</source>
+        <translation>Saltar ao seguinte vídeo</translation>
+    </message>
+    <message>
+        <source>Ctrl+Right</source>
+        <translation type="obsolete">Ctrl+Right</translation>
+    </message>
+    <message>
+        <source>&amp;Pause</source>
+        <translation>&amp;Pausar</translation>
+    </message>
+    <message>
+        <source>Pause playback</source>
+        <translation>Pausar a reprodución</translation>
+    </message>
+    <message>
+        <source>Space</source>
+        <translation type="obsolete">Barra de espaço</translation>
+    </message>
+    <message>
+        <source>&amp;Full Screen</source>
+        <translation>&amp;Pantalla completa</translation>
+    </message>
+    <message>
+        <source>Go full screen</source>
+        <translation>Ir á pantalla completa</translation>
+    </message>
+    <message>
+        <source>Alt+Return</source>
+        <translation type="obsolete">Alt+Enter</translation>
+    </message>
+    <message>
+        <source>&amp;YouTube</source>
+        <translation type="obsolete">&amp;YouTube</translation>
+    </message>
+    <message>
+        <source>Open the YouTube video page</source>
+        <translation type="obsolete">Abrir a páxina do vídeo en YouTube</translation>
+    </message>
+    <message>
+        <source>Ctrl+Y</source>
+        <translation type="obsolete">Ctrl+Y</translation>
+    </message>
+    <message>
+        <source>&amp;Remove</source>
+        <translation>&amp;Eliminar</translation>
+    </message>
+    <message>
+        <source>Remove the selected videos from the playlist</source>
+        <translation>Eliminar os videos seleccionados da lista de reprodución</translation>
+    </message>
+    <message>
+        <source>Move &amp;Up</source>
+        <translation>Mover cara &amp;arriba</translation>
+    </message>
+    <message>
+        <source>Move up the selected videos in the playlist</source>
+        <translation>Mover cara arriba os vídeos selecionados na lista de reprodución</translation>
+    </message>
+    <message>
+        <source>Ctrl+Up</source>
+        <translation type="obsolete">Ctrl+Up</translation>
+    </message>
+    <message>
+        <source>Move &amp;Down</source>
+        <translation>Mover cara a&amp;baixo</translation>
+    </message>
+    <message>
+        <source>Move down the selected videos in the playlist</source>
+        <translation>Mover cara abaixo os vídeos selecionados na lista de reprodución</translation>
+    </message>
+    <message>
+        <source>Ctrl+Down</source>
+        <translation type="obsolete">Ctrl+Down</translation>
+    </message>
+    <message>
+        <source>&amp;Quit</source>
+        <translation>&amp;Saír</translation>
+    </message>
+    <message>
+        <source>Ctrl+Q</source>
+        <translation>Ctrl+Q</translation>
+    </message>
+    <message>
+        <source>Bye</source>
+        <translation>Deica logo</translation>
+    </message>
+    <message>
+        <source>&amp;Website</source>
+        <translation>Sitio &amp;web</translation>
+    </message>
+    <message>
+        <source>Minitube on the Web</source>
+        <translation type="obsolete">Minitube na Web</translation>
+    </message>
+    <message>
+        <source>%1 on the Web</source>
+        <translation>%1 na web</translation>
+    </message>
+    <message>
+        <source>&amp;Donate via PayPal</source>
+        <translation type="obsolete">&amp;Doar ao través de PayPal</translation>
+    </message>
+    <message>
+        <source>Please support the continued development of %1</source>
+        <translation>Por favor apoie o desenvolvemento continuo de %1</translation>
+    </message>
+    <message>
+        <source>&amp;About</source>
+        <translation>&amp;Acerca de</translation>
+    </message>
+    <message>
+        <source>Info about %1</source>
+        <translation>Información acerca de %1</translation>
+    </message>
+    <message>
+        <source>&amp;Search</source>
+        <translation type="obsolete">&amp;Pesquisar</translation>
+    </message>
+    <message>
+        <source>&amp;Application</source>
+        <translation>&amp;Aplicativo</translation>
+    </message>
+    <message>
+        <source>&amp;Playlist</source>
+        <translation>&amp;Lista de reprodución</translation>
+    </message>
+    <message>
+        <source>&amp;Video</source>
+        <translation>&amp;Vídeo</translation>
+    </message>
+    <message>
+        <source>&amp;Help</source>
+        <translation>A&amp;xuda</translation>
+    </message>
+    <message>
+        <source>Opening %1</source>
+        <translation>Abrindo %1</translation>
+    </message>
+    <message>
+        <source>&amp;Play</source>
+        <translation>&amp;Reproducir</translation>
+    </message>
+    <message>
+        <source>Resume playback</source>
+        <translation>Continuar a reprodución</translation>
+    </message>
+    <message>
+        <source>Exit &amp;Full Screen</source>
+        <translation>Saír de &amp;pantalla completa</translation>
+    </message>
+    <message>
+        <source>&amp;Compact mode</source>
+        <translation>Modo &amp;compacto</translation>
+    </message>
+    <message>
+        <source>Hide the playlist and the toolbar</source>
+        <translation>Agochar a lista de reprodución e a barra de tarefas</translation>
+    </message>
+    <message>
+        <source>Fatal error: %1</source>
+        <translation>Erro fatal: %1</translation>
+    </message>
+    <message>
+        <source>Error: %1</source>
+        <translation>Erro: %1</translation>
+    </message>
+    <message>
+        <source>Ctrl+M</source>
+        <translation>Ctrl+M</translation>
+    </message>
+    <message>
+        <source>Volume at %1%</source>
+        <translation>Volume ao %1%</translation>
+    </message>
+    <message>
+        <source>Volume is muted</source>
+        <translation>O volume está silenciado</translation>
+    </message>
+    <message>
+        <source>Volume is unmuted</source>
+        <translation>O volume non está silenciado</translation>
+    </message>
+    <message>
+        <source>Search</source>
+        <translation>Buscar</translation>
+    </message>
+    <message>
+        <source>Mute volume</source>
+        <translation>Silenciar o volume</translation>
+    </message>
+    <message>
+        <source>Press %1 to raise the volume, %2 to lower it</source>
+        <translation>Prema %1 para aumentar o volume, %2 para diminuilo</translation>
+    </message>
+    <message>
+        <source>Remaining time: %1</source>
+        <translation>Tempo restante: %1</translation>
+    </message>
+    <message>
+        <source>High Definition video is enabled</source>
+        <translation type="obsolete">O vídeo en alta definición está activado</translation>
+    </message>
+    <message>
+        <source>High Definition video is not enabled</source>
+        <translation type="obsolete">O vídeo en alta definición non está activado</translation>
+    </message>
+    <message>
+        <source>The current video is in High Definition</source>
+        <translation type="obsolete">O vídeo actual é de alta definición</translation>
+    </message>
+    <message>
+        <source>The current video is not in High Definition</source>
+        <translation type="obsolete">O vídeo actual non é de alta definición</translation>
+    </message>
+    <message>
+        <source>&amp;Clear recent keywords</source>
+        <translation>Limpar palabras &amp;clave recentes</translation>
+    </message>
+    <message>
+        <source>Clear the search history. Cannot be undone.</source>
+        <translation>Limpar o historial de buscas. Non se pode desfacer.</translation>
+    </message>
+    <message>
+        <source>Your privacy is now safe</source>
+        <translation>A súa intimidade agora está segura</translation>
+    </message>
+    <message>
+        <source>Open the &amp;YouTube page</source>
+        <translation>Abrir a páxina de &amp;YouTube</translation>
+    </message>
+    <message>
+        <source>Go to the YouTube video page and pause playback</source>
+        <translation>Ir á páxina de vídeos YouTube e pausar a reprodución</translation>
+    </message>
+    <message>
+        <source>Copy the YouTube &amp;link</source>
+        <translation>Copiar a &amp;ligazón de YouTube</translation>
+    </message>
+    <message>
+        <source>Copy the current video YouTube link to the clipboard</source>
+        <translation>Copiar a ligazón actual de vídeo de YouTube no portaretallos</translation>
+    </message>
+    <message>
+        <source>Copy the video stream &amp;URL</source>
+        <translation>Copiar o &amp;URL de fluxo de vídeo</translation>
+    </message>
+    <message>
+        <source>Copy the current video stream URL to the clipboard</source>
+        <translation>Copiar o URL actual do fluxo de vídeo no portaretallos</translation>
+    </message>
+    <message>
+        <source>Make a &amp;donation</source>
+        <translation>Facer unha &amp;donación</translation>
+    </message>
+    <message>
+        <source>Maximum video definition set to %1</source>
+        <translation>Resolución máxima de vídeo fixada en %1</translation>
+    </message>
+</context>
+<context>
+    <name>MediaView</name>
+    <message>
+        <source>Most relevant</source>
+        <translation>Máis destacados</translation>
+    </message>
+    <message>
+        <source>Most recent</source>
+        <translation>Máis recentes</translation>
+    </message>
+    <message>
+        <source>Most viewed</source>
+        <translation>Máis vistos</translation>
+    </message>
+    <message>
+        <source>You&apos;re watching &quot;%1&quot;</source>
+        <translation>Está a ver &quot;%1&quot;</translation>
+    </message>
+    <message>
+        <source>You can now paste the YouTube link into another application</source>
+        <translation>Agora pode pegar a ligazón de YouTube noutro aplicativo</translation>
+    </message>
+    <message>
+        <source>You can now paste the video stream URL into another application</source>
+        <translation>Agora pode pegar o URL do fluxo de vídeo noutro aplicativo</translation>
+    </message>
+    <message>
+        <source>The link will be valid only for a limited time.</source>
+        <translation>A ligazón ten validez só por un tempo limitado.</translation>
+    </message>
+</context>
+<context>
+    <name>NetworkAccess</name>
+    <message>
+        <source>Network error: %1</source>
+        <translation>Erro na rede: %1</translation>
+    </message>
+</context>
+<context>
+    <name>PrettyItemDelegate</name>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 visualizacións</translation>
+    </message>
+</context>
+<context>
+    <name>SearchLineEdit</name>
+    <message>
+        <source>Search</source>
+        <translation>Buscar</translation>
+    </message>
+</context>
+<context>
+    <name>SearchView</name>
+    <message>
+        <source>Welcome to &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</source>
+        <translation>Benvindo ao &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
+    </message>
+    <message>
+        <source>Enter a keyword to start watching videos.</source>
+        <translation>Escriba unha palabra clave para comezar a ver os vídeos.</translation>
+    </message>
+    <message>
+        <source>Watch</source>
+        <translation>Ver</translation>
+    </message>
+    <message>
+        <source>Recent keywords</source>
+        <translation>Palabra clave recente</translation>
+    </message>
+    <message>
+        <source>A new version of %1 is available. Please &lt;a href=&apos;%2&apos;&gt;update to version %3&lt;/a&gt;</source>
+        <translation>Está dispoñible unha nova versión de %1. Por favor, &lt;a href=&apos;%2&apos;&gt;actualice cara a versión %3&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>Make yourself comfortable</source>
+        <translation>Síntase cómodo</translation>
+    </message>
+</context>
+<context>
+    <name>SettingsView</name>
+    <message>
+        <source>Preferences</source>
+        <translation type="obsolete">Preferencias</translation>
+    </message>
+    <message>
+        <source>&amp;Video options</source>
+        <translation type="obsolete">Opções de &amp;vídeo</translation>
+    </message>
+    <message>
+        <source>Use high quality video when available</source>
+        <translation type="obsolete">Utilizar vídeo de alta qualidade quando estiver disponível</translation>
+    </message>
+    <message>
+        <source>&amp;Saved recent keywords</source>
+        <translation type="obsolete">Palavras-chave &amp;salvas recentemente</translation>
+    </message>
+    <message>
+        <source>&amp;Clear recent keywords</source>
+        <translation type="obsolete">Palavras-chave &amp;limpas recentemente</translation>
+    </message>
+    <message>
+        <source>&amp;Close</source>
+        <translation type="obsolete">&amp;Fechar</translation>
+    </message>
+</context>
+<context>
+    <name>Video</name>
+    <message>
+        <source>Network error: %1 for %2</source>
+        <translation>Erro na rede: %1 por %2</translation>
+    </message>
+</context>
+</TS>
diff --git a/locale/he_IL.ts b/locale/he_IL.ts
new file mode 100644 (file)
index 0000000..3474648
--- /dev/null
@@ -0,0 +1,465 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.0" language="he_IL">
+<defaultcodec>UTF-8</defaultcodec>
+<context>
+    <name>AboutView</name>
+    <message>
+        <source>There&apos;s life outside the browser!</source>
+        <translation>יש חיים מחוץ לדפדפן!</translation>
+    </message>
+    <message>
+        <source>Version %1</source>
+        <translation>גרסה %1</translation>
+    </message>
+    <message>
+        <source>This is a &quot;Technology Preview&quot; release, do not expect it to be perfect.</source>
+        <translation>הפצה זו הנה &quot;הצגה טכנולוגית מקדימה&quot;, אל תצפו ממנה להיות מושלמת.</translation>
+    </message>
+    <message>
+        <source>Report bugs and send in your ideas to %1</source>
+        <translation>יש לדווח על באגים ורעיונות אל %1</translation>
+    </message>
+    <message>
+        <source>%1 is Free Software but its development takes precious time.</source>
+        <translation>%1 הינה תוכנה חופשית אך פיתוחה לוקח המון זמן יקר.</translation>
+    </message>
+    <message>
+        <source>Please &lt;a href=&apos;%1&apos;&gt;donate via PayPal&lt;/a&gt; to support the continued development of %2.</source>
+        <translation>אנא &lt;a href=&apos;%1&apos;&gt;תרמו באמצעות PayPal&lt;/a&gt; כדי לתמוך בהמשך הפיתוח של %2.</translation>
+    </message>
+    <message>
+        <source>Icon designed by %1.</source>
+        <translation>הסמל עוצב על ידי %1.</translation>
+    </message>
+    <message>
+        <source>Compact mode contributed by %1.</source>
+        <translation>המצב החסכוני נתרם על ידי %1.</translation>
+    </message>
+    <message>
+        <source>Translated by %1</source>
+        <translation>תורגם על ידי %1.</translation>
+    </message>
+    <message>
+        <source>Released under the </source>
+        <translation>שוחרר תחת תנאי</translation>
+    </message>
+    <message>
+        <source></source>
+        <translation>&amp;סגירה</translation>
+    </message>
+    <message>
+        <source>About</source>
+        <translation>על אודות</translation>
+    </message>
+    <message>
+        <source>What you always wanted to know about %1 and never dared to ask</source>
+        <translation>מה שתמיד רצית לדעת אודות %1 ולא העזת לשאול</translation>
+    </message>
+    <message>
+        <source>Released under the &lt;a href=&apos;%1&apos;&gt;GNU General Public License&lt;/a&gt;</source>
+        <translation>התוכנה שוחררה תחת תנאי &lt;a href=&apos;%1&apos;&gt;הרישיון הציבורי הכללי של GNU&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>&amp;Close</source>
+        <translation>&amp;סגירה</translation>
+    </message>
+    <message>
+        <source>HTTP proxy support contributed by %1.</source>
+        <translation>התמיכה במתווך HTTP נתרמה על ידי %1.</translation>
+    </message>
+    <message>
+        <source>Windows version built by %1</source>
+        <translation>הגרסה ל־Windows נבנתה על ידי %1</translation>
+    </message>
+    <message>
+        <source>Please &lt;a href=&apos;%1&apos;&gt;donate&lt;/a&gt; to support the continued development of %2.</source>
+        <translation>אנא &lt;a href=&apos;%1&apos;&gt;תרמו&lt;/a&gt; כדי לתמוך בהמשך הפיתוח של %2.</translation>
+    </message>
+</context>
+<context>
+    <name>ClearButton</name>
+    <message>
+        <source>Clear</source>
+        <translation>מחיקה</translation>
+    </message>
+</context>
+<context>
+    <name>ListModel</name>
+    <message>
+        <source>Searching...</source>
+        <translation>מחפש...</translation>
+    </message>
+    <message>
+        <source>Show %1 More</source>
+        <translation>הצגת %1 נוספים</translation>
+    </message>
+    <message>
+        <source>No videos</source>
+        <translation>אין סרטונים</translation>
+    </message>
+    <message>
+        <source>No more videos</source>
+        <translation>אין עוד סרטונים</translation>
+    </message>
+</context>
+<context>
+    <name>LoadingWidget</name>
+    <message>
+        <source>Error</source>
+        <translation>שגיאה</translation>
+    </message>
+</context>
+<context>
+    <name>MainWindow</name>
+    <message>
+        <source></source>
+        <translation>&amp;חזרה</translation>
+    </message>
+    <message>
+        <source>Go to the previous view</source>
+        <translation>מעבר אל התצוגה הקודמת</translation>
+    </message>
+    <message>
+        <source>&amp;Stop</source>
+        <translation>&amp;עצירה</translation>
+    </message>
+    <message>
+        <source>Stop playback and go back to the search view</source>
+        <translation>עצירת הנגינה וחזרה אל תצוגת החיפוש</translation>
+    </message>
+    <message>
+        <source>S&amp;kip</source>
+        <translation>&amp;דילוג</translation>
+    </message>
+    <message>
+        <source>Skip to the next video</source>
+        <translation>דילוג אל הסרטון הבא</translation>
+    </message>
+    <message>
+        <source>&amp;Pause</source>
+        <translation>ה&amp;שהיה</translation>
+    </message>
+    <message>
+        <source>Pause playback</source>
+        <translation>השהיית הנגינה</translation>
+    </message>
+    <message>
+        <source>&amp;Full Screen</source>
+        <translation>&amp;מסך מלא</translation>
+    </message>
+    <message>
+        <source>Go full screen</source>
+        <translation>מעבר למסך מלא</translation>
+    </message>
+    <message>
+        <source>&amp;Compact mode</source>
+        <translation>מצב &amp;חסכוני</translation>
+    </message>
+    <message>
+        <source>Hide the playlist and the toolbar</source>
+        <translation>הסתרת רשימת ההשמעה וסרגל הכלים</translation>
+    </message>
+    <message>
+        <source>&amp;YouTube</source>
+        <translation>&amp;YouTube</translation>
+    </message>
+    <message>
+        <source>Open the YouTube video page</source>
+        <translation>פתיחת עמוד הווידאו ב־YouTube</translation>
+    </message>
+    <message>
+        <source>&amp;Remove</source>
+        <translation>ה&amp;סרה</translation>
+    </message>
+    <message>
+        <source>Remove the selected videos from the playlist</source>
+        <translation>הסרת הסרטונים הנבחרים מרשימת ההשמעה</translation>
+    </message>
+    <message>
+        <source>Move &amp;Up</source>
+        <translation>הזזה מ&amp;מעלה</translation>
+    </message>
+    <message>
+        <source>Move up the selected videos in the playlist</source>
+        <translation>הזזת הסרטונים הנבחרים במעלה רשימת ההשמעה</translation>
+    </message>
+    <message>
+        <source>Move &amp;Down</source>
+        <translation>הזזה מ&amp;טה</translation>
+    </message>
+    <message>
+        <source>Move down the selected videos in the playlist</source>
+        <translation>הזזת הסרטונים הנבחרים במורד רשימת ההשמעה</translation>
+    </message>
+    <message>
+        <source>&amp;Quit</source>
+        <translation>י&amp;ציאה</translation>
+    </message>
+    <message>
+        <source>Ctrl+Q</source>
+        <translation>Ctrl+Q</translation>
+    </message>
+    <message>
+        <source>Bye</source>
+        <translation>להתראות</translation>
+    </message>
+    <message>
+        <source>&amp;Website</source>
+        <translation>&amp;אתר האינטרנט</translation>
+    </message>
+    <message>
+        <source>%1 on the Web</source>
+        <translation>%1 באינטרנט</translation>
+    </message>
+    <message>
+        <source>&amp;Donate via PayPal</source>
+        <translation>&amp;תרומה באמצעות PayPal</translation>
+    </message>
+    <message>
+        <source>Please support the continued development of %1</source>
+        <translation>אנא תמכו בהמשך הפיתוח של %1</translation>
+    </message>
+    <message>
+        <source>&amp;About</source>
+        <translation>&amp;על אודות</translation>
+    </message>
+    <message>
+        <source>Info about %1</source>
+        <translation>מידע אודות %1</translation>
+    </message>
+    <message>
+        <source>&amp;Search</source>
+        <translation>&amp;חיפוש</translation>
+    </message>
+    <message>
+        <source>&amp;Application</source>
+        <translation>&amp;יישום</translation>
+    </message>
+    <message>
+        <source>&amp;Playlist</source>
+        <translation>&amp;רשימת השמעה</translation>
+    </message>
+    <message>
+        <source>&amp;Video</source>
+        <translation>&amp;וידאו</translation>
+    </message>
+    <message>
+        <source>&amp;Help</source>
+        <translation>&amp;עזרה</translation>
+    </message>
+    <message>
+        <source>Opening %1</source>
+        <translation>%1 נפתח</translation>
+    </message>
+    <message>
+        <source>Fatal error: %1</source>
+        <translation>שגיאה מכרעת: %1</translation>
+    </message>
+    <message>
+        <source>Error: %1</source>
+        <translation>שגיאה: %1</translation>
+    </message>
+    <message>
+        <source>&amp;Play</source>
+        <translation>&amp;נגינה</translation>
+    </message>
+    <message>
+        <source>Resume playback</source>
+        <translation>המשך הנגינה</translation>
+    </message>
+    <message>
+        <source>Exit &amp;Full Screen</source>
+        <translation>יציאה ממ&amp;סך מלא</translation>
+    </message>
+    <message>
+        <source>&amp;Back</source>
+        <translation>&amp;חזרה</translation>
+    </message>
+    <message>
+        <source>Search</source>
+        <translation>חיפוש</translation>
+    </message>
+    <message>
+        <source>Mute volume</source>
+        <translation>השתקת השמע</translation>
+    </message>
+    <message>
+        <source>Ctrl+M</source>
+        <translation>Ctrl+M</translation>
+    </message>
+    <message>
+        <source>Press %1 to raise the volume, %2 to lower it</source>
+        <translation>ניתן ללחוץ על %1 כדי להגביר את עצמת השמע, %2 כדי להנמיך אותה</translation>
+    </message>
+    <message>
+        <source>Remaining time: %1</source>
+        <translation>הזמן הנותר: %1</translation>
+    </message>
+    <message>
+        <source>Volume at %1%</source>
+        <translation>עצמת השמע היא %1%</translation>
+    </message>
+    <message>
+        <source>Volume is muted</source>
+        <translation>השמע מושתק</translation>
+    </message>
+    <message>
+        <source>Volume is unmuted</source>
+        <translation>השמע אינו מושתק</translation>
+    </message>
+    <message>
+        <source>High Definition video is enabled</source>
+        <translation>האפשרות לצפיה באיכות גבוהה פעילה</translation>
+    </message>
+    <message>
+        <source>High Definition video is not enabled</source>
+        <translation>האפשרות לצפיה באיכות גבוהה אינה פעילה</translation>
+    </message>
+    <message>
+        <source>The current video is in High Definition</source>
+        <translation>סרטון הווידאו הנוכחי הנו באיכות גבוהה (HD)</translation>
+    </message>
+    <message>
+        <source>The current video is not in High Definition</source>
+        <translation>סרטון הווידאו הנוכחי אינו באיכות גבוהה (HD)</translation>
+    </message>
+    <message>
+        <source>&amp;Clear recent keywords</source>
+        <translation>מחיקת &amp;מילות המפתח האחרונות</translation>
+    </message>
+    <message>
+        <source>Clear the search history. Cannot be undone.</source>
+        <translation>מחיקת היסטוריית החיפוש. לא ניתן לבטל</translation>
+    </message>
+    <message>
+        <source>Your privacy is now safe</source>
+        <translation>פרטיותך מוגנת כעת.</translation>
+    </message>
+    <message>
+        <source>Open the &amp;YouTube page</source>
+        <translation>פthe &amp;תיחת העמוד ב־YouTube</translation>
+    </message>
+    <message>
+        <source>Go to the YouTube video page and pause playback</source>
+        <translation>מעבר לעמוד הווידאו ב־YouTube והשהיית הנגינה.</translation>
+    </message>
+    <message>
+        <source>Copy the YouTube &amp;link</source>
+        <translation>העתקת ה&amp;קישור ל־YouTube</translation>
+    </message>
+    <message>
+        <source>Copy the current video YouTube link to the clipboard</source>
+        <translation>העתקת הקישור אל הווידאו הנוכחי אל לוח הגזירים</translation>
+    </message>
+    <message>
+        <source>Copy the video stream &amp;URL</source>
+        <translation>העתקת כתובת &amp;תזרים הווידאו</translation>
+    </message>
+    <message>
+        <source>Copy the current video stream URL to the clipboard</source>
+        <translation>העתקת כתובת תזרים הווידאו אל לוח הגזירים</translation>
+    </message>
+    <message>
+        <source>Make a &amp;donation</source>
+        <translation>&amp;מתן תרומה</translation>
+    </message>
+    <message>
+        <source>Maximum video definition set to %1</source>
+        <translation>איכות הנגינה המירבית מוגדרת ל־%1</translation>
+    </message>
+</context>
+<context>
+    <name>MediaView</name>
+    <message>
+        <source>Most relevant</source>
+        <translation>הרלוונטיים ביותר</translation>
+    </message>
+    <message>
+        <source>Most recent</source>
+        <translation>העדכניים ביותר</translation>
+    </message>
+    <message>
+        <source>Most viewed</source>
+        <translation>הנצפים ביותר</translation>
+    </message>
+    <message>
+        <source>You&apos;re watching &quot;%1&quot;</source>
+        <translation>הנכם צופים ב־&quot;%1&quot;</translation>
+    </message>
+    <message>
+        <source>You can now paste the YouTube link into another application</source>
+        <translation>כעת ניתן להדביק את קישור ה־YouTube שלכם ליישום אחר</translation>
+    </message>
+    <message>
+        <source>You can now paste the video stream URL into another application</source>
+        <translation>כעת ניתן להדביק את כתובת תזרים הווידאו ליישום אחר</translation>
+    </message>
+    <message>
+        <source>The link will be valid only for a limited time.</source>
+        <translation>הקישור יהיה תקף לזמן מוגבל בלבד.</translation>
+    </message>
+</context>
+<context>
+    <name>NetworkAccess</name>
+    <message>
+        <source>Network error: %1</source>
+        <translation>שגיאת רשת: %1</translation>
+    </message>
+</context>
+<context>
+    <name>PrettyItemDelegate</name>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 צפיות</translation>
+    </message>
+</context>
+<context>
+    <name>SearchLineEdit</name>
+    <message>
+        <source>Search</source>
+        <translation>חיפוש</translation>
+    </message>
+</context>
+<context>
+    <name>SearchView</name>
+    <message>
+        <source>Welcome to &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</source>
+        <translation>ברוכים הבאים אל &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
+    </message>
+    <message>
+        <source>Enter a keyword to start watching videos.</source>
+        <translation>הזינו מילת מפתח כדי להתחיל לצפות בסרטונים.</translation>
+    </message>
+    <message>
+        <source>Watch</source>
+        <translation>צפיה</translation>
+    </message>
+    <message>
+        <source>Recent keywords</source>
+        <translation>מילות מפתח אחרונות</translation>
+    </message>
+    <message>
+        <source>A new version of %1 is available. Please &lt;a href=&apos;%2&apos;&gt;update to version %3&lt;/a&gt;</source>
+        <translation>גרסה חדשה של %1 זמינה להורדה. אנא &lt;a href=&apos;%2&apos;&gt;עדכנו לגרסה %3&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>Make yourself comfortable</source>
+        <translation>שבו בניחותא</translation>
+    </message>
+</context>
+<context>
+    <name>SettingsView</name>
+    <message>
+        <source>Preferences</source>
+        <translation>העדפות</translation>
+    </message>
+</context>
+<context>
+    <name>Video</name>
+    <message>
+        <source>Network error: %1 for %2</source>
+        <translation>שגיאת רשת: %1 עבור %2</translation>
+    </message>
+</context>
+</TS>
diff --git a/locale/hr_HR.ts b/locale/hr_HR.ts
new file mode 100644 (file)
index 0000000..a53d5d2
--- /dev/null
@@ -0,0 +1,449 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.0" language="hr_HR">
+<defaultcodec>UTF-8</defaultcodec>
+<context>
+    <name>AboutView</name>
+    <message>
+        <source>There&apos;s life outside the browser!</source>
+        <translation>Postoji život izvan preglednika!</translation>
+    </message>
+    <message>
+        <source>Version %1</source>
+        <translation>Verzija %1</translation>
+    </message>
+    <message>
+        <source>This is a &quot;Technology Preview&quot; release, do not expect it to be perfect.</source>
+        <translation type="obsolete">Ovo je &quot;Technology Preview&quot;izdanje, te ne očekujte da bude savršeno.</translation>
+    </message>
+    <message>
+        <source>Report bugs and send in your ideas to %1</source>
+        <translation>Prijavite sve bugove i posaljite svoje ideje na %1</translation>
+    </message>
+    <message>
+        <source>%1 is Free Software but its development takes precious time.</source>
+        <translation>%1 je besplatan program ali njegov razvoj uzima dragocjeno vrijeme.</translation>
+    </message>
+    <message>
+        <source>Please &lt;a href=&apos;%1&apos;&gt;donate via PayPal&lt;/a&gt; to support the continued development of %2.</source>
+        <translation type="obsolete">Molimo &lt;a href=&apos;%1 &apos;&gt;donirajte preko PayPal-a&lt;/a&gt; za podršku i kontinuiran razvoj %2.</translation>
+    </message>
+    <message>
+        <source>Icon designed by %1.</source>
+        <translation>Ikonu je dizajnirao %1.</translation>
+    </message>
+    <message>
+        <source>Compact mode contributed by %1.</source>
+        <translation>Kompaktni način pridonio je %1.</translation>
+    </message>
+    <message>
+        <source>HTTP proxy support contributed by %1.</source>
+        <translation>HTTP proxy podršku je pridonio %1.</translation>
+    </message>
+    <message>
+        <source>Windows version built by %1</source>
+        <translation type="obsolete">Windows verziju uradio je %1</translation>
+    </message>
+    <message>
+        <source>Translated by %1</source>
+        <translation>Preveo je %1</translation>
+    </message>
+    <message>
+        <source>Released under the &lt;a href=&apos;%1&apos;&gt;GNU General Public License&lt;/a&gt;</source>
+        <translation>Objavljeno je pod &lt;a href=&apos;%1&apos;&gt;GNU General Public License&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>&amp;Close</source>
+        <translation>&amp;Zatvori</translation>
+    </message>
+    <message>
+        <source>About</source>
+        <translation>O</translation>
+    </message>
+    <message>
+        <source>What you always wanted to know about %1 and never dared to ask</source>
+        <translation>Što ste oduvijek željeli znati o %1 a nikad se niste usudili pitati</translation>
+    </message>
+    <message>
+        <source>Please &lt;a href=&apos;%1&apos;&gt;donate&lt;/a&gt; to support the continued development of %2.</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>ClearButton</name>
+    <message>
+        <source>Clear</source>
+        <translation>Očisti</translation>
+    </message>
+</context>
+<context>
+    <name>ListModel</name>
+    <message>
+        <source>Searching...</source>
+        <translation>Pretraživanje...</translation>
+    </message>
+    <message>
+        <source>Show %1 More</source>
+        <translation>Pokaži još jedan %1</translation>
+    </message>
+    <message>
+        <source>No videos</source>
+        <translation>Nema videa</translation>
+    </message>
+    <message>
+        <source>No more videos</source>
+        <translation>Nema više videa</translation>
+    </message>
+</context>
+<context>
+    <name>LoadingWidget</name>
+    <message>
+        <source>Error</source>
+        <translation>Greška</translation>
+    </message>
+</context>
+<context>
+    <name>MainWindow</name>
+    <message>
+        <source>&amp;Back</source>
+        <translation type="obsolete">&amp;Natrag</translation>
+    </message>
+    <message>
+        <source>Go to the previous view</source>
+        <translation type="obsolete">Idi na prethodni prikaz</translation>
+    </message>
+    <message>
+        <source>&amp;Stop</source>
+        <translation>&amp;Stani</translation>
+    </message>
+    <message>
+        <source>Stop playback and go back to the search view</source>
+        <translation>Zaustavi playback i vrati se na pretraživanje</translation>
+    </message>
+    <message>
+        <source>S&amp;kip</source>
+        <translation>Preskoči</translation>
+    </message>
+    <message>
+        <source>Skip to the next video</source>
+        <translation>Preskoči na sljedeći video</translation>
+    </message>
+    <message>
+        <source>&amp;Pause</source>
+        <translation>&amp;Pauza</translation>
+    </message>
+    <message>
+        <source>Pause playback</source>
+        <translation>Pauziraj playback</translation>
+    </message>
+    <message>
+        <source>&amp;Full Screen</source>
+        <translation>&amp;Cijeli ekran</translation>
+    </message>
+    <message>
+        <source>Go full screen</source>
+        <translation>Gledaj preko cijelog ekrana</translation>
+    </message>
+    <message>
+        <source>&amp;Compact mode</source>
+        <translation>&amp;Kompaktan način</translation>
+    </message>
+    <message>
+        <source>Hide the playlist and the toolbar</source>
+        <translation>Sakrij naslove i alatnu traku</translation>
+    </message>
+    <message>
+        <source>&amp;YouTube</source>
+        <translation type="obsolete">&amp;YouTube</translation>
+    </message>
+    <message>
+        <source>Open the YouTube video page</source>
+        <translation type="obsolete">Otvori YouTube video stranicu</translation>
+    </message>
+    <message>
+        <source>&amp;Remove</source>
+        <translation>&amp;Ukloni</translation>
+    </message>
+    <message>
+        <source>Remove the selected videos from the playlist</source>
+        <translation>Ukloni odabrane snimke sa playliste</translation>
+    </message>
+    <message>
+        <source>Move &amp;Up</source>
+        <translation>Premjesti gore</translation>
+    </message>
+    <message>
+        <source>Move up the selected videos in the playlist</source>
+        <translation>Premjesti gore selektirane snimke u playlistu</translation>
+    </message>
+    <message>
+        <source>Move &amp;Down</source>
+        <translation>Premjesti &amp;Dolje</translation>
+    </message>
+    <message>
+        <source>Move down the selected videos in the playlist</source>
+        <translation>Premjesti dolje selektirane snimke u playlistu</translation>
+    </message>
+    <message>
+        <source>&amp;Quit</source>
+        <translation>&amp;Odustani</translation>
+    </message>
+    <message>
+        <source>Ctrl+Q</source>
+        <translation>Ctrl+Q</translation>
+    </message>
+    <message>
+        <source>Bye</source>
+        <translation>Pozdrav</translation>
+    </message>
+    <message>
+        <source>&amp;Website</source>
+        <translation>&amp;Web stranica</translation>
+    </message>
+    <message>
+        <source>%1 on the Web</source>
+        <translation>%1 na internetu</translation>
+    </message>
+    <message>
+        <source>&amp;Donate via PayPal</source>
+        <translation type="obsolete">&amp;Donirajte preko PayPal-a</translation>
+    </message>
+    <message>
+        <source>Please support the continued development of %1</source>
+        <translation>Molimo potporu u daljnjem razvoju %1</translation>
+    </message>
+    <message>
+        <source>&amp;About</source>
+        <translation>&amp;O</translation>
+    </message>
+    <message>
+        <source>Info about %1</source>
+        <translation>Info o %1</translation>
+    </message>
+    <message>
+        <source>Search</source>
+        <translation>Pretraga</translation>
+    </message>
+    <message>
+        <source>Mute volume</source>
+        <translation>Isključi zvuk</translation>
+    </message>
+    <message>
+        <source>Ctrl+M</source>
+        <translation>Ctrl+M</translation>
+    </message>
+    <message>
+        <source>&amp;Playlist</source>
+        <translation>&amp;Playlista</translation>
+    </message>
+    <message>
+        <source>&amp;Video</source>
+        <translation>&amp;Video</translation>
+    </message>
+    <message>
+        <source>&amp;Help</source>
+        <translation>&amp;Pomoć</translation>
+    </message>
+    <message>
+        <source>Press %1 to raise the volume, %2 to lower it</source>
+        <translation>Pritisnite %1 za povećati zvuk, %2 za smanjiti zvuk</translation>
+    </message>
+    <message>
+        <source>Opening %1</source>
+        <translation>Otvaranje %1</translation>
+    </message>
+    <message>
+        <source>Fatal error: %1</source>
+        <translation>Fatalna greška: %1</translation>
+    </message>
+    <message>
+        <source>Error: %1</source>
+        <translation>Greška: %1</translation>
+    </message>
+    <message>
+        <source>&amp;Play</source>
+        <translation>&amp;Pusti</translation>
+    </message>
+    <message>
+        <source>Resume playback</source>
+        <translation>Nastavi playback</translation>
+    </message>
+    <message>
+        <source>Exit &amp;Full Screen</source>
+        <translation>Izlaz&amp;Cijeli ekran</translation>
+    </message>
+    <message>
+        <source>Remaining time: %1</source>
+        <translation>Preostalo vrijeme: %1</translation>
+    </message>
+    <message>
+        <source>Volume at %1%</source>
+        <translation>Jačina zvuka je na %1%</translation>
+    </message>
+    <message>
+        <source>Volume is muted</source>
+        <translation>Zvuk je isključen</translation>
+    </message>
+    <message>
+        <source>Volume is unmuted</source>
+        <translation>Zvuk je uključen</translation>
+    </message>
+    <message>
+        <source>High Definition video is enabled</source>
+        <translation type="obsolete">Visoka rezolucija videa je uključena</translation>
+    </message>
+    <message>
+        <source>High Definition video is not enabled</source>
+        <translation type="obsolete">Visoka rezolucija videa je isključena</translation>
+    </message>
+    <message>
+        <source>The current video is in High Definition</source>
+        <translation type="obsolete">Odabrani video je u visokoj rezoluciji</translation>
+    </message>
+    <message>
+        <source>The current video is not in High Definition</source>
+        <translation type="obsolete">Odabrani video nije u visokoj rezoluciji</translation>
+    </message>
+    <message>
+        <source>&amp;Clear recent keywords</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>&amp;Application</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Clear the search history. Cannot be undone.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Your privacy is now safe</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Open the &amp;YouTube page</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Go to the YouTube video page and pause playback</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Copy the YouTube &amp;link</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Copy the current video YouTube link to the clipboard</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Copy the video stream &amp;URL</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Copy the current video stream URL to the clipboard</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Make a &amp;donation</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Maximum video definition set to %1</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>MediaView</name>
+    <message>
+        <source>Most relevant</source>
+        <translation>Najznačajniji</translation>
+    </message>
+    <message>
+        <source>Most recent</source>
+        <translation>Najnoviji</translation>
+    </message>
+    <message>
+        <source>Most viewed</source>
+        <translation>Najgledaniji</translation>
+    </message>
+    <message>
+        <source>You&apos;re watching &quot;%1&quot;</source>
+        <translation>Gledate &quot;%1&quot;</translation>
+    </message>
+    <message>
+        <source>You can now paste the YouTube link into another application</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>You can now paste the video stream URL into another application</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>The link will be valid only for a limited time.</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>NetworkAccess</name>
+    <message>
+        <source>Network error: %1</source>
+        <translation>Greška u mreži:%1</translation>
+    </message>
+</context>
+<context>
+    <name>PrettyItemDelegate</name>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 gledano</translation>
+    </message>
+</context>
+<context>
+    <name>SearchLineEdit</name>
+    <message>
+        <source>Search</source>
+        <translation>Pretraga</translation>
+    </message>
+</context>
+<context>
+    <name>SearchView</name>
+    <message>
+        <source>Welcome to &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</source>
+        <translation>Dobro došli &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>Enter a keyword to start watching videos.</source>
+        <translation>Unesite ključnu riječ za početak gledanja videa.</translation>
+    </message>
+    <message>
+        <source>Watch</source>
+        <translation>Gledaj</translation>
+    </message>
+    <message>
+        <source>Recent keywords</source>
+        <translation>Nedavno gledano</translation>
+    </message>
+    <message>
+        <source>A new version of %1 is available. Please &lt;a href=&apos;%2&apos;&gt;update to version %3&lt;/a&gt;</source>
+        <translation>Nova verzija %1 je dostupna. Molimo &lt;a href=&apos;%2&apos;&gt;nadogradite verziju %3&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>Make yourself comfortable</source>
+        <translation>Osjećajte se udobno</translation>
+    </message>
+</context>
+<context>
+    <name>SettingsView</name>
+    <message>
+        <source>Preferences</source>
+        <translation type="obsolete">Postavke</translation>
+    </message>
+</context>
+<context>
+    <name>Video</name>
+    <message>
+        <source>Network error: %1 for %2</source>
+        <translation>Greška u mreži:%1 za %2</translation>
+    </message>
+</context>
+</TS>
diff --git a/locale/hu_HU.ts b/locale/hu_HU.ts
new file mode 100644 (file)
index 0000000..8c30751
--- /dev/null
@@ -0,0 +1,449 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.0" language="hu_HU">
+<defaultcodec>UTF-8</defaultcodec>
+<context>
+    <name>AboutView</name>
+    <message>
+        <source>There&apos;s life outside the browser!</source>
+        <translation>Van élet a böngészőn kívül!</translation>
+    </message>
+    <message>
+        <source>Version %1</source>
+        <translation>%1 verzió</translation>
+    </message>
+    <message>
+        <source>This is a &quot;Technology Preview&quot; release, do not expect it to be perfect.</source>
+        <translation type="obsolete">Ez egy bemutató verzió, nem biztos, hogy tökéletesen működik.</translation>
+    </message>
+    <message>
+        <source>Report bugs and send in your ideas to %1</source>
+        <translation>Hibabejelentés, ötletek: %1</translation>
+    </message>
+    <message>
+        <source>%1 is Free Software but its development takes precious time.</source>
+        <translation>A %1 szabad szoftver, de a fejlesztés időbe telik és az idő pénz.</translation>
+    </message>
+    <message>
+        <source>Please &lt;a href=&apos;%1&apos;&gt;donate via PayPal&lt;/a&gt; to support the continued development of %2.</source>
+        <translation type="obsolete">Kérlek &lt;a href=&apos;%1&apos;&gt;adakozz Paypalon keresztül&lt;/a&gt;, hogy segítsd a %2 fejlesztését.</translation>
+    </message>
+    <message>
+        <source>Icon designed by %1.</source>
+        <translation>Az ikonokat tervezte: %1.</translation>
+    </message>
+    <message>
+        <source>Compact mode contributed by %1.</source>
+        <translation>Kompakt mód: %1.</translation>
+    </message>
+    <message>
+        <source>HTTP proxy support contributed by %1.</source>
+        <translation>HTTP proxy támogatás: %1.</translation>
+    </message>
+    <message>
+        <source>Windows version built by %1</source>
+        <translation type="obsolete">Windows verzió: %1</translation>
+    </message>
+    <message>
+        <source>Translated by %1</source>
+        <translation>Fordította: %1</translation>
+    </message>
+    <message>
+        <source>Released under the &lt;a href=&apos;%1&apos;&gt;GNU General Public License&lt;/a&gt;</source>
+        <translation>Kiadva &lt;a href=&apos;%1&apos;&gt;GNU General Public License&lt;/a&gt; alatt</translation>
+    </message>
+    <message>
+        <source>&amp;Close</source>
+        <translation>&amp;Bezárás</translation>
+    </message>
+    <message>
+        <source>About</source>
+        <translation>Névjegy</translation>
+    </message>
+    <message>
+        <source>What you always wanted to know about %1 and never dared to ask</source>
+        <translation>Amit mindig is tudni akartál a %1-ról, de sosem merted megkérdezni</translation>
+    </message>
+    <message>
+        <source>Please &lt;a href=&apos;%1&apos;&gt;donate&lt;/a&gt; to support the continued development of %2.</source>
+        <translation>Kérlek &lt;a href=&apos;%1&apos;&gt;adakozz&lt;/a&gt;, hogy folytathassam a %2 fejlesztését</translation>
+    </message>
+</context>
+<context>
+    <name>ClearButton</name>
+    <message>
+        <source>Clear</source>
+        <translation>Törlés</translation>
+    </message>
+</context>
+<context>
+    <name>ListModel</name>
+    <message>
+        <source>Searching...</source>
+        <translation>Keresés...</translation>
+    </message>
+    <message>
+        <source>Show %1 More</source>
+        <translation>Mutass még %1-et</translation>
+    </message>
+    <message>
+        <source>No videos</source>
+        <translation>Nincsenek videók</translation>
+    </message>
+    <message>
+        <source>No more videos</source>
+        <translation>Nincs több videó</translation>
+    </message>
+</context>
+<context>
+    <name>LoadingWidget</name>
+    <message>
+        <source>Error</source>
+        <translation>Hiba</translation>
+    </message>
+</context>
+<context>
+    <name>MainWindow</name>
+    <message>
+        <source>&amp;Back</source>
+        <translation type="obsolete">&amp;Vissza</translation>
+    </message>
+    <message>
+        <source>Go to the previous view</source>
+        <translation type="obsolete">Vissza az előző nézethez</translation>
+    </message>
+    <message>
+        <source>&amp;Stop</source>
+        <translation>&amp;Állj</translation>
+    </message>
+    <message>
+        <source>Stop playback and go back to the search view</source>
+        <translation>Megáll és visszamegy a keresőbe</translation>
+    </message>
+    <message>
+        <source>S&amp;kip</source>
+        <translation>&amp;Kihagyás</translation>
+    </message>
+    <message>
+        <source>Skip to the next video</source>
+        <translation>Kihagyja a következő videót</translation>
+    </message>
+    <message>
+        <source>&amp;Pause</source>
+        <translation>&amp;Szünet</translation>
+    </message>
+    <message>
+        <source>Pause playback</source>
+        <translation>Szünetelteti a lejátszást</translation>
+    </message>
+    <message>
+        <source>&amp;Full Screen</source>
+        <translation>&amp;Teljes képernyő</translation>
+    </message>
+    <message>
+        <source>Go full screen</source>
+        <translation>Teljes képernyőre teszi a videót</translation>
+    </message>
+    <message>
+        <source>&amp;Compact mode</source>
+        <translation>K&amp;ompakt mód</translation>
+    </message>
+    <message>
+        <source>Hide the playlist and the toolbar</source>
+        <translation>Elrejti a lejátszó listát és az eszköztárat</translation>
+    </message>
+    <message>
+        <source>&amp;YouTube</source>
+        <translation type="obsolete">&amp;YouTube</translation>
+    </message>
+    <message>
+        <source>Open the YouTube video page</source>
+        <translation type="obsolete">Megnyitja a YouTube videómegosztó oldalt</translation>
+    </message>
+    <message>
+        <source>&amp;Remove</source>
+        <translation>&amp;Törlés</translation>
+    </message>
+    <message>
+        <source>Remove the selected videos from the playlist</source>
+        <translation>Eltávolítja a kijelölt videókat a listából</translation>
+    </message>
+    <message>
+        <source>Move &amp;Up</source>
+        <translation>&amp;Fel</translation>
+    </message>
+    <message>
+        <source>Move up the selected videos in the playlist</source>
+        <translation>Előrehozza a kijelölt videókat a listán</translation>
+    </message>
+    <message>
+        <source>Move &amp;Down</source>
+        <translation>&amp;Le</translation>
+    </message>
+    <message>
+        <source>Move down the selected videos in the playlist</source>
+        <translation>Lejjebb viszi a kijelölt videókat a listán</translation>
+    </message>
+    <message>
+        <source>&amp;Quit</source>
+        <translation>&amp;Kilépés</translation>
+    </message>
+    <message>
+        <source>Ctrl+Q</source>
+        <translation>Ctrl+Q</translation>
+    </message>
+    <message>
+        <source>Bye</source>
+        <translation>Viszlát</translation>
+    </message>
+    <message>
+        <source>&amp;Website</source>
+        <translation>&amp;Weboldal</translation>
+    </message>
+    <message>
+        <source>%1 on the Web</source>
+        <translation>%1 a weben</translation>
+    </message>
+    <message>
+        <source>&amp;Donate via PayPal</source>
+        <translation type="obsolete">&amp;Adakozás PayPalon keresztül</translation>
+    </message>
+    <message>
+        <source>Please support the continued development of %1</source>
+        <translation>Kérlek támogasd a  %1 fejlesztését</translation>
+    </message>
+    <message>
+        <source>&amp;About</source>
+        <translation>&amp;Névjegy</translation>
+    </message>
+    <message>
+        <source>Info about %1</source>
+        <translation>Információk a %1-ról</translation>
+    </message>
+    <message>
+        <source>Search</source>
+        <translation>Keresés</translation>
+    </message>
+    <message>
+        <source>Mute volume</source>
+        <translation>Némítás</translation>
+    </message>
+    <message>
+        <source>Ctrl+M</source>
+        <translation>Ctrl+M</translation>
+    </message>
+    <message>
+        <source>&amp;Playlist</source>
+        <translation>&amp;Lejátszó lista</translation>
+    </message>
+    <message>
+        <source>&amp;Video</source>
+        <translation>&amp;Video</translation>
+    </message>
+    <message>
+        <source>&amp;Help</source>
+        <translation>&amp;Segítség</translation>
+    </message>
+    <message>
+        <source>Press %1 to raise the volume, %2 to lower it</source>
+        <translation>Nyomd meg a %1 gombot a hangosításhoz, a %2 gombot a halkításhoz</translation>
+    </message>
+    <message>
+        <source>Opening %1</source>
+        <translation>Megnyitás %1</translation>
+    </message>
+    <message>
+        <source>Fatal error: %1</source>
+        <translation>Végzetes hiba: %1</translation>
+    </message>
+    <message>
+        <source>Error: %1</source>
+        <translation>Hiba: %1</translation>
+    </message>
+    <message>
+        <source>&amp;Play</source>
+        <translation>&amp;Lejátszás</translation>
+    </message>
+    <message>
+        <source>Resume playback</source>
+        <translation>Lejátszás folytatása</translation>
+    </message>
+    <message>
+        <source>Exit &amp;Full Screen</source>
+        <translation>Kilépés a &amp;Teljes képernyőből</translation>
+    </message>
+    <message>
+        <source>Remaining time: %1</source>
+        <translation>Hátralévő idő: %1</translation>
+    </message>
+    <message>
+        <source>Volume at %1%</source>
+        <translation>Hangerő: %1%</translation>
+    </message>
+    <message>
+        <source>Volume is muted</source>
+        <translation>Némítás bekapcsolva</translation>
+    </message>
+    <message>
+        <source>Volume is unmuted</source>
+        <translation>Némítás kikapcsolva</translation>
+    </message>
+    <message>
+        <source>High Definition video is enabled</source>
+        <translation type="obsolete">HD videó bekapcsolva</translation>
+    </message>
+    <message>
+        <source>High Definition video is not enabled</source>
+        <translation type="obsolete">HD videó kikapcsolva</translation>
+    </message>
+    <message>
+        <source>The current video is in High Definition</source>
+        <translation type="obsolete">Ez a videó HD minőségű</translation>
+    </message>
+    <message>
+        <source>The current video is not in High Definition</source>
+        <translation type="obsolete">Ez a videó nem HD minőségű</translation>
+    </message>
+    <message>
+        <source>&amp;Clear recent keywords</source>
+        <translation>&amp;Legújabb kulcsszavak törlése</translation>
+    </message>
+    <message>
+        <source>&amp;Application</source>
+        <translation>&amp;Alkalmazás</translation>
+    </message>
+    <message>
+        <source>Clear the search history. Cannot be undone.</source>
+        <translation>Keresési előzmények törlése. Nem visszavonható.</translation>
+    </message>
+    <message>
+        <source>Your privacy is now safe</source>
+        <translation>Biztonságban vagy</translation>
+    </message>
+    <message>
+        <source>Open the &amp;YouTube page</source>
+        <translation>A YouTube megnyitása</translation>
+    </message>
+    <message>
+        <source>Go to the YouTube video page and pause playback</source>
+        <translation>A YouTube oldalra irányítás és a lejátszás szüneteltetése</translation>
+    </message>
+    <message>
+        <source>Copy the YouTube &amp;link</source>
+        <translation>YouTube link másolása</translation>
+    </message>
+    <message>
+        <source>Copy the current video YouTube link to the clipboard</source>
+        <translation>Az aktuális YouTube video link másolása vágólapra</translation>
+    </message>
+    <message>
+        <source>Copy the video stream &amp;URL</source>
+        <translation>A video stream &amp;URL másolása</translation>
+    </message>
+    <message>
+        <source>Copy the current video stream URL to the clipboard</source>
+        <translation>Az aktuális video stream URL másolása vágólapra</translation>
+    </message>
+    <message>
+        <source>Make a &amp;donation</source>
+        <translation>A&amp;dakozz</translation>
+    </message>
+    <message>
+        <source>Maximum video definition set to %1</source>
+        <translation>Maximum videó minőség beállítása %1-ra</translation>
+    </message>
+</context>
+<context>
+    <name>MediaView</name>
+    <message>
+        <source>Most relevant</source>
+        <translation>Kapcsolódó videók</translation>
+    </message>
+    <message>
+        <source>Most recent</source>
+        <translation>Legújabbak</translation>
+    </message>
+    <message>
+        <source>Most viewed</source>
+        <translation>Legnézettebbek</translation>
+    </message>
+    <message>
+        <source>You&apos;re watching &quot;%1&quot;</source>
+        <translation>A &quot;%1&quot;-t nézed</translation>
+    </message>
+    <message>
+        <source>You can now paste the YouTube link into another application</source>
+        <translation>Most már beillesztheted a YouTube linket más alkalmazásba</translation>
+    </message>
+    <message>
+        <source>You can now paste the video stream URL into another application</source>
+        <translation>Most már beillesztheted a video stream URL-t más alkalmazásba</translation>
+    </message>
+    <message>
+        <source>The link will be valid only for a limited time.</source>
+        <translation>A link korlátozott ideig érvényes</translation>
+    </message>
+</context>
+<context>
+    <name>NetworkAccess</name>
+    <message>
+        <source>Network error: %1</source>
+        <translation>Hálózati hiba: %1</translation>
+    </message>
+</context>
+<context>
+    <name>PrettyItemDelegate</name>
+    <message>
+        <source>%1 views</source>
+        <translation>%1x megtekintve</translation>
+    </message>
+</context>
+<context>
+    <name>SearchLineEdit</name>
+    <message>
+        <source>Search</source>
+        <translation>Keresés</translation>
+    </message>
+</context>
+<context>
+    <name>SearchView</name>
+    <message>
+        <source>Welcome to &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</source>
+        <translation>Üdvözöl a &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
+    </message>
+    <message>
+        <source>Enter a keyword to start watching videos.</source>
+        <translation>Írd be a keresendő kifejezést.</translation>
+    </message>
+    <message>
+        <source>Watch</source>
+        <translation>Keresés</translation>
+    </message>
+    <message>
+        <source>Recent keywords</source>
+        <translation>Gyakori kulcsszavak</translation>
+    </message>
+    <message>
+        <source>A new version of %1 is available. Please &lt;a href=&apos;%2&apos;&gt;update to version %3&lt;/a&gt;</source>
+        <translation>A %1 új verziója elérhető. Kérlek &lt;a href=&apos;%2&apos;&gt;frissíts erre a verzióra: %3&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>Make yourself comfortable</source>
+        <translation>Helyezd magad kényelembe</translation>
+    </message>
+</context>
+<context>
+    <name>SettingsView</name>
+    <message>
+        <source>Preferences</source>
+        <translation type="obsolete">Beállítások</translation>
+    </message>
+</context>
+<context>
+    <name>Video</name>
+    <message>
+        <source>Network error: %1 for %2</source>
+        <translation>Hálózati hiba: %1 for %2</translation>
+    </message>
+</context>
+</TS>
diff --git a/locale/it_IT.ts b/locale/it_IT.ts
new file mode 100644 (file)
index 0000000..c886beb
--- /dev/null
@@ -0,0 +1,465 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.0" language="it_IT" sourcelanguage="en_US">
+<defaultcodec>UTF-8</defaultcodec>
+<context>
+    <name>AboutView</name>
+    <message>
+        <source>There&apos;s life outside the browser!</source>
+        <translation>C&apos;è vita fuori del browser!</translation>
+    </message>
+    <message>
+        <source>Version %1</source>
+        <translation>Versione %1</translation>
+    </message>
+    <message>
+        <source>This is a &quot;Technology Preview&quot; release, do not expect it to be perfect.</source>
+        <translation type="obsolete">Questa è una versione Beta, non aspettarti che sia perfetta.</translation>
+    </message>
+    <message>
+        <source>Report bugs and send in your ideas to %1</source>
+        <translation>Segnala problemi e manda le tue idee a %1</translation>
+    </message>
+    <message>
+        <source>%1 is Free Software but its development takes precious time.</source>
+        <translation>%1 è Software Libero ma il suo sviluppo richiede tempo prezioso.</translation>
+    </message>
+    <message>
+        <source>Please &lt;a href=&apos;%1&apos;&gt;donate via PayPal&lt;/a&gt; to support the continued development of %2.</source>
+        <translation type="obsolete">Per favore &lt;a href=&apos;%1&apos;&gt;fai una donazione con PayPal&lt;/a&gt; per aiutare lo sviluppo di %2.</translation>
+    </message>
+    <message>
+        <source>Icon designed by %1.</source>
+        <translation>Icona disegnata da %1.</translation>
+    </message>
+    <message>
+        <source>Compact mode contributed by %1.</source>
+        <translation>Modalità compatta sviluppata da %1</translation>
+    </message>
+    <message>
+        <source>Translated by %1</source>
+        <translation>Tradotto da %1</translation>
+    </message>
+    <message>
+        <source>Released under the &lt;a href=&apos;%1&apos;&gt;GNU General Public License&lt;/a&gt;</source>
+        <translation>Rilasciato sotto licenza &lt;a href=&quot;%1&quot;&gt;GNU General Public License&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>&amp;Close</source>
+        <translation>&amp;Chiudi</translation>
+    </message>
+    <message>
+        <source>About</source>
+        <translation>Informazioni</translation>
+    </message>
+    <message>
+        <source>What you always wanted to know about %1 and never dared to ask</source>
+        <translation>Quello che hai sempre voluto sapere su %1 e non hai mai osato chiedere</translation>
+    </message>
+    <message>
+        <source>HTTP proxy support contributed by %1.</source>
+        <translation>Supporto per i proxy HTTP svilupparo da %1</translation>
+    </message>
+    <message>
+        <source>Windows version built by %1</source>
+        <translation type="obsolete">Versione per Windows compilata da %</translation>
+    </message>
+    <message>
+        <source>Please &lt;a href=&apos;%1&apos;&gt;donate&lt;/a&gt; to support the continued development of %2.</source>
+        <translation>&lt;a href=&quot;%1&quot;&gt;Fai una donazione&lt;/a&gt; per aiutare lo sviluppo di %2.</translation>
+    </message>
+</context>
+<context>
+    <name>ClearButton</name>
+    <message>
+        <source>Clear</source>
+        <translation>Cancella</translation>
+    </message>
+</context>
+<context>
+    <name>ListModel</name>
+    <message>
+        <source>Searching...</source>
+        <translation>Ricerca...</translation>
+    </message>
+    <message>
+        <source>Show %1 More</source>
+        <translation>Mostra altri %1</translation>
+    </message>
+    <message>
+        <source>No videos</source>
+        <translation>Nessun video</translation>
+    </message>
+    <message>
+        <source>No more videos</source>
+        <translation>Nessun altro video</translation>
+    </message>
+</context>
+<context>
+    <name>LoadingWidget</name>
+    <message>
+        <source>Error</source>
+        <translation>Errore</translation>
+    </message>
+</context>
+<context>
+    <name>MainWindow</name>
+    <message>
+        <source>&amp;Back</source>
+        <translation type="obsolete">&amp;Indietro</translation>
+    </message>
+    <message>
+        <source>Go to the previous view</source>
+        <translation type="obsolete">Vai alla vista precedente</translation>
+    </message>
+    <message>
+        <source>&amp;Stop</source>
+        <translation>&amp;Ferma</translation>
+    </message>
+    <message>
+        <source>Stop playback and go back to the search view</source>
+        <translation>Ferma il video e torna alla ricerca</translation>
+    </message>
+    <message>
+        <source>S&amp;kip</source>
+        <translation>&amp;Salta</translation>
+    </message>
+    <message>
+        <source>Skip to the next video</source>
+        <translation>Salta al prossimo video</translation>
+    </message>
+    <message>
+        <source>&amp;Pause</source>
+        <translation>&amp;Pausa</translation>
+    </message>
+    <message>
+        <source>Pause playback</source>
+        <translation>Metti in pausa</translation>
+    </message>
+    <message>
+        <source>&amp;Full Screen</source>
+        <translation>&amp;Schermo intero</translation>
+    </message>
+    <message>
+        <source>Go full screen</source>
+        <translation>Vai in modalità schermo intero</translation>
+    </message>
+    <message>
+        <source>&amp;Compact mode</source>
+        <translation>Modalità compatta</translation>
+    </message>
+    <message>
+        <source>Hide the playlist and the toolbar</source>
+        <translation>Nascondi la playlist e la barra degli strumenti</translation>
+    </message>
+    <message>
+        <source>Open the YouTube video page</source>
+        <translation type="obsolete">Apri la pagina del video su YouTube</translation>
+    </message>
+    <message>
+        <source>&amp;Remove</source>
+        <translation>&amp;Elimina</translation>
+    </message>
+    <message>
+        <source>Remove the selected videos from the playlist</source>
+        <translation>Elimina i video selezionati dalla playlist</translation>
+    </message>
+    <message>
+        <source>Move &amp;Up</source>
+        <translation>Sposta &amp;sopra</translation>
+    </message>
+    <message>
+        <source>Move up the selected videos in the playlist</source>
+        <translation>Sposta video selezionati verso l&apos;alto</translation>
+    </message>
+    <message>
+        <source>Move &amp;Down</source>
+        <translation>Sposta so&amp;tto</translation>
+    </message>
+    <message>
+        <source>Move down the selected videos in the playlist</source>
+        <translation>Sposta i video selezionati verso il basso</translation>
+    </message>
+    <message>
+        <source>&amp;Quit</source>
+        <translation>&amp;Esci</translation>
+    </message>
+    <message>
+        <source>Ctrl+Q</source>
+        <translation></translation>
+    </message>
+    <message>
+        <source>Bye</source>
+        <translation>Ciao</translation>
+    </message>
+    <message>
+        <source>&amp;Website</source>
+        <translation>Sito &amp;web</translation>
+    </message>
+    <message>
+        <source>Ctrl+M</source>
+        <translation>Ctrl+M</translation>
+    </message>
+    <message>
+        <source>Volume at %1%</source>
+        <translation>Volume al %1%</translation>
+    </message>
+    <message>
+        <source>Volume is muted</source>
+        <translation>Audio disattivato</translation>
+    </message>
+    <message>
+        <source>Volume is unmuted</source>
+        <translation>Audio attivato</translation>
+    </message>
+    <message>
+        <source>Minitube on the Web</source>
+        <translation type="obsolete">Minitube sul Web</translation>
+    </message>
+    <message>
+        <source>%1 on the Web</source>
+        <translation>%1 sul Web</translation>
+    </message>
+    <message>
+        <source>&amp;Donate via PayPal</source>
+        <translation type="obsolete">Fai una &amp;donazione con PayPal</translation>
+    </message>
+    <message>
+        <source>Please support the continued development of %1</source>
+        <translation>Supporta lo sviluppo di %1</translation>
+    </message>
+    <message>
+        <source>&amp;About</source>
+        <translation>&amp;Informazioni</translation>
+    </message>
+    <message>
+        <source>Info about %1</source>
+        <translation>Informazioni su %1</translation>
+    </message>
+    <message>
+        <source>&amp;Search</source>
+        <translation type="obsolete">&amp;Ricerca</translation>
+    </message>
+    <message>
+        <source>&amp;Application</source>
+        <translation>&amp;Applicazione</translation>
+    </message>
+    <message>
+        <source>&amp;Playlist</source>
+        <translation></translation>
+    </message>
+    <message>
+        <source>&amp;Video</source>
+        <translation></translation>
+    </message>
+    <message>
+        <source>&amp;Help</source>
+        <translation>&amp;Aiuto</translation>
+    </message>
+    <message>
+        <source>Opening %1</source>
+        <translation>Apertura di %1</translation>
+    </message>
+    <message>
+        <source>Fatal error: %1</source>
+        <translation>Errore fatale: %1</translation>
+    </message>
+    <message>
+        <source>Error: %1</source>
+        <translation>Errore: %1</translation>
+    </message>
+    <message>
+        <source>&amp;Play</source>
+        <translation></translation>
+    </message>
+    <message>
+        <source>Resume playback</source>
+        <translation>Continua</translation>
+    </message>
+    <message>
+        <source>Exit &amp;Full Screen</source>
+        <translation>&amp;Esci dallo schermo intero</translation>
+    </message>
+    <message>
+        <source>Search</source>
+        <translation>Cerca</translation>
+    </message>
+    <message>
+        <source>Mute volume</source>
+        <translation>Disattiva l&apos;audio</translation>
+    </message>
+    <message>
+        <source>Press %1 to raise the volume, %2 to lower it</source>
+        <translation>Premi %1 per alzare il volume, %2 per abbassarlo</translation>
+    </message>
+    <message>
+        <source>Remaining time: %1</source>
+        <translation>Tempo rimanente: %1</translation>
+    </message>
+    <message>
+        <source>High Definition video is enabled</source>
+        <translation type="obsolete">Il video ad alta definizione è abilitato</translation>
+    </message>
+    <message>
+        <source>High Definition video is not enabled</source>
+        <translation type="obsolete">Il video ad alta definizione è disabilitato</translation>
+    </message>
+    <message>
+        <source>The current video is in High Definition</source>
+        <translation type="obsolete">Il video corrente è in alta definizione</translation>
+    </message>
+    <message>
+        <source>The current video is not in High Definition</source>
+        <translation type="obsolete">Il video corrente non è in alta definizione</translation>
+    </message>
+    <message>
+        <source>&amp;Clear recent keywords</source>
+        <translation>&amp;Cancella le ultime ricerche</translation>
+    </message>
+    <message>
+        <source>Clear the search history. Cannot be undone.</source>
+        <translation>&amp;Cancella le ultime ricerche. L&apos;azione non potrà essere annullata.</translation>
+    </message>
+    <message>
+        <source>Your privacy is now safe</source>
+        <translation>La tua privacy è al sicuro</translation>
+    </message>
+    <message>
+        <source>Open the &amp;YouTube page</source>
+        <translation>Apri su &amp;YouTube</translation>
+    </message>
+    <message>
+        <source>Go to the YouTube video page and pause playback</source>
+        <translation>Vai su YouTube e metti in pausa</translation>
+    </message>
+    <message>
+        <source>Copy the YouTube &amp;link</source>
+        <translation>Copia il &amp;link a YouTube</translation>
+    </message>
+    <message>
+        <source>Copy the current video YouTube link to the clipboard</source>
+        <translation>Copia negli appunti il link a YouTube per il video corrente</translation>
+    </message>
+    <message>
+        <source>Copy the video stream &amp;URL</source>
+        <translation>Copia la &amp;URL dello stream video</translation>
+    </message>
+    <message>
+        <source>Copy the current video stream URL to the clipboard</source>
+        <translation>Copia negli appunti la URL dello stream per il video corrente</translation>
+    </message>
+    <message>
+        <source>Make a &amp;donation</source>
+        <translation>Fai una donazione</translation>
+    </message>
+    <message>
+        <source>Maximum video definition set to %1</source>
+        <translation>La definizione video massima è impostata a %1</translation>
+    </message>
+</context>
+<context>
+    <name>MediaView</name>
+    <message>
+        <source>Most relevant</source>
+        <translation>Più rilevanti</translation>
+    </message>
+    <message>
+        <source>Most recent</source>
+        <translation>Più recenti</translation>
+    </message>
+    <message>
+        <source>Most viewed</source>
+        <translation>Più visti</translation>
+    </message>
+    <message>
+        <source>You&apos;re watching &quot;%1&quot;</source>
+        <translation>Stai guardando &quot;%1&quot;</translation>
+    </message>
+    <message>
+        <source>You can now paste the YouTube link into another application</source>
+        <translation>Ora puoi incollare il link a YouTube in un&apos;altra applicazione</translation>
+    </message>
+    <message>
+        <source>You can now paste the video stream URL into another application</source>
+        <translation>Ora puoi incollare la URL dello stream in un&apos;altra applicazione</translation>
+    </message>
+    <message>
+        <source>The link will be valid only for a limited time.</source>
+        <translation>Il link rimarrà valido per un periodo di tempo limitato.</translation>
+    </message>
+</context>
+<context>
+    <name>NetworkAccess</name>
+    <message>
+        <source>Network error: %1</source>
+        <translation>Errore di rete: %1</translation>
+    </message>
+</context>
+<context>
+    <name>PrettyItemDelegate</name>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 visualizzazioni</translation>
+    </message>
+</context>
+<context>
+    <name>SearchLineEdit</name>
+    <message>
+        <source>Search</source>
+        <translation>Cerca</translation>
+    </message>
+</context>
+<context>
+    <name>SearchView</name>
+    <message>
+        <source>Welcome to &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</source>
+        <translation>Benvenuto su &lt;a href=&quot;%1&quot;&gt;%2&lt;/a&gt;,</translation>
+    </message>
+    <message>
+        <source>Enter a keyword to start watching videos.</source>
+        <translation>Scrivi una parola chiave per iniziare a guardare i video.</translation>
+    </message>
+    <message>
+        <source>Watch</source>
+        <translation>Guarda</translation>
+    </message>
+    <message>
+        <source>Recent keywords</source>
+        <translation>Ultime ricerche</translation>
+    </message>
+    <message>
+        <source>A new version of %1 is available. Please &lt;a href=&apos;%2&apos;&gt;update to version %3&lt;/a&gt;</source>
+        <translation>È disponibile una nuova versione di %1. &lt;a href=&apos;%2&apos;&gt;Aggiorna alla versione %3&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>Make yourself comfortable</source>
+        <translation>Mettiti comodo</translation>
+    </message>
+</context>
+<context>
+    <name>SettingsView</name>
+    <message>
+        <source>Preferences</source>
+        <translation type="obsolete">Opzioni</translation>
+    </message>
+    <message>
+        <source>&amp;Video options</source>
+        <translation type="obsolete">Opzioni &amp;video</translation>
+    </message>
+    <message>
+        <source>Use high quality video when available</source>
+        <translation type="obsolete">Usa video ad alta definizione se disponibile</translation>
+    </message>
+    <message>
+        <source>&amp;Close</source>
+        <translation type="obsolete">&amp;Chiudi</translation>
+    </message>
+</context>
+<context>
+    <name>Video</name>
+    <message>
+        <source>Network error: %1 for %2</source>
+        <translation>Errore di rete: %1 per %2</translation>
+    </message>
+</context>
+</TS>
diff --git a/locale/ja_JP.ts b/locale/ja_JP.ts
new file mode 100644 (file)
index 0000000..7ae44c7
--- /dev/null
@@ -0,0 +1,433 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.0" language="ja_JP">
+<defaultcodec>UTF-8</defaultcodec>
+<context>
+    <name>AboutView</name>
+    <message>
+        <source>There&apos;s life outside the browser!</source>
+        <translation>Webブラウザなしでも大丈夫!</translation>
+    </message>
+    <message>
+        <source>Version %1</source>
+        <translation>バージョン %1</translation>
+    </message>
+    <message>
+        <source>This is a &quot;Technology Preview&quot; release, do not expect it to be perfect.</source>
+        <translation type="obsolete">このプログラムは&quot;テクノロジープレビュー&quot;です。完璧な動作は期待しないでください。</translation>
+    </message>
+    <message>
+        <source>Report bugs and send in your ideas to %1</source>
+        <translation>バグレポートやアイデアは%1までお願いします</translation>
+    </message>
+    <message>
+        <source>%1 is Free Software but its development takes precious time.</source>
+        <translation>%1はフリーソフトウェアですが、開発には貴重な時間が費やされています。</translation>
+    </message>
+    <message>
+        <source>Please &lt;a href=&apos;%1&apos;&gt;donate via PayPal&lt;/a&gt; to support the continued development of %2.</source>
+        <translation type="obsolete">%2の開発/サポートのため、&lt;a href=&apos;%1&apos;&gt;寄付&lt;/a&gt;をお願いします。</translation>
+    </message>
+    <message>
+        <source>Icon designed by %1.</source>
+        <translation>アイコンは%1さんのデザインです。</translation>
+    </message>
+    <message>
+        <source>Compact mode contributed by %1.</source>
+        <translation>コンパクトモードは%1さんの貢献です。</translation>
+    </message>
+    <message>
+        <source>Translated by %1</source>
+        <translation>翻訳は%1さん達の協力です</translation>
+    </message>
+    <message>
+        <source>Released under the &lt;a href=&apos;%1&apos;&gt;GNU General Public License&lt;/a&gt;</source>
+        <translation>&lt;a href=&apos;%1&apos;&gt;GNU General Public License&lt;/a&gt;で配布されます</translation>
+    </message>
+    <message>
+        <source>&amp;Close</source>
+        <translation>閉じる(&amp;C)</translation>
+    </message>
+    <message>
+        <source>About</source>
+        <translation>プログラムについて</translation>
+    </message>
+    <message>
+        <source>What you always wanted to know about %1 and never dared to ask</source>
+        <translation>%1について知りたいことがあったら、思いきって連絡をください</translation>
+    </message>
+    <message>
+        <source>HTTP proxy support contributed by %1.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Please &lt;a href=&apos;%1&apos;&gt;donate&lt;/a&gt; to support the continued development of %2.</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>ClearButton</name>
+    <message>
+        <source>Clear</source>
+        <translation>クリア</translation>
+    </message>
+</context>
+<context>
+    <name>ListModel</name>
+    <message>
+        <source>Searching...</source>
+        <translation>検索中...</translation>
+    </message>
+    <message>
+        <source>Show %1 More</source>
+        <translation>さらに%1エントリ観る</translation>
+    </message>
+    <message>
+        <source>No videos</source>
+        <translation>見つかりませんでした</translation>
+    </message>
+    <message>
+        <source>No more videos</source>
+        <translation>見つかりませんでした</translation>
+    </message>
+</context>
+<context>
+    <name>LoadingWidget</name>
+    <message>
+        <source>Error</source>
+        <translation>エラー</translation>
+    </message>
+</context>
+<context>
+    <name>MainWindow</name>
+    <message>
+        <source>&amp;Back</source>
+        <translation type="obsolete">戻る(&amp;B)</translation>
+    </message>
+    <message>
+        <source>Go to the previous view</source>
+        <translation type="obsolete">前のビューに戻る</translation>
+    </message>
+    <message>
+        <source>&amp;Stop</source>
+        <translation>ストップ(&amp;S)</translation>
+    </message>
+    <message>
+        <source>Stop playback and go back to the search view</source>
+        <translation>再生を停止させて、検索ビューに戻ります</translation>
+    </message>
+    <message>
+        <source>S&amp;kip</source>
+        <translation>スキップ(&amp;k)</translation>
+    </message>
+    <message>
+        <source>Skip to the next video</source>
+        <translation>次の動画へ</translation>
+    </message>
+    <message>
+        <source>&amp;Pause</source>
+        <translation>一時停止(&amp;P)</translation>
+    </message>
+    <message>
+        <source>Pause playback</source>
+        <translation>再生を一時停止します</translation>
+    </message>
+    <message>
+        <source>&amp;Full Screen</source>
+        <translation>フルスクリーン(&amp;F)</translation>
+    </message>
+    <message>
+        <source>Go full screen</source>
+        <translation>フルスクリーン</translation>
+    </message>
+    <message>
+        <source>&amp;Compact mode</source>
+        <translation>コンパクトモード(&amp;C)</translation>
+    </message>
+    <message>
+        <source>Hide the playlist and the toolbar</source>
+        <translation>プレイリストとツールバーを隠す</translation>
+    </message>
+    <message>
+        <source>&amp;YouTube</source>
+        <translation type="obsolete">YouTube(&amp;Y)</translation>
+    </message>
+    <message>
+        <source>Open the YouTube video page</source>
+        <translation type="obsolete">YouTubeの動画Webページを開く</translation>
+    </message>
+    <message>
+        <source>&amp;Remove</source>
+        <translation>削除(&amp;R)</translation>
+    </message>
+    <message>
+        <source>Remove the selected videos from the playlist</source>
+        <translation>プレイリストから選択した動画を削除</translation>
+    </message>
+    <message>
+        <source>Move &amp;Up</source>
+        <translation>上へ(&amp;U)</translation>
+    </message>
+    <message>
+        <source>Move up the selected videos in the playlist</source>
+        <translation>選択した動画をプレイリスト内で上へ移動させます</translation>
+    </message>
+    <message>
+        <source>Move &amp;Down</source>
+        <translation>下へ(&amp;D)</translation>
+    </message>
+    <message>
+        <source>Move down the selected videos in the playlist</source>
+        <translation>選択した動画をプレイリスト内で下へ移動させます</translation>
+    </message>
+    <message>
+        <source>&amp;Quit</source>
+        <translation>終了(&amp;Q)</translation>
+    </message>
+    <message>
+        <source>Ctrl+Q</source>
+        <translation>Ctrl+Q</translation>
+    </message>
+    <message>
+        <source>Bye</source>
+        <translation>プログラムを終了</translation>
+    </message>
+    <message>
+        <source>&amp;Website</source>
+        <translation>Webページへ(&amp;W)</translation>
+    </message>
+    <message>
+        <source>%1 on the Web</source>
+        <translation>%1のWebページを開きます</translation>
+    </message>
+    <message>
+        <source>&amp;Donate via PayPal</source>
+        <translation type="obsolete">寄付(&amp;D)</translation>
+    </message>
+    <message>
+        <source>Please support the continued development of %1</source>
+        <translation>%1の開発をサポートしてください!</translation>
+    </message>
+    <message>
+        <source>&amp;About</source>
+        <translation>プログラムについて(&amp;A)</translation>
+    </message>
+    <message>
+        <source>Info about %1</source>
+        <translation>%1について</translation>
+    </message>
+    <message>
+        <source>Ctrl+M</source>
+        <translation>Ctrl-M</translation>
+    </message>
+    <message>
+        <source>&amp;Application</source>
+        <translation type="unfinished">アプリケーション(&amp;A)</translation>
+    </message>
+    <message>
+        <source>&amp;Playlist</source>
+        <translation>プレイリスト(&amp;P)</translation>
+    </message>
+    <message>
+        <source>&amp;Video</source>
+        <translation>動画(&amp;V)</translation>
+    </message>
+    <message>
+        <source>&amp;Help</source>
+        <translation>ヘルプ(&amp;H)</translation>
+    </message>
+    <message>
+        <source>Opening %1</source>
+        <translation>%1を開いています</translation>
+    </message>
+    <message>
+        <source>Fatal error: %1</source>
+        <translation>致命的なエラー: %1</translation>
+    </message>
+    <message>
+        <source>Error: %1</source>
+        <translation>エラー: %1</translation>
+    </message>
+    <message>
+        <source>&amp;Play</source>
+        <translation>再生(&amp;P)</translation>
+    </message>
+    <message>
+        <source>Resume playback</source>
+        <translation>再生再開します</translation>
+    </message>
+    <message>
+        <source>Exit &amp;Full Screen</source>
+        <translation>フルスクリーンから戻る(&amp;F)</translation>
+    </message>
+    <message>
+        <source>Volume at %1%</source>
+        <translation>ボリューム%1%</translation>
+    </message>
+    <message>
+        <source>Volume is muted</source>
+        <translation>音量OFFにしました</translation>
+    </message>
+    <message>
+        <source>Volume is unmuted</source>
+        <translation>音量ONにしました</translation>
+    </message>
+    <message>
+        <source>Search</source>
+        <translation type="unfinished">検索</translation>
+    </message>
+    <message>
+        <source>Mute volume</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Press %1 to raise the volume, %2 to lower it</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Remaining time: %1</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>&amp;Clear recent keywords</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Clear the search history. Cannot be undone.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Your privacy is now safe</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Open the &amp;YouTube page</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Go to the YouTube video page and pause playback</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Copy the YouTube &amp;link</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Copy the current video YouTube link to the clipboard</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Copy the video stream &amp;URL</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Copy the current video stream URL to the clipboard</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Make a &amp;donation</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Maximum video definition set to %1</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>MediaView</name>
+    <message>
+        <source>Most relevant</source>
+        <translation>関連度</translation>
+    </message>
+    <message>
+        <source>Most recent</source>
+        <translation>新着</translation>
+    </message>
+    <message>
+        <source>Most viewed</source>
+        <translation>閲覧回数</translation>
+    </message>
+    <message>
+        <source>You&apos;re watching &quot;%1&quot;</source>
+        <translation>&quot;%1&quot;を閲覧中</translation>
+    </message>
+    <message>
+        <source>You can now paste the YouTube link into another application</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>You can now paste the video stream URL into another application</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>The link will be valid only for a limited time.</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>NetworkAccess</name>
+    <message>
+        <source>Network error: %1</source>
+        <translation>ネットワークエラー: %1</translation>
+    </message>
+</context>
+<context>
+    <name>PrettyItemDelegate</name>
+    <message>
+        <source>%1 views</source>
+        <translation>%1回 閲覧</translation>
+    </message>
+</context>
+<context>
+    <name>SearchLineEdit</name>
+    <message>
+        <source>Search</source>
+        <translation>検索</translation>
+    </message>
+</context>
+<context>
+    <name>SearchView</name>
+    <message>
+        <source>Welcome to &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</source>
+        <translation>ようこそ&lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;へ!</translation>
+    </message>
+    <message>
+        <source>Enter a keyword to start watching videos.</source>
+        <translation>探している動画のキーワードを入力。</translation>
+    </message>
+    <message>
+        <source>Watch</source>
+        <translation>検索</translation>
+    </message>
+    <message>
+        <source>Recent keywords</source>
+        <translation>最近検索したキーワード</translation>
+    </message>
+    <message>
+        <source>A new version of %1 is available. Please &lt;a href=&apos;%2&apos;&gt;update to version %3&lt;/a&gt;</source>
+        <translation>新しい%1があります。&lt;a href=&apos;%2&apos;&gt;バージョン%3へバージョンアップ&lt;/a&gt;しましょう</translation>
+    </message>
+    <message>
+        <source>Make yourself comfortable</source>
+        <translation>さあリラックスしましょう</translation>
+    </message>
+</context>
+<context>
+    <name>SettingsView</name>
+    <message>
+        <source>Preferences</source>
+        <translation type="obsolete">設定</translation>
+    </message>
+    <message>
+        <source>&amp;Close</source>
+        <translation type="obsolete">閉じる(&amp;C)</translation>
+    </message>
+</context>
+<context>
+    <name>Video</name>
+    <message>
+        <source>Network error: %1 for %2</source>
+        <translation>ネットワークエラー: %1 (%2へのアクセスにて)</translation>
+    </message>
+</context>
+</TS>
diff --git a/locale/lat.ts b/locale/lat.ts
new file mode 100644 (file)
index 0000000..4c46643
--- /dev/null
@@ -0,0 +1,453 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.0" language="lv_LV" sourcelanguage="en_US">
+<defaultcodec>UTF-8</defaultcodec>
+<context>
+    <name>AboutView</name>
+    <message>
+        <source>There&apos;s life outside the browser!</source>
+        <translation>Arī ārpus web pārlūka ir dzīve!</translation>
+    </message>
+    <message>
+        <source>Version %1</source>
+        <translation>Versija %1</translation>
+    </message>
+    <message>
+        <source>This is a &quot;Technology Preview&quot; release, do not expect it to be perfect.</source>
+        <translation>Šis ir priekšapskates izlaidums, negaidiet no tā brīnumus.</translation>
+    </message>
+    <message>
+        <source>Report bugs and send in your ideas to %1</source>
+        <translation>Ziņojiet par kļūdām un iesūtiet idejas: %1</translation>
+    </message>
+    <message>
+        <source>%1 is Free Software but its development takes precious time.</source>
+        <translation>%1 ir Bezmaksas Programma, taču tās izstrāde prasa dārgu laiku.</translation>
+    </message>
+    <message>
+        <source>Please &lt;a href=&apos;%1&apos;&gt;donate via PayPal&lt;/a&gt; to support the continued development of %2.</source>
+        <translation>Lūdzu, &lt;a href=&apos;%1&apos;&gt;ziedojiet ar PayPal&lt;/a&gt;, lai atbalstītu %2 turpmāku izstrādi.</translation>
+    </message>
+    <message>
+        <source>Icon designed by %1.</source>
+        <translation>Ikonu izveidoja %1.</translation>
+    </message>
+    <message>
+        <source>Compact mode contributed by %1.</source>
+        <translation>Kompakto režīmu iespējoja %1.</translation>
+    </message>
+    <message>
+        <source>HTTP proxy support contributed by %1.</source>
+        <translation>HTTP proxy atbalstu iespējoja by %1.</translation>
+    </message>
+    <message>
+        <source>Windows version built by %1</source>
+        <translation>Windows versiju izveidoja %1</translation>
+    </message>
+    <message>
+        <source>Translated by %1</source>
+        <translation>Pārtulkoja %1</translation>
+    </message>
+    <message>
+        <source>Released under the &lt;a href=&apos;%1&apos;&gt;GNU General Public License&lt;/a&gt;</source>
+        <translation>Licencēts pēc &lt;a href=&apos;%1&apos;&gt;GNU Vispārējās Publiskās Licences&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>&amp;Close</source>
+        <translation>&amp;Aizvērt</translation>
+    </message>
+    <message>
+        <source>About</source>
+        <translation>Par</translation>
+    </message>
+    <message>
+        <source>What you always wanted to know about %1 and never dared to ask</source>
+        <translation>Ko jūs vienmēr vēlējāties zināt par %1, taču neuzdrošinājāties pajautāt</translation>
+    </message>
+    <message>
+        <source>Please &lt;a href=&apos;%1&apos;&gt;donate&lt;/a&gt; to support the continued development of %2.</source>
+       <translation>Lūdzu, &lt;a href=&apos;%1&apos;&gt;ziedojiet&lt;/a&gt;, lai atbalstītu %2 turpmāku izstrādi.</translation>
+    </message>
+</context>
+<context>
+    <name>ClearButton</name>
+    <message>
+        <source>Clear</source>
+        <translation>Dzēst</translation>
+    </message>
+</context>
+<context>
+    <name>ListModel</name>
+    <message>
+        <source>Searching...</source>
+        <translation>Meklē...</translation>
+    </message>
+    <message>
+        <source>Show %1 More</source>
+        <translation>Parādīt vairāk no %1</translation>
+    </message>
+    <message>
+        <source>No videos</source>
+        <translation>Nav klipu</translation>
+    </message>
+    <message>
+        <source>No more videos</source>
+        <translation>Vairs nav klipu</translation>
+    </message>
+</context>
+<context>
+    <name>LoadingWidget</name>
+    <message>
+        <source>Error</source>
+        <translation>Kļūda</translation>
+    </message>
+</context>
+<context>
+    <name>MainWindow</name>
+    <message>
+        <source>&amp;Back</source>
+        <translation>Atpakaļ</translation>
+    </message>
+    <message>
+        <source>Go to the previous view</source>
+        <translation>Atgriezties iepriekšējā skatā</translation>
+    </message>
+    <message>
+        <source>&amp;Stop</source>
+        <translation>Apturēt</translation>
+    </message>
+    <message>
+        <source>Stop playback and go back to the search view</source>
+        <translation>Apturēt atskaņošanu un atgriezties meklēšanas logā</translation>
+    </message>
+    <message>
+        <source>S&amp;kip</source>
+        <translation>Izlaist</translation>
+    </message>
+    <message>
+        <source>Skip to the next video</source>
+        <translation>Pārlēkt uz nākamo klipu</translation>
+    </message>
+    <message>
+        <source>&amp;Pause</source>
+        <translation>Pauze</translation>
+    </message>
+    <message>
+        <source>Pause playback</source>
+        <translation>Apturēt</translation>
+    </message>
+    <message>
+        <source>&amp;Full Screen</source>
+        <translation>Pilnekrāna režīms</translation>
+    </message>
+    <message>
+        <source>Go full screen</source>
+        <translation>Ieslēgt pilnekrāna režīmu</translation>
+    </message>
+    <message>
+        <source>&amp;Compact mode</source>
+        <translation>Kompaktais režīms</translation>
+    </message>
+    <message>
+        <source>Hide the playlist and the toolbar</source>
+        <translation>Paslēpt klipu sarakstu un rīkjoslu</translation>
+    </message>
+    <message>
+        <source>&amp;YouTube</source>
+       <translation>&amp;YouTube</translation>
+    </message>
+    <message>
+        <source>Open the YouTube video page</source>
+        <translation>Atvērt YouTube vietni</translation>
+    </message>
+    <message>
+        <source>&amp;Remove</source>
+        <translation>Izņemt</translation>
+    </message>
+    <message>
+        <source>Remove the selected videos from the playlist</source>
+        <translation>Izņemt atzīmētos klipus</translation>
+    </message>
+    <message>
+        <source>Move &amp;Up</source>
+        <translation>Pārcelt augšup</translation>
+    </message>
+    <message>
+        <source>Move up the selected videos in the playlist</source>
+        <translation>Pārcelt atzīmētos klipus augšup</translation>
+    </message>
+    <message>
+        <source>Move &amp;Down</source>
+        <translation>Pārcelt lejup</translation>
+    </message>
+    <message>
+        <source>Move down the selected videos in the playlist</source>
+        <translation>Pārcelt atzīmētos klipus lejup</translation>
+    </message>
+    <message>
+        <source>&amp;Quit</source>
+        <translation>Beigt darbu</translation>
+    </message>
+    <message>
+        <source>Ctrl+Q</source>
+        <translation>Ctrl+Q</translation>
+    </message>
+    <message>
+        <source>Bye</source>
+        <translation>Visu labu</translation>
+    </message>
+    <message>
+        <source>&amp;Website</source>
+        <translation>Tīmekļa vietne</translation>
+    </message>
+    <message>
+        <source>%1 on the Web</source>
+        <translation>%1 tīmeklī</translation>
+    </message>
+    <message>
+        <source>&amp;Donate via PayPal</source>
+        <translation>Ziedojiet ar PayPal</translation>
+    </message>
+    <message>
+        <source>Please support the continued development of %1</source>
+        <translation>Lūdzu, atbalstiet %1 turpmāku izstrādi</translation>
+    </message>
+    <message>
+        <source>&amp;About</source>
+        <translation>Par</translation>
+    </message>
+    <message>
+        <source>Info par %1</source>
+        <translation>Ziņas par %1</translation>
+    </message>
+    <message>
+        <source>Search</source>
+        <translation>Meklēt</translation>
+    </message>
+    <message>
+        <source>Mute volume</source>
+        <translation>Izslēgt skaņu</translation>
+    </message>
+    <message>
+        <source>Ctrl+M</source>
+        <translation>Ctrl+M</translation>
+    </message>
+    <message>
+        <source>&amp;Playlist</source>
+        <translation>Klipu saraksts</translation>
+    </message>
+    <message>
+        <source>&amp;Video</source>
+        <translation>Klips</translation>
+    </message>
+    <message>
+        <source>&amp;Help</source>
+        <translation>Palīdzība</translation>
+    </message>
+    <message>
+        <source>Press %1 to raise the volume, %2 to lower it</source>
+        <translation>Nospiediet %1 lai palielinātu skaļumu, %2 lai to samazinātu</translation>
+    </message>
+    <message>
+        <source>Opening %1</source>
+        <translation>Atvēru %1</translation>
+    </message>
+    <message>
+        <source>Fatal error: %1</source>
+        <translation>Nenovēršama kļūda: %1</translation>
+    </message>
+    <message>
+        <source>Error: %1</source>
+        <translation>Kļūda: %1</translation>
+    </message>
+    <message>
+        <source>&amp;Play</source>
+        <translation>Atskaņot</translation>
+    </message>
+    <message>
+        <source>Resume playback</source>
+        <translation>Turpināt atskaņošanu</translation>
+    </message>
+    <message>
+        <source>Exit &amp;Full Screen</source>
+        <translation>Pārslēgties pilnekrāna režīmā</translation>
+    </message>
+    <message>
+        <source>Remaining time: %1</source>
+        <translation>Atlikušais laiks: %1</translation>
+    </message>
+    <message>
+        <source>Volume at %1%</source>
+        <translation>Skaļuma līmenis: %1%</translation>
+    </message>
+    <message>
+        <source>Volume is muted</source>
+        <translation>Skaņa ir atslēgta</translation>
+    </message>
+    <message>
+        <source>Volume is unmuted</source>
+        <translation>Skaņa ir ieslēgta</translation>
+    </message>
+    <message>
+        <source>Info about %1</source>
+        <translation>Ziņas par %1</translation>
+    </message>
+    <message>
+        <source>High Definition video is enabled</source>
+        <translation>Augstā izšķirtspēja ir ieslēgta</translation>
+    </message>
+    <message>
+        <source>High Definition video is not enabled</source>
+        <translation>Augstā izšķirtspēja nav ieslēgta</translation>
+    </message>
+    <message>
+        <source>The current video is in High Definition</source>
+        <translation>Šis ir augstās izšķirtspējas video</translation>
+    </message>
+    <message>
+        <source>The current video is not in High Definition</source>
+        <translation>Šis nav augstās izšķirtspējas video</translation>
+    </message>
+    <message>
+        <source>&amp;Clear recent keywords</source>
+        <translation>Dzēst atslēgvārdu vēsturi</translation>
+    </message>
+    <message>
+        <source>&amp;Application</source>
+        <translation>Programma</translation>
+    </message>
+    <message>
+        <source>Clear the search history. Cannot be undone.</source>
+        <translation>Dzēst meklēšanas vēsturi. Šī rīcība būs neatgriezeniska.</translation>
+    </message>
+    <message>
+        <source>Your privacy is now safe</source>
+        <translation>Jūsu privātums tagad ir drošībā</translation>
+    </message>
+    <message>
+        <source>Open the &amp;YouTube page</source>
+       <translation>Atvērt &amp;YouTube vietni</translation>
+    </message>
+    <message>
+        <source>Go to the YouTube video page and pause playback</source>
+        <translation>Doties uz YouTube video vietni un apturēt atskaņošanu</translation>
+    </message>
+    <message>
+        <source>Copy the YouTube &amp;link</source>
+        <translation>Nokopēt YouTube norādi</translation>
+    </message>
+    <message>
+        <source>Copy the current video YouTube link to the clipboard</source>
+        <translation>Nokopēt esošā klipa YouTube norādi starpliktuvē</translation>
+    </message>
+    <message>
+        <source>Copy the video stream &amp;URL</source>
+       <translation>Nokopēt video plūsmas &amp;URL</translation>
+    </message>
+    <message>
+        <source>Copy the current video stream URL to the clipboard</source>
+       <translation>Nokopēt esošās video plūsmas URL starpliktuvē</translation>
+    </message>
+    <message>
+        <source>Make a &amp;donation</source>
+        <translation>Ziedot</translation>
+    </message>
+    <message>
+        <source>Maximum video definition set to %1</source>
+        <translation>Klipa maksimālā izšķirtspēja ir %1</translation>
+    </message>
+</context>
+<context>
+    <name>MediaView</name>
+    <message>
+        <source>Most relevant</source>
+        <translation>Atbilstošākie</translation>
+    </message>
+    <message>
+        <source>Most recent</source>
+        <translation>Jaunākie</translation>
+    </message>
+    <message>
+        <source>Most viewed</source>
+        <translation>Apmeklētākie</translation>
+    </message>
+    <message>
+        <source>You&apos;re watching &quot;%1&quot;</source>
+        <translation>Jūs skatāties &quot;%1&quot;</translation>
+    </message>
+    <message>
+        <source>You can now paste the YouTube link into another application</source>
+        <translation>Tagad varat ielīmēt YouTube norādi citā programmā</translation>
+    </message>
+    <message>
+        <source>You can now paste the video stream URL into another application</source>
+       <translation>Tagad varat ielīmēt video plūsmas URL citā programmā</translation>
+    </message>
+    <message>
+        <source>The link will be valid only for a limited time.</source>
+        <translation>Norāde būs derīga tikai ierobežotu laiku</translation>
+    </message>
+</context>
+<context>
+    <name>NetworkAccess</name>
+    <message>
+        <source>Network error: %1</source>
+        <translation>Tīkla kļūda: %1</translation>
+    </message>
+</context>
+<context>
+    <name>PrettyItemDelegate</name>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 skatījumi</translation>
+    </message>
+</context>
+<context>
+    <name>SearchLineEdit</name>
+    <message>
+        <source>Search</source>
+        <translation>Meklēt</translation>
+    </message>
+</context>
+<context>
+    <name>SearchView</name>
+    <message>
+        <source>Welcome to &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</source>
+        <translation>Laipni lūdzam &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
+    </message>
+    <message>
+        <source>Enter a keyword to start watching videos.</source>
+        <translation>Ierakstiet atslēgvārdu lai sāktu skatīties klipus</translation>
+    </message>
+    <message>
+        <source>Watch</source>
+        <translation>Skatīties</translation>
+    </message>
+    <message>
+        <source>Recent keywords</source>
+        <translation>Iepriekšējie atslēgvārdi</translation>
+    </message>
+    <message>
+        <source>A new version of %1 is available. Please &lt;a href=&apos;%2&apos;&gt;update to version %3&lt;/a&gt;</source>
+        <translation>%1 jaunā versija ir pieejama. Lūdzu, &lt;a href=&apos;%2&apos;&gt;atjauniniet pret %3 versiju&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>Make yourself comfortable</source>
+        <translation>Jūsu ērtībai</translation>
+    </message>
+</context>
+<context>
+    <name>SettingsView</name>
+    <message>
+        <source>Preferences</source>
+        <translation>Uzstādījumi</translation>
+    </message>
+</context>
+<context>
+    <name>Video</name>
+    <message>
+        <source>Network error: %1 for %2</source>
+        <translation>Tīkla kļūda: %1 ar %2</translation>
+    </message>
+</context>
+</TS>
diff --git a/locale/locale.pri b/locale/locale.pri
new file mode 100644 (file)
index 0000000..72c6f7c
--- /dev/null
@@ -0,0 +1,45 @@
+# This voodoo comes from the Arora project
+
+INCLUDEPATH += $$PWD
+DEPENDPATH += $$PWD
+
+TRANSLATIONS += \
+    it_IT.ts \
+    pt_BR.ts \
+    ru_RU.ts \
+    pl_PL.ts \
+    de_DE.ts \
+    ja_JP.ts \
+    cs_CZ.ts \
+    uk.ts \
+    he_IL.ts \
+    lat.ts \
+    hr_HR.ts \
+    es.ts \
+    gl.ts \
+    fr_FR.ts \
+    hu_HU.ts \
+    tr_TR.ts \
+    nb_NO.ts \
+    ro_RO.ts \
+    el_GR.ts \
+    nl_NL.ts \
+    ar.ts \
+    pt_PT.ts \
+    fi_FI.ts \
+    bg_BG.ts
+
+isEmpty(QMAKE_LRELEASE) {
+    win32:QMAKE_LRELEASE = $$[QT_INSTALL_BINS]\lrelease.exe
+    else:QMAKE_LRELEASE = $$[QT_INSTALL_BINS]/lrelease
+}
+
+updateqm.input = TRANSLATIONS
+updateqm.output = build/target/locale/${QMAKE_FILE_BASE}.qm
+updateqm.commands = $$QMAKE_LRELEASE ${QMAKE_FILE_IN} -qm build/target/locale/${QMAKE_FILE_BASE}.qm
+updateqm.CONFIG += no_link target_predeps
+QMAKE_EXTRA_COMPILERS += updateqm
+
+#qmfiles.files = TRANSLATIONS
+#qmfiles.path = Content/Resources
+#QMAKE_BUNDLE_DATA += qmfiles
diff --git a/locale/lupdate.sh b/locale/lupdate.sh
new file mode 100755 (executable)
index 0000000..102d707
--- /dev/null
@@ -0,0 +1,14 @@
+#!/bin/bash
+#
+# This script was written to update all the .ts files in one go
+#
+# This script is donated to the public domain
+#
+# Flavio Tordini, 2009
+
+for I in `ls -1 *.ts`;
+do
+  echo Updating $I
+  lupdate-qt4 ../minitube.pro -ts $I
+done
+
diff --git a/locale/nb_NO.ts b/locale/nb_NO.ts
new file mode 100644 (file)
index 0000000..d7b7293
--- /dev/null
@@ -0,0 +1,449 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.0" language="nb_NO" sourcelanguage="en">
+<defaultcodec>UTF-8</defaultcodec>
+<context>
+    <name>AboutView</name>
+    <message>
+        <source>There&apos;s life outside the browser!</source>
+        <translation>Det finnes et liv utenfor nettleseren!</translation>
+    </message>
+    <message>
+        <source>Version %1</source>
+        <translation>Versjon %1</translation>
+    </message>
+    <message>
+        <source>This is a &quot;Technology Preview&quot; release, do not expect it to be perfect.</source>
+        <translation type="obsolete">Dette er en betarelease, ikke forvent at den er perfekt.</translation>
+    </message>
+    <message>
+        <source>Report bugs and send in your ideas to %1</source>
+        <translation>Rapporter bugs og send inn dine ideer til %1</translation>
+    </message>
+    <message>
+        <source>%1 is Free Software but its development takes precious time.</source>
+        <translation>%1 er Fri Programvare men utviklingen tar verdifull tid.</translation>
+    </message>
+    <message>
+        <source>Please &lt;a href=&apos;%1&apos;&gt;donate via PayPal&lt;/a&gt; to support the continued development of %2.</source>
+        <translation type="obsolete">&lt;a href=&apos;%1&apos;&gt;doner via PayPal&lt;/a&gt; for å gjøre fortsatt utvikling av %2 mulig.</translation>
+    </message>
+    <message>
+        <source>Icon designed by %1.</source>
+        <translation>Ikon er designet av %1.</translation>
+    </message>
+    <message>
+        <source>Compact mode contributed by %1.</source>
+        <translation>Kompakt-Visning er utviklet av %1.</translation>
+    </message>
+    <message>
+        <source>HTTP proxy support contributed by %1.</source>
+        <translation>HTTP proxy support utviklet av %1.</translation>
+    </message>
+    <message>
+        <source>Windows version built by %1</source>
+        <translation type="obsolete">Windows versjon bygget av %1</translation>
+    </message>
+    <message>
+        <source>Translated by %1</source>
+        <translation>Oversatt av %1</translation>
+    </message>
+    <message>
+        <source>Released under the &lt;a href=&apos;%1&apos;&gt;GNU General Public License&lt;/a&gt;</source>
+        <translation>Sluppet under &lt;a href=&apos;%1&apos;&gt;GNU General Public License&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>&amp;Close</source>
+        <translation>&amp;Steng</translation>
+    </message>
+    <message>
+        <source>About</source>
+        <translation>Om</translation>
+    </message>
+    <message>
+        <source>What you always wanted to know about %1 and never dared to ask</source>
+        <translation>Hva du alltid har villet visst om %1 men aldri torde å spørre om</translation>
+    </message>
+    <message>
+        <source>Please &lt;a href=&apos;%1&apos;&gt;donate&lt;/a&gt; to support the continued development of %2.</source>
+        <translation>Vennligst &lt;a href=&apos;%1&apos;&gt;doner&lt;/a&gt; for og støtte vidre utvikling av %2.</translation>
+    </message>
+</context>
+<context>
+    <name>ClearButton</name>
+    <message>
+        <source>Clear</source>
+        <translation>Nullstill</translation>
+    </message>
+</context>
+<context>
+    <name>ListModel</name>
+    <message>
+        <source>Searching...</source>
+        <translation>Søker...</translation>
+    </message>
+    <message>
+        <source>Show %1 More</source>
+        <translation>Vis %1 flere</translation>
+    </message>
+    <message>
+        <source>No videos</source>
+        <translation>Ingen videoer</translation>
+    </message>
+    <message>
+        <source>No more videos</source>
+        <translation>Ingen flere videoer</translation>
+    </message>
+</context>
+<context>
+    <name>LoadingWidget</name>
+    <message>
+        <source>Error</source>
+        <translation>Feil</translation>
+    </message>
+</context>
+<context>
+    <name>MainWindow</name>
+    <message>
+        <source>&amp;Back</source>
+        <translation type="obsolete">&amp;Tilbake</translation>
+    </message>
+    <message>
+        <source>Go to the previous view</source>
+        <translation type="obsolete">Gå tilbake til forrige visning</translation>
+    </message>
+    <message>
+        <source>&amp;Stop</source>
+        <translation>&amp;Stopp</translation>
+    </message>
+    <message>
+        <source>Stop playback and go back to the search view</source>
+        <translation>Stopp avspilling og gå tilbake til dine søk</translation>
+    </message>
+    <message>
+        <source>S&amp;kip</source>
+        <translation>&amp;Hopp over</translation>
+    </message>
+    <message>
+        <source>Skip to the next video</source>
+        <translation>Hopp over neste video</translation>
+    </message>
+    <message>
+        <source>&amp;Pause</source>
+        <translation>&amp;Pause</translation>
+    </message>
+    <message>
+        <source>Pause playback</source>
+        <translation>Sett avspilling på pause</translation>
+    </message>
+    <message>
+        <source>&amp;Full Screen</source>
+        <translation>&amp;Fullskjerm</translation>
+    </message>
+    <message>
+        <source>Go full screen</source>
+        <translation>Fullskjermsvisning</translation>
+    </message>
+    <message>
+        <source>&amp;Compact mode</source>
+        <translation>&amp;Kompakt-Visning</translation>
+    </message>
+    <message>
+        <source>Hide the playlist and the toolbar</source>
+        <translation>Gjem spillelista og verktøylinja</translation>
+    </message>
+    <message>
+        <source>&amp;YouTube</source>
+        <translation type="obsolete">&amp;YouTube</translation>
+    </message>
+    <message>
+        <source>Open the YouTube video page</source>
+        <translation type="obsolete">Åpne filmen på youtube</translation>
+    </message>
+    <message>
+        <source>&amp;Remove</source>
+        <translation>&amp;Fjern</translation>
+    </message>
+    <message>
+        <source>Remove the selected videos from the playlist</source>
+        <translation>Fjern valgte videoer fra spillelista</translation>
+    </message>
+    <message>
+        <source>Move &amp;Up</source>
+        <translation>Flytt &amp;opp</translation>
+    </message>
+    <message>
+        <source>Move up the selected videos in the playlist</source>
+        <translation>Flytt opp valgte videoer i spillelista</translation>
+    </message>
+    <message>
+        <source>Move &amp;Down</source>
+        <translation>Flytt &amp;Ned</translation>
+    </message>
+    <message>
+        <source>Move down the selected videos in the playlist</source>
+        <translation>Flytt ned valgte videoer i spillelista</translation>
+    </message>
+    <message>
+        <source>&amp;Quit</source>
+        <translation>&amp;Avslutt</translation>
+    </message>
+    <message>
+        <source>Ctrl+Q</source>
+        <translation>Ctrl+Q</translation>
+    </message>
+    <message>
+        <source>Bye</source>
+        <translation>Ha en fin dag</translation>
+    </message>
+    <message>
+        <source>&amp;Website</source>
+        <translation>&amp;Nettsted</translation>
+    </message>
+    <message>
+        <source>%1 on the Web</source>
+        <translation>%1 på internett</translation>
+    </message>
+    <message>
+        <source>&amp;Donate via PayPal</source>
+        <translation type="obsolete">&amp;Doner via PayPal</translation>
+    </message>
+    <message>
+        <source>Please support the continued development of %1</source>
+        <translation>Bidra til fortsatt utvikling av %1</translation>
+    </message>
+    <message>
+        <source>&amp;About</source>
+        <translation>&amp;Om</translation>
+    </message>
+    <message>
+        <source>Info about %1</source>
+        <translation>Informasjon om %1</translation>
+    </message>
+    <message>
+        <source>Search</source>
+        <translation>Søk</translation>
+    </message>
+    <message>
+        <source>Mute volume</source>
+        <translation>Mute</translation>
+    </message>
+    <message>
+        <source>Ctrl+M</source>
+        <translation>Ctrl+M</translation>
+    </message>
+    <message>
+        <source>&amp;Playlist</source>
+        <translation>&amp;Spilleliste</translation>
+    </message>
+    <message>
+        <source>&amp;Video</source>
+        <translation>&amp;Video</translation>
+    </message>
+    <message>
+        <source>&amp;Help</source>
+        <translation>&amp;Hjelp</translation>
+    </message>
+    <message>
+        <source>Press %1 to raise the volume, %2 to lower it</source>
+        <translation>Trykk på %1 for å stille opp volum, %2 for å skru ned</translation>
+    </message>
+    <message>
+        <source>Opening %1</source>
+        <translation>Åpner %1</translation>
+    </message>
+    <message>
+        <source>Fatal error: %1</source>
+        <translation>Kritisk feil: %1</translation>
+    </message>
+    <message>
+        <source>Error: %1</source>
+        <translation>Feil: %1</translation>
+    </message>
+    <message>
+        <source>&amp;Play</source>
+        <translation>&amp;Spill av</translation>
+    </message>
+    <message>
+        <source>Resume playback</source>
+        <translation>Gjenoppta avspilling</translation>
+    </message>
+    <message>
+        <source>Exit &amp;Full Screen</source>
+        <translation>Avslutt &amp;Fullskjerm</translation>
+    </message>
+    <message>
+        <source>Remaining time: %1</source>
+        <translation>Gjenstående tid: %1</translation>
+    </message>
+    <message>
+        <source>Volume at %1%</source>
+        <translation>Volum %1%</translation>
+    </message>
+    <message>
+        <source>Volume is muted</source>
+        <translation>Mute er på</translation>
+    </message>
+    <message>
+        <source>Volume is unmuted</source>
+        <translation>Mute er av</translation>
+    </message>
+    <message>
+        <source>High Definition video is enabled</source>
+        <translation type="obsolete">High Definition video er aktivert</translation>
+    </message>
+    <message>
+        <source>High Definition video is not enabled</source>
+        <translation type="obsolete">High Definition video er ikke aktivert</translation>
+    </message>
+    <message>
+        <source>The current video is in High Definition</source>
+        <translation type="obsolete">Denne videoen er i High Definition</translation>
+    </message>
+    <message>
+        <source>The current video is not in High Definition</source>
+        <translation type="obsolete">Denne videoen er ikke i High Definition</translation>
+    </message>
+    <message>
+        <source>&amp;Clear recent keywords</source>
+        <translation>&amp;Nullstill siste søkeord</translation>
+    </message>
+    <message>
+        <source>&amp;Application</source>
+        <translation>Applikasjon</translation>
+    </message>
+    <message>
+        <source>Clear the search history. Cannot be undone.</source>
+        <translation>Klarer søkehistorie. Kan ikke bli angert.</translation>
+    </message>
+    <message>
+        <source>Your privacy is now safe</source>
+        <translation>Ditt privatliv er nå sikkert</translation>
+    </message>
+    <message>
+        <source>Open the &amp;YouTube page</source>
+        <translation>Åpne &amp;YouTube side</translation>
+    </message>
+    <message>
+        <source>Go to the YouTube video page and pause playback</source>
+        <translation>Gå til YouTube video siden og pause avspillingen</translation>
+    </message>
+    <message>
+        <source>Copy the YouTube &amp;link</source>
+        <translation>Kopier YouTube &amp;link</translation>
+    </message>
+    <message>
+        <source>Copy the current video YouTube link to the clipboard</source>
+        <translation>Kopier denne YouTube video linken til utklippstavle</translation>
+    </message>
+    <message>
+        <source>Copy the video stream &amp;URL</source>
+        <translation>Kopier video stream &amp;URL</translation>
+    </message>
+    <message>
+        <source>Copy the current video stream URL to the clipboard</source>
+        <translation>Kopier denne video stream URL til utklippstavle</translation>
+    </message>
+    <message>
+        <source>Make a &amp;donation</source>
+        <translation>Gjør en &amp;donasjon</translation>
+    </message>
+    <message>
+        <source>Maximum video definition set to %1</source>
+        <translation>Maksimum video definisjon innstilling til %1</translation>
+    </message>
+</context>
+<context>
+    <name>MediaView</name>
+    <message>
+        <source>Most relevant</source>
+        <translation>Mest relevant</translation>
+    </message>
+    <message>
+        <source>Most recent</source>
+        <translation>Nyeste</translation>
+    </message>
+    <message>
+        <source>Most viewed</source>
+        <translation>Mest sett</translation>
+    </message>
+    <message>
+        <source>You&apos;re watching &quot;%1&quot;</source>
+        <translation>Du ser på &quot;%1&quot;</translation>
+    </message>
+    <message>
+        <source>You can now paste the YouTube link into another application</source>
+        <translation>Du kan nå lime inn YouTube linken i et annet program</translation>
+    </message>
+    <message>
+        <source>You can now paste the video stream URL into another application</source>
+        <translation>Du kan nå lime inn video stream URL i et annet applikasjon</translation>
+    </message>
+    <message>
+        <source>The link will be valid only for a limited time.</source>
+        <translation>Denne linken vil kun være gyldig i en begrenset tid.</translation>
+    </message>
+</context>
+<context>
+    <name>NetworkAccess</name>
+    <message>
+        <source>Network error: %1</source>
+        <translation>Feil på nettverk: %1</translation>
+    </message>
+</context>
+<context>
+    <name>PrettyItemDelegate</name>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 visninger</translation>
+    </message>
+</context>
+<context>
+    <name>SearchLineEdit</name>
+    <message>
+        <source>Search</source>
+        <translation>Søk</translation>
+    </message>
+</context>
+<context>
+    <name>SearchView</name>
+    <message>
+        <source>Welcome to &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</source>
+        <translation>Velommen til &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
+    </message>
+    <message>
+        <source>Enter a keyword to start watching videos.</source>
+        <translation>Skriv inn et søkeord for å se på videoer.</translation>
+    </message>
+    <message>
+        <source>Watch</source>
+        <translation>Se Film</translation>
+    </message>
+    <message>
+        <source>Recent keywords</source>
+        <translation>Aktuelle søkeord</translation>
+    </message>
+    <message>
+        <source>A new version of %1 is available. Please &lt;a href=&apos;%2&apos;&gt;update to version %3&lt;/a&gt;</source>
+        <translation>En ny versjon av %1 er tilgjengelig. Vennligst &lt;a href=&apos;%2&apos;&gt;oppdater til versjon %3&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>Make yourself comfortable</source>
+        <translation>Finn deg til rette</translation>
+    </message>
+</context>
+<context>
+    <name>SettingsView</name>
+    <message>
+        <source>Preferences</source>
+        <translation type="obsolete">Innstillinger</translation>
+    </message>
+</context>
+<context>
+    <name>Video</name>
+    <message>
+        <source>Network error: %1 for %2</source>
+        <translation>Feil på nettverk: %1 for %2</translation>
+    </message>
+</context>
+</TS>
diff --git a/locale/nl_NL.ts b/locale/nl_NL.ts
new file mode 100644 (file)
index 0000000..206ca0c
--- /dev/null
@@ -0,0 +1,394 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.0" language="nl_NL">
+<defaultcodec>UTF-8</defaultcodec>
+<context>
+    <name>AboutView</name>
+    <message>
+        <source>There&apos;s life outside the browser!</source>
+        <translation>Er is leven buiten de browser!</translation>
+    </message>
+    <message>
+        <source>Version %1</source>
+        <translation>Versie %1</translation>
+    </message>
+    <message>
+        <source>%1 is Free Software but its development takes precious time.</source>
+        <translation>%1 is Gratis Software maar de ontwikkeling is kostbare tijd.</translation>
+    </message>
+    <message>
+        <source>Please &lt;a href=&apos;%1&apos;&gt;donate&lt;/a&gt; to support the continued development of %2.</source>
+        <translation>&lt;a href=&apos;%1&apos;&gt;Doneer&lt;/a&gt; om bij te dragen aan de ontwikkeling van %2.</translation>
+    </message>
+    <message>
+        <source>Report bugs and send in your ideas to %1</source>
+        <translation>Raporteer fouten en stuur je ideën naar %1</translation>
+    </message>
+    <message>
+        <source>Icon designed by %1.</source>
+        <translation>Icon ontworpen door %1.</translation>
+    </message>
+    <message>
+        <source>Compact mode contributed by %1.</source>
+        <translation>Compact mode aangedragen door %1.</translation>
+    </message>
+    <message>
+        <source>HTTP proxy support contributed by %1.</source>
+        <translation>HTTP proxy ondersteuning aangedragen door %1.</translation>
+    </message>
+    <message>
+        <source>Translated by %1</source>
+        <translation>Vertaald door %1</translation>
+    </message>
+    <message>
+        <source>Released under the &lt;a href=&apos;%1&apos;&gt;GNU General Public License&lt;/a&gt;</source>
+        <translation>Uitgebracht onder de &lt;a href=&apos;%1&apos;&gt;GNU General Public License&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>&amp;Close</source>
+        <translation>&amp;Sluiten</translation>
+    </message>
+    <message>
+        <source>About</source>
+        <translation>Over</translation>
+    </message>
+    <message>
+        <source>What you always wanted to know about %1 and never dared to ask</source>
+        <translation>Wat u altijd al had willen weten over %1 maar nooit heeft durven vragen</translation>
+    </message>
+</context>
+<context>
+    <name>ClearButton</name>
+    <message>
+        <source>Clear</source>
+        <translation>Wis</translation>
+    </message>
+</context>
+<context>
+    <name>ListModel</name>
+    <message>
+        <source>Searching...</source>
+        <translation>Bezig met Zoeken...</translation>
+    </message>
+    <message>
+        <source>Show %1 More</source>
+        <translation>Laat %1 meer zien</translation>
+    </message>
+    <message>
+        <source>No videos</source>
+        <translation>Geen video&apos;s</translation>
+    </message>
+    <message>
+        <source>No more videos</source>
+        <translation>Geen video&apos;s meer</translation>
+    </message>
+</context>
+<context>
+    <name>LoadingWidget</name>
+    <message>
+        <source>Error</source>
+        <translation>Fout</translation>
+    </message>
+</context>
+<context>
+    <name>MainWindow</name>
+    <message>
+        <source>&amp;Stop</source>
+        <translation>&amp;Stop</translation>
+    </message>
+    <message>
+        <source>Stop playback and go back to the search view</source>
+        <translation>Stop het afspelen en ga terug naar het zoek overzicht</translation>
+    </message>
+    <message>
+        <source>S&amp;kip</source>
+        <translation>&amp;Overslaan</translation>
+    </message>
+    <message>
+        <source>Skip to the next video</source>
+        <translation>Overslaan en naar de volgende video</translation>
+    </message>
+    <message>
+        <source>&amp;Pause</source>
+        <translation>&amp;Pauzeer</translation>
+    </message>
+    <message>
+        <source>Pause playback</source>
+        <translation>Pauzeer afspelen</translation>
+    </message>
+    <message>
+        <source>&amp;Full Screen</source>
+        <translation>&amp;Volledig scherm</translation>
+    </message>
+    <message>
+        <source>Go full screen</source>
+        <translation>Ga naar volledig scherm</translation>
+    </message>
+    <message>
+        <source>&amp;Compact mode</source>
+        <translation>&amp;Compacte modus</translation>
+    </message>
+    <message>
+        <source>Hide the playlist and the toolbar</source>
+        <translation>Verberg de overzichtslijst en de werkbalk</translation>
+    </message>
+    <message>
+        <source>Open the &amp;YouTube page</source>
+        <translation>Open de &amp;YouTube pagina</translation>
+    </message>
+    <message>
+        <source>Go to the YouTube video page and pause playback</source>
+        <translation>Ga naar de YouTube video pagina en pauzeer het afspelen</translation>
+    </message>
+    <message>
+        <source>Copy the YouTube &amp;link</source>
+        <translation>Kopieer de YouTube &amp;link</translation>
+    </message>
+    <message>
+        <source>Copy the current video YouTube link to the clipboard</source>
+        <translation>Kopieer de huidige YouTube link naar het klembord</translation>
+    </message>
+    <message>
+        <source>Copy the video stream &amp;URL</source>
+        <translation>Kopieer de video stream &amp;URL</translation>
+    </message>
+    <message>
+        <source>Copy the current video stream URL to the clipboard</source>
+        <translation>Kopieer de huidige video stream URL naar het klembord</translation>
+    </message>
+    <message>
+        <source>&amp;Remove</source>
+        <translation>&amp;Verwijder</translation>
+    </message>
+    <message>
+        <source>Remove the selected videos from the playlist</source>
+        <translation>Verwijder de geselecteerde video&apos;s van de afspeellijst</translation>
+    </message>
+    <message>
+        <source>Move &amp;Up</source>
+        <translation>Verplaats &amp;Omhoog</translation>
+    </message>
+    <message>
+        <source>Move up the selected videos in the playlist</source>
+        <translation>Verplaats de geselecteerde video&apos;s in de afspeellijst naar boven</translation>
+    </message>
+    <message>
+        <source>Move &amp;Down</source>
+        <translation>Verplaats &amp;Omlaag</translation>
+    </message>
+    <message>
+        <source>Move down the selected videos in the playlist</source>
+        <translation>Verplaats de geselecteerde video&apos;s in de afspeelijst naar beneden</translation>
+    </message>
+    <message>
+        <source>&amp;Clear recent keywords</source>
+        <translation>&amp;Wis de recente zoekopdrachten</translation>
+    </message>
+    <message>
+        <source>Clear the search history. Cannot be undone.</source>
+        <translation>Wis de zoek geschiedenis. Dit kan niet ongedaan worden gemaakt.</translation>
+    </message>
+    <message>
+        <source>&amp;Quit</source>
+        <translation>&amp;Afsluiten</translation>
+    </message>
+    <message>
+        <source>Ctrl+Q</source>
+        <translation>Ctrl+Q</translation>
+    </message>
+    <message>
+        <source>Bye</source>
+        <translation>Tot ziens</translation>
+    </message>
+    <message>
+        <source>&amp;Website</source>
+        <translation>&amp;Website</translation>
+    </message>
+    <message>
+        <source>%1 on the Web</source>
+        <translation>%1 op het internet</translation>
+    </message>
+    <message>
+        <source>Make a &amp;donation</source>
+        <translation>Maak een &amp;donatie</translation>
+    </message>
+    <message>
+        <source>Please support the continued development of %1</source>
+        <translation>Ondersteun de ontwikkeling van %1</translation>
+    </message>
+    <message>
+        <source>&amp;About</source>
+        <translation>&amp;Over</translation>
+    </message>
+    <message>
+        <source>Info about %1</source>
+        <translation>Informatie over %1</translation>
+    </message>
+    <message>
+        <source>Search</source>
+        <translation>Zoek</translation>
+    </message>
+    <message>
+        <source>Mute volume</source>
+        <translation>Geen volume</translation>
+    </message>
+    <message>
+        <source>Ctrl+M</source>
+        <translation>Ctrl+M</translation>
+    </message>
+    <message>
+        <source>&amp;Application</source>
+        <translation>&amp;Applicatie</translation>
+    </message>
+    <message>
+        <source>&amp;Playlist</source>
+        <translation>&amp;Afspeellijst</translation>
+    </message>
+    <message>
+        <source>&amp;Video</source>
+        <translation>&amp;Video</translation>
+    </message>
+    <message>
+        <source>&amp;Help</source>
+        <translation>&amp;Help</translation>
+    </message>
+    <message>
+        <source>Press %1 to raise the volume, %2 to lower it</source>
+        <translation>Druk op %1 voor meer volume en op %2 voor minder</translation>
+    </message>
+    <message>
+        <source>Opening %1</source>
+        <translation>Openen van %1</translation>
+    </message>
+    <message>
+        <source>Fatal error: %1</source>
+        <translation>Fatale fout: %1</translation>
+    </message>
+    <message>
+        <source>Error: %1</source>
+        <translation>Fout: %1</translation>
+    </message>
+    <message>
+        <source>&amp;Play</source>
+        <translation>&amp;Afspelen</translation>
+    </message>
+    <message>
+        <source>Resume playback</source>
+        <translation>Afspelen hervatten</translation>
+    </message>
+    <message>
+        <source>Exit &amp;Full Screen</source>
+        <translation>Sluit &amp;Volledig Scherm</translation>
+    </message>
+    <message>
+        <source>Remaining time: %1</source>
+        <translation>Overige tijd: %1</translation>
+    </message>
+    <message>
+        <source>Volume at %1%</source>
+        <translation>Volume op %1%</translation>
+    </message>
+    <message>
+        <source>Volume is muted</source>
+        <translation>Volume is uit</translation>
+    </message>
+    <message>
+        <source>Volume is unmuted</source>
+        <translation>Volume is aan</translation>
+    </message>
+    <message>
+        <source>Maximum video definition set to %1</source>
+        <translation>Maximale video defititie staat op %1</translation>
+    </message>
+    <message>
+        <source>Your privacy is now safe</source>
+        <translation>Uw privacy is nu veilig</translation>
+    </message>
+</context>
+<context>
+    <name>MediaView</name>
+    <message>
+        <source>Most relevant</source>
+        <translation>Meest relevant</translation>
+    </message>
+    <message>
+        <source>Most recent</source>
+        <translation>Meest recent</translation>
+    </message>
+    <message>
+        <source>Most viewed</source>
+        <translation>Meest bekeken</translation>
+    </message>
+    <message>
+        <source>You can now paste the YouTube link into another application</source>
+        <translation>U kunt nu uw YouTube link kopieeren naar een andere applicatie</translation>
+    </message>
+    <message>
+        <source>You can now paste the video stream URL into another application</source>
+        <translation>U kunt nu uw video stream URL kopieeren naar een andere applicatie</translation>
+    </message>
+    <message>
+        <source>The link will be valid only for a limited time.</source>
+        <translation>De link zal maar een beperkte tijd geldig zijn.</translation>
+    </message>
+    <message>
+        <source>You&apos;re watching &quot;%1&quot;</source>
+        <translation>U kijkt naar &quot;%1&quot;</translation>
+    </message>
+</context>
+<context>
+    <name>NetworkAccess</name>
+    <message>
+        <source>Network error: %1</source>
+        <translation>Netwerk fout %1</translation>
+    </message>
+</context>
+<context>
+    <name>PrettyItemDelegate</name>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 bekeken</translation>
+    </message>
+</context>
+<context>
+    <name>SearchLineEdit</name>
+    <message>
+        <source>Search</source>
+        <translation>Zoek</translation>
+    </message>
+</context>
+<context>
+    <name>SearchView</name>
+    <message>
+        <source>Welcome to &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</source>
+        <translation>Welkom bij &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
+    </message>
+    <message>
+        <source>Enter a keyword to start watching videos.</source>
+        <translation>Vul een zoekterm in om te beginnen met het bekijken van video&apos;s.</translation>
+    </message>
+    <message>
+        <source>Watch</source>
+        <translation>Bekijk</translation>
+    </message>
+    <message>
+        <source>Recent keywords</source>
+        <translation>Recente zoekopdrachten</translation>
+    </message>
+    <message>
+        <source>A new version of %1 is available. Please &lt;a href=&apos;%2&apos;&gt;update to version %3&lt;/a&gt;</source>
+        <translation>Een nieuwere versie van %1 is beschikbaar.&lt;a href=&apos;%2&apos;&gt;Update naar versie %3&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>Make yourself comfortable</source>
+        <translation>Ga er lekker voor zitten</translation>
+    </message>
+</context>
+<context>
+    <name>Video</name>
+    <message>
+        <source>Network error: %1 for %2</source>
+        <translation>Netwerk fout %1 voor %2</translation>
+    </message>
+</context>
+</TS>
diff --git a/locale/pl_PL.ts b/locale/pl_PL.ts
new file mode 100644 (file)
index 0000000..f73d312
--- /dev/null
@@ -0,0 +1,469 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.0" language="pl_PL">
+<defaultcodec>UTF-8</defaultcodec>
+<context>
+    <name>AboutView</name>
+    <message>
+        <source>There&apos;s life outside the browser!</source>
+        <translation>Istnieje życie poza przeglądarką internetową!</translation>
+    </message>
+    <message>
+        <source>Version %1</source>
+        <translation>Wersja %1</translation>
+    </message>
+    <message>
+        <source>This is a &quot;Technology Preview&quot; release, do not expect it to be perfect.</source>
+        <translation type="obsolete">To jest wydanie &quot;Technology Preview&quot; więc nie spodziewaj się, że będzie idealne.</translation>
+    </message>
+    <message>
+        <source>Report bugs and send in your ideas to %1</source>
+        <translation>Raporty o błędach oraz pomysłach ślij na adres %1</translation>
+    </message>
+    <message>
+        <source>%1 is Free Software but its development takes precious time.</source>
+        <translation>%1 to darmowe oprogramowanie ale jego tworzenie zabiera cenny czas.</translation>
+    </message>
+    <message>
+        <source>Please &lt;a href=&apos;%1&apos;&gt;donate via PayPal&lt;/a&gt; to support the continued development of %2.</source>
+        <translation type="obsolete">Proszę &lt;a href=&apos;%1&apos;&gt;wspomóż finansowo przez PayPal&lt;/a&gt; prace nad programem %2.</translation>
+    </message>
+    <message>
+        <source>Icon designed by %1.</source>
+        <translation>Ikony stworzone przez %1.</translation>
+    </message>
+    <message>
+        <source>Compact mode contributed by %1.</source>
+        <translation>Tryb kompaktowy dzięĸi %1.</translation>
+    </message>
+    <message>
+        <source>Translated by %1</source>
+        <translation>Udział w tłumaczeniu mają: %1</translation>
+    </message>
+    <message>
+        <source>Released under the &lt;a href=&apos;%1&apos;&gt;GNU General Public License&lt;/a&gt;</source>
+        <translation>Program wydany na licencji &lt;a href=&apos;%1&apos;&gt;GNU General Public License&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>&amp;Close</source>
+        <translation>&amp;Zamknij</translation>
+    </message>
+    <message>
+        <source>About</source>
+        <translation>O programie</translation>
+    </message>
+    <message>
+        <source>What you always wanted to know about %1 and never dared to ask</source>
+        <translation>To co zawsze chciałeś wiedzieć o %1 ale bałeś się zapytać</translation>
+    </message>
+    <message>
+        <source>HTTP proxy support contributed by %1.</source>
+        <translation>Wsparcie dla HTTP proxy dzięki wkładowi %1.</translation>
+    </message>
+    <message>
+        <source>Windows version built by %1</source>
+        <translation type="obsolete">%1 - kompilacja dla Windows</translation>
+    </message>
+    <message>
+        <source>Please &lt;a href=&apos;%1&apos;&gt;donate&lt;/a&gt; to support the continued development of %2.</source>
+        <translation>Proszę &lt;a href=&apos;%1&apos;&gt;wspomóż&lt;/a&gt; prace nad programem %2.</translation>
+    </message>
+</context>
+<context>
+    <name>ClearButton</name>
+    <message>
+        <source>Clear</source>
+        <translation>Wyczyść</translation>
+    </message>
+</context>
+<context>
+    <name>ListModel</name>
+    <message>
+        <source>Searching...</source>
+        <translation>Szukanie...</translation>
+    </message>
+    <message>
+        <source>Show %1 More</source>
+        <translation>Pokaż następne %1</translation>
+    </message>
+    <message>
+        <source>No videos</source>
+        <translation>Brak plików wideo</translation>
+    </message>
+    <message>
+        <source>No more videos</source>
+        <translation>Nie ma więcej filmów</translation>
+    </message>
+</context>
+<context>
+    <name>LoadingWidget</name>
+    <message>
+        <source>Error</source>
+        <translation>Błąd</translation>
+    </message>
+</context>
+<context>
+    <name>MainWindow</name>
+    <message>
+        <source>&amp;Back</source>
+        <translation type="obsolete">Powrót</translation>
+    </message>
+    <message>
+        <source>Go to the previous view</source>
+        <translation type="obsolete">Wróć do poprzedniego filmu</translation>
+    </message>
+    <message>
+        <source>&amp;Stop</source>
+        <translation></translation>
+    </message>
+    <message>
+        <source>Stop playback and go back to the search view</source>
+        <translation>Zatrzymaj odtwarzanie i wróć do wyszukiwania</translation>
+    </message>
+    <message>
+        <source>S&amp;kip</source>
+        <translation>Następny</translation>
+    </message>
+    <message>
+        <source>Skip to the next video</source>
+        <translation>Pomiń i włącz następny film</translation>
+    </message>
+    <message>
+        <source>&amp;Pause</source>
+        <translation>&amp;Pauza</translation>
+    </message>
+    <message>
+        <source>Pause playback</source>
+        <translation>Wstrzymaj odtwarzanie</translation>
+    </message>
+    <message>
+        <source>&amp;Full Screen</source>
+        <translation>Pełny &amp;Ekran</translation>
+    </message>
+    <message>
+        <source>Go full screen</source>
+        <translation>Włącz widok pełnoekranowy</translation>
+    </message>
+    <message>
+        <source>&amp;Compact mode</source>
+        <translation>Tryb kompaktowy</translation>
+    </message>
+    <message>
+        <source>Hide the playlist and the toolbar</source>
+        <translation>Ukryj listę odtwarzania i pasek narzędziowy</translation>
+    </message>
+    <message>
+        <source>Open the YouTube video page</source>
+        <translation type="obsolete">Otwórz film YouTube w przeglądarce</translation>
+    </message>
+    <message>
+        <source>&amp;Remove</source>
+        <translation>Usuń</translation>
+    </message>
+    <message>
+        <source>Remove the selected videos from the playlist</source>
+        <translation>Usuń zaznaczone filmy z listy odtwarzania</translation>
+    </message>
+    <message>
+        <source>Move &amp;Up</source>
+        <translation>Przes&amp;uń w górę</translation>
+    </message>
+    <message>
+        <source>Move up the selected videos in the playlist</source>
+        <translation>Przesuń do góry zaznaczone filmy na liście odtwarzania</translation>
+    </message>
+    <message>
+        <source>Move &amp;Down</source>
+        <translation>Przesuń w &amp;dół</translation>
+    </message>
+    <message>
+        <source>Move down the selected videos in the playlist</source>
+        <translation>Przesuń na dół zaznaczone filmy na liście odtwarzania</translation>
+    </message>
+    <message>
+        <source>&amp;Quit</source>
+        <translation>Wyjdź</translation>
+    </message>
+    <message>
+        <source>Ctrl+Q</source>
+        <translation></translation>
+    </message>
+    <message>
+        <source>Bye</source>
+        <translation>Na razie</translation>
+    </message>
+    <message>
+        <source>&amp;Website</source>
+        <translation>Strona domo&amp;wa</translation>
+    </message>
+    <message>
+        <source>%1 on the Web</source>
+        <translation>Strona domowa %1</translation>
+    </message>
+    <message>
+        <source>&amp;Donate via PayPal</source>
+        <translation type="obsolete">&amp;Dotacja dla twórcy przez PayPal</translation>
+    </message>
+    <message>
+        <source>Please support the continued development of %1</source>
+        <translation>Proszę, wspomóż finansowo prace nad %1</translation>
+    </message>
+    <message>
+        <source>&amp;About</source>
+        <translation>O progr&amp;amie</translation>
+    </message>
+    <message>
+        <source>Info about %1</source>
+        <translation>Info o %1</translation>
+    </message>
+    <message>
+        <source>&amp;Search</source>
+        <translation type="obsolete">Szukaj</translation>
+    </message>
+    <message>
+        <source>&amp;Application</source>
+        <translation>Progr&amp;am</translation>
+    </message>
+    <message>
+        <source>&amp;Playlist</source>
+        <translation>Lista odtwarzania</translation>
+    </message>
+    <message>
+        <source>&amp;Video</source>
+        <translation>Film</translation>
+    </message>
+    <message>
+        <source>&amp;Help</source>
+        <translation>Pomoc</translation>
+    </message>
+    <message>
+        <source>Opening %1</source>
+        <translation>Otwieranie %1</translation>
+    </message>
+    <message>
+        <source>Fatal error: %1</source>
+        <translation>Fatalny błąd: %1</translation>
+    </message>
+    <message>
+        <source>Error: %1</source>
+        <translation>Błąd: %1</translation>
+    </message>
+    <message>
+        <source>&amp;Play</source>
+        <translation>&amp;Odtwarzaj</translation>
+    </message>
+    <message>
+        <source>Resume playback</source>
+        <translation>Wznów odtwarzanie</translation>
+    </message>
+    <message>
+        <source>Exit &amp;Full Screen</source>
+        <translation>Wyłącz &amp;pełny ekran</translation>
+    </message>
+    <message>
+        <source>Ctrl+M</source>
+        <translation></translation>
+    </message>
+    <message>
+        <source>Volume at %1%</source>
+        <translation>Głośność %1%</translation>
+    </message>
+    <message>
+        <source>Volume is muted</source>
+        <translation>Dźwięk jest wyłączony</translation>
+    </message>
+    <message>
+        <source>Volume is unmuted</source>
+        <translation>Dźwięk jest włączony</translation>
+    </message>
+    <message>
+        <source>Search</source>
+        <translation>Szukaj</translation>
+    </message>
+    <message>
+        <source>Mute volume</source>
+        <translation>Wyłącz dźwięk</translation>
+    </message>
+    <message>
+        <source>Press %1 to raise the volume, %2 to lower it</source>
+        <translation>Wciśnij %1 aby zwiększyć głośność, %2 żeby ściszyć</translation>
+    </message>
+    <message>
+        <source>Remaining time: %1</source>
+        <translation>Pozostały czas: %1</translation>
+    </message>
+    <message>
+        <source>High Definition video is enabled</source>
+        <translation type="obsolete">High Definition jest włączone</translation>
+    </message>
+    <message>
+        <source>High Definition video is not enabled</source>
+        <translation type="obsolete">High Definition jest wyłączone</translation>
+    </message>
+    <message>
+        <source>The current video is in High Definition</source>
+        <translation type="obsolete">Bieżący film jest dostępny w High Definition</translation>
+    </message>
+    <message>
+        <source>The current video is not in High Definition</source>
+        <translation type="obsolete">Bieżący film nie jest dostępny w High Definition</translation>
+    </message>
+    <message>
+        <source>&amp;Clear recent keywords</source>
+        <translation>&amp;Usuń ostatnie szukane słowa</translation>
+    </message>
+    <message>
+        <source>Clear the search history. Cannot be undone.</source>
+        <translation>Usuń historię wyszukiwania. Operacja nie może zostać cofnięta.</translation>
+    </message>
+    <message>
+        <source>Your privacy is now safe</source>
+        <translation>Twoja prywatność jest teraz zapewniona</translation>
+    </message>
+    <message>
+        <source>Open the &amp;YouTube page</source>
+        <translation>Otwórz stronę &amp;YouTube</translation>
+    </message>
+    <message>
+        <source>Go to the YouTube video page and pause playback</source>
+        <translation>Idź do strony YouTube i wstrzymaj odtwarzanie</translation>
+    </message>
+    <message>
+        <source>Copy the YouTube &amp;link</source>
+        <translation>Kopiuj &amp;link YouTube</translation>
+    </message>
+    <message>
+        <source>Copy the current video YouTube link to the clipboard</source>
+        <translation>Kopiuj bieżący link YouTube do schowka</translation>
+    </message>
+    <message>
+        <source>Copy the video stream &amp;URL</source>
+        <translation>Kopiuj &amp;URL strumienia wideo</translation>
+    </message>
+    <message>
+        <source>Copy the current video stream URL to the clipboard</source>
+        <translation>Kopiuj URL bieżącego strumienia wideo do schowka</translation>
+    </message>
+    <message>
+        <source>Make a &amp;donation</source>
+        <translation>Wspomóż finansowo</translation>
+    </message>
+    <message>
+        <source>Maximum video definition set to %1</source>
+        <translation>Aktualne ustawienie jakości wideo to %1</translation>
+    </message>
+</context>
+<context>
+    <name>MediaView</name>
+    <message>
+        <source>Most relevant</source>
+        <translation>Najbardziej trafne</translation>
+    </message>
+    <message>
+        <source>Most recent</source>
+        <translation>Najnowsze</translation>
+    </message>
+    <message>
+        <source>Most viewed</source>
+        <translation>Najpopularniejsze</translation>
+    </message>
+    <message>
+        <source>You&apos;re watching &quot;%1&quot;</source>
+        <translation>Oglądasz &quot;%1&quot;</translation>
+    </message>
+    <message>
+        <source>You can now paste the YouTube link into another application</source>
+        <translation>Teraz możesz wkleić link YouTube do innego programu</translation>
+    </message>
+    <message>
+        <source>You can now paste the video stream URL into another application</source>
+        <translation>Teraz możesz wkleić URL strumienia wideo do innego programu</translation>
+    </message>
+    <message>
+        <source>The link will be valid only for a limited time.</source>
+        <translation>Link będzie ważny tylko przez określony czas.</translation>
+    </message>
+</context>
+<context>
+    <name>NetworkAccess</name>
+    <message>
+        <source>Network error: %1</source>
+        <translation>Błąd sieci: %1</translation>
+    </message>
+</context>
+<context>
+    <name>PrettyItemDelegate</name>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 wyświetleń</translation>
+    </message>
+</context>
+<context>
+    <name>SearchLineEdit</name>
+    <message>
+        <source>Search</source>
+        <translation>Szukaj</translation>
+    </message>
+</context>
+<context>
+    <name>SearchView</name>
+    <message>
+        <source>Welcome to &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</source>
+        <translation>Witaj w &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
+    </message>
+    <message>
+        <source>Enter a keyword to start watching videos.</source>
+        <translation>Wpisz szukane słowo i zacznij oglądać filmy.</translation>
+    </message>
+    <message>
+        <source>Watch</source>
+        <translation>Wyświetl</translation>
+    </message>
+    <message>
+        <source>Recent keywords</source>
+        <translation>Ostatnio szukane</translation>
+    </message>
+    <message>
+        <source>A new version of %1 is available. Please &lt;a href=&apos;%2&apos;&gt;update to version %3&lt;/a&gt;</source>
+        <translation>Nowa wersja %1 jest dostępna. Proszę &lt;a href=&apos;%2&apos;&gt; zaktualizuj do wersji %3&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>Make yourself comfortable</source>
+        <translation>Usiądź wygodnie</translation>
+    </message>
+</context>
+<context>
+    <name>SettingsView</name>
+    <message>
+        <source>Preferences</source>
+        <translation type="obsolete">Opcje</translation>
+    </message>
+    <message>
+        <source>&amp;Video options</source>
+        <translation type="obsolete">Opcje &amp;obrazu</translation>
+    </message>
+    <message>
+        <source>Use high quality video when available</source>
+        <translation type="obsolete">Użyj wysokiej jakości wideo jeśli jest to możliwe</translation>
+    </message>
+    <message>
+        <source>&amp;Saved recent keywords</source>
+        <translation type="obsolete">&amp;Zapisz ostatnie słowa szukane</translation>
+    </message>
+    <message>
+        <source>&amp;Clear recent keywords</source>
+        <translation type="obsolete">&amp;Usuń ostatnie słowa szukane</translation>
+    </message>
+    <message>
+        <source>&amp;Close</source>
+        <translation type="obsolete">&amp;Zamknij</translation>
+    </message>
+</context>
+<context>
+    <name>Video</name>
+    <message>
+        <source>Network error: %1 for %2</source>
+        <translation>Błąd sieci: %1 dla %2</translation>
+    </message>
+</context>
+</TS>
diff --git a/locale/pt_BR.ts b/locale/pt_BR.ts
new file mode 100644 (file)
index 0000000..5db5d34
--- /dev/null
@@ -0,0 +1,509 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.0" language="pt_BR">
+<defaultcodec>UTF-8</defaultcodec>
+<context>
+    <name>AboutView</name>
+    <message>
+        <source>There&apos;s life outside the browser!</source>
+        <translation>Não há vida fora do navegador!</translation>
+    </message>
+    <message>
+        <source>Version %1</source>
+        <translation>Versão %1</translation>
+    </message>
+    <message>
+        <source>This is a &quot;Technology Preview&quot; release, do not expect it to be perfect.</source>
+        <translation type="obsolete">Este é um lançamento &quot;Prévio da Tecnologia&quot;, não esperamos que ela seja perfeita.</translation>
+    </message>
+    <message>
+        <source>Report bugs and send in your ideas to %1</source>
+        <translation>Relate as falhas e envie suas ideias para %1</translation>
+    </message>
+    <message>
+        <source>%1 is Free Software but its development takes precious time.</source>
+        <translation>%1 é um Software livre, mas o seu desenvolvimento tem um tempo precioso.</translation>
+    </message>
+    <message>
+        <source>Please &lt;a href=&apos;%1&apos;&gt;donate via PayPal&lt;/a&gt; to support the continued development of %2.</source>
+        <translation type="obsolete">Por favor, &lt;a href=&apos;%1&apos;&gt;doções via PayPal&lt;/a&gt; para apoiar o desenvolvimento contínuo de %2.</translation>
+    </message>
+    <message>
+        <source>Icon designed by %1.</source>
+        <translation>Ícone desenhado por %1.</translation>
+    </message>
+    <message>
+        <source>Translated by %1</source>
+        <translation>Traduzido por %1</translation>
+    </message>
+    <message>
+        <source>Released under the &lt;a href=&apos;%1&apos;&gt;GNU General Public License&lt;/a&gt;</source>
+        <translation>Lançado sob a &lt;a href=&apos;%1&apos;&gt;Licença Pública Geral GNU&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>&amp;Close</source>
+        <translation>&amp;Fechar</translation>
+    </message>
+    <message>
+        <source>About</source>
+        <translation>Sobre</translation>
+    </message>
+    <message>
+        <source>What you always wanted to know about %1 and never dared to ask</source>
+        <translation>O que você sempre quis saber sobre %1 e nunca se atreveu a perguntar</translation>
+    </message>
+    <message>
+        <source>Compact mode contributed by %1.</source>
+        <translation>Modo compacto contribuído por %1.</translation>
+    </message>
+    <message>
+        <source>HTTP proxy support contributed by %1.</source>
+        <translation>Suporte a HTTP proxy contribuído por %1.</translation>
+    </message>
+    <message>
+        <source>Windows version built by %1</source>
+        <translation type="obsolete">Vesão da janela construída por %1</translation>
+    </message>
+    <message>
+        <source>Please &lt;a href=&apos;%1&apos;&gt;donate&lt;/a&gt; to support the continued development of %2.</source>
+        <translation>Por favor &lt;a href=&apos;%1&apos;&gt;doe&lt;/a&gt; para apoiar o desenvolvimento contínuo de %2.</translation>
+    </message>
+</context>
+<context>
+    <name>ClearButton</name>
+    <message>
+        <source>Clear</source>
+        <translation>Limpar</translation>
+    </message>
+</context>
+<context>
+    <name>ListModel</name>
+    <message>
+        <source>Searching...</source>
+        <translation>Pesquisando...</translation>
+    </message>
+    <message>
+        <source>Show %1 More</source>
+        <translation>Mostrar %1 a mais</translation>
+    </message>
+    <message>
+        <source>No videos</source>
+        <translation>Não há vídeos</translation>
+    </message>
+    <message>
+        <source>No more videos</source>
+        <translation>Não há mais vídeos</translation>
+    </message>
+</context>
+<context>
+    <name>LoadingWidget</name>
+    <message>
+        <source>Error</source>
+        <translation>Erro</translation>
+    </message>
+</context>
+<context>
+    <name>MainWindow</name>
+    <message>
+        <source>&amp;Back</source>
+        <translation type="obsolete">&amp;Voltar</translation>
+    </message>
+    <message>
+        <source>Alt+Left</source>
+        <translation type="obsolete">Alt+Left</translation>
+    </message>
+    <message>
+        <source>Go to the previous view</source>
+        <translation type="obsolete">Ir para a visualização anterior</translation>
+    </message>
+    <message>
+        <source>&amp;Stop</source>
+        <translation>&amp;Parar</translation>
+    </message>
+    <message>
+        <source>Stop playback and go back to the search view</source>
+        <translation>Parar a reprodução e voltar à visualização da pesquisa</translation>
+    </message>
+    <message>
+        <source>Esc</source>
+        <translation type="obsolete">Esc</translation>
+    </message>
+    <message>
+        <source>S&amp;kip</source>
+        <translation>Pu&amp;lar</translation>
+    </message>
+    <message>
+        <source>Skip to the next video</source>
+        <translation>Pular para o próximo vídeo</translation>
+    </message>
+    <message>
+        <source>Ctrl+Right</source>
+        <translation type="obsolete">Ctrl+Right</translation>
+    </message>
+    <message>
+        <source>&amp;Pause</source>
+        <translation>&amp;Pausar</translation>
+    </message>
+    <message>
+        <source>Pause playback</source>
+        <translation>Pausar a reprodução</translation>
+    </message>
+    <message>
+        <source>Space</source>
+        <translation type="obsolete">Barra de espaço</translation>
+    </message>
+    <message>
+        <source>&amp;Full Screen</source>
+        <translation>&amp;Tela Cheia</translation>
+    </message>
+    <message>
+        <source>Go full screen</source>
+        <translation>Ir para a tela cheia</translation>
+    </message>
+    <message>
+        <source>Alt+Return</source>
+        <translation type="obsolete">Alt+Enter</translation>
+    </message>
+    <message>
+        <source>&amp;YouTube</source>
+        <translation type="obsolete">&amp;YouTube</translation>
+    </message>
+    <message>
+        <source>Open the YouTube video page</source>
+        <translation type="obsolete">Abrir a página de vídeo do YouTube</translation>
+    </message>
+    <message>
+        <source>Ctrl+Y</source>
+        <translation type="obsolete">Ctrl+Y</translation>
+    </message>
+    <message>
+        <source>&amp;Remove</source>
+        <translation>&amp;Remover</translation>
+    </message>
+    <message>
+        <source>Remove the selected videos from the playlist</source>
+        <translation>Remover os vídeos selecionados da playlist</translation>
+    </message>
+    <message>
+        <source>Move &amp;Up</source>
+        <translation>Mover para &amp;cima</translation>
+    </message>
+    <message>
+        <source>Move up the selected videos in the playlist</source>
+        <translation>Mover para cima os vídeos selecionados na playlist</translation>
+    </message>
+    <message>
+        <source>Ctrl+Up</source>
+        <translation type="obsolete">Ctrl+Up</translation>
+    </message>
+    <message>
+        <source>Move &amp;Down</source>
+        <translation>Mover para &amp;baixo</translation>
+    </message>
+    <message>
+        <source>Move down the selected videos in the playlist</source>
+        <translation>Mover para baixo os vídeos selecionados na playlist</translation>
+    </message>
+    <message>
+        <source>Ctrl+Down</source>
+        <translation type="obsolete">Ctrl+Down</translation>
+    </message>
+    <message>
+        <source>&amp;Quit</source>
+        <translation>&amp;Sair</translation>
+    </message>
+    <message>
+        <source>Ctrl+Q</source>
+        <translation>Ctrl+Q</translation>
+    </message>
+    <message>
+        <source>Bye</source>
+        <translation>Tchau</translation>
+    </message>
+    <message>
+        <source>&amp;Website</source>
+        <translation>Si&amp;te</translation>
+    </message>
+    <message>
+        <source>Minitube on the Web</source>
+        <translation type="obsolete">Minitube na Web</translation>
+    </message>
+    <message>
+        <source>%1 on the Web</source>
+        <translation>%1 na Web</translation>
+    </message>
+    <message>
+        <source>&amp;Donate via PayPal</source>
+        <translation type="obsolete">&amp;Doações via PayPal</translation>
+    </message>
+    <message>
+        <source>Please support the continued development of %1</source>
+        <translation>Por favor, apoie o desenvolvimento contínuo de %1</translation>
+    </message>
+    <message>
+        <source>&amp;About</source>
+        <translation>&amp;Sobre</translation>
+    </message>
+    <message>
+        <source>Info about %1</source>
+        <translation>Informações sobre %1</translation>
+    </message>
+    <message>
+        <source>&amp;Search</source>
+        <translation type="obsolete">&amp;Pesquisar</translation>
+    </message>
+    <message>
+        <source>&amp;Application</source>
+        <translation>&amp;Aplicação</translation>
+    </message>
+    <message>
+        <source>&amp;Playlist</source>
+        <translation>&amp;Playlist</translation>
+    </message>
+    <message>
+        <source>&amp;Video</source>
+        <translation>&amp;Vídeo</translation>
+    </message>
+    <message>
+        <source>&amp;Help</source>
+        <translation>&amp;Ajuda</translation>
+    </message>
+    <message>
+        <source>Opening %1</source>
+        <translation>Abrindo %1</translation>
+    </message>
+    <message>
+        <source>&amp;Play</source>
+        <translation>&amp;Reproduzir</translation>
+    </message>
+    <message>
+        <source>Resume playback</source>
+        <translation>Continuar reprodução</translation>
+    </message>
+    <message>
+        <source>Exit &amp;Full Screen</source>
+        <translation>Sair de &amp;Tela Cheia</translation>
+    </message>
+    <message>
+        <source>&amp;Compact mode</source>
+        <translation>Modo &amp;compacto</translation>
+    </message>
+    <message>
+        <source>Hide the playlist and the toolbar</source>
+        <translation>Ocultar a playlist e a barra de ferramentas</translation>
+    </message>
+    <message>
+        <source>Fatal error: %1</source>
+        <translation>Erro fatal: %1</translation>
+    </message>
+    <message>
+        <source>Error: %1</source>
+        <translation>Erro: %1</translation>
+    </message>
+    <message>
+        <source>Ctrl+M</source>
+        <translation>Ctrl+M</translation>
+    </message>
+    <message>
+        <source>Volume at %1%</source>
+        <translation>Volume a %1%</translation>
+    </message>
+    <message>
+        <source>Volume is muted</source>
+        <translation>Volume está mudo</translation>
+    </message>
+    <message>
+        <source>Volume is unmuted</source>
+        <translation>Volume não está mudo</translation>
+    </message>
+    <message>
+        <source>Search</source>
+        <translation>Pesquisar</translation>
+    </message>
+    <message>
+        <source>Mute volume</source>
+        <translation>Volume mudo</translation>
+    </message>
+    <message>
+        <source>Press %1 to raise the volume, %2 to lower it</source>
+        <translation>Pressione %1 para aumentar o volume, %2 para diminui-lo</translation>
+    </message>
+    <message>
+        <source>Remaining time: %1</source>
+        <translation>Tempo restante: %1</translation>
+    </message>
+    <message>
+        <source>High Definition video is enabled</source>
+        <translation type="obsolete">Vídeo de Alta Definição está habilitado</translation>
+    </message>
+    <message>
+        <source>High Definition video is not enabled</source>
+        <translation type="obsolete">Vídeo de Alta Definição não está habilitado</translation>
+    </message>
+    <message>
+        <source>The current video is in High Definition</source>
+        <translation type="obsolete">O vídeo atual está em Alta Definição</translation>
+    </message>
+    <message>
+        <source>The current video is not in High Definition</source>
+        <translation type="obsolete">O vídeo atual nõ está em Alta Definição</translation>
+    </message>
+    <message>
+        <source>&amp;Clear recent keywords</source>
+        <translation>&amp;Apagar palavras-chave recentes</translation>
+    </message>
+    <message>
+        <source>Clear the search history. Cannot be undone.</source>
+        <translation>Apagar o histórico de pesquisa. Não pode ser desfeito.</translation>
+    </message>
+    <message>
+        <source>Your privacy is now safe</source>
+        <translation>Sua privacidade está agora segura</translation>
+    </message>
+    <message>
+        <source>Open the &amp;YouTube page</source>
+        <translation>Abrir a página do &amp;YouTube</translation>
+    </message>
+    <message>
+        <source>Go to the YouTube video page and pause playback</source>
+        <translation>Ir à página de vídeo do YouTube e pausar a reprodução</translation>
+    </message>
+    <message>
+        <source>Copy the YouTube &amp;link</source>
+        <translation>Copiar o &amp;link do YouTube</translation>
+    </message>
+    <message>
+        <source>Copy the current video YouTube link to the clipboard</source>
+        <translation>Copiar o link do vídeo atual do YouTube para a área de transferência</translation>
+    </message>
+    <message>
+        <source>Copy the video stream &amp;URL</source>
+        <translation>Copiar a &amp;URL do stream do vídeo</translation>
+    </message>
+    <message>
+        <source>Copy the current video stream URL to the clipboard</source>
+        <translation>Copiar a URL do stream do vídeo atual para a área de transferência</translation>
+    </message>
+    <message>
+        <source>Make a &amp;donation</source>
+        <translation>Faça uma &amp;doação</translation>
+    </message>
+    <message>
+        <source>Maximum video definition set to %1</source>
+        <translation>Máxima definição de vídeo definida para %1</translation>
+    </message>
+</context>
+<context>
+    <name>MediaView</name>
+    <message>
+        <source>Most relevant</source>
+        <translation>Mais relevantes</translation>
+    </message>
+    <message>
+        <source>Most recent</source>
+        <translation>Mais recentes</translation>
+    </message>
+    <message>
+        <source>Most viewed</source>
+        <translation>Mais vistos</translation>
+    </message>
+    <message>
+        <source>You&apos;re watching &quot;%1&quot;</source>
+        <translation>Você está assistindo &quot;%1&quot;</translation>
+    </message>
+    <message>
+        <source>You can now paste the YouTube link into another application</source>
+        <translation>Agora você pode colar o link do YouTube em outro programa</translation>
+    </message>
+    <message>
+        <source>You can now paste the video stream URL into another application</source>
+        <translation>Agora você pode colar a URL do stream de vídeo em outro programa</translation>
+    </message>
+    <message>
+        <source>The link will be valid only for a limited time.</source>
+        <translation>O link só será válido por um tempo limitado.</translation>
+    </message>
+</context>
+<context>
+    <name>NetworkAccess</name>
+    <message>
+        <source>Network error: %1</source>
+        <translation>Erro na rede: %1</translation>
+    </message>
+</context>
+<context>
+    <name>PrettyItemDelegate</name>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 exibições</translation>
+    </message>
+</context>
+<context>
+    <name>SearchLineEdit</name>
+    <message>
+        <source>Search</source>
+        <translation>Pesquisar</translation>
+    </message>
+</context>
+<context>
+    <name>SearchView</name>
+    <message>
+        <source>Welcome to &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</source>
+        <translation>Bem-vindo ao &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
+    </message>
+    <message>
+        <source>Enter a keyword to start watching videos.</source>
+        <translation>Digite uma palavra-chave para começar a assistir os vídeos.</translation>
+    </message>
+    <message>
+        <source>Watch</source>
+        <translation>Assistir</translation>
+    </message>
+    <message>
+        <source>Recent keywords</source>
+        <translation>Palavra-chave recente</translation>
+    </message>
+    <message>
+        <source>A new version of %1 is available. Please &lt;a href=&apos;%2&apos;&gt;update to version %3&lt;/a&gt;</source>
+        <translation>Um nova versão de %1 está disponível. Por favor, &lt;a href=&apos;%2&apos;&gt;atualize para a versão %3&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>Make yourself comfortable</source>
+        <translation>Sinta-se confortável</translation>
+    </message>
+</context>
+<context>
+    <name>SettingsView</name>
+    <message>
+        <source>Preferences</source>
+        <translation type="obsolete">Preferências</translation>
+    </message>
+    <message>
+        <source>&amp;Video options</source>
+        <translation type="obsolete">Opções de &amp;vídeo</translation>
+    </message>
+    <message>
+        <source>Use high quality video when available</source>
+        <translation type="obsolete">Utilizar vídeo de alta qualidade quando estiver disponível</translation>
+    </message>
+    <message>
+        <source>&amp;Saved recent keywords</source>
+        <translation type="obsolete">Palavras-chave &amp;salvas recentemente</translation>
+    </message>
+    <message>
+        <source>&amp;Clear recent keywords</source>
+        <translation type="obsolete">Palavras-chave &amp;limpas recentemente</translation>
+    </message>
+    <message>
+        <source>&amp;Close</source>
+        <translation type="obsolete">&amp;Fechar</translation>
+    </message>
+</context>
+<context>
+    <name>Video</name>
+    <message>
+        <source>Network error: %1 for %2</source>
+        <translation>Erro na rede: %1 para %2</translation>
+    </message>
+</context>
+</TS>
diff --git a/locale/pt_PT.ts b/locale/pt_PT.ts
new file mode 100644 (file)
index 0000000..1b7f7ba
--- /dev/null
@@ -0,0 +1,394 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.0" language="pt_PT">
+<defaultcodec>UTF-8</defaultcodec>
+<context>
+    <name>AboutView</name>
+    <message>
+        <source>There&apos;s life outside the browser!</source>
+        <translation>Existe vida para além do navegador!</translation>
+    </message>
+    <message>
+        <source>Version %1</source>
+        <translation>Versão %1</translation>
+    </message>
+    <message>
+        <source>%1 is Free Software but its development takes precious time.</source>
+        <translation>%1 é Software Livre mas o seu desenvolvimento leva tempo precioso.</translation>
+    </message>
+    <message>
+        <source>Please &lt;a href=&apos;%1&apos;&gt;donate&lt;/a&gt; to support the continued development of %2.</source>
+        <translation>Por favor &lt;a href=&apos;%1&apos;&gt;Faça um donativo&lt;/a&gt; para suportar o desenvolvimento continuado do%2.</translation>
+    </message>
+    <message>
+        <source>Report bugs and send in your ideas to %1</source>
+        <translation>Reporte erros e envie as suas ideias para%1</translation>
+    </message>
+    <message>
+        <source>Icon designed by %1.</source>
+        <translation>Icon criado por %1.</translation>
+    </message>
+    <message>
+        <source>Compact mode contributed by %1.</source>
+        <translation>Modo compacto contribuído por%1.</translation>
+    </message>
+    <message>
+        <source>HTTP proxy support contributed by %1.</source>
+        <translation>Suporte a proxy HTTP contribuído por%1.</translation>
+    </message>
+    <message>
+        <source>Translated by %1</source>
+        <translation>Traduzido por %1</translation>
+    </message>
+    <message>
+        <source>Released under the &lt;a href=&apos;%1&apos;&gt;GNU General Public License&lt;/a&gt;</source>
+        <translation>Lançado nos termos da &lt;a href=&apos;&quot;%1&apos;&gt;GNU General Public License&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>&amp;Close</source>
+        <translation>&amp;Fechar</translation>
+    </message>
+    <message>
+        <source>About</source>
+        <translation>Sobre</translation>
+    </message>
+    <message>
+        <source>What you always wanted to know about %1 and never dared to ask</source>
+        <translation>Tudo o que sempre quis saber sobre o %1 e nunca se atreveu a perguntar</translation>
+    </message>
+</context>
+<context>
+    <name>ClearButton</name>
+    <message>
+        <source>Clear</source>
+        <translation>Limpar</translation>
+    </message>
+</context>
+<context>
+    <name>ListModel</name>
+    <message>
+        <source>Searching...</source>
+        <translation>A pesquisar...</translation>
+    </message>
+    <message>
+        <source>Show %1 More</source>
+        <translation>Mostrar mais %1</translation>
+    </message>
+    <message>
+        <source>No videos</source>
+        <translation>Sem vídeos</translation>
+    </message>
+    <message>
+        <source>No more videos</source>
+        <translation>Sem mais vídeos</translation>
+    </message>
+</context>
+<context>
+    <name>LoadingWidget</name>
+    <message>
+        <source>Error</source>
+        <translation>Erro</translation>
+    </message>
+</context>
+<context>
+    <name>MainWindow</name>
+    <message>
+        <source>&amp;Stop</source>
+        <translation>&amp;Parar</translation>
+    </message>
+    <message>
+        <source>Stop playback and go back to the search view</source>
+        <translation>Para a reprodução e voltar à vista de pesquisa</translation>
+    </message>
+    <message>
+        <source>S&amp;kip</source>
+        <translation>S&amp;altar</translation>
+    </message>
+    <message>
+        <source>Skip to the next video</source>
+        <translation>Saltar para o próximo vídeo</translation>
+    </message>
+    <message>
+        <source>&amp;Pause</source>
+        <translation>&amp;Pausar</translation>
+    </message>
+    <message>
+        <source>Pause playback</source>
+        <translation>Pausar reprodução</translation>
+    </message>
+    <message>
+        <source>&amp;Full Screen</source>
+        <translation>Ecrã &amp;Completo</translation>
+    </message>
+    <message>
+        <source>Go full screen</source>
+        <translation>Ir para ecrã completo</translation>
+    </message>
+    <message>
+        <source>&amp;Compact mode</source>
+        <translation>Modo &amp;Compacto</translation>
+    </message>
+    <message>
+        <source>Hide the playlist and the toolbar</source>
+        <translation>Esconder a lista de reprodução e a barra de ferramentas</translation>
+    </message>
+    <message>
+        <source>Open the &amp;YouTube page</source>
+        <translation>Abrir a página do &amp;Youtube</translation>
+    </message>
+    <message>
+        <source>Go to the YouTube video page and pause playback</source>
+        <translation>Ir para a página do Youtube do vídeo e parar a reprodução</translation>
+    </message>
+    <message>
+        <source>Copy the YouTube &amp;link</source>
+        <translation>Copiar o &amp;link do Youtube</translation>
+    </message>
+    <message>
+        <source>Copy the current video YouTube link to the clipboard</source>
+        <translation>Copiar o link do Youtube do vídeo actual para a área de transferência</translation>
+    </message>
+    <message>
+        <source>Copy the video stream &amp;URL</source>
+        <translation>Copiar o &amp;URL da emissão de vídeo</translation>
+    </message>
+    <message>
+        <source>Copy the current video stream URL to the clipboard</source>
+        <translation>Copiar o URL da emissão de vídeo actual para a área de transferência</translation>
+    </message>
+    <message>
+        <source>&amp;Remove</source>
+        <translation>&amp;Remover</translation>
+    </message>
+    <message>
+        <source>Remove the selected videos from the playlist</source>
+        <translation>Remover os vídeos seleccionados da lista de reprodução</translation>
+    </message>
+    <message>
+        <source>Move &amp;Up</source>
+        <translation>Mover para &amp;Cima</translation>
+    </message>
+    <message>
+        <source>Move up the selected videos in the playlist</source>
+        <translation>Mover para cima os vídeos seleccionados na lista de reprodução</translation>
+    </message>
+    <message>
+        <source>Move &amp;Down</source>
+        <translation>Mover para &amp;Baixo</translation>
+    </message>
+    <message>
+        <source>Move down the selected videos in the playlist</source>
+        <translation>Mover para baixo os vídeos seleccionados na lista de reprodução</translation>
+    </message>
+    <message>
+        <source>&amp;Clear recent keywords</source>
+        <translation>&amp;Limpar pesquisas recentes</translation>
+    </message>
+    <message>
+        <source>Clear the search history. Cannot be undone.</source>
+        <translation>Limpar o histório de pesquisa. Não pode ser desfeito.</translation>
+    </message>
+    <message>
+        <source>&amp;Quit</source>
+        <translation>&amp;Sair</translation>
+    </message>
+    <message>
+        <source>Ctrl+Q</source>
+        <translation>Ctrl+Q</translation>
+    </message>
+    <message>
+        <source>Bye</source>
+        <translation>Adeus</translation>
+    </message>
+    <message>
+        <source>&amp;Website</source>
+        <translation>Sítio &amp;Web</translation>
+    </message>
+    <message>
+        <source>%1 on the Web</source>
+        <translation>%1 na Internet</translation>
+    </message>
+    <message>
+        <source>Make a &amp;donation</source>
+        <translation>Fazer uma &amp;doação</translation>
+    </message>
+    <message>
+        <source>Please support the continued development of %1</source>
+        <translation>Por favor suporte o desenvolvimento continuado do %1</translation>
+    </message>
+    <message>
+        <source>&amp;About</source>
+        <translation>&amp;Sobre</translation>
+    </message>
+    <message>
+        <source>Info about %1</source>
+        <translation>Informação sobre %1</translation>
+    </message>
+    <message>
+        <source>Search</source>
+        <translation>Pesquisar</translation>
+    </message>
+    <message>
+        <source>Mute volume</source>
+        <translation>Silenciar o volume</translation>
+    </message>
+    <message>
+        <source>Ctrl+M</source>
+        <translation>Ctrl+M</translation>
+    </message>
+    <message>
+        <source>&amp;Application</source>
+        <translation>&amp;Aplicação</translation>
+    </message>
+    <message>
+        <source>&amp;Playlist</source>
+        <translation>&amp;Lista de reprodução</translation>
+    </message>
+    <message>
+        <source>&amp;Video</source>
+        <translation>&amp;Vídeo</translation>
+    </message>
+    <message>
+        <source>&amp;Help</source>
+        <translation>&amp;Ajuda</translation>
+    </message>
+    <message>
+        <source>Press %1 to raise the volume, %2 to lower it</source>
+        <translation>Prima %1 para aumentar o volume, %2 para reduzi-lo</translation>
+    </message>
+    <message>
+        <source>Opening %1</source>
+        <translation>A abrir %1</translation>
+    </message>
+    <message>
+        <source>Fatal error: %1</source>
+        <translation>Erro fatal:%1</translation>
+    </message>
+    <message>
+        <source>Error: %1</source>
+        <translation>Erro:%1</translation>
+    </message>
+    <message>
+        <source>&amp;Play</source>
+        <translation>&amp;Reproduzir</translation>
+    </message>
+    <message>
+        <source>Resume playback</source>
+        <translation>Retomar a reprodução</translation>
+    </message>
+    <message>
+        <source>Exit &amp;Full Screen</source>
+        <translation>Sair do Ecrã &amp;Completo</translation>
+    </message>
+    <message>
+        <source>Remaining time: %1</source>
+        <translation>Tempo restante:%1</translation>
+    </message>
+    <message>
+        <source>Volume at %1%</source>
+        <translation>Volume a :%1%</translation>
+    </message>
+    <message>
+        <source>Volume is muted</source>
+        <translation>O volume está silenciado</translation>
+    </message>
+    <message>
+        <source>Volume is unmuted</source>
+        <translation>O volume não está silenciado</translation>
+    </message>
+    <message>
+        <source>Maximum video definition set to %1</source>
+        <translation>Definição de vídeo máxima estabelecida em %1</translation>
+    </message>
+    <message>
+        <source>Your privacy is now safe</source>
+        <translation>A sua privacidade está agora assegurada</translation>
+    </message>
+</context>
+<context>
+    <name>MediaView</name>
+    <message>
+        <source>Most relevant</source>
+        <translation>Mais relevantes</translation>
+    </message>
+    <message>
+        <source>Most recent</source>
+        <translation>Mais recentes</translation>
+    </message>
+    <message>
+        <source>Most viewed</source>
+        <translation>Mais vistos</translation>
+    </message>
+    <message>
+        <source>You can now paste the YouTube link into another application</source>
+        <translation>Pode agora colar o link do Youtube noutra aplicação</translation>
+    </message>
+    <message>
+        <source>You can now paste the video stream URL into another application</source>
+        <translation>Pode agora colar o URL da emissão de vídeo noutra aplicação</translation>
+    </message>
+    <message>
+        <source>The link will be valid only for a limited time.</source>
+        <translation>O link será válido apenas por algum tempo.</translation>
+    </message>
+    <message>
+        <source>You&apos;re watching &quot;%1&quot;</source>
+        <translation>Está a assistir a &quot;%1&quot;</translation>
+    </message>
+</context>
+<context>
+    <name>NetworkAccess</name>
+    <message>
+        <source>Network error: %1</source>
+        <translation>Erro de rede: %1</translation>
+    </message>
+</context>
+<context>
+    <name>PrettyItemDelegate</name>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 visualizações</translation>
+    </message>
+</context>
+<context>
+    <name>SearchLineEdit</name>
+    <message>
+        <source>Search</source>
+        <translation>Pesquisar</translation>
+    </message>
+</context>
+<context>
+    <name>SearchView</name>
+    <message>
+        <source>Welcome to &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</source>
+        <translation>Bem-vindo ao &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>Enter a keyword to start watching videos.</source>
+        <translation>Insira uma pesquisa para começar a assitir a vídeos.</translation>
+    </message>
+    <message>
+        <source>Watch</source>
+        <translation>Assistir</translation>
+    </message>
+    <message>
+        <source>Recent keywords</source>
+        <translation>Pesquisas recentes</translation>
+    </message>
+    <message>
+        <source>A new version of %1 is available. Please &lt;a href=&apos;%2&apos;&gt;update to version %3&lt;/a&gt;</source>
+        <translation>Uma nova versão do %1 está disponível. Por favor &lt;a href=&apos;%2&apos;&gt;actualize para a versão %3&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>Make yourself comfortable</source>
+        <translation>Acomode-se confortavelmente</translation>
+    </message>
+</context>
+<context>
+    <name>Video</name>
+    <message>
+        <source>Network error: %1 for %2</source>
+        <translation>Erro de rede: %1 para %2</translation>
+    </message>
+</context>
+</TS>
diff --git a/locale/ro_RO.ts b/locale/ro_RO.ts
new file mode 100644 (file)
index 0000000..d8f2ed3
--- /dev/null
@@ -0,0 +1,449 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.0" language="ro_RO">
+<defaultcodec>UTF-8</defaultcodec>
+<context>
+    <name>AboutView</name>
+    <message>
+        <source>There&apos;s life outside the browser!</source>
+        <translation>Există viaţă dincolo de browser!</translation>
+    </message>
+    <message>
+        <source>Version %1</source>
+        <translation>Versiunea %1</translation>
+    </message>
+    <message>
+        <source>This is a &quot;Technology Preview&quot; release, do not expect it to be perfect.</source>
+        <translation type="obsolete">Această versiune este doar pentru o &quot;impresie tehnlogică&quot;, nu te aştepta să funcţioneze perfect.</translation>
+    </message>
+    <message>
+        <source>Report bugs and send in your ideas to %1</source>
+        <translation>Raportează erori şi trimite ideile tale la %1</translation>
+    </message>
+    <message>
+        <source>%1 is Free Software but its development takes precious time.</source>
+        <translation>%1 este un program gratuit, dar dezvoltarea acestuia necesită timp preţios.</translation>
+    </message>
+    <message>
+        <source>Please &lt;a href=&apos;%1&apos;&gt;donate via PayPal&lt;/a&gt; to support the continued development of %2.</source>
+        <translation type="obsolete">Te gog &lt;a href=&apos;%1&apos;&gt;donează o sumă modestă&lt;/a&gt; pentru a ajuta dezvoltarea %2.</translation>
+    </message>
+    <message>
+        <source>Icon designed by %1.</source>
+        <translation>Iconul a fost conceput de %1.</translation>
+    </message>
+    <message>
+        <source>Compact mode contributed by %1.</source>
+        <translation>Modul compact a fost dezvoltat de %1.</translation>
+    </message>
+    <message>
+        <source>HTTP proxy support contributed by %1.</source>
+        <translation>Suportul pentru proxy-uri HTTP a fost dezvoltat de %1.</translation>
+    </message>
+    <message>
+        <source>Windows version built by %1</source>
+        <translation type="obsolete">Version Windows faite par %1</translation>
+    </message>
+    <message>
+        <source>Translated by %1</source>
+        <translation>Tradus de %1</translation>
+    </message>
+    <message>
+        <source>Released under the &lt;a href=&apos;%1&apos;&gt;GNU General Public License&lt;/a&gt;</source>
+        <translation>Oferit sub &lt;a href=&apos;%1&apos;&gt;Licenţa Publică Generală GNU&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>&amp;Close</source>
+        <translation>În&amp;chide</translation>
+    </message>
+    <message>
+        <source>About</source>
+        <translation>Despre</translation>
+    </message>
+    <message>
+        <source>What you always wanted to know about %1 and never dared to ask</source>
+        <translation>Tot ce ai vrut să ştii despre %1 dar n-ai vrut să îndrăzneşti a întreba</translation>
+    </message>
+    <message>
+        <source>Please &lt;a href=&apos;%1&apos;&gt;donate&lt;/a&gt; to support the continued development of %2.</source>
+        <translation>Vă rugăm să &lt;a href=&apos;%1&apos;&gt;donaţi&lt;/a&gt; pentru a ajuta la continuarea dezvoltării %2.</translation>
+    </message>
+</context>
+<context>
+    <name>ClearButton</name>
+    <message>
+        <source>Clear</source>
+        <translation>Şterge</translation>
+    </message>
+</context>
+<context>
+    <name>ListModel</name>
+    <message>
+        <source>Searching...</source>
+        <translation>Căutare...</translation>
+    </message>
+    <message>
+        <source>Show %1 More</source>
+        <translation>Afişează încă %1</translation>
+    </message>
+    <message>
+        <source>No videos</source>
+        <translation>Nu sunt videoclipuri</translation>
+    </message>
+    <message>
+        <source>No more videos</source>
+        <translation>Nu mai sunt videoclipuri</translation>
+    </message>
+</context>
+<context>
+    <name>LoadingWidget</name>
+    <message>
+        <source>Error</source>
+        <translation>Eroare</translation>
+    </message>
+</context>
+<context>
+    <name>MainWindow</name>
+    <message>
+        <source>&amp;Back</source>
+        <translation type="obsolete">Îna&amp;poi</translation>
+    </message>
+    <message>
+        <source>Go to the previous view</source>
+        <translation type="obsolete">Întoarcere</translation>
+    </message>
+    <message>
+        <source>&amp;Stop</source>
+        <translation>&amp;Stop</translation>
+    </message>
+    <message>
+        <source>Stop playback and go back to the search view</source>
+        <translation>Opreşte redarea şi întoarce-te la căsuţa de căutare</translation>
+    </message>
+    <message>
+        <source>S&amp;kip</source>
+        <translation>O&amp;mite</translation>
+    </message>
+    <message>
+        <source>Skip to the next video</source>
+        <translation>Treci la videoclipul următor</translation>
+    </message>
+    <message>
+        <source>&amp;Pause</source>
+        <translation>&amp;Pauză</translation>
+    </message>
+    <message>
+        <source>Pause playback</source>
+        <translation>Pune redarea pe pauză</translation>
+    </message>
+    <message>
+        <source>&amp;Full Screen</source>
+        <translation>Ec&amp;ran complet</translation>
+    </message>
+    <message>
+        <source>Go full screen</source>
+        <translation>Schimbă modul de vizualizare pe tot ecranul</translation>
+    </message>
+    <message>
+        <source>&amp;Compact mode</source>
+        <translation>Mod &amp;compact</translation>
+    </message>
+    <message>
+        <source>Hide the playlist and the toolbar</source>
+        <translation>Ascunde lista de redare şi bara de unelte</translation>
+    </message>
+    <message>
+        <source>&amp;YouTube</source>
+        <translation type="obsolete">&amp;Youtube</translation>
+    </message>
+    <message>
+        <source>Open the YouTube video page</source>
+        <translation type="obsolete">Deschide pagina YouTube</translation>
+    </message>
+    <message>
+        <source>&amp;Remove</source>
+        <translation>Ş&amp;terge</translation>
+    </message>
+    <message>
+        <source>Remove the selected videos from the playlist</source>
+        <translation>Şterge clipul selectat din lista de redare</translation>
+    </message>
+    <message>
+        <source>Move &amp;Up</source>
+        <translation>Mută în &amp;sus</translation>
+    </message>
+    <message>
+        <source>Move up the selected videos in the playlist</source>
+        <translation>Mută în sus videoclipul selectat</translation>
+    </message>
+    <message>
+        <source>Move &amp;Down</source>
+        <translation>Mută în &amp;jos</translation>
+    </message>
+    <message>
+        <source>Move down the selected videos in the playlist</source>
+        <translation>Mută în jos videoclipul selectat</translation>
+    </message>
+    <message>
+        <source>&amp;Quit</source>
+        <translation>&amp;Ieşire</translation>
+    </message>
+    <message>
+        <source>Ctrl+Q</source>
+        <translation>Ctrl+Q</translation>
+    </message>
+    <message>
+        <source>Bye</source>
+        <translation>La revedere</translation>
+    </message>
+    <message>
+        <source>&amp;Website</source>
+        <translation>Site &amp;Web</translation>
+    </message>
+    <message>
+        <source>%1 on the Web</source>
+        <translation>%1 pe Web</translation>
+    </message>
+    <message>
+        <source>&amp;Donate via PayPal</source>
+        <translation type="obsolete">&amp;Donează prin PayPal</translation>
+    </message>
+    <message>
+        <source>Please support the continued development of %1</source>
+        <translation>Te rog să susţii dezvoltarea aplicaţiei %1</translation>
+    </message>
+    <message>
+        <source>&amp;About</source>
+        <translation>&amp;Despre</translation>
+    </message>
+    <message>
+        <source>Info about %1</source>
+        <translation>Informaţii despre %1</translation>
+    </message>
+    <message>
+        <source>Search</source>
+        <translation>Căutare</translation>
+    </message>
+    <message>
+        <source>Mute volume</source>
+        <translation>Oprire sunet</translation>
+    </message>
+    <message>
+        <source>Ctrl+M</source>
+        <translation>Ctrl+M</translation>
+    </message>
+    <message>
+        <source>&amp;Playlist</source>
+        <translation>&amp;Listă de redare</translation>
+    </message>
+    <message>
+        <source>&amp;Video</source>
+        <translation>&amp;Video</translation>
+    </message>
+    <message>
+        <source>&amp;Help</source>
+        <translation>&amp;Ajutor</translation>
+    </message>
+    <message>
+        <source>Press %1 to raise the volume, %2 to lower it</source>
+        <translation>Apasă pe %1 pentru a da volumul mai tare sau pe %2 pentru a-l da mai încet</translation>
+    </message>
+    <message>
+        <source>Opening %1</source>
+        <translation>Deschidere %1</translation>
+    </message>
+    <message>
+        <source>Fatal error: %1</source>
+        <translation>Eroare fatală : %1</translation>
+    </message>
+    <message>
+        <source>Error: %1</source>
+        <translation>Eroare: %1</translation>
+    </message>
+    <message>
+        <source>&amp;Play</source>
+        <translation>&amp;Redare</translation>
+    </message>
+    <message>
+        <source>Resume playback</source>
+        <translation>Rezumă redarea videoclipului</translation>
+    </message>
+    <message>
+        <source>Exit &amp;Full Screen</source>
+        <translation>Părăseşte modul &quot;Ec&amp;ran complet&quot;</translation>
+    </message>
+    <message>
+        <source>Remaining time: %1</source>
+        <translation>Timp rămas : %1</translation>
+    </message>
+    <message>
+        <source>Volume at %1%</source>
+        <translation>Volum %1</translation>
+    </message>
+    <message>
+        <source>Volume is muted</source>
+        <translation>Sunetul este oprit</translation>
+    </message>
+    <message>
+        <source>Volume is unmuted</source>
+        <translation>Sunetul este pornit</translation>
+    </message>
+    <message>
+        <source>High Definition video is enabled</source>
+        <translation type="obsolete">Modul Înaltă Definiţie este activ</translation>
+    </message>
+    <message>
+        <source>High Definition video is not enabled</source>
+        <translation type="obsolete">Modul Înaltă Definiţie nu este activ</translation>
+    </message>
+    <message>
+        <source>The current video is in High Definition</source>
+        <translation type="obsolete">Videoclipul curent este în format Înaltă Definiţie</translation>
+    </message>
+    <message>
+        <source>The current video is not in High Definition</source>
+        <translation type="obsolete">Videoclipul curent nu este în format Înaltă Definiţie</translation>
+    </message>
+    <message>
+        <source>&amp;Clear recent keywords</source>
+        <translation>Şterge &amp;căutările recente</translation>
+    </message>
+    <message>
+        <source>&amp;Application</source>
+        <translation>&amp;Aplicaţie</translation>
+    </message>
+    <message>
+        <source>Clear the search history. Cannot be undone.</source>
+        <translation>Curăţă istoricul de căutări. Această acţiune nu poate fi anulată.</translation>
+    </message>
+    <message>
+        <source>Your privacy is now safe</source>
+        <translation>Intimitatea dumneavoastră este acum în siguranţă</translation>
+    </message>
+    <message>
+        <source>Open the &amp;YouTube page</source>
+        <translation>Deschide pagina &amp;YouTube</translation>
+    </message>
+    <message>
+        <source>Go to the YouTube video page and pause playback</source>
+        <translation>Deschide pagina de pe YouTube si pune pauză</translation>
+    </message>
+    <message>
+        <source>Copy the YouTube &amp;link</source>
+        <translation>Copiază &amp;link-ul de YouTube</translation>
+    </message>
+    <message>
+        <source>Copy the current video YouTube link to the clipboard</source>
+        <translation>Copiază link-ul videoclipului curent în clipboard</translation>
+    </message>
+    <message>
+        <source>Copy the video stream &amp;URL</source>
+        <translation>Copiază adresa streamul&amp;ui</translation>
+    </message>
+    <message>
+        <source>Copy the current video stream URL to the clipboard</source>
+        <translation>Copiază adresa stream-ului în clipboard</translation>
+    </message>
+    <message>
+        <source>Make a &amp;donation</source>
+        <translation>&amp;Donează</translation>
+    </message>
+    <message>
+        <source>Maximum video definition set to %1</source>
+        <translation>Rezoluţia maxima a videoclipului este setată la %1</translation>
+    </message>
+</context>
+<context>
+    <name>MediaView</name>
+    <message>
+        <source>Most relevant</source>
+        <translation>Cele mai relevante</translation>
+    </message>
+    <message>
+        <source>Most recent</source>
+        <translation>Cele mai recente</translation>
+    </message>
+    <message>
+        <source>Most viewed</source>
+        <translation>Cele mai vizualizate</translation>
+    </message>
+    <message>
+        <source>You&apos;re watching &quot;%1&quot;</source>
+        <translation>Te uiţi la %1</translation>
+    </message>
+    <message>
+        <source>You can now paste the YouTube link into another application</source>
+        <translation>Acum poţi lipi link-ul într-o altă aplicaţie</translation>
+    </message>
+    <message>
+        <source>You can now paste the video stream URL into another application</source>
+        <translation>Acum poţi lipi link-ul stream-ului într-o altă aplicaţie</translation>
+    </message>
+    <message>
+        <source>The link will be valid only for a limited time.</source>
+        <translation>Link-ul va fi valid doar pentru o perioadă limitată de timp.</translation>
+    </message>
+</context>
+<context>
+    <name>NetworkAccess</name>
+    <message>
+        <source>Network error: %1</source>
+        <translation>Eroare de reţea : %1</translation>
+    </message>
+</context>
+<context>
+    <name>PrettyItemDelegate</name>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 vizualizări</translation>
+    </message>
+</context>
+<context>
+    <name>SearchLineEdit</name>
+    <message>
+        <source>Search</source>
+        <translation>Căutare</translation>
+    </message>
+</context>
+<context>
+    <name>SearchView</name>
+    <message>
+        <source>Welcome to &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</source>
+        <translation>Bine aţi venit la &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
+    </message>
+    <message>
+        <source>Enter a keyword to start watching videos.</source>
+        <translation>Efectuaţi o căutare pentru a începe să vizionaţi videoclipuri.</translation>
+    </message>
+    <message>
+        <source>Watch</source>
+        <translation>Urmăreşte</translation>
+    </message>
+    <message>
+        <source>Recent keywords</source>
+        <translation>Căutări recente</translation>
+    </message>
+    <message>
+        <source>A new version of %1 is available. Please &lt;a href=&apos;%2&apos;&gt;update to version %3&lt;/a&gt;</source>
+        <translation>Este disponibilă o nouă versiune a %1. Te rog &lt;a href=&apos;%2&apos;&gt;să actualizezi&lt;/a&gt; aplicaţia la versiunea %3</translation>
+    </message>
+    <message>
+        <source>Make yourself comfortable</source>
+        <translation>Faceţi-vă confortabil</translation>
+    </message>
+</context>
+<context>
+    <name>SettingsView</name>
+    <message>
+        <source>Preferences</source>
+        <translation type="obsolete">Preferinţe</translation>
+    </message>
+</context>
+<context>
+    <name>Video</name>
+    <message>
+        <source>Network error: %1 for %2</source>
+        <translation>Eroare reţea: %1 pentru %2</translation>
+    </message>
+</context>
+</TS>
diff --git a/locale/ru_RU.ts b/locale/ru_RU.ts
new file mode 100644 (file)
index 0000000..bf0f394
--- /dev/null
@@ -0,0 +1,509 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.0" language="ru_RU">
+<defaultcodec>UTF-8</defaultcodec>
+<context>
+    <name>AboutView</name>
+    <message>
+        <source>There&apos;s life outside the browser!</source>
+        <translation>Жизнь за пределами браузера!</translation>
+    </message>
+    <message>
+        <source>Version %1</source>
+        <translation>Версия %1</translation>
+    </message>
+    <message>
+        <source>This is a &quot;Technology Preview&quot; release, do not expect it to be perfect.</source>
+        <translation type="obsolete">Эта предварительная версия, не стоит ожидать совершенства.</translation>
+    </message>
+    <message>
+        <source>Report bugs and send in your ideas to %1</source>
+        <translation>Отзывы и сообщения об ошибках следует отправлять на %1</translation>
+    </message>
+    <message>
+        <source>%1 is Free Software but its development takes precious time.</source>
+        <translation>%1 - свободное ПО, но его разработка отнимает драгоценное время.</translation>
+    </message>
+    <message>
+        <source>Please &lt;a href=&apos;%1&apos;&gt;donate via PayPal&lt;/a&gt; to support the continued development of %2.</source>
+        <translation type="obsolete">&lt;a href=&apos;%1&apos;&gt;Поддержите через PayPal&lt;/a&gt; дальнейшую разработку %2.</translation>
+    </message>
+    <message>
+        <source>Icon designed by %1.</source>
+        <translation>Автор значка %1.</translation>
+    </message>
+    <message>
+        <source>Translated by %1</source>
+        <translation>Перевод выполнили: %1</translation>
+    </message>
+    <message>
+        <source>Released under the &lt;a href=&apos;%1&apos;&gt;GNU General Public License&lt;/a&gt;</source>
+        <translation>Выпущено на условиях &lt;a href=&apos;%1&apos;&gt;GNU General Public License&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>&amp;Close</source>
+        <translation>&amp;Закрыть</translation>
+    </message>
+    <message>
+        <source>About</source>
+        <translation>О программе</translation>
+    </message>
+    <message>
+        <source>What you always wanted to know about %1 and never dared to ask</source>
+        <translation>Все что Вы всегда хотели узнать о %1 и никогда бы не спросили</translation>
+    </message>
+    <message>
+        <source>Compact mode contributed by %1.</source>
+        <translation>Облегчённый режим предоставлен %1.</translation>
+    </message>
+    <message>
+        <source>HTTP proxy support contributed by %1.</source>
+        <translation>Поддержка HTTP-прокси предоставлена %1.</translation>
+    </message>
+    <message>
+        <source>Windows version built by %1</source>
+        <translation type="obsolete">Сборку под Windows выполнил %1</translation>
+    </message>
+    <message>
+        <source>Please &lt;a href=&apos;%1&apos;&gt;donate&lt;/a&gt; to support the continued development of %2.</source>
+        <translation>&lt;a href=&apos;%1&apos;&gt;Поддержите&lt;/a&gt; дальнейшую разработку %2.</translation>
+    </message>
+</context>
+<context>
+    <name>ClearButton</name>
+    <message>
+        <source>Clear</source>
+        <translation>Очистить</translation>
+    </message>
+</context>
+<context>
+    <name>ListModel</name>
+    <message>
+        <source>Searching...</source>
+        <translation>Поиск...</translation>
+    </message>
+    <message>
+        <source>Show %1 More</source>
+        <translation>Показать больше %1</translation>
+    </message>
+    <message>
+        <source>No videos</source>
+        <translation>Клипы не найдены</translation>
+    </message>
+    <message>
+        <source>No more videos</source>
+        <translation>Больше нет клипов</translation>
+    </message>
+</context>
+<context>
+    <name>LoadingWidget</name>
+    <message>
+        <source>Error</source>
+        <translation>Ошибка</translation>
+    </message>
+</context>
+<context>
+    <name>MainWindow</name>
+    <message>
+        <source>&amp;Back</source>
+        <translation type="obsolete">&amp;Назад</translation>
+    </message>
+    <message>
+        <source>Alt+Left</source>
+        <translation type="obsolete">Alt+стрелка назад</translation>
+    </message>
+    <message>
+        <source>Go to the previous view</source>
+        <translation type="obsolete">Перейти к предыдущему</translation>
+    </message>
+    <message>
+        <source>&amp;Stop</source>
+        <translation>&amp;Остановить</translation>
+    </message>
+    <message>
+        <source>Stop playback and go back to the search view</source>
+        <translation>Остановить воспроизведение и вернуться к поиску</translation>
+    </message>
+    <message>
+        <source>Esc</source>
+        <translation type="obsolete">Esc</translation>
+    </message>
+    <message>
+        <source>S&amp;kip</source>
+        <translation>П&amp;ропустить</translation>
+    </message>
+    <message>
+        <source>Skip to the next video</source>
+        <translation>Перейти к следующему клипу</translation>
+    </message>
+    <message>
+        <source>Ctrl+Right</source>
+        <translation type="obsolete">Ctrl+стрелка вправо</translation>
+    </message>
+    <message>
+        <source>&amp;Pause</source>
+        <translation>&amp;Приостановить</translation>
+    </message>
+    <message>
+        <source>Pause playback</source>
+        <translation>Приостановить воспроизведение</translation>
+    </message>
+    <message>
+        <source>Space</source>
+        <translation type="obsolete">Пробел</translation>
+    </message>
+    <message>
+        <source>&amp;Full Screen</source>
+        <translation>&amp;На весь экран</translation>
+    </message>
+    <message>
+        <source>Go full screen</source>
+        <translation>Полноэкранное воспроизведение</translation>
+    </message>
+    <message>
+        <source>Alt+Return</source>
+        <translation type="obsolete">Alt+Enter</translation>
+    </message>
+    <message>
+        <source>&amp;YouTube</source>
+        <translation type="obsolete">&amp;YouTube</translation>
+    </message>
+    <message>
+        <source>Open the YouTube video page</source>
+        <translation type="obsolete">Открыть страницу видео в YouTube</translation>
+    </message>
+    <message>
+        <source>Ctrl+Y</source>
+        <translation type="obsolete">Ctrl+Y</translation>
+    </message>
+    <message>
+        <source>&amp;Remove</source>
+        <translation>&amp;Удалить</translation>
+    </message>
+    <message>
+        <source>Remove the selected videos from the playlist</source>
+        <translation>Удалить выбранные клипы из списка воспроизведения</translation>
+    </message>
+    <message>
+        <source>Move &amp;Up</source>
+        <translation>В&amp;верх</translation>
+    </message>
+    <message>
+        <source>Move up the selected videos in the playlist</source>
+        <translation>Переместить выбранные видеоклипы вверх в списке воспроизведения</translation>
+    </message>
+    <message>
+        <source>Ctrl+Up</source>
+        <translation type="obsolete">Ctrl+стрелка вверх</translation>
+    </message>
+    <message>
+        <source>Move &amp;Down</source>
+        <translation>В&amp;низ</translation>
+    </message>
+    <message>
+        <source>Move down the selected videos in the playlist</source>
+        <translation>Переместить выбранные видеоклипы вниз в списке воспроизведения</translation>
+    </message>
+    <message>
+        <source>Ctrl+Down</source>
+        <translation type="obsolete">Ctrl+стрелка вниз</translation>
+    </message>
+    <message>
+        <source>&amp;Quit</source>
+        <translation>&amp;Выход</translation>
+    </message>
+    <message>
+        <source>Ctrl+Q</source>
+        <translation>Ctrl+Q</translation>
+    </message>
+    <message>
+        <source>Bye</source>
+        <translation>Пока</translation>
+    </message>
+    <message>
+        <source>&amp;Website</source>
+        <translation>&amp;Домашняя страница</translation>
+    </message>
+    <message>
+        <source>Minitube on the Web</source>
+        <translation type="obsolete">Minitube в интернете</translation>
+    </message>
+    <message>
+        <source>%1 on the Web</source>
+        <translation>%1 в интернете</translation>
+    </message>
+    <message>
+        <source>&amp;Donate via PayPal</source>
+        <translation type="obsolete">&amp;Поддержать через PayPal</translation>
+    </message>
+    <message>
+        <source>Please support the continued development of %1</source>
+        <translation>Поддержите дальнейшую разработку %1</translation>
+    </message>
+    <message>
+        <source>&amp;About</source>
+        <translation>&amp;О программе</translation>
+    </message>
+    <message>
+        <source>Info about %1</source>
+        <translation>Сведения о %1</translation>
+    </message>
+    <message>
+        <source>&amp;Search</source>
+        <translation type="obsolete">&amp;Поиск</translation>
+    </message>
+    <message>
+        <source>&amp;Application</source>
+        <translation>Пр&amp;иложение</translation>
+    </message>
+    <message>
+        <source>&amp;Playlist</source>
+        <translation>&amp;Список воспроизведения</translation>
+    </message>
+    <message>
+        <source>&amp;Video</source>
+        <translation>&amp;Клипы</translation>
+    </message>
+    <message>
+        <source>&amp;Help</source>
+        <translation>Спр&amp;авка</translation>
+    </message>
+    <message>
+        <source>Opening %1</source>
+        <translation>Открытие %1</translation>
+    </message>
+    <message>
+        <source>&amp;Play</source>
+        <translation>Пр&amp;оиграть</translation>
+    </message>
+    <message>
+        <source>Resume playback</source>
+        <translation>Продолжить воспроизведение</translation>
+    </message>
+    <message>
+        <source>Exit &amp;Full Screen</source>
+        <translation>В&amp;ыйти из полноэкранного воспроизведения</translation>
+    </message>
+    <message>
+        <source>&amp;Compact mode</source>
+        <translation>&amp;Облегчённый режим</translation>
+    </message>
+    <message>
+        <source>Hide the playlist and the toolbar</source>
+        <translation>Скрыть список воспроизведения и панель инструментов</translation>
+    </message>
+    <message>
+        <source>Fatal error: %1</source>
+        <translation>Фатальная ошибка: %1</translation>
+    </message>
+    <message>
+        <source>Error: %1</source>
+        <translation>Ошибка: %1</translation>
+    </message>
+    <message>
+        <source>Ctrl+M</source>
+        <translation>Ctrl+M</translation>
+    </message>
+    <message>
+        <source>Volume at %1%</source>
+        <translation>Громкость %1%</translation>
+    </message>
+    <message>
+        <source>Volume is muted</source>
+        <translation>Звук выключен</translation>
+    </message>
+    <message>
+        <source>Volume is unmuted</source>
+        <translation>Звук включен</translation>
+    </message>
+    <message>
+        <source>Search</source>
+        <translation>Поиск</translation>
+    </message>
+    <message>
+        <source>Mute volume</source>
+        <translation>Выключить звук</translation>
+    </message>
+    <message>
+        <source>Press %1 to raise the volume, %2 to lower it</source>
+        <translation>Нажмите %1 для увеличения громкости и %2 для снижения</translation>
+    </message>
+    <message>
+        <source>Remaining time: %1</source>
+        <translation>Времени осталось: %1</translation>
+    </message>
+    <message>
+        <source>High Definition video is enabled</source>
+        <translation type="obsolete">Показ видео в высоком разрешении включен</translation>
+    </message>
+    <message>
+        <source>High Definition video is not enabled</source>
+        <translation type="obsolete">Показ видео в высоком разрешении выключен</translation>
+    </message>
+    <message>
+        <source>The current video is in High Definition</source>
+        <translation type="obsolete">Видео воспроизводится в высоком разрешении</translation>
+    </message>
+    <message>
+        <source>The current video is not in High Definition</source>
+        <translation type="obsolete">Видео воспроизводится не в высоком разрешении</translation>
+    </message>
+    <message>
+        <source>&amp;Clear recent keywords</source>
+        <translation>&amp;Очистить последние запросы</translation>
+    </message>
+    <message>
+        <source>Clear the search history. Cannot be undone.</source>
+        <translation>Очистить журнал запросов. Изменения необратимы.</translation>
+    </message>
+    <message>
+        <source>Your privacy is now safe</source>
+        <translation>Конфиденциальность обеспечена</translation>
+    </message>
+    <message>
+        <source>Open the &amp;YouTube page</source>
+        <translation>Открыть страницу &amp;YouTube</translation>
+    </message>
+    <message>
+        <source>Go to the YouTube video page and pause playback</source>
+        <translation>Приостановить воспроизведение и перейти на страницу видео в YouTube</translation>
+    </message>
+    <message>
+        <source>Copy the YouTube &amp;link</source>
+        <translation>Копировать &amp;ссылку YouTube</translation>
+    </message>
+    <message>
+        <source>Copy the current video YouTube link to the clipboard</source>
+        <translation>Скопировать ссылку текущего клипа YouTube в буфер обмена</translation>
+    </message>
+    <message>
+        <source>Copy the video stream &amp;URL</source>
+        <translation>Копировать &amp;адрес видео потока</translation>
+    </message>
+    <message>
+        <source>Copy the current video stream URL to the clipboard</source>
+        <translation>Скопировать адрес текущего видео потока в буфер обмена</translation>
+    </message>
+    <message>
+        <source>Make a &amp;donation</source>
+        <translation>Сделать &amp;пожертвование</translation>
+    </message>
+    <message>
+        <source>Maximum video definition set to %1</source>
+        <translation>Максимальное разрешение видео: %1</translation>
+    </message>
+</context>
+<context>
+    <name>MediaView</name>
+    <message>
+        <source>Most relevant</source>
+        <translation>Похожие видео</translation>
+    </message>
+    <message>
+        <source>Most recent</source>
+        <translation>Недавно просмотренные</translation>
+    </message>
+    <message>
+        <source>Most viewed</source>
+        <translation>Самые популярные</translation>
+    </message>
+    <message>
+        <source>You&apos;re watching &quot;%1&quot;</source>
+        <translation>Сейчас просматривается &quot;%1&quot;</translation>
+    </message>
+    <message>
+        <source>You can now paste the YouTube link into another application</source>
+        <translation>Теперь можно вставить адрес YouTube в другое приложение</translation>
+    </message>
+    <message>
+        <source>You can now paste the video stream URL into another application</source>
+        <translation>Теперь можно вставить адрес видео потока в другое приложение</translation>
+    </message>
+    <message>
+        <source>The link will be valid only for a limited time.</source>
+        <translation>Адрес будет существовать ограниченное время.</translation>
+    </message>
+</context>
+<context>
+    <name>NetworkAccess</name>
+    <message>
+        <source>Network error: %1</source>
+        <translation>Ошибка сети: %1</translation>
+    </message>
+</context>
+<context>
+    <name>PrettyItemDelegate</name>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 просмотров</translation>
+    </message>
+</context>
+<context>
+    <name>SearchLineEdit</name>
+    <message>
+        <source>Search</source>
+        <translation>Поиск</translation>
+    </message>
+</context>
+<context>
+    <name>SearchView</name>
+    <message>
+        <source>Welcome to &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</source>
+        <translation>Добро пожаловать в &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
+    </message>
+    <message>
+        <source>Enter a keyword to start watching videos.</source>
+        <translation>Введите ключевые слова для начала просмотра видео.</translation>
+    </message>
+    <message>
+        <source>Watch</source>
+        <translation>Смотреть</translation>
+    </message>
+    <message>
+        <source>Recent keywords</source>
+        <translation>Последние запросы</translation>
+    </message>
+    <message>
+        <source>A new version of %1 is available. Please &lt;a href=&apos;%2&apos;&gt;update to version %3&lt;/a&gt;</source>
+        <translation>Доступна новая версия %1. &lt;a href=&apos;%2&apos;&gt;Обновите до %3&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>Make yourself comfortable</source>
+        <translation>Чувствуйте себя как дома</translation>
+    </message>
+</context>
+<context>
+    <name>SettingsView</name>
+    <message>
+        <source>Preferences</source>
+        <translation type="obsolete">Настройки</translation>
+    </message>
+    <message>
+        <source>&amp;Video options</source>
+        <translation type="obsolete">&amp;Параметры видео</translation>
+    </message>
+    <message>
+        <source>Use high quality video when available</source>
+        <translation type="obsolete">Использовать по возможности видео высокого качества</translation>
+    </message>
+    <message>
+        <source>&amp;Saved recent keywords</source>
+        <translation type="obsolete">&amp;Сохранённые последние запросы</translation>
+    </message>
+    <message>
+        <source>&amp;Clear recent keywords</source>
+        <translation type="obsolete">&amp;Очистить последние запросы</translation>
+    </message>
+    <message>
+        <source>&amp;Close</source>
+        <translation type="obsolete">&amp;Закрыть</translation>
+    </message>
+</context>
+<context>
+    <name>Video</name>
+    <message>
+        <source>Network error: %1 for %2</source>
+        <translation>Ошибка сети: %1 на %2</translation>
+    </message>
+</context>
+</TS>
diff --git a/locale/tr_TR.ts b/locale/tr_TR.ts
new file mode 100644 (file)
index 0000000..1564f7e
--- /dev/null
@@ -0,0 +1,513 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.0" language="tr_TR" sourcelanguage="en_US">
+<defaultcodec>UTF-8</defaultcodec>
+<context>
+    <name>AboutView</name>
+    <message>
+        <source>There&apos;s life outside the browser!</source>
+        <translation>Tarayıcının dışındaki yaşam!</translation>
+    </message>
+    <message>
+        <source>Version %1</source>
+        <translation>Version %1</translation>
+    </message>
+    <message>
+        <source>This is a &quot;Technology Preview&quot; release, do not expect it to be perfect.</source>
+        <translation type="obsolete">Bu &quot;Teknoloji gösterimi&quot; sürümüdür, mükemmel olmasını beklemeyin.</translation>
+    </message>
+    <message>
+        <source>Report bugs and send in your ideas to %1</source>
+        <translation>Hataları ve yeni fikirlerinizi  %1 adresine gönderin</translation>
+    </message>
+    <message>
+        <source>%1 is Free Software but its development takes precious time.</source>
+        <translation>%1 özgür yazılımdır ama geliştirmek değerli zaman alır.</translation>
+    </message>
+    <message>
+        <source>Please &lt;a href=&apos;%1&apos;&gt;donate via PayPal&lt;/a&gt; to support the continued development of %2.</source>
+        <translation type="obsolete">Lütfen %2 gelişiminin sürekliliği için &lt;a href=&apos;%1&apos;&gt;PayPal ile bağış&lt;/a&gt; yaparak destek olun.</translation>
+    </message>
+    <message>
+        <source>Icon designed by %1.</source>
+        <translation>Simge %1 tarafından tasarlandı.</translation>
+    </message>
+    <message>
+        <source>Compact mode contributed by %1.</source>
+        <translation>Pencereye sığdırmada %1&apos;ün katkısı var.</translation>
+    </message>
+    <message>
+        <source>HTTP proxy support contributed by %1.</source>
+        <translation>HTTP Vekil desteğinde %1&apos;nin katkısı var.</translation>
+    </message>
+    <message>
+        <source>Windows version built by %1</source>
+        <translation type="obsolete">Windows sürümü %1 tarafından yapıldı.</translation>
+    </message>
+    <message>
+        <source>Translated by %1</source>
+        <translation>Çevirenler %1</translation>
+    </message>
+    <message>
+        <source>Released under the &lt;a href=&apos;%1&apos;&gt;GNU General Public License&lt;/a&gt;</source>
+        <translation>&lt;a href=&apos;%1&apos;&gt;GNU Genel Kamu Lisansı&lt;/a&gt; ile yayınlanmıştır</translation>
+    </message>
+    <message>
+        <source>&amp;Close</source>
+        <translation>&amp;Kapat</translation>
+    </message>
+    <message>
+        <source>About</source>
+        <translation>Hakkında</translation>
+    </message>
+    <message>
+        <source>What you always wanted to know about %1 and never dared to ask</source>
+        <translation></translation>
+    </message>
+    <message>
+        <source>Please &lt;a href=&apos;%1&apos;&gt;donate&lt;/a&gt; to support the continued development of %2.</source>
+        <translation>Lütfen %2 geliştirilmesinin devam etmesi için &lt;a href=&apos;%1&apos;&gt;bağış&lt;/a&gt; yapın.</translation>
+    </message>
+</context>
+<context>
+    <name>ClearButton</name>
+    <message>
+        <source>Clear</source>
+        <translation>Temizle</translation>
+    </message>
+</context>
+<context>
+    <name>ListModel</name>
+    <message>
+        <source>Searching...</source>
+        <translation>Aranıyor...</translation>
+    </message>
+    <message>
+        <source>Show %1 More</source>
+        <translation>%1 Tane daha</translation>
+    </message>
+    <message>
+        <source>No videos</source>
+        <translation>Video yok</translation>
+    </message>
+    <message>
+        <source>No more videos</source>
+        <translation>Daha fazla yok</translation>
+    </message>
+</context>
+<context>
+    <name>LoadingWidget</name>
+    <message>
+        <source>Error</source>
+        <translation>Hata</translation>
+    </message>
+</context>
+<context>
+    <name>MainWindow</name>
+    <message>
+        <source>&amp;Back</source>
+        <translation type="obsolete">&amp;Geri</translation>
+    </message>
+    <message>
+        <source>Go to the previous view</source>
+        <translation type="obsolete">Öncekine git</translation>
+    </message>
+    <message>
+        <source>&amp;Stop</source>
+        <translation>&amp;Dur</translation>
+    </message>
+    <message>
+        <source>Stop playback and go back to the search view</source>
+        <translation>Oynatmayı durdur ve aramaya geri dön</translation>
+    </message>
+    <message>
+        <source>S&amp;kip</source>
+        <translation>&amp;Atla</translation>
+    </message>
+    <message>
+        <source>Skip to the next video</source>
+        <translation>Sonraki videoya atla</translation>
+    </message>
+    <message>
+        <source>&amp;Pause</source>
+        <translation>&amp;Bekle</translation>
+    </message>
+    <message>
+        <source>Pause playback</source>
+        <translation>Oynatmayı beklet</translation>
+    </message>
+    <message>
+        <source>&amp;Full Screen</source>
+        <translation>&amp;Tam ekran</translation>
+    </message>
+    <message>
+        <source>Go full screen</source>
+        <translation>Tam ekran görünümü</translation>
+    </message>
+    <message>
+        <source>&amp;Compact View</source>
+        <translation type="obsolete">&amp;Kompakt Ansicht</translation>
+    </message>
+    <message>
+        <source>Go compact view</source>
+        <translation type="obsolete">Kompakt Ansicht aktivieren</translation>
+    </message>
+    <message>
+        <source>&amp;YouTube</source>
+        <translation type="obsolete">&amp;YouTube</translation>
+    </message>
+    <message>
+        <source>Open the YouTube video page</source>
+        <translation type="obsolete">YouTube video sayfasını aç</translation>
+    </message>
+    <message>
+        <source>Ctrl+Y</source>
+        <translation type="obsolete">Ctrl+Y</translation>
+    </message>
+    <message>
+        <source>&amp;Compact mode</source>
+        <translation>&amp;Pencereye sığdır</translation>
+    </message>
+    <message>
+        <source>Hide the playlist and the toolbar</source>
+        <translation>Listeyi ve araç çubuğunu gizle</translation>
+    </message>
+    <message>
+        <source>&amp;Download</source>
+        <translation type="obsolete">&amp;Herunterladen</translation>
+    </message>
+    <message>
+        <source>Download this video</source>
+        <translation type="obsolete">Dieses Video herunterladen</translation>
+    </message>
+    <message>
+        <source>Ctrl+S</source>
+        <translation type="obsolete">Strg+S</translation>
+    </message>
+    <message>
+        <source>&amp;Remove</source>
+        <translation>&amp;Kaldır</translation>
+    </message>
+    <message>
+        <source>Remove the selected videos from the playlist</source>
+        <translation>Seçilen videoları listeden kaldır</translation>
+    </message>
+    <message>
+        <source>Move &amp;Up</source>
+        <translation>Y&amp;ukarı taşı</translation>
+    </message>
+    <message>
+        <source>Move up the selected videos in the playlist</source>
+        <translation>Seçilen videoları listede yukarı taşı</translation>
+    </message>
+    <message>
+        <source>Move &amp;Down</source>
+        <translation>A&amp;şağı taşı</translation>
+    </message>
+    <message>
+        <source>Move down the selected videos in the playlist</source>
+        <translation>Seçilen videoları listede aşağı taşı</translation>
+    </message>
+    <message>
+        <source>&amp;Quit</source>
+        <translation>&amp;Çık</translation>
+    </message>
+    <message>
+        <source>Ctrl+Q</source>
+        <translation>Ctrl+Ç</translation>
+    </message>
+    <message>
+        <source>Bye</source>
+        <translation>Gülegüle</translation>
+    </message>
+    <message>
+        <source>&amp;Website</source>
+        <translation>&amp;Website</translation>
+    </message>
+    <message>
+        <source>%1 on the Web</source>
+        <translation>%1 İnternette</translation>
+    </message>
+    <message>
+        <source>&amp;Donate via PayPal</source>
+        <translation type="obsolete">PayPal ile ba&amp;ğış yap</translation>
+    </message>
+    <message>
+        <source>Please support the continued development of %1</source>
+        <translation>Lütfen %1 gelişiminin sürekliliği için destekleyin</translation>
+    </message>
+    <message>
+        <source>&amp;About</source>
+        <translation>&amp;Hakkında</translation>
+    </message>
+    <message>
+        <source>Info about %1</source>
+        <translation>%1 Hakkında</translation>
+    </message>
+    <message>
+        <source>Search</source>
+        <translation>Ara</translation>
+    </message>
+    <message>
+        <source>Mute volume</source>
+        <translation>Sesi kes</translation>
+    </message>
+    <message>
+        <source>Ctrl+M</source>
+        <translation>Ctrl+M</translation>
+    </message>
+    <message>
+        <source>Press %1 to raise the volume, %2 to lower it</source>
+        <translation>%1 Sesi arttır, %2 sesi azalt</translation>
+    </message>
+    <message>
+        <source>Remaining time: %1</source>
+        <translation>Kalan zaman: %1</translation>
+    </message>
+    <message>
+        <source>High Definition video is enabled</source>
+        <translation type="obsolete">Yüksek çözünürlüklü video aktif</translation>
+    </message>
+    <message>
+        <source>High Definition video is not enabled</source>
+        <translation type="obsolete">Yüksek çözünürlüklü video aktif değil</translation>
+    </message>
+    <message>
+        <source>The current video is in High Definition</source>
+        <translation type="obsolete">Geçerli video yüksek çözünürlüklü</translation>
+    </message>
+    <message>
+        <source>The current video is not in High Definition</source>
+        <translation type="obsolete">Geçerli video yüksek çözünürlüklü değil</translation>
+    </message>
+    <message>
+        <source>No Video playing</source>
+        <translation type="obsolete">Es wird kein Video abgespielt</translation>
+    </message>
+    <message>
+        <source>You must first play the video you intent to download !</source>
+        <translation type="obsolete">Du musst das Video erst abspielen !</translation>
+    </message>
+    <message>
+        <source>Save video as...</source>
+        <translation type="obsolete">Video speichern unter...</translation>
+    </message>
+    <message>
+        <source>minitube video.mp4</source>
+        <translation type="obsolete">minitube video.mp4</translation>
+    </message>
+    <message>
+        <source>Downloading: </source>
+        <translation type="obsolete">Herunterladen:</translation>
+    </message>
+    <message>
+        <source>Abort Download</source>
+        <translation type="obsolete">Herunterladen abbrechen</translation>
+    </message>
+    <message>
+        <source>File creation failed</source>
+        <translation type="obsolete">Das anlegen der Datei ist fehlgeschlagen</translation>
+    </message>
+    <message>
+        <source>Download failed</source>
+        <translation type="obsolete">Herunterladen fehlgeschlagen</translation>
+    </message>
+    <message>
+        <source>Volume at %1%</source>
+        <translation>Ses %1%</translation>
+    </message>
+    <message>
+        <source>Volume is muted</source>
+        <translation>Ses kapandı</translation>
+    </message>
+    <message>
+        <source>Volume is unmuted</source>
+        <translation>Ses açık</translation>
+    </message>
+    <message>
+        <source>&amp;Search</source>
+        <translation type="obsolete">&amp;Suche</translation>
+    </message>
+    <message>
+        <source>&amp;Application</source>
+        <translation>&amp;Uygulama</translation>
+    </message>
+    <message>
+        <source>&amp;Playlist</source>
+        <translation>Oynatma &amp;listesi</translation>
+    </message>
+    <message>
+        <source>&amp;Video</source>
+        <translation>&amp;Video</translation>
+    </message>
+    <message>
+        <source>&amp;Help</source>
+        <translation>Yardı&amp;m</translation>
+    </message>
+    <message>
+        <source>Opening %1</source>
+        <translation>%1 Açılıyor</translation>
+    </message>
+    <message>
+        <source>Fatal error: %1</source>
+        <translation>İç hata: %1</translation>
+    </message>
+    <message>
+        <source>Error: %1</source>
+        <translation>Hata: %1</translation>
+    </message>
+    <message>
+        <source>&amp;Play</source>
+        <translation>&amp;Oynat</translation>
+    </message>
+    <message>
+        <source>Resume playback</source>
+        <translation>Oynatmaya devam et</translation>
+    </message>
+    <message>
+        <source>Exit &amp;Full Screen</source>
+        <translation>&amp;Tam ekrandan çık</translation>
+    </message>
+    <message>
+        <source>&amp;Clear recent keywords</source>
+        <translation>Geçmiş aramaları &amp;sil</translation>
+    </message>
+    <message>
+        <source>Clear the search history. Cannot be undone.</source>
+        <translation>Arama geçmişini sil. Bu geri alınamaz.</translation>
+    </message>
+    <message>
+        <source>Your privacy is now safe</source>
+        <translation>Şimdi gizliliğiniz güvende</translation>
+    </message>
+    <message>
+        <source>Open the &amp;YouTube page</source>
+        <translation>&amp;YouTube sayfasını aç</translation>
+    </message>
+    <message>
+        <source>Go to the YouTube video page and pause playback</source>
+        <translation>Oynatmayı beklet ve YouTube sayfasına git</translation>
+    </message>
+    <message>
+        <source>Copy the YouTube &amp;link</source>
+        <translation>YouTube bağlantısını &amp;kopyala</translation>
+    </message>
+    <message>
+        <source>Copy the current video YouTube link to the clipboard</source>
+        <translation>Geçerli videonun YouTube bağlantısını panoya kopyala</translation>
+    </message>
+    <message>
+        <source>Copy the video stream &amp;URL</source>
+        <translation>Video akışı &amp;URL adresini kopyala</translation>
+    </message>
+    <message>
+        <source>Copy the current video stream URL to the clipboard</source>
+        <translation>Geçerli video akışının URL adresini panoya kopyala</translation>
+    </message>
+    <message>
+        <source>Make a &amp;donation</source>
+        <translation>&amp;Bağış yap</translation>
+    </message>
+    <message>
+        <source>Maximum video definition set to %1</source>
+        <translation>En yüksek video tanımı %1 olarak ayarlı</translation>
+    </message>
+</context>
+<context>
+    <name>MediaView</name>
+    <message>
+        <source>Most relevant</source>
+        <translation>En ilgili</translation>
+    </message>
+    <message>
+        <source>Most recent</source>
+        <translation>En yeni</translation>
+    </message>
+    <message>
+        <source>Most viewed</source>
+        <translation>Çok izlenen</translation>
+    </message>
+    <message>
+        <source>You&apos;re watching &quot;%1&quot;</source>
+        <translation>&quot;%1&quot; İzliyorsunuz</translation>
+    </message>
+    <message>
+        <source>You can now paste the YouTube link into another application</source>
+        <translation>Şimdi YouTube bağlantısını başka bir uygulamaya yapıştırabilirsiniz</translation>
+    </message>
+    <message>
+        <source>You can now paste the video stream URL into another application</source>
+        <translation>Şimdi video akışı URL adresini başka bir uygulamaya yapıştırabilirsiniz</translation>
+    </message>
+    <message>
+        <source>The link will be valid only for a limited time.</source>
+        <translation>Bağlantı kısıtlı bir süre için geçerli olacak.</translation>
+    </message>
+</context>
+<context>
+    <name>NetworkAccess</name>
+    <message>
+        <source>Network error: %1</source>
+        <translation>Ağ hatası: %1</translation>
+    </message>
+</context>
+<context>
+    <name>PrettyItemDelegate</name>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 görüntüleme</translation>
+    </message>
+</context>
+<context>
+    <name>SearchLineEdit</name>
+    <message>
+        <source>Search</source>
+        <translation>Ara</translation>
+    </message>
+</context>
+<context>
+    <name>SearchView</name>
+    <message>
+        <source>Welcome to &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</source>
+        <translation>&lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;&apos;a Hoşgeldiniz</translation>
+    </message>
+    <message>
+        <source>Enter a keyword to start watching videos.</source>
+        <translation>Bir anahtar kelime girerek izlemeye başlayın.</translation>
+    </message>
+    <message>
+        <source>Watch</source>
+        <translation>İzle</translation>
+    </message>
+    <message>
+        <source>Recent keywords</source>
+        <translation>Son arananlar</translation>
+    </message>
+    <message>
+        <source>A new version of %1 is available. Please &lt;a href=&apos;%2&apos;&gt;update to version %3&lt;/a&gt;</source>
+        <translation>%1&apos;un yeni sürümü mevcut. Lütfen buradan &lt;a href=&apos;%2&apos;&gt; %3 sürümüne yükseltin&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>Make yourself comfortable</source>
+        <translation>Kendinize konfor sunun</translation>
+    </message>
+</context>
+<context>
+    <name>SettingsView</name>
+    <message>
+        <source>Preferences</source>
+        <translation type="obsolete">Seçenekler</translation>
+    </message>
+    <message>
+        <source>&amp;Close</source>
+        <translation type="obsolete">S&amp;chließen</translation>
+    </message>
+</context>
+<context>
+    <name>Video</name>
+    <message>
+        <source>Network error: %1 for %2</source>
+        <translation>Ağ hatası: %2 için  %1</translation>
+    </message>
+</context>
+</TS>
diff --git a/locale/uk.ts b/locale/uk.ts
new file mode 100644 (file)
index 0000000..4ba4b57
--- /dev/null
@@ -0,0 +1,509 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.0" language="uk_UA">
+<defaultcodec>UTF-8</defaultcodec>
+<context>
+    <name>AboutView</name>
+    <message>
+        <source>There&apos;s life outside the browser!</source>
+        <translation>За межами браузера також є життя!</translation>
+    </message>
+    <message>
+        <source>Version %1</source>
+        <translation>Версія %1</translation>
+    </message>
+    <message>
+        <source>This is a &quot;Technology Preview&quot; release, do not expect it to be perfect.</source>
+        <translation type="obsolete">Це технологічний попередній випуск програми, не очікуйте від нього досконалості.</translation>
+    </message>
+    <message>
+        <source>Report bugs and send in your ideas to %1</source>
+        <translation>Повідомляйте про несправності та висилайте ваші ідеї на %1</translation>
+    </message>
+    <message>
+        <source>%1 is Free Software but its development takes precious time.</source>
+        <translation>%1 є Вільним Програмним Забезпеченням але на його розробку було затрачено коштовний час.</translation>
+    </message>
+    <message>
+        <source>Please &lt;a href=&apos;%1&apos;&gt;donate via PayPal&lt;/a&gt; to support the continued development of %2.</source>
+        <translation type="obsolete">Будь ласка  &lt;a href=&apos;%1&apos;&gt;скористайтесь PayPal&lt;/a&gt; щоб підтримати подальший розвиток %2.</translation>
+    </message>
+    <message>
+        <source>Icon designed by %1.</source>
+        <translation>Розробник піктограм %1.</translation>
+    </message>
+    <message>
+        <source>Translated by %1</source>
+        <translation>Переклад %1</translation>
+    </message>
+    <message>
+        <source>Released under the &lt;a href=&apos;%1&apos;&gt;GNU General Public License&lt;/a&gt;</source>
+        <translation>Випущено під  &lt;a href=&apos;%1&apos;&gt;Загальною громадською ліцензією GNU&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>&amp;Close</source>
+        <translation>Пове&amp;рнутися</translation>
+    </message>
+    <message>
+        <source>About</source>
+        <translation>Про програму</translation>
+    </message>
+    <message>
+        <source>What you always wanted to know about %1 and never dared to ask</source>
+        <translation>Все що ви завжди хотіли знати про %1 але не наважувались запитати</translation>
+    </message>
+    <message>
+        <source>Compact mode contributed by %1.</source>
+        <translation>Розробка компактного режиму %1.</translation>
+    </message>
+    <message>
+        <source>HTTP proxy support contributed by %1.</source>
+        <translation>Реалізація підтримки HTTP проксі %1.</translation>
+    </message>
+    <message>
+        <source>Windows version built by %1</source>
+        <translation type="obsolete">Версія для Windows %1</translation>
+    </message>
+    <message>
+        <source>Please &lt;a href=&apos;%1&apos;&gt;donate&lt;/a&gt; to support the continued development of %2.</source>
+        <translation>Будь ласка &lt;a href=&apos;%1&apos;&gt;внесіть пожертву&lt;/a&gt; щоб підтримати подальший розвиток %2.</translation>
+    </message>
+</context>
+<context>
+    <name>ClearButton</name>
+    <message>
+        <source>Clear</source>
+        <translation>Очистити</translation>
+    </message>
+</context>
+<context>
+    <name>ListModel</name>
+    <message>
+        <source>Searching...</source>
+        <translation>Шукаю...</translation>
+    </message>
+    <message>
+        <source>Show %1 More</source>
+        <translation>Наступні %1 </translation>
+    </message>
+    <message>
+        <source>No videos</source>
+        <translation>Нічого не знайдено </translation>
+    </message>
+    <message>
+        <source>No more videos</source>
+        <translation>Більше немає</translation>
+    </message>
+</context>
+<context>
+    <name>LoadingWidget</name>
+    <message>
+        <source>Error</source>
+        <translation>Помилка</translation>
+    </message>
+</context>
+<context>
+    <name>MainWindow</name>
+    <message>
+        <source>&amp;Back</source>
+        <translation type="obsolete">&amp;Назад</translation>
+    </message>
+    <message>
+        <source>Alt+Left</source>
+        <translation type="obsolete">Alt+Вліво</translation>
+    </message>
+    <message>
+        <source>Go to the previous view</source>
+        <translation type="obsolete">Повернутися до попереднього режиму</translation>
+    </message>
+    <message>
+        <source>&amp;Stop</source>
+        <translation>&amp;Зупинити</translation>
+    </message>
+    <message>
+        <source>Stop playback and go back to the search view</source>
+        <translation>Зупинити відтворення і перейти до вікна пошуку</translation>
+    </message>
+    <message>
+        <source>Esc</source>
+        <translation type="obsolete">Esc</translation>
+    </message>
+    <message>
+        <source>S&amp;kip</source>
+        <translation>П&amp;ропустити</translation>
+    </message>
+    <message>
+        <source>Skip to the next video</source>
+        <translation>Перейти до наступного відео</translation>
+    </message>
+    <message>
+        <source>Ctrl+Right</source>
+        <translation type="obsolete">Ctrl+Вправо</translation>
+    </message>
+    <message>
+        <source>&amp;Pause</source>
+        <translation>&amp;Призупинити</translation>
+    </message>
+    <message>
+        <source>Pause playback</source>
+        <translation>Призупинити відтворення</translation>
+    </message>
+    <message>
+        <source>Space</source>
+        <translation type="obsolete">Клавіша пробілу</translation>
+    </message>
+    <message>
+        <source>&amp;Full Screen</source>
+        <translation>Н&amp;а весь экран</translation>
+    </message>
+    <message>
+        <source>Go full screen</source>
+        <translation>Перехід в повноекранний режим</translation>
+    </message>
+    <message>
+        <source>Alt+Return</source>
+        <translation type="obsolete">Alt+Enter</translation>
+    </message>
+    <message>
+        <source>&amp;YouTube</source>
+        <translation type="obsolete">&amp;YouTube</translation>
+    </message>
+    <message>
+        <source>Open the YouTube video page</source>
+        <translation type="obsolete">Відкрити сторінку YouTube</translation>
+    </message>
+    <message>
+        <source>Ctrl+Y</source>
+        <translation type="obsolete">Ctrl+Y</translation>
+    </message>
+    <message>
+        <source>&amp;Remove</source>
+        <translation>&amp;Видалити</translation>
+    </message>
+    <message>
+        <source>Remove the selected videos from the playlist</source>
+        <translation>Видалити обрані відео з переліку композицій</translation>
+    </message>
+    <message>
+        <source>Move &amp;Up</source>
+        <translation>Зсув в&amp;гору</translation>
+    </message>
+    <message>
+        <source>Move up the selected videos in the playlist</source>
+        <translation>Зсунути обране відео на позицію вгору в переліку композицій</translation>
+    </message>
+    <message>
+        <source>Ctrl+Up</source>
+        <translation type="obsolete">Ctrl+Вгору</translation>
+    </message>
+    <message>
+        <source>Move &amp;Down</source>
+        <translation>Зсув вни&amp;з</translation>
+    </message>
+    <message>
+        <source>Move down the selected videos in the playlist</source>
+        <translation>Зсунути обране відео на позицію вниз в переліку композицій</translation>
+    </message>
+    <message>
+        <source>Ctrl+Down</source>
+        <translation type="obsolete">Ctrl+Вниз</translation>
+    </message>
+    <message>
+        <source>&amp;Quit</source>
+        <translation>Ви&amp;хід</translation>
+    </message>
+    <message>
+        <source>Ctrl+Q</source>
+        <translation>Ctrl+Q</translation>
+    </message>
+    <message>
+        <source>Bye</source>
+        <translation>Хай щастить</translation>
+    </message>
+    <message>
+        <source>&amp;Website</source>
+        <translation>Домашн&amp;я сторінка програми</translation>
+    </message>
+    <message>
+        <source>Minitube on the Web</source>
+        <translation type="obsolete">Minitube в мережі</translation>
+    </message>
+    <message>
+        <source>%1 on the Web</source>
+        <translation>%1 в мережі</translation>
+    </message>
+    <message>
+        <source>&amp;Donate via PayPal</source>
+        <translation type="obsolete">Підтрима&amp;йте проект через PayPal</translation>
+    </message>
+    <message>
+        <source>Please support the continued development of %1</source>
+        <translation>Будь ласка підтримайте подальший розвиток %1</translation>
+    </message>
+    <message>
+        <source>&amp;About</source>
+        <translation>&amp;Про програму</translation>
+    </message>
+    <message>
+        <source>Info about %1</source>
+        <translation>Інформація про %1</translation>
+    </message>
+    <message>
+        <source>&amp;Search</source>
+        <translation type="obsolete">Пош&amp;ук</translation>
+    </message>
+    <message>
+        <source>&amp;Application</source>
+        <translation>&amp;Програма</translation>
+    </message>
+    <message>
+        <source>&amp;Playlist</source>
+        <translation>Перелік &amp;композицій</translation>
+    </message>
+    <message>
+        <source>&amp;Video</source>
+        <translation>В&amp;ідео</translation>
+    </message>
+    <message>
+        <source>&amp;Help</source>
+        <translation>&amp;Допомога</translation>
+    </message>
+    <message>
+        <source>Opening %1</source>
+        <translation>Відкриваю %1</translation>
+    </message>
+    <message>
+        <source>&amp;Play</source>
+        <translation>Відтворюв&amp;ати</translation>
+    </message>
+    <message>
+        <source>Resume playback</source>
+        <translation>Продовжити відтворення</translation>
+    </message>
+    <message>
+        <source>Exit &amp;Full Screen</source>
+        <translation>Вийти з повноекр&amp;анного режиму</translation>
+    </message>
+    <message>
+        <source>&amp;Compact mode</source>
+        <translation>&amp;Компактний режим</translation>
+    </message>
+    <message>
+        <source>Hide the playlist and the toolbar</source>
+        <translation>Приховати перелік композицій та панель інструментів</translation>
+    </message>
+    <message>
+        <source>Fatal error: %1</source>
+        <translation>Невиправна помилка: %1</translation>
+    </message>
+    <message>
+        <source>Error: %1</source>
+        <translation>Помилка: %1</translation>
+    </message>
+    <message>
+        <source>Ctrl+M</source>
+        <translation>Ctrl+M</translation>
+    </message>
+    <message>
+        <source>Volume at %1%</source>
+        <translation>Гучність %1%</translation>
+    </message>
+    <message>
+        <source>Volume is muted</source>
+        <translation>Гучність приглушено</translation>
+    </message>
+    <message>
+        <source>Volume is unmuted</source>
+        <translation>Гучність відновлено</translation>
+    </message>
+    <message>
+        <source>Search</source>
+        <translation>Пошук</translation>
+    </message>
+    <message>
+        <source>Mute volume</source>
+        <translation>Приглушити гучність </translation>
+    </message>
+    <message>
+        <source>Press %1 to raise the volume, %2 to lower it</source>
+        <translation>%1 щоб приглушити гучність, %2 щоб відновити </translation>
+    </message>
+    <message>
+        <source>Remaining time: %1</source>
+        <translation>Час до завершення: %1</translation>
+    </message>
+    <message>
+        <source>High Definition video is enabled</source>
+        <translation type="obsolete">Відео високої чіткості активоване</translation>
+    </message>
+    <message>
+        <source>High Definition video is not enabled</source>
+        <translation type="obsolete">Відео високої чіткості не активоване</translation>
+    </message>
+    <message>
+        <source>The current video is in High Definition</source>
+        <translation type="obsolete">Відео в режимі високої чіткості</translation>
+    </message>
+    <message>
+        <source>The current video is not in High Definition</source>
+        <translation type="obsolete">ВІдео не в режимі високої чіткості</translation>
+    </message>
+    <message>
+        <source>&amp;Clear recent keywords</source>
+        <translation>Очистити недавн&amp;і терміни пошуку</translation>
+    </message>
+    <message>
+        <source>Clear the search history. Cannot be undone.</source>
+        <translation>Очистити історію пошуку (зміни незвороні).</translation>
+    </message>
+    <message>
+        <source>Your privacy is now safe</source>
+        <translation>Ваша приватність застережена</translation>
+    </message>
+    <message>
+        <source>Open the &amp;YouTube page</source>
+        <translation>&amp;Відкрити сторінку YouTube</translation>
+    </message>
+    <message>
+        <source>Go to the YouTube video page and pause playback</source>
+        <translation>Відкрити сторінку YouTube і призупини відтворення відео</translation>
+    </message>
+    <message>
+        <source>Copy the YouTube &amp;link</source>
+        <translation>Скопіювати YouTube &amp;посилання в буфер</translation>
+    </message>
+    <message>
+        <source>Copy the current video YouTube link to the clipboard</source>
+        <translation>Скопіювати YouTube посилання поточного відео в буфер</translation>
+    </message>
+    <message>
+        <source>Copy the video stream &amp;URL</source>
+        <translation>Скопіювати &amp;посилання в буфер</translation>
+    </message>
+    <message>
+        <source>Copy the current video stream URL to the clipboard</source>
+        <translation>Скопіювати посилання поточного відео в буфер</translation>
+    </message>
+    <message>
+        <source>Make a &amp;donation</source>
+        <translation>Підтрима&amp;йте проект</translation>
+    </message>
+    <message>
+        <source>Maximum video definition set to %1</source>
+        <translation>Режим чіткості відео %1</translation>
+    </message>
+</context>
+<context>
+    <name>MediaView</name>
+    <message>
+        <source>Most relevant</source>
+        <translation>Подібні</translation>
+    </message>
+    <message>
+        <source>Most recent</source>
+        <translation>Недавні</translation>
+    </message>
+    <message>
+        <source>Most viewed</source>
+        <translation>Популярні</translation>
+    </message>
+    <message>
+        <source>You&apos;re watching &quot;%1&quot;</source>
+        <translation>Ви дивитесь &quot;%1&quot;</translation>
+    </message>
+    <message>
+        <source>You can now paste the YouTube link into another application</source>
+        <translation>Ви можете використовувати YouTube посилання на відео в завнішній програмі </translation>
+    </message>
+    <message>
+        <source>You can now paste the video stream URL into another application</source>
+        <translation>Ви можете використовувати посилання на відео в завнішній програмі </translation>
+    </message>
+    <message>
+        <source>The link will be valid only for a limited time.</source>
+        <translation>Посилання буде дійсне тільки протягом обмеженого часу.</translation>
+    </message>
+</context>
+<context>
+    <name>NetworkAccess</name>
+    <message>
+        <source>Network error: %1</source>
+        <translation>Помилка мережі %1</translation>
+    </message>
+</context>
+<context>
+    <name>PrettyItemDelegate</name>
+    <message>
+        <source>%1 views</source>
+        <translation>переглядів %1</translation>
+    </message>
+</context>
+<context>
+    <name>SearchLineEdit</name>
+    <message>
+        <source>Search</source>
+        <translation>Пошук</translation>
+    </message>
+</context>
+<context>
+    <name>SearchView</name>
+    <message>
+        <source>Welcome to &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</source>
+        <translation>Вітаємо в &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
+    </message>
+    <message>
+        <source>Enter a keyword to start watching videos.</source>
+        <translation>Введіть терміни для пошуку відео.</translation>
+    </message>
+    <message>
+        <source>Watch</source>
+        <translation>Перегляд</translation>
+    </message>
+    <message>
+        <source>Recent keywords</source>
+        <translation>Недавній пошук</translation>
+    </message>
+    <message>
+        <source>A new version of %1 is available. Please &lt;a href=&apos;%2&apos;&gt;update to version %3&lt;/a&gt;</source>
+        <translation>Наявна нова версія %1. Будь ласка &lt;a href=&apos;%2&apos;&gt; обновіть програму до версії %3&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>Make yourself comfortable</source>
+        <translation>Влаштовуйтеся зручніше</translation>
+    </message>
+</context>
+<context>
+    <name>SettingsView</name>
+    <message>
+        <source>Preferences</source>
+        <translation type="obsolete">Налаштування</translation>
+    </message>
+    <message>
+        <source>&amp;Video options</source>
+        <translation type="obsolete">Відео &amp;можливості</translation>
+    </message>
+    <message>
+        <source>Use high quality video when available</source>
+        <translation type="obsolete">Відтворювати відео високої якості при наявності</translation>
+    </message>
+    <message>
+        <source>&amp;Saved recent keywords</source>
+        <translation type="obsolete">&amp;Збережені недавні терміни пошуку</translation>
+    </message>
+    <message>
+        <source>&amp;Clear recent keywords</source>
+        <translation type="obsolete">Очистити недавн&amp;і терміни пошуку</translation>
+    </message>
+    <message>
+        <source>&amp;Close</source>
+        <translation type="obsolete">За&amp;крити</translation>
+    </message>
+</context>
+<context>
+    <name>Video</name>
+    <message>
+        <source>Network error: %1 for %2</source>
+        <translation>Помилка мережі: %1 для %2</translation>
+    </message>
+</context>
+</TS>
diff --git a/minitube.desktop b/minitube.desktop
new file mode 100644 (file)
index 0000000..a89db43
--- /dev/null
@@ -0,0 +1,18 @@
+[Desktop Entry]
+Name=Minitube
+Name[en_US]=Minitube
+Comment=Watch YouTube videos
+Comment[es]=Vea los videos de YouTube
+Comment[gl]=Vexa os vídeos de YouTube
+Comment[it]=Guarda i video di YouTube
+GenericName=YouTube client
+GenericName[en_US]=YouTube client
+GenericName[es]=Cliente de YouTube
+GenericName[gl]=Cliente de YouTube
+GenericName[it]=Client per YouTube
+Exec=minitube
+Terminal=false
+Type=Application
+Icon=minitube
+Categories=Application;Qt;AudioVideo;Video;
+StartupNotify=true
diff --git a/minitube.icns b/minitube.icns
new file mode 100644 (file)
index 0000000..0053278
Binary files /dev/null and b/minitube.icns differ
diff --git a/minitube.ico b/minitube.ico
new file mode 100644 (file)
index 0000000..9a3acbe
Binary files /dev/null and b/minitube.ico differ
diff --git a/minitube.pro b/minitube.pro
new file mode 100755 (executable)
index 0000000..09f3649
--- /dev/null
@@ -0,0 +1,130 @@
+INCLUDEPATH += /usr/include/phonon
+CONFIG += release
+TEMPLATE = app
+
+# TODO Saner string behaviour
+# DEFINES += QT_NO_CAST_FROM_ASCII QT_NO_CAST_TO_ASCII QT_STRICT_ITERATORS
+TARGET = minitube
+mac { 
+    TARGET = Minitube
+    QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.4
+}
+QT += network \
+    xml \
+    phonon
+include(src/qtsingleapplication/qtsingleapplication.pri)
+include(src/thlibrary/thlibrary.pri)
+HEADERS += src/MainWindow.h \
+    src/SearchView.h \
+    src/MediaView.h \
+    src/AboutView.h \
+    src/youtubesearch.h \
+    src/video.h \
+    src/youtubestreamreader.h \
+    src/View.h \
+    src/searchlineedit.h \
+    src/urllineedit.h \
+    src/spacer.h \
+    src/Constants.h \
+    src/iconloader/qticonloader.h \
+    src/faderwidget/FaderWidget.h \
+    src/ListModel.h \
+    src/playlist/PrettyItemDelegate.h \
+    src/networkaccess.h \
+    src/videomimedata.h \
+    src/global.h \
+    src/updatechecker.h \
+    src/playlistwidget.h \
+    src/searchparams.h \
+    src/minisplitter.h \
+    src/loadingwidget.h \
+    src/videoareawidget.h \
+    src/googlesuggest.h \
+    src/videowidget.h \
+    src/flickcharm.h \
+    src/videodefinition.h
+SOURCES += src/main.cpp \
+    src/MainWindow.cpp \
+    src/SearchView.cpp \
+    src/MediaView.cpp \
+    src/AboutView.cpp \
+    src/youtubesearch.cpp \
+    src/youtubestreamreader.cpp \
+    src/searchlineedit.cpp \
+    src/urllineedit.cpp \
+    src/spacer.cpp \
+    src/video.cpp \
+    src/iconloader/qticonloader.cpp \
+    src/faderwidget/FaderWidget.cpp \
+    src/ListModel.cpp \
+    src/playlist/PrettyItemDelegate.cpp \
+    src/videomimedata.cpp \
+    src/updatechecker.cpp \
+    src/networkaccess.cpp \
+    src/playlistwidget.cpp \
+    src/searchparams.cpp \
+    src/minisplitter.cpp \
+    src/loadingwidget.cpp \
+    src/videoareawidget.cpp \
+    src/googlesuggest.cpp \
+    src/videowidget.cpp \
+    src/flickcharm.cpp \
+    src/videodefinition.cpp
+RESOURCES += resources.qrc
+DESTDIR = build/target/
+OBJECTS_DIR = build/obj/
+MOC_DIR = build/moc/
+RCC_DIR = build/rcc/
+
+# Tell Qt Linguist that we use UTF-8 strings in our sources
+CODECFORTR = UTF-8
+CODECFORSRC = UTF-8
+include(locale/locale.pri)
+
+# deploy
+DISTFILES += CHANGES \
+    COPYING
+mac { 
+    CONFIG += x86 \
+        ppc
+    QMAKE_INFO_PLIST = Info.plist
+    ICON = minitube.icns
+}
+unix { 
+    isEmpty(PREFIX):PREFIX = /usr
+    BINDIR = $$PREFIX/bin
+    INSTALLS += target
+    target.path = $$BINDIR
+    DATADIR = $$PREFIX/share
+    PKGDATADIR = $$DATADIR/minitube
+    DEFINES += DATADIR=\\\"$$DATADIR\\\" \
+        PKGDATADIR=\\\"$$PKGDATADIR\\\"
+    INSTALLS += translations \
+        desktop \
+        iconsvg \
+        icon16 \
+        icon32 \
+        icon48 \
+        icon64 \
+        icon128 \
+        icon256
+    translations.path = $$PKGDATADIR
+    translations.files += $$DESTDIR/locale
+    desktop.path = $$DATADIR/applications
+    desktop.files += minitube.desktop
+    iconsvg.path = $$DATADIR/icons/hicolor/scalable/apps
+    iconsvg.files += data/minitube.svg
+    icon16.path = $$DATADIR/icons/hicolor/16x16/apps
+    icon16.files += data/16x16/minitube.png
+    icon32.path = $$DATADIR/icons/hicolor/32x32/apps
+    icon32.files += data/32x32/minitube.png
+    icon48.path = $$DATADIR/icons/hicolor/48x48/apps
+    icon48.files += data/48x48/minitube.png
+    icon64.path = $$DATADIR/icons/hicolor/64x64/apps
+    icon64.files += data/64x64/minitube.png
+    icon128.path = $$DATADIR/icons/hicolor/128x128/apps
+    icon128.files += data/128x128/minitube.png
+    icon256.path = $$DATADIR/icons/hicolor/256x256/apps
+    icon256.files += data/256x256/minitube.png
+}
+win32:RC_FILE = minitube.rc
diff --git a/minitube.rc b/minitube.rc
new file mode 100644 (file)
index 0000000..2c1795b
--- /dev/null
@@ -0,0 +1,2 @@
+IDI_ICON1               ICON    DISCARDABLE     "minitube.ico"
+
diff --git a/resources.qrc b/resources.qrc
new file mode 100755 (executable)
index 0000000..639421a
--- /dev/null
@@ -0,0 +1,10 @@
+<RCC>
+    <qresource prefix="/">
+        <file>images/app.png</file>
+        <file>images/view-fullscreen.png</file>
+        <file>images/media-playback-pause.png</file>
+        <file>images/media-playback-start.png</file>
+        <file>images/media-playback-stop.png</file>
+        <file>images/media-skip-forward.png</file>
+    </qresource>
+</RCC>
diff --git a/src/AboutView.cpp b/src/AboutView.cpp
new file mode 100644 (file)
index 0000000..e9357ea
--- /dev/null
@@ -0,0 +1,84 @@
+#include "AboutView.h"
+#include "Constants.h"
+
+AboutView::AboutView(QWidget *parent) : QWidget(parent) {
+
+    QBoxLayout *aboutlayout = new QHBoxLayout(this);
+    aboutlayout->setAlignment(Qt::AlignCenter);
+    aboutlayout->setSpacing(30);
+
+    QLabel *logo = new QLabel(this);
+    logo->setPixmap(QPixmap(":/images/app.png"));
+    aboutlayout->addWidget(logo, 0, Qt::AlignTop);
+
+    QBoxLayout *layout = new QVBoxLayout();
+    layout->setAlignment(Qt::AlignCenter);
+    layout->setSpacing(30);
+    aboutlayout->addLayout(layout);
+
+    QString info = "<h1>" + QString(Constants::APP_NAME) + "</h1>"
+                   "<p>" + tr("There's life outside the browser!") + "</p>"
+                   "<p>" + tr("Version %1").arg(Constants::VERSION) + "</p>"
+                   + QString("<p><a href=\"%1/\">%1</a></p>").arg(Constants::WEBSITE) +
+
+                   "<p>" +  tr("%1 is Free Software but its development takes precious time.").arg(Constants::APP_NAME) + "<br/>"
+                   + tr("Please <a href='%1'>donate</a> to support the continued development of %2.")
+                   .arg(QString(Constants::WEBSITE).append("#donate"), Constants::APP_NAME) + "</p>"
+
+                   "<p>" + tr("Report bugs and send in your ideas to %1")
+                   .arg(QString("<a href=\"mailto:%1\">%1</a>").arg(Constants::EMAIL)) + "</p>"
+
+                   "<p>"
+                   + tr("Icon designed by %1.").arg("Sebastian Kraft")
+                   + "<br>" + tr("Compact mode contributed by %1.").arg("Stefan Brück")
+                   + "<br>" + tr("HTTP proxy support contributed by %1.").arg("Kiwamu Okabe")
+                   + "</p>"
+
+                   "<p>" + tr("Translated by %1").arg("Nikita Lyalin (ru_RU), "
+                                                      "Márcio Moraes (pt_BR), "
+                                                      // "Sergio Tocalini Joerg (es_AR), "
+                                                      "Stefan Brück (de_DE), "
+                                                      "Grzegorz Gibas (pl_PL), "
+                                                      "Kiwamu Okabe (ja_JP), "
+                                                      "Dan Vrátil (cs_CZ), "
+                                                      // "Rafa (es_ES), "
+                                                      "Yaron Shahrabani (he_IL), "
+                                                      "Oleksandr Korneta (uk), "
+                                                      "Inga Muste (lat), "
+                                                      "Srecko Belaic (hr_HR), "
+                                                      "Miguel Anxo Bouzada (es, gl), "
+                                                      "Guillaume Betous & Mathieu Dimanche (fr_FR), "
+                                                      "Krisztián Horváth (hu_HU), "
+                                                      "Ali E. İmrek (tr_TR), "
+                                                      "Jan W. Skjoldal & Halvor Lyche Strandvoll (nb_NO), "
+                                                      "Ovidiu Niţan (ro_RO), "
+                                                      "Giorgos Skettos (el_GR), "
+                                                      "Brian Keetman (nl_NL), "
+                                                      "Sderawi (ar), "
+                                                      "Daniel Rodrigues (pt_PT), "
+                                                      "Jesse Jaara (fi_FI), "
+                                                      "Tsvyatko Makazchiev (bg_BG)"
+                                                      ) + "</p>"
+
+                   "<p>" + tr("Released under the <a href='%1'>GNU General Public License</a>")
+                   .arg("http://www.gnu.org/licenses/gpl.html") + "</p>"
+
+                   "<p>&copy; 2009-2010 " + Constants::ORG_NAME + "</p>";
+    QLabel *infoLabel = new QLabel(info, this);
+    infoLabel->setOpenExternalLinks(true);
+    infoLabel->setWordWrap(true);
+    layout->addWidget(infoLabel);
+
+    QLayout *buttonLayout = new QHBoxLayout();
+    buttonLayout->setAlignment(Qt::AlignLeft);
+    QPushButton *closeButton = new QPushButton(tr("&Close"), this);
+    closeButton->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
+
+    closeButton->setDefault(true);
+    closeButton->setFocus(Qt::OtherFocusReason);
+    connect(closeButton, SIGNAL(clicked()), parent, SLOT(goBack()));
+    buttonLayout->addWidget(closeButton);
+
+    layout->addLayout(buttonLayout);
+
+}
diff --git a/src/AboutView.h b/src/AboutView.h
new file mode 100644 (file)
index 0000000..ba5fb7f
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef ABOUTVIEW_H
+#define ABOUTVIEW_H
+
+#include <QtGui>
+#include "View.h"
+#include "Constants.h"
+
+class AboutView : public QWidget, public View {
+
+    Q_OBJECT
+
+public:
+    AboutView(QWidget *parent);
+    void appear() {}
+    void disappear() {}
+    QMap<QString, QVariant> metadata() {
+        QMap<QString, QVariant> metadata;
+        metadata.insert("title", tr("About"));
+        metadata.insert("description",
+                        tr("What you always wanted to know about %1 and never dared to ask")
+                        .arg(Constants::APP_NAME));
+        return metadata;
+    }
+
+};
+#endif
diff --git a/src/Constants.h b/src/Constants.h
new file mode 100755 (executable)
index 0000000..8ba79e8
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef CONSTANTS_H
+#define CONSTANTS_H
+
+#include <QString>
+
+namespace Constants {
+    static const char *VERSION = "1.0";
+    static const char *APP_NAME = "Minitube";
+    static const char *ORG_NAME = "Flavio Tordini";
+    static const char *ORG_DOMAIN = "flavio.tordini.org";
+    static const char *WEBSITE = "http://flavio.tordini.org/minitube";
+    static const char *EMAIL = "flavio.tordini@gmail.com";
+    static const QString USER_AGENT = QString(APP_NAME) + " " + VERSION + " (" + WEBSITE + ")";
+}
+
+#endif
diff --git a/src/ListModel.cpp b/src/ListModel.cpp
new file mode 100755 (executable)
index 0000000..e7592ec
--- /dev/null
@@ -0,0 +1,409 @@
+#include "ListModel.h"
+#include "videomimedata.h"
+
+#define MAX_ITEMS 10
+static const QString recentKeywordsKey = "recentKeywords";
+
+ListModel::ListModel(QWidget *parent) : QAbstractListModel(parent) {
+    youtubeSearch = 0;
+    searching = false;
+    canSearchMore = true;
+    m_activeVideo = 0;
+    m_activeRow = -1;
+    skip = 1;
+}
+
+ListModel::~ListModel() {
+    delete youtubeSearch;
+}
+
+int ListModel::rowCount(const QModelIndex &/*parent*/) const {
+    int count = videos.size();
+    
+    // add the message item
+    if (videos.isEmpty() || !searching)
+        count++;
+    
+    return count;
+}
+
+QVariant ListModel::data(const QModelIndex &index, int role) const {
+    
+    int row = index.row();
+    
+    if (row == videos.size()) {
+        
+        QPalette palette;
+        QFont boldFont;
+        boldFont.setBold(true);
+        
+        switch (role) {
+        case ItemTypeRole:
+            return ItemTypeShowMore;
+        case Qt::DisplayRole:
+        case Qt::StatusTipRole:
+            if (!errorMessage.isEmpty()) return errorMessage;
+            if (searching) return tr("Searching...");
+            if (canSearchMore) return tr("Show %1 More").arg(MAX_ITEMS);
+            if (videos.isEmpty()) return tr("No videos");
+            else return tr("No more videos");
+        case Qt::TextAlignmentRole:
+            return QVariant(int(Qt::AlignHCenter | Qt::AlignVCenter));
+        case Qt::ForegroundRole:
+            if (!errorMessage.isEmpty())
+                return palette.color(QPalette::ToolTipText);
+            else
+                return palette.color(QPalette::Dark);
+        case Qt::BackgroundColorRole:
+            if (!errorMessage.isEmpty())
+                return palette.color(QPalette::ToolTipBase);
+            else
+                return QVariant();
+        case Qt::FontRole:
+            return boldFont;
+        default:
+            return QVariant();
+        }
+        
+    } else if (row < 0 || row >= videos.size())
+        return QVariant();
+    
+    Video *video = videos.at(row);
+    
+    switch (role) {
+    case ItemTypeRole:
+        return ItemTypeVideo;
+    case VideoRole:
+        return QVariant::fromValue(QPointer<Video>(video));
+    case ActiveTrackRole:
+        return video == m_activeVideo;
+    case Qt::DisplayRole:
+    case Qt::StatusTipRole:
+        return video->title();
+        /*
+        case Qt::ToolTipRole:
+          
+            QString tooltip;
+            if (!element.firstChildElement().text().isEmpty()) {
+                tooltip.append(QString("<b>").append(element.firstChildElement().text()).append("</b><br/>"));
+            }
+            if (!fromDate.isEmpty()) {
+                tooltip.append("<i>Pubblicato il</i> ").append(fromDate);
+            }
+            if (!toDate.isEmpty()) {
+                tooltip.append("<br/><i>Scadenza</i>: ").append(toDate);
+            }
+            tooltip.append("<br/><i>Tipo</i>: ").append(typeName)
+                .append("<br/><i>Id</i>: ").appen    QFont boldFont;
+    boldFont.setBold(true);d(id);
+            return tooltip;
+            */
+        
+        // case StreamUrlRole:
+        // return video->streamUrl();
+    }
+    
+    return QVariant();
+}
+
+void ListModel::setActiveRow( int row) {
+    if ( rowExists( row ) ) {
+        
+        m_activeRow = row;
+        m_activeVideo = videoAt(row);
+        
+        // setStateOfRow( row, Item::Played );
+        
+        int oldactiverow = m_activeRow;
+        
+        if ( rowExists( oldactiverow ) )
+            emit dataChanged( createIndex( oldactiverow, 0 ), createIndex( oldactiverow, columnCount() - 1 ) );
+        
+        emit dataChanged( createIndex( m_activeRow, 0 ), createIndex( m_activeRow, columnCount() - 1 ) );
+        emit activeRowChanged(row);
+        
+    } else {
+        m_activeRow = -1;
+        m_activeVideo = 0;
+    }
+
+}
+
+int ListModel::nextRow() const {
+    int nextRow = m_activeRow + 1;
+    if (rowExists(nextRow))
+        return nextRow;
+    return -1;
+}
+
+Video* ListModel::videoAt( int row ) const {
+    if ( rowExists( row ) )
+        return videos.at( row );
+    return 0;
+}
+
+Video* ListModel::activeVideo() const {
+    return m_activeVideo;
+}
+
+void ListModel::search(SearchParams *searchParams) {
+
+    // delete current videos
+    while (!videos.isEmpty())
+        delete videos.takeFirst();
+    m_activeVideo = 0;
+    m_activeRow = -1;
+    skip = 1;
+    errorMessage.clear();
+    reset();
+
+    // (re)initialize the YouTubeSearch
+    if (youtubeSearch) delete youtubeSearch;
+    youtubeSearch = new YouTubeSearch();
+    connect(youtubeSearch, SIGNAL(gotVideo(Video*)), this, SLOT(addVideo(Video*)));
+    connect(youtubeSearch, SIGNAL(finished(int)), this, SLOT(searchFinished(int)));
+    connect(youtubeSearch, SIGNAL(error(QString)), this, SLOT(searchError(QString)));
+
+    this->searchParams = searchParams;
+    searching = true;
+    youtubeSearch->search(searchParams, MAX_ITEMS, skip);
+    skip += MAX_ITEMS;
+}
+
+void ListModel::searchMore(int max) {
+    if (searching) return;
+    searching = true;
+    errorMessage.clear();
+    youtubeSearch->search(searchParams, max, skip);
+    skip += max;
+}
+
+void ListModel::searchMore() {
+    searchMore(MAX_ITEMS);
+}
+
+void ListModel::searchNeeded() {
+    int remainingRows = videos.size() - m_activeRow;
+    int rowsNeeded = MAX_ITEMS - remainingRows;
+    if (rowsNeeded > 0)
+        searchMore(rowsNeeded);
+}
+
+void ListModel::abortSearch() {
+    while (!videos.isEmpty())
+        delete videos.takeFirst();
+    reset();
+    youtubeSearch->abort();
+    searching = false;
+}
+
+void ListModel::searchFinished(int total) {
+    searching = false;
+    canSearchMore = total > 0;
+
+    // update the message item
+    emit dataChanged( createIndex( MAX_ITEMS, 0 ), createIndex( MAX_ITEMS, columnCount() - 1 ) );
+}
+
+void ListModel::searchError(QString message) {
+    errorMessage = message;
+    // update the message item
+    emit dataChanged( createIndex( MAX_ITEMS, 0 ), createIndex( MAX_ITEMS, columnCount() - 1 ) );
+}
+
+void ListModel::addVideo(Video* video) {
+    
+    connect(video, SIGNAL(gotThumbnail()), this, SLOT(updateThumbnail()));
+
+    beginInsertRows(QModelIndex(), videos.size(), videos.size());
+    videos << video;
+    endInsertRows();
+    
+    // first result!
+    if (videos.size() == 1) {
+        // autoplay
+        setActiveRow(0);
+
+        // save keyword
+        QString query = searchParams->keywords();
+        QSettings settings;
+        QStringList keywords = settings.value(recentKeywordsKey).toStringList();
+        keywords.removeAll(query);
+        keywords.prepend(query);
+        while (keywords.size() > 10)
+            keywords.removeLast();
+        settings.setValue(recentKeywordsKey, keywords);
+    }
+
+}
+
+void ListModel::updateThumbnail() {
+
+    Video *video = static_cast<Video *>(sender());
+    if (!video) {
+        qDebug() << "Cannot get sender";
+        return;
+    }
+
+    int row = rowForVideo(video);
+    emit dataChanged( createIndex( row, 0 ), createIndex( row, columnCount() - 1 ) );
+
+}
+
+// --- item removal
+
+/**
+  * This function does not free memory
+  */
+bool ListModel::removeRows(int position, int rows, const QModelIndex & /*parent*/) {
+    beginRemoveRows(QModelIndex(), position, position+rows-1);
+    for (int row = 0; row < rows; ++row) {
+        videos.removeAt(position);
+    }
+    endRemoveRows();
+    return true;
+}
+
+void ListModel::removeIndexes(QModelIndexList &indexes) {
+    QList<Video*> originalList(videos);
+    QList<Video*> delitems;
+    foreach (QModelIndex index, indexes) {
+        Video* video = originalList.at(index.row());
+        int idx = videos.indexOf(video);
+        if (idx != -1) {
+            beginRemoveRows(QModelIndex(), idx, idx);
+            delitems.append(video);
+            videos.removeAll(video);
+            endRemoveRows();
+        }
+    }
+
+    qDeleteAll(delitems);
+
+}
+
+// --- Sturm und drang ---
+
+
+
+Qt::DropActions ListModel::supportedDropActions() const {
+    return Qt::MoveAction;
+}
+
+Qt::ItemFlags ListModel::flags(const QModelIndex &index) const {
+    Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index);
+
+    if (index.isValid()) {
+        if (index.row() == videos.size()) {
+            // don't drag the "show 10 more" item
+            return defaultFlags;
+        } else
+            return ( defaultFlags | Qt::ItemIsDropEnabled | Qt::ItemIsDragEnabled );
+    } else
+        return Qt::ItemIsDropEnabled | defaultFlags;
+}
+
+QStringList ListModel::mimeTypes() const {
+    QStringList types;
+    types << "application/x-minitube-video";
+    return types;
+}
+
+QMimeData* ListModel::mimeData( const QModelIndexList &indexes ) const {
+    VideoMimeData* mime = new VideoMimeData();
+
+    foreach( const QModelIndex &it, indexes ) {
+        int row = it.row();
+        if (row >= 0 && row < videos.size())
+            mime->addVideo( videos.at( it.row() ) );
+    }
+
+    return mime;
+}
+
+bool ListModel::dropMimeData(const QMimeData *data,
+                             Qt::DropAction action, int row, int column,
+                             const QModelIndex &parent) {
+    if (action == Qt::IgnoreAction)
+        return true;
+
+    if (!data->hasFormat("application/x-minitube-video"))
+        return false;
+
+    if (column > 0)
+        return false;
+
+    int beginRow;
+    if (row != -1)
+        beginRow = row;
+    else if (parent.isValid())
+        beginRow = parent.row();
+    else
+        beginRow = rowCount(QModelIndex());
+
+    const VideoMimeData* videoMimeData = dynamic_cast<const VideoMimeData*>( data );
+    if(!videoMimeData ) return false;
+
+    QList<Video*> droppedVideos = videoMimeData->videos();
+    foreach( Video *video, droppedVideos) {
+        
+        // remove videos
+        int videoRow = videos.indexOf(video);
+        removeRows(videoRow, 1, QModelIndex());
+        
+        // and then add them again at the new position
+        beginInsertRows(QModelIndex(), beginRow, beginRow);
+        videos.insert(beginRow, video);
+        endInsertRows();
+
+    }
+
+    // fix m_activeRow after all this
+    m_activeRow = videos.indexOf(m_activeVideo);
+
+    // let the MediaView restore the selection
+    emit needSelectionFor(droppedVideos);
+
+    return true;
+
+}
+
+int ListModel::rowForVideo(Video* video) {
+    return videos.indexOf(video);
+}
+
+QModelIndex ListModel::indexForVideo(Video* video) {
+    return createIndex(videos.indexOf(video), 0);
+}
+
+void ListModel::move(QModelIndexList &indexes, bool up) {
+
+    QList<Video*> movedVideos;
+
+    foreach (QModelIndex index, indexes) {
+        int row = index.row();
+        // qDebug() << "index row" << row;
+        Video *video = videoAt(row);
+        movedVideos << video;
+    }
+
+    int counter = 1;
+    foreach (Video *video, movedVideos) {
+
+        int row = rowForVideo(video);
+        // qDebug() << "video row" << row;
+        removeRows(row, 1, QModelIndex());
+
+        if (up) row--;
+        else row++;
+
+        beginInsertRows(QModelIndex(), row, row);
+        videos.insert(row, video);
+        endInsertRows();
+
+        counter++;
+    }
+
+    emit needSelectionFor(movedVideos);
+
+}
diff --git a/src/ListModel.h b/src/ListModel.h
new file mode 100755 (executable)
index 0000000..c4c078f
--- /dev/null
@@ -0,0 +1,91 @@
+#ifndef LISTMODEL_H
+#define LISTMODEL_H
+
+#include "video.h"
+#include "youtubesearch.h"
+#include "searchparams.h"
+
+enum DataRoles {
+    ItemTypeRole = Qt::UserRole,
+    VideoRole,
+    ActiveTrackRole
+};
+
+enum ItemTypes {
+    ItemTypeVideo = 1,
+    ItemTypeShowMore
+};
+
+class ListModel : public QAbstractListModel {
+
+    Q_OBJECT
+
+public:
+
+    ListModel(QWidget *parent);
+    ~ListModel();
+
+    // inherited from QAbstractListModel
+    int rowCount(const QModelIndex &parent = QModelIndex()) const;
+    // int rowCount( const QModelIndex& parent = QModelIndex() ) const { Q_UNUSED( parent ); return videos.size(); }
+    int columnCount( const QModelIndex& parent = QModelIndex() ) const { Q_UNUSED( parent ); return 4; }
+    QVariant data(const QModelIndex &index, int role) const;
+    bool removeRows(int position, int rows, const QModelIndex &parent);
+
+    Qt::ItemFlags flags(const QModelIndex &index) const;
+    QStringList mimeTypes() const;
+    Qt::DropActions supportedDropActions() const;
+    QMimeData* mimeData( const QModelIndexList &indexes ) const;
+    bool dropMimeData(const QMimeData *data,
+                      Qt::DropAction action, int row, int column,
+                      const QModelIndex &parent);
+
+    // custom methods
+    void setActiveRow( int row );
+    bool rowExists( int row ) const { return (( row >= 0 ) && ( row < videos.size() ) ); }
+    int activeRow() const { return m_activeRow; } // returns -1 if there is no active row
+    int nextRow() const;
+    void removeIndexes(QModelIndexList &indexes);
+    int rowForVideo(Video* video);
+    QModelIndex indexForVideo(Video* video);
+    void move(QModelIndexList &indexes, bool up);
+
+    Video* videoAt( int row ) const;
+    Video* activeVideo() const;
+
+    // video search methods
+    void search(SearchParams *searchParams);
+    void abortSearch();
+
+
+public slots:
+    void searchMore();
+    void searchNeeded();
+    void addVideo(Video* video);
+    void searchFinished(int total);
+    void searchError(QString message);
+    void updateThumbnail();
+
+signals:
+    void activeRowChanged(int);
+    void needSelectionFor(QList<Video*>);
+
+private:
+    void searchMore(int max);
+
+    YouTubeSearch *youtubeSearch;
+    SearchParams *searchParams;
+    bool searching;
+    bool canSearchMore;
+
+    QList<Video*> videos;
+    int skip;
+
+    // the row being played
+    int m_activeRow;
+    Video *m_activeVideo;
+
+    QString errorMessage;
+};
+
+#endif
diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp
new file mode 100755 (executable)
index 0000000..31398c4
--- /dev/null
@@ -0,0 +1,801 @@
+#include "MainWindow.h"
+#include "spacer.h"
+#include "Constants.h"
+#include "iconloader/qticonloader.h"
+#include "global.h"
+#include "videodefinition.h"
+
+MainWindow::MainWindow() :
+        mediaObject(0),
+        audioOutput(0),
+        aboutView(0),
+        m_fullscreen(false) {
+
+    // views mechanism
+    history = new QStack<QWidget*>();
+    views = new QStackedWidget(this);
+
+    // views
+    searchView = new SearchView(this);
+    connect(searchView, SIGNAL(search(QString)), this, SLOT(showMedia(QString)));
+    views->addWidget(searchView);
+
+    mediaView = new MediaView(this);
+    views->addWidget(mediaView);
+
+    toolbarSearch = new SearchLineEdit(this);
+    toolbarSearch->setFont(qApp->font());
+    toolbarSearch->setMinimumWidth(toolbarSearch->fontInfo().pixelSize()*15);
+    connect(toolbarSearch, SIGNAL(search(const QString&)), searchView, SLOT(watch(const QString&)));
+
+    // build ui
+    createActions();
+    createMenus();
+    createToolBars();
+    createStatusBar();
+
+    initPhonon();
+    mediaView->setMediaObject(mediaObject);
+
+    // remove that useless menu/toolbar context menu
+    this->setContextMenuPolicy(Qt::NoContextMenu);
+
+    // mediaView init stuff thats needs actions
+    mediaView->initialize();
+
+    // restore window position
+    readSettings();
+
+    // cool toolbar on the Mac
+    // this is too buggy to be enabled
+    // setUnifiedTitleAndToolBarOnMac(true);
+
+    // event filter to block ugly toolbar tooltips
+    qApp->installEventFilter(this);
+
+    // show the initial view
+    showWidget(searchView);
+
+    setCentralWidget(views);
+}
+
+MainWindow::~MainWindow() {
+    delete history;
+}
+
+bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
+    if (event->type() == QEvent::ToolTip) {
+        // kill tooltips
+        return true;
+    } else {
+        // standard event processing
+        return QObject::eventFilter(obj, event);
+    }
+}
+
+void MainWindow::createActions() {
+
+    QMap<QString, QAction*> *actions = The::globalActions();
+
+    stopAct = new QAction(QtIconLoader::icon("media-playback-stop", QIcon(":/images/media-playback-stop.png")), tr("&Stop"), this);
+    stopAct->setStatusTip(tr("Stop playback and go back to the search view"));
+    stopAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::Key_Escape) << QKeySequence(Qt::Key_MediaStop));
+    actions->insert("stop", stopAct);
+    connect(stopAct, SIGNAL(triggered()), this, SLOT(stop()));
+
+    skipAct = new QAction(QtIconLoader::icon("media-skip-forward", QIcon(":/images/media-skip-forward.png")), tr("S&kip"), this);
+    skipAct->setStatusTip(tr("Skip to the next video"));
+    skipAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::CTRL + Qt::Key_Right) << QKeySequence(Qt::Key_MediaNext));
+    skipAct->setEnabled(false);
+    actions->insert("skip", skipAct);
+    connect(skipAct, SIGNAL(triggered()), mediaView, SLOT(skip()));
+
+    pauseAct = new QAction(QtIconLoader::icon("media-playback-pause", QIcon(":/images/media-playback-pause.png")), tr("&Pause"), this);
+    pauseAct->setStatusTip(tr("Pause playback"));
+    pauseAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::Key_Space) << QKeySequence(Qt::Key_MediaPlay));
+    pauseAct->setEnabled(false);
+    actions->insert("pause", pauseAct);
+    connect(pauseAct, SIGNAL(triggered()), mediaView, SLOT(pause()));
+
+    fullscreenAct = new QAction(QtIconLoader::icon("view-fullscreen", QIcon(":/images/view-fullscreen.png")), tr("&Full Screen"), this);
+    fullscreenAct->setStatusTip(tr("Go full screen"));
+    fullscreenAct->setShortcut(QKeySequence(Qt::ALT + Qt::Key_Return));
+    fullscreenAct->setShortcutContext(Qt::ApplicationShortcut);
+    actions->insert("fullscreen", fullscreenAct);
+    connect(fullscreenAct, SIGNAL(triggered()), this, SLOT(fullscreen()));
+
+    compactViewAct = new QAction(tr("&Compact mode"), this);
+    compactViewAct->setStatusTip(tr("Hide the playlist and the toolbar"));
+    compactViewAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Return));
+    compactViewAct->setCheckable(true);
+    compactViewAct->setChecked(false);
+    compactViewAct->setEnabled(false);
+    actions->insert("compactView", compactViewAct);
+    connect(compactViewAct, SIGNAL(toggled(bool)), this, SLOT(compactView(bool)));
+
+    webPageAct = new QAction(tr("Open the &YouTube page"), this);
+    webPageAct->setStatusTip(tr("Go to the YouTube video page and pause playback"));
+    webPageAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Y));
+    webPageAct->setEnabled(false);
+    actions->insert("webpage", webPageAct);
+    connect(webPageAct, SIGNAL(triggered()), mediaView, SLOT(openWebPage()));
+
+    copyPageAct = new QAction(tr("Copy the YouTube &link"), this);
+    copyPageAct->setStatusTip(tr("Copy the current video YouTube link to the clipboard"));
+    copyPageAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_L));
+    copyPageAct->setEnabled(false);
+    actions->insert("pagelink", copyPageAct);
+    connect(copyPageAct, SIGNAL(triggered()), mediaView, SLOT(copyWebPage()));
+
+    copyLinkAct = new QAction(tr("Copy the video stream &URL"), this);
+    copyLinkAct->setStatusTip(tr("Copy the current video stream URL to the clipboard"));
+    copyLinkAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_U));
+    copyLinkAct->setEnabled(false);
+    actions->insert("videolink", copyLinkAct);
+    connect(copyLinkAct, SIGNAL(triggered()), mediaView, SLOT(copyVideoLink()));
+
+    removeAct = new QAction(tr("&Remove"), this);
+    removeAct->setStatusTip(tr("Remove the selected videos from the playlist"));
+    removeAct->setShortcuts(QList<QKeySequence>() << QKeySequence("Del") << QKeySequence("Backspace"));
+    removeAct->setEnabled(false);
+    actions->insert("remove", removeAct);
+    connect(removeAct, SIGNAL(triggered()), mediaView, SLOT(removeSelected()));
+
+    moveUpAct = new QAction(tr("Move &Up"), this);
+    moveUpAct->setStatusTip(tr("Move up the selected videos in the playlist"));
+    moveUpAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Up));
+    moveUpAct->setEnabled(false);
+    actions->insert("moveUp", moveUpAct);
+    connect(moveUpAct, SIGNAL(triggered()), mediaView, SLOT(moveUpSelected()));
+
+    moveDownAct = new QAction(tr("Move &Down"), this);
+    moveDownAct->setStatusTip(tr("Move down the selected videos in the playlist"));
+    moveDownAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Down));
+    moveDownAct->setEnabled(false);
+    actions->insert("moveDown", moveDownAct);
+    connect(moveDownAct, SIGNAL(triggered()), mediaView, SLOT(moveDownSelected()));
+
+    clearAct = new QAction(tr("&Clear recent keywords"), this);
+    clearAct->setMenuRole(QAction::ApplicationSpecificRole);
+    clearAct->setShortcuts(QList<QKeySequence>()
+                           << QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Delete)
+                           << QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Backspace));
+    clearAct->setStatusTip(tr("Clear the search history. Cannot be undone."));
+    clearAct->setEnabled(true);
+    actions->insert("clearRecentKeywords", clearAct);
+    connect(clearAct, SIGNAL(triggered()), SLOT(clearRecentKeywords()));
+
+    quitAct = new QAction(tr("&Quit"), this);
+    quitAct->setMenuRole(QAction::QuitRole);
+    quitAct->setShortcuts(QList<QKeySequence>() << QKeySequence(tr("Ctrl+Q")) << QKeySequence(Qt::CTRL + Qt::Key_W));
+    quitAct->setStatusTip(tr("Bye"));
+    actions->insert("quit", quitAct);
+    connect(quitAct, SIGNAL(triggered()), this, SLOT(quit()));
+
+    siteAct = new QAction(tr("&Website"), this);
+    siteAct->setShortcut(QKeySequence::HelpContents);
+    siteAct->setStatusTip(tr("%1 on the Web").arg(Constants::APP_NAME));
+    actions->insert("site", siteAct);
+    connect(siteAct, SIGNAL(triggered()), this, SLOT(visitSite()));
+
+    donateAct = new QAction(tr("Make a &donation"), this);
+    donateAct->setStatusTip(tr("Please support the continued development of %1").arg(Constants::APP_NAME));
+    actions->insert("donate", donateAct);
+    connect(donateAct, SIGNAL(triggered()), this, SLOT(donate()));
+
+    aboutAct = new QAction(tr("&About"), this);
+    aboutAct->setMenuRole(QAction::AboutRole);
+    aboutAct->setStatusTip(tr("Info about %1").arg(Constants::APP_NAME));
+    actions->insert("about", aboutAct);
+    connect(aboutAct, SIGNAL(triggered()), this, SLOT(about()));
+
+    // Invisible actions
+
+    searchFocusAct = new QAction(this);
+    searchFocusAct->setShortcut(QKeySequence::Find);
+    searchFocusAct->setStatusTip(tr("Search"));
+    actions->insert("search", searchFocusAct);
+    connect(searchFocusAct, SIGNAL(triggered()), this, SLOT(searchFocus()));
+    addAction(searchFocusAct);
+
+    volumeUpAct = new QAction(this);
+    volumeUpAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::CTRL + Qt::Key_Plus) << QKeySequence(Qt::Key_VolumeUp));
+    actions->insert("volume-up", volumeUpAct);
+    connect(volumeUpAct, SIGNAL(triggered()), this, SLOT(volumeUp()));
+    addAction(volumeUpAct);
+
+    volumeDownAct = new QAction(this);
+    volumeDownAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::CTRL + Qt::Key_Minus) << QKeySequence(Qt::Key_VolumeDown));
+    actions->insert("volume-down", volumeDownAct);
+    connect(volumeDownAct, SIGNAL(triggered()), this, SLOT(volumeDown()));
+    addAction(volumeDownAct);
+
+    volumeMuteAct = new QAction(this);
+    volumeMuteAct->setStatusTip(tr("Mute volume"));
+    volumeMuteAct->setShortcuts(QList<QKeySequence>() << QKeySequence(tr("Ctrl+M")) << QKeySequence(Qt::Key_VolumeMute));
+    actions->insert("volume-mute", volumeMuteAct);
+    connect(volumeMuteAct, SIGNAL(triggered()), this, SLOT(volumeMute()));
+    addAction(volumeMuteAct);
+
+    QAction *definitionAct = new QAction(this);
+    definitionAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::CTRL + Qt::Key_D));
+    /*
+    QMenu *definitionMenu = new QMenu(this);
+    foreach (QString definition, VideoDefinition::getDefinitionNames()) {
+        definitionMenu->addAction(definition);
+    }
+    definitionAct->setMenu(definitionMenu);
+    */
+    actions->insert("definition", definitionAct);
+    connect(definitionAct, SIGNAL(triggered()), SLOT(toggleDefinitionMode()));
+    addAction(definitionAct);
+
+    // common action properties
+    foreach (QAction *action, actions->values()) {
+
+        // add actions to the MainWindow so that they work
+        // when the menu is hidden
+        addAction(action);
+
+        // never autorepeat.
+        // unexperienced users tend to keep keys pressed for a "long" time
+        action->setAutoRepeat(false);
+
+        // set to something more meaningful then the toolbar text
+        // HELP! how to remove tooltips altogether?!
+        if (!action->statusTip().isEmpty())
+            action->setToolTip(action->statusTip());
+
+        // show keyboard shortcuts in the status bar
+        if (!action->shortcut().isEmpty())
+            action->setStatusTip(action->statusTip() + " (" + action->shortcut().toString(QKeySequence::NativeText) + ")");
+
+        // no icons in menus
+        action->setIconVisibleInMenu(false);
+
+    }
+
+}
+
+void MainWindow::createMenus() {
+
+    QMap<QString, QMenu*> *menus = The::globalMenus();
+
+    fileMenu = menuBar()->addMenu(tr("&Application"));
+    // menus->insert("file", fileMenu);
+    fileMenu->addAction(clearAct);
+#ifndef Q_WS_MAC
+    fileMenu->addSeparator();
+#endif
+    fileMenu->addAction(quitAct);
+
+    playlistMenu = menuBar()->addMenu(tr("&Playlist"));
+    menus->insert("playlist", playlistMenu);
+    playlistMenu->addAction(removeAct);
+    playlistMenu->addSeparator();
+    playlistMenu->addAction(moveUpAct);
+    playlistMenu->addAction(moveDownAct);
+
+    viewMenu = menuBar()->addMenu(tr("&Video"));
+    menus->insert("video", viewMenu);
+    viewMenu->addAction(stopAct);
+    viewMenu->addAction(pauseAct);
+    viewMenu->addAction(skipAct);
+    viewMenu->addSeparator();
+    viewMenu->addAction(webPageAct);
+    viewMenu->addAction(copyPageAct);
+    viewMenu->addAction(copyLinkAct);
+    viewMenu->addSeparator();
+    viewMenu->addAction(compactViewAct);
+    viewMenu->addAction(fullscreenAct);
+
+    helpMenu = menuBar()->addMenu(tr("&Help"));
+    helpMenu->addAction(siteAct);
+    helpMenu->addAction(donateAct);
+    helpMenu->addAction(aboutAct);
+}
+
+void MainWindow::createToolBars() {
+
+    mainToolBar = new QToolBar(this);
+#if QT_VERSION < 0x040600 || defined(Q_WS_MAC)
+    mainToolBar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
+#else
+    mainToolBar->setToolButtonStyle(Qt::ToolButtonFollowStyle);
+#endif
+    mainToolBar->setFloatable(false);
+    mainToolBar->setMovable(false);
+
+    QFont smallerFont;
+    smallerFont.setPointSize(smallerFont.pointSize()*.85);
+    QFontInfo fontInfo(smallerFont);
+    if (fontInfo.pixelSize() < 10) {
+        smallerFont.setPixelSize(10);
+    }
+    mainToolBar->setFont(smallerFont);
+
+#ifdef Q_WS_MAC
+    mainToolBar->setIconSize(QSize(32, 32));
+#endif
+
+    mainToolBar->addAction(stopAct);
+    mainToolBar->addAction(pauseAct);
+    mainToolBar->addAction(skipAct);
+    mainToolBar->addAction(fullscreenAct);
+
+    seekSlider = new Phonon::SeekSlider(this);
+    seekSlider->setIconVisible(false);
+    Spacer *seekSliderSpacer = new Spacer(mainToolBar, seekSlider);
+    seekSliderSpacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+    mainToolBar->addWidget(seekSliderSpacer);
+
+    volumeSlider = new Phonon::VolumeSlider(this);
+    // qDebug() << volumeSlider->children();
+    // status tip for the volume slider
+    QSlider* volumeQSlider = volumeSlider->findChild<QSlider*>();
+    if (volumeQSlider)
+        volumeQSlider->setStatusTip(tr("Press %1 to raise the volume, %2 to lower it").arg(
+                volumeUpAct->shortcut().toString(QKeySequence::NativeText), volumeDownAct->shortcut().toString(QKeySequence::NativeText)));
+    // status tip for the mute button
+    QToolButton* muteToolButton = volumeSlider->findChild<QToolButton*>();
+    if (muteToolButton)
+        muteToolButton->setStatusTip(volumeMuteAct->statusTip());
+    // this makes the volume slider smaller
+    volumeSlider->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+    mainToolBar->addWidget(new Spacer(mainToolBar, volumeSlider));
+
+    toolbarSearch->setStatusTip(searchFocusAct->statusTip());
+    mainToolBar->addWidget(new Spacer(mainToolBar, toolbarSearch));
+
+    addToolBar(mainToolBar);
+}
+
+void MainWindow::createStatusBar() {
+
+    // remove ugly borders on OSX
+    // also remove excessive spacing
+    statusBar()->setStyleSheet("::item{border:0 solid} QToolBar {padding:0;spacing:0;margin:0}");
+
+    currentTime = new QLabel(this);
+    statusBar()->addPermanentWidget(currentTime);
+
+    totalTime = new QLabel(this);
+    statusBar()->addPermanentWidget(totalTime);
+
+    QToolBar *toolBar = new QToolBar(this);
+    toolBar->setToolButtonStyle(Qt::ToolButtonTextOnly);
+    toolBar->addAction(The::globalActions()->value("definition"));
+    statusBar()->addPermanentWidget(toolBar);
+
+    statusBar()->show();
+}
+
+void MainWindow::readSettings() {
+    QSettings settings;
+    restoreGeometry(settings.value("geometry").toByteArray());
+    setDefinitionMode(settings.value("definition", VideoDefinition::getDefinitionNames().first()).toString());
+    audioOutput->setVolume(settings.value("volume", 1).toDouble());
+    audioOutput->setMuted(settings.value("volumeMute").toBool());
+}
+
+void MainWindow::writeSettings() {
+    // do not save geometry when in full screen
+    if (m_fullscreen)
+        return;
+    QSettings settings;
+    settings.setValue("geometry", saveGeometry());
+    settings.setValue("volume", audioOutput->volume());
+    settings.setValue("volumeMute", audioOutput->isMuted());
+    mediaView->saveSplitterState();
+}
+
+void MainWindow::goBack() {
+    if ( history->size() > 1 ) {
+        history->pop();
+        QWidget *widget = history->pop();
+        showWidget(widget);
+    }
+}
+
+void MainWindow::showWidget ( QWidget* widget ) {
+
+    setUpdatesEnabled(false);
+
+    // call hide method on the current view
+    View* oldView = dynamic_cast<View *> (views->currentWidget());
+    if (oldView) {
+        oldView->disappear();
+    }
+
+    // call show method on the new view
+    View* newView = dynamic_cast<View *> (widget);
+    if (newView) {
+        newView->appear();
+        QMap<QString,QVariant> metadata = newView->metadata();
+        QString windowTitle = metadata.value("title").toString();
+        if (windowTitle.length())
+            windowTitle += " - ";
+        setWindowTitle(windowTitle + Constants::APP_NAME);
+        statusBar()->showMessage((metadata.value("description").toString()));
+    }
+
+    stopAct->setEnabled(widget == mediaView);
+    fullscreenAct->setEnabled(widget == mediaView);
+    compactViewAct->setEnabled(widget == mediaView);
+    webPageAct->setEnabled(widget == mediaView);
+    copyPageAct->setEnabled(widget == mediaView);
+    copyLinkAct->setEnabled(widget == mediaView);
+    aboutAct->setEnabled(widget != aboutView);
+
+    // toolbar only for the mediaView
+    mainToolBar->setVisible(widget == mediaView && !compactViewAct->isChecked());
+
+    setUpdatesEnabled(true);
+
+    QWidget *oldWidget = views->currentWidget();
+    views->setCurrentWidget(widget);
+
+#ifdef Q_WS_MAC
+    // crossfade only on OSX
+    // where we can be sure of video performance
+    fadeInWidget(oldWidget, widget);
+#endif
+
+    history->push(widget);
+}
+
+void MainWindow::fadeInWidget(QWidget *oldWidget, QWidget *newWidget) {
+    if (faderWidget) faderWidget->close();
+    if (!oldWidget || !newWidget) {
+        // qDebug() << "no widgets";
+        return;
+    }
+    faderWidget = new FaderWidget(newWidget);
+    faderWidget->start(QPixmap::grabWidget(oldWidget));
+}
+
+void MainWindow::about() {
+    if (!aboutView) {
+        aboutView = new AboutView(this);
+        views->addWidget(aboutView);
+    }
+    showWidget(aboutView);
+}
+
+void MainWindow::visitSite() {
+    QUrl url(Constants::WEBSITE);
+    statusBar()->showMessage(QString(tr("Opening %1").arg(url.toString())));
+    QDesktopServices::openUrl(url);
+}
+
+void MainWindow::donate() {
+    QUrl url(QString(Constants::WEBSITE) + "#donate");
+    statusBar()->showMessage(QString(tr("Opening %1").arg(url.toString())));
+    QDesktopServices::openUrl(url);
+}
+
+void MainWindow::quit() {
+    writeSettings();
+    qApp->quit();
+}
+
+void MainWindow::closeEvent(QCloseEvent *event) {
+    quit();
+    QWidget::closeEvent(event);
+}
+
+/*
+void MainWindow::showSettings() {
+    if (!settingsView) {
+        settingsView = new SettingsView(this);
+        views->addWidget(settingsView);
+    }
+    showWidget(settingsView);
+}*/
+
+void MainWindow::showSearch() {
+    showWidget(searchView);
+    currentTime->clear();
+    totalTime->clear();
+}
+
+void MainWindow::showMedia(QString query) {
+    SearchParams *searchParams = new SearchParams();
+    searchParams->setKeywords(query);
+    mediaView->search(searchParams);
+    showWidget(mediaView);
+}
+
+void MainWindow::stateChanged(Phonon::State newState, Phonon::State /* oldState */) {
+
+    // qDebug() << "Phonon state: " << newState;
+
+    switch (newState) {
+
+    case Phonon::ErrorState:
+        if (mediaObject->errorType() == Phonon::FatalError) {
+            statusBar()->showMessage(tr("Fatal error: %1").arg(mediaObject->errorString()));
+        } else {
+            statusBar()->showMessage(tr("Error: %1").arg(mediaObject->errorString()));
+        }
+        break;
+
+         case Phonon::PlayingState:
+        pauseAct->setEnabled(true);
+        pauseAct->setIcon(QtIconLoader::icon("media-playback-pause", QIcon(":/images/media-playback-pause.png")));
+        pauseAct->setText(tr("&Pause"));
+        pauseAct->setStatusTip(tr("Pause playback") + " (" +  pauseAct->shortcut().toString(QKeySequence::NativeText) + ")");
+        skipAct->setEnabled(true);
+        break;
+
+         case Phonon::StoppedState:
+        pauseAct->setEnabled(false);
+        skipAct->setEnabled(false);
+        break;
+
+         case Phonon::PausedState:
+        skipAct->setEnabled(true);
+        pauseAct->setEnabled(true);
+        pauseAct->setIcon(QtIconLoader::icon("media-playback-start", QIcon(":/images/media-playback-start.png")));
+        pauseAct->setText(tr("&Play"));
+        pauseAct->setStatusTip(tr("Resume playback") + " (" +  pauseAct->shortcut().toString(QKeySequence::NativeText) + ")");
+        break;
+
+         case Phonon::BufferingState:
+         case Phonon::LoadingState:
+        skipAct->setEnabled(true);
+        pauseAct->setEnabled(false);
+        currentTime->clear();
+        totalTime->clear();
+        break;
+
+         default:
+        ;
+    }
+}
+
+void MainWindow::stop() {
+    mediaView->stop();
+    showSearch();
+}
+
+void MainWindow::fullscreen() {
+
+    setUpdatesEnabled(false);
+
+    // No compact view action when in full screen
+    compactViewAct->setVisible(m_fullscreen);
+    compactViewAct->setChecked(false);
+
+    // Also no Youtube action since it opens a new window
+    webPageAct->setVisible(m_fullscreen);
+    copyPageAct->setVisible(m_fullscreen);
+    copyLinkAct->setVisible(m_fullscreen);
+
+    stopAct->setVisible(m_fullscreen);
+
+    // workaround: prevent focus on the search bar
+    // it steals the Space key needed for Play/Pause
+    mainToolBar->setEnabled(m_fullscreen);
+
+    // Hide anything but the video
+    mediaView->setPlaylistVisible(m_fullscreen);
+    statusBar()->setVisible(m_fullscreen);
+
+#ifndef Q_WS_MAC
+    menuBar()->setVisible(m_fullscreen);
+#endif
+
+#ifdef Q_WS_MAC
+    // make the actions work when video is fullscreen (on the Mac)
+    QMap<QString, QAction*> *actions = The::globalActions();
+    foreach (QAction *action, actions->values()) {
+        if (m_fullscreen) {
+            action->setShortcutContext(Qt::WindowShortcut);
+        } else {
+            action->setShortcutContext(Qt::ApplicationShortcut);
+        }
+    }
+#endif
+
+    if (m_fullscreen) {
+        // use setShortucs instead of setShortcut
+        // the latter seems not to work
+        fullscreenAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::ALT + Qt::Key_Return));
+        fullscreenAct->setText(tr("&Full Screen"));
+        stopAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::Key_Escape) << QKeySequence(Qt::Key_MediaStop));
+
+        mainToolBar->show();
+        if (m_maximized) showMaximized();
+        else showNormal();
+
+        // Make sure the window has focus (Mac)
+        activateWindow();
+
+    } else {
+        stopAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::Key_MediaStop));
+        fullscreenAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::Key_Escape) << QKeySequence(Qt::ALT + Qt::Key_Return));
+        fullscreenAct->setText(tr("Exit &Full Screen"));
+        m_maximized = isMaximized();
+
+        // save geometry now, if the user quits when in full screen
+        // geometry won't be saved
+        writeSettings();
+
+        mainToolBar->hide();
+        showFullScreen();
+    }
+
+    m_fullscreen = !m_fullscreen;
+
+    setUpdatesEnabled(true);
+}
+
+void MainWindow::compactView(bool enable) {
+
+    setUpdatesEnabled(false);
+
+    mainToolBar->setVisible(!enable);
+    mainToolBar->setEnabled(!enable);
+    mediaView->setPlaylistVisible(!enable);
+    statusBar()->setVisible(!enable);
+
+#ifndef Q_WS_MAC
+    menuBar()->setVisible(!enable);
+#endif
+
+    // ensure focus does not end up to the search box
+    // as it would steal the Space shortcut
+    // toolbarSearch->setEnabled(!enable);
+
+    if (enable) {
+        stopAct->setShortcut(QString(""));
+        QList<QKeySequence> shortcuts;
+        // for some reason it is important that ESC comes first
+        shortcuts << QKeySequence(Qt::CTRL + Qt::Key_Return) << QKeySequence(Qt::Key_Escape);
+        compactViewAct->setShortcuts(shortcuts);
+    } else {
+        compactViewAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Return));
+        stopAct->setShortcut(QKeySequence(Qt::Key_Escape));
+    }
+
+    setUpdatesEnabled(true);
+}
+
+void MainWindow::searchFocus() {
+    QWidget *view = views->currentWidget();
+    if (view == mediaView) {
+        toolbarSearch->selectAll();
+        toolbarSearch->setFocus();
+    }
+}
+
+void MainWindow::initPhonon() {
+    // Phonon initialization
+    if (mediaObject) delete mediaObject;
+    if (audioOutput) delete audioOutput;
+    mediaObject = new Phonon::MediaObject(this);
+    mediaObject->setTickInterval(100);
+    connect(mediaObject, SIGNAL(stateChanged(Phonon::State, Phonon::State)),
+            this, SLOT(stateChanged(Phonon::State, Phonon::State)));
+    connect(mediaObject, SIGNAL(tick(qint64)), this, SLOT(tick(qint64)));
+    connect(mediaObject, SIGNAL(totalTimeChanged(qint64)), this, SLOT(totalTimeChanged(qint64)));
+    seekSlider->setMediaObject(mediaObject);
+    audioOutput = new Phonon::AudioOutput(Phonon::VideoCategory, this);
+    connect(audioOutput, SIGNAL(volumeChanged(qreal)), this, SLOT(volumeChanged(qreal)));
+    connect(audioOutput, SIGNAL(mutedChanged(bool)), this, SLOT(volumeMutedChanged(bool)));
+    volumeSlider->setAudioOutput(audioOutput);
+    Phonon::createPath(mediaObject, audioOutput);
+}
+
+void MainWindow::tick(qint64 time) {
+    if (time <= 0) {
+        currentTime->clear();
+        return;
+    }
+
+    currentTime->setText(formatTime(time));
+
+    // remaining time
+    const qint64 remainingTime = mediaObject->remainingTime();
+    currentTime->setStatusTip(tr("Remaining time: %1").arg(formatTime(remainingTime)));
+
+}
+
+void MainWindow::totalTimeChanged(qint64 time) {
+    if (time <= 0) {
+        totalTime->clear();
+        return;
+    }
+    totalTime->setText("/ " + formatTime(time));
+}
+
+QString MainWindow::formatTime(qint64 time) {
+    QTime displayTime;
+    displayTime = displayTime.addMSecs(time);
+    QString timeString;
+    // 60 * 60 * 1000 = 3600000
+    if (time > 3600000)
+        timeString = displayTime.toString("h:mm:ss");
+    else
+        timeString = displayTime.toString("m:ss");
+    return timeString;
+}
+
+void MainWindow::volumeUp() {
+    qreal newVolume = volumeSlider->audioOutput()->volume() + .1;
+    if (newVolume > volumeSlider->maximumVolume())
+        newVolume = volumeSlider->maximumVolume();
+    volumeSlider->audioOutput()->setVolume(newVolume);
+}
+
+void MainWindow::volumeDown() {
+    qreal newVolume = volumeSlider->audioOutput()->volume() - .1;
+    if (newVolume < 0)
+        newVolume = 0;
+    volumeSlider->audioOutput()->setVolume(newVolume);
+}
+
+void MainWindow::volumeMute() {
+    volumeSlider->audioOutput()->setMuted(!volumeSlider->audioOutput()->isMuted());
+}
+
+void MainWindow::volumeChanged(qreal newVolume) {
+    // automatically unmute when volume changes
+    if (volumeSlider->audioOutput()->isMuted())
+        volumeSlider->audioOutput()->setMuted(false);
+    statusBar()->showMessage(tr("Volume at %1%").arg(newVolume*100));
+}
+
+void MainWindow::volumeMutedChanged(bool muted) {
+    if (muted)
+        statusBar()->showMessage(tr("Volume is muted"));
+    else
+        statusBar()->showMessage(tr("Volume is unmuted"));
+}
+
+void MainWindow::setDefinitionMode(QString definitionName) {
+    QAction *definitionAct = The::globalActions()->value("definition");
+    definitionAct->setText(definitionName);
+    definitionAct->setStatusTip(tr("Maximum video definition set to %1").arg(definitionAct->text())
+                                + " (" +  definitionAct->shortcut().toString(QKeySequence::NativeText) + ")");
+    statusBar()->showMessage(definitionAct->statusTip());
+    QSettings settings;
+    settings.setValue("definition", definitionName);
+}
+
+void MainWindow::toggleDefinitionMode() {
+    QSettings settings;
+    QString currentDefinition = settings.value("definition").toString();
+    QStringList definitionNames = VideoDefinition::getDefinitionNames();
+    int currentIndex = definitionNames.indexOf(currentDefinition);
+    int nextIndex = 0;
+    if (currentIndex != definitionNames.size() - 1) {
+        nextIndex = currentIndex + 1;
+    }
+    QString nextDefinition = definitionNames.at(nextIndex);
+    setDefinitionMode(nextDefinition);
+}
+
+void MainWindow::showFullscreenToolbar(bool show) {
+    if (!m_fullscreen) return;
+
+    if (show) {
+        mainToolBar->show();
+    } else {
+        mainToolBar->hide();
+    }
+    mainToolBar->setEnabled(show);
+}
+
+void MainWindow::showFullscreenPlaylist(bool show) {
+    if (!m_fullscreen) return;
+    mediaView->setPlaylistVisible(show);
+}
+
+void MainWindow::clearRecentKeywords() {
+    QSettings settings;
+    settings.remove("recentKeywords");
+    searchView->updateRecentKeywords();
+    statusBar()->showMessage(tr("Your privacy is now safe"));
+}
diff --git a/src/MainWindow.h b/src/MainWindow.h
new file mode 100755 (executable)
index 0000000..1eb0251
--- /dev/null
@@ -0,0 +1,133 @@
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QtGui>
+#include "faderwidget/FaderWidget.h"
+#include "searchlineedit.h"
+#include <phonon/audiooutput.h>
+#include <phonon/volumeslider.h>
+#include <phonon/mediaobject.h>
+#include <phonon/seekslider.h>
+#include "View.h"
+#include "SearchView.h"
+#include "MediaView.h"
+#include "AboutView.h"
+
+class MainWindow : public QMainWindow {
+
+    Q_OBJECT
+
+public:
+    MainWindow();
+    ~MainWindow();
+
+protected:
+    void closeEvent(QCloseEvent *);
+    bool eventFilter(QObject *obj, QEvent *event);
+
+private slots:
+    void fadeInWidget(QWidget *oldWidget, QWidget *newWidget);
+    void goBack();
+    void showSearch();
+    void showMedia(QString query);
+    void visitSite();
+    void donate();
+    void about();
+    void quit();
+    void fullscreen();
+    void compactView(bool enable);
+    void stop();
+    void stateChanged(Phonon::State newState, Phonon::State oldState);
+    void searchFocus();
+    void tick(qint64 time);
+    void totalTimeChanged(qint64 time);
+    void setDefinitionMode(QString definitionName);
+    void toggleDefinitionMode();
+    void clearRecentKeywords();
+
+    // volume shortcuts
+    void volumeUp();
+    void volumeDown();
+    void volumeMute();
+    void volumeChanged(qreal newVolume);
+    void volumeMutedChanged(bool muted);
+
+    // fullscreen toolbar
+    void showFullscreenToolbar(bool show);
+    void showFullscreenPlaylist(bool show);
+
+private:
+    void initPhonon();
+    void createActions();
+    void createMenus();
+    void createToolBars();
+    void createStatusBar();
+    void readSettings();
+    void writeSettings();
+    void showWidget(QWidget*);
+    static QString formatTime(qint64 time);
+
+    // view mechanism
+    QPointer<FaderWidget> faderWidget;
+    QStackedWidget *views;
+    QStack<QWidget*> *history;
+
+    // view widgets
+    SearchView *searchView;
+    MediaView *mediaView;
+    QWidget *aboutView;
+
+    // actions
+    QAction *addGadgetAct;
+    QAction *backAct;
+    QAction *quitAct;
+    QAction *siteAct;
+    QAction *donateAct;
+    QAction *aboutAct;
+    QAction *searchFocusAct;
+
+    // media actions
+    QAction *skipAct;
+    QAction *pauseAct;
+    QAction *stopAct;
+    QAction *fullscreenAct;
+    QAction *compactViewAct;
+    QAction *webPageAct;
+    QAction *copyPageAct;
+    QAction *copyLinkAct;
+    QAction *downloadAct;
+    QAction *volumeUpAct;
+    QAction *volumeDownAct;
+    QAction *volumeMuteAct;
+
+    // playlist actions
+    QAction *removeAct;
+    QAction *moveDownAct;
+    QAction *moveUpAct;
+    QAction *fetchMoreAct;
+    QAction *clearAct;
+
+    // menus
+    QMenu *fileMenu;
+    QMenu *viewMenu;
+    QMenu *playlistMenu;
+    QMenu *helpMenu;
+
+    // toolbar
+    QToolBar *mainToolBar;
+    SearchLineEdit *toolbarSearch;
+
+    // phonon
+    Phonon::SeekSlider *seekSlider;
+    Phonon::VolumeSlider *volumeSlider;
+    Phonon::MediaObject *mediaObject;
+    Phonon::AudioOutput *audioOutput;
+    QLabel *currentTime;
+    QLabel *totalTime;
+
+    bool m_fullscreen;
+    bool m_maximized;
+
+};
+
+#endif
diff --git a/src/MediaView.cpp b/src/MediaView.cpp
new file mode 100644 (file)
index 0000000..8735f8a
--- /dev/null
@@ -0,0 +1,425 @@
+#include "MediaView.h"
+#include "playlist/PrettyItemDelegate.h"
+#include "networkaccess.h"
+#include "videowidget.h"
+#include "minisplitter.h"
+#include "flickcharm.h"
+
+namespace The {
+    QMap<QString, QAction*>* globalActions();
+    QMap<QString, QMenu*>* globalMenus();
+    QNetworkAccessManager* networkAccessManager();
+}
+
+MediaView::MediaView(QWidget *parent) : QWidget(parent) {
+
+    reallyStopped = false;
+
+    QBoxLayout *layout = new QHBoxLayout();
+    layout->setMargin(0);
+
+    splitter = new MiniSplitter(this);
+    splitter->setChildrenCollapsible(false);
+
+    sortBar = new THBlackBar(this);
+    mostRelevantAction = new QAction(tr("Most relevant"), this);
+    QKeySequence keySequence(Qt::CTRL + Qt::Key_1);
+    mostRelevantAction->setShortcut(keySequence);
+    mostRelevantAction->setStatusTip(mostRelevantAction->text() + " (" + keySequence.toString(QKeySequence::NativeText) + ")");
+    addAction(mostRelevantAction);
+    connect(mostRelevantAction, SIGNAL(triggered()), this, SLOT(searchMostRelevant()), Qt::QueuedConnection);
+    sortBar->addAction(mostRelevantAction);
+    mostRecentAction = new QAction(tr("Most recent"), this);
+    keySequence = QKeySequence(Qt::CTRL + Qt::Key_2);
+    mostRecentAction->setShortcut(keySequence);
+    mostRecentAction->setStatusTip(mostRecentAction->text() + " (" + keySequence.toString(QKeySequence::NativeText) + ")");
+    addAction(mostRecentAction);
+    connect(mostRecentAction, SIGNAL(triggered()), this, SLOT(searchMostRecent()), Qt::QueuedConnection);
+    sortBar->addAction(mostRecentAction);
+    mostViewedAction = new QAction(tr("Most viewed"), this);
+    keySequence = QKeySequence(Qt::CTRL + Qt::Key_3);
+    mostViewedAction->setShortcut(keySequence);
+    mostViewedAction->setStatusTip(mostViewedAction->text() + " (" + keySequence.toString(QKeySequence::NativeText) + ")");
+    addAction(mostViewedAction);
+    connect(mostViewedAction, SIGNAL(triggered()), this, SLOT(searchMostViewed()), Qt::QueuedConnection);
+    sortBar->addAction(mostViewedAction);
+
+    listView = new QListView(this);
+    listView->setItemDelegate(new PrettyItemDelegate(this));
+    listView->setSelectionMode(QAbstractItemView::ExtendedSelection);
+
+    // dragndrop
+    listView->setDragEnabled(true);
+    listView->setAcceptDrops(true);
+    listView->setDropIndicatorShown(true);
+    listView->setDragDropMode(QAbstractItemView::DragDrop);
+
+    // cosmetics
+    listView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
+    listView->setFrameShape( QFrame::NoFrame );
+    listView->setAttribute(Qt::WA_MacShowFocusRect, false);
+    listView->setMinimumSize(320,240);
+    listView->setUniformItemSizes(true);
+
+    // respond to the user doubleclicking a playlist item
+    connect(listView, SIGNAL(activated(const QModelIndex &)), this, SLOT(itemActivated(const QModelIndex &)));
+
+    listModel = new ListModel(this);
+    connect(listModel, SIGNAL(activeRowChanged(int)), this, SLOT(activeRowChanged(int)));
+    // needed to restore the selection after dragndrop
+    connect(listModel, SIGNAL(needSelectionFor(QList<Video*>)), this, SLOT(selectVideos(QList<Video*>)));
+    listView->setModel(listModel);
+
+    connect(listView->selectionModel(),
+            SIGNAL(selectionChanged ( const QItemSelection & , const QItemSelection & )),
+            this, SLOT(selectionChanged ( const QItemSelection & , const QItemSelection & )));
+
+    playlistWidget = new PlaylistWidget(this, sortBar, listView);
+
+    splitter->addWidget(playlistWidget);
+
+    videoAreaWidget = new VideoAreaWidget(this);
+    videoAreaWidget->setMinimumSize(320,240);
+
+#ifdef Q_WS_MAC
+    // mouse autohide does not work on the Mac (no mouseMoveEvent)
+    videoWidget = new Phonon::VideoWidget(this);
+#else
+    videoWidget = new VideoWidget(this);
+#endif
+
+    videoAreaWidget->setVideoWidget(videoWidget);
+    videoAreaWidget->setListModel(listModel);
+
+    loadingWidget = new LoadingWidget(this);
+    videoAreaWidget->setLoadingWidget(loadingWidget);
+
+    splitter->addWidget(videoAreaWidget);
+
+    layout->addWidget(splitter);
+    setLayout(layout);
+
+    // restore splitter state
+    QSettings settings;
+    splitter->restoreState(settings.value("splitter").toByteArray());
+
+    errorTimer = new QTimer(this);
+    errorTimer->setSingleShot(true);
+    errorTimer->setInterval(3000);
+    connect(errorTimer, SIGNAL(timeout()), SLOT(skipVideo()));
+
+    workaroundTimer = new QTimer(this);
+    workaroundTimer->setSingleShot(true);
+    workaroundTimer->setInterval(3000);
+    connect(workaroundTimer, SIGNAL(timeout()), SLOT(timerPlay()));
+
+    // TODO Enable this on touch devices
+    // FlickCharm *flickCharm = new FlickCharm(this);
+    // flickCharm->activateOn(listView);
+
+}
+
+MediaView::~MediaView() {
+
+}
+
+void MediaView::initialize() {
+    connect(videoAreaWidget, SIGNAL(doubleClicked()), The::globalActions()->value("fullscreen"), SLOT(trigger()));
+    videoAreaWidget->setContextMenuPolicy(Qt::CustomContextMenu);
+    connect(videoAreaWidget, SIGNAL(customContextMenuRequested(QPoint)),
+            this, SLOT(showVideoContextMenu(QPoint)));
+}
+
+void MediaView::setMediaObject(Phonon::MediaObject *mediaObject) {
+    this->mediaObject = mediaObject;
+    Phonon::createPath(this->mediaObject, videoWidget);
+    connect(mediaObject, SIGNAL(finished()), this, SLOT(skip()));
+    connect(mediaObject, SIGNAL(stateChanged(Phonon::State, Phonon::State)),
+            this, SLOT(stateChanged(Phonon::State, Phonon::State)));
+    connect(mediaObject, SIGNAL(currentSourceChanged(Phonon::MediaSource)),
+            this, SLOT(currentSourceChanged(Phonon::MediaSource)));
+    connect(mediaObject, SIGNAL(bufferStatus(int)), loadingWidget, SLOT(bufferStatus(int)));
+}
+
+void MediaView::search(SearchParams *searchParams) {
+    reallyStopped = false;
+
+    videoAreaWidget->clear();
+    workaroundTimer->stop();
+    errorTimer->stop();
+
+    this->searchParams = searchParams;
+
+    // start serching for videos
+    listModel->search(searchParams);
+
+    // this implies that the enum and the bar action order is the same
+    sortBar->setCheckedAction(searchParams->sortBy()-1);
+
+    listView->setFocus();
+
+}
+
+void MediaView::disappear() {
+    timerPlayFlag = true;
+}
+
+void MediaView::handleError(QString message) {
+    videoAreaWidget->showError(message);
+    skippedVideo = listModel->activeVideo();
+    // recover from errors by skipping to the next video
+    errorTimer->start(2000);
+}
+
+void MediaView::stateChanged(Phonon::State newState, Phonon::State /*oldState*/)
+{
+
+    // qDebug() << "Phonon state: " << newState << oldState;
+
+    switch (newState) {
+
+    case Phonon::ErrorState:
+        qDebug() << "Phonon error:" << mediaObject->errorString() << mediaObject->errorType();
+        handleError(mediaObject->errorString());
+        break;
+
+    case Phonon::PlayingState:
+        //qDebug("playing");
+        videoAreaWidget->showVideo();
+        break;
+
+    case Phonon::StoppedState:
+        //qDebug("stopped");
+        // play() has already been called when setting the source
+        // but Phonon on Linux needs a little more help to start playback
+        if (!reallyStopped) mediaObject->play();
+
+#ifdef Q_WS_MAC
+        // Workaround for Mac playback start problem
+        if (!timerPlayFlag) {
+            workaroundTimer->start();
+        }
+#endif
+
+        break;
+
+         case Phonon::PausedState:
+        //qDebug("paused");
+        break;
+
+         case Phonon::BufferingState:
+        //qDebug("buffering");
+        break;
+
+         case Phonon::LoadingState:
+        //qDebug("loading");
+        break;
+
+         default:
+        ;
+    }
+}
+
+void MediaView::pause() {
+    // qDebug() << "pause() called" << mediaObject->state();
+    switch( mediaObject->state() ) {
+    case Phonon::PlayingState:
+        mediaObject->pause();
+        break;
+    default:
+        mediaObject->play();
+        break;
+    }
+}
+
+void MediaView::stop() {
+    listModel->abortSearch();
+    reallyStopped = true;
+    mediaObject->stop();
+    videoAreaWidget->clear();
+    workaroundTimer->stop();
+    errorTimer->stop();
+    listView->selectionModel()->clearSelection();
+}
+
+void MediaView::activeRowChanged(int row) {
+    if (reallyStopped) return;
+
+    Video *video = listModel->videoAt(row);
+    if (!video) return;
+
+    // now that we have a new video to play
+    // stop all the timers
+    workaroundTimer->stop();
+    errorTimer->stop();
+
+    // immediately show the loading widget
+    videoAreaWidget->showLoading(video);
+
+    connect(video, SIGNAL(gotStreamUrl(QUrl)), SLOT(gotStreamUrl(QUrl)));
+    // TODO handle signal in a proper slot and impl item error status
+    connect(video, SIGNAL(errorStreamUrl(QString)), SLOT(handleError(QString)));
+
+    video->loadStreamUrl();
+
+    // reset the timer flag
+    timerPlayFlag = false;
+
+    // video title in the statusbar
+    QMainWindow* mainWindow = dynamic_cast<QMainWindow*>(window());
+    if (mainWindow) mainWindow->statusBar()->showMessage(video->title());
+
+    // see you in gotStreamUrl...
+
+}
+
+void MediaView::gotStreamUrl(QUrl streamUrl) {
+    if (reallyStopped) return;
+
+    // go!
+    mediaObject->setCurrentSource(streamUrl);
+    mediaObject->play();
+
+    // ensure we always have 10 videos ahead
+    listModel->searchNeeded();
+
+    // ensure active item is visible
+    int row = listModel->activeRow();
+    if (row != -1) {
+        QModelIndex index = listModel->index(row, 0, QModelIndex());
+        listView->scrollTo(index, QAbstractItemView::EnsureVisible);
+    }
+}
+
+void MediaView::itemActivated(const QModelIndex &index) {
+    if (listModel->rowExists(index.row()))
+        listModel->setActiveRow(index.row());
+    // the user doubleclicked on the "Search More" item
+    else listModel->searchMore();
+}
+
+void MediaView::currentSourceChanged(const Phonon::MediaSource source) {
+    qDebug() << "Playing" << source.url().toString();
+}
+
+void MediaView::skipVideo() {
+    // skippedVideo is useful for DELAYED skip operations
+    // in order to be sure that we're skipping the video we wanted
+    // and not another one
+    if (skippedVideo) {
+        if (listModel->activeVideo() != skippedVideo) {
+            qDebug() << "Skip of video canceled";
+            return;
+        }
+        int nextRow = listModel->rowForVideo(skippedVideo);
+        nextRow++;
+        if (nextRow == -1) return;
+        listModel->setActiveRow(nextRow);
+    }
+}
+
+void MediaView::skip() {
+    int nextRow = listModel->nextRow();
+    if (nextRow == -1) return;
+    listModel->setActiveRow(nextRow);
+}
+
+void MediaView::openWebPage() {
+    Video* video = listModel->activeVideo();
+    if (!video) return;
+    mediaObject->pause();
+    QDesktopServices::openUrl(video->webpage());
+}
+
+void MediaView::copyWebPage() {
+    Video* video = listModel->activeVideo();
+    if (!video) return;
+    QString address = video->webpage().toString();
+    address.remove("&feature=youtube_gdata");
+    QApplication::clipboard()->setText(address);
+    QMainWindow* mainWindow = dynamic_cast<QMainWindow*>(window());
+    QString message = tr("You can now paste the YouTube link into another application");
+    if (mainWindow) mainWindow->statusBar()->showMessage(message);
+}
+
+void MediaView::copyVideoLink() {
+    Video* video = listModel->activeVideo();
+    if (!video) return;
+    QApplication::clipboard()->setText(video->getStreamUrl().toString());
+    QString message = tr("You can now paste the video stream URL into another application")
+                      + ". " + tr("The link will be valid only for a limited time.");
+    QMainWindow* mainWindow = dynamic_cast<QMainWindow*>(window());
+    if (mainWindow) mainWindow->statusBar()->showMessage(message);
+}
+
+void MediaView::removeSelected() {
+    if (!listView->selectionModel()->hasSelection()) return;
+    QModelIndexList indexes = listView->selectionModel()->selectedIndexes();
+    listModel->removeIndexes(indexes);
+}
+
+void MediaView::selectVideos(QList<Video*> videos) {
+    foreach (Video *video, videos) {
+        QModelIndex index = listModel->indexForVideo(video);
+        listView->selectionModel()->select(index, QItemSelectionModel::Select);
+        listView->scrollTo(index, QAbstractItemView::EnsureVisible);
+    }
+}
+
+void MediaView::selectionChanged(const QItemSelection & /*selected*/, const QItemSelection & /*deselected*/) {
+    const bool gotSelection = listView->selectionModel()->hasSelection();
+    The::globalActions()->value("remove")->setEnabled(gotSelection);
+    The::globalActions()->value("moveUp")->setEnabled(gotSelection);
+    The::globalActions()->value("moveDown")->setEnabled(gotSelection);
+}
+
+void MediaView::moveUpSelected() {
+    if (!listView->selectionModel()->hasSelection()) return;
+    QModelIndexList indexes = listView->selectionModel()->selectedIndexes();
+    listModel->move(indexes, true);
+}
+
+void MediaView::moveDownSelected() {
+    if (!listView->selectionModel()->hasSelection()) return;
+    QModelIndexList indexes = listView->selectionModel()->selectedIndexes();
+    listModel->move(indexes, false);
+}
+
+void MediaView::showVideoContextMenu(QPoint point) {
+    The::globalMenus()->value("video")->popup(videoWidget->mapToGlobal(point));
+}
+
+void MediaView::searchMostRelevant() {
+    searchParams->setSortBy(SearchParams::SortByRelevance);
+    search(searchParams);
+}
+
+void MediaView::searchMostRecent() {
+    searchParams->setSortBy(SearchParams::SortByNewest);
+    search(searchParams);
+}
+
+void MediaView::searchMostViewed() {
+    searchParams->setSortBy(SearchParams::SortByViewCount);
+    search(searchParams);
+}
+
+void MediaView::setPlaylistVisible(bool visible) {
+    playlistWidget->setVisible(visible);
+}
+
+void MediaView::timerPlay() {
+    // Workaround Phonon bug on Mac OSX
+    // qDebug() << mediaObject->currentTime();
+    if (mediaObject->currentTime() <= 0 && mediaObject->state() == Phonon::PlayingState) {
+        // qDebug() << "Mac playback workaround";
+        mediaObject->pause();
+        // QTimer::singleShot(1000, mediaObject, SLOT(play()));
+        mediaObject->play();
+    }
+}
+
+void MediaView::saveSplitterState() {
+    QSettings settings;
+    settings.setValue("splitter", splitter->saveState());
+}
diff --git a/src/MediaView.h b/src/MediaView.h
new file mode 100644 (file)
index 0000000..26775b6
--- /dev/null
@@ -0,0 +1,105 @@
+#ifndef __MEDIAVIEW_H__
+#define __MEDIAVIEW_H__
+
+#include <QtGui>
+#include <QtNetwork>
+#include <phonon/mediaobject.h>
+#include <phonon/videowidget.h>
+#include "View.h"
+#include "ListModel.h"
+#include "thblackbar.h"
+#include "searchparams.h"
+#include "playlistwidget.h"
+#include "loadingwidget.h"
+#include "videoareawidget.h"
+
+class MediaView : public QWidget, public View {
+    Q_OBJECT
+
+public:
+    MediaView(QWidget *parent);
+    ~MediaView();
+    void initialize();
+
+    // View
+    void appear() {}
+    void disappear();
+    QMap<QString, QVariant> metadata() {
+        QMap<QString, QVariant> metadata;
+        if (searchParams) {
+            metadata.insert("title", searchParams->keywords());
+            metadata.insert("description", tr("You're watching \"%1\"").arg(searchParams->keywords()));
+        }
+        return metadata;
+    }
+
+    void setMediaObject(Phonon::MediaObject *mediaObject);
+
+public slots:
+    void search(SearchParams *searchParams);
+    void pause();
+    void stop();
+    void skip();
+    void skipVideo();
+    void openWebPage();
+    void copyWebPage();
+    void copyVideoLink();
+    void removeSelected();
+    void moveUpSelected();
+    void moveDownSelected();
+    void setPlaylistVisible(bool visible=true);
+    void saveSplitterState();
+
+private slots:
+    // list/model
+    void itemActivated(const QModelIndex &index);
+    void selectionChanged (const QItemSelection & selected, const QItemSelection & deselected);
+    void activeRowChanged(int);
+    void selectVideos(QList<Video*> videos);
+    void gotStreamUrl(QUrl streamUrl);
+    void handleError(QString message);
+    // phonon
+    void stateChanged(Phonon::State newState, Phonon::State oldState);
+    void currentSourceChanged(const Phonon::MediaSource source);
+    void showVideoContextMenu(QPoint point);
+    // bar
+    void searchMostRelevant();
+    void searchMostRecent();
+    void searchMostViewed();
+    // timer
+    void timerPlay();
+
+private:
+
+    SearchParams *searchParams;
+
+    QSplitter *splitter;
+
+    PlaylistWidget *playlistWidget;
+    QListView *listView;
+    ListModel *listModel;
+
+    // sortBar
+    THBlackBar *sortBar;
+    QAction *mostRelevantAction;
+    QAction *mostRecentAction;
+    QAction *mostViewedAction;
+
+    // phonon
+    Phonon::MediaObject *mediaObject;
+    Phonon::VideoWidget *videoWidget;
+
+    // loadingWidget
+    VideoAreaWidget *videoAreaWidget;
+    LoadingWidget *loadingWidget;
+
+    bool timerPlayFlag;
+    bool reallyStopped;
+
+    QTimer *errorTimer;
+    QTimer *workaroundTimer;
+    Video *skippedVideo;
+
+};
+
+#endif // __MEDIAVIEW_H__
diff --git a/src/SearchView.cpp b/src/SearchView.cpp
new file mode 100644 (file)
index 0000000..191b1bd
--- /dev/null
@@ -0,0 +1,199 @@
+#include "SearchView.h"
+#include "Constants.h"
+
+namespace The {
+    QMap<QString, QAction*>* globalActions();
+}
+
+static const QString recentKeywordsKey = "recentKeywords";
+static const int PADDING = 30;
+
+SearchView::SearchView(QWidget *parent) : QWidget(parent) {
+
+    QFont biggerFont;
+    biggerFont.setPointSize(biggerFont.pointSize()*1.5);
+
+    QFont smallerFont;
+    smallerFont.setPointSize(smallerFont.pointSize()*.85);
+    smallerFont.setBold(true);
+
+    QBoxLayout *mainLayout = new QVBoxLayout();
+    mainLayout->setMargin(0);
+    mainLayout->setSpacing(0);
+
+    // hidden message widget
+    message = new QLabel(this);
+    message->hide();
+    mainLayout->addWidget(message);
+
+    mainLayout->addStretch();
+
+    QBoxLayout *hLayout = new QHBoxLayout();
+    hLayout->setAlignment(Qt::AlignCenter);
+    mainLayout->addLayout(hLayout);
+
+    QLabel *logo = new QLabel(this);
+    logo->setPixmap(QPixmap(":/images/app.png"));
+    hLayout->addWidget(logo, 0, Qt::AlignTop);
+    hLayout->addSpacing(PADDING);
+
+    QVBoxLayout *layout = new QVBoxLayout();
+    layout->setAlignment(Qt::AlignCenter);
+    hLayout->addLayout(layout);
+
+    QLabel *welcomeLabel =
+            new QLabel("<h1>" +
+                       tr("Welcome to <a href='%1'>%2</a>,")
+                       .replace("<a ", "<a style='color:palette(text)'")
+                       .arg(Constants::WEBSITE, Constants::APP_NAME)
+                       + "</h1>", this);
+    welcomeLabel->setOpenExternalLinks(true);
+    layout->addWidget(welcomeLabel);
+
+    layout->addSpacing(PADDING);
+
+    QLabel *tipLabel = new QLabel(tr("Enter a keyword to start watching videos."), this);
+    tipLabel->setFont(biggerFont);
+    layout->addWidget(tipLabel);
+
+    layout->addSpacing(10);
+
+    QHBoxLayout *searchLayout = new QHBoxLayout();
+    searchLayout->setAlignment(Qt::AlignVCenter);
+
+    queryEdit = new SearchLineEdit(this);
+    queryEdit->setFont(biggerFont);
+    queryEdit->setMinimumWidth(queryEdit->fontInfo().pixelSize()*15);
+    queryEdit->sizeHint();
+    queryEdit->setFocus(Qt::OtherFocusReason);
+    connect(queryEdit, SIGNAL(search(const QString&)), this, SLOT(watch(const QString&)));
+    connect(queryEdit, SIGNAL(textChanged(const QString &)), this, SLOT(textChanged(const QString &)));
+    searchLayout->addWidget(queryEdit);
+
+    searchLayout->addSpacing(10);
+
+    watchButton = new QPushButton(tr("Watch"), this);
+    watchButton->setDefault(true);
+    watchButton->setEnabled(false);
+    watchButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+    connect(watchButton, SIGNAL(clicked()), this, SLOT(watch()));
+    searchLayout->addWidget(watchButton);
+
+    layout->addItem(searchLayout);
+
+    layout->addSpacing(PADDING);
+
+    QHBoxLayout *otherLayout = new QHBoxLayout();
+
+    recentKeywordsLayout = new QVBoxLayout();
+    recentKeywordsLayout->setSpacing(5);
+    recentKeywordsLayout->setAlignment(Qt::AlignVCenter | Qt::AlignLeft);
+    recentKeywordsLabel = new QLabel(tr("Recent keywords").toUpper(), this);
+    recentKeywordsLabel->hide();
+    recentKeywordsLabel->setForegroundRole(QPalette::Dark);
+    recentKeywordsLabel->setFont(smallerFont);
+    recentKeywordsLayout->addWidget(recentKeywordsLabel);
+
+    otherLayout->addLayout(recentKeywordsLayout);
+
+    layout->addLayout(otherLayout);
+
+    mainLayout->addStretch();
+
+    setLayout(mainLayout);
+
+    updateChecker = 0;
+    checkForUpdate();
+}
+
+void SearchView::updateRecentKeywords() {
+
+    // cleanup
+    QLayoutItem *item;
+    while ((item = recentKeywordsLayout->takeAt(1)) != 0) {
+        item->widget()->close();
+        delete item;
+    }
+
+    // load
+    QSettings settings;
+    QStringList keywords = settings.value(recentKeywordsKey).toStringList();
+    recentKeywordsLabel->setVisible(!keywords.isEmpty());
+    The::globalActions()->value("clearRecentKeywords")->setEnabled(!keywords.isEmpty());
+
+    foreach (QString keyword, keywords) {
+        QLabel *itemLabel = new QLabel("<a href=\"" + keyword
+                                       + "\" style=\"color:palette(text); text-decoration:none\">"
+                                       + keyword + "</a>", this);
+
+        itemLabel->setMaximumWidth(queryEdit->width() + watchButton->width());
+        // itemLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+        // Make links navigable with the keyboard too
+        itemLabel->setTextInteractionFlags(Qt::LinksAccessibleByKeyboard | Qt::LinksAccessibleByMouse);
+
+        connect(itemLabel, SIGNAL(linkActivated(QString)), this, SLOT(watch(QString)));
+        recentKeywordsLayout->addWidget(itemLabel);
+    }
+
+}
+
+
+
+void SearchView::watch() {
+    QString query = queryEdit->text().simplified();
+    watch(query);
+}
+
+void SearchView::textChanged(const QString &text) {
+    watchButton->setEnabled(!text.simplified().isEmpty());
+}
+
+void SearchView::watch(QString query) {
+
+    // check for empty query
+    if (query.length() == 0) {
+        queryEdit->setFocus(Qt::OtherFocusReason);
+        return;
+    }
+
+    // go!
+    emit search(query);
+}
+
+void SearchView::checkForUpdate() {
+    static const QString updateCheckKey = "updateCheck";
+
+    // check every 24h
+    QSettings settings;
+    uint unixTime = QDateTime::currentDateTime().toTime_t();
+    int lastCheck = settings.value(updateCheckKey).toInt();
+    int secondsSinceLastCheck = unixTime - lastCheck;
+    // qDebug() << "secondsSinceLastCheck" << unixTime << lastCheck << secondsSinceLastCheck;
+    if (secondsSinceLastCheck < 86400) return;
+
+    // check it out
+    if (updateChecker) delete updateChecker;
+    updateChecker = new UpdateChecker();
+    connect(updateChecker, SIGNAL(newVersion(QString)),
+            this, SLOT(gotNewVersion(QString)));
+    updateChecker->checkForUpdate();
+    settings.setValue(updateCheckKey, unixTime);
+
+}
+
+void SearchView::gotNewVersion(QString version) {
+    message->setText(
+            tr("A new version of %1 is available. Please <a href='%2'>update to version %3</a>")
+            .arg(
+                    Constants::APP_NAME,
+                    QString(Constants::WEBSITE).append("#download"),
+                    version)
+            );
+    message->setOpenExternalLinks(true);
+    message->setMargin(10);
+    message->setBackgroundRole(QPalette::ToolTipBase);
+    message->setForegroundRole(QPalette::ToolTipText);
+    message->setAutoFillBackground(true);
+    message->show();
+    if (updateChecker) delete updateChecker;
+}
diff --git a/src/SearchView.h b/src/SearchView.h
new file mode 100644 (file)
index 0000000..6ad912d
--- /dev/null
@@ -0,0 +1,57 @@
+#ifndef __SEARCHVIEW_H__
+#define __SEARCHVIEW_H__
+
+#include <QtGui>
+#include "View.h"
+#include "searchlineedit.h"
+#include "updatechecker.h"
+
+class SearchView : public QWidget, public View {
+
+    Q_OBJECT
+
+public:
+    SearchView(QWidget *parent);
+    void updateRecentKeywords();
+
+    void appear() {
+        updateRecentKeywords();
+        queryEdit->clear();
+        queryEdit->setFocus(Qt::OtherFocusReason);
+        queryEdit->enableSuggest();
+    }
+
+    void disappear() {}
+
+    QMap<QString, QVariant> metadata() {
+        QMap<QString, QVariant> metadata;
+        metadata.insert("title", "");
+        metadata.insert("description", tr("Make yourself comfortable"));
+        return metadata;
+    }
+
+public slots:
+    void watch(QString query);
+    void gotNewVersion(QString version);
+
+signals:
+    void search(QString query);
+
+private slots:
+    void watch();
+    void textChanged(const QString &text);
+
+private:
+    void checkForUpdate();
+
+    SearchLineEdit *queryEdit;
+    QLabel *recentKeywordsLabel;
+    QVBoxLayout *recentKeywordsLayout;
+    QLabel *message;
+    QPushButton *watchButton;
+
+    UpdateChecker *updateChecker;
+
+};
+
+#endif // __SEARCHVIEW_H__
diff --git a/src/View.h b/src/View.h
new file mode 100644 (file)
index 0000000..ea701bd
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef VIEW_H
+#define VIEW_H
+
+class View {
+
+    public:
+        virtual QMap<QString, QVariant> metadata() = 0;
+        virtual void appear() = 0;
+        virtual void disappear() = 0;
+
+};
+
+#endif // VIEW_H
diff --git a/src/faderwidget/FaderWidget.cpp b/src/faderwidget/FaderWidget.cpp
new file mode 100644 (file)
index 0000000..f2634ba
--- /dev/null
@@ -0,0 +1,29 @@
+#include "FaderWidget.h"
+
+// http://labs.trolltech.com/blogs/2007/08/21/fade-effects-a-blast-from-the-past/
+
+FaderWidget::FaderWidget(QWidget *parent) : QWidget(parent) {
+    timeLine = new QTimeLine(250, this);
+    timeLine->setFrameRange(1000, 0);
+    connect(timeLine, SIGNAL(frameChanged(int)), this, SLOT(update()));
+    setAttribute(Qt::WA_DeleteOnClose);
+    resize(parent->size());
+}
+
+void FaderWidget::start(QPixmap frozenView) {
+    this->frozenView = frozenView;
+    timeLine->start();
+    show();
+}
+
+void FaderWidget::paintEvent(QPaintEvent *) {
+    const qreal opacity = timeLine->currentFrame() / 1000.;
+    QPainter painter(this);
+    painter.setOpacity(opacity);
+    painter.drawPixmap(0, 0, frozenView);
+    // qDebug() << opacity;
+
+    if (opacity <= 0.)
+        close();
+
+}
diff --git a/src/faderwidget/FaderWidget.h b/src/faderwidget/FaderWidget.h
new file mode 100644 (file)
index 0000000..6dacaac
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef FADERWIDGET_H
+#define FADERWIDGET_H
+
+#include <QtGui>
+
+class FaderWidget : public QWidget {
+
+    Q_OBJECT
+    Q_PROPERTY(int fadeDuration READ fadeDuration WRITE setFadeDuration)
+
+public:
+
+    FaderWidget(QWidget *parent);
+
+    int fadeDuration() const {
+        return timeLine->duration();
+    }
+    void setFadeDuration(int milliseconds) {
+        timeLine->setDuration(milliseconds);
+    }
+    void start(QPixmap frozenView);
+
+protected:
+    void paintEvent(QPaintEvent *event);
+
+private:
+    QTimeLine *timeLine;
+    QPixmap frozenView;
+
+};
+
+#endif
diff --git a/src/flickcharm.cpp b/src/flickcharm.cpp
new file mode 100644 (file)
index 0000000..1f5175b
--- /dev/null
@@ -0,0 +1,327 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Graphics Dojo project on Qt Labs.
+**
+** This file may be used under the terms of the GNU General Public
+** License version 2.0 or 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of
+** this file.  Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+****************************************************************************/
+
+#include "flickcharm.h"
+
+#include <QAbstractScrollArea>
+#include <QApplication>
+#include <QBasicTimer>
+#include <QEvent>
+#include <QHash>
+#include <QList>
+#include <QMouseEvent>
+#include <QScrollBar>
+
+#include <QDebug>
+
+struct FlickData {
+    typedef enum { Steady, Pressed, ManualScroll, AutoScroll, Stop } State;
+    State state;
+    QWidget *widget;
+    QPoint pressPos;
+    QPoint offset;
+    QPoint dragPos;
+    QPoint speed;
+    QList<QEvent*> ignored;
+};
+
+class FlickCharmPrivate
+{
+public:
+    QHash<QWidget*, FlickData*> flickData;
+    QBasicTimer ticker;
+};
+
+FlickCharm::FlickCharm(QObject *parent): QObject(parent)
+{
+    d = new FlickCharmPrivate;
+}
+
+FlickCharm::~FlickCharm()
+{
+    delete d;
+}
+
+void FlickCharm::activateOn(QWidget *widget)
+{
+    QAbstractScrollArea *scrollArea = dynamic_cast<QAbstractScrollArea*>(widget);
+    if (scrollArea) {
+        scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+        scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+
+        QWidget *viewport = scrollArea->viewport();
+
+        viewport->installEventFilter(this);
+        scrollArea->installEventFilter(this);
+
+        d->flickData.remove(viewport);
+        d->flickData[viewport] = new FlickData;
+        d->flickData[viewport]->widget = widget;
+        d->flickData[viewport]->state = FlickData::Steady;
+
+        return;
+    }
+
+    qWarning() << "FlickCharm only works on QAbstractScrollArea (and derived classes)";
+}
+
+void FlickCharm::deactivateFrom(QWidget *widget)
+{
+    QAbstractScrollArea *scrollArea = dynamic_cast<QAbstractScrollArea*>(widget);
+    if (scrollArea) {
+        QWidget *viewport = scrollArea->viewport();
+
+        viewport->removeEventFilter(this);
+        scrollArea->removeEventFilter(this);
+
+        delete d->flickData[viewport];
+        d->flickData.remove(viewport);
+
+        return;
+    }
+}
+
+static QPoint scrollOffset(QWidget *widget)
+{
+    int x = 0, y = 0;
+
+    QAbstractScrollArea *scrollArea = dynamic_cast<QAbstractScrollArea*>(widget);
+    if (scrollArea) {
+        x = scrollArea->horizontalScrollBar()->value();
+        y = scrollArea->verticalScrollBar()->value();
+    }
+
+    return QPoint(x, y);
+}
+
+static void setScrollOffset(QWidget *widget, const QPoint &p)
+{
+    QAbstractScrollArea *scrollArea = dynamic_cast<QAbstractScrollArea*>(widget);
+    if (scrollArea) {
+        scrollArea->horizontalScrollBar()->setValue(p.x());
+        scrollArea->verticalScrollBar()->setValue(p.y());
+    }
+}
+
+static QPoint deaccelerate(const QPoint &speed, int a = 1, int max = 64)
+{
+    int x = qBound(-max, speed.x(), max);
+    int y = qBound(-max, speed.y(), max);
+    x = (x == 0) ? x : (x > 0) ? qMax(0, x - a) : qMin(0, x + a);
+    y = (y == 0) ? y : (y > 0) ? qMax(0, y - a) : qMin(0, y + a);
+    return QPoint(x, y);
+}
+
+bool FlickCharm::eventFilter(QObject *object, QEvent *event)
+{
+
+    if (!object->isWidgetType())
+        return false;
+
+    QEvent::Type type = event->type();
+    if (type != QEvent::MouseButtonPress &&
+        type != QEvent::MouseButtonRelease &&
+        type != QEvent::MouseMove)
+        return false;
+
+    QMouseEvent *mouseEvent = dynamic_cast<QMouseEvent*>(event);
+    if (!mouseEvent || mouseEvent->modifiers() != Qt::NoModifier)
+        return false;
+
+    QWidget *viewport = dynamic_cast<QWidget*>(object);
+    FlickData *data = d->flickData.value(viewport);
+    if (!viewport || !data || data->ignored.removeAll(event))
+        return false;
+
+    QWidget *scrollArea = dynamic_cast<QWidget*>(object);
+
+    bool consumed = false;
+    switch (data->state) {
+
+    case FlickData::Steady:
+        if (mouseEvent->type() == QEvent::MouseButtonPress)
+            if (mouseEvent->buttons() == Qt::LeftButton) {
+            consumed = true;
+            data->state = FlickData::Pressed;
+            data->pressPos = mouseEvent->pos();
+            data->offset = scrollOffset(data->widget);
+        }
+        break;
+
+    case FlickData::Pressed:
+        if (mouseEvent->type() == QEvent::MouseButtonRelease) {
+            consumed = true;
+            data->state = FlickData::Steady;
+
+            QMouseEvent *event1 = new QMouseEvent(QEvent::MouseButtonPress,
+                                                  data->pressPos, Qt::LeftButton,
+                                                  Qt::LeftButton, Qt::NoModifier);
+            QMouseEvent *event2 = new QMouseEvent(*mouseEvent);
+
+            data->ignored << event1;
+            data->ignored << event2;
+            QApplication::postEvent(object, event1);
+            QApplication::postEvent(object, event2);
+        }
+        if (mouseEvent->type() == QEvent::MouseMove) {
+
+            consumed = true;
+            data->state = FlickData::ManualScroll;
+            data->dragPos = QCursor::pos();
+            if (!d->ticker.isActive())
+                d->ticker.start(20, this);
+
+        }
+        break;
+
+    case FlickData::ManualScroll:
+        if (mouseEvent->type() == QEvent::MouseMove) {
+            QPoint pos = scrollArea->mapFromGlobal(QCursor::pos());
+            if (pos.x() > scrollArea->width() || pos.x() < 0) {
+                pos.setX(1);
+                QMouseEvent *event1 = new QMouseEvent(QEvent::MouseButtonPress,
+                                                      pos, Qt::LeftButton,
+                                                      Qt::LeftButton, Qt::NoModifier);
+                QMouseEvent *event2 = new QMouseEvent(QEvent::MouseMove,
+                                                      pos, Qt::LeftButton,
+                                                      Qt::LeftButton, Qt::NoModifier);
+
+                data->ignored << event1;
+                data->ignored << event2;
+                QApplication::postEvent(object, event1);
+                QApplication::postEvent(object, event2);
+                data->state = FlickData::Steady;
+                consumed = true;
+            } else {
+                consumed = true;
+                QPoint delta = mouseEvent->pos() - data->pressPos;
+                setScrollOffset(data->widget, data->offset - delta);
+            }
+        }
+        if (mouseEvent->type() == QEvent::MouseButtonRelease) {
+            consumed = true;
+            data->state = FlickData::AutoScroll;
+        }
+        break;
+
+    case FlickData::AutoScroll:
+        if (mouseEvent->type() == QEvent::MouseButtonPress) {
+            consumed = true;
+            data->state = FlickData::Stop;
+            data->speed = QPoint(0, 0);
+            data->pressPos = mouseEvent->pos();
+            data->offset = scrollOffset(data->widget);
+        }
+        if (mouseEvent->type() == QEvent::MouseButtonRelease) {
+            consumed = true;
+            data->state = FlickData::Steady;
+            data->speed = QPoint(0, 0);
+        }
+        break;
+
+    case FlickData::Stop:
+        if (mouseEvent->type() == QEvent::MouseButtonRelease) {
+            consumed = true;
+            data->state = FlickData::Steady;
+        }
+        if (mouseEvent->type() == QEvent::MouseMove) {
+            consumed = true;
+            data->state = FlickData::ManualScroll;
+            data->dragPos = QCursor::pos();
+            if (!d->ticker.isActive())
+                d->ticker.start(20, this);
+        }
+        break;
+
+    default:
+        break;
+    }
+
+    return consumed;
+}
+
+void FlickCharm::timerEvent(QTimerEvent *event)
+{
+    int count = 0;
+    QHashIterator<QWidget*, FlickData*> item(d->flickData);
+    while (item.hasNext()) {
+        item.next();
+        FlickData *data = item.value();
+
+        const bool scrolling = data->state == FlickData::ManualScroll || data->state == FlickData::AutoScroll;
+        scrollBarShow(data->widget, scrolling);
+        // data->widget->setUpdatesEnabled(!scrolling);
+
+        if (data->state == FlickData::ManualScroll) {
+            count++;
+            data->speed = QCursor::pos() - data->dragPos;
+            data->dragPos = QCursor::pos();
+        }
+
+        if (data->state == FlickData::AutoScroll) {
+            count++;
+            data->speed = deaccelerate(data->speed);
+            QPoint p = scrollOffset(data->widget);
+            setScrollOffset(data->widget, p - data->speed);
+            if (data->speed == QPoint(0, 0))
+                data->state = FlickData::Steady;
+        }
+    }
+
+    if (!count)
+        d->ticker.stop();
+
+    QObject::timerEvent(event);
+}
+
+void FlickCharm::showScrollBars(QWidget *widget) {
+    QAbstractScrollArea *scrollArea = dynamic_cast<QAbstractScrollArea*>(widget);
+    if (scrollArea) {
+        // scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+        scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+    }
+}
+
+void FlickCharm::hideScrollBars(QWidget *widget) {
+    QAbstractScrollArea *scrollArea = dynamic_cast<QAbstractScrollArea*>(widget);
+    if (scrollArea) {
+        scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+        scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+    }
+}
+
+void FlickCharm::scrollBarShow(QWidget *widget, bool show) {
+    static bool shown = false;
+
+    if (show) {
+        if (!shown) {
+            showScrollBars(widget);
+            shown = true;
+        }
+    } else {
+        if (shown) {
+            hideScrollBars(widget);
+            shown = false;
+        }
+    }
+}
diff --git a/src/flickcharm.h b/src/flickcharm.h
new file mode 100644 (file)
index 0000000..2a1bfea
--- /dev/null
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Graphics Dojo project on Qt Labs.
+**
+** This file may be used under the terms of the GNU General Public
+** License version 2.0 or 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of
+** this file.  Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+****************************************************************************/
+
+#ifndef FLICKCHARM_H
+#define FLICKCHARM_H
+
+#include <QObject>
+
+class FlickCharmPrivate;
+class QWidget;
+
+class FlickCharm: public QObject
+{
+    Q_OBJECT
+public:
+    FlickCharm(QObject *parent = 0);
+    ~FlickCharm();
+    void activateOn(QWidget *widget);
+    void deactivateFrom(QWidget *widget);
+    bool eventFilter(QObject *object, QEvent *event);
+
+protected:
+    void timerEvent(QTimerEvent *event);
+
+private:
+    void scrollBarShow(QWidget *widget, bool show);
+    void hideScrollBars(QWidget *widget);
+    void showScrollBars(QWidget *widget);
+    FlickCharmPrivate *d;
+};
+
+#endif // FLICKCHARM_H
diff --git a/src/global.h b/src/global.h
new file mode 100644 (file)
index 0000000..502e329
--- /dev/null
@@ -0,0 +1,157 @@
+#ifndef GLOBAL_H
+#define GLOBAL_H
+
+#include <QtGui>
+#include <QStringList>
+#include <QNetworkProxy>
+#include <QNetworkAccessManager>
+#include <QNetworkProxyFactory>
+#include <cstdlib>
+#include "networkaccess.h"
+
+namespace The {
+
+    static QMap<QString, QAction*> *g_actions = 0;
+
+    QMap<QString, QAction*>* globalActions() {
+        if (!g_actions)
+            g_actions = new QMap<QString, QAction*>;
+        return g_actions;
+    }
+
+    static QMap<QString, QMenu*> *g_menus = 0;
+
+    QMap<QString, QMenu*>* globalMenus() {
+        if (!g_menus)
+            g_menus = new QMap<QString, QMenu*>;
+        return g_menus;
+    }
+
+    void maybeSetSystemProxy() {
+
+        QNetworkProxyQuery proxyQuery(QUrl("http://www"));
+        proxyQuery.setProtocolTag("http");
+        QList<QNetworkProxy> proxylist = QNetworkProxyFactory::systemProxyForQuery(proxyQuery);
+
+        for (int i = 0; i < proxylist.count(); i++) {
+            QNetworkProxy proxy = proxylist.at(i);
+
+            /*
+            qDebug() << i << " type:"<< proxy.type();
+            qDebug() << i << " host:" << proxy.hostName();
+            qDebug() << i << " port:" << proxy.port();
+            qDebug() << i << " user:" << proxy.user();
+            qDebug() << i << " pass:" << proxy.password();
+            */
+
+            if (!proxy.hostName().isEmpty()) {
+                qDebug() << "Using proxy:" << proxy.hostName() << proxy.port();
+                QNetworkProxy::setApplicationProxy(proxy);
+                return;
+            }
+        }
+    }
+
+    void networkHttpProxySetting() {
+        char *http_proxy_env;
+        http_proxy_env = std::getenv("http_proxy");
+        if (!http_proxy_env) {
+            http_proxy_env = std::getenv("HTTP_PROXY");
+        }
+
+        if (http_proxy_env) {
+            QString proxy_host = "";
+            QString proxy_port = "";
+            QString proxy_user = "";
+            QString proxy_pass = "";
+            QString http_proxy = QString(http_proxy_env);
+            http_proxy.remove(QRegExp("^http://"));
+
+            // Remove trailing slash, if any
+            // Fix by Eduardo Suarez-Santana
+            http_proxy.remove(QRegExp("/$"));
+
+            // parse username and password
+            if (http_proxy.contains(QChar('@'))) {
+                QStringList http_proxy_list = http_proxy.split(QChar('@'));
+                QStringList http_proxy_user_pass = http_proxy_list[0].split(QChar(':'));
+                if (http_proxy_user_pass.size() > 0) {
+                    proxy_user = http_proxy_user_pass[0];
+                }
+                if (http_proxy_user_pass.size() == 2) {
+                    proxy_pass = http_proxy_user_pass[1];
+                }
+                if (http_proxy_list.size() > 1) {
+                    http_proxy = http_proxy_list[1];
+                }
+            }
+
+            // parse hostname and port
+            QStringList http_proxy_list = http_proxy.split(QChar(':'));
+            if (http_proxy_list.size() > 0) {
+                proxy_host = http_proxy_list[0];
+            }
+            if (http_proxy_list.size() > 1) {
+                proxy_port = http_proxy_list[1];
+            }
+
+            /*
+            qDebug() << "proxy_host: " << proxy_host;
+            qDebug() << "proxy_port: " << proxy_port;
+            qDebug() << "proxy_user: " << proxy_user;
+            qDebug() << "proxy_pass: " << proxy_pass;
+            */
+
+            // set proxy setting
+            if (!proxy_host.isEmpty()) {
+                QNetworkProxy proxy;
+                proxy.setType(QNetworkProxy::HttpProxy);
+                proxy.setHostName(proxy_host);
+                if (!proxy_port.isEmpty()) {
+                    proxy.setPort(proxy_port.toUShort());
+                }
+                if (!proxy_user.isEmpty()) {
+                    proxy.setUser(proxy_user);
+                }
+                if (!proxy_pass.isEmpty()) {
+                    proxy.setPassword(proxy_pass);
+                }
+
+                qDebug() << "Using HTTP proxy:" << http_proxy_env;
+                QNetworkProxy::setApplicationProxy(proxy);
+            }
+        }
+    }
+
+    static QNetworkAccessManager *nam = 0;
+
+    QNetworkAccessManager* networkAccessManager() {
+        if (!nam) {
+            networkHttpProxySetting();
+            maybeSetSystemProxy();
+            nam = new QNetworkAccessManager();
+
+            // A simple disk based cache
+            /*
+            QNetworkDiskCache *cache = new QNetworkDiskCache();
+            QString cacheLocation = QDesktopServices::storageLocation(QDesktopServices::DataLocation);
+            qDebug() << cacheLocation;
+            cache->setCacheDirectory(cacheLocation);
+            nam->setCache(cache);
+            */
+        }
+        return nam;
+    }
+
+    static NetworkAccess *g_http = 0;
+    NetworkAccess* http() {
+        if (!g_http) {
+            // qDebug() << "Creating NetworkAccess";
+            g_http = new NetworkAccess();
+        }
+        return g_http;
+    }
+
+}
+
+#endif // GLOBAL_H
diff --git a/src/googlesuggest.cpp b/src/googlesuggest.cpp
new file mode 100644 (file)
index 0000000..0002ed0
--- /dev/null
@@ -0,0 +1,204 @@
+#include "googlesuggest.h"
+#include "networkaccess.h"
+
+#define GSUGGEST_URL "http://suggestqueries.google.com/complete/search?ds=yt&output=toolbar&hl=%1&q=%2"
+
+namespace The {
+    NetworkAccess* http();
+}
+
+GSuggestCompletion::GSuggestCompletion(QWidget *parent, QLineEdit *editor):
+        QObject(parent), buddy(parent), editor(editor) {
+
+    enabled = true;
+
+    popup = new QListWidget;
+    popup->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+    popup->installEventFilter(this);
+    popup->setMouseTracking(true);
+
+    connect(popup, SIGNAL(itemClicked(QListWidgetItem*)),
+            SLOT(doneCompletion()));
+
+    // connect(popup, SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)),
+    //    SLOT(currentItemChanged(QListWidgetItem *)));
+
+    // mouse hover
+    // connect(popup, SIGNAL(itemEntered(QListWidgetItem*)),
+    //    SLOT(currentItemChanged(QListWidgetItem *)));
+
+    popup->setWindowFlags(Qt::Popup);
+    popup->setFocusPolicy(Qt::NoFocus);
+    popup->setFocusProxy(parent);
+
+    timer = new QTimer(this);
+    timer->setSingleShot(true);
+    timer->setInterval(300);
+    connect(timer, SIGNAL(timeout()), SLOT(autoSuggest()));
+    connect(editor, SIGNAL(textEdited(QString)), timer, SLOT(start()));
+
+}
+
+GSuggestCompletion::~GSuggestCompletion() {
+    delete popup;
+}
+
+bool GSuggestCompletion::eventFilter(QObject *obj, QEvent *ev) {
+    if (obj != popup)
+        return false;
+
+    if (ev->type() == QEvent::MouseButtonPress) {
+        popup->hide();
+        editor->setFocus();
+        editor->setText(originalText);
+        return true;
+    }
+
+    if (ev->type() == QEvent::KeyPress) {
+
+        bool consumed = false;
+
+        QKeyEvent *keyEvent = static_cast<QKeyEvent*>(ev);
+        int key = keyEvent->key();
+        // qDebug() << keyEvent->text();
+        switch (key) {
+        case Qt::Key_Enter:
+        case Qt::Key_Return:
+            if (popup->currentItem()) {
+                doneCompletion();
+                consumed = true;
+            } else {
+                editor->setFocus();
+                editor->event(ev);
+                popup->hide();
+            }
+            break;
+
+        case Qt::Key_Escape:
+            editor->setFocus();
+            editor->setText(originalText);
+            popup->hide();
+            consumed = true;
+            break;
+
+        case Qt::Key_Up:
+        case Qt::Key_Down:
+        case Qt::Key_Home:
+        case Qt::Key_End:
+        case Qt::Key_PageUp:
+        case Qt::Key_PageDown:
+            break;
+
+        default:
+            // qDebug() << keyEvent->text();
+            editor->setFocus();
+            editor->event(ev);
+            popup->hide();
+            break;
+        }
+
+        return consumed;
+    }
+
+    return false;
+}
+
+void GSuggestCompletion::showCompletion(const QStringList &choices) {
+
+    if (choices.isEmpty())
+        return;
+
+    popup->setUpdatesEnabled(false);
+    popup->clear();
+    for (int i = 0; i < choices.count(); ++i) {
+        QListWidgetItem * item;
+        item = new QListWidgetItem(popup);
+        item->setText(choices[i]);
+    }
+    popup->setCurrentItem(0);
+    popup->adjustSize();
+    popup->setUpdatesEnabled(true);
+
+    int h = popup->sizeHintForRow(0) * choices.count() + 4;
+    popup->resize(buddy->width(), h);
+
+    popup->move(buddy->mapToGlobal(QPoint(0, buddy->height())));
+
+    popup->setFocus();
+    popup->show();
+}
+
+void GSuggestCompletion::doneCompletion() {
+    timer->stop();
+    popup->hide();
+    editor->setFocus();
+    QListWidgetItem *item = popup->currentItem();
+    if (item) {
+        editor->setText(item->text());
+        QKeyEvent *e;
+        e = new QKeyEvent(QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier);
+        QApplication::postEvent(editor, e);
+        e = new QKeyEvent(QEvent::KeyRelease, Qt::Key_Enter, Qt::NoModifier);
+        QApplication::postEvent(editor, e);
+    }
+}
+
+void GSuggestCompletion::preventSuggest() {
+    // qDebug() << "preventSuggest";
+    timer->stop();
+    enabled = false;
+    popup->hide();
+}
+
+void GSuggestCompletion::enableSuggest() {
+    // qDebug() << "enableSuggest";
+    enabled = true;
+}
+
+void GSuggestCompletion::autoSuggest() {
+    if (!enabled) return;
+
+    QString query = editor->text();
+    originalText = query;
+    // qDebug() << "originalText" << originalText;
+    if (query.isEmpty()) return;
+
+    QString locale = QLocale::system().name().replace("_", "-");
+    // case for system locales such as "C"
+    if (locale.length() < 2) {
+        locale = "en-US";
+    }
+
+    QString url = QString(GSUGGEST_URL).arg(locale, query);
+
+    QObject *reply = The::http()->get(url);
+    connect(reply, SIGNAL(data(QByteArray)), SLOT(handleNetworkData(QByteArray)));
+}
+
+void GSuggestCompletion::handleNetworkData(QByteArray response) {
+    if (!enabled) return;
+
+    QStringList choices;
+
+    QXmlStreamReader xml(response);
+    while (!xml.atEnd()) {
+        xml.readNext();
+        if (xml.tokenType() == QXmlStreamReader::StartElement)
+            if (xml.name() == "suggestion") {
+            QStringRef str = xml.attributes().value("data");
+            choices << str.toString();
+        }
+    }
+
+    showCompletion(choices);
+
+}
+
+void GSuggestCompletion::currentItemChanged(QListWidgetItem *current) {
+    if (current) {
+        // qDebug() << "current" << current->text();
+        current->setSelected(true);
+        editor->setText(current->text());
+        editor->setSelection(originalText.length(), editor->text().length());
+    }
+}
diff --git a/src/googlesuggest.h b/src/googlesuggest.h
new file mode 100644 (file)
index 0000000..9773380
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef GOOGLESUGGEST_H
+#define GOOGLESUGGEST_H
+
+#include <QtGui>
+
+class GSuggestCompletion : public QObject {
+    Q_OBJECT
+
+public:
+    GSuggestCompletion(QWidget *parent, QLineEdit *editor);
+    ~GSuggestCompletion();
+    bool eventFilter(QObject *obj, QEvent *ev);
+    void showCompletion(const QStringList &choices);
+
+public slots:
+    void doneCompletion();
+    void preventSuggest();
+    void enableSuggest();
+    void autoSuggest();
+    void handleNetworkData(QByteArray response);
+    void currentItemChanged(QListWidgetItem *current);
+
+private:
+    QWidget *buddy;
+    QLineEdit *editor;
+    QString originalText;
+    QListWidget *popup;
+    QTimer *timer;
+    bool enabled;
+
+};
+
+#endif // GOOGLESUGGEST_H
diff --git a/src/iconloader/qticonloader.cpp b/src/iconloader/qticonloader.cpp
new file mode 100644 (file)
index 0000000..551e1ac
--- /dev/null
@@ -0,0 +1,351 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+****************************************************************************/
+
+
+#include "qticonloader.h"
+#include <QtGui/QPixmapCache>
+
+#include <QtCore/QList>
+#include <QtCore/QHash>
+#include <QtCore/QDir>
+#include <QtCore/QString>
+#include <QtCore/QLibrary>
+#include <QtCore/QSettings>
+#include <QtCore/QTextStream>
+
+#ifdef Q_WS_X11
+
+class QIconTheme
+{
+public:
+    QIconTheme(QHash <int, QString> dirList, QStringList parents) :
+            _dirList(dirList), _parents(parents), _valid(true){ }
+    QIconTheme() : _valid(false){ }
+    QHash <int, QString> dirList() {return _dirList;}
+    QStringList parents() {return _parents;}
+    bool isValid() {return _valid;}
+
+private:
+    QHash <int, QString> _dirList;
+    QStringList _parents;
+    bool _valid;
+};
+
+class QtIconLoaderImplementation
+{
+public:
+    QtIconLoaderImplementation();
+    QPixmap findIcon(int size, const QString &name) const;
+
+private:
+    QIconTheme parseIndexFile(const QString &themeName) const;
+    void lookupIconTheme() const;
+    QPixmap findIconHelper(int size,
+                           const QString &themeName,
+                           const QString &iconName,
+                           QStringList &visited) const;
+    mutable QString themeName;
+    mutable QStringList iconDirs;
+    mutable QHash <QString, QIconTheme> themeList;
+};
+
+Q_GLOBAL_STATIC(QtIconLoaderImplementation, iconLoaderInstance)
+#endif
+
+/*!
+
+    Returns the standard icon for the given icon /a name
+    as specified in the freedesktop icon spec
+    http://standards.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html
+
+    /a fallback is an optional argument to specify the icon to be used if
+    no icon is found on the platform. This is particularily useful for
+    crossplatform code.
+
+*/
+QIcon QtIconLoader::icon(const QString &name, const QIcon &fallback)
+{
+    QIcon icon;
+
+#if QT_VERSION < 0x040600
+
+#ifdef Q_WS_X11
+    QString pngExtension(QLatin1String(".png"));
+    QList<int> iconSizes;
+    iconSizes << 16 << 24 << 32 << 48 << 64;
+    Q_FOREACH (int size, iconSizes) {
+        icon.addPixmap(iconLoaderInstance()->findIcon(size, name + pngExtension));
+    }
+#endif
+
+#else
+    icon = QIcon::fromTheme(name, fallback);
+#endif
+
+    if (icon.isNull())
+        icon = fallback;
+    Q_UNUSED(name);
+    return icon;
+}
+
+#ifdef Q_WS_X11
+
+QtIconLoaderImplementation::QtIconLoaderImplementation()
+{
+    lookupIconTheme();
+}
+
+extern "C" {
+    struct GConfClient;
+    struct GError;
+    typedef void (*Ptr_g_type_init)();
+    typedef GConfClient* (*Ptr_gconf_client_get_default)();
+    typedef char* (*Ptr_gconf_client_get_string)(GConfClient*, const char*, GError **);
+    typedef void (*Ptr_g_object_unref)(void *);
+    typedef void (*Ptr_g_error_free)(GError *);
+    typedef void (*Ptr_g_free)(void*);
+    static Ptr_g_type_init p_g_type_init = 0;
+    static Ptr_gconf_client_get_default p_gconf_client_get_default = 0;
+    static Ptr_gconf_client_get_string p_gconf_client_get_string = 0;
+    static Ptr_g_object_unref p_g_object_unref = 0;
+    static Ptr_g_error_free p_g_error_free = 0;
+    static Ptr_g_free p_g_free = 0;
+}
+
+
+static int kdeVersion()
+{
+    static int version = qgetenv("KDE_SESSION_VERSION").toInt();
+    return version;
+}
+
+static QString kdeHome()
+{
+    static QString kdeHomePath;
+    if (kdeHomePath.isEmpty()) {
+        kdeHomePath = QFile::decodeName(qgetenv("KDEHOME"));
+        if (kdeHomePath.isEmpty()) {
+            int kdeSessionVersion = kdeVersion();
+            QDir homeDir(QDir::homePath());
+            QString kdeConfDir(QLatin1String("/.kde"));
+            if (4 == kdeSessionVersion && homeDir.exists(QLatin1String(".kde4")))
+                kdeConfDir = QLatin1String("/.kde4");
+            kdeHomePath = QDir::homePath() + kdeConfDir;
+        }
+    }
+    return kdeHomePath;
+}
+
+void QtIconLoaderImplementation::lookupIconTheme() const
+{
+    
+#ifdef Q_WS_X11
+    QString dataDirs = QFile::decodeName(getenv("XDG_DATA_DIRS"));
+    if (dataDirs.isEmpty())
+        dataDirs = QLatin1String("/usr/local/share/:/usr/share/");
+    
+    dataDirs.prepend(QDir::homePath() + QLatin1String("/:"));
+    iconDirs = dataDirs.split(QLatin1Char(':'));
+    
+    // If we are running GNOME we resolve and use GConf. In all other
+    // cases we currently use the KDE icon theme
+    
+    if (qgetenv("DESKTOP_SESSION") == "gnome" ||
+        !qgetenv("GNOME_DESKTOP_SESSION_ID").isEmpty()) {
+        
+        if (themeName.isEmpty()) {
+            // Resolve glib and gconf
+            
+            p_g_type_init =              (Ptr_g_type_init)QLibrary::resolve(QLatin1String("gobject-2.0"), 0, "g_type_init");
+            p_gconf_client_get_default = (Ptr_gconf_client_get_default)QLibrary::resolve(QLatin1String("gconf-2"), 4, "gconf_client_get_default");
+            p_gconf_client_get_string =  (Ptr_gconf_client_get_string)QLibrary::resolve(QLatin1String("gconf-2"), 4, "gconf_client_get_string");
+            p_g_object_unref =           (Ptr_g_object_unref)QLibrary::resolve(QLatin1String("gobject-2.0"), 0, "g_object_unref");
+            p_g_error_free =             (Ptr_g_error_free)QLibrary::resolve(QLatin1String("glib-2.0"), 0, "g_error_free");
+            p_g_free =                   (Ptr_g_free)QLibrary::resolve(QLatin1String("glib-2.0"), 0, "g_free");
+            
+            if (p_g_type_init && p_gconf_client_get_default &&
+                p_gconf_client_get_string && p_g_object_unref &&
+                p_g_error_free && p_g_free) {
+                
+                p_g_type_init();
+                GConfClient* client = p_gconf_client_get_default();
+                GError *err = 0;
+                
+                char *str = p_gconf_client_get_string(client, "/desktop/gnome/interface/icon_theme", &err);
+                if (!err) {
+                    themeName = QString::fromUtf8(str);
+                    p_g_free(str);
+                }
+                
+                p_g_object_unref(client);
+                if (err)
+                    p_g_error_free (err);
+            }
+            if (themeName.isEmpty())
+                themeName = QLatin1String("gnome");
+        }
+        
+        if (!themeName.isEmpty())
+            return;
+    }
+    
+    // KDE (and others)
+    if (dataDirs.isEmpty())
+        dataDirs = QLatin1String("/usr/local/share/:/usr/share/");
+    
+    dataDirs += QLatin1Char(':') + kdeHome() + QLatin1String("/share");
+    dataDirs.prepend(QDir::homePath() + QLatin1String("/:"));
+    QStringList kdeDirs = QFile::decodeName(getenv("KDEDIRS")).split(QLatin1Char(':'));
+    Q_FOREACH (const QString dirName, kdeDirs)
+        dataDirs.append(QLatin1Char(':') + dirName + QLatin1String("/share"));
+    iconDirs = dataDirs.split(QLatin1Char(':'));
+    
+    QFileInfo fileInfo(QLatin1String("/usr/share/icons/default.kde"));
+    QDir dir(fileInfo.canonicalFilePath());
+    QString kdeDefault = kdeVersion() >= 4 ? QString::fromLatin1("oxygen") : QString::fromLatin1("crystalsvg");
+    QString defaultTheme = fileInfo.exists() ? dir.dirName() : kdeDefault;
+    QSettings settings(kdeHome() + QLatin1String("/share/config/kdeglobals"), QSettings::IniFormat);
+    settings.beginGroup(QLatin1String("Icons"));
+    themeName = settings.value(QLatin1String("Theme"), defaultTheme).toString();
+#endif
+}
+
+QIconTheme QtIconLoaderImplementation::parseIndexFile(const QString &themeName) const
+{
+    QIconTheme theme;
+    QFile themeIndex;
+    QStringList parents;
+    QHash <int, QString> dirList;
+
+    for ( int i = 0 ; i < iconDirs.size() && !themeIndex.exists() ; ++i) {
+        const QString &contentDir = QLatin1String(iconDirs[i].startsWith(QDir::homePath()) ? "/.icons/" : "/icons/");
+        themeIndex.setFileName(iconDirs[i] + contentDir + themeName + QLatin1String("/index.theme"));
+    }
+
+    if (themeIndex.exists()) {
+        QSettings indexReader(themeIndex.fileName(), QSettings::IniFormat);
+        Q_FOREACH (const QString &key, indexReader.allKeys()) {
+            if (key.endsWith("/Size")) {
+                if (int size = indexReader.value(key).toInt())
+                    dirList.insertMulti(size, key.left(key.size() - 5));
+            }
+        }
+
+        // Parent themes provide fallbacks for missing icons
+        // parents = indexReader.value(QLatin1String("Icon Theme/Inherits")).toString().split(QLatin1Char(','));
+        parents = indexReader.value(QLatin1String("Icon Theme/Inherits")).toStringList();
+
+
+    }
+
+    if (kdeVersion() >= 3) {
+        QFileInfo fileInfo(QLatin1String("/usr/share/icons/default.kde"));
+        QDir dir(fileInfo.canonicalFilePath());
+        QString defaultKDETheme = dir.exists() ? dir.dirName() : kdeVersion() == 3 ?
+                                  QString::fromLatin1("crystalsvg") : QString::fromLatin1("oxygen");
+        if (!parents.contains(defaultKDETheme) && themeName != defaultKDETheme)
+            parents.append(defaultKDETheme);
+    } else if (parents.isEmpty() && themeName != QLatin1String("hicolor")) {
+        parents.append(QLatin1String("hicolor"));
+    }
+    
+    theme = QIconTheme(dirList, parents);
+    return theme;
+}
+
+QPixmap QtIconLoaderImplementation::findIconHelper(int size, const QString &themeName,
+                                                   const QString &iconName, QStringList &visited) const
+{
+    QPixmap pixmap;
+    
+    if (!themeName.isEmpty()) {
+        visited << themeName;
+        QIconTheme theme = themeList.value(themeName);
+        
+        if (!theme.isValid()) {
+            theme = parseIndexFile(themeName);
+            themeList.insert(themeName, theme);
+        }
+        
+        if (!theme.isValid())
+            return QPixmap();
+        
+        QList <QString> subDirs = theme.dirList().values(size);
+        
+        for ( int i = 0 ; i < iconDirs.size() ; ++i) {
+            for ( int j = 0 ; j < subDirs.size() ; ++j) {
+                QString contentDir = (iconDirs[i].startsWith(QDir::homePath())) ?
+                                     QLatin1String("/.icons/") : QLatin1String("/icons/");
+                QString fileName = iconDirs[i] + contentDir + themeName + QLatin1Char('/') + subDirs[j] + QLatin1Char('/') + iconName;
+                QFile file(fileName);
+                if (file.exists())
+                    pixmap.load(fileName);
+                if (!pixmap.isNull())
+                    break;
+            }
+        }
+        
+        if (pixmap.isNull()) {
+            QStringList parents = theme.parents();
+            //search recursively through inherited themes
+            for (int i = 0 ; pixmap.isNull() && i < parents.size() ; ++i) {
+                QString parentTheme = parents[i].trimmed();
+                if (!visited.contains(parentTheme)) //guard against endless recursion
+                    pixmap = findIconHelper(size, parentTheme, iconName, visited);
+            }
+        }
+    }
+    return pixmap;
+}
+
+QPixmap QtIconLoaderImplementation::findIcon(int size, const QString &name) const
+{
+    QPixmap pixmap;
+    QString pixmapName = QLatin1String("$qt") + name + QString::number(size);
+    if (QPixmapCache::find(pixmapName, pixmap))
+        return pixmap;
+    
+    if (!themeName.isEmpty()) {
+        QStringList visited;
+        pixmap = findIconHelper(size, themeName, name, visited);
+    }
+    QPixmapCache::insert(pixmapName, pixmap);
+    return pixmap;
+}
+#endif //Q_WS_X11
diff --git a/src/iconloader/qticonloader.h b/src/iconloader/qticonloader.h
new file mode 100644 (file)
index 0000000..89fc1b2
--- /dev/null
@@ -0,0 +1,56 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+****************************************************************************/
+
+
+#ifndef QTICONLOADER_H
+#define QTICONLOADER_H
+
+#include <QtGui/QIcon>
+
+// This is the QtIconLoader 
+// Version 0.1
+//
+
+class QtIconLoader
+{
+public:
+    static QIcon icon(const QString &name, const QIcon &fallback = QIcon());
+};
+
+#endif // QTICONLOADER_H
diff --git a/src/loadingwidget.cpp b/src/loadingwidget.cpp
new file mode 100644 (file)
index 0000000..ce830ba
--- /dev/null
@@ -0,0 +1,83 @@
+#include "loadingwidget.h"
+
+LoadingWidget::LoadingWidget(QWidget *parent) : QWidget(parent) {
+
+    QPalette p = palette();
+    p.setBrush(QPalette::Window, Qt::black);
+    p.setBrush(QPalette::Text, Qt::white);
+    setPalette(p);
+
+    setAutoFillBackground(true);
+
+    QFont bigFont;
+    bigFont.setPointSize(bigFont.pointSize()*4);
+    QFontMetrics fm(bigFont);
+    int textHeightInPixels = fm.height();
+    int spacing = textHeightInPixels / 2;
+
+    QBoxLayout *layout = new QVBoxLayout();
+    layout->setSpacing(spacing);
+    layout->setMargin(spacing);
+
+    titleLabel = new QLabel(this);
+    titleLabel->setAlignment(Qt::AlignHCenter | Qt::AlignBottom);
+    titleLabel->setPalette(p);
+    titleLabel->setForegroundRole(QPalette::Text);
+    titleLabel->setWordWrap(true);
+    titleLabel->setFont(bigFont);
+    titleLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+    layout->addWidget(titleLabel);
+
+    QFont biggerFont;
+    biggerFont.setPointSize(biggerFont.pointSize()*2);
+
+    descriptionLabel = new QLabel(this);
+    descriptionLabel->setAlignment(Qt::AlignHCenter | Qt::AlignTop);
+    descriptionLabel->setPalette(p);
+    descriptionLabel->setForegroundRole(QPalette::Text);
+    descriptionLabel->setWordWrap(true);
+    descriptionLabel->setFont(biggerFont);
+    descriptionLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+    layout->addWidget(descriptionLabel);
+
+    /*
+    progressBar = new QProgressBar(this);
+    progressBar->hide();
+    layout->addWidget(progressBar);
+    */
+
+    setMouseTracking(true);
+
+    setLayout(layout);
+}
+
+void LoadingWidget::setVideo(Video *video) {
+    QString title = video->title();
+    // enhance legibility by splitting the title
+    title = title.replace(" - ", "<p>");
+    title = title.replace("] ", "]<p>");
+    title = title.replace(" [", "<p>[");
+    titleLabel->setText(title);
+    descriptionLabel->setText(video->description());
+    // progressBar->hide();
+}
+
+void LoadingWidget::setError(QString message) {
+    titleLabel->setText(tr("Error"));
+    descriptionLabel->setText(message);
+    // progressBar->hide();
+}
+
+void LoadingWidget::bufferStatus(int /* percent */) {
+    /*
+    qDebug() << percent;
+    progressBar->setShown(percent > 0);
+    progressBar->setValue(percent);
+    */
+}
+
+void LoadingWidget::clear() {
+    titleLabel->clear();
+    descriptionLabel->clear();
+    // progressBar->hide();
+}
diff --git a/src/loadingwidget.h b/src/loadingwidget.h
new file mode 100644 (file)
index 0000000..77a3b1d
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef LOADINGWIDGET_H
+#define LOADINGWIDGET_H
+
+#include <QtGui>
+#include "video.h"
+
+class LoadingWidget : public QWidget {
+
+    Q_OBJECT
+
+public:
+    LoadingWidget(QWidget *parent);
+    void setVideo(Video *video);
+    void setError(QString message);
+    void clear();
+
+public slots:
+    void bufferStatus(int);
+
+private:
+    QLabel *titleLabel;
+    QLabel *descriptionLabel;
+    // TODO uncomment the whole progress bar feature
+    // when the Phonon backends will correctly emit bufferStatus(int)
+    // QProgressBar *progressBar;
+
+};
+
+#endif // LOADINGWIDGET_H
diff --git a/src/main.cpp b/src/main.cpp
new file mode 100755 (executable)
index 0000000..a0b87be
--- /dev/null
@@ -0,0 +1,71 @@
+#include <QtGui>
+#include <qtsingleapplication.h>
+#include "Constants.h"
+#include "MainWindow.h"
+
+int main(int argc, char **argv) {
+
+    QtSingleApplication app(argc, argv);
+    if (app.sendMessage("Wake up!"))
+        return 0;
+
+    app.setApplicationName(Constants::APP_NAME);
+    app.setOrganizationName(Constants::ORG_NAME);
+    app.setOrganizationDomain(Constants::ORG_DOMAIN);
+#ifndef Q_WS_MAC
+    app.setWheelScrollLines(1);
+#endif
+
+    const QString locale = QLocale::system().name();
+
+    // qt translations
+    QTranslator qtTranslator;
+    qtTranslator.load("qt_" + locale,
+                      QLibraryInfo::location(QLibraryInfo::TranslationsPath));
+    app.installTranslator(&qtTranslator);
+
+    // app translations
+#ifdef PKGDATADIR
+    QString dataDir = QLatin1String(PKGDATADIR);
+#else
+    QString dataDir = "";
+#endif
+    QString localeDir = dataDir + QDir::separator() + "locale";
+    // if app was not "installed" use the app directory
+    if (!QFile::exists(localeDir)) {
+        localeDir = qApp->applicationDirPath() + QDir::separator() + "locale";
+        // qDebug() << "Using locale dir" << localeDir << locale;
+    }
+    QTranslator translator;
+    translator.load(locale, localeDir);
+    app.installTranslator(&translator);
+    QTextCodec::setCodecForTr(QTextCodec::codecForName("utf8"));
+
+    MainWindow mainWin;
+    mainWin.setWindowTitle(Constants::APP_NAME);
+
+// no window icon on Mac
+#ifndef Q_WS_MAC
+    if (!QFile::exists(dataDir)) {
+        dataDir = qApp->applicationDirPath() + "/data";
+    }
+    const int iconSizes [] = { 16, 22, 24, 32, 48, 64, 128, 256 };
+    QIcon appIcon;
+    for (int i = 0; i < 8; i++) {
+        QString size = QString::number(iconSizes[i]);
+        QString png = dataDir + "/" + size + "x" + size + "/minitube.png";
+        // qDebug() << png;
+        appIcon.addFile(png, QSize(iconSizes[i], iconSizes[i]));
+    }
+    mainWin.setWindowIcon(appIcon);
+#endif
+
+    mainWin.show();
+
+    app.setActivationWindow(&mainWin, true);
+
+    // all string literals are UTF-8
+    QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
+
+    return app.exec();
+}
diff --git a/src/minisplitter.cpp b/src/minisplitter.cpp
new file mode 100644 (file)
index 0000000..02fdacf
--- /dev/null
@@ -0,0 +1,85 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact:  Qt Software Information (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+**************************************************************************/
+
+#include "minisplitter.h"
+
+#include <QtGui/QPaintEvent>
+#include <QtGui/QPainter>
+#include <QtGui/QSplitterHandle>
+
+class MiniSplitterHandle : public QSplitterHandle
+{
+public:
+    MiniSplitterHandle(Qt::Orientation orientation, QSplitter *parent)
+            : QSplitterHandle(orientation, parent)
+    {
+        setMask(QRegion(contentsRect()));
+        setAttribute(Qt::WA_MouseNoMask, true);
+    }
+protected:
+    void resizeEvent(QResizeEvent *event);
+    void paintEvent(QPaintEvent *event);
+};
+
+void MiniSplitterHandle::resizeEvent(QResizeEvent *event)
+{
+    if (orientation() == Qt::Horizontal)
+        setContentsMargins(2, 0, 2, 0);
+    else
+        setContentsMargins(0, 2, 0, 2);
+    setMask(QRegion(contentsRect()));
+    QSplitterHandle::resizeEvent(event);
+}
+
+void MiniSplitterHandle::paintEvent(QPaintEvent *event)
+{
+    QPainter painter(this);
+    painter.fillRect(event->rect(), Qt::black);
+}
+
+QSplitterHandle *MiniSplitter::createHandle()
+{
+    return new MiniSplitterHandle(orientation(), this);
+}
+
+MiniSplitter::MiniSplitter(QWidget *parent)
+    : QSplitter(parent)
+{
+    setHandleWidth(1);
+    setChildrenCollapsible(false);
+    setProperty("minisplitter", true);
+}
+
+MiniSplitter::MiniSplitter(Qt::Orientation orientation)
+    : QSplitter(orientation)
+{
+    setHandleWidth(1);
+    setChildrenCollapsible(false);
+    setProperty("minisplitter", true);
+}
diff --git a/src/minisplitter.h b/src/minisplitter.h
new file mode 100644 (file)
index 0000000..51a1c01
--- /dev/null
@@ -0,0 +1,50 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact:  Qt Software Information (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+**************************************************************************/
+
+#ifndef MINISPLITTER_H
+#define MINISPLITTER_H
+
+#include <QtGui/QSplitter>
+
+QT_BEGIN_NAMESPACE
+class QSplitterHandle;
+QT_END_NAMESPACE
+
+/*! This is a simple helper-class to obtain mac-style 1-pixel wide splitters */
+class MiniSplitter : public QSplitter
+{
+public:
+    MiniSplitter(QWidget *parent = 0);
+    MiniSplitter(Qt::Orientation orientation);
+
+protected:
+    QSplitterHandle *createHandle();
+};
+
+#endif // MINISPLITTER_H
diff --git a/src/networkaccess.cpp b/src/networkaccess.cpp
new file mode 100644 (file)
index 0000000..47d09df
--- /dev/null
@@ -0,0 +1,201 @@
+#include "networkaccess.h"
+#include "Constants.h"
+#include <QtGui>
+
+namespace The {
+    NetworkAccess* http();
+}
+
+NetworkReply::NetworkReply(QNetworkReply *networkReply) : QObject(networkReply) {
+    this->networkReply = networkReply;
+}
+
+void NetworkReply::finished() {
+
+    QUrl redirection = networkReply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
+    if (redirection.isValid()) {
+
+        // qDebug() << "Redirect!"; // << redirection;
+
+        QNetworkReply *redirectReply = The::http()->simpleGet(redirection, networkReply->operation());
+
+        setParent(redirectReply);
+        networkReply->deleteLater();
+        networkReply = redirectReply;
+
+        // when the request is finished we'll invoke the target method
+        connect(networkReply, SIGNAL(finished()), this, SLOT(finished()), Qt::AutoConnection);
+
+        return;
+    }
+
+
+    emit finished(networkReply);
+
+    // get the HTTP response body
+    QByteArray bytes = networkReply->readAll();
+
+    emit data(bytes);
+
+    // bye bye my reply
+    // this will also delete this NetworkReply as the QNetworkReply is its parent
+    networkReply->deleteLater();
+}
+
+void NetworkReply::requestError(QNetworkReply::NetworkError code) {
+    emit error(networkReply);
+}
+
+/* --- NetworkAccess --- */
+
+NetworkAccess::NetworkAccess( QObject* parent) : QObject( parent ) {}
+
+QNetworkReply* NetworkAccess::simpleGet(QUrl url, int operation) {
+
+    QNetworkAccessManager *manager = The::networkAccessManager();
+
+    QNetworkRequest request(url);
+    request.setRawHeader("User-Agent", Constants::USER_AGENT.toUtf8());
+    request.setRawHeader("Connection", "Keep-Alive");
+
+    QNetworkReply *networkReply;
+    switch (operation) {
+
+    case QNetworkAccessManager::GetOperation:
+        qDebug() << "GET" << url.toString();
+        networkReply = manager->get(request);
+        break;
+
+    case QNetworkAccessManager::HeadOperation:
+        qDebug() << "HEAD" << url.toString();
+        networkReply = manager->head(request);
+        break;
+
+    default:
+        qDebug() << "Unknown operation:" << operation;
+        return 0;
+
+    }
+
+    // error handling
+    connect(networkReply, SIGNAL(error(QNetworkReply::NetworkError)),
+            this, SLOT(error(QNetworkReply::NetworkError)));
+
+    return networkReply;
+
+}
+
+NetworkReply* NetworkAccess::get(const QUrl url) {
+
+    QNetworkReply *networkReply = simpleGet(url);
+    NetworkReply *reply = new NetworkReply(networkReply);
+
+    // error signal
+    connect(networkReply, SIGNAL(error(QNetworkReply::NetworkError)),
+            reply, SLOT(requestError(QNetworkReply::NetworkError)));
+
+    // when the request is finished we'll invoke the target method
+    connect(networkReply, SIGNAL(finished()), reply, SLOT(finished()), Qt::AutoConnection);
+
+    return reply;
+
+}
+
+NetworkReply* NetworkAccess::head(const QUrl url) {
+
+    QNetworkReply *networkReply = simpleGet(url, QNetworkAccessManager::HeadOperation);
+    NetworkReply *reply = new NetworkReply(networkReply);
+
+    // error signal
+    connect(networkReply, SIGNAL(error(QNetworkReply::NetworkError)),
+            reply, SLOT(requestError(QNetworkReply::NetworkError)));
+
+    // when the request is finished we'll invoke the target method
+    connect(networkReply, SIGNAL(finished()), reply, SLOT(finished()), Qt::AutoConnection);
+
+    return reply;
+
+}
+
+/*** sync ***/
+
+
+QNetworkReply* NetworkAccess::syncGet(QUrl url) {
+
+    working = true;
+
+    networkReply = simpleGet(url);
+    connect(networkReply, SIGNAL(metaDataChanged()),
+            this, SLOT(syncMetaDataChanged()), Qt::AutoConnection);
+    connect(networkReply, SIGNAL(finished()),
+            this, SLOT(syncFinished()), Qt::AutoConnection);
+    connect(networkReply, SIGNAL(error(QNetworkReply::NetworkError)),
+            this, SLOT(error(QNetworkReply::NetworkError)));
+
+    // A little trick to make this function blocking
+    while (working) {
+        // Do something else, maybe even network processing events
+        qApp->processEvents();
+    }
+
+    networkReply->deleteLater();
+    return networkReply;
+
+}
+
+void NetworkAccess::syncMetaDataChanged() {
+
+    QUrl redirection = networkReply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
+    if (redirection.isValid()) {
+
+        qDebug() << "Redirect" << redirection;
+        networkReply->deleteLater();
+        syncGet(redirection);
+
+        /*
+        QNetworkAccessManager *manager = The::networkAccessManager();
+        networkReply->deleteLater();
+        networkReply = manager->get(QNetworkRequest(redirection));
+        connect(networkReply, SIGNAL(metaDataChanged()),
+                this, SLOT(metaDataChanged()), Qt::AutoConnection);
+        connect(networkReply, SIGNAL(finished()),
+                this, SLOT(finished()), Qt::AutoConnection);
+        */
+    }
+
+}
+
+void NetworkAccess::syncFinished() {
+    // got it!
+    working = false;
+}
+
+void NetworkAccess::error(QNetworkReply::NetworkError code) {
+    // get the QNetworkReply that sent the signal
+    QNetworkReply *networkReply = static_cast<QNetworkReply *>(sender());
+    if (!networkReply) {
+        qDebug() << "Cannot get sender";
+        return;
+    }
+
+    // Ignore HEADs
+    if (networkReply->operation() == QNetworkAccessManager::HeadOperation)
+        return;
+
+    // report the error in the status bar
+    QMainWindow* mainWindow = dynamic_cast<QMainWindow*>(qApp->topLevelWidgets().first());
+    if (mainWindow) mainWindow->statusBar()->showMessage(
+            tr("Network error: %1").arg(networkReply->errorString()));
+
+    qDebug() << "Network error:" << networkReply->errorString() << code;
+
+    networkReply->deleteLater();
+}
+
+QByteArray NetworkAccess::syncGetBytes(QUrl url) {
+    return syncGet(url)->readAll();
+}
+
+QString NetworkAccess::syncGetString(QUrl url) {
+    return QString::fromUtf8(syncGetBytes(url));
+}
diff --git a/src/networkaccess.h b/src/networkaccess.h
new file mode 100644 (file)
index 0000000..64cf508
--- /dev/null
@@ -0,0 +1,59 @@
+#ifndef NETWORKACCESS_H
+#define NETWORKACCESS_H
+
+#include <QtNetwork>
+
+namespace The {
+    QNetworkAccessManager* networkAccessManager();
+}
+
+class NetworkReply : public QObject {
+
+    Q_OBJECT
+
+public:
+    NetworkReply(QNetworkReply* networkReply);
+
+public slots:
+    void finished();
+    void requestError(QNetworkReply::NetworkError);
+
+signals:
+    void data(QByteArray);
+    void error(QNetworkReply*);
+    void finished(QNetworkReply*);
+
+private:
+    QNetworkReply *networkReply;
+
+};
+
+
+class NetworkAccess : public QObject {
+
+    Q_OBJECT
+
+public:
+    NetworkAccess( QObject* parent=0);
+    QNetworkReply* simpleGet(QUrl url, int operation = QNetworkAccessManager::GetOperation);
+    NetworkReply* get(QUrl url);
+    NetworkReply* head(QUrl url);
+    QNetworkReply* syncGet(QUrl url);
+    QByteArray syncGetBytes(QUrl url);
+    QString syncGetString(QUrl url);
+
+private slots:
+    void error(QNetworkReply::NetworkError);
+    void syncMetaDataChanged();
+    void syncFinished();
+
+private:
+    QNetworkReply *networkReply;
+    bool working;
+
+};
+
+typedef QPointer<QObject> ObjectPointer;
+Q_DECLARE_METATYPE(ObjectPointer)
+
+#endif // NETWORKACCESS_H
diff --git a/src/playlist/PrettyItemDelegate.cpp b/src/playlist/PrettyItemDelegate.cpp
new file mode 100644 (file)
index 0000000..9463b66
--- /dev/null
@@ -0,0 +1,246 @@
+#include "PrettyItemDelegate.h"
+#include "../ListModel.h"
+
+#include <QFontMetricsF>
+#include <QPainter>
+
+const qreal PrettyItemDelegate::THUMB_HEIGHT = 90.0;
+const qreal PrettyItemDelegate::THUMB_WIDTH = 120.0;
+const qreal PrettyItemDelegate::PADDING = 10.0;
+
+PrettyItemDelegate::PrettyItemDelegate( QObject* parent ) : QStyledItemDelegate( parent ) {
+
+    boldFont.setBold(true);
+    smallerFont.setPointSize(smallerFont.pointSize()*.85);
+    smallerBoldFont.setBold(true);
+    smallerBoldFont.setPointSize(smallerBoldFont.pointSize()*.85);
+    QFontInfo fontInfo(smallerFont);
+    if (fontInfo.pixelSize() < 10) {
+        smallerFont.setPixelSize(10);
+        smallerBoldFont.setPixelSize(10);
+    }
+    createPlayIcon();
+}
+
+void PrettyItemDelegate::createPlayIcon() {
+    playIcon = QPixmap(THUMB_WIDTH, THUMB_HEIGHT);
+    playIcon.fill(Qt::transparent);
+    QPainter painter(&playIcon);
+    QPolygon polygon;
+    polygon << QPoint(PADDING*4, PADDING*2)
+            << QPoint(THUMB_WIDTH-PADDING*4, THUMB_HEIGHT/2)
+            << QPoint(PADDING*4, THUMB_HEIGHT-PADDING*2);
+    painter.setRenderHints(QPainter::Antialiasing, true);
+    painter.setBrush(Qt::white);
+    QPen pen;
+    pen.setColor(Qt::white);
+    pen.setWidth(PADDING);
+    pen.setJoinStyle(Qt::RoundJoin);
+    pen.setCapStyle(Qt::RoundCap);
+    painter.setPen(pen);
+    painter.drawPolygon(polygon);
+}
+
+PrettyItemDelegate::~PrettyItemDelegate() { }
+
+QSize PrettyItemDelegate::sizeHint( const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/ ) const {
+    return QSize( 256, THUMB_HEIGHT+1.0);
+}
+
+void PrettyItemDelegate::paint( QPainter* painter,
+                                const QStyleOptionViewItem& option, const QModelIndex& index ) const {
+
+    int itemType = index.data(ItemTypeRole).toInt();
+    if (itemType == ItemTypeVideo) {
+        QApplication::style()->drawPrimitive( QStyle::PE_PanelItemViewItem, &option, painter );
+        paintBody( painter, option, index );
+    } else
+        QStyledItemDelegate::paint( painter, option, index );
+
+}
+
+void PrettyItemDelegate::paintBody( QPainter* painter,
+                                    const QStyleOptionViewItem& option,
+                                    const QModelIndex& index ) const {
+
+    painter->save();
+    painter->translate( option.rect.topLeft() );
+
+
+    const QRectF line(0, 0, option.rect.width(), option.rect.height());
+    painter->setClipRect(line);
+
+    const bool isActive = index.data( ActiveTrackRole ).toBool();
+    const bool isSelected = option.state & QStyle::State_Selected;
+
+    // draw the "current track" highlight underneath the text
+    if (isActive && !isSelected) {
+        paintActiveOverlay(painter, line.x(), line.y(), line.width(), line.height());
+    }
+
+    // get the video metadata
+    const VideoPointer videoPointer = index.data( VideoRole ).value<VideoPointer>();
+    const Video *video = videoPointer.data();
+
+    // thumb
+    if (!video->thumbnail().isNull()) {
+        painter->drawImage(QRect(0, 0, THUMB_WIDTH, THUMB_HEIGHT), video->thumbnail());
+
+        // play icon overlayed on the thumb
+        if (isActive)
+            paintPlayIcon(painter);
+
+        // time
+        QString timeString;
+        int duration = video->duration();
+        if ( duration > 3600 )
+            timeString = QTime().addSecs(duration).toString("h:mm:ss");
+        else
+            timeString = QTime().addSecs(duration).toString("m:ss");
+        drawTime(painter, timeString, line);
+
+    }
+
+    if (isActive) painter->setFont(boldFont);
+    const QFontMetricsF fm(painter->font());
+    const QFontMetricsF boldMetrics(boldFont);
+
+    // text color
+    if (isSelected)
+        painter->setPen(QPen(option.palette.brush(QPalette::HighlightedText), 0));
+    else
+        painter->setPen(QPen(option.palette.brush(QPalette::Text), 0));
+
+    // title
+    QString videoTitle = video->title();
+    QRectF textBox = line.adjusted(PADDING+THUMB_WIDTH, PADDING, -2 * PADDING, -PADDING);
+    textBox = painter->boundingRect( textBox, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, videoTitle);
+    painter->drawText(textBox, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, videoTitle);
+
+    painter->setFont(smallerFont);
+
+    // published date
+    QString publishedString = video->published().date().toString(Qt::DefaultLocaleShortDate);
+    QSizeF publishedStringSize(QFontMetrics(painter->font()).size( Qt::TextSingleLine, publishedString ) );
+    QPointF textLoc(PADDING+THUMB_WIDTH, PADDING*2 + textBox.height());
+    QRectF publishedTextBox( textLoc , publishedStringSize);
+    painter->drawText(publishedTextBox, Qt::AlignLeft | Qt::AlignTop, publishedString);
+
+    // author
+    painter->save();
+    painter->setFont(smallerBoldFont);
+    if (!isSelected && !isActive)
+        painter->setPen(QPen(option.palette.brush(QPalette::Mid), 0));
+    QString authorString = video->author();
+    QSizeF authorStringSize(QFontMetrics(painter->font()).size( Qt::TextSingleLine, authorString ) );
+    textLoc.setX(textLoc.x() + publishedStringSize.width() + PADDING);
+    QRectF authorTextBox( textLoc , authorStringSize);
+    painter->drawText(authorTextBox, Qt::AlignLeft | Qt::AlignTop, authorString);
+    painter->restore();
+
+    // view count
+    if (video->viewCount() >= 0) {
+        painter->save();
+        QLocale locale;
+        QString viewCountString = tr("%1 views").arg(locale.toString(video->viewCount()));
+        QSizeF viewCountStringSize(QFontMetrics(painter->font()).size( Qt::TextSingleLine, viewCountString ) );
+        textLoc.setX(textLoc.x() + authorStringSize.width() + PADDING);
+        QRectF viewCountTextBox( textLoc , viewCountStringSize);
+        painter->drawText(viewCountTextBox, Qt::AlignLeft | Qt::AlignBottom, viewCountString);
+        painter->restore();
+    }
+
+    /*
+    QLinearGradient myGradient;
+    QPen myPen;
+    QFont myFont;
+    QPointF baseline(authorTextBox.x(), authorTextBox.y() + authorTextBox.height());
+    QPainterPath myPath;
+    myPath.addText(baseline, boldFont, authorString);
+    painter->setBrush(palette.color(QPalette::WindowText));
+    painter->setPen(palette.color(QPalette::Dark));
+    painter->setRenderHints (QPainter::Antialiasing, true);
+    painter->drawPath(myPath);
+    */
+
+    // separator
+    painter->setPen(option.palette.color(QPalette::Midlight));
+    painter->drawLine(THUMB_WIDTH, THUMB_HEIGHT, line.width(), THUMB_HEIGHT);
+    if (!video->thumbnail().isNull())
+        painter->setPen(Qt::black);
+    painter->drawLine(0, THUMB_HEIGHT, THUMB_WIDTH-1, THUMB_HEIGHT);
+
+    painter->restore();
+
+}
+
+QPointF PrettyItemDelegate::centerImage( const QPixmap& pixmap, const QRectF& rect ) const {
+    qreal pixmapRatio = ( qreal )pixmap.width() / ( qreal )pixmap.height();
+
+    qreal moveByX = 0.0;
+    qreal moveByY = 0.0;
+
+    if ( pixmapRatio >= 1 )
+        moveByY = ( rect.height() - ( rect.width() / pixmapRatio ) ) / 2.0;
+    else
+        moveByX = ( rect.width() - ( rect.height() * pixmapRatio ) ) / 2.0;
+
+    return QPointF( moveByX, moveByY );
+}
+
+void PrettyItemDelegate::paintActiveOverlay( QPainter *painter, qreal x, qreal y, qreal w, qreal h ) const {
+
+    QPalette palette;
+    QColor highlightColor = palette.color(QPalette::Highlight);
+    QColor backgroundColor = palette.color(QPalette::Base);
+    const float animation = 0.25;
+    const int gradientRange = 16;
+
+    QColor color2 = QColor::fromHsv(
+            highlightColor.hue(),
+            (int) (backgroundColor.saturation() * (1.0f - animation) + highlightColor.saturation() * animation),
+            (int) (backgroundColor.value() * (1.0f - animation) + highlightColor.value() * animation)
+            );
+    QColor color1 = QColor::fromHsv(
+            color2.hue(),
+            qMax(color2.saturation() - gradientRange, 0),
+            qMin(color2.value() + gradientRange, 255)
+            );
+    QRect rect((int) x, (int) y, (int) w, (int) h);
+    painter->save();
+    painter->setPen(Qt::NoPen);
+    QLinearGradient linearGradient(0, 0, 0, rect.height());
+    linearGradient.setColorAt(0.0, color1);
+    linearGradient.setColorAt(1.0, color2);
+    painter->setBrush(linearGradient);
+    painter->drawRect(rect);
+    painter->restore();
+}
+
+void PrettyItemDelegate::paintPlayIcon(QPainter *painter) const {
+    painter->save();
+    painter->setOpacity(.5);
+    painter->drawPixmap(playIcon.rect(), playIcon);
+    painter->restore();
+}
+
+void PrettyItemDelegate::drawTime(QPainter *painter, QString time, QRectF line) const {
+    static const int timePadding = 4;
+    QRectF textBox = painter->boundingRect(line, Qt::AlignLeft | Qt::AlignTop, time);
+    // add padding
+    textBox.adjust(0, 0, timePadding, 0);
+    // move to bottom right corner of the thumb
+    textBox.translate(THUMB_WIDTH - textBox.width(), THUMB_HEIGHT - textBox.height());
+
+    painter->save();
+    painter->setPen(Qt::NoPen);
+    painter->setBrush(Qt::black);
+    painter->setOpacity(.5);
+    painter->drawRect(textBox);
+    painter->restore();
+
+    painter->save();
+    painter->setPen(Qt::white);
+    painter->drawText(textBox, Qt::AlignCenter, time);
+    painter->restore();
+}
diff --git a/src/playlist/PrettyItemDelegate.h b/src/playlist/PrettyItemDelegate.h
new file mode 100644 (file)
index 0000000..6fc547b
--- /dev/null
@@ -0,0 +1,42 @@
+#ifndef PRETTYITEMDELEGATE_H
+#define PRETTYITEMDELEGATE_H
+
+#include <QModelIndex>
+#include <QStyledItemDelegate>
+
+class QPainter;
+
+class PrettyItemDelegate : public QStyledItemDelegate {
+
+    Q_OBJECT
+
+public:
+    PrettyItemDelegate( QObject* parent = 0 );
+    ~PrettyItemDelegate();
+
+    QSize sizeHint( const QStyleOptionViewItem&, const QModelIndex& ) const;
+    void paint( QPainter*, const QStyleOptionViewItem&, const QModelIndex& ) const;
+
+private:
+    void createPlayIcon();
+    void paintBody( QPainter*, const QStyleOptionViewItem&, const QModelIndex& ) const;
+    QPointF centerImage( const QPixmap&, const QRectF& ) const;
+
+    // active track painting
+    void paintActiveOverlay( QPainter *painter, qreal x, qreal y, qreal w, qreal h ) const;
+    void paintPlayIcon(QPainter *painter) const;
+
+    //  Paints the video duration
+    void drawTime(QPainter *painter, QString time, QRectF line) const;
+
+    static const qreal THUMB_WIDTH;
+    static const qreal THUMB_HEIGHT;
+    static const qreal PADDING;
+
+    QPixmap playIcon;
+    QFont boldFont;
+    QFont smallerFont;
+    QFont smallerBoldFont;
+};
+
+#endif
diff --git a/src/playlistwidget.cpp b/src/playlistwidget.cpp
new file mode 100644 (file)
index 0000000..61b1336
--- /dev/null
@@ -0,0 +1,11 @@
+#include "playlistwidget.h"
+
+PlaylistWidget::PlaylistWidget (QWidget *parent, THBlackBar *tabBar, QListView *listView)
+    : QWidget(parent) {
+    QBoxLayout *layout = new QVBoxLayout();
+    layout->setMargin(0);
+    layout->setSpacing(0);
+    layout->addWidget(tabBar);
+    layout->addWidget(listView);
+    setLayout(layout);
+}
diff --git a/src/playlistwidget.h b/src/playlistwidget.h
new file mode 100644 (file)
index 0000000..8d2bb29
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef PLAYLISTWIDGET_H
+#define PLAYLISTWIDGET_H
+
+#include <QtGui>
+#include "thblackbar.h"
+
+class PlaylistWidget : public QWidget
+{
+public:
+    PlaylistWidget(QWidget *parent, THBlackBar *tabBar, QListView *listView);
+};
+
+#endif // PLAYLISTWIDGET_H
diff --git a/src/qtsingleapplication/QtLockedFile b/src/qtsingleapplication/QtLockedFile
new file mode 100644 (file)
index 0000000..16b48ba
--- /dev/null
@@ -0,0 +1 @@
+#include "qtlockedfile.h"
diff --git a/src/qtsingleapplication/QtSingleApplication b/src/qtsingleapplication/QtSingleApplication
new file mode 100644 (file)
index 0000000..d111bf7
--- /dev/null
@@ -0,0 +1 @@
+#include "qtsingleapplication.h"
diff --git a/src/qtsingleapplication/qtlocalpeer.cpp b/src/qtsingleapplication/qtlocalpeer.cpp
new file mode 100644 (file)
index 0000000..1d494a7
--- /dev/null
@@ -0,0 +1,203 @@
+/****************************************************************************
+**
+** This file is part of a Qt Solutions component.
+** 
+** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** 
+** Contact:  Qt Software Information (qt-info@nokia.com)
+** 
+** Commercial Usage  
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Solutions Commercial License Agreement provided
+** with the Software or, alternatively, in accordance with the terms
+** contained in a written agreement between you and Nokia.
+** 
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+** 
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+** 
+** GNU General Public License Usage 
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+** 
+** Please note Third Party Software included with Qt Solutions may impose
+** additional restrictions and it is the user's responsibility to ensure
+** that they have met the licensing requirements of the GPL, LGPL, or Qt
+** Solutions Commercial license and the relevant license of the Third
+** Party Software they are using.
+** 
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** 
+****************************************************************************/
+
+
+#include "qtlocalpeer.h"
+#include <QtCore/QCoreApplication>
+#include <QtCore/QTime>
+
+#if defined(Q_OS_WIN)
+#include <QtCore/QLibrary>
+#include <QtCore/qt_windows.h>
+typedef BOOL(WINAPI*PProcessIdToSessionId)(DWORD,DWORD*);
+static PProcessIdToSessionId pProcessIdToSessionId = 0;
+#endif
+#if defined(Q_OS_UNIX)
+#include <time.h>
+#endif
+
+namespace QtLP_Private {
+#include "qtlockedfile.cpp"
+#if defined(Q_OS_WIN)
+#include "qtlockedfile_win.cpp"
+#else
+#include "qtlockedfile_unix.cpp"
+#endif
+}
+
+const char* QtLocalPeer::ack = "ack";
+
+QtLocalPeer::QtLocalPeer(QObject* parent, const QString &appId)
+    : QObject(parent), id(appId)
+{
+    QString prefix = id;
+    if (id.isEmpty()) {
+        id = QCoreApplication::applicationFilePath();
+#if defined(Q_OS_WIN)
+        id = id.toLower();
+#endif
+        prefix = id.section(QLatin1Char('/'), -1);
+    }
+    prefix.remove(QRegExp("[^a-zA-Z]"));
+    prefix.truncate(6);
+
+    QByteArray idc = id.toUtf8();
+    quint16 idNum = qChecksum(idc.constData(), idc.size());
+    socketName = QLatin1String("qtsingleapp-") + prefix
+                 + QLatin1Char('-') + QString::number(idNum, 16);
+
+#if defined(Q_OS_WIN)
+    if (!pProcessIdToSessionId) {
+        QLibrary lib("kernel32");
+        pProcessIdToSessionId = (PProcessIdToSessionId)lib.resolve("ProcessIdToSessionId");
+    }
+    if (pProcessIdToSessionId) {
+        DWORD sessionId = 0;
+        pProcessIdToSessionId(GetCurrentProcessId(), &sessionId);
+        socketName += QLatin1Char('-') + QString::number(sessionId, 16);
+    }
+#else
+    socketName += QLatin1Char('-') + QString::number(::getuid(), 16);
+#endif
+
+    server = new QLocalServer(this);
+    QString lockName = QDir(QDir::tempPath()).absolutePath()
+                       + QLatin1Char('/') + socketName
+                       + QLatin1String("-lockfile");
+    lockFile.setFileName(lockName);
+    lockFile.open(QIODevice::ReadWrite);
+}
+
+
+
+bool QtLocalPeer::isClient()
+{
+    if (lockFile.isLocked())
+        return false;
+
+    if (!lockFile.lock(QtLP_Private::QtLockedFile::WriteLock, false))
+        return true;
+
+    bool res = server->listen(socketName);
+#if defined(Q_OS_UNIX) && (QT_VERSION >= QT_VERSION_CHECK(4,5,0))
+    // ### Workaround
+    if (!res && server->serverError() == QAbstractSocket::AddressInUseError) {
+        QFile::remove(QDir::cleanPath(QDir::tempPath())+QLatin1Char('/')+socketName);
+        res = server->listen(socketName);
+    }
+#endif
+    if (!res)
+        qWarning("QtSingleCoreApplication: listen on local socket failed, %s", qPrintable(server->errorString()));
+    QObject::connect(server, SIGNAL(newConnection()), SLOT(receiveConnection()));
+    return false;
+}
+
+
+bool QtLocalPeer::sendMessage(const QString &message, int timeout)
+{
+    if (!isClient())
+        return false;
+
+    QLocalSocket socket;
+    bool connOk = false;
+    for(int i = 0; i < 2; i++) {
+        // Try twice, in case the other instance is just starting up
+        socket.connectToServer(socketName);
+        connOk = socket.waitForConnected(timeout/2);
+        if (connOk || i)
+            break;
+        int ms = 250;
+#if defined(Q_OS_WIN)
+        Sleep(DWORD(ms));
+#else
+        struct timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 };
+        nanosleep(&ts, NULL);
+#endif
+    }
+    if (!connOk)
+        return false;
+
+    QByteArray uMsg(message.toUtf8());
+    QDataStream ds(&socket);
+    ds.writeBytes(uMsg.constData(), uMsg.size());
+    bool res = socket.waitForBytesWritten(timeout);
+    res &= socket.waitForReadyRead(timeout);   // wait for ack
+    res &= (socket.read(qstrlen(ack)) == ack);
+    return res;
+}
+
+
+void QtLocalPeer::receiveConnection()
+{
+    QLocalSocket* socket = server->nextPendingConnection();
+    if (!socket)
+        return;
+
+    while (socket->bytesAvailable() < (int)sizeof(quint32))
+        socket->waitForReadyRead();
+    QDataStream ds(socket);
+    QByteArray uMsg;
+    quint32 remaining;
+    ds >> remaining;
+    uMsg.resize(remaining);
+    int got = 0;
+    char* uMsgBuf = uMsg.data();
+    do {
+        got = ds.readRawData(uMsgBuf, remaining);
+        remaining -= got;
+        uMsgBuf += got;
+    } while (remaining && got >= 0 && socket->waitForReadyRead(2000));
+    if (got < 0) {
+        qWarning() << "QtLocalPeer: Message reception failed" << socket->errorString();
+        delete socket;
+        return;
+    }
+    QString message(QString::fromUtf8(uMsg));
+    socket->write(ack, qstrlen(ack));
+    socket->waitForBytesWritten(1000);
+    delete socket;
+    emit messageReceived(message); //### (might take a long time to return)
+}
diff --git a/src/qtsingleapplication/qtlocalpeer.h b/src/qtsingleapplication/qtlocalpeer.h
new file mode 100644 (file)
index 0000000..1169e1f
--- /dev/null
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** This file is part of a Qt Solutions component.
+** 
+** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** 
+** Contact:  Qt Software Information (qt-info@nokia.com)
+** 
+** Commercial Usage  
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Solutions Commercial License Agreement provided
+** with the Software or, alternatively, in accordance with the terms
+** contained in a written agreement between you and Nokia.
+** 
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+** 
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+** 
+** GNU General Public License Usage 
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+** 
+** Please note Third Party Software included with Qt Solutions may impose
+** additional restrictions and it is the user's responsibility to ensure
+** that they have met the licensing requirements of the GPL, LGPL, or Qt
+** Solutions Commercial license and the relevant license of the Third
+** Party Software they are using.
+** 
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** 
+****************************************************************************/
+
+
+#include <QtNetwork/QLocalServer>
+#include <QtNetwork/QLocalSocket>
+#include <QtCore/QDir>
+
+namespace QtLP_Private {
+#include "qtlockedfile.h"
+}
+
+class QtLocalPeer : public QObject
+{
+    Q_OBJECT
+
+public:
+    QtLocalPeer(QObject *parent = 0, const QString &appId = QString());
+    bool isClient();
+    bool sendMessage(const QString &message, int timeout);
+    QString applicationId() const
+        { return id; }
+
+Q_SIGNALS:
+    void messageReceived(const QString &message);
+
+protected Q_SLOTS:
+    void receiveConnection();
+
+protected:
+    QString id;
+    QString socketName;
+    QLocalServer* server;
+    QtLP_Private::QtLockedFile lockFile;
+
+private:
+    static const char* ack;
+};
diff --git a/src/qtsingleapplication/qtlockedfile.cpp b/src/qtsingleapplication/qtlockedfile.cpp
new file mode 100644 (file)
index 0000000..d4fad75
--- /dev/null
@@ -0,0 +1,199 @@
+/****************************************************************************
+**
+** This file is part of a Qt Solutions component.
+** 
+** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** 
+** Contact:  Qt Software Information (qt-info@nokia.com)
+** 
+** Commercial Usage  
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Solutions Commercial License Agreement provided
+** with the Software or, alternatively, in accordance with the terms
+** contained in a written agreement between you and Nokia.
+** 
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+** 
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+** 
+** GNU General Public License Usage 
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+** 
+** Please note Third Party Software included with Qt Solutions may impose
+** additional restrictions and it is the user's responsibility to ensure
+** that they have met the licensing requirements of the GPL, LGPL, or Qt
+** Solutions Commercial license and the relevant license of the Third
+** Party Software they are using.
+** 
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** 
+****************************************************************************/
+
+#include "qtlockedfile.h"
+
+/*!
+    \class QtLockedFile
+
+    \brief The QtLockedFile class extends QFile with advisory locking
+    functions.
+
+    A file may be locked in read or write mode. Multiple instances of
+    \e QtLockedFile, created in multiple processes running on the same
+    machine, may have a file locked in read mode. Exactly one instance
+    may have it locked in write mode. A read and a write lock cannot
+    exist simultaneously on the same file.
+
+    The file locks are advisory. This means that nothing prevents
+    another process from manipulating a locked file using QFile or
+    file system functions offered by the OS. Serialization is only
+    guaranteed if all processes that access the file use
+    QLockedFile. Also, while holding a lock on a file, a process
+    must not open the same file again (through any API), or locks
+    can be unexpectedly lost.
+
+    The lock provided by an instance of \e QtLockedFile is released
+    whenever the program terminates. This is true even when the
+    program crashes and no destructors are called.
+*/
+
+/*! \enum QtLockedFile::LockMode
+
+    This enum describes the available lock modes.
+
+    \value ReadLock A read lock.
+    \value WriteLock A write lock.
+    \value NoLock Neither a read lock nor a write lock.
+*/
+
+/*!
+    Constructs an unlocked \e QtLockedFile object. This constructor
+    behaves in the same way as \e QFile::QFile().
+
+    \sa QFile::QFile()
+*/
+QtLockedFile::QtLockedFile()
+    : QFile()
+{
+#ifdef Q_OS_WIN
+    wmutex = 0;
+    rmutex = 0;
+#endif
+    m_lock_mode = NoLock;
+}
+
+/*!
+    Constructs an unlocked QtLockedFile object with file \a name. This
+    constructor behaves in the same way as \e QFile::QFile(const
+    QString&).
+
+    \sa QFile::QFile()
+*/
+QtLockedFile::QtLockedFile(const QString &name)
+    : QFile(name)
+{
+#ifdef Q_OS_WIN
+    wmutex = 0;
+    rmutex = 0;
+#endif
+    m_lock_mode = NoLock;
+}
+
+/*!
+  Opens the file in OpenMode \a mode.
+
+  This is identical to QFile::open(), with the one exception that the
+  Truncate mode flag is disallowed. Truncation would conflict with the
+  advisory file locking, since the file would be modified before the
+  write lock is obtained. If truncation is required, use resize(0)
+  after obtaining the write lock.
+
+  Returns true if successful; otherwise false.
+
+  \sa QFile::open(), QFile::resize()
+*/
+bool QtLockedFile::open(OpenMode mode)
+{
+    if (mode & QIODevice::Truncate) {
+        qWarning("QtLockedFile::open(): Truncate mode not allowed.");
+        return false;
+    }
+    return QFile::open(mode);
+}
+
+/*!
+    Returns \e true if this object has a in read or write lock;
+    otherwise returns \e false.
+
+    \sa lockMode()
+*/
+bool QtLockedFile::isLocked() const
+{
+    return m_lock_mode != NoLock;
+}
+
+/*!
+    Returns the type of lock currently held by this object, or \e
+    QtLockedFile::NoLock.
+
+    \sa isLocked()
+*/
+QtLockedFile::LockMode QtLockedFile::lockMode() const
+{
+    return m_lock_mode;
+}
+
+/*!
+    \fn bool QtLockedFile::lock(LockMode mode, bool block = true)
+
+    Obtains a lock of type \a mode. The file must be opened before it
+    can be locked.
+
+    If \a block is true, this function will block until the lock is
+    aquired. If \a block is false, this function returns \e false
+    immediately if the lock cannot be aquired.
+
+    If this object already has a lock of type \a mode, this function
+    returns \e true immediately. If this object has a lock of a
+    different type than \a mode, the lock is first released and then a
+    new lock is obtained.
+
+    This function returns \e true if, after it executes, the file is
+    locked by this object, and \e false otherwise.
+
+    \sa unlock(), isLocked(), lockMode()
+*/
+
+/*!
+    \fn bool QtLockedFile::unlock()
+
+    Releases a lock.
+
+    If the object has no lock, this function returns immediately.
+
+    This function returns \e true if, after it executes, the file is
+    not locked by this object, and \e false otherwise.
+
+    \sa lock(), isLocked(), lockMode()
+*/
+
+/*!
+    \fn QtLockedFile::~QtLockedFile()
+
+    Destroys the \e QtLockedFile object. If any locks were held, they
+    are released.
+*/
diff --git a/src/qtsingleapplication/qtlockedfile.h b/src/qtsingleapplication/qtlockedfile.h
new file mode 100644 (file)
index 0000000..e229484
--- /dev/null
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** This file is part of a Qt Solutions component.
+** 
+** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** 
+** Contact:  Qt Software Information (qt-info@nokia.com)
+** 
+** Commercial Usage  
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Solutions Commercial License Agreement provided
+** with the Software or, alternatively, in accordance with the terms
+** contained in a written agreement between you and Nokia.
+** 
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+** 
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+** 
+** GNU General Public License Usage 
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+** 
+** Please note Third Party Software included with Qt Solutions may impose
+** additional restrictions and it is the user's responsibility to ensure
+** that they have met the licensing requirements of the GPL, LGPL, or Qt
+** Solutions Commercial license and the relevant license of the Third
+** Party Software they are using.
+** 
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** 
+****************************************************************************/
+
+#ifndef QTLOCKEDFILE_H
+#define QTLOCKEDFILE_H
+
+#include <QtCore/QFile>
+#ifdef Q_OS_WIN
+#include <QtCore/QVector>
+#endif
+
+#if defined(Q_WS_WIN)
+#  if !defined(QT_QTLOCKEDFILE_EXPORT) && !defined(QT_QTLOCKEDFILE_IMPORT)
+#    define QT_QTLOCKEDFILE_EXPORT
+#  elif defined(QT_QTLOCKEDFILE_IMPORT)
+#    if defined(QT_QTLOCKEDFILE_EXPORT)
+#      undef QT_QTLOCKEDFILE_EXPORT
+#    endif
+#    define QT_QTLOCKEDFILE_EXPORT __declspec(dllimport)
+#  elif defined(QT_QTLOCKEDFILE_EXPORT)
+#    undef QT_QTLOCKEDFILE_EXPORT
+#    define QT_QTLOCKEDFILE_EXPORT __declspec(dllexport)
+#  endif
+#else
+#  define QT_QTLOCKEDFILE_EXPORT
+#endif
+
+class QT_QTLOCKEDFILE_EXPORT QtLockedFile : public QFile
+{
+public:
+    enum LockMode { NoLock = 0, ReadLock, WriteLock };
+
+    QtLockedFile();
+    QtLockedFile(const QString &name);
+    ~QtLockedFile();
+
+    bool open(OpenMode mode);
+
+    bool lock(LockMode mode, bool block = true);
+    bool unlock();
+    bool isLocked() const;
+    LockMode lockMode() const;
+
+private:
+#ifdef Q_OS_WIN
+    Qt::HANDLE wmutex;
+    Qt::HANDLE rmutex;
+    QVector<Qt::HANDLE> rmutexes;
+    QString mutexname;
+
+    Qt::HANDLE getMutexHandle(int idx, bool doCreate);
+    bool waitMutex(Qt::HANDLE mutex, bool doBlock);
+
+#endif
+    LockMode m_lock_mode;
+};
+
+#endif
diff --git a/src/qtsingleapplication/qtlockedfile_unix.cpp b/src/qtsingleapplication/qtlockedfile_unix.cpp
new file mode 100644 (file)
index 0000000..f5997ba
--- /dev/null
@@ -0,0 +1,121 @@
+/****************************************************************************
+**
+** This file is part of a Qt Solutions component.
+** 
+** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** 
+** Contact:  Qt Software Information (qt-info@nokia.com)
+** 
+** Commercial Usage  
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Solutions Commercial License Agreement provided
+** with the Software or, alternatively, in accordance with the terms
+** contained in a written agreement between you and Nokia.
+** 
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+** 
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+** 
+** GNU General Public License Usage 
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+** 
+** Please note Third Party Software included with Qt Solutions may impose
+** additional restrictions and it is the user's responsibility to ensure
+** that they have met the licensing requirements of the GPL, LGPL, or Qt
+** Solutions Commercial license and the relevant license of the Third
+** Party Software they are using.
+** 
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** 
+****************************************************************************/
+
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "qtlockedfile.h"
+
+bool QtLockedFile::lock(LockMode mode, bool block)
+{
+    if (!isOpen()) {
+        qWarning("QtLockedFile::lock(): file is not opened");
+        return false;
+    }
+    if (mode == NoLock)
+        return unlock();
+           
+    if (mode == m_lock_mode)
+        return true;
+
+    if (m_lock_mode != NoLock)
+        unlock();
+
+    struct flock fl;
+    fl.l_whence = SEEK_SET;
+    fl.l_start = 0;
+    fl.l_len = 0;
+    fl.l_type = (mode == ReadLock) ? F_RDLCK : F_WRLCK;
+    int cmd = block ? F_SETLKW : F_SETLK;
+    int ret = fcntl(handle(), cmd, &fl);
+    
+    if (ret == -1) {
+        if (errno != EINTR && errno != EAGAIN)
+            qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno));
+        return false;
+    }
+
+    
+    m_lock_mode = mode;
+    return true;
+}
+
+
+bool QtLockedFile::unlock()
+{
+    if (!isOpen()) {
+        qWarning("QtLockedFile::unlock(): file is not opened");
+        return false;
+    }
+
+    if (!isLocked())
+        return true;
+
+    struct flock fl;
+    fl.l_whence = SEEK_SET;
+    fl.l_start = 0;
+    fl.l_len = 0;
+    fl.l_type = F_UNLCK;
+    int ret = fcntl(handle(), F_SETLKW, &fl);
+    
+    if (ret == -1) {
+        qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno));
+        return false;
+    }
+    
+    m_lock_mode = NoLock;
+    return true;
+}
+
+QtLockedFile::~QtLockedFile()
+{
+    if (isOpen())
+        unlock();
+}
+
diff --git a/src/qtsingleapplication/qtlockedfile_win.cpp b/src/qtsingleapplication/qtlockedfile_win.cpp
new file mode 100644 (file)
index 0000000..8bbaa53
--- /dev/null
@@ -0,0 +1,213 @@
+/****************************************************************************
+**
+** This file is part of a Qt Solutions component.
+** 
+** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** 
+** Contact:  Qt Software Information (qt-info@nokia.com)
+** 
+** Commercial Usage  
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Solutions Commercial License Agreement provided
+** with the Software or, alternatively, in accordance with the terms
+** contained in a written agreement between you and Nokia.
+** 
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+** 
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+** 
+** GNU General Public License Usage 
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+** 
+** Please note Third Party Software included with Qt Solutions may impose
+** additional restrictions and it is the user's responsibility to ensure
+** that they have met the licensing requirements of the GPL, LGPL, or Qt
+** Solutions Commercial license and the relevant license of the Third
+** Party Software they are using.
+** 
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** 
+****************************************************************************/
+
+#include "qtlockedfile.h"
+#include <qt_windows.h>
+#include <QtCore/QFileInfo>
+
+#define MUTEX_PREFIX "QtLockedFile mutex "
+// Maximum number of concurrent read locks. Must not be greater than MAXIMUM_WAIT_OBJECTS
+#define MAX_READERS MAXIMUM_WAIT_OBJECTS
+
+Qt::HANDLE QtLockedFile::getMutexHandle(int idx, bool doCreate)
+{
+    if (mutexname.isEmpty()) {
+        QFileInfo fi(*this);
+        mutexname = QString::fromLatin1(MUTEX_PREFIX)
+                    + fi.absoluteFilePath().toLower();
+    }
+    QString mname(mutexname);
+    if (idx >= 0)
+        mname += QString::number(idx);
+
+    Qt::HANDLE mutex;
+    if (doCreate) {
+        QT_WA( { mutex = CreateMutexW(NULL, FALSE, (TCHAR*)mname.utf16()); },
+               { mutex = CreateMutexA(NULL, FALSE, mname.toLocal8Bit().constData()); } );
+        if (!mutex) {
+            qErrnoWarning("QtLockedFile::lock(): CreateMutex failed");
+            return 0;
+        }
+    }
+    else {
+        QT_WA( { mutex = OpenMutexW(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, (TCHAR*)mname.utf16()); },
+               { mutex = OpenMutexA(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, mname.toLocal8Bit().constData()); } );
+        if (!mutex) {
+            if (GetLastError() != ERROR_FILE_NOT_FOUND)
+                qErrnoWarning("QtLockedFile::lock(): OpenMutex failed");
+            return 0;
+        }
+    }
+    return mutex;
+}
+
+bool QtLockedFile::waitMutex(Qt::HANDLE mutex, bool doBlock)
+{
+    Q_ASSERT(mutex);
+    DWORD res = WaitForSingleObject(mutex, doBlock ? INFINITE : 0);
+    switch (res) {
+    case WAIT_OBJECT_0:
+    case WAIT_ABANDONED:
+        return true;
+        break;
+    case WAIT_TIMEOUT:
+        break;
+    default:
+        qErrnoWarning("QtLockedFile::lock(): WaitForSingleObject failed");
+    }
+    return false;
+}
+
+
+
+bool QtLockedFile::lock(LockMode mode, bool block)
+{
+    if (!isOpen()) {
+        qWarning("QtLockedFile::lock(): file is not opened");
+        return false;
+    }
+
+    if (mode == NoLock)
+        return unlock();
+
+    if (mode == m_lock_mode)
+        return true;
+
+    if (m_lock_mode != NoLock)
+        unlock();
+
+    if (!wmutex && !(wmutex = getMutexHandle(-1, true)))
+        return false;
+
+    if (!waitMutex(wmutex, block))
+        return false;
+
+    if (mode == ReadLock) {
+        int idx = 0;
+        for (; idx < MAX_READERS; idx++) {
+            rmutex = getMutexHandle(idx, false);
+            if (!rmutex || waitMutex(rmutex, false))
+                break;
+            CloseHandle(rmutex);
+        }
+        bool ok = true;
+        if (idx >= MAX_READERS) {
+            qWarning("QtLockedFile::lock(): too many readers");
+            rmutex = 0;
+            ok = false;
+        }
+        else if (!rmutex) {
+            rmutex = getMutexHandle(idx, true);
+            if (!rmutex || !waitMutex(rmutex, false))
+                ok = false;
+        }
+        if (!ok && rmutex) {
+            CloseHandle(rmutex);
+            rmutex = 0;
+        }
+        ReleaseMutex(wmutex);
+        if (!ok)
+            return false;
+    }
+    else {
+        Q_ASSERT(rmutexes.isEmpty());
+        for (int i = 0; i < MAX_READERS; i++) {
+            Qt::HANDLE mutex = getMutexHandle(i, false);
+            if (mutex)
+                rmutexes.append(mutex);
+        }
+        if (rmutexes.size()) {
+            DWORD res = WaitForMultipleObjects(rmutexes.size(), rmutexes.constData(),
+                                               TRUE, block ? INFINITE : 0);
+            if (res != WAIT_OBJECT_0 && res != WAIT_ABANDONED) {
+                if (res != WAIT_TIMEOUT)
+                    qErrnoWarning("QtLockedFile::lock(): WaitForMultipleObjects failed");
+                m_lock_mode = WriteLock;  // trick unlock() to clean up - semiyucky
+                unlock();
+                return false;
+            }
+        }
+    }
+
+    m_lock_mode = mode;
+    return true;
+}
+
+bool QtLockedFile::unlock()
+{
+    if (!isOpen()) {
+        qWarning("QtLockedFile::unlock(): file is not opened");
+        return false;
+    }
+
+    if (!isLocked())
+        return true;
+
+    if (m_lock_mode == ReadLock) {
+        ReleaseMutex(rmutex);
+        CloseHandle(rmutex);
+        rmutex = 0;
+    }
+    else {
+        foreach(Qt::HANDLE mutex, rmutexes) {
+            ReleaseMutex(mutex);
+            CloseHandle(mutex);
+        }
+        rmutexes.clear();
+        ReleaseMutex(wmutex);
+    }
+
+    m_lock_mode = QtLockedFile::NoLock;
+    return true;
+}
+
+QtLockedFile::~QtLockedFile()
+{
+    if (isOpen())
+        unlock();
+    if (wmutex)
+        CloseHandle(wmutex);
+}
diff --git a/src/qtsingleapplication/qtsingleapplication.cpp b/src/qtsingleapplication/qtsingleapplication.cpp
new file mode 100644 (file)
index 0000000..2ab0bbd
--- /dev/null
@@ -0,0 +1,351 @@
+/****************************************************************************
+**
+** This file is part of a Qt Solutions component.
+** 
+** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** 
+** Contact:  Qt Software Information (qt-info@nokia.com)
+** 
+** Commercial Usage  
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Solutions Commercial License Agreement provided
+** with the Software or, alternatively, in accordance with the terms
+** contained in a written agreement between you and Nokia.
+** 
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+** 
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+** 
+** GNU General Public License Usage 
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+** 
+** Please note Third Party Software included with Qt Solutions may impose
+** additional restrictions and it is the user's responsibility to ensure
+** that they have met the licensing requirements of the GPL, LGPL, or Qt
+** Solutions Commercial license and the relevant license of the Third
+** Party Software they are using.
+** 
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** 
+****************************************************************************/
+
+
+#include "qtsingleapplication.h"
+#include "qtlocalpeer.h"
+#include <QtGui/QWidget>
+
+
+/*!
+    \class QtSingleApplication qtsingleapplication.h
+    \brief The QtSingleApplication class provides an API to detect and
+    communicate with running instances of an application.
+
+    This class allows you to create applications where only one
+    instance should be running at a time. I.e., if the user tries to
+    launch another instance, the already running instance will be
+    activated instead. Another usecase is a client-server system,
+    where the first started instance will assume the role of server,
+    and the later instances will act as clients of that server.
+
+    By default, the full path of the executable file is used to
+    determine whether two processes are instances of the same
+    application. You can also provide an explicit identifier string
+    that will be compared instead.
+
+    The application should create the QtSingleApplication object early
+    in the startup phase, and call isRunning() or sendMessage() to
+    find out if another instance of this application is already
+    running. Startup parameters (e.g. the name of the file the user
+    wanted this new instance to open) can be passed to the running
+    instance in the sendMessage() function.
+
+    If isRunning() or sendMessage() returns false, it means that no
+    other instance is running, and this instance has assumed the role
+    as the running instance. The application should continue with the
+    initialization of the application user interface before entering
+    the event loop with exec(), as normal. The messageReceived()
+    signal will be emitted when the application receives messages from
+    another instance of the same application.
+
+    If isRunning() or sendMessage() returns true, another instance is
+    already running, and the application should terminate or enter
+    client mode.
+
+    If a message is received it might be helpful to the user to raise
+    the application so that it becomes visible. To facilitate this,
+    QtSingleApplication provides the setActivationWindow() function
+    and the activateWindow() slot.
+
+    Here's an example that shows how to convert an existing
+    application to use QtSingleApplication. It is very simple and does
+    not make use of all QtSingleApplication's functionality (see the
+    examples for that).
+
+    \code
+    // Original
+    int main(int argc, char **argv)
+    {
+        QApplication app(argc, argv);
+
+        MyMainWidget mmw;
+
+        mmw.show();
+        return app.exec();
+    }
+
+    // Single instance
+    int main(int argc, char **argv)
+    {
+        QtSingleApplication app(argc, argv);
+
+        if (app.isRunning())
+            return 0;
+
+        MyMainWidget mmw;
+
+        app.setActivationWindow(&mmw);
+
+        mmw.show();
+        return app.exec();
+    }
+    \endcode
+
+    Once this QtSingleApplication instance is destroyed(for example,
+    when the user quits), when the user next attempts to run the
+    application this instance will not, of course, be encountered. The
+    next instance to call isRunning() or sendMessage() will assume the
+    role as the new running instance.
+
+    For console (non-GUI) applications, QtSingleCoreApplication may be
+    used instead of this class, to avoid the dependency on the QtGui
+    library.
+
+    \sa QtSingleCoreApplication
+*/
+
+
+void QtSingleApplication::sysInit(const QString &appId)
+{
+    actWin = 0;
+    peer = new QtLocalPeer(this, appId);
+    connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&)));
+}
+
+
+/*!
+    Creates a QtSingleApplication object. The application identifier
+    will be QCoreApplication::applicationFilePath(). \a argc, \a
+    argv, and \a GUIenabled are passed on to the QAppliation constructor.
+
+    If you are creating a console application (i.e. setting \a
+    GUIenabled to false), you may consider using
+    QtSingleCoreApplication instead.
+*/
+
+QtSingleApplication::QtSingleApplication(int &argc, char **argv, bool GUIenabled)
+    : QApplication(argc, argv, GUIenabled)
+{
+    sysInit();
+}
+
+
+/*!
+    Creates a QtSingleApplication object with the application
+    identifier \a appId. \a argc and \a argv are passed on to the
+    QAppliation constructor.
+*/
+
+QtSingleApplication::QtSingleApplication(const QString &appId, int &argc, char **argv)
+    : QApplication(argc, argv)
+{
+    sysInit(appId);
+}
+
+
+/*!
+    Creates a QtSingleApplication object. The application identifier
+    will be QCoreApplication::applicationFilePath(). \a argc, \a
+    argv, and \a type are passed on to the QAppliation constructor.
+*/
+QtSingleApplication::QtSingleApplication(int &argc, char **argv, Type type)
+    : QApplication(argc, argv, type)
+{
+    sysInit();
+}
+
+
+#if defined(Q_WS_X11)
+/*!
+  Special constructor for X11, ref. the documentation of
+  QApplication's corresponding constructor. The application identifier
+  will be QCoreApplication::applicationFilePath(). \a dpy, \a visual,
+  and \a cmap are passed on to the QApplication constructor.
+*/
+QtSingleApplication::QtSingleApplication(Display* dpy, Qt::HANDLE visual, Qt::HANDLE cmap)
+    : QApplication(dpy, visual, cmap)
+{
+    sysInit();
+}
+
+/*!
+  Special constructor for X11, ref. the documentation of
+  QApplication's corresponding constructor. The application identifier
+  will be QCoreApplication::applicationFilePath(). \a dpy, \a argc, \a
+  argv, \a visual, and \a cmap are passed on to the QApplication
+  constructor.
+*/
+QtSingleApplication::QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual, Qt::HANDLE cmap)
+    : QApplication(dpy, argc, argv, visual, cmap)
+{
+    sysInit();
+}
+
+/*!
+  Special constructor for X11, ref. the documentation of
+  QApplication's corresponding constructor. The application identifier
+  will be \a appId. \a dpy, \a argc, \a
+  argv, \a visual, and \a cmap are passed on to the QApplication
+  constructor.
+*/
+QtSingleApplication::QtSingleApplication(Display* dpy, const QString &appId, int argc, char **argv, Qt::HANDLE visual, Qt::HANDLE cmap)
+    : QApplication(dpy, argc, argv, visual, cmap)
+{
+    sysInit(appId);
+}
+#endif
+
+
+/*!
+    Returns true if another instance of this application is running;
+    otherwise false.
+
+    This function does not find instances of this application that are
+    being run by a different user (on Windows: that are running in
+    another session).
+
+    \sa sendMessage()
+*/
+
+bool QtSingleApplication::isRunning()
+{
+    return peer->isClient();
+}
+
+
+/*!
+    Tries to send the text \a message to the currently running
+    instance. The QtSingleApplication object in the running instance
+    will emit the messageReceived() signal when it receives the
+    message.
+
+    This function returns true if the message has been sent to, and
+    processed by, the current instance. If there is no instance
+    currently running, or if the running instance fails to process the
+    message within \a timeout milliseconds, this function return false.
+
+    \sa isRunning(), messageReceived()
+*/
+bool QtSingleApplication::sendMessage(const QString &message, int timeout)
+{
+    return peer->sendMessage(message, timeout);
+}
+
+
+/*!
+    Returns the application identifier. Two processes with the same
+    identifier will be regarded as instances of the same application.
+*/
+QString QtSingleApplication::id() const
+{
+    return peer->applicationId();
+}
+
+
+/*!
+  Sets the activation window of this application to \a aw. The
+  activation window is the widget that will be activated by
+  activateWindow(). This is typically the application's main window.
+
+  If \a activateOnMessage is true (the default), the window will be
+  activated automatically every time a message is received, just prior
+  to the messageReceived() signal being emitted.
+
+  \sa activateWindow(), messageReceived()
+*/
+
+void QtSingleApplication::setActivationWindow(QWidget* aw, bool activateOnMessage)
+{
+    actWin = aw;
+    if (activateOnMessage)
+        connect(peer, SIGNAL(messageReceived(const QString&)), this, SLOT(activateWindow()));
+    else
+        disconnect(peer, SIGNAL(messageReceived(const QString&)), this, SLOT(activateWindow()));
+}
+
+
+/*!
+    Returns the applications activation window if one has been set by
+    calling setActivationWindow(), otherwise returns 0.
+
+    \sa setActivationWindow()
+*/
+QWidget* QtSingleApplication::activationWindow() const
+{
+    return actWin;
+}
+
+
+/*!
+  De-minimizes, raises, and activates this application's activation window.
+  This function does nothing if no activation window has been set.
+
+  This is a convenience function to show the user that this
+  application instance has been activated when he has tried to start
+  another instance.
+
+  This function should typically be called in response to the
+  messageReceived() signal. By default, that will happen
+  automatically, if an activation window has been set.
+
+  \sa setActivationWindow(), messageReceived(), initialize()
+*/
+void QtSingleApplication::activateWindow()
+{
+    if (actWin) {
+        actWin->setWindowState(actWin->windowState() & ~Qt::WindowMinimized);
+        actWin->raise();
+        actWin->activateWindow();
+    }
+}
+
+
+/*!
+    \fn void QtSingleApplication::messageReceived(const QString& message)
+
+    This signal is emitted when the current instance receives a \a
+    message from another instance of this application.
+
+    \sa sendMessage(), setActivationWindow(), activateWindow()
+*/
+
+
+/*!
+    \fn void QtSingleApplication::initialize(bool dummy = true)
+
+    \obsolete
+*/
diff --git a/src/qtsingleapplication/qtsingleapplication.h b/src/qtsingleapplication/qtsingleapplication.h
new file mode 100644 (file)
index 0000000..b73f5d6
--- /dev/null
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** This file is part of a Qt Solutions component.
+** 
+** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** 
+** Contact:  Qt Software Information (qt-info@nokia.com)
+** 
+** Commercial Usage  
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Solutions Commercial License Agreement provided
+** with the Software or, alternatively, in accordance with the terms
+** contained in a written agreement between you and Nokia.
+** 
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+** 
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+** 
+** GNU General Public License Usage 
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+** 
+** Please note Third Party Software included with Qt Solutions may impose
+** additional restrictions and it is the user's responsibility to ensure
+** that they have met the licensing requirements of the GPL, LGPL, or Qt
+** Solutions Commercial license and the relevant license of the Third
+** Party Software they are using.
+** 
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** 
+****************************************************************************/
+
+
+#include <QtGui/QApplication>
+
+class QtLocalPeer;
+
+#if defined(Q_WS_WIN)
+#  if !defined(QT_QTSINGLEAPPLICATION_EXPORT) && !defined(QT_QTSINGLEAPPLICATION_IMPORT)
+#    define QT_QTSINGLEAPPLICATION_EXPORT
+#  elif defined(QT_QTSINGLEAPPLICATION_IMPORT)
+#    if defined(QT_QTSINGLEAPPLICATION_EXPORT)
+#      undef QT_QTSINGLEAPPLICATION_EXPORT
+#    endif
+#    define QT_QTSINGLEAPPLICATION_EXPORT __declspec(dllimport)
+#  elif defined(QT_QTSINGLEAPPLICATION_EXPORT)
+#    undef QT_QTSINGLEAPPLICATION_EXPORT
+#    define QT_QTSINGLEAPPLICATION_EXPORT __declspec(dllexport)
+#  endif
+#else
+#  define QT_QTSINGLEAPPLICATION_EXPORT
+#endif
+
+class QT_QTSINGLEAPPLICATION_EXPORT QtSingleApplication : public QApplication
+{
+    Q_OBJECT
+
+public:
+    QtSingleApplication(int &argc, char **argv, bool GUIenabled = true);
+    QtSingleApplication(const QString &id, int &argc, char **argv);
+    QtSingleApplication(int &argc, char **argv, Type type);
+#if defined(Q_WS_X11)
+    QtSingleApplication(Display* dpy, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0);
+    QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE cmap= 0);
+    QtSingleApplication(Display* dpy, const QString &appId, int argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0);
+#endif
+
+    bool isRunning();
+    QString id() const;
+
+    void setActivationWindow(QWidget* aw, bool activateOnMessage = true);
+    QWidget* activationWindow() const;
+
+    // Obsolete:
+    void initialize(bool dummy = true)
+        { isRunning(); Q_UNUSED(dummy) }
+
+public Q_SLOTS:
+    bool sendMessage(const QString &message, int timeout = 5000);
+    void activateWindow();
+
+
+Q_SIGNALS:
+    void messageReceived(const QString &message);
+
+
+private:
+    void sysInit(const QString &appId = QString());
+    QtLocalPeer *peer;
+    QWidget *actWin;
+};
diff --git a/src/qtsingleapplication/qtsingleapplication.pri b/src/qtsingleapplication/qtsingleapplication.pri
new file mode 100644 (file)
index 0000000..02de47e
--- /dev/null
@@ -0,0 +1,15 @@
+INCLUDEPATH += $$PWD
+DEPENDPATH += $$PWD
+QT *= network
+
+qtsingleapplication-uselib:!qtsingleapplication-buildlib {
+    LIBS += -L$$QTSINGLEAPPLICATION_LIBDIR -l$$QTSINGLEAPPLICATION_LIBNAME
+} else {
+    SOURCES += $$PWD/qtsingleapplication.cpp $$PWD/qtlocalpeer.cpp
+    HEADERS += $$PWD/qtsingleapplication.h $$PWD/qtlocalpeer.h
+}
+
+win32 {
+    contains(TEMPLATE, lib):contains(CONFIG, shared):DEFINES += QT_QTSINGLEAPPLICATION_EXPORT
+    else:qtsingleapplication-uselib:DEFINES += QT_QTSINGLEAPPLICATION_IMPORT
+}
diff --git a/src/qtsingleapplication/qtsinglecoreapplication.cpp b/src/qtsingleapplication/qtsinglecoreapplication.cpp
new file mode 100644 (file)
index 0000000..9b7fc40
--- /dev/null
@@ -0,0 +1,155 @@
+/****************************************************************************
+**
+** This file is part of a Qt Solutions component.
+** 
+** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** 
+** Contact:  Qt Software Information (qt-info@nokia.com)
+** 
+** Commercial Usage  
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Solutions Commercial License Agreement provided
+** with the Software or, alternatively, in accordance with the terms
+** contained in a written agreement between you and Nokia.
+** 
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+** 
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+** 
+** GNU General Public License Usage 
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+** 
+** Please note Third Party Software included with Qt Solutions may impose
+** additional restrictions and it is the user's responsibility to ensure
+** that they have met the licensing requirements of the GPL, LGPL, or Qt
+** Solutions Commercial license and the relevant license of the Third
+** Party Software they are using.
+** 
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** 
+****************************************************************************/
+
+
+#include "qtsinglecoreapplication.h"
+#include "qtlocalpeer.h"
+
+/*!
+    \class QtSingleCoreApplication qtsinglecoreapplication.h
+    \brief A variant of the QtSingleApplication class for non-GUI applications.
+
+    This class is a variant of QtSingleApplication suited for use in
+    console (non-GUI) applications. It is an extension of
+    QCoreApplication (instead of QApplication). It does not require
+    the QtGui library.
+
+    The API and usage is identical to QtSingleApplication, except that
+    functions relating to the "activation window" are not present, for
+    obvious reasons. Please refer to the QtSingleApplication
+    documentation for explanation of the usage.
+
+    A QtSingleCoreApplication instance can communicate to a
+    QtSingleApplication instance if they share the same application
+    id. Hence, this class can be used to create a light-weight
+    command-line tool that sends commands to a GUI application.
+
+    \sa QtSingleApplication
+*/
+
+/*!
+    Creates a QtSingleCoreApplication object. The application identifier
+    will be QCoreApplication::applicationFilePath(). \a argc and \a
+    argv are passed on to the QCoreAppliation constructor.
+*/
+
+QtSingleCoreApplication::QtSingleCoreApplication(int &argc, char **argv)
+    : QCoreApplication(argc, argv)
+{
+    peer = new QtLocalPeer(this);
+    connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&)));
+}
+
+
+/*!
+    Creates a QtSingleCoreApplication object with the application
+    identifier \a appId. \a argc and \a argv are passed on to the
+    QCoreAppliation constructor.
+*/
+QtSingleCoreApplication::QtSingleCoreApplication(const QString &appId, int &argc, char **argv)
+    : QCoreApplication(argc, argv)
+{
+    peer = new QtLocalPeer(this, appId);
+    connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&)));
+}
+
+
+/*!
+    Returns true if another instance of this application is running;
+    otherwise false.
+
+    This function does not find instances of this application that are
+    being run by a different user (on Windows: that are running in
+    another session).
+
+    \sa sendMessage()
+*/
+
+bool QtSingleCoreApplication::isRunning()
+{
+    return peer->isClient();
+}
+
+
+/*!
+    Tries to send the text \a message to the currently running
+    instance. The QtSingleCoreApplication object in the running instance
+    will emit the messageReceived() signal when it receives the
+    message.
+
+    This function returns true if the message has been sent to, and
+    processed by, the current instance. If there is no instance
+    currently running, or if the running instance fails to process the
+    message within \a timeout milliseconds, this function return false.
+
+    \sa isRunning(), messageReceived()
+*/
+
+bool QtSingleCoreApplication::sendMessage(const QString &message, int timeout)
+{
+    return peer->sendMessage(message, timeout);
+}
+
+
+/*!
+    Returns the application identifier. Two processes with the same
+    identifier will be regarded as instances of the same application.
+*/
+
+QString QtSingleCoreApplication::id() const
+{
+    return peer->applicationId();
+}
+
+
+/*!
+    \fn void QtSingleCoreApplication::messageReceived(const QString& message)
+
+    This signal is emitted when the current instance receives a \a
+    message from another instance of this application.
+
+    \sa sendMessage()
+*/
diff --git a/src/qtsingleapplication/qtsinglecoreapplication.h b/src/qtsingleapplication/qtsinglecoreapplication.h
new file mode 100644 (file)
index 0000000..f4738d5
--- /dev/null
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** This file is part of a Qt Solutions component.
+** 
+** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** 
+** Contact:  Qt Software Information (qt-info@nokia.com)
+** 
+** Commercial Usage  
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Solutions Commercial License Agreement provided
+** with the Software or, alternatively, in accordance with the terms
+** contained in a written agreement between you and Nokia.
+** 
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+** 
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+** 
+** GNU General Public License Usage 
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+** 
+** Please note Third Party Software included with Qt Solutions may impose
+** additional restrictions and it is the user's responsibility to ensure
+** that they have met the licensing requirements of the GPL, LGPL, or Qt
+** Solutions Commercial license and the relevant license of the Third
+** Party Software they are using.
+** 
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** 
+****************************************************************************/
+
+
+#include <QtCore/QCoreApplication>
+
+class QtLocalPeer;
+
+class QtSingleCoreApplication : public QCoreApplication
+{
+    Q_OBJECT
+
+public:
+    QtSingleCoreApplication(int &argc, char **argv);
+    QtSingleCoreApplication(const QString &id, int &argc, char **argv);
+
+    bool isRunning();
+    QString id() const;
+
+public Q_SLOTS:
+    bool sendMessage(const QString &message, int timeout = 5000);
+
+
+Q_SIGNALS:
+    void messageReceived(const QString &message);
+
+
+private:
+    QtLocalPeer* peer;
+};
diff --git a/src/qtsingleapplication/qtsinglecoreapplication.pri b/src/qtsingleapplication/qtsinglecoreapplication.pri
new file mode 100644 (file)
index 0000000..d2d6cc3
--- /dev/null
@@ -0,0 +1,10 @@
+INCLUDEPATH    += $$PWD
+DEPENDPATH      += $$PWD
+HEADERS                += $$PWD/qtsinglecoreapplication.h $$PWD/qtlocalpeer.h
+SOURCES                += $$PWD/qtsinglecoreapplication.cpp $$PWD/qtlocalpeer.cpp
+
+QT *= network
+
+win32:contains(TEMPLATE, lib):contains(CONFIG, shared) {
+    DEFINES += QT_QTSINGLECOREAPPLICATION_EXPORT=__declspec(dllexport)
+}
diff --git a/src/searchlineedit.cpp b/src/searchlineedit.cpp
new file mode 100644 (file)
index 0000000..42c2aee
--- /dev/null
@@ -0,0 +1,268 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://www.qtsoftware.com/contact.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "searchlineedit.h"
+
+#include <QtGui/QPainter>
+#include <QtGui/QMouseEvent>
+#include <QtGui/QMenu>
+#include <QtGui/QStyle>
+#include <QtGui/QStyleOptionFrameV2>
+
+#include "googlesuggest.h"
+
+ClearButton::ClearButton(QWidget *parent)
+    : QAbstractButton(parent)
+{
+    setCursor(Qt::ArrowCursor);
+    setToolTip(tr("Clear"));
+    setVisible(false);
+    setFocusPolicy(Qt::NoFocus);
+}
+
+void ClearButton::paintEvent(QPaintEvent *event)
+{
+    Q_UNUSED(event);
+    QPainter painter(this);
+    int height = this->height();
+
+    painter.setRenderHint(QPainter::Antialiasing, true);
+    QColor color = palette().color(QPalette::Mid);
+    painter.setBrush(isDown()
+                     ? palette().color(QPalette::Dark)
+                         : palette().color(QPalette::Mid));
+    painter.setPen(painter.brush().color());
+    int size = width();
+    int offset = size / 3.5;
+    int radius = size - offset * 2;
+    painter.drawEllipse(offset, offset, radius, radius);
+
+    painter.setPen(QPen(palette().color(QPalette::Base),2));
+    int border = offset * 1.6;
+    painter.drawLine(border, border, width() - border, height - border);
+    painter.drawLine(border, height - border, width() - border, border);
+}
+
+void ClearButton::textChanged(const QString &text)
+{
+    setVisible(!text.isEmpty());
+}
+
+/*
+    Search icon on the left hand side of the search widget
+    When a menu is set a down arrow appears
+ */
+class SearchButton : public QAbstractButton {
+public:
+    SearchButton(QWidget *parent = 0);
+    void paintEvent(QPaintEvent *event);
+    QMenu *m_menu;
+
+protected:
+    void mousePressEvent(QMouseEvent *event);
+};
+
+SearchButton::SearchButton(QWidget *parent)
+    : QAbstractButton(parent),
+    m_menu(0)
+{
+    setObjectName(QLatin1String("SearchButton"));
+    setCursor(Qt::ArrowCursor);
+    setFocusPolicy(Qt::NoFocus);
+}
+
+void SearchButton::mousePressEvent(QMouseEvent *event)
+{
+    if (m_menu && event->button() == Qt::LeftButton) {
+        QWidget *p = parentWidget();
+        if (p) {
+            QPoint r = p->mapToGlobal(QPoint(0, p->height()));
+            m_menu->exec(QPoint(r.x() + height() / 2, r.y()));
+        }
+        event->accept();
+    }
+    QAbstractButton::mousePressEvent(event);
+}
+
+void SearchButton::paintEvent(QPaintEvent *event)
+{
+    Q_UNUSED(event);
+    QPainterPath myPath;
+
+    int radius = (height() / 5) * 2;
+    QRect circle(height() / 5.5, height() / 3.5, radius, radius);
+    myPath.addEllipse(circle);
+
+    myPath.arcMoveTo(circle, 315);
+    QPointF c = myPath.currentPosition();
+    int diff = height() / 6;
+    myPath.lineTo(qMin(width() - 2, (int)c.x() + diff), c.y() + diff);
+
+    QPainter painter(this);
+    painter.setRenderHint(QPainter::Antialiasing, true);
+    painter.setPen(QPen(palette().color(QPalette::Mid), height() / 10));
+    painter.drawPath(myPath);
+
+    if (m_menu) {
+        QPainterPath dropPath;
+        dropPath.arcMoveTo(circle, 320);
+        QPointF c = dropPath.currentPosition();
+        c = QPointF(c.x() + 3.5, c.y() + 0.5);
+        dropPath.moveTo(c);
+        dropPath.lineTo(c.x() + 4, c.y());
+        dropPath.lineTo(c.x() + 2, c.y() + 2);
+        dropPath.closeSubpath();
+        painter.setPen(Qt::darkGray);
+        painter.setBrush(Qt::darkGray);
+        painter.setRenderHint(QPainter::Antialiasing, false);
+        painter.drawPath(dropPath);
+    }
+    painter.end();
+}
+
+/*
+    SearchLineEdit is an enhanced QLineEdit
+    - A Search icon on the left with optional menu
+    - When there is no text and doesn't have focus an "inactive text" is displayed
+    - When there is text a clear button is displayed on the right hand side
+ */
+SearchLineEdit::SearchLineEdit(QWidget *parent) : ExLineEdit(parent),
+m_searchButton(new SearchButton(this))
+{
+    connect(lineEdit(), SIGNAL(textChanged(const QString &)),
+            this, SIGNAL(textChanged(const QString &)));
+
+    connect(lineEdit(), SIGNAL(returnPressed()),
+            this, SLOT(returnPressed()));
+
+    setLeftWidget(m_searchButton);
+    m_inactiveText = tr("Search");
+
+    QSizePolicy policy = sizePolicy();
+    setSizePolicy(QSizePolicy::Preferred, policy.verticalPolicy());
+
+    // completion
+    completion = new GSuggestCompletion(this, m_lineEdit);
+
+}
+
+void SearchLineEdit::paintEvent(QPaintEvent *event)
+{
+    if (lineEdit()->text().isEmpty() && !hasFocus() && !m_inactiveText.isEmpty()) {
+        ExLineEdit::paintEvent(event);
+        QStyleOptionFrameV2 panel;
+        initStyleOption(&panel);
+        QRect r = style()->subElementRect(QStyle::SE_LineEditContents, &panel, this);
+        QFontMetrics fm = fontMetrics();
+        int horizontalMargin = lineEdit()->x();
+        QRect lineRect(horizontalMargin + r.x(), r.y() + (r.height() - fm.height() + 1) / 2,
+                       r.width() - 2 * horizontalMargin, fm.height());
+        QPainter painter(this);
+        painter.setPen(palette().brush(QPalette::Disabled, QPalette::Text).color());
+        painter.drawText(lineRect, Qt::AlignLeft|Qt::AlignVCenter, m_inactiveText);
+    } else {
+        ExLineEdit::paintEvent(event);
+    }
+}
+
+void SearchLineEdit::resizeEvent(QResizeEvent *event)
+{
+    updateGeometries();
+    ExLineEdit::resizeEvent(event);
+}
+
+void SearchLineEdit::updateGeometries()
+{
+    int menuHeight = height();
+    int menuWidth = menuHeight + 1;
+    if (!m_searchButton->m_menu)
+        menuWidth = (menuHeight / 5) * 4;
+    m_searchButton->resize(QSize(menuWidth, menuHeight));
+}
+
+QString SearchLineEdit::inactiveText() const
+{
+    return m_inactiveText;
+}
+
+void SearchLineEdit::setInactiveText(const QString &text)
+{
+    m_inactiveText = text;
+}
+
+void SearchLineEdit::setMenu(QMenu *menu)
+{
+    if (m_searchButton->m_menu)
+        m_searchButton->m_menu->deleteLater();
+    m_searchButton->m_menu = menu;
+    updateGeometries();
+}
+
+QMenu *SearchLineEdit::menu() const
+{
+    if (!m_searchButton->m_menu) {
+        m_searchButton->m_menu = new QMenu(m_searchButton);
+        if (isVisible())
+            (const_cast<SearchLineEdit*>(this))->updateGeometries();
+    }
+    return m_searchButton->m_menu;
+}
+
+void SearchLineEdit::returnPressed()
+{
+    if (!lineEdit()->text().isEmpty()) {
+        completion->preventSuggest();
+        emit search(lineEdit()->text());
+    }
+}
+
+void SearchLineEdit::enableSuggest() {
+    completion->enableSuggest();
+}
+
+void SearchLineEdit::preventSuggest() {
+    completion->preventSuggest();
+}
+
+void SearchLineEdit::focusInEvent(QFocusEvent *event) {
+    ExLineEdit::focusInEvent(event);
+    enableSuggest();
+}
diff --git a/src/searchlineedit.h b/src/searchlineedit.h
new file mode 100644 (file)
index 0000000..a1d71c4
--- /dev/null
@@ -0,0 +1,114 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://www.qtsoftware.com/contact.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef SEARCHLINEEDIT_H
+#define SEARCHLINEEDIT_H
+
+#include "urllineedit.h"
+
+#include <QtGui/QLineEdit>
+#include <QtGui/QAbstractButton>
+
+QT_BEGIN_NAMESPACE
+class QMenu;
+QT_END_NAMESPACE
+
+class SearchButton;
+class GSuggestCompletion;
+
+/*
+    Clear button on the right hand side of the search widget.
+    Hidden by default
+    "A circle with an X in it"
+ */
+class ClearButton : public QAbstractButton
+{
+    Q_OBJECT
+
+public:
+    ClearButton(QWidget *parent = 0);
+    void paintEvent(QPaintEvent *event);
+
+public slots:
+    void textChanged(const QString &text);
+};
+
+
+class SearchLineEdit : public ExLineEdit
+{
+    Q_OBJECT
+    Q_PROPERTY(QString inactiveText READ inactiveText WRITE setInactiveText)
+
+signals:
+    void textChanged(const QString &text);
+    void search(const QString &text);
+
+public:
+    SearchLineEdit(QWidget *parent = 0);
+
+    QString inactiveText() const;
+    void setInactiveText(const QString &text);
+
+    QMenu *menu() const;
+    void setMenu(QMenu *menu);
+    void updateGeometries();
+    void enableSuggest();
+    void preventSuggest();
+    void selectAll() { lineEdit()->selectAll(); };
+
+protected:
+    void resizeEvent(QResizeEvent *event);
+    void paintEvent(QPaintEvent *event);
+    void focusInEvent(QFocusEvent *event);
+
+private slots:
+    void returnPressed();
+
+private:
+
+    SearchButton *m_searchButton;
+    QString m_inactiveText;
+
+    GSuggestCompletion *completion;
+};
+
+#endif // SEARCHLINEEDIT_H
+
diff --git a/src/searchparams.cpp b/src/searchparams.cpp
new file mode 100644 (file)
index 0000000..896a0f1
--- /dev/null
@@ -0,0 +1,5 @@
+#include "searchparams.h"
+
+SearchParams::SearchParams() {
+    m_sortBy = SortByRelevance;
+}
diff --git a/src/searchparams.h b/src/searchparams.h
new file mode 100644 (file)
index 0000000..e8525c9
--- /dev/null
@@ -0,0 +1,31 @@
+#ifndef SEARCHPARAMS_H
+#define SEARCHPARAMS_H
+
+#include <QObject>
+
+
+
+class SearchParams : public QObject {
+
+public:
+    SearchParams();
+
+    const QString keywords() const { return m_keywords; }
+    void setKeywords( QString keywords ) { m_keywords = keywords; }
+
+    int sortBy() const { return m_sortBy; }
+    void setSortBy( int sortBy ) { m_sortBy = sortBy; }
+
+    enum SortBy {
+        SortByRelevance = 1,
+        SortByNewest,
+        SortByViewCount
+    };
+
+private:
+    QString m_keywords;
+    int m_sortBy;
+
+};
+
+#endif // SEARCHPARAMS_H
diff --git a/src/spacer.cpp b/src/spacer.cpp
new file mode 100644 (file)
index 0000000..d71367a
--- /dev/null
@@ -0,0 +1,7 @@
+#include "spacer.h"
+
+Spacer::Spacer(QWidget *parent, QWidget *child) : QWidget(parent) {
+    QBoxLayout *layout = new QHBoxLayout();
+    layout->addWidget(child);
+    setLayout(layout);
+}
diff --git a/src/spacer.h b/src/spacer.h
new file mode 100644 (file)
index 0000000..668001f
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef SPACER_H
+#define SPACER_H
+
+#include <QtGui>
+
+class Spacer : public QWidget
+{
+public:
+    Spacer(QWidget *parent, QWidget *child);
+};
+
+#endif // SPACER_H
diff --git a/src/thlibrary/thblackbar.cpp b/src/thlibrary/thblackbar.cpp
new file mode 100644 (file)
index 0000000..0043fc5
--- /dev/null
@@ -0,0 +1,259 @@
+#include <QPainter>
+#include <QPaintEvent>
+#include <QList>
+#include <QtGui>
+
+#include "thblackbar.h"
+
+/* ============================================================================
+ *  PRIVATE Class
+ */
+class THBlackBar::Private {
+        public:
+    QList<QAction *> actionList;
+    QAction *checkedAction;
+    QAction *hoveredAction;
+};
+
+/* ============================================================================
+ *  PUBLIC Constructor/Destructors
+ */
+THBlackBar::THBlackBar (QWidget *parent)
+        : QWidget(parent), d(new THBlackBar::Private)
+{
+    // Setup Widget Options
+    setMouseTracking(true);
+
+    // Setup Members
+    d->hoveredAction = NULL;
+    d->checkedAction = NULL;
+}
+
+THBlackBar::~THBlackBar() {
+    delete d;
+}
+
+/* ============================================================================
+ *  PUBLIC Methods
+ */
+QAction *THBlackBar::addAction (QAction *action) {
+    action->setCheckable(true);
+    d->actionList.append(action);
+    return(action);
+}
+
+QAction *THBlackBar::addAction (const QString& text) {
+    QAction *action = new QAction(text, this);
+    action->setCheckable(true);
+    d->actionList.append(action);
+    return(action);
+}
+
+void THBlackBar::setCheckedAction(int index) {
+    if (d->checkedAction)
+        d->checkedAction->setChecked(false);
+    d->checkedAction = d->actionList.at(index);
+    d->checkedAction->setChecked(true);
+    update();
+}
+
+QSize THBlackBar::minimumSizeHint (void) const {       
+    int itemsWidth = calculateButtonWidth() * d->actionList.size();
+    return(QSize(100 + itemsWidth, 32));
+}
+
+/* ============================================================================
+ *  PROTECTED Methods
+ */
+void THBlackBar::paintEvent (QPaintEvent *event) {
+    int height = event->rect().height();
+    int width = event->rect().width();
+    // int mh = (height / 2);
+
+    // THPainter p(this);
+    QPainter p(this);
+
+    /*
+    // Draw Background
+    QLinearGradient linearGradUp(QPointF(0, 0), QPointF(0, mh));
+    linearGradUp.setColorAt(0, QColor(0x97, 0x97, 0x97));
+    linearGradUp.setColorAt(1, QColor(0x4d, 0x4d, 0x4d));
+    p.fillRect(0, 0, width, mh, QBrush(linearGradUp));
+
+    QLinearGradient linearGradDw(QPointF(0, mh), QPointF(0, height));
+    linearGradDw.setColorAt(0, QColor(0x3a, 0x3a, 0x3a));
+    linearGradDw.setColorAt(1, QColor(0x42, 0x42, 0x42));
+    p.fillRect(0, mh, width, mh, QBrush(linearGradDw));
+    */
+    
+    // Calculate Buttons Size & Location
+    int buttonWidth = width / d->actionList.size(); // calculateButtonWidth();
+    // int buttonsWidth = width; // buttonWidth * d->actionList.size();
+    int buttonsX = 0; // (width / 2) - (buttonsWidth / 2);
+
+    // Draw Buttons
+    // p.translate(0, 4);
+    QRect rect(buttonsX, 0, buttonWidth, height);
+    foreach (QAction *action, d->actionList) {
+        drawButton(&p, rect, action);
+        rect.moveLeft(rect.x() + rect.width());
+    }
+    // p.translate(0, -4);
+
+    // Draw Buttons Shadow
+    // p.fillRect(buttonsX, height - 4, buttonsWidth, 1, QColor(0x6d, 0x6d, 0x6d));
+
+    p.end();
+}
+
+void THBlackBar::mouseMoveEvent (QMouseEvent *event) {
+    QWidget::mouseMoveEvent(event);
+
+    QAction *action = hoveredAction(event->pos());
+
+    if (action == NULL && d->hoveredAction != NULL) {
+        // d->hoveredAction->hover(false);
+        d->hoveredAction = NULL;
+        update();
+    } else if (action != NULL) {
+        d->hoveredAction = action;
+        action->hover();
+        update();
+
+        // status tip
+        QMainWindow* mainWindow = dynamic_cast<QMainWindow*>(window());
+        if (mainWindow) mainWindow->statusBar()->showMessage(action->statusTip());
+    }
+}
+
+void THBlackBar::mousePressEvent (QMouseEvent *event) {
+    QWidget::mousePressEvent(event);
+
+    if (d->hoveredAction != NULL) {
+
+        if (d->checkedAction != NULL) {
+            // already checked
+            if (d->checkedAction == d->hoveredAction) return;
+            d->checkedAction->setChecked(false);
+        }
+
+        d->checkedAction = d->hoveredAction;
+        d->hoveredAction->setChecked(true);
+        d->hoveredAction->trigger();
+
+        update();
+    }
+}
+
+void THBlackBar::leaveEvent(QEvent *event) {
+    // status tip
+    QMainWindow* mainWindow = static_cast<QMainWindow*>(window());
+    if (mainWindow) mainWindow->statusBar()->clearMessage();
+}
+
+QAction *THBlackBar::hoveredAction (const QPoint& pos) const {
+    if (pos.y() <= 0 || pos.y() >= height())
+        return(NULL);
+
+    /*
+    int buttonWidth = calculateButtonWidth();
+    int buttonsWidth = buttonWidth * d->actionList.size();
+    int buttonsX = (width() / 2) - (buttonsWidth / 2);
+    */
+    
+    int buttonWidth = width() / d->actionList.size(); // calculateButtonWidth();
+    int buttonsWidth = width(); // buttonWidth * d->actionList.size();
+    int buttonsX = 0; // (width / 2) - (buttonsWidth / 2);
+    
+    if (pos.x() <= buttonsX || pos.x() >= (buttonsX + buttonsWidth))
+        return(NULL);
+
+    int buttonIndex = (pos.x() - buttonsX) / buttonWidth;
+
+    if (buttonIndex >= d->actionList.size())
+        return(NULL);
+    return(d->actionList[buttonIndex]);
+}
+
+int THBlackBar::calculateButtonWidth (void) const {
+    QFont smallerBoldFont;
+    smallerBoldFont.setBold(true);
+    smallerBoldFont.setPointSize(smallerBoldFont.pointSize()*.85);
+    QFontMetrics fontMetrics(smallerBoldFont);
+    int tmpItemWidth, itemWidth = 0;
+    foreach (QAction *action, d->actionList) {
+        tmpItemWidth = fontMetrics.width(action->text());
+        if (itemWidth < tmpItemWidth) itemWidth = tmpItemWidth;
+    }
+    return itemWidth;
+}
+
+
+/* ============================================================================
+ *  PRIVATE Methods
+ */
+void THBlackBar::drawUnselectedButton (        QPainter *painter,
+                                        const QRect& rect,
+                                        const QAction *action)
+{
+    QLinearGradient linearGrad(QPointF(0, 0), QPointF(0, rect.height() / 2));    
+    linearGrad.setColorAt(0, QColor(0x8e, 0x8e, 0x8e));
+    linearGrad.setColorAt(1, QColor(0x5c, 0x5c, 0x5c));
+    /*
+    QPalette palette;
+    linearGrad.setColorAt(0, palette.color(QPalette::Dark));
+    linearGrad.setColorAt(1, palette.color(QPalette::Midlight));
+*/
+    drawButton(painter, rect, linearGrad, QColor(0x41, 0x41, 0x41), action);
+    // drawButton(painter, rect, linearGrad, palette.color(QPalette::Shadow), action);
+}
+
+void THBlackBar::drawSelectedButton (  QPainter *painter,
+                                        const QRect& rect,
+                                        const QAction *action)
+{
+    QLinearGradient linearGrad(QPointF(0, 0), QPointF(0, rect.height() / 2));
+    linearGrad.setColorAt(0, QColor(0x6d, 0x6d, 0x6d));
+    linearGrad.setColorAt(1, QColor(0x25, 0x25, 0x25));
+    drawButton(painter, rect, linearGrad, QColor(0x00, 0x00, 0x00), action);
+}
+
+void THBlackBar::drawButton (  QPainter *painter,
+                                const QRect& rect,
+                                const QAction *action)
+{
+    if (action->isChecked())
+        drawSelectedButton(painter, rect, action);
+    else
+        drawUnselectedButton(painter, rect, action);
+}
+
+void THBlackBar::drawButton (  QPainter *painter, 
+                                const QRect& rect,
+                                const QLinearGradient& gradient,
+                                const QColor& color,
+                                const QAction *action)
+{
+    painter->save();
+
+    int height = rect.height();
+    int width = rect.width();
+    int mh = (height / 2);
+
+    painter->translate(rect.x(), rect.y());
+    painter->setPen(QColor(0x28, 0x28, 0x28));
+
+    painter->fillRect(0, 0, width, mh, QBrush(gradient));
+    painter->fillRect(0, mh, width, mh, color);
+    painter->drawRect(0, 0, width, height);
+
+    QFont smallerBoldFont;
+    smallerBoldFont.setBold(true);
+    smallerBoldFont.setPointSize(smallerBoldFont.pointSize()*.85);
+    painter->setFont(smallerBoldFont);
+    painter->setPen(QPen(QColor(0xff, 0xff, 0xff), 1));
+    painter->drawText(0, 1, width, height, Qt::AlignCenter, action->text());
+
+    painter->restore();
+}
+
diff --git a/src/thlibrary/thblackbar.h b/src/thlibrary/thblackbar.h
new file mode 100644 (file)
index 0000000..4695780
--- /dev/null
@@ -0,0 +1,54 @@
+#ifndef _THBLACKBAR_H_
+#define _THBLACKBAR_H_
+
+#include <QWidget>
+#include <QAction>
+
+class THBlackBar : public QWidget {
+    
+    Q_OBJECT
+    
+        public:
+    THBlackBar (QWidget *parent = 0);
+    ~THBlackBar();
+    
+        public:
+    QAction *addAction (QAction *action);
+    QAction *addAction (const QString& text);
+    void setCheckedAction(int index);
+    
+    QSize minimumSizeHint (void) const;
+    
+        protected:
+    void paintEvent (QPaintEvent *event);
+    
+    void mouseMoveEvent (QMouseEvent *event);
+    void mousePressEvent (QMouseEvent *event);
+    void leaveEvent(QEvent *event);
+    
+        private:
+    void drawUnselectedButton (        QPainter *painter,
+                                const QRect& rect,
+                                const QAction *action);
+    void drawSelectedButton (  QPainter *painter,
+                                const QRect& rect,
+                                const QAction *action);
+    void drawButton (  QPainter *painter,
+                        const QRect& rect,
+                        const QAction *action);
+    void drawButton (  QPainter *painter,
+                        const QRect& rect,
+                        const QLinearGradient& gradient,
+                        const QColor& color,
+                        const QAction *action);
+    
+    QAction *hoveredAction (const QPoint& pos) const;
+    int calculateButtonWidth (void) const;             
+    
+        private:
+    class Private;
+    Private *d;
+};
+
+#endif /* !_THBLACKBAR_H_ */
+
diff --git a/src/thlibrary/thlibrary.pri b/src/thlibrary/thlibrary.pri
new file mode 100644 (file)
index 0000000..e87b1c3
--- /dev/null
@@ -0,0 +1,4 @@
+DEPENDPATH += $$PWD
+INCLUDEPATH += $$PWD
+HEADERS += thblackbar.h
+SOURCES += thblackbar.cpp
diff --git a/src/updatechecker.cpp b/src/updatechecker.cpp
new file mode 100644 (file)
index 0000000..35c4639
--- /dev/null
@@ -0,0 +1,62 @@
+#include "updatechecker.h"
+#include "networkaccess.h"
+#include "Constants.h"
+
+namespace The {
+    NetworkAccess* http();
+}
+
+UpdateChecker::UpdateChecker() {
+    m_needUpdate = false;
+}
+
+void UpdateChecker::checkForUpdate() {
+    QUrl updateUrl(QString(Constants::WEBSITE) + "-ws/release.xml");
+    // QUrl updateUrl("http://flavio.tordini.org:8012/release.xml");
+
+    QObject *reply = The::http()->get(updateUrl);
+    connect(reply, SIGNAL(data(QByteArray)), SLOT(requestFinished(QByteArray)));
+
+}
+
+void UpdateChecker::requestFinished(QByteArray data) {
+        UpdateCheckerStreamReader reader;
+        reader.read(data);
+        m_needUpdate = reader.needUpdate();
+        m_remoteVersion = reader.remoteVersion();
+        if (m_needUpdate && !m_remoteVersion.isEmpty()) emit newVersion(m_remoteVersion);
+}
+
+QString UpdateChecker::remoteVersion() {
+    return m_remoteVersion;
+}
+
+// --- Reader ---
+
+bool UpdateCheckerStreamReader::read(QByteArray data) {
+    addData(data);
+
+    while (!atEnd()) {
+        readNext();
+        if (isStartElement()) {
+            if (name() == "release") {
+                while (!atEnd()) {
+                    readNext();
+                    if (isStartElement() && name() == "version") {
+                        QString remoteVersion = readElementText();
+                        qDebug() << remoteVersion << QString(Constants::VERSION);
+                        m_needUpdate = remoteVersion != QString(Constants::VERSION);
+                        m_remoteVersion = remoteVersion;
+                        break;
+                    }
+                }
+            }
+        }
+    }
+
+    return !error();
+}
+
+QString UpdateCheckerStreamReader::remoteVersion() {
+    return m_remoteVersion;
+}
diff --git a/src/updatechecker.h b/src/updatechecker.h
new file mode 100644 (file)
index 0000000..8d6cf08
--- /dev/null
@@ -0,0 +1,42 @@
+#ifndef UPDATECHECKER_H
+#define UPDATECHECKER_H
+
+#include <QXmlStreamReader>
+#include <QNetworkReply>
+
+class UpdateChecker : public QObject {
+    Q_OBJECT
+
+public:
+    UpdateChecker();
+    void checkForUpdate();
+    QString remoteVersion();
+
+signals:
+    void newVersion(QString);
+
+private slots:
+    void requestFinished(QByteArray);
+
+private:
+
+    bool m_needUpdate;
+    QString m_remoteVersion;
+    QNetworkReply *networkReply;
+
+};
+
+class UpdateCheckerStreamReader : public QXmlStreamReader {
+
+public:
+    bool read(QByteArray data);
+    QString remoteVersion();
+    bool needUpdate() { return m_needUpdate; }
+
+private:
+    QString m_remoteVersion;
+    bool m_needUpdate;
+
+};
+
+#endif // UPDATECHECKER_H
diff --git a/src/urllineedit.cpp b/src/urllineedit.cpp
new file mode 100644 (file)
index 0000000..d91ddd5
--- /dev/null
@@ -0,0 +1,198 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://www.qtsoftware.com/contact.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "urllineedit.h"
+#include "searchlineedit.h"
+
+#include <QtCore/QEvent>
+
+#include <QtGui/QApplication>
+#include <QtGui/QCompleter>
+#include <QtGui/QFocusEvent>
+#include <QtGui/QHBoxLayout>
+#include <QtGui/QLabel>
+#include <QtGui/QLineEdit>
+#include <QtGui/QPainter>
+#include <QtGui/QStyle>
+#include <QtGui/QStyleOptionFrameV2>
+
+#include <QtCore/QDebug>
+
+ExLineEdit::ExLineEdit(QWidget *parent)
+    : QWidget(parent)
+    , m_leftWidget(0)
+    , m_lineEdit(new QLineEdit(this))
+    , m_clearButton(0)
+{
+    setFocusPolicy(m_lineEdit->focusPolicy());
+    setAttribute(Qt::WA_InputMethodEnabled);
+    setSizePolicy(m_lineEdit->sizePolicy());
+    setBackgroundRole(m_lineEdit->backgroundRole());
+    setMouseTracking(true);
+    setAcceptDrops(true);
+    setAttribute(Qt::WA_MacShowFocusRect, true);
+    QPalette p = m_lineEdit->palette();
+    setPalette(p);
+
+    // line edit
+    m_lineEdit->setFrame(false);
+    m_lineEdit->setFocusProxy(this);
+    m_lineEdit->setAttribute(Qt::WA_MacShowFocusRect, false);
+    QPalette clearPalette = m_lineEdit->palette();
+    clearPalette.setBrush(QPalette::Base, QBrush(Qt::transparent));
+    m_lineEdit->setPalette(clearPalette);
+
+    // clearButton
+    m_clearButton = new ClearButton(this);
+    connect(m_clearButton, SIGNAL(clicked()),
+            m_lineEdit, SLOT(clear()));
+    connect(m_lineEdit, SIGNAL(textChanged(const QString&)),
+            m_clearButton, SLOT(textChanged(const QString&)));
+}
+
+void ExLineEdit::setFont(const QFont &font) {
+    m_lineEdit->setFont(font);
+    updateGeometries();
+}
+
+void ExLineEdit::setLeftWidget(QWidget *widget)
+{
+    m_leftWidget = widget;
+}
+
+QWidget *ExLineEdit::leftWidget() const
+{
+    return m_leftWidget;
+}
+
+void ExLineEdit::resizeEvent(QResizeEvent *event)
+{
+    Q_ASSERT(m_leftWidget);
+    updateGeometries();
+    QWidget::resizeEvent(event);
+}
+
+void ExLineEdit::updateGeometries()
+{
+    QStyleOptionFrameV2 panel;
+    initStyleOption(&panel);
+    QRect rect = style()->subElementRect(QStyle::SE_LineEditContents, &panel, this);
+
+    int padding = 3;
+    // int height = rect.height() + padding*2;
+    int width = rect.width();
+
+    // int m_leftWidgetHeight = m_leftWidget->height();
+    m_leftWidget->setGeometry(rect.x() + 2,          0,
+                              m_leftWidget->width(), m_leftWidget->height());
+
+    int clearButtonWidth = this->height();
+    m_lineEdit->setGeometry(m_leftWidget->x() + m_leftWidget->width(),        padding,
+                            width - clearButtonWidth - m_leftWidget->width(), this->height() - padding*2);
+
+    m_clearButton->setGeometry(this->width() - clearButtonWidth, 0,
+                               clearButtonWidth, this->height());
+}
+
+void ExLineEdit::initStyleOption(QStyleOptionFrameV2 *option) const
+{
+    option->initFrom(this);
+    option->rect = contentsRect();
+    option->lineWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth, option, this);
+    option->midLineWidth = 0;
+    option->state |= QStyle::State_Sunken;
+    if (m_lineEdit->isReadOnly())
+        option->state |= QStyle::State_ReadOnly;
+#ifdef QT_KEYPAD_NAVIGATION
+    if (hasEditFocus())
+        option->state |= QStyle::State_HasEditFocus;
+#endif
+    option->features = QStyleOptionFrameV2::None;
+}
+
+QSize ExLineEdit::sizeHint() const
+{
+    m_lineEdit->setFrame(true);
+    QSize size = m_lineEdit->sizeHint();
+    m_lineEdit->setFrame(false);
+    return size;
+}
+
+void ExLineEdit::focusInEvent(QFocusEvent *event)
+{
+    m_lineEdit->event(event);
+    QWidget::focusInEvent(event);
+}
+
+void ExLineEdit::focusOutEvent(QFocusEvent *event)
+{
+    m_lineEdit->event(event);
+
+    if (m_lineEdit->completer()) {
+        connect(m_lineEdit->completer(), SIGNAL(activated(QString)),
+                         m_lineEdit, SLOT(setText(QString)));
+        connect(m_lineEdit->completer(), SIGNAL(highlighted(QString)),
+                         m_lineEdit, SLOT(_q_completionHighlighted(QString)));
+    }
+    QWidget::focusOutEvent(event);
+}
+
+void ExLineEdit::keyPressEvent(QKeyEvent *event)
+{
+    m_lineEdit->event(event);
+    QWidget::keyPressEvent(event);
+}
+
+bool ExLineEdit::event(QEvent *event)
+{
+    if (event->type() == QEvent::ShortcutOverride ||
+        event->type() == QEvent::InputMethod)
+        m_lineEdit->event(event);
+    return QWidget::event(event);
+}
+
+void ExLineEdit::paintEvent(QPaintEvent *)
+{
+    QPainter p(this);
+    QStyleOptionFrameV2 panel;
+    initStyleOption(&panel);
+    style()->drawPrimitive(QStyle::PE_PanelLineEdit, &panel, &p, this);
+}
diff --git a/src/urllineedit.h b/src/urllineedit.h
new file mode 100644 (file)
index 0000000..037bb8b
--- /dev/null
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://www.qtsoftware.com/contact.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef URLLINEEDIT_H
+#define URLLINEEDIT_H
+
+#include <QtCore/QUrl>
+#include <QtGui/QWidget>
+#include <QtGui/QLineEdit>
+#include <QtGui/QStyleOptionFrame>
+
+QT_BEGIN_NAMESPACE
+class QLineEdit;
+QT_END_NAMESPACE
+
+class ClearButton;
+class ExLineEdit : public QWidget
+{
+    Q_OBJECT
+
+public:
+    ExLineEdit(QWidget *parent = 0);
+
+    inline QLineEdit *lineEdit() const { return m_lineEdit; }
+
+    void setLeftWidget(QWidget *widget);
+    QWidget *leftWidget() const;
+    void clear() {
+        m_lineEdit->clear();
+    }
+    QString text() {
+        return m_lineEdit->text();
+    }
+    QSize sizeHint() const;
+    void updateGeometries();
+    void setFont(const QFont &);
+
+protected:
+    void focusInEvent(QFocusEvent *event);
+    void focusOutEvent(QFocusEvent *event);
+    void keyPressEvent(QKeyEvent *event);
+    void paintEvent(QPaintEvent *event);
+    void resizeEvent(QResizeEvent *event);
+    bool event(QEvent *event);
+    void initStyleOption(QStyleOptionFrameV2 *option) const;
+
+    QWidget *m_leftWidget;
+    QLineEdit *m_lineEdit;
+    ClearButton *m_clearButton;
+};
+
+#endif // URLLINEEDIT_H
+
diff --git a/src/video.cpp b/src/video.cpp
new file mode 100644 (file)
index 0000000..635c95c
--- /dev/null
@@ -0,0 +1,213 @@
+#include "video.h"
+#include "networkaccess.h"
+#include <QtNetwork>
+#include "videodefinition.h"
+
+namespace The {
+    NetworkAccess* http();
+}
+
+Video::Video() : m_duration(0),
+m_viewCount(-1),
+definitionCode(0),
+elIndex(0) { }
+
+void Video::preloadThumbnail() {
+    if (m_thumbnailUrls.isEmpty()) return;
+    QObject *reply = The::http()->get(m_thumbnailUrls.first());
+    connect(reply, SIGNAL(data(QByteArray)), SLOT(setThumbnail(QByteArray)));
+}
+
+void Video::setThumbnail(QByteArray bytes) {
+    m_thumbnail = QImage::fromData(bytes);
+    emit gotThumbnail();
+}
+
+const QImage Video::thumbnail() const {
+    return m_thumbnail;
+}
+
+static const QStringList elTypes = QStringList() << "embedded" << "vevo" << "detailpage";
+
+void Video::loadStreamUrl() {
+
+    // https://develop.participatoryculture.org/trac/democracy/browser/trunk/tv/portable/flashscraper.py
+
+    // Get Video ID
+    // youtube-dl line 428
+    // QRegExp re("^((?:http://)?(?:\\w+\\.)?youtube\\.com/(?:(?:v/)|(?:(?:watch(?:\\.php)?)?\\?(?:.+&)?v=)))?([0-9A-Za-z_-]+)(?(1).+)?$");
+    QRegExp re("^http://www\\.youtube\\.com/watch\\?v=([0-9A-Za-z_-]+).*");
+    bool match = re.exactMatch(m_webpage.toString());
+    if (!match || re.numCaptures() < 1) {
+        emit errorStreamUrl(QString("Cannot get video id for %1").arg(m_webpage.toString()));
+        return;
+    }
+    videoId = re.cap(1);
+
+    getVideoInfo();
+
+}
+
+void  Video::getVideoInfo() {
+
+    if (elIndex > elTypes.size() - 1) {
+        // Don't panic! We have a plan B.
+        // get the youtube video webpage
+        QObject *reply = The::http()->get(webpage().toString());
+        connect(reply, SIGNAL(data(QByteArray)), SLOT(scrapeWebPage(QByteArray)));
+        connect(reply, SIGNAL(error(QNetworkReply*)), SLOT(errorVideoInfo(QNetworkReply*)));
+        // see you in scrapWebPage(QByteArray)
+        return;
+    }
+
+    // Get Video Token
+    QUrl videoInfoUrl = QUrl(QString(
+            "http://www.youtube.com/get_video_info?video_id=%1&el=%2&ps=default&eurl="
+            ).arg(videoId, elTypes.at(elIndex)));
+
+    QObject *reply = The::http()->get(videoInfoUrl);
+    connect(reply, SIGNAL(data(QByteArray)), SLOT(gotVideoInfo(QByteArray)));
+    connect(reply, SIGNAL(error(QNetworkReply*)), SLOT(errorVideoInfo(QNetworkReply*)));
+
+    // see you in gotVideoInfo...
+
+}
+
+void  Video::gotVideoInfo(QByteArray data) {
+    QString videoInfo = QString::fromUtf8(data);
+
+    QRegExp re = QRegExp("^.*&token=([^&]+).*$");
+    bool match = re.exactMatch(videoInfo);
+
+    // handle regexp failure
+    if (!match || re.numCaptures() < 1) {
+        // Don't panic! We're gonna try another magic "el" param
+        elIndex++;
+        getVideoInfo();
+        return;
+    }
+
+    QString videoToken = re.cap(1);
+    // FIXME proper decode
+    videoToken = videoToken.replace("%3D", "=");
+    // we'll need this in gotHeadHeaders()
+    this->videoToken = videoToken;
+
+    // qDebug() << "token" << videoToken;
+
+    QSettings settings;
+    QString definitionName = settings.value("definition").toString();
+    int definitionCode = VideoDefinition::getDefinitionCode(definitionName);
+    if (definitionCode == 18) {
+        // This is assumed always available
+        foundVideoUrl(videoToken, 18);
+    } else {
+        findVideoUrl(definitionCode);
+    }
+
+}
+
+void Video::foundVideoUrl(QString videoToken, int definitionCode) {
+
+    QUrl videoUrl = QUrl(QString(
+            "http://www.youtube.com/get_video?video_id=%1&t=%2&eurl=&el=embedded&ps=default&fmt=%3"
+            ).arg(videoId, videoToken, QString::number(definitionCode)));
+
+    m_streamUrl = videoUrl;
+    emit gotStreamUrl(videoUrl);
+}
+
+void Video::errorVideoInfo(QNetworkReply *reply) {
+    emit errorStreamUrl(tr("Network error: %1 for %2").arg(reply->errorString(), reply->url().toString()));
+}
+
+void Video::scrapeWebPage(QByteArray data) {
+
+    QString videoHTML = QString::fromUtf8(data);
+    QRegExp re(".*, \"t\": \"([^\"]+)\".*");
+    bool match = re.exactMatch(videoHTML);
+
+    // on regexp failure, stop and report error
+    if (!match || re.numCaptures() < 1) {
+        emit errorStreamUrl("Error parsing video page");
+        return;
+    }
+
+    QString videoToken = re.cap(1);
+    // FIXME proper decode
+    videoToken = videoToken.replace("%3D", "=");
+
+    // we'll need this in gotHeadHeaders()
+    this->videoToken = videoToken;
+
+    // qDebug() << "token" << videoToken;
+
+    QSettings settings;
+    QString definitionName = settings.value("definition").toString();
+    int definitionCode = VideoDefinition::getDefinitionCode(definitionName);
+    if (definitionCode == 18) {
+        // This is assumed always available
+        foundVideoUrl(videoToken, 18);
+    } else {
+        findVideoUrl(definitionCode);
+    }
+
+}
+
+void Video::gotHeadHeaders(QNetworkReply* reply) {
+    int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+    // qDebug() << "gotHeaders" << statusCode;
+    if (statusCode == 200) {
+        foundVideoUrl(videoToken, definitionCode);
+    } else {
+
+        // try next (lower quality) definition
+        /*
+        QStringList definitionNames = VideoDefinition::getDefinitionNames();
+        int currentIndex = definitionNames.indexOf(currentDefinition);
+        int previousIndex = 0;
+        if (currentIndex > 0) {
+            previousIndex = currentIndex - 1;
+        }
+        if (previousIndex > 0) {
+            QString nextDefinitionName = definitionNames.at(previousIndex);
+            findVideoUrl(nextDefinitionName);
+        } else {
+            foundVideoUrl(videoToken, 18);
+        }*/
+
+
+        QList<int> definitionCodes = VideoDefinition::getDefinitionCodes();
+        int currentIndex = definitionCodes.indexOf(definitionCode);
+        int previousIndex = 0;
+        if (currentIndex > 0) {
+            previousIndex = currentIndex - 1;
+            int definitionCode = definitionCodes.at(previousIndex);
+            if (definitionCode == 18) {
+                // This is assumed always available
+                foundVideoUrl(videoToken, 18);
+            } else {
+                findVideoUrl(definitionCode);
+            }
+
+        } else {
+            foundVideoUrl(videoToken, 18);
+        }
+
+    }
+}
+
+void Video::findVideoUrl(int definitionCode) {
+    this->definitionCode = definitionCode;
+
+    QUrl videoUrl = QUrl(QString(
+            "http://www.youtube.com/get_video?video_id=%1&t=%2&eurl=&el=embedded&ps=default&fmt=%3"
+            ).arg(videoId, videoToken, QString::number(definitionCode)));
+
+    QObject *reply = The::http()->head(videoUrl);
+    connect(reply, SIGNAL(finished(QNetworkReply*)), SLOT(gotHeadHeaders(QNetworkReply*)));
+    // connect(reply, SIGNAL(error(QNetworkReply*)), SLOT(errorVideoInfo(QNetworkReply*)));
+
+    // see you in gotHeadHeaders()
+
+}
diff --git a/src/video.h b/src/video.h
new file mode 100644 (file)
index 0000000..2702d7a
--- /dev/null
@@ -0,0 +1,95 @@
+#ifndef VIDEO_H
+#define VIDEO_H
+
+#include <QtGui>
+#include <QtNetwork>
+
+class Video : public QObject {
+
+    Q_OBJECT
+
+public:
+    Video();
+
+    const QString title() const { return m_title; }
+    void setTitle( QString title ) { m_title = title; }
+
+    const QString description() const { return m_description; }
+    void setDescription( QString description ) { m_description = description; }
+
+    const QString author() const { return m_author; }
+    void setAuthor( QString author ) { m_author = author; }
+
+    const QUrl webpage() const { return m_webpage; }
+    void setWebpage( QUrl webpage ) { m_webpage = webpage; }
+
+    QList<QUrl> thumbnailUrls() const { return m_thumbnailUrls; }
+    void addThumbnailUrl(QUrl url) {
+        m_thumbnailUrls << url;
+    }
+
+    void preloadThumbnail();
+    const QImage thumbnail() const;
+
+    int duration() const { return m_duration; }
+    void setDuration( int duration ) { m_duration = duration; }
+
+    int viewCount() const { return m_viewCount; }
+    void setViewCount( int viewCount ) { m_viewCount = viewCount; }
+
+    const QDateTime published() const { return m_published; }
+    void setPublished( QDateTime published ) { m_published = published; }
+
+    bool getDefinitionCode() const { return definitionCode; }
+
+    void loadStreamUrl();
+    QUrl getStreamUrl() { return m_streamUrl; }
+
+public slots:
+    void setThumbnail(QByteArray bytes);
+
+signals:
+    void gotThumbnail();
+    void gotStreamUrl(QUrl streamUrl);
+    void errorStreamUrl(QString message);
+
+private slots:
+    void gotVideoInfo(QByteArray);
+    void errorVideoInfo(QNetworkReply*);
+    void scrapeWebPage(QByteArray);
+    void gotHeadHeaders(QNetworkReply*);
+
+private:
+    void getVideoInfo();
+    void findVideoUrl(int definitionCode);
+    void foundVideoUrl(QString videoToken, int definitionCode);
+
+    QString m_title;
+    QString m_description;
+    QString m_author;
+    QUrl m_webpage;
+    QUrl m_streamUrl;
+    QImage m_thumbnail;
+    QList<QUrl> m_thumbnailUrls;
+    int m_duration;
+    QDateTime m_published;
+    int m_viewCount;
+
+    // The YouTube video id
+    // This is needed by the gotVideoInfo callback
+    QString videoId;
+
+    QString videoToken;
+    int definitionCode;
+
+    // current index for the elTypes list
+    // needed to iterate on elTypes
+    int elIndex;
+};
+
+// This is required in order to use QPointer<Video> as a QVariant
+// as used by the Model/View playlist
+typedef QPointer<Video> VideoPointer;
+Q_DECLARE_METATYPE(VideoPointer)
+
+#endif // VIDEO_H
diff --git a/src/videoareawidget.cpp b/src/videoareawidget.cpp
new file mode 100644 (file)
index 0000000..f4b48e6
--- /dev/null
@@ -0,0 +1,117 @@
+#include "videoareawidget.h"
+#include "videomimedata.h"
+
+VideoAreaWidget::VideoAreaWidget(QWidget *parent) : QWidget(parent) {
+    QBoxLayout *vLayout = new QVBoxLayout(this);
+    vLayout->setMargin(0);
+    vLayout->setSpacing(0);
+    
+    // hidden message widget
+    messageLabel = new QLabel(this);
+    messageLabel->setOpenExternalLinks(true);
+    messageLabel->setMargin(7);
+    messageLabel->setBackgroundRole(QPalette::ToolTipBase);
+    messageLabel->setForegroundRole(QPalette::ToolTipText);
+    messageLabel->setAutoFillBackground(true);
+    messageLabel->setWordWrap(true);
+    messageLabel->hide();
+    vLayout->addWidget(messageLabel);
+    
+    stackedLayout = new QStackedLayout();
+    vLayout->addLayout(stackedLayout);
+    
+    setLayout(vLayout);
+    setAcceptDrops(true);
+    
+    setMouseTracking(true);
+}
+
+void VideoAreaWidget::setVideoWidget(QWidget *videoWidget) {
+    this->videoWidget = videoWidget;
+    videoWidget->setMouseTracking(true);
+    stackedLayout->addWidget(videoWidget);
+}
+
+void VideoAreaWidget::setLoadingWidget(LoadingWidget *loadingWidget) {
+    this->loadingWidget = loadingWidget;
+    stackedLayout->addWidget(loadingWidget);
+}
+
+void VideoAreaWidget::showVideo() {
+    stackedLayout->setCurrentWidget(videoWidget);
+}
+
+void VideoAreaWidget::showError(QString message) {
+    // loadingWidget->setError(message);
+    messageLabel->setText(message);
+    messageLabel->show();
+    stackedLayout->setCurrentWidget(loadingWidget);
+}
+
+void VideoAreaWidget::showLoading(Video *video) {
+    this->loadingWidget->setVideo(video);
+    stackedLayout->setCurrentWidget(loadingWidget);
+    messageLabel->hide();
+    messageLabel->clear();
+}
+
+void VideoAreaWidget::clear() {
+    stackedLayout->setCurrentWidget(loadingWidget);
+    loadingWidget->clear();
+    messageLabel->hide();
+    messageLabel->clear();
+}
+
+void VideoAreaWidget::mouseDoubleClickEvent(QMouseEvent *event) {
+    if (event->button() == Qt::LeftButton)
+        emit doubleClicked();
+}
+
+void VideoAreaWidget::mousePressEvent(QMouseEvent *event) {
+    QWidget::mousePressEvent(event);
+
+    if(event->button() == Qt::RightButton)
+        emit rightClicked();
+}
+
+void VideoAreaWidget::dragEnterEvent(QDragEnterEvent *event) {
+    // qDebug() << event->mimeData()->formats();
+    if (event->mimeData()->hasFormat("application/x-minitube-video")) {
+        event->acceptProposedAction();
+    }
+}
+
+void VideoAreaWidget::dropEvent(QDropEvent *event) {
+    
+    const VideoMimeData* videoMimeData = dynamic_cast<const VideoMimeData*>( event->mimeData() );
+    if(!videoMimeData ) return;
+    
+    QList<Video*> droppedVideos = videoMimeData->videos();
+    if (droppedVideos.isEmpty())
+        return;
+    Video *video = droppedVideos.first();
+    int row = listModel->rowForVideo(video);
+    if (row != -1)
+        listModel->setActiveRow(row);
+    event->acceptProposedAction();
+}
+
+void VideoAreaWidget::mouseMoveEvent(QMouseEvent *event) {
+    QWidget::mouseMoveEvent(event);
+
+    QWidget* mainWindow = window();
+    if (!mainWindow->isFullScreen()) return;
+
+    // qDebug() << "VideoAreaWidget::mouseMoveEvent" << event->pos();
+
+    const int x = event->pos().x();
+    const int y = event->pos().y();
+
+    bool visible = y <= 0;
+    bool ret = QMetaObject::invokeMethod(mainWindow, "showFullscreenToolbar", Qt::DirectConnection, Q_ARG(bool, visible));
+    if (!ret) qDebug() << "showFullscreenToolbar invokeMethod failed";
+
+    visible = x <= 0;
+    ret = QMetaObject::invokeMethod(mainWindow, "showFullscreenPlaylist", Qt::DirectConnection, Q_ARG(bool, visible));
+    if (!ret) qDebug() << "showFullscreenPlaylist invokeMethod failed";
+}
diff --git a/src/videoareawidget.h b/src/videoareawidget.h
new file mode 100644 (file)
index 0000000..e1f7cea
--- /dev/null
@@ -0,0 +1,45 @@
+#ifndef VIDEOAREAWIDGET_H
+#define VIDEOAREAWIDGET_H
+
+#include <QWidget>
+#include "video.h"
+#include "loadingwidget.h"
+#include "ListModel.h"
+
+class VideoAreaWidget : public QWidget {
+
+    Q_OBJECT
+
+public:
+    VideoAreaWidget(QWidget *parent);
+    void setVideoWidget(QWidget *videoWidget);
+    void setLoadingWidget(LoadingWidget *loadingWidget);
+    void showLoading(Video* video);
+    void showVideo();
+    void showError(QString message);
+    void clear();
+    void setListModel(ListModel *listModel) {
+        this->listModel = listModel;
+    }
+
+signals:
+    void doubleClicked();
+    void rightClicked();
+
+protected:
+    void mouseDoubleClickEvent(QMouseEvent *event);
+    void mousePressEvent(QMouseEvent *event);
+    void dragEnterEvent(QDragEnterEvent *event);
+    void dropEvent(QDropEvent *event);
+    void mouseMoveEvent(QMouseEvent *event);
+
+private:
+    QStackedLayout *stackedLayout;
+    QWidget *videoWidget;
+    LoadingWidget *loadingWidget;
+    ListModel *listModel;
+    QLabel *messageLabel;
+
+};
+
+#endif // VIDEOAREAWIDGET_H
diff --git a/src/videodefinition.cpp b/src/videodefinition.cpp
new file mode 100644 (file)
index 0000000..c2180e2
--- /dev/null
@@ -0,0 +1,25 @@
+#include "videodefinition.h"
+
+QStringList VideoDefinition::getDefinitionNames() {
+    static QStringList definitionNames = QStringList() << "360p" << "720p" << "1080p";
+    return definitionNames;
+}
+
+QList<int> VideoDefinition::getDefinitionCodes() {
+    static QList<int> definitionCodes = QList<int>() << 18 << 22 << 37;
+    return definitionCodes;
+}
+
+QHash<QString, int> VideoDefinition::getDefinitions() {
+    static QHash<QString, int> definitions;
+    if (definitions.isEmpty()) {
+        definitions.insert("360p", 18);
+        definitions.insert("720p", 22);
+        definitions.insert("1080p", 37);
+    }
+    return definitions;
+}
+
+int VideoDefinition::getDefinitionCode(QString name) {
+    return VideoDefinition::getDefinitions().value(name);
+}
diff --git a/src/videodefinition.h b/src/videodefinition.h
new file mode 100644 (file)
index 0000000..8e2e4c0
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef VIDEODEFINITION_H
+#define VIDEODEFINITION_H
+
+#include <QtCore>
+
+class VideoDefinition {
+
+public:
+    static QStringList getDefinitionNames();
+    static QList<int> getDefinitionCodes();
+    static QHash<QString, int> getDefinitions();
+    static int getDefinitionCode(QString name);
+
+};
+
+#endif // VIDEODEFINITION_H
diff --git a/src/videomimedata.cpp b/src/videomimedata.cpp
new file mode 100644 (file)
index 0000000..38998f2
--- /dev/null
@@ -0,0 +1,13 @@
+#include "videomimedata.h"
+
+VideoMimeData::VideoMimeData() {}
+
+QStringList VideoMimeData::formats() const {
+    QStringList formats( QMimeData::formats() );
+    formats.append("application/x-minitube-video");
+    return formats;
+}
+
+bool VideoMimeData::hasFormat( const QString &mimeType ) const {
+    return mimeType == "application/x-minitube-video";
+}
diff --git a/src/videomimedata.h b/src/videomimedata.h
new file mode 100644 (file)
index 0000000..ce06bd2
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef VIDEOMIMEDATA_H
+#define VIDEOMIMEDATA_H
+
+#include <QMimeData>
+#include "video.h"
+
+class VideoMimeData : public QMimeData {
+
+public:
+    VideoMimeData();
+
+    virtual QStringList formats() const;
+    virtual bool hasFormat( const QString &mimeType ) const;
+
+    QList<Video*> videos() const { return m_videos; }
+
+    void addVideo(Video *video) {
+        m_videos << video;
+    }
+
+private:
+    QList<Video*> m_videos;
+
+};
+
+#endif // VIDEOMIMEDATA_H
diff --git a/src/videowidget.cpp b/src/videowidget.cpp
new file mode 100644 (file)
index 0000000..ed6d689
--- /dev/null
@@ -0,0 +1,27 @@
+#include "videowidget.h"
+
+VideoWidget::VideoWidget(QWidget *parent) : Phonon::VideoWidget(parent) {
+    // mouse autohide
+    setMouseTracking(true);
+    mouseTimer = new QTimer(this);
+    mouseTimer->setInterval(3000);
+    mouseTimer->setSingleShot(true);
+    connect(mouseTimer, SIGNAL(timeout()), SLOT(hideMouse()));
+}
+
+void VideoWidget::mouseMoveEvent(QMouseEvent *event) {
+    Phonon::VideoWidget::mouseMoveEvent(event);
+
+    // qDebug() << "VideoWidget::mouseMoveEvent" << event->pos();
+
+    // show the normal cursor
+    unsetCursor();
+
+    // then hide it again after a few seconds
+    mouseTimer->start();
+}
+
+void VideoWidget::hideMouse() {
+    // qDebug() << "hideMouse()";
+    setCursor(Qt::BlankCursor);
+}
diff --git a/src/videowidget.h b/src/videowidget.h
new file mode 100644 (file)
index 0000000..deac833
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef VIDEOWIDGET_H
+#define VIDEOWIDGET_H
+
+#include <QtGui>
+#include <phonon/videowidget.h>
+#include <QTimer>
+
+class VideoWidget : public Phonon::VideoWidget {
+
+    Q_OBJECT
+
+public:
+    VideoWidget(QWidget *parent);
+
+protected:
+    void mouseMoveEvent (QMouseEvent *event);
+
+private slots:
+    void hideMouse();
+
+private:
+    QTimer *mouseTimer;
+
+};
+
+#endif // VIDEOWIDGET_H
diff --git a/src/youtubesearch.cpp b/src/youtubesearch.cpp
new file mode 100644 (file)
index 0000000..664240b
--- /dev/null
@@ -0,0 +1,74 @@
+#include "youtubesearch.h"
+#include "youtubestreamreader.h"
+#include "Constants.h"
+#include "networkaccess.h"
+
+namespace The {
+    NetworkAccess* http();
+}
+
+YouTubeSearch::YouTubeSearch() : QObject() {}
+
+void YouTubeSearch::search(SearchParams *searchParams, int max, int skip) {
+    this->abortFlag = false;
+
+    QString urlString = QString(
+            "http://gdata.youtube.com/feeds/api/videos?q=%1&max-results=%2&start-index=%3")
+            .arg(searchParams->keywords(), QString::number(max), QString::number(skip));
+
+    // Useful to test with a local webserver
+    /*
+    urlString = QString("http://localhost/oringo/video.xml?q=%1&max-results=%2&start-index=%3")
+                .arg(searchParams->keywords(), QString::number(max), QString::number(skip));
+                */
+
+    switch (searchParams->sortBy()) {
+    case SearchParams::SortByNewest:
+        urlString.append("&orderby=published");
+        break;
+    case SearchParams::SortByViewCount:
+        urlString.append("&orderby=viewCount");
+        break;
+    }
+
+    QUrl url(urlString);
+
+    QObject *reply = The::http()->get(url);
+    connect(reply, SIGNAL(data(QByteArray)), SLOT(parseResults(QByteArray)));
+    connect(reply, SIGNAL(error(QNetworkReply*)), SLOT(error(QNetworkReply*)));
+
+}
+
+void YouTubeSearch::error(QNetworkReply *reply) {
+    emit error(reply->errorString());
+}
+
+void YouTubeSearch::parseResults(QByteArray data) {
+
+    YouTubeStreamReader reader;
+    if (!reader.read(data)) {
+        qDebug() << "Error parsing XML";
+    }
+    videos = reader.getVideos();
+
+    foreach (Video *video, videos) {
+        // send it to the model
+        emit gotVideo(video);
+    }
+
+    foreach (Video *video, videos) {
+        // preload the thumb
+        if (abortFlag) return;
+        video->preloadThumbnail();
+    }
+
+    emit finished(videos.size());
+}
+
+QList<Video*> YouTubeSearch::getResults() {
+    return videos;
+}
+
+void YouTubeSearch::abort() {
+    this->abortFlag = true;
+}
diff --git a/src/youtubesearch.h b/src/youtubesearch.h
new file mode 100644 (file)
index 0000000..dc2739d
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef YOUTUBESEARCH_H
+#define YOUTUBESEARCH_H
+
+#include "video.h"
+#include "searchparams.h"
+
+class YouTubeSearch : public QObject {
+
+    Q_OBJECT
+
+public:
+    YouTubeSearch();
+    void search(SearchParams *searchParams, int max, int skip);
+    void abort();
+    QList<Video*> getResults();
+
+signals:
+    void gotVideo(Video*);
+    void finished(int total);
+    void error(QString message);
+
+private slots:
+    void parseResults(QByteArray data);
+    void error(QNetworkReply *reply);
+
+private:
+
+    QList<Video*> videos;
+
+    bool abortFlag;
+
+};
+
+#endif // YOUTUBESEARCH_H
diff --git a/src/youtubestreamreader.cpp b/src/youtubestreamreader.cpp
new file mode 100644 (file)
index 0000000..fd0ce99
--- /dev/null
@@ -0,0 +1,111 @@
+#include "youtubestreamreader.h"
+#include <QtGui>
+
+
+YouTubeStreamReader::YouTubeStreamReader() {
+
+}
+
+bool YouTubeStreamReader::read(QByteArray data) {
+    addData(data);
+
+    while (!atEnd()) {
+        readNext();
+        if (isStartElement()) {
+            if (name() == "feed") {
+                while (!atEnd()) {
+                    readNext();
+                    if (isStartElement() && name() == "entry") {
+                        readEntry();
+                    }
+                }
+            }
+        }
+    }
+
+    return !error();
+}
+
+void YouTubeStreamReader::readMediaGroup() {
+
+}
+
+void YouTubeStreamReader::readEntry() {
+    Video* video = new Video();
+    // qDebug(" *** ENTRY ***");
+
+    while (!atEnd()) {
+        readNext();
+
+        /*
+        qDebug() << name();
+        QXmlStreamAttribute attribute;
+        foreach (attribute, attributes())
+            qDebug() << attribute.name() << ":" << attribute.value();
+        */
+
+        if (isEndElement() && name() == "entry") break;
+        if (isStartElement()) {
+
+            if (name() == "link"
+                && attributes().value("rel").toString() == "alternate"
+                && attributes().value("type").toString() == "text/html"
+                ) {
+                QString webpage = attributes().value("href").toString();
+                // qDebug() << "Webpage: " << webpage;
+                video->setWebpage(QUrl(webpage));
+            } else if (name() == "author") {
+                readNext();
+                if (name() == "name") {
+                    QString author = readElementText();
+                    // qDebug() << "Author: " << author;
+                    video->setAuthor(author);
+                }
+            } else if (name() == "published") {
+                video->setPublished(QDateTime::fromString(readElementText(), Qt::ISODate));
+            } else if (namespaceUri() == "http://gdata.youtube.com/schemas/2007" && name() == "statistics") {
+
+                QString viewCount = attributes().value("viewCount").toString();
+                // qDebug() << "viewCount: " << viewCount;
+                video->setViewCount(viewCount.toInt());
+            }
+            else if (namespaceUri() == "http://search.yahoo.com/mrss/" && name() == "group") {
+
+                // read media group
+                while (!atEnd()) {
+                    readNext();
+                    if (isEndElement() && name() == "group") break;
+                    if (isStartElement()) {
+                        if (name() == "thumbnail") {
+                            // qDebug() << "Thumb: " << attributes().value("url").toString();
+                            // video->thumbnailUrls() << QUrl(attributes().value("url").toString());
+                            video->addThumbnailUrl(QUrl(attributes().value("url").toString()));
+                        }
+                        else if (name() == "title") {
+                            QString title = readElementText();
+                            // qDebug() << "Title: " << title;
+                            video->setTitle(title);
+                        }
+                        else if (name() == "description") {
+                            QString desc = readElementText();
+                            // qDebug() << "Description: " << desc;
+                            video->setDescription(desc);
+                        }
+                        else if (name() == "duration") {
+                            QString duration = attributes().value("seconds").toString();
+                            // qDebug() << "Duration: " << duration;
+                            video->setDuration(duration.toInt());
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    videos.append(video);
+
+}
+
+QList<Video*> YouTubeStreamReader::getVideos() {
+    return videos;
+}
diff --git a/src/youtubestreamreader.h b/src/youtubestreamreader.h
new file mode 100644 (file)
index 0000000..4cd3894
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef YOUTUBESTREAMREADER_H
+#define YOUTUBESTREAMREADER_H
+
+#include <QXmlStreamReader>
+#include <QBuffer>
+#include "video.h"
+
+class YouTubeStreamReader : public QXmlStreamReader
+{
+public:
+    YouTubeStreamReader();
+    bool read(QByteArray data);
+    QList<Video*> getVideos();
+
+private:
+    void readMediaGroup();
+    void readEntry();
+    QList<Video*> videos;
+};
+
+#endif // YOUTUBESTREAMREADER_H