Initial code import from debian snapshot

https://snapshot.debian.org/package/codeine/1.0.1-3.dfsg-3.1/

Signed-off-by: Michele Calgaro <michele.calgaro@yahoo.it>
pull/1/head
Michele Calgaro 5 years ago
commit 5f44f7b187
Signed by: MicheleC
GPG Key ID: 2A75B7CA8ADED5CF

@ -0,0 +1,340 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
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
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 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 2 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, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision 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, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This 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 Library General
Public License instead of this License.

@ -0,0 +1,52 @@
1.0.1
Mute button for KPart
Play DVD entry for KDE 3.5 + media:/ when DVD inserted
DVD-Menu-Toggle is no longer a KToggleAction because I can't detect when DVD menus change, but it still acts as a toggle button
Made record work for systems other than mine! (hard-coded path)
Made record shortcut CTRL-R so it doesn't conflict with the DVD-Root-Menu toggle
videoWindow doesn't judder when toolbar appears in fullscreen anymore
dvd-toolbar is gone, instead root menu button appears when dvd is playing
toolbar in fullscreen mode shows on mouse move
toolbar in fullscreen mode respects user-positioning
media kioslave support
double-clicking the video toggles fullscreen
don't show part in K-Menu
a volume toolbar button, - available from the configure-toolbar dialog, it's not very good yet
1.0-rc2
Seek fixes
Improved error messages
KPart crash on exit fix
1.0-beta6
Frame capture function
Aspect Ratio setting
Snap for videoSettings dialog sliders
Polish to all dialogs, menus
Hide cursor in fullscreen mode bug fixed
Many other fixes and lots of polish
1.0-beta3
Made Codeine single-window
Removed Pause KAction, instead toggling play pauses
Made it remember all details about how you like to view videos (eg.
contrast, brightness, size)
Shows toolbar when mouse is a screen-top in fullscreen mode
Bug fixes
1.0-beta2
Fixed fullscreen not covering Kicker
Added stop KAction
Added "You must install!" message after make does linking
Set busy cursor during unresponsive init period
Made the GUI detect lack of a ui file and show a useful message
If you quit during playback, the track volume fades out
Various feel/feedback fixes
Recent-files list in initial definately doesn't have duplicates anymore, sort order is also corrected
Routed out some nasty freeze bugs
Automatic frame format change handling
Stream recording
Many little bug-fixes and improvements
1.0-beta1
Initial release

@ -0,0 +1,17 @@
pkgname=codeine
pkgver=1.0.1
pkgrel=1
pkgdesc="A simple xine-based video player"
url="http://www.methylblue.com/codeine/"
build() {
echo -e "\033[0;34m==>\033[0;0;1m Configure \033[0;0m"
cd "$startdir"
./configure prefix=/opt/kde
echo -e "\033[0;34m==>\033[0;0;1m Make \033[0;0m"
make || return 1
echo -e "\033[0;34m==>\033[0;0;1m Install \033[0;0m"
DESTDIR="$startdir/pkg" make install
}

@ -0,0 +1,48 @@
INTRODUCTION
Codeine is a very simple xine-based media player.
I make the following promises:
* I will not add any substantial features after version 1.0.0
* After then, improvements will only be in the realm of usability and bug
fixes
You can rely on Codeine for now and until xine is obsolete to be a simple
no-frills video(/media) player.
Visit #codeine on freenode.net!
Max Howell
REQUIREMENTS
You will need at least:
* Qt 3.3.0 (Qt 3.2.x may work, it is just not tested)
* KDElibs 3.3.0
* xine-lib 1.0.0-rc4
You also need python installed in order to build Codeine.
INSTALLATION
I use scons + bksys as the build system. But you can still do the following:
% ./configure && make && su -c "make install"
Or if you have scons installed, simply:
% scons && su -c "scons install"
Note that scons is a little silly and this kind of thing doesn't work:
% ./configure --prefix=/foo/bar --debug=full
Instead do:
% ./configure prefix=/foo/bar debug=full
TRANSLATIONS
I will make the po file available for translation at the 1.0-rc1 stage, if
you want to make a translation I thank you in advance :-)

@ -0,0 +1,168 @@
#!/usr/bin/python
###########################################
## Common section, for loading the tools
## Load the builders in config
env = Environment(TARGS=COMMAND_LINE_TARGETS, ARGS=ARGUMENTS, tools=['default', 'generic', 'kde', 'codeine'], toolpath=['./scons/'])
## the configuration should be done by now, quit
if 'configure' in COMMAND_LINE_TARGETS:
env.Exit(0)
"""
Overview of the module system :
Each module (kde.py, generic.py, sound.py..) tries to load a stored
configuration when run. If the stored configuration does not exist
or if 'configure' is given on the command line (scons configure),
the module launches the verifications and detectioins and stores
the results. Modules also call exit when the detection fail.
For example, kde.py stores its config into kde.cache.py
This has several advantages for both developers and users :
- Users do not have to run ./configure to compile
- The build is insensitive to environment changes
- The cache maintains the objects so the config can be changed often
- Each module adds its own help via env.Help("message")
"""
## Use the variables available in the environment - unsafe, but moc, meinproc need it :-/
import os
env.AppendUnique( ENV = os.environ )
## If you do not want to copy the whole environment, you can use this instead (HOME is necessary for uic):
#env.AppendUnique( ENV = {'PATH' : os.environ['PATH'], 'HOME' : os.environ['HOME']} )
## The target make dist requires the python module shutil which is in 2.3
env.EnsurePythonVersion(2, 3)
## Bksys requires scons 0.96
env.EnsureSConsVersion(0, 96)
"""
Explanation of the 'env = Environment...' line :
* the command line arguments and targets are stored in env['TARGS'] and env['ARGS'] for use by the tools
* the part 'tools=['default', 'generic ..' detect and load the necessary functions for doing the things
* the part "toolpath=['./']" tells that the tools can be found in the current directory (generic.py, kde.py ..)
"""
"""
To load more configuration modules one should only have to add the appropriate tool
ie: to detect alsa and add the proper cflags, ldflags ..
a file alsa.py file will be needed, and one should then use :
env = Environment(TARGS=COMMAND_LINE_TARGETS, ARGS=ARGUMENTS, tools=['default', 'generic', 'kde', 'alsa'], toolpath=['./'])
You can also load environments that are targetted to different platforms
ie: if os.sys.platform = "darwin":
env = Environment(...
elsif os.sys.platform = "linux":
env = Environment(...
"""
## Setup the cache directory - this avoids recompiling the same files over and over again
## this is very handy when working with cvs
env.CacheDir('cache')
env.SConsignFile('scons/signatures')
## If you need more libs and they rely on pkg-config
## ie: add support for GTK (source: the scons wiki on www.scons.org)
# env.ParseConfig('pkg-config --cflags --libs gtk+-2.0')
"""
This tell scons that there are no rcs or sccs files - this trick
can speed up things a bit when having lots of #include
in the source code and for network file systems
"""
env.SourceCode(".", None)
dirs = [ '.', 'src', 'src/part', 'src/app' ]
for dir in dirs:
env.SourceCode(dir, None)
## If we had only one program (named kvigor) to build,
## we could add before exporting the env (some kde
## helpers in kde.py need it) :
# env['APPNAME'] = 'kvigor'
## Use this define if you are using the kde translation scheme (.po files)
env.Append( CPPFLAGS = ['-DQT_NO_TRANSLATION'] )
## Uncomment the following if you need threading support threading
#env.Append( CPPFLAGS = ['-DQT_THREAD_SUPPORT', '-D_REENTRANT'] )
#if os.uname()[0] == "FreeBSD":
# env.Append(LINKFLAGS=["-pthread"])
## Important : export the environment so that SConscript files can the
## configuration and builders in it
Export("env")
def string_it(target, source, env):
print "Visit #codeine on irc.freenode.net!"
return 0
env.AddPostAction( "install", string_it )
env.SConscript( "src/SConscript", build_dir='build', duplicate=0 )
if 'dist' in COMMAND_LINE_TARGETS:
APPNAME = 'codeine'
VERSION = os.popen("cat VERSION").read().rstrip()
FOLDER = APPNAME+'-'+VERSION
ARCHIVE = FOLDER+'.tar.bz2'
GREEN ="\033[92m"
NORMAL ="\033[0m"
import shutil
import glob
## check if the temporary directory already exists
if os.path.isdir(FOLDER):
shutil.rmtree(FOLDER)
## create a temporary directory
startdir = os.getcwd()
# TODO copying the cache takes forever! delete it first
shutil.copytree(startdir, FOLDER)
## remove the unnecessary files
os.popen("find "+FOLDER+" -name \"{arch}\" | xargs rm -rf")
os.popen("find "+FOLDER+" -name \".arch-ids\" | xargs rm -rf")
os.popen("find "+FOLDER+" -name \".arch-inventory\" | xargs rm -f")
os.popen("find "+FOLDER+" -name \".scon*\" | xargs rm -rf")
os.popen("find "+FOLDER+" -name \"kdiss*-data\" | xargs rm -rf")
os.popen("find "+FOLDER+" -name \"*.pyc\" | xargs rm -f")
os.popen("find "+FOLDER+" -name \"*.cache.py\" | xargs rm -f")
os.popen("find "+FOLDER+" -name \"*.log\" | xargs rm -f")
os.popen("find "+FOLDER+" -name \"*.kdevelop.*\" | xargs rm -f")
os.popen("find "+FOLDER+" -name \"*~\" | xargs rm -f")
os.popen("rm -rf "+FOLDER+"/autopackage")
os.popen("rm -rf "+FOLDER+"/build")
os.popen("rm -rf "+FOLDER+"/cache")
os.popen("rm -f " +FOLDER+"/codeine-*.tar.bz2")
os.popen("rm -f " +FOLDER+"/config.py*")
os.popen("rm -f " +FOLDER+"/src/configure.h")
os.popen("rm -f " +FOLDER+"/Doxyfile")
os.popen("rm -f " +FOLDER+"/Makefile")
os.popen("rm -rf "+FOLDER+"/packages")
os.popen("rm -rf "+FOLDER+"/screenshots")
os.popen("rm -f " +FOLDER+"/scons/signatures.dblite")
## make the tarball
print GREEN+"Writing archive "+ARCHIVE+NORMAL
os.popen("tar cjf "+ARCHIVE+" "+FOLDER)
## remove the temporary directory
if os.path.isdir(FOLDER):
shutil.rmtree(FOLDER)
env.Default(None)
env.Exit(0)

@ -0,0 +1 @@
1.0.1

87
configure vendored

@ -0,0 +1,87 @@
#! /bin/sh
# TODO parse each passed argument and remove any "--" prefix
BOLD="\033[1m"
RED="\033[91m"
GREEN="\033[92m"
YELLOW="\033[93m"
CYAN="\033[96m"
NORMAL="\033[0m"
if command -v scons >/dev/null 2>&1;
then
SCONS=scons
else
if [ ! -e "scons/scons" ]; then
echo ""
echo -ne "Unpacking mini-scons..."$RED
pushd scons >/dev/null 2>&1
tar xjvf scons-mini.tar.bz2 > /dev/null 2>&1
if [[ "$?" == "0" ]]; then
echo -e $GREEN"done"$NORMAL
else
echo -e $RED"failed!"$NORMAL
exit 2
fi
popd > /dev/null
fi
SCONS=scons/scons
fi
if [[ "$1" == "--help" ]]; then
$SCONS -Q configure --help
exit
fi
echo ""
echo "Configuring Codeine "`cat VERSION`"..."
echo ""
#TODO remove all prefixed "--"
$SCONS -Q configure $@ || exit 1
echo ""
echo -e "Your configure completed "$GREEN"successfully"$NORMAL", now type "$BOLD"make"$NORMAL
echo ""
cat > Makefile << EOF
## Makefile automatically generated by unpack_local_scons.sh
SCONS=$SCONS
# scons : compile
# scons -c : clean
# scons install : install
# scons -c install : uninstall and clean
# default target : use scons to build the programs
all:
\$(SCONS) -Q
### There are several possibilities to help debugging :
# scons --debug=explain, scons --debug=tree ..
#
### To optimize the runtime, use
# scons --max-drift=1 --implicit-deps-unchanged
debug:
\$(SCONS) -Q --debug=tree
clean:
\$(SCONS) -c
install:
\$(SCONS) install
uninstall:
\$(SCONS) -c install
## this target creates a tarball of the project
dist:
\$(SCONS) dist
EOF

@ -0,0 +1,9 @@
[Desktop Entry]
Name=Codeine
Exec=codeine %u
Icon=codeine
Type=Application
Encoding=UTF-8
MimeType=video/x-theora;video/x-ogm;video/x-ms-wmv;video/x-msvideo;video/x-ms-asf;video/x-matroska;video/mpeg;video/avi;video/quicktime;video/vnd.rn-realvideo;video/x-flic;
Categories=Qt;KDE;AudioVideo;Player
GenericName=Video Player

@ -0,0 +1,10 @@
[Desktop Entry]
Encoding=UTF-8
Icon=codeine
MimeType=video/x-theora;video/x-ogm;video/x-ms-wmv;video/x-msvideo;video/x-ms-asf;video/x-matroska;video/mpeg;video/avi;video/quicktime;video/vnd.rn-realvideo;video/x-flic;
Name=Codeine
Comment=Embeddable Video Player
ServiceTypes=KParts/ReadOnlyPart
Type=Service
X-KDE-Library=libcodeine
InitialPreference=9

@ -0,0 +1,39 @@
[Desktop Entry]
ServiceTypes=media/dvdvideo
Actions=Play;
Encoding=UTF-8
X-KDE-Priority=TopLevel
[Desktop Action Play]
Name=Play DVD with Codeine
Name[bg]=Възпроизвеждане на DVD с Codeine
Name[bn]=ক্যাফিন দিয়ে ডিভিডি চালাও
Name[br]=Seniñ an DVD gant Codeine
Name[ca]=Reprodueix DVD amb Codeine
Name[cs]=Přehrát DVD v Codeine
Name[da]=Spil dvd med Codeine
Name[de]=DVD mit Codeine abspielen
Name[el]=Αναπαραγωγή DVD με το Codeine
Name[es]=Reproducir DVD con Codeine
Name[et]=Esita DVD Codeine'is
Name[fi]=Toista dvd-levy Codeinessa
Name[fr]=Lire le DVD avec Codeine
Name[ga]=Seinn DVD le Codeine
Name[he]=נגן תקליטור DVD עם Codeine
Name[it]=Riproduci DVD con Codeine
Name[ja]=CodeineでDVDを再生
Name[nb]=Spill DVD med Codeine
Name[nl]=DVD met Codeine afspelen
Name[nn]=Spel DVD med Codeine
Name[pa]=ਕੈਫੀਨ ਨਾਲ DVD ਚਲਾਓ
Name[pl]=Odtwarzaj DVD w Codeine
Name[pt]=Ver o DVD com o Codeine
Name[pt_BR]=Reproduzir o DVD com o Codeine
Name[sr]=Пусти DVD Codeine-ом
Name[sr@Latn]=Pusti DVD Codeine-om
Name[sv]=Spela dvd med Codeine
Name[tr]=DVD'yi Codeine ile oynat
Name[xx]=xxPlay DVD with Codeinexx
Name[zh_CN]=用 Codeine 播放 DVD
Icon=codeine
Exec=codeine --play-dvd %u

@ -0,0 +1,10 @@
[KFileDialog Settings]
ShowPreviews=false
[MainWindow Toolbar dvdToolBar]
Hidden=true
IconText=IconTextRight
Index=0
[MainWindow Toolbar mainToolBar]
Index=1

@ -0,0 +1,33 @@
<!DOCTYPE kpartgui>
<kpartgui name="codeine" version="4">
<MenuBar>
<Menu name="file" noMerge="1"><text>&amp;Play</text>
<Action name="play_media"/>
<Separator/>
<Action name="play"/>
<Action name="stop"/>
<Separator/>
<Action name="file_quit"/>
</Menu>
<Menu name="settings" noMerge="1"><text>&amp;Settings</text>
<Separator/><!-- this seperator doesn't show :( -->
<Action name="fullscreen"/>
<Separator/><!-- this seperator doesn't show :( -->
<Action name="options_configure_keybinding"/>
<Action name="options_configure_toolbars"/>
<Separator/>
<Action name="video_settings"/>
<Action name="xine_settings"/>
</Menu>
</MenuBar>
<ToolBar name="mainToolBar"><text>Main Toolbar</text>
<Action name="play"/>
<Separator lineSeparator="false"/>
<Action name="position_slider"/>
<Separator lineSeparator="false"/>
<Action name="toggle_dvd_menu"/>
<Action name="fullscreen"/>
</ToolBar>
</kpartgui>

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 870 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

@ -0,0 +1,484 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2005-08-01 17:30+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
#: ../src/app/videoSettings.cpp:91
msgid "Video Settings"
msgstr ""
#: ../src/app/playDialog.cpp:27
msgid "Play Media"
msgstr ""
#: ../src/app/playDialog.cpp:33
msgid "What media would you like to play?"
msgstr ""
#: ../src/app/playDialog.cpp:38
msgid "Play File..."
msgstr ""
#: ../src/app/playDialog.cpp:42
msgid "Play VCD"
msgstr ""
#: ../src/app/playDialog.cpp:46
msgid "Play DVD"
msgstr ""
#: ../src/app/playDialog.cpp:73
msgid "Recently Played Media"
msgstr ""
#: ../src/app/xineConfig.cpp:60
msgid "Configure xine"
msgstr ""
#: ../src/app/xineConfig.cpp:84
msgid ""
"xine's defaults are usually sensible and should not require modification. "
"However, full configurability is provided for your pleasure ;-)."
msgstr ""
#: ../src/app/adjustSizeButton.cpp:31
msgid "Preferred Scale"
msgstr ""
#: ../src/app/adjustSizeButton.cpp:35
msgid "Scale 100%"
msgstr ""
#: ../src/app/adjustSizeButton.cpp:41
msgid "<b>Adjust video scale?"
msgstr ""
#: ../src/app/insertAspectRatioMenuItems.cpp:15
msgid "Determine &Automatically"
msgstr ""
#: ../src/app/insertAspectRatioMenuItems.cpp:17
msgid "&Square (1:1)"
msgstr ""
#: ../src/app/insertAspectRatioMenuItems.cpp:18
msgid "&4:3"
msgstr ""
#: ../src/app/insertAspectRatioMenuItems.cpp:19
msgid "Ana&morphic (16:9)"
msgstr ""
#: ../src/app/insertAspectRatioMenuItems.cpp:20
msgid "&DVB (2.11:1)"
msgstr ""
#: ../src/app/main.cpp:14
msgid "A video player that has a usability focus"
msgstr ""
#: ../src/app/main.cpp:15
msgid "Copyright 2005, Max Howell"
msgstr ""
#: ../src/app/main.cpp:19
msgid "Play 'URL'"
msgstr ""
#: ../src/app/main.cpp:28
msgid "Handbook"
msgstr ""
#: ../src/app/main.cpp:29
msgid "Great reference code"
msgstr ""
#: ../src/app/main.cpp:30
msgid "The current Codeine icon"
msgstr ""
#: ../src/app/main.cpp:31
msgid "The video for \"Call on Me\" encouraged plenty of debugging! ;)"
msgstr ""
#: ../src/app/playlistFile.cpp:32
msgid "The file is not a playlist"
msgstr ""
#: ../src/app/playlistFile.cpp:39
msgid "Codeine could not download the remote playlist: %1"
msgstr ""
#: ../src/app/playlistFile.cpp:54
msgid ""
"<qt>The playlist, <i>'%1'</i>, could not be interpreted. Perhaps it is empty?"
msgstr ""
#: ../src/app/playlistFile.cpp:58
msgid "Codeine could not open the file: %1"
msgstr ""
#: ../src/app/stateChange.cpp:83
msgid "&Pause"
msgstr ""
#: ../src/app/stateChange.cpp:83
msgid "&Play"
msgstr ""
#: ../src/app/stateChange.cpp:147
msgid "No media loaded"
msgstr ""
#: ../src/app/stateChange.cpp:150
msgid "Paused"
msgstr ""
#: ../src/app/actions.cpp:13 ../src/part/part.cpp:38
msgid "Play"
msgstr ""
#: ../src/app/xineEngine.cpp:127 ../src/part/xineEngine.cpp:50
msgid "xine was unable to initialize any video-drivers."
msgstr ""
#: ../src/app/xineEngine.cpp:129 ../src/part/xineEngine.cpp:48
msgid "xine was unable to initialize any audio-drivers."
msgstr ""
#: ../src/app/xineEngine.cpp:231
msgid "Loading media: %1"
msgstr ""
#: ../src/app/xineEngine.cpp:333
msgid "Recording to: %1"
msgstr ""
#: ../src/app/xineEngine.cpp:364
msgid "Playback paused"
msgstr ""
#: ../src/app/xineEngine.cpp:369
msgid "Playback resumed"
msgstr ""
#: ../src/app/xineEngine.cpp:382
msgid "There is no input plugin that can read: %1."
msgstr ""
#: ../src/app/xineEngine.cpp:385
msgid "There is no demux plugin available for %1."
msgstr ""
#: ../src/app/xineEngine.cpp:388
msgid "Demuxing failed for %1."
msgstr ""
#: ../src/app/xineEngine.cpp:393
msgid "Internal error while attempting to play %1."
msgstr ""
#: ../src/app/xineEngine.cpp:433
msgid "xine cannot currently seek in flac media"
msgstr ""
#: ../src/app/xineEngine.cpp:585 ../src/app/xineEngine.cpp:593
msgid "Channel %1"
msgstr ""
#: ../src/app/xineEngine.cpp:692 ../src/part/xineEngine.cpp:282
msgid "The source is encrypted and can not be decrypted."
msgstr ""
#: ../src/app/xineEngine.cpp:694 ../src/part/xineEngine.cpp:284
msgid "The host is unknown for the URL: <i>%1</i>"
msgstr ""
#: ../src/app/xineEngine.cpp:696 ../src/part/xineEngine.cpp:286
msgid "The device name you specified seems invalid."
msgstr ""
#: ../src/app/xineEngine.cpp:698 ../src/part/xineEngine.cpp:288
msgid "The network appears unreachable."
msgstr ""
#: ../src/app/xineEngine.cpp:700 ../src/part/xineEngine.cpp:290
msgid "Audio output unavailable; the device is busy."
msgstr ""
#: ../src/app/xineEngine.cpp:702 ../src/part/xineEngine.cpp:292
msgid "The connection was refused for the URL: <i>%1</i>"
msgstr ""
#: ../src/app/xineEngine.cpp:704 ../src/part/xineEngine.cpp:294
msgid "xine could not find the URL: <i>%1</i>"
msgstr ""
#: ../src/app/xineEngine.cpp:706 ../src/part/xineEngine.cpp:296
msgid "Access was denied for the URL: <i>%1</i>"
msgstr ""
#: ../src/app/xineEngine.cpp:708 ../src/part/xineEngine.cpp:298
msgid "The source cannot be read for the URL: <i>%1</i>"
msgstr ""
#: ../src/app/xineEngine.cpp:710 ../src/part/xineEngine.cpp:300
msgid "A problem occurred while loading a library or decoder."
msgstr ""
#: ../src/app/xineEngine.cpp:737 ../src/part/xineEngine.cpp:327
msgid "Sorry, no additional information is available."
msgstr ""
#: ../src/app/captureFrame.cpp:82
msgid "Capture - %1"
msgstr ""
#: ../src/app/captureFrame.cpp:98
msgid ""
"*.png|PNG Format\n"
"*.jpeg|JPEG Format"
msgstr ""
#: ../src/app/captureFrame.cpp:100
msgid "Save Frame"
msgstr ""
#: ../src/app/captureFrame.cpp:111
msgid "%1 saved successfully"
msgstr ""
#: ../src/app/captureFrame.cpp:113
msgid "Sorry, could not save %1"
msgstr ""
#: ../src/app/fullScreenAction.cpp:31
msgid "Exit F&ull Screen Mode"
msgstr ""
#: ../src/app/fullScreenAction.cpp:37
msgid "F&ull Screen Mode"
msgstr ""
#: ../src/app/mainWindow.cpp:94
msgid "&Subtitles"
msgstr ""
#: ../src/app/mainWindow.cpp:95
msgid "A&udio Channels"
msgstr ""
#: ../src/app/mainWindow.cpp:96
msgid "Aspect &Ratio"
msgstr ""
#: ../src/app/mainWindow.cpp:106
msgid "<qt>"
msgstr ""
#: ../src/app/mainWindow.cpp:106
msgid " could not load its interface, this probably means that "
msgstr ""
#: ../src/app/mainWindow.cpp:106
msgid ""
" is not installed to the correct prefix. If you installed from packages "
"please contact the packager, if you installed from source please try running "
"the <b>configure</b> script again like this: <pre> % ./configure --"
"prefix=`kde-config --prefix`</pre>"
msgstr ""
#: ../src/app/mainWindow.cpp:139
msgid "<qt>xine could not be successfully initialised. "
msgstr ""
#: ../src/app/mainWindow.cpp:139
msgid ""
" will now exit. You can try to identify what is wrong with your xine "
"installation using the <b>xine-check</b> command at a command-prompt."
msgstr ""
#: ../src/app/mainWindow.cpp:208
msgid "Play &Media..."
msgstr ""
#: ../src/app/mainWindow.cpp:214
msgid "Record"
msgstr ""
#: ../src/app/mainWindow.cpp:216
msgid "Reset Video Scale"
msgstr ""
#: ../src/app/mainWindow.cpp:217 ../src/app/mainWindow.cpp:423
msgid "Media Information"
msgstr ""
#: ../src/app/mainWindow.cpp:218
msgid "Menu Toggle"
msgstr ""
#: ../src/app/mainWindow.cpp:219
msgid "&Capture Frame"
msgstr ""
#: ../src/app/mainWindow.cpp:221
msgid "Video Settings..."
msgstr ""
#: ../src/app/mainWindow.cpp:222
msgid "Configure xine..."
msgstr ""
#: ../src/app/mainWindow.cpp:224
msgid "Position Slider"
msgstr ""
#: ../src/app/mainWindow.cpp:317
msgid "Codeine was asked to open an empty URL; it cannot."
msgstr ""
#: ../src/app/mainWindow.cpp:366
msgid "Supported Media Formats"
msgstr ""
#: ../src/app/mainWindow.cpp:366
msgid "All Files"
msgstr ""
#: ../src/app/mainWindow.cpp:367
msgid "Select A File To Play"
msgstr ""
#: ../src/app/mainWindow.cpp:438
msgid "&Determine Automatically"
msgstr ""
#: ../src/app/mainWindow.cpp:450
msgid "&Off"
msgstr ""
#: ../src/app/mainWindow.cpp:492
msgid "Sorry, no media was found in the drop"
msgstr ""
#: ../src/app/theStream.cpp:107
msgid "Metadata"
msgstr ""
#: ../src/app/theStream.cpp:109
msgid "Title"
msgstr ""
#: ../src/app/theStream.cpp:110
msgid "Comment"
msgstr ""
#: ../src/app/theStream.cpp:111
msgid "Artist"
msgstr ""
#: ../src/app/theStream.cpp:112
msgid "Genre"
msgstr ""
#: ../src/app/theStream.cpp:113
msgid "Album"
msgstr ""
#: ../src/app/theStream.cpp:114
msgid "Year"
msgstr ""
#: ../src/app/theStream.cpp:116
msgid "Audio Properties"
msgstr ""
#: ../src/app/theStream.cpp:118
msgid "Bitrate"
msgstr ""
#: ../src/app/theStream.cpp:118
msgid "%1 bps"
msgstr ""
#: ../src/app/theStream.cpp:119
msgid "Sample-rate"
msgstr ""
#: ../src/app/theStream.cpp:119
msgid "%1 Hz"
msgstr ""
#: ../src/app/theStream.cpp:121
msgid "Technical Information"
msgstr ""
#: ../src/app/theStream.cpp:123
msgid "Video Codec"
msgstr ""
#: ../src/app/theStream.cpp:124
msgid "Audio Codec"
msgstr ""
#: ../src/app/theStream.cpp:125
msgid "System Layer"
msgstr ""
#: ../src/app/theStream.cpp:126
msgid "Input Plugin"
msgstr ""
#: ../src/app/theStream.cpp:127
msgid "CDINDEX_DISCID"
msgstr ""
#: ../src/app/videoWindow.cpp:140
msgid "Pause"
msgstr ""
#: ../src/part/xineEngine.cpp:159
msgid "The Codeine video player could not find an input plugin for '%1'."
msgstr ""
#: ../src/part/xineEngine.cpp:162
msgid "The Codeine video player could not find a demux plugin for '%1'."
msgstr ""
#: ../src/part/xineEngine.cpp:165
msgid ""
"The Codeine video player failed to demux '%1'; please check your xine "
"installation."
msgstr ""
#: ../src/part/xineEngine.cpp:170
msgid ""
"The Codeine video player reports an internal error; please check your xine "
"installation."
msgstr ""
#: ../src/_translatorinfo.cpp:1
msgid ""
"_: NAME OF TRANSLATORS\n"
"Your names"
msgstr ""
#: ../src/_translatorinfo.cpp:3
msgid ""
"_: EMAIL OF TRANSLATORS\n"
"Your emails"
msgstr ""

@ -0,0 +1,60 @@
#!/bin/sh
# Inspired by Makefile.common from coolo
# this script is used to update the .po files
# To update the translations, you will need a specific gettext
# patched for kde and a lot of patience, tenacity, luck, time ..
# I guess one should only update the .po files when all .cpp files
# are generated (after a make or scons)
# If you have a better way to do this, do not keep that info
# for yourself and help me to improve this script, thanks
# (tnagyemail-mail tat yahoo d0tt fr)
SRCDIR=../src # srcdir is the directory containing the source code
TIPSDIR=$SRCDIR # tipsdir is the directory containing the tips
KDEDIR=`kde-config --prefix`
EXTRACTRC=extractrc
KDEPOT=`kde-config --prefix`/include/kde.pot
XGETTEXT="xgettext -C -ki18n -ktr2i18n -kI18N_NOOP -ktranslate -kaliasLocale -x $KDEPOT "
## check that kde.pot is available
if ! test -e $KDEPOT; then
echo "$KDEPOT does not exist, there is something wrong with your installation!"
XGETTEXT="xgettext -C -ki18n -ktr2i18n -kI18N_NOOP -ktranslate -kaliasLocale "
fi
> rc.cpp
## extract the strings
echo "extracting the strings"
# process the .ui and .rc files
$EXTRACTRC `find $SRCDIR -iname *.rc` >> rc.cpp
$EXTRACTRC `find $SRCDIR -iname *.ui` >> rc.cpp
echo -e 'i18n("_: NAME OF TRANSLATORS\\n"\n"Your names")\ni18n("_: EMAIL OF TRANSLATORS\\n"\n"Your emails")' > $SRCDIR/_translatorinfo.cpp
# process the tips - $SRCDIR is supposed to be where the tips are living
pushd $TIPSDIR; preparetips >tips.cpp; popd
$XGETTEXT `find $SRCDIR -name "*.cpp"` -o codeine.pot
# remove the intermediate files
rm -f $TIPSDIR/tips.cpp
rm -f rc.cpp
rm -f $SRCDIR/_translatorinfo.cpp
## now merge the .po files ..
echo "merging the .po files"
for i in `ls *.po`; do
msgmerge $i kdissert.pot -o $i || exit 1
done
## finished
echo "Done"

@ -0,0 +1,101 @@
## Max Howell, 2005
BOLD ="\033[1m"
RED ="\033[91m"
GREEN ="\033[92m"
YELLOW ="\033[93m"
CYAN ="\033[96m"
NORMAL ="\033[0m"
import os
def exists( env ):
return true
def generate( env ):
if 'configure' in env['TARGS']:
xine_lib_test_source_file = """
#include <qstring.h>
#include <xine.h>
int main( int argc, char **argv )
{
if( XINE_MAJOR_VERSION < 1 )
return 1;
const QString version( XINE_VERSION );
// eg. VERSION 1.0
if( version[1] == '.' )
return 0;
if( version == "1-cvs" )
return 0;
if( version.startsWith( "1-rc" ) && QString(version[4]).toInt() > 3 )
return 0;
return 2; //too old
}"""
def CheckKdeLibs( context ):
# ideally should be able to tell bksys what version we need
context.Message( 'Checking for KDElibs 3.3...' )
kde_version = os.popen("kde-config --version|grep KDE").read().strip().split()[1]
result = int( kde_version[0] ) == 3 and int( kde_version[2] ) >= 3
context.Result( result )
return result
def CheckXineLib( context ):
context.Message('Checking for xine-lib 1.0...')
result = context.TryLink(xine_lib_test_source_file, '.cpp')
context.Result(result)
return result
# prolly best to use a fresh env
# this seems to import the user's CXXFLAGS, etc., which may break
confenv = env.Copy()
configure = confenv.Configure(custom_tests = {'CheckXineLib' : CheckXineLib, 'CheckKdeLibs' : CheckKdeLibs}, log_file='configure.log')
confenv.AppendUnique(LIBS = 'qt-mt')
confenv.AppendUnique(LINKFLAGS = '-L/usr/X11R6/lib')
if not configure.CheckKdeLibs():
print # 1 2 3 4 5 6 7 8'
print 'Configure could not detect KDElibs 3.3, which is required for Codeine to '
print 'compile.'
print
confenv.Exit( 1 )
if not configure.CheckLibWithHeader( 'xine', 'xine.h', 'c++' ):
print # 1 2 3 4 5 6 7 8'
print 'Configure could not find either the xine library or header on your system. You '
print 'should ammend the relevant paths. If you know which ones please email me so I '
print 'can update this message!'
print
confenv.Exit( 2 )
if not configure.CheckXineLib():
print # 1 2 3 4 5 6 7 8'
print 'Your xine-lib is either too old, or can not be linked against. Sorry for not '
print 'being more specific..'
print
confenv.Exit( 3 )
if not configure.CheckLibWithHeader( 'Xtst', 'X11/extensions/XTest.h', 'c' ):
print # 1 2 3 4 5 6 7 8'
print 'libxtst was not found, this means the screensaver cannot be disabled during '
print 'playback. YOU CAN STILL BUILD CODEINE! :)'
print
file = open ( 'src/configure.h', 'w' )
file.write( "#define NO_XTEST_EXTENSION\n" )
file.close()
else:
# FIXME - thus only one thing can be in configure.h - lol
file = open ( 'src/configure.h', 'w' )
file.write( "" )
file.close()
env = configure.Finish()

@ -0,0 +1,95 @@
## Thomas Nagy, 2005
"""
Detect and store the most common options
* kdecxxflags : debug=1 (-g) or debug=full (-g3, slower)
else use the user CXXFLAGS if any, - or -O2 by default
* prefix : the installation path
* extraincludes : a list of paths separated by ':'
ie: scons configure debug=full prefix=/usr/local extraincludes=/tmp/include:/usr/local
"""
BOLD ="\033[1m"
RED ="\033[91m"
GREEN ="\033[92m"
YELLOW ="\033[93m"
CYAN ="\033[96m"
NORMAL ="\033[0m"
import os
def exists(env):
return true
def generate(env):
env.Help("""
"""+BOLD+
"""*** Generic options ***
-----------------------"""+NORMAL+"""
"""+BOLD+"""* debug """+NORMAL+""": debug=1 (-g) or debug=full (-g3, slower) else use environment CXXFLAGS, or -O2 by default
"""+BOLD+"""* prefix """+NORMAL+""": the installation path
"""+BOLD+"""* extraincludes """+NORMAL+""": a list of paths separated by ':'
ie: """+BOLD+"""scons configure debug=full prefix=/usr/local extraincludes=/tmp/include:/usr/local
"""+NORMAL)
# load the options
from SCons.Options import Options, PathOption
opts = Options('generic.cache.py')
opts.AddOptions(
( 'KDECXXFLAGS', 'debug level for the project : full or just anything' ),
( 'PREFIX', 'prefix for installation' ),
( 'EXTRAINCLUDES', 'extra include paths for the project' ),
)
opts.Update(env)
# use this to avoid an error message 'how to make target configure ?'
env.Alias('configure', None)
# configure the environment if needed
if 'configure' in env['TARGS'] or not env.has_key('KDECXXFLAGS'):
# need debugging ?
if env.has_key('KDECXXFLAGS'):
env.__delitem__('KDECXXFLAGS')
if env['ARGS'].get('debug', None):
debuglevel = env['ARGS'].get('debug', None)
print CYAN+'** Enabling debug for the project **' + NORMAL
if (debuglevel == "full"):
env['KDECXXFLAGS'] = ['-DDEBUG', '-ggdb', '-pipe', '-Wall']
else:
env['KDECXXFLAGS'] = ['-DDEBUG', '-g']
else:
if os.environ.has_key('CXXFLAGS'):
# user-defined flags (gentooers will be delighted)
import SCons.Util
env['KDECXXFLAGS'] = SCons.Util.CLVar( os.environ['CXXFLAGS'] )
env.Append( KDECXXFLAGS = ['-DNDEBUG', '-DNO_DEBUG'] )
else:
env.Append(KDECXXFLAGS = ['-O2', '-DNDEBUG', '-DNO_DEBUG'])
# user-specified prefix
if env['ARGS'].get('prefix', None):
env['PREFIX'] = env['ARGS'].get('prefix', None)
print CYAN+'** set the installation prefix for the project : ' + env['PREFIX'] +' **'+ NORMAL
elif env.has_key('PREFIX'):
env.__delitem__('PREFIX')
# user-specified include paths
env['EXTRAINCLUDES'] = env['ARGS'].get('extraincludes', None)
if env['ARGS'].get('extraincludes', None):
print CYAN+'** set extra include paths for the project : ' + env['EXTRAINCLUDES'] +' **'+ NORMAL
elif env.has_key('EXTRAINCLUDES'):
env.__delitem__('EXTRAINCLUDES')
# and finally save the options in a cache
opts.Save('generic.cache.py', env)
if env.has_key('KDECXXFLAGS'):
# load the flags
env.AppendUnique( CPPFLAGS = env['KDECXXFLAGS'] )
if env.has_key('EXTRAINCLUDES'):
incpaths = []
for dir in str(env['EXTRAINCLUDES']).split(':'):
incpaths.append( dir )
env.Append(CPPPATH = incpaths)

@ -0,0 +1,771 @@
# Copyright (c) 2001, 2002, 2003, 2004 The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
# Shamelessly stolen from qt.py and (heavily) modified into kde.py :)
# Thomas Nagy, 2004, 2005 <tnagy2^8@yahoo.fr>
"""
Here follow the basic rules for building kde programs
The detection is done in detect_kde when needed
We wan to use the cached variables as much as possible
The variables used when configuring are :
* prefix : base install path, eg: /usr/local
* execprefix : install path for binaries, eg: /usr/bin
* datadir : install path for the data, eg: /usr/local/share
* libdir : install path for the libs, eg: /usr/lib
* libsuffix : for those who need /usr/lib64 and the like ..
* kdeincludes: path to the kde includes (/usr/include/kde on debian, ...)
* qtincludes : same punishment, for qt includes (/usr/include/qt on debian, ...)
* kdelibs : path to the kde libs, for linking the programs
* qtlibs : same punishment, for qt libraries
eg: scons configure libdir=/usr/local/lib qtincludes=/usr/include/qt
"""
BOLD ="\033[1m"
RED ="\033[91m"
GREEN ="\033[92m"
YELLOW ="\033[93m"
CYAN ="\033[96m"
NORMAL ="\033[0m"
def exists(env):
return True
def detect_kde(env):
""" Detect the qt and kde environment using kde-config mostly """
import os, sys, re
prefix = env['ARGS'].get('prefix', None)
execprefix = env['ARGS'].get('execprefix', None)
datadir = env['ARGS'].get('datadir', None)
libdir = env['ARGS'].get('libdir', None)
libsuffix = env['ARGS'].get('libsuffix', '')
kdeincludes = env['ARGS'].get('kdeincludes', None)
kdelibs = env['ARGS'].get('kdelibs', None)
qtincludes = env['ARGS'].get('qtincludes', None)
qtlibs = env['ARGS'].get('qtlibs', None)
if libdir:
libdir = libdir+libsuffix
## Detect the kde libraries
print "Checking for kde-config : ",
kde_config = os.popen("which kde-config 2>/dev/null").read().strip()
if len(kde_config):
print GREEN + "kde-config was found" + NORMAL
else:
print RED + "kde-config was NOT found in your PATH"+ NORMAL
print "Make sure kde is installed properly"
print "(missing package kdebase-devel?)"
# TODO : prompt the user for the path of kde-config ?
sys.exit(1)
env['KDEDIR'] = os.popen('kde-config -prefix').read().strip()
print "Checking for kde version : ",
kde_version = os.popen("kde-config --version|grep KDE").read().strip().split()[1]
if int(kde_version[0]) != 3 or int(kde_version[2]) < 2:
print RED + kde_version
print RED + "Your kde version can be too old" + NORMAL
print RED + "Please make sure kde is at least 3.2" + NORMAL
else:
print GREEN + kde_version + NORMAL
## Detect the qt library
print "Checking for the qt library : ",
qtdir = os.getenv("QTDIR")
if qtdir:
print GREEN + "qt is in " + qtdir + NORMAL
else:
m = re.search('(.*)/lib/libqt.*', os.popen('ldd `kde-config --expandvars --install lib`' + '/libkdeui.so.4 | grep libqt').read().strip().split()[2])
if m:
qtdir = m.group(1)
print YELLOW + "qt was found as " + m.group(1) + NORMAL
else:
print RED + "qt was not found" + NORMAL
print RED + "Please set QTDIR first (/usr/lib/qt3?)" + NORMAL
sys.exit(1)
env['QTDIR'] = qtdir.strip()
## Find the necessary programs uic and moc
print "Checking for uic : ",
uic = qtdir + "/bin/uic"
if os.path.isfile(uic):
print GREEN + "uic was found as " + uic + NORMAL
else:
uic = os.popen("which uic 2>/dev/null").read().strip()
if len(uic):
print YELLOW + "uic was found as " + uic + NORMAL
else:
uic = os.popen("which uic 2>/dev/null").read().strip()
if len(uic):
print YELLOW + "uic was found as " + uic + NORMAL
else:
print RED + "uic was not found - set QTDIR put it in your PATH ?" + NORMAL
sys.exit(1)
env['QT_UIC'] = uic
print "Checking for moc : ",
moc = qtdir + "/bin/moc"
if os.path.isfile(moc):
print GREEN + "moc was found as " + moc + NORMAL
else:
moc = os.popen("which moc 2>/dev/null").read().strip()
if len(moc):
print YELLOW + "moc was found as " + moc + NORMAL
elif os.path.isfile("/usr/share/qt3/bin/moc"):
moc = "/usr/share/qt3/bin/moc"
print YELLOW + "moc was found as " + moc + NORMAL
else:
print RED + "moc was not found - set QTDIR or put it in your PATH ?" + NORMAL
sys.exit(1)
env['QT_MOC'] = moc
## check for the qt and kde includes
print "Checking for the qt includes : ",
if qtincludes and os.path.isfile(qtincludes + "/qlayout.h"):
# The user told where to look for and it looks valid
print GREEN + "ok " + qtincludes + NORMAL
else:
if os.path.isfile(qtdir + "/include/qlayout.h"):
# Automatic detection
print GREEN + "ok " + qtdir + "/include/ " + NORMAL
qtincludes = qtdir + "/include/"
elif os.path.isfile("/usr/include/qt3/qlayout.h"):
# Debian probably
print YELLOW + "the qt headers were found in /usr/include/qt3/ " + NORMAL
qtincludes = "/usr/include/qt3"
else:
print RED + "the qt headers were not found" + NORMAL
sys.exit(1)
print "Checking for the kde includes : ",
kdeprefix = os.popen("kde-config --prefix").read().strip()
if not kdeincludes:
kdeincludes = kdeprefix+"/include/"
if os.path.isfile(kdeincludes + "/klineedit.h"):
print GREEN + "ok " + kdeincludes + NORMAL
else:
if os.path.isfile(kdeprefix+"/include/kde/klineedit.h"):
# Debian, Fedora probably
print YELLOW + "the kde headers were found in " + kdeprefix + "/include/kde/" + NORMAL
kdeincludes = kdeprefix + "/include/kde/"
else:
print RED + "The kde includes were NOT found" + NORMAL
sys.exit(1)
if prefix:
## use the user-specified prefix
if not execprefix:
execprefix = prefix
if not datadir:
datadir = prefix + "/share"
if not libdir:
libdir = execprefix + "/lib"+libsuffix
subst_vars = lambda x: x.replace('${exec_prefix}', execprefix).replace('${datadir}',
datadir).replace('${libdir}', libdir)
debian_fix = lambda x: x.replace('/usr/share', '${datadir}')
env['KDEBIN'] = subst_vars(os.popen('kde-config --install exe').read().strip())
env['KDEAPPS'] = subst_vars(os.popen('kde-config --install apps').read().strip())
env['KDEDATA'] = subst_vars(os.popen('kde-config --install data').read().strip())
env['KDEMODULE']= subst_vars(os.popen('kde-config --install module').read().strip())
env['KDELOCALE']= subst_vars(os.popen('kde-config --install locale').read().strip())
env['KDEDOC'] = subst_vars( debian_fix(os.popen('kde-config --install html').read().strip()) )
env['KDEKCFG'] = subst_vars(os.popen('kde-config --install kcfg').read().strip())
env['KDEXDG'] = subst_vars(os.popen('kde-config --install xdgdata-apps').read().strip())
env['KDEMENU'] = subst_vars(os.popen('kde-config --install apps').read().strip())
env['KDEMIME'] = subst_vars(os.popen('kde-config --install mime').read().strip())
env['KDEICONS'] = subst_vars(os.popen('kde-config --install icon').read().strip())
env['KDESERV'] = subst_vars(os.popen('kde-config --install services').read().strip())
else:
# the user has given no prefix, install as a normal kde app
env['PREFIX'] = os.popen('kde-config --prefix').read().strip()
env['KDEBIN'] = os.popen('kde-config --expandvars --install exe').read().strip()
env['KDEAPPS'] = os.popen('kde-config --expandvars --install apps').read().strip()
env['KDEDATA'] = os.popen('kde-config --expandvars --install data').read().strip()
env['KDEMODULE']= os.popen('kde-config --expandvars --install module').read().strip()
env['KDELOCALE']= os.popen('kde-config --expandvars --install locale').read().strip()
env['KDEDOC'] = os.popen('kde-config --expandvars --install html').read().strip()
env['KDEKCFG'] = os.popen('kde-config --expandvars --install kcfg').read().strip()
env['KDEXDG'] = os.popen('kde-config --expandvars --install xdgdata-apps').read().strip()
env['KDEMENU'] = os.popen('kde-config --expandvars --install apps').read().strip()
env['KDEMIME'] = os.popen('kde-config --expandvars --install mime').read().strip()
env['KDEICONS'] = os.popen('kde-config --expandvars --install icon').read().strip()
env['KDESERV'] = os.popen('kde-config --expandvars --install services').read().strip()
env['QTPLUGINS']=os.popen('kde-config --expandvars --install qtplugins').read().strip()
## kde libs and includes
env['KDEINCLUDEPATH']= kdeincludes
if not kdelibs:
kdelibs = os.popen('kde-config --expandvars --install lib').read().strip()
env['KDELIBPATH']= kdelibs
## qt libs and includes
env['QTINCLUDEPATH']= qtincludes
if not qtlibs:
qtlibs = qtdir+ "/lib"
env['QTLIBPATH']= qtlibs
def generate(env):
""""Set up the qt and kde environment and builders - the moc part is difficult to understand """
env.Help("""
"""+BOLD+
"""*** KDE options ***
-------------------"""
+NORMAL+"""
"""+BOLD+"""* prefix """+NORMAL+""": base install path, ie: /usr/local
"""+BOLD+"""* execprefix """+NORMAL+""": install path for binaries, ie: /usr/bin
"""+BOLD+"""* datadir """+NORMAL+""": install path for the data, ie: /usr/local/share
"""+BOLD+"""* libdir """+NORMAL+""": install path for the libs, ie: /usr/lib
"""+BOLD+"""* libsuffix """+NORMAL+""": suffix of libraries on amd64, ie: 64, 32
"""+BOLD+"""* kdeincludes"""+NORMAL+""": path to the kde includes (/usr/include/kde on debian, ...)
"""+BOLD+"""* qtincludes """+NORMAL+""": same punishment, for qt includes (/usr/include/qt on debian, ...)
"""+BOLD+"""* kdelibs """+NORMAL+""": path to the kde libs, for linking the programs
"""+BOLD+"""* qtlibs """+NORMAL+""": same punishment, for qt libraries
ie: """+BOLD+"""scons configure libdir=/usr/local/lib qtincludes=/usr/include/qt
"""+NORMAL)
import os.path
import re
import SCons.Defaults
import SCons.Tool
import SCons.Util
ui_extensions = [".ui", ".Ui", ".UI"]
header_extensions = [".h", ".hxx", ".hpp", ".hh", ".H", ".HH"]
source_extensions = [".cpp", ".cxx", ".cc", ".CPP", ".CXX", ".CC"]
def find_file(filename, paths, node_factory):
retval = None
for dir in paths:
node = node_factory(filename, dir)
if node.rexists():
return node
return None
class _Metasources:
""" Callable class, which works as an emitter for Programs, SharedLibraries
and StaticLibraries."""
def __init__(self, objBuilderName):
self.objBuilderName = objBuilderName
def __call__(self, target, source, env):
""" Smart autoscan function. Gets the list of objects for the Program
or Lib. Adds objects and builders for the special qt files. """
try:
if int(env.subst('$QT_AUTOSCAN')) == 0:
return target, source
except ValueError:
pass
try:
qtdebug = int(env.subst('$QT_DEBUG'))
except ValueError:
qtdebug = 0
# some shortcuts used in the scanner
FS = SCons.Node.FS.default_fs
splitext = SCons.Util.splitext
objBuilder = getattr(env, self.objBuilderName)
# some regular expressions:
# Q_OBJECT detection
q_object_search = re.compile(r'[^A-Za-z0-9]Q_OBJECT[^A-Za-z0-9]')
# cxx and c comment 'eater'
#comment = re.compile(r'(//.*)|(/\*(([^*])|(\*[^/]))*\*/)')
# CW: something must be wrong with the regexp. See also bug #998222
# CURRENTLY THERE IS NO TEST CASE FOR THAT
# The following is kind of hacky to get builders working properly (FIXME)
objBuilderEnv = objBuilder.env
objBuilder.env = env
mocBuilderEnv = env.Moc.env
env.Moc.env = env
# make a deep copy for the result; MocH objects will be appended
out_sources = source[:]
for obj in source:
if not obj.has_builder():
# binary obj file provided
if qtdebug:
print "scons: qt: '%s' seems to be a binary. Discarded." % str(obj)
continue
cpp = obj.sources[0]
if not splitext(str(cpp))[1] in source_extensions:
if qtdebug:
print "scons: qt: '%s' is no cxx file. Discarded." % str(cpp)
# c or fortran source
continue
#cpp_contents = comment.sub('', cpp.get_contents())
cpp_contents = cpp.get_contents()
h = None
ui = None
for ui_ext in ui_extensions:
# try to find the ui file in the corresponding source directory
uiname = splitext(cpp.name)[0] + ui_ext
ui = find_file(uiname, (cpp.get_dir(),), FS.File)
if ui:
if qtdebug:
print "scons: qt: found .ui file of header" #% (str(h), str(cpp))
#h_contents = comment.sub('', h.get_contents())
break
# if we have a .ui file, do not continue, it is automatically handled by Uic
if ui:
continue
for h_ext in header_extensions:
# try to find the header file in the corresponding source
# directory
hname = splitext(cpp.name)[0] + h_ext
h = find_file(hname, (cpp.get_dir(),), FS.File)
if h:
if qtdebug:
print "scons: qt: Scanning '%s' (header of '%s')" % (str(h), str(cpp))
#h_contents = comment.sub('', h.get_contents())
h_contents = h.get_contents()
break
if not h and qtdebug:
print "scons: qt: no header for '%s'." % (str(cpp))
if h and q_object_search.search(h_contents):
# h file with the Q_OBJECT macro found -> add .moc or _moc.cpp file
moc_cpp = None
if env.has_key('NOMOCSCAN'):
moc_cpp = env.Moc(h)
else:
reg = '\n\s*#include\s+"'+splitext(cpp.name)[0]+'.moc"'
meta_object_search = re.compile(reg)
if meta_object_search.search(cpp_contents):
moc_cpp = env.Moc(h)
else:
moc_cpp = env.Moccpp(h)
moc_o = objBuilder(moc_cpp)
out_sources.append(moc_o)
if qtdebug:
print "scons: qt: found Q_OBJECT macro in '%s', moc'ing to '%s'" % (str(h), str(moc_cpp[0]))
if cpp and q_object_search.search(cpp_contents):
print "error, bksys cannot handle cpp files with Q_OBJECT classes"
print "if you are sure this is a feature worth the effort, "
print "report this to the authors tnagyemail-mail yahoo.fr"
# restore the original env attributes (FIXME)
objBuilder.env = objBuilderEnv
env.Moc.env = mocBuilderEnv
return (target, out_sources)
MetasourcesShared = _Metasources('SharedObject')
MetasourcesStatic = _Metasources('StaticObject')
CLVar = SCons.Util.CLVar
splitext = SCons.Util.splitext
Builder = SCons.Builder.Builder
# Detect the environment - replaces ./configure implicitely
# and store the options into a cache
from SCons.Options import Options
opts = Options('kde.cache.py')
opts.AddOptions(
( 'QTDIR', 'root of qt directory' ),
( 'QTLIBPATH', 'path to the qt libraries' ),
( 'QTINCLUDEPATH', 'path to the qt includes' ),
( 'QT_UIC', 'moc directory'),
( 'QT_MOC', 'moc executable command'),
( 'QTPLUGINS', 'uic executable command'),
( 'KDEDIR', 'root of kde directory' ),
( 'KDELIBPATH', 'path to the kde libs' ),
( 'KDEINCLUDEPATH', 'path to the kde includes' ),
( 'PREFIX', 'root of the program installation'),
( 'KDEBIN', 'installation path of the kde binaries'),
( 'KDEMODULE', 'installation path of the parts and libs'),
( 'KDEAPPS', ''),
( 'KDEDATA', 'installation path of the application data'),
( 'KDELOCALE', ''),
( 'KDEDOC', 'installation path of the application documentation'),
( 'KDEKCFG', 'installation path of the .kcfg files'),
( 'KDEXDG', 'installation path of the service types'),
( 'KDEMENU', ''),
( 'KDEMIME', 'installation path of to the mimetypes'),
( 'KDEICONS', ''),
( 'KDESERV', ''),
)
opts.Update(env)
# reconfigure when things are missing
if 'configure' in env['TARGS'] or not env.has_key('QTDIR') or not env.has_key('KDEDIR'):
detect_kde(env)
# finally save the configuration
opts.Save('kde.cache.py', env)
## set default variables, one can override them in sconscript files
env.Append(CXXFLAGS = ['-I'+env['KDEINCLUDEPATH'], '-I'+env['QTINCLUDEPATH'] ])
env.Append(LIBPATH = [env['KDELIBPATH'], env['QTLIBPATH'] ])
env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1
env['QT_AUTOSCAN'] = 1
env['QT_DEBUG'] = 0
env['QT_UIC_HFLAGS'] = '-L $QTPLUGINS -nounload'
env['QT_UIC_CFLAGS'] = '$QT_UIC_HFLAGS -tr tr2i18n'
env['QT_LIBS'] = 'qt-mt'
env['LIBTOOL_FLAGS'] = '--silent --mode=compile --tag=CXX'
env['QT_UICIMPLPREFIX'] = ''
env['QT_UICIMPLSUFFIX'] = '.cpp'
env['QT_MOCHPREFIX'] = ''
env['QT_MOCHSUFFIX'] = '.moc'
env['KDE_KCFG_IMPLPREFIX'] = ''
env['KDE_KCFG_IMPL_HSUFFIX'] = '.h'
env['KDE_KCFG_IMPL_CSUFFIX'] = '.cpp'
env['KDE_SKEL_IMPL_SUFFIX'] = '.skel'
env['MEINPROC'] = 'meinproc'
env['MSGFMT'] = 'msgfmt'
###### ui file processing
def uicGenerator(target, source, env, for_signature):
act=[]
act.append('$QT_UIC $QT_UIC_HFLAGS -o '+target[0].path+' '+source[0].path)
act.append('rm -f ' +target[1].path)
act.append('echo \'#include <klocale.h>\' >> '+target[1].path)
act.append('echo \'#include <kdialog.h>\' >> '+target[1].path)
act.append('$QT_UIC $QT_UIC_CFLAGS -impl '+target[0].path+' -o '+target[1].path+'.tmp '+source[0].path)
act.append('cat '+target[1].path+'.tmp >> '+target[1].path)
act.append('rm -f '+target[1].path+'.tmp')
act.append('echo \'#include "' + target[2].name + '"\' >> '+target[1].path)
act.append('$QT_MOC -o '+target[2].path+' '+target[0].path)
return act
def uicEmitter(target, source, env):
adjustixes = SCons.Util.adjustixes
bs = SCons.Util.splitext(str(source[0].name))[0]
bs = os.path.join(str(target[0].get_dir()),bs)
# first target is automatically added by builder (.h file)
if len(target) < 2:
# second target is .cpp file
target.append(adjustixes(bs,
env.subst('$QT_UICIMPLPREFIX'),
env.subst('$QT_UICIMPLSUFFIX')))
if len(target) < 3:
# third target is .moc file
target.append(adjustixes(bs,
env.subst('$QT_MOCHPREFIX'),
env.subst('$QT_MOCHSUFFIX')))
return target, source
UIC_BUILDER = Builder(
generator = uicGenerator,
emitter = uicEmitter,
suffix = '.h',
src_suffix = '.ui' )
###### moc file processing
env['QT_MOCCOM'] = ('$QT_MOC -o ${TARGETS[0]} $SOURCE')
MOC_BUILDER = Builder(
action = '$QT_MOCCOM',
suffix = '.moc',
src_suffix = '.h' )
MOCCPP_BUILDER = Builder(
action = '$QT_MOCCOM',
suffix = '_moc.cpp',
src_suffix = '.h' )
###### kcfg file processing
def kcfgGenerator(target, source, env, for_signature):
act=[]
act.append('kconfig_compiler -d'+str(source[0].get_dir())+' '+source[1].path+' '+source[0].path)
return act
def kcfgEmitter(target, source, env):
adjustixes = SCons.Util.adjustixes
bs = SCons.Util.splitext(str(source[0].name))[0]
bs = os.path.join(str(target[0].get_dir()),bs)
# first target is automatically added by builder (.h file)
if len(target) < 2:
# second target is .cpp file
target.append(adjustixes(bs, env.subst('$KDE_KCFG_IMPLPREFIX'), env.subst('$KDE_KCFG_IMPL_CSUFFIX')))
# find_file(kcfgfile, (source[0].get_dir(),) ,SCons.Node.FS.default_fs)
if len(source) <2:
if not os.path.isfile(str(source[0])):
print RED+'kcfg file given'+str(source[0])+' does not exist !'+NORMAL
return target, source
kfcgfilename = ""
kcfgFileDeclRx = re.compile("^[fF]ile\s*=\s*(.+)\s*$")
for line in file(str(source[0]), "r").readlines():
match = kcfgFileDeclRx.match(line.strip())
if match:
kcfgfilename = match.group(1)
break
source.append( str(source[0].get_dir())+'/'+kcfgfilename )
return target, source
KCFG_BUILDER = Builder(
generator = kcfgGenerator,
emitter = kcfgEmitter,
suffix = '.h',
src_suffix = '.kcfgc' )
###### dcop processing
def dcopGenerator(target, source, env, for_signature):
act=[]
act.append('dcopidl '+source[0].path+' > '+target[1].path+'|| ( rm -f '+target[1].path+' ; false )')
act.append('dcopidl2cpp --c++-suffix cpp --no-signals --no-stub '+target[1].path)
return act
def dcopEmitter(target, source, env):
bs = SCons.Util.splitext(str(source[0].name))[0]
bs = os.path.join(str(target[0].get_dir()),bs)
target.append(bs+'.kidl')
#target.append(bs+'_skel.cpp')
return target, source
DCOP_BUILDER = Builder(
generator = dcopGenerator,
emitter = dcopEmitter,
suffix = '_skel.cpp',
src_suffix = '.h' )
###### documentation (meinproc) processing
MEINPROC_BUILDER = Builder(
action = '$MEINPROC --check --cache $TARGET $SOURCE',
suffix = '.cache.bz2',
src_suffix = '.docbook' )
###### translation files builder
TRANSFILES_BUILDER = Builder(
action = '$MSGFMT $SOURCE -o $TARGET',
suffix = '.gmo',
src_suffix = '.po' )
###### libtool file builder
def laGenerator(target, source, env, for_signature):
act=[]
act.append('echo "dlname=\''+source[0].name+'\'" > '+target[0].path)
act.append('echo "library_names=\''+source[0].name+' '+source[0].name+' '+source[0].name+'\'" >> '+target[0].path)
act.append('echo "old_library=\'\'">> '+target[0].path)
act.append('echo "dependency_libs=\'\'">> '+target[0].path)
act.append('echo "current=0">> '+target[0].path)
act.append('echo "age=0">> '+target[0].path)
act.append('echo "revision=0">> '+target[0].path)
act.append('echo "installed=yes">> '+target[0].path)
act.append('echo "shouldnotlink=no">> '+target[0].path)
act.append('echo "dlopen=\'\'">> '+target[0].path)
act.append('echo "dlpreopen=\'\'">> '+target[0].path)
act.append('echo "libdir=\''+env['KDEMODULE']+'\'" >> '+target[0].path)
return act
LA_BUILDER = Builder(
generator = laGenerator,
suffix = '.la',
src_suffix = '.so' )
####### TODO : real libtool builder (but i hate libtool - ita)
# def libtoolGenerator(target, source, env, for_signature):
# act=[]
# act.append('libtool $LIBTOOL_FLAGS $CXX $CXXFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o '+target[0].path+' '+source[0].path)
# return act
# LIBTOOL_BUILDER = Builder(
# generator = libtoolGenerator,
# suffix = '.lo',
# src_suffix = '.cpp' )
##### register the builders
env['BUILDERS']['Uic'] = UIC_BUILDER
env['BUILDERS']['Moc'] = MOC_BUILDER
env['BUILDERS']['Moccpp'] = MOCCPP_BUILDER
env['BUILDERS']['Dcop'] = DCOP_BUILDER
env['BUILDERS']['Kcfg'] = KCFG_BUILDER
env['BUILDERS']['LaFile'] = LA_BUILDER
#env['BUILDERS']['Libtool'] = LIBTOOL_BUILDER
env['BUILDERS']['Meinproc'] = MEINPROC_BUILDER
env['BUILDERS']['Transfiles'] = TRANSFILES_BUILDER
static_obj, shared_obj = SCons.Tool.createObjBuilders(env)
static_obj.src_builder.append('Uic')
shared_obj.src_builder.append('Uic')
static_obj.src_builder.append('Kcfg')
shared_obj.src_builder.append('Kcfg')
static_obj.src_builder.append('LaFile')
shared_obj.src_builder.append('LaFile')
static_obj.src_builder.append('Meinproc')
shared_obj.src_builder.append('Meinproc')
static_obj.src_builder.append('Transfiles')
shared_obj.src_builder.append('Transfiles')
## find the files to moc, dcop, and link against kde and qt
env.AppendUnique(PROGEMITTER = [MetasourcesStatic], SHLIBEMITTER=[MetasourcesShared], LIBEMITTER =[MetasourcesStatic])
###########################################
## Handy helpers for building kde programs
## You should not have to modify them ..
import SCons.Util
skel_ext = [".skel", ".SKEL"]
def KDEfiles(target, source, env):
"""
Returns a list of files for scons (handles kde tricks like .skel)
It also makes custom checks against double includes like : ['file.ui', 'file.cpp']
(file.cpp is already included because of file.ui)
"""
src=[]
ui_files=[]
kcfg_files=[]
skel_files=[]
other_files=[]
# For each file, check wether it is a dcop file or not, and create the complete list of sources
for file in source:
bs = SCons.Util.splitext(file)[0]
ext = SCons.Util.splitext(file)[1]
if ext in skel_ext:
env.Dcop(bs+'.h')
src.append(bs+'_skel.cpp')
else:
src.append(file)
if ext == '.ui':
ui_files.append(bs)
elif ext == '.kcfgc':
kcfg_files.append(bs)
elif ext == '.skel':
skel_files.append(bs)
else:
other_files.append(bs)
# Now check against newbie errors
for file in ui_files:
for ofile in other_files:
if ofile == file:
print RED+"WARNING: You have included "+file+".ui and another file of the same prefix"+NORMAL
print "Files generated by uic (file.h, file.cpp must not be included"
for file in kcfg_files:
for ofile in other_files:
if ofile == file:
print RED+"WARNING: You have included "+file+".kcfg and another file of the same prefix"+NORMAL
print "Files generated by kconfig_compiler (settings.h, settings.cpp) must not be included"
#for file in skel_files:
# for ofile in other_files:
# if ofile == file:
# print RED+"WARNING: source contain "+file+".skel and another file of the same prefix"+NORMAL
# print "Files generated automatically from .skel file must not be included (file.h, file.idl)"
return src
# Special trick for installing rpms ...
env['DESTDIR']=''
if 'install' in env['TARGS'] and os.environ.has_key('DESTDIR'):
env['DESTDIR']=os.environ['DESTDIR']+'/'
print CYAN+'** Enabling DESTDIR for the project **' + NORMAL + env['DESTDIR']
def KDEinstall(path, file, lenv):
""" Quick wrapper """
if 'install' in lenv['TARGS']:
lenv.Alias('install', lenv.Install( lenv['DESTDIR']+path, file ) )
def KDEinstallas(destfile, file, lenv):
""" Quick wrapper """
if 'install' in lenv['TARGS']:
lenv.Alias('install', lenv.InstallAs( lenv['DESTDIR']+destfile, file ) )
def KDEprogram(target, source, lenv):
""" Makes a kde program
The program is installed except if one sets env['NOAUTOINSTALL'] """
src = KDEfiles(target, source, lenv)
lenv.Program(target, src)
if not lenv.has_key('NOAUTOINSTALL'):
KDEinstall(env['KDEBIN'], target, lenv)
def KDEshlib(target, source, lenv):
""" Makes a shared library for kde (.la file for klibloader)
The library is installed except if one sets env['NOAUTOINSTALL'] """
src = KDEfiles(target, source, lenv)
lenv.SharedLibrary( target, src )
lenv.LaFile( target, target+'.so' )
if not lenv.has_key('NOAUTOINSTALL'):
KDEinstall(env['KDEMODULE'], target+'.so', lenv)
KDEinstall(env['KDEMODULE'], target+'.la', lenv)
def KDEstaticlib(target, source, lenv):
""" Makes a static library for kde - in practice you should not use static libraries
1. they take more memory than shared ones
2. makefile.am needed it because of stupid limitations
(cannot handle sources in separate folders - takes extra processing) """
src = KDEfiles(target, source, lenv)
lenv.StaticLibrary( target, src )
# # do not install static libraries
# if not lenv.has_key('NOAUTOINSTALL'):
# KDEinstall(env['KDEMODULE'], target+'.a', lenv)
def KDEaddlibs(libs, lenv):
""" Helper function """
lenv.AppendUnique(LIBS = libs)
def KDEaddpaths(paths, lenv):
""" Helper function """
lenv.AppendUnique(CPPPATH = paths)
def KDElang(transfiles, lenv):
""" Process translations (.po files) in a po/ dir """
if not lenv['APPNAME']:
print "define lenv['APPNAME'] before using KDElang !!"
return
for lang in transfiles:
lenv.Transfiles( lang+'.po' )
KDEinstallas( lenv['KDELOCALE']+'/'+lang+'/LC_MESSAGES/'+lenv['APPNAME']+'.mo',
lang+'.gmo', lenv )
def KDEdoc(lang, file, lenv):
""" Install the documentation """
if not lenv['APPNAME']:
print "define lenv['APPNAME'] before using KDEdoc !!"
env.Exit(1)
KDEinstall( lenv['KDEDOC']+'/'+lang+'/'+lenv['APPNAME'], file, lenv )
# Export variables so that sconscripts in subdirectories can use them
env.Export("KDEprogram KDEshlib KDEaddpaths KDEaddlibs KDEinstall KDEinstallas KDElang KDEdoc")

Binary file not shown.

@ -0,0 +1,13 @@
================================================================================
FAQ
================================================================================
This FAQ regards the source code and its layout.
Q: Why reimplement code in the part rather than consolidate it with the app?
A: Mainly to make the application start faster, loading a part is not free. But
also to make the part start faster as it has much lower requirements.
I admit the maintainance consequences aren't pretty, but the part has very
little code, so theoretically it should be easy to keep bug fixes in sync.
=========1=========2=========3=========4=========5=========6=========7=========8

@ -0,0 +1,21 @@
############################
## load the config
## Use the environment and the tools set in the top-level
## SConstruct file (set with 'Export') - this is very important
Import( '*' )
myenv=env.Copy()
myenv.SConscript( dirs = Split( "app part") )
KDEinstall( env['KDEDATA']+'/codeine', '../misc/codeineui.rc', myenv )
KDEinstall( env['KDEDATA']+'/konqueror/servicemenus', '../misc/codeine_play_dvd.desktop', myenv )
KDEinstall( env['KDESERV'], '../misc/codeine_part.desktop', myenv )
KDEinstall( env['KDEXDG'], '../misc/codeine.desktop', myenv )
for size in ['16', '22', '32', '48', '64', '128']:
KDEinstallas( env['KDEICONS']+'/crystalsvg/'+size+'x'+size+'/apps/codeine.png', '../misc/cr'+size+'-app-codeine.png', myenv )
#print env['KDECXXFLAGS']

@ -0,0 +1,38 @@
1.0.1
Volume state saving
1.0.x
Improve error messages and error handling
Consolidate error handling code in part and application so we don't diverge
No audio icon in analyzer when no audio
Playback finished message (amaroK statusbar code?)
Mouse move to show toolbar in fullscreen mode
People are likely to use "F" keyboard shortcut or escape after film ends in fullscreen mode
but the popup of the menu prevents this, as for some reason Qt stops handling keyboard
shortcuts. We need a solution. The escape issue makes the popup menu less useful. So
escape with the menu should exit fullscreen too. All shortcuts in the menu should work.
Shortcuts not in the menu shouldn't work as this is a modal menu! Could be dangerous.
Consider adding quit to the fullscreen context menu always, or make the menubar show with
toolbar when mouse moves.
Add tooltips to PlayMedia dialog for recent file listview entries (directory info etc.)
Show zoom percentage in statusbar/OSD when resizing window, snap to 100%?
More feedback at playback end
Better loading/buffering status/feedback
Save prettyTitle with url and show that in recent file list,
- save m3u's? <-- needs thought
- mark remote files?
Support .srt files like Kaffeine/xine-ui does, ask berkus if stuck.
DVD fullscreen, DVD menu shows or something
1.1
Volume button (not there by default)
Two entries in Konqueror: 1. open in new window 2. open in current window
Play from begin scroll up popup when resuming playback
Much better DVD support
Show length information in normal window
'o' in fullscreen mode shows OSD of length and elapsed time info, <-- emulate mplayer
ACTION
xine config dialog is modal
REACTION
none, at least yet, it is far easier to maintain modal dialogs

@ -0,0 +1,59 @@
############################
## load the config
## Use the environment and the tools set in the top-level
## SConstruct file (set with 'Export') - this is very important
Import( '*' )
myenv=env.Copy()
#############################
## the programs to build
# we put the stuff that could fail due to bad xine.h locations, etc. at the beginning
# so if the build fails the user knows quickly
app_sources = Split("""
xineEngine.cpp
xineConfig.cpp
xineScope.c
theStream.cpp
videoWindow.cpp
videoSettings.cpp
captureFrame.cpp
actions.cpp
stateChange.cpp
slider.cpp
analyzer.cpp
playDialog.cpp
listView.cpp
adjustSizeButton.cpp
fullScreenAction.cpp
insertAspectRatioMenuItems.cpp
playlistFile.cpp
volumeAction.cpp
../mxcl.library.cpp
main.cpp
mainWindow.cpp""")
KDEprogram( "codeine", app_sources, myenv )
############################
## Customization
## Additional include paths for compiling the source files
## Always add '../' (top-level directory) because moc makes code that needs it
KDEaddpaths( ['./', '../', '../../'], myenv )
## Necessary libraries to link against
KDEaddlibs( ['qt-mt', 'kio', 'kdecore', 'kdeui', 'xine', 'Xtst'], myenv )
## This shows how to add other link flags to the program
myenv['LINKFLAGS'].append('-L/usr/X11R6/lib')
## If you are using QThread, add this line
# myenv.AppendUnique( CPPFLAGS = ['-DQT_THREAD_SUPPORT'] )

@ -0,0 +1,27 @@
// Copyright 2005 Max Howell (max.howell@methylblue.com)
// See COPYING file for licensing information
#include "actions.h"
#include "debug.h"
#include "mxcl.library.h"
#include <qtoolbutton.h>
#include "xineEngine.h"
namespace Codeine
{
PlayAction::PlayAction( QObject *receiver, const char *slot, KActionCollection *ac )
: KToggleAction( i18n("Play"), "player_play", Qt::Key_Space, receiver, slot, ac, "play" )
{}
void
PlayAction::setChecked( bool b )
{
if( videoWindow()->state() == Engine::Empty && sender() && QCString(sender()->className()) == "KToolBarButton" ) {
// clicking play when empty means open PlayMediaDialog, but we have to uncheck the toolbar button
// as KDElibs sets that checked automatically..
((QToolButton*)sender())->setOn( false );
}
else
KToggleAction::setChecked( b );
}
}

@ -0,0 +1,26 @@
// (c) 2004 Max Howell (max.howell@methylblue.com)
// See COPYING file for licensing information
#ifndef CODEINEACTIONS_H
#define CODEINEACTIONS_H
#include <kactionclasses.h> //baseclass
#include <kactioncollection.h> //convenience
namespace Codeine
{
KActionCollection *actionCollection(); ///defined in mainWindow.cpp
KAction *action( const char* ); ///defined in mainWindow.cpp
inline KToggleAction *toggleAction( const char *name ) { return (KToggleAction*)action( name ); }
class PlayAction : public KToggleAction
{
public:
PlayAction( QObject *receiver, const char *slot, KActionCollection* );
protected:
virtual void setChecked( bool );
};
}
#endif

@ -0,0 +1,125 @@
// (C) 2005 Max Howell (max.howell@methylblue.com)
// See COPYING file for licensing information
#include "adjustSizeButton.h"
#include "extern.h"
#include <kpushbutton.h>
#include <qapplication.h>
#include <qevent.h>
#include <qlabel.h>
#include <qlayout.h>
#include <qpainter.h>
#include "theStream.h"
#include "xineEngine.h" //videoWindow()
QString i18n( const char *text );
namespace Codeine
{
AdjustSizeButton::AdjustSizeButton( QWidget *parent )
: QFrame( parent )
, m_counter( 0 )
, m_stage( 1 )
, m_offset( 0 )
{
parent->installEventFilter( this );
setPalette( QApplication::palette() ); //videoWindow has different palette
setFrameStyle( QFrame::Plain | QFrame::Box );
m_preferred = new KPushButton( KGuiItem( i18n("Preferred Scale"), "viewmag" ), this );
connect( m_preferred, SIGNAL(clicked()), qApp->mainWidget(), SLOT(adjustSize()) );
connect( m_preferred, SIGNAL(clicked()), SLOT(deleteLater()) );
m_oneToOne = new KPushButton( KGuiItem( i18n("Scale 100%"), "viewmag1" ), this );
connect( m_oneToOne, SIGNAL(clicked()), (QObject*)videoWindow(), SLOT(resetZoom()) );
connect( m_oneToOne, SIGNAL(clicked()), SLOT(deleteLater()) );
QBoxLayout *hbox = new QHBoxLayout( this, 8, 6 );
QBoxLayout *vbox = new QVBoxLayout( hbox );
vbox->addWidget( new QLabel( i18n( "<b>Adjust video scale?" ), this ) );
vbox->addWidget( m_preferred );
vbox->addWidget( m_oneToOne );
hbox->addWidget( m_thingy = new QFrame( this ) );
m_thingy->setFixedWidth( fontMetrics().width( "X" ) );
m_thingy->setFrameStyle( QFrame::Plain | QFrame::Box );
m_thingy->setPaletteForegroundColor( paletteBackgroundColor().dark() );
QEvent e( QEvent::Resize );
eventFilter( 0, &e );
adjustSize();
show();
m_timerId = startTimer( 5 );
}
void
AdjustSizeButton::timerEvent( QTimerEvent* )
{
QFrame *&h = m_thingy;
switch( m_stage )
{
case 1: //raise
move();
m_offset++;
if( m_offset > height() )
killTimer( m_timerId ),
m_timerId = startTimer( 40 ),
m_stage = 2;
break;
case 2: //fill in pause timer bar
if( m_counter < h->height() - 3 )
QPainter( h ).fillRect( 2, 2, h->width() - 4, m_counter, palette().active().highlight() );
if( !hasMouse() )
m_counter++;
if( m_counter > h->height() + 5 ) //pause for 360ms before lowering
m_stage = 3,
killTimer( m_timerId ),
m_timerId = startTimer( 6 );
break;
case 3: //lower
if( hasMouse() ) {
m_stage = 1;
m_counter = 0;
m_thingy->repaint();
break; }
m_offset--;
move();
if( m_offset < 0 )
deleteLater();
}
}
bool
AdjustSizeButton::eventFilter( QObject *o, QEvent *e )
{
if( e->type() == QEvent::Resize ) {
const QSize preferredSize = TheStream::profile()->readSizeEntry( "Preferred Size" );
const QSize defaultSize = TheStream::defaultVideoSize();
const QSize parentSize = parentWidget()->size();
m_preferred->setEnabled( preferredSize.isValid() && parentSize != preferredSize && defaultSize != preferredSize );
m_oneToOne->setEnabled( defaultSize != parentSize );
move();
if( !m_preferred->isEnabled() && !m_oneToOne->isEnabled() && m_counter == 0 )
deleteLater();
}
return false;
}
}

@ -0,0 +1,37 @@
// (C) 2005 Max Howell (max.howell@methylblue.com)
// See COPYING file for licensing information
#ifndef CODEINE_ADJUST_SIZE_BUTTON_H
#define CODEINE_ADJUST_SIZE_BUTTON_H
#include <qframe.h>
namespace Codeine
{
class AdjustSizeButton : public QFrame
{
int m_counter;
int m_stage;
int m_offset;
int m_timerId;
QWidget *m_preferred;
QWidget *m_oneToOne;
QFrame *m_thingy;
public:
AdjustSizeButton( QWidget *parent );
private:
virtual void timerEvent( QTimerEvent* );
virtual bool eventFilter( QObject*, QEvent* );
inline void move()
{
QWidget::move( parentWidget()->width() - width(), parentWidget()->height() - m_offset );
}
};
}
#endif

@ -0,0 +1,131 @@
// (c) 2004 Max Howell (max.howell@methylblue.com)
// See COPYING file for licensing information
#include "analyzer.h"
#include "codeine.h"
#include "debug.h"
#include <math.h> //interpolate()
#include <qevent.h> //event()
#include "xineEngine.h"
#include "fht.cpp"
template<class W>
Analyzer::Base<W>::Base( QWidget *parent, uint timeout )
: W( parent, "Analyzer" )
, m_timeout( timeout )
{}
template<class W> bool
Analyzer::Base<W>::event( QEvent *e )
{
switch( e->type() ) {
case QEvent::Hide:
m_timer.stop();
break;
case QEvent::Show:
m_timer.start( timeout() );
break;
default:
;
}
return QWidget::event( e );
}
Analyzer::Base2D::Base2D( QWidget *parent, uint timeout )
: Base<QWidget>( parent, timeout )
{
setWFlags( Qt::WNoAutoErase ); //no flicker
connect( &m_timer, SIGNAL(timeout()), SLOT(draw()) );
}
void
Analyzer::Base2D::draw()
{
switch( Codeine::engine()->state() ) {
case Engine::Playing:
{
const Engine::Scope &thescope = Codeine::engine()->scope();
static Analyzer::Scope scope( Analyzer::SCOPE_SIZE );
for( int x = 0; x < Analyzer::SCOPE_SIZE; ++x )
scope[x] = double(thescope[x]) / (1<<15);
transform( scope );
analyze( scope );
scope.resize( Analyzer::SCOPE_SIZE );
bitBlt( this, 0, 0, canvas() );
break;
}
case Engine::Paused:
break;
default:
erase();
}
}
void
Analyzer::Base2D::resizeEvent( QResizeEvent* )
{
m_canvas.resize( size() );
m_canvas.fill( colorGroup().background() );
}
// Author: Max Howell <max.howell@methylblue.com>, (C) 2003
// Copyright: See COPYING file that comes with this distribution
#include <qpainter.h>
Analyzer::Block::Block( QWidget *parent )
: Analyzer::Base2D( parent, 20 )
{
setMinimumWidth( 64 ); //-1 is padding, no drawing takes place there
setMaximumWidth( 128 );
//TODO yes, do height for width
}
void
Analyzer::Block::transform( Analyzer::Scope &scope ) //pure virtual
{
static FHT fht( Analyzer::SCOPE_SIZE_EXP );
for( uint x = 0; x < scope.size(); ++x )
scope[x] *= 2;
float *front = static_cast<float*>( &scope.front() );
fht.spectrum( front );
fht.scale( front, 1.0 / 40 );
}
#include <math.h>
void
Analyzer::Block::analyze( const Analyzer::Scope &s )
{
canvas()->fill( colorGroup().foreground().light() );
QPainter p( canvas() );
p.setPen( colorGroup().background() );
const double F = double(height()) / (log10( 256 ) * 1.1 /*<- max. amplitude*/);
for( uint x = 0; x < s.size(); ++x )
//we draw the blank bit
p.drawLine( x, 0, x, int(height() - log10( s[x] * 256.0 ) * F) );
}
int
Analyzer::Block::heightForWidth( int w ) const
{
return w / 2;
}

@ -0,0 +1,75 @@
// (c) 2004 Max Howell (max.howell@methylblue.com)
// See COPYING file for licensing information
#ifndef ANALYZER_H
#define ANALYZER_H
#ifdef __FreeBSD__
#include <sys/types.h>
#endif
#include <qpixmap.h> //stack allocated and convenience
#include <qtimer.h> //stack allocated
#include <qwidget.h> //baseclass
#include <vector> //included for convenience
namespace Analyzer
{
typedef std::vector<float> Scope;
template<class W> class Base : public W
{
public:
uint timeout() const { return m_timeout; }
protected:
Base( QWidget*, uint );
virtual void transform( Scope& ) = 0;
virtual void analyze( const Scope& ) = 0;
private:
virtual bool event( QEvent* );
protected:
QTimer m_timer;
uint m_timeout;
};
class Base2D : public Base<QWidget>
{
Q_OBJECT
public:
const QPixmap *canvas() const { return &m_canvas; }
private slots:
void draw();
protected:
Base2D( QWidget*, uint timeout );
QPixmap *canvas() { return &m_canvas; }
void paintEvent( QPaintEvent* ) { if( !m_canvas.isNull() ) bitBlt( this, 0, 0, canvas() ); }
void resizeEvent( QResizeEvent* );
private:
QPixmap m_canvas;
};
class Block : public Analyzer::Base2D
{
public:
Block( QWidget* );
protected:
virtual void transform( Analyzer::Scope& );
virtual void analyze( const Analyzer::Scope& );
virtual int heightForWidth( int ) const;
virtual void show() {} //TODO temporary as the scope plugin causes freezes
};
}
#endif

@ -0,0 +1,296 @@
// (C) 2005 Max Howell (max.howell@methylblue.com)
// See COPYING file for licensing information
#include "debug.h"
#include <kfiledialog.h>
#include <kpreviewwidgetbase.h>
#include <kpushbutton.h>
#include <kstatusbar.h>
#include <kstdguiitem.h>
#include "mainWindow.h"
#include "mxcl.library.h"
#include <qdialog.h>
#include <qhbox.h>
#include <qlabel.h>
#include <qimage.h>
#include <qlayout.h>
#include <qpainter.h>
#include <qstringlist.h>
#include "theStream.h"
#include "xineEngine.h"
#include <xine.h>
namespace Codeine {
class FrameCapturePreview : public KPreviewWidgetBase
{
QImage m_frame;
virtual void showPreview( const KURL& ) {}
virtual void clearPreview() {}
virtual void paintEvent( QPaintEvent* )
{
QPainter painter( this );
const uint h = int( double(m_frame.height()) / m_frame.width() * (width()-5) );
const uint y = (height() - h) / 2;
painter.drawImage( QRect( 5, y, width(), h ), m_frame );
const QString text = QString("%1x%2").arg( m_frame.width() ).arg( m_frame.height() );
const uint x = (width() - fontMetrics().width( text ))/2;
painter.drawText( x, y + h + fontMetrics().height() + 5, text );
}
public:
FrameCapturePreview( const QImage& frame, QWidget *parent )
: KPreviewWidgetBase( parent )
, m_frame( frame )
{
setMinimumWidth( 200 );
}
};
class FrameCaptureDialog : public QDialog
{
const QImage m_frame;
const QString m_time;
const QString m_title;
void message( const QString &text ) { ((MainWindow*)parentWidget())->statusBar()->message( text, 4000 ); }
public:
FrameCaptureDialog( const QImage &frame, const QString &time, MainWindow *parent )
: QDialog( parent, 0, false /*modal*/, Qt::WDestructiveClose )
, m_frame( frame )
, m_time( time )
, m_title( TheStream::prettyTitle() )
{
(new QVBoxLayout( this ))->setAutoAdd( true );
(new QLabel( this ))->setPixmap( frame );
QHBox *box = new QHBox( this );
KPushButton *o = new KPushButton( KStdGuiItem::save(), box );
connect( o, SIGNAL(clicked()), SLOT(accept()) );
o = new KPushButton( KStdGuiItem::cancel(), box );
o->setText( i18n("Discard") );
connect( o, SIGNAL(clicked()), SLOT(reject()) );
setCaption( i18n("Capture - %1").arg( time ) );
setFixedSize( sizeHint() );
show();
//TODO don't activate
//TODO move to the parent's side - not centrally aligned
}
~FrameCaptureDialog()
{
delete [] m_frame.bits();
}
virtual void accept()
{
KFileDialog dialog( ":frame_capture", i18n("*.png|PNG Format\n*.jpeg|JPEG Format"), this, 0, false );
dialog.setOperationMode( KFileDialog::Saving );
dialog.setCaption( i18n("Save Frame") );
dialog.setSelection( m_title + " - " + m_time + ".png" );
dialog.setPreviewWidget( new FrameCapturePreview( m_frame, &dialog ) );
if( dialog.exec() == Accepted ) {
const QString fileName = dialog.selectedFile();
if( fileName.isEmpty() )
return;
const QString type = dialog.currentFilter().remove( 0, 2 ).upper();
if( m_frame.save( fileName, type ) )
message( i18n("%1 saved successfully").arg( fileName ) );
else
message( i18n("Sorry, could not save %1").arg( fileName ) );
}
deleteLater();
}
};
void
MainWindow::captureFrame()
{
new FrameCaptureDialog( videoWindow()->captureFrame(), m_timeLabel->text(), this );
}
/************************************************************
* Helpers to convert yuy and yv12 frames to rgb *
* code from gxine modified for 32bit output *
* Copyright (C) 2000-2003 the xine project *
************************************************************/
static void
yuy2Toyv12( uint8_t *y, uint8_t *u, uint8_t *v, uint8_t *input, int w, int h )
{
const int w2 = w / 2;
for( int j, i = 0; i < h; i += 2 ) {
for( j = 0; j < w2; j++ )
{
// packed YUV 422 is: Y[i] U[i] Y[i+1] V[i]
*(y++) = *(input++);
*(u++) = *(input++);
*(y++) = *(input++);
*(v++) = *(input++);
}
// down sampling
for( j = 0; j < w2; j++ ) {
// skip every second line for U and V
*(y++) = *(input++);
input++;
*(y++) = *(input++);
input++;
}
}
}
static uchar*
yv12ToRgb( uint8_t *src_y, uint8_t *src_u, uint8_t *src_v, const int w, const int h )
{
/// Create rgb data from yv12
#define clip_8_bit(val) \
{ \
if( val < 0 ) \
val = 0; \
else if( val > 255 ) \
val = 255; \
}
int y, u, v;
int r, g, b;
int sub_i_uv;
int sub_j_uv;
const int uv_width = w / 2;
const int uv_height = h / 2;
uchar * const rgb = new uchar[(w * h * 4)]; //qt needs a 32bit align
if( !rgb )
return 0;
for( int i = 0; i < h; ++i ) {
// calculate u & v rows
sub_i_uv = ((i * uv_height) / h);
for( int j = 0; j < w; ++j ) {
// calculate u & v columns
sub_j_uv = (j * uv_width) / w;
/***************************************************
*
* Colour conversion from
* http://www.inforamp.net/~poynton/notes/colour_and_gamma/ColorFAQ.html#RTFToC30
*
* Thanks to Billy Biggs <vektor@dumbterm.net>
* for the pointer and the following conversion.
*
* R' = [ 1.1644 0 1.5960 ] ([ Y' ] [ 16 ])
* G' = [ 1.1644 -0.3918 -0.8130 ] * ([ Cb ] - [ 128 ])
* B' = [ 1.1644 2.0172 0 ] ([ Cr ] [ 128 ])
*
* Where in xine the above values are represented as
*
* Y' == image->y
* Cb == image->u
* Cr == image->v
*
***************************************************/
y = src_y[(i * w) + j] - 16;
u = src_u[(sub_i_uv * uv_width) + sub_j_uv] - 128;
v = src_v[(sub_i_uv * uv_width) + sub_j_uv] - 128;
r = (int)((1.1644 * (double)y) + (1.5960 * (double)v));
g = (int)((1.1644 * (double)y) - (0.3918 * (double)u) - (0.8130 * (double)v));
b = (int)((1.1644 * (double)y) + (2.0172 * (double)u));
clip_8_bit( r );
clip_8_bit( g );
clip_8_bit( b );
rgb[(i * w + j) * 4 + 0] = b;
rgb[(i * w + j) * 4 + 1] = g;
rgb[(i * w + j) * 4 + 2] = r;
rgb[(i * w + j) * 4 + 3] = 0;
}
}
return rgb;
}
/************************************************************/
QImage
VideoWindow::captureFrame() const
{
DEBUG_BLOCK
int ratio, format, w, h;
if( !xine_get_current_frame( *engine(), &w, &h, &ratio, &format, NULL ) )
return QImage();
uint8_t *yuv = new uint8_t[((w+8) * (h+1) * 2)];
if( yuv == 0 ) {
Debug::error() << "Not enough memory to make screenframe!\n";
return QImage(); }
xine_get_current_frame( *engine(), &w, &h, &ratio, &format, yuv );
// convert to yv12 if necessary
uint8_t *y = 0, *u = 0, *v = 0;
switch( format )
{
case XINE_IMGFMT_YUY2: {
uint8_t *yuy2 = yuv;
yuv = new uint8_t[(w * h * 2)];
if( yuv == 0 ) {
Debug::error() << "Not enough memory to make screenframe!\n";
delete [] yuy2;
return QImage(); }
y = yuv;
u = yuv + w * h;
v = yuv + w * h * 5 / 4;
yuy2Toyv12( y, u, v, yuy2, w, h );
delete [] yuy2;
} break;
case XINE_IMGFMT_YV12:
y = yuv;
u = yuv + w * h;
v = yuv + w * h * 5 / 4;
break;
default:
Debug::warning() << "Format " << format << " not supported!\n";
delete [] yuv;
return QImage();
}
// convert to rgb
uchar *rgb = yv12ToRgb( y, u, v, w, h );
QImage frame( rgb, w, h, 32, 0, 0, QImage::IgnoreEndian );
delete [] yuv;
return frame;
}
}

@ -0,0 +1,20 @@
// (c) 2004 Max Howell (max.howell@methylblue.com)
// See COPYING file for licensing information
#ifndef CODEINECONFIG_H
#define CODEINECONFIG_H
#include <kconfig.h>
#include <kglobal.h>
namespace Codeine
{
static inline KConfig *config( const QString &group )
{
KConfig* const instance = KGlobal::config();
instance->setGroup( group );
return instance;
}
}
#endif

@ -0,0 +1,28 @@
// (C) 2005 Max Howell (max.howell@methylblue.com)
// See COPYING file for licensing information
#ifndef CODEINE_EXTERN_H
#define CODEINE_EXTERN_H
extern "C"
{
typedef struct xine_s xine_t;
}
class QPopupMenu;
class QWidget;
namespace Codeine
{
class VideoWindow;
class XineEngine;
VideoWindow* const engine(); //defined in xineEngine.h
VideoWindow* const videoWindow(); //defined in xineEngine.h
void showVideoSettingsDialog( QWidget* );
void showXineConfigurationDialog( QWidget*, xine_t* );
void insertAspectRatioMenuItems( QPopupMenu* );
}
#endif

@ -0,0 +1,262 @@
// FHT - Fast Hartley Transform Class
//
// Copyright (C) 2004 Melchior FRANZ - mfranz@kde.org
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// 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, write to the Free Software
// Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
//
// $Id: fht.cpp,v 1.3 2004/06/05 20:20:36 mfranz Exp $
#include <math.h>
#include <string.h>
#include "fht.h"
FHT::FHT(int n) :
m_buf(0),
m_tab(0),
m_log(0)
{
if (n < 3) {
m_num = 0;
m_exp2 = -1;
return;
}
m_exp2 = n;
m_num = 1 << n;
if (n > 3) {
m_buf = new float[m_num];
m_tab = new float[m_num * 2];
makeCasTable();
}
}
FHT::~FHT()
{
delete[] m_buf;
delete[] m_tab;
delete[] m_log;
}
void FHT::makeCasTable(void)
{
float d, *costab, *sintab;
int ul, ndiv2 = m_num / 2;
for (costab = m_tab, sintab = m_tab + m_num / 2 + 1, ul = 0; ul < m_num; ul++) {
d = M_PI * ul / ndiv2;
*costab = *sintab = cos(d);
costab += 2, sintab += 2;
if (sintab > m_tab + m_num * 2)
sintab = m_tab + 1;
}
}
float* FHT::copy(float *d, float *s)
{
return (float *)memcpy(d, s, m_num * sizeof(float));
}
float* FHT::clear(float *d)
{
return (float *)memset(d, 0, m_num * sizeof(float));
}
void FHT::scale(float *p, float d)
{
for (int i = 0; i < (m_num / 2); i++)
*p++ *= d;
}
void FHT::ewma(float *d, float *s, float w)
{
for (int i = 0; i < (m_num / 2); i++, d++, s++)
*d = *d * w + *s * (1 - w);
}
static inline float sind(float d) { return sin(d * M_PI / 180); }
void FHT::pattern(float *p, bool rect = false)
{
static float f = 1.0;
static float h = 0.1;
int i;
for (i = 0; i < 3 * m_num / 4; i++, p++) {
float o = 360.0 * i / m_num;
*p = sind(f * o);
if (rect)
*p = *p < 0 ? -1.0 : 1.0;
}
for (; i < m_num; i++)
*p++ = 0.0;
if (f > m_num / 2.0 || f < .05)
h = -h;
f += h;
}
void FHT::logSpectrum(float *out, float *p)
{
int n = m_num / 2, i, j, k, *r;
if (!m_log) {
m_log = new int[n];
float f = n / log10(n);
for (i = 0, r = m_log; i < n; i++, r++) {
j = int(rint(log10(i + 1.0) * f));
*r = j >= n ? n - 1 : j;
}
}
semiLogSpectrum(p);
*out++ = *p = *p / 100;
for (k = i = 1, r = m_log; i < n; i++) {
j = *r++;
if (i == j)
*out++ = p[i];
else {
float base = p[k - 1];
float step = (p[j] - base) / (j - (k - 1));
for (float corr = 0; k <= j; k++, corr += step)
*out++ = base + corr;
}
}
}
void FHT::semiLogSpectrum(float *p)
{
float e;
power2(p);
for (int i = 0; i < (m_num / 2); i++, p++) {
e = 10.0 * log10(sqrt(*p * .5));
*p = e < 0 ? 0 : e;
}
}
void FHT::spectrum(float *p)
{
power2(p);
for (int i = 0; i < (m_num / 2); i++, p++)
*p = (float)sqrt(*p * .5);
}
void FHT::power(float *p)
{
power2(p);
for (int i = 0; i < (m_num / 2); i++)
*p++ *= .5;
}
void FHT::power2(float *p)
{
int i;
float *q;
_transform(p, m_num, 0);
*p = (*p * *p), *p += *p, p++;
for (i = 1, q = p + m_num - 2; i < (m_num / 2); i++, --q)
*p++ = (*p * *p) + (*q * *q);
}
void FHT::transform(float *p)
{
if (m_num == 8)
transform8(p);
else
_transform(p, m_num, 0);
}
void FHT::transform8(float *p)
{
float a, b, c, d, e, f, g, h, b_f2, d_h2;
float a_c_eg, a_ce_g, ac_e_g, aceg, b_df_h, bdfh;
a = *p++, b = *p++, c = *p++, d = *p++;
e = *p++, f = *p++, g = *p++, h = *p;
b_f2 = (b - f) * M_SQRT2;
d_h2 = (d - h) * M_SQRT2;
a_c_eg = a - c - e + g;
a_ce_g = a - c + e - g;
ac_e_g = a + c - e - g;
aceg = a + c + e + g;
b_df_h = b - d + f - h;
bdfh = b + d + f + h;
*p = a_c_eg - d_h2;
*--p = a_ce_g - b_df_h;
*--p = ac_e_g - b_f2;
*--p = aceg - bdfh;
*--p = a_c_eg + d_h2;
*--p = a_ce_g + b_df_h;
*--p = ac_e_g + b_f2;
*--p = aceg + bdfh;
}
void FHT::_transform(float *p, int n, int k)
{
if (n == 8) {
transform8(p + k);
return;
}
int i, j, ndiv2 = n / 2;
float a, *t1, *t2, *t3, *t4, *ptab, *pp;
for (i = 0, t1 = m_buf, t2 = m_buf + ndiv2, pp = &p[k]; i < ndiv2; i++)
*t1++ = *pp++, *t2++ = *pp++;
memcpy(p + k, m_buf, sizeof(float) * n);
_transform(p, ndiv2, k);
_transform(p, ndiv2, k + ndiv2);
j = m_num / ndiv2 - 1;
t1 = m_buf;
t2 = t1 + ndiv2;
t3 = p + k + ndiv2;
ptab = m_tab;
pp = p + k;
a = *ptab++ * *t3++;
a += *ptab * *pp;
ptab += j;
*t1++ = *pp + a;
*t2++ = *pp++ - a;
for (i = 1, t4 = p + k + n; i < ndiv2; i++, ptab += j) {
a = *ptab++ * *t3++;
a += *ptab * *--t4;
*t1++ = *pp + a;
*t2++ = *pp++ - a;
}
memcpy(p + k, m_buf, sizeof(float) * n);
}

@ -0,0 +1,126 @@
// FHT - Fast Hartley Transform Class
//
// Copyright (C) 2004 Melchior FRANZ - mfranz@kde.org
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// 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, write to the Free Software
// Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
//
// $Id: fht.h,v 1.3 2004/06/05 20:20:36 mfranz Exp $
#ifndef FHT_H
#define FHT_H
/**
* Implementation of the Hartley Transform after Bracewell's discrete
* algorithm. The algorithm is subject to US patent No. 4,646,256 (1987)
* but was put into public domain by the Board of Trustees of Stanford
* University in 1994 and is now freely available[1].
*
* [1] Computer in Physics, Vol. 9, No. 4, Jul/Aug 1995 pp 373-379
*/
class FHT
{
int m_exp2;
int m_num;
float *m_buf;
float *m_tab;
int *m_log;
/**
* Create a table of CAS (cosine and sine) values.
* Has only to be done in the constructor and saves from
* calculating the same values over and over while transforming.
*/
void makeCasTable();
/**
* Recursive in-place Hartley transform. For internal use only!
*/
void _transform(float *, int, int);
public:
/**
* Prepare transform for data sets with @f$2^n@f$ numbers, whereby @f$n@f$
* should be at least 3. Values of more than 3 need a trigonometry table.
* @see makeCasTable()
*/
FHT(int);
~FHT();
inline int sizeExp() const { return m_exp2; }
inline int size() const { return m_num; }
float *copy(float *, float *);
float *clear(float *);
void scale(float *, float);
/**
* Exponentially Weighted Moving Average (EWMA) filter.
* @param d is the filtered data.
* @param s is fresh input.
* @param w is the weighting factor.
*/
void ewma(float *d, float *s, float w);
/**
* Test routine to create wobbling sine or rectangle wave.
* @param d destination vector.
* @param rect rectangle if true, sine otherwise.
*/
void pattern(float *d, bool rect);
/**
* Logarithmic audio spectrum. Maps semi-logarithmic spectrum
* to logarithmic frequency scale, interpolates missing values.
* A logarithmic index map is calculated at the first run only.
* @param p is the input array.
* @param out is the spectrum.
*/
void logSpectrum(float *out, float *p);
/**
* Semi-logarithmic audio spectrum.
*/
void semiLogSpectrum(float *);
/**
* Fourier spectrum.
*/
void spectrum(float *);
/**
* Calculates a mathematically correct FFT power spectrum.
* If further scaling is applied later, use power2 instead
* and factor the 0.5 in the final scaling factor.
* @see FHT::power2()
*/
void power(float *);
/**
* Calculates an FFT power spectrum with doubled values as a
* result. The values need to be multiplied by 0.5 to be exact.
* Note that you only get @f$2^{n-1}@f$ power values for a data set
* of @f$2^n@f$ input values.
* @see FHT::power()
*/
void power2(float *);
/**
* Discrete Hartley transform of data sets with 8 values.
*/
void transform8(float *);
void transform(float *);
};
#endif

@ -0,0 +1,96 @@
// (C) 2005 Max Howell (max.howell@methylblue.com)
// See COPYING file for licensing information
#include "extern.h"
#include "fullScreenAction.h"
#include <klocale.h>
#include <kwin.h>
#include <qwidget.h>
#include "xineEngine.h" //videoWindow()
FullScreenAction::FullScreenAction( QWidget* window, KActionCollection *parent )
: KToggleAction( QString::null, Key_F, 0, 0, parent, "fullscreen" )
, m_window( window )
, m_shouldBeDisabled( false )
, m_state( 0 )
{
window->installEventFilter( this );
setChecked( false );
}
void
FullScreenAction::setChecked( bool setChecked )
{
KToggleAction::setChecked( setChecked );
m_window->raise();
const int id = m_window->winId();
if( setChecked ) {
setText( i18n("Exit F&ull Screen Mode") );
setIcon("window_nofullscreen");
m_state = KWin::windowInfo( id ).state();
KWin::setState( id, NET::FullScreen );
}
else {
setText(i18n("F&ull Screen Mode"));
setIcon("window_fullscreen");
KWin::clearState( id, NET::FullScreen );
KWin::setState( id, m_state ); // get round bug in KWin where it forgets maximisation state
}
if( setChecked == false && m_shouldBeDisabled )
setEnabled( false );
}
void
FullScreenAction::setEnabled( bool setEnabled )
{
if( setEnabled == false && isChecked() )
// don't disable the action if we are currently in fullscreen mode
// as then the user can't exit fullscreen mode! Instead disable it
// when we next get toggled out of fullscreen mode
m_shouldBeDisabled = true;
else {
//FIXME Codeine specific (because videoWindow isn't the window we control, we control the KMainWindow)
//NOTE also if the videoWindow is hidden at some point, this is broken..
//TODO new type of actionclass that event filters and is always correct state
if( setEnabled && reinterpret_cast<QWidget*>(Codeine::videoWindow())->isHidden() )
setEnabled = false;
m_shouldBeDisabled = false;
KToggleAction::setEnabled( setEnabled );
}
}
bool
FullScreenAction::eventFilter( QObject *o, QEvent *e )
{
if( o == m_window )
switch( e->type() ) {
#if QT_VERSION >= 0x030300
case QEvent::WindowStateChange:
#else
case QEvent::ShowFullScreen:
case QEvent::ShowNormal:
case QEvent::ShowMaximized:
case QEvent::ShowMinimized:
#endif
if (m_window->isFullScreen() != isChecked())
slotActivated(); // setChecked( window->isFullScreen()) wouldn't emit signals
if (m_window->isFullScreen() && !isEnabled()) {
m_shouldBeDisabled = true;
setEnabled( true );
}
break;
default:
;
}
return false;
}

@ -0,0 +1,27 @@
// (C) 2005 Max Howell (max.howell@methylblue.com)
// See COPYING file for licensing information
#include <kaction.h>
/**
* @class FullSCreenAction
* @author Max Howell <max.howell@methylblue.com>
* @short Adapted KToggleFullScreenAction, mainly because that class is shit
*/
class FullScreenAction : public KToggleAction
{
public:
FullScreenAction( QWidget *window, KActionCollection* );
virtual void setChecked( bool );
virtual void setEnabled( bool );
protected:
virtual bool eventFilter( QObject* o, QEvent* e );
private:
QWidget *m_window;
bool m_shouldBeDisabled;
unsigned long m_state;
};

@ -0,0 +1,24 @@
// Copyright 2005 Max Howell (max.howell@methylblue.com)
// See COPYING file for licensing information
#include <qpopupmenu.h>
#include <xine.h>
QString i18n( const char *text );
namespace Codeine
{
void
insertAspectRatioMenuItems( QPopupMenu *menu )
{
menu->insertItem( i18n( "Determine &Automatically" ), XINE_VO_ASPECT_AUTO );
menu->insertSeparator();
menu->insertItem( i18n( "&Square (1:1)" ), XINE_VO_ASPECT_SQUARE );
menu->insertItem( i18n( "&4:3" ), XINE_VO_ASPECT_4_3 );
menu->insertItem( i18n( "Ana&morphic (16:9)" ), XINE_VO_ASPECT_ANAMORPHIC );
menu->insertItem( i18n( "&DVB (2.11:1)" ), XINE_VO_ASPECT_DVB );
menu->setItemChecked( XINE_VO_ASPECT_AUTO, true );
}
}

@ -0,0 +1,39 @@
// (c) 2004 Max Howell (max.howell@methylblue.com)
// See COPYING file for licensing information
#ifndef CODEINELISTVIEW_CPP
#define CODEINELISTVIEW_CPP
#include <klistview.h>
namespace Codeine
{
class ListView : public KListView
{
public:
ListView( QWidget *parent ) : KListView( parent )
{
addColumn( QString::null, 0 );
addColumn( QString::null );
setResizeMode( LastColumn );
setMargin( 2 );
setSorting( -1 );
setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
setAllColumnsShowFocus( true );
setItemMargin( 3 );
}
virtual QSize sizeHint() const
{
const QSize sh = KListView::sizeHint();
return QSize( sh.width(),
childCount() == 0
? 50
: QMIN( sh.height(), childCount() * (firstChild()->height()) + margin() * 2 + 4 + reinterpret_cast<QWidget*>(header())->height() ) );
}
};
}
#endif

@ -0,0 +1,52 @@
// (c) 2004 Max Howell (max.howell@methylblue.com)
// See COPYING file for licensing information
#include "codeine.h"
#include <kaboutdata.h>
#include <kapplication.h>
#include <kcmdlineargs.h>
#include "mainWindow.h"
#include <X11/Xlib.h>
static KAboutData aboutData( APP_NAME,
I18N_NOOP(PRETTY_NAME), APP_VERSION,
I18N_NOOP("A video player that has a usability focus"), KAboutData::License_GPL_V2,
I18N_NOOP("Copyright 2006, Max Howell"), 0,
"http://www.methylblue.com/codeine/",
"codeine@methylblue.com" );
static const KCmdLineOptions options[] = {
{ "+[URL]", I18N_NOOP( "Play 'URL'" ), 0 },
{ "play-dvd", I18N_NOOP( "Play DVD Video" ), 0 },
{ 0, 0, 0 } };
int
main( int argc, char **argv )
{
//we need to do this, says adrianS from SuSE
if( !XInitThreads() )
return 1;
aboutData.addCredit( "Mike Diehl", I18N_NOOP("Handbook") );
aboutData.addCredit( "The Kaffeine Developers", I18N_NOOP("Great reference code") );
aboutData.addCredit( "Eric Prydz", I18N_NOOP("The video for \"Call on Me\" encouraged plenty of debugging! ;)") );
aboutData.addCredit( "David Vignoni", I18N_NOOP("The current Codeine icon") );
aboutData.addCredit( "Ian Monroe", I18N_NOOP("Patches, advice and moral support") );
KCmdLineArgs::init( argc, argv, &aboutData );
KCmdLineArgs::addCmdLineOptions( options );
KApplication application;
int returnValue;
{
Codeine::MainWindow mainWindow;
mainWindow.show();
returnValue = application.exec();
}
return returnValue;
}

@ -0,0 +1,714 @@
// (C) 2005 Max Howell (max.howell@methylblue.com)
// See COPYING file for licensing information
#include "actions.h"
#include "analyzer.h"
#include "config.h"
#include "configure.h"
#include <cstdlib>
#include "debug.h"
#include "extern.h" //dialog creation function definitions
#include "fullScreenAction.h"
#include <kapplication.h>
#include <kcmdlineargs.h>
#include <kcursor.h>
#include <kfiledialog.h> //::open()
#include <kglobalsettings.h> //::timerEvent()
#include <kio/netaccess.h>
#include <ksqueezedtextlabel.h>
#include <kstatusbar.h>
#include <ktoolbar.h>
#include <kurldrag.h>
#include <kwin.h>
#include "mainWindow.h"
#include "playDialog.h" //::play()
#include "playlistFile.h"
#include "mxcl.library.h"
#include <qcstring.h>
#include <qdesktopwidget.h>
#include <qevent.h> //::stateChanged()
#include <qlayout.h> //ctor
#include <qpopupmenu.h> //because XMLGUI is poorly designed
#include <qobjectlist.h>
#include "slider.h"
#include "theStream.h"
#include "volumeAction.h"
#include "xineEngine.h"
#ifndef NO_XTEST_EXTENSION
extern "C"
{
#include <X11/extensions/XTest.h>
#include <X11/keysym.h>
}
#endif
namespace Codeine {
/// @see codeine.h
QWidget *mainWindow() { return kapp->mainWidget(); }
MainWindow::MainWindow()
: KMainWindow()
, m_positionSlider( new Slider( this, 65535 ) )
, m_timeLabel( new QLabel( " 0:00:00 ", this ) )
, m_titleLabel( new KSqueezedTextLabel( this ) )
{
DEBUG_BLOCK
clearWFlags( WDestructiveClose ); //we are allocated on the stack
kapp->setMainWidget( this );
new VideoWindow( this );
setCentralWidget( videoWindow() );
setFocusProxy( videoWindow() ); // essential! See VideoWindow::event(), QEvent::FocusOut
// these have no affect beccause "KDE Knows Best" FFS
setDockEnabled( toolBar(), Qt::DockRight, false ); //doesn't make sense due to our large horizontal slider
setDockEnabled( toolBar(), Qt::DockLeft, false ); //as above
m_titleLabel->setMargin( 2 );
m_timeLabel->setFont( KGlobalSettings::fixedFont() );
m_timeLabel->setAlignment( AlignCenter );
m_timeLabel->setMinimumSize( m_timeLabel->sizeHint() );
// work around a bug in KStatusBar
// sizeHint width of statusbar seems to get stupidly large quickly
statusBar()->setSizePolicy( QSizePolicy::Ignored, QSizePolicy::Maximum );
statusBar()->addWidget( m_titleLabel, 1, false );
statusBar()->addWidget( m_analyzer = new Analyzer::Block( this ), 0, true );
statusBar()->addWidget( m_timeLabel, 0, true );
setupActions();
setupGUI();
setStandardToolBarMenuEnabled( false ); //bah to setupGUI()!
toolBar()->show(); //it's possible it would be hidden, but we don't want that as no UI way to show it!
// only show dvd button when playing a dvd
{
struct KdeIsTehSuck : public QObject
{
virtual bool eventFilter( QObject*, QEvent *e )
{
if (e->type() != QEvent::LayoutHint)
return false;
// basically, KDE shows all tool-buttons, even if they are
// hidden after it does any layout operation. Yay for KDE. Yay.
QWidget *button = (QWidget*)((KMainWindow*)mainWindow())->toolBar()->child( "toolbutton_toggle_dvd_menu" );
if (button)
button->setShown( TheStream::url().protocol() == "dvd" );
return false;
}
} *o;
o = new KdeIsTehSuck;
toolBar()->installEventFilter( o );
insertChild( o );
}
{
QPopupMenu *menu = 0, *settings = static_cast<QPopupMenu*>(factory()->container( "settings", this ));
int id = SubtitleChannelsMenuItemId, index = 0;
#define make_menu( name, text ) \
menu = new QPopupMenu( this, name ); \
menu->setCheckable( true ); \
connect( menu, SIGNAL(activated( int )), engine(), SLOT(setStreamParameter( int )) ); \
connect( menu, SIGNAL(aboutToShow()), SLOT(aboutToShowMenu()) ); \
settings->insertItem( text, menu, id, index ); \
settings->setItemEnabled( id, false ); \
id++, index++;
make_menu( "subtitle_channels_menu", i18n( "&Subtitles" ) );
make_menu( "audio_channels_menu", i18n( "A&udio Channels" ) );
make_menu( "aspect_ratio_menu", i18n( "Aspect &Ratio" ) );
#undef make_menu
Codeine::insertAspectRatioMenuItems( menu ); //so we don't have to include xine.h here
settings->insertSeparator( index );
}
QObjectList *list = toolBar()->queryList( "KToolBarButton" );
if (list->isEmpty()) {
MessageBox::error( i18n(
"<qt>" PRETTY_NAME " could not load its interface, this probably means that " PRETTY_NAME " is not "
"installed to the correct prefix. If you installed from packages please contact the packager, if "
"you installed from source please try running the <b>configure</b> script again like this: "
"<pre> % ./configure --prefix=`kde-config --prefix`</pre>" ) );
std::exit( 1 );
}
delete list;
KXMLGUIClient::stateChanged( "empty" );
KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
if( args->count() || args->isSet( "play-dvd" ) || kapp->isRestored() )
//we need to resize the window, so we can't show the window yet
init();
else {
//"faster" startup
//TODO if we have a size stored for this video, do the "faster" route
QTimer::singleShot( 0, this, SLOT(init()) );
QApplication::setOverrideCursor( KCursor::waitCursor() ); }
}
void
MainWindow::init()
{
DEBUG_BLOCK
connect( engine(), SIGNAL(statusMessage( const QString& )), this, SLOT(engineMessage( const QString& )) );
connect( engine(), SIGNAL(stateChanged( Engine::State )), this, SLOT(engineStateChanged( Engine::State )) );
connect( engine(), SIGNAL(channelsChanged( const QStringList& )), this, SLOT(setChannels( const QStringList& )) );
connect( engine(), SIGNAL(titleChanged( const QString& )), m_titleLabel, SLOT(setText( const QString& )) );
connect( m_positionSlider, SIGNAL(valueChanged( int )), this, SLOT(showTime( int )) );
if( !engine()->init() ) {
KMessageBox::error( this, i18n(
"<qt>xine could not be successfully initialised. " PRETTY_NAME " will now exit. "
"You can try to identify what is wrong with your xine installation using the <b>xine-check</b> command at a command-prompt.") );
std::exit( 2 );
}
//would be dangerous for these to65535 happen before the videoWindow() is initialised
setAcceptDrops( true );
connect( m_positionSlider, SIGNAL(sliderReleased( uint )), engine(), SLOT(seek( uint )) );
connect( statusBar(), SIGNAL(messageChanged( const QString& )), engine(), SLOT(showOSD( const QString& )) );
QApplication::restoreOverrideCursor();
if( !kapp->isRestored() ) {
KCmdLineArgs &args = *KCmdLineArgs::parsedArgs();
if (args.isSet( "play-dvd" ))
open( "dvd:/" );
else if (args.count() > 0 ) {
open( args.url( 0 ) );
args.clear();
adjustSize(); //will resize us to reflect the videoWindow's sizeHint()
}
else
//show the welcome dialog
playMedia( true ); // true = show in style of welcome dialog
}
else
//session management must be done after the videoWindow() has been initialised
restore( 1, false );
//don't do until videoWindow() is initialised!
startTimer( 50 );
}
MainWindow::~MainWindow()
{
DEBUG_FUNC_INFO
hide(); //so we appear to have quit, and then sound fades out below
delete videoWindow(); //fades out sound in dtor
}
bool
MainWindow::queryExit()
{
if( toggleAction( "fullscreen" )->isChecked() ) {
// there seems to be no other way to stop KMainWindow
// saving the window state without any controls
fullScreenToggled( false );
showNormal();
QApplication::sendPostedEvents( this, 0 );
// otherwise KMainWindow saves the screensize as maximised
Codeine::MessageBox::sorry(
"This annoying messagebox is to get round a bug in either KDE or Qt. "
"Just press OK and Codeine will quit." );
//NOTE not actually needed
saveAutoSaveSettings();
hide();
}
return true;
}
void
MainWindow::setupActions()
{
DEBUG_BLOCK
KActionCollection * const ac = actionCollection();
KStdAction::quit( kapp, SLOT(quit()), ac );
KStdAction::open( this, SLOT(playMedia()), ac, "play_media" )->setText( i18n("Play &Media...") );
connect( new FullScreenAction( this, ac ), SIGNAL(toggled( bool )), SLOT(fullScreenToggled( bool )) );
new PlayAction( this, SLOT(play()), ac );
new KAction( i18n("Stop"), "player_stop", Key_S, engine(), SLOT(stop()), ac, "stop" );
new KToggleAction( i18n("Record"), "player_record", CTRL + Key_R, engine(), SLOT(record()), ac, "record" );
new KAction( i18n("Reset Video Scale"), "viewmag1", Key_Equal, videoWindow(), SLOT(resetZoom()), ac, "reset_zoom" );
new KAction( i18n("Media Information"), "messagebox_info", Key_I, this, SLOT(streamInformation()), ac, "information" );
new KAction( i18n("Menu Toggle"), "dvd_unmount", Key_R, engine(), SLOT(toggleDVDMenu()), ac, "toggle_dvd_menu" );
new KAction( i18n("&Capture Frame"), "frame_image", Key_C, this, SLOT(captureFrame()), ac, "capture_frame" );
new KAction( i18n("Video Settings..."), "configure", Key_V, this, SLOT(configure()), ac, "video_settings" );
new KAction( i18n("Configure xine..."), "configure", 0, this, SLOT(configure()), ac, "xine_settings" );
(new KWidgetAction( m_positionSlider, i18n("Position Slider"), 0, 0, 0, ac, "position_slider" ))->setAutoSized( true );
new VolumeAction( toolBar(), ac );
}
void
MainWindow::saveProperties( KConfig *config )
{
config->writeEntry( "url", TheStream::url().url() );
config->writeEntry( "time", engine()->time() );
}
void
MainWindow::readProperties( KConfig *config )
{
if( engine()->load( config->readPathEntry( "url" ) ) )
engine()->play( config->readNumEntry( "time" ) );
}
void
MainWindow::timerEvent( QTimerEvent* )
{
static int counter = 0;
if( engine()->state() == Engine::Playing ) {
++counter &= 1023;
m_positionSlider->setValue( engine()->position() );
if( !m_positionSlider->isEnabled() && counter % 10 == 0 ) // 0.5 seconds
// usually the slider emits a signal that updates the timeLabel
// but not if the slider isn't moving because there is no length
showTime();
#ifndef NO_XTEST_EXTENSION
if( counter == 0 /*1020*/ ) { // 51 seconds //do at 0 to ensure screensaver doesn't happen before 51 seconds is up (somehow)
const bool isOnThisDesktop = KWin::windowInfo( winId() ).isOnDesktop( KWin::currentDesktop() );
if( videoWindow()->isVisible() && isOnThisDesktop ) {
int key = XKeysymToKeycode( x11Display(), XK_Shift_R );
XTestFakeKeyEvent( x11Display(), key, true, CurrentTime );
XTestFakeKeyEvent( x11Display(), key, false, CurrentTime );
XSync( x11Display(), false );
}
}
#endif
}
}
void
MainWindow::showTime( int pos )
{
#define zeroPad( n ) n < 10 ? QString("0%1").arg( n ) : QString::number( n )
const int ms = (pos == -1) ? engine()->time() : int(engine()->length() * (pos / 65535.0));
const int s = ms / 1000;
const int m = s / 60;
const int h = m / 60;
QString time = zeroPad( s % 60 ); //seconds
time.prepend( ':' );
time.prepend( zeroPad( m % 60 ) ); //minutes
time.prepend( ':' );
time.prepend( QString::number( h ) ); //hours
m_timeLabel->setText( time );
}
void
MainWindow::engineMessage( const QString &message )
{
statusBar()->message( message, 3500 );
}
bool
MainWindow::open( const KURL &url )
{
DEBUG_BLOCK
debug() << url << endl;
if( load( url ) ) {
const int offset = TheStream::hasProfile()
// adjust offset if we have session history for this video
? TheStream::profile()->readNumEntry( "Position", 0 )
: 0;
return engine()->play( offset );
}
return false;
}
bool
MainWindow::load( const KURL &url )
{
//FileWatch the file that is opened
if( url.isEmpty() ) {
MessageBox::sorry( i18n( "Codeine was asked to open an empty URL; it cannot." ) );
return false;
}
PlaylistFile playlist( url );
if( playlist.isPlaylist() ) {
//TODO: problem is we return out of the function
//statusBar()->message( i18n("Parsing playlist file...") );
if( playlist.isValid() )
return engine()->load( playlist.firstUrl() );
else {
MessageBox::sorry( playlist.error() );
return false;
}
}
if (url.protocol() == "media") {
#define UDS_LOCAL_PATH (72 | KIO::UDS_STRING)
KIO::UDSEntry e;
if (!KIO::NetAccess::stat( url, e, 0 ))
MessageBox::sorry( "There was an internal error with the media slave..." );
else {
KIO::UDSEntry::ConstIterator end = e.end();
for (KIO::UDSEntry::ConstIterator it = e.begin(); it != end; ++it)
if ((*it).m_uds == UDS_LOCAL_PATH && !(*it).m_str.isEmpty())
return engine()->load( KURL::fromPathOrURL( (*it).m_str ) );
}
}
//let xine handle invalid, etc, KURLS
//TODO it handles non-existant files with bad error message
return engine()->load( url );
}
void
MainWindow::play()
{
switch( engine()->state() ) {
case Engine::Loaded:
engine()->play();
break;
case Engine::Playing:
case Engine::Paused:
engine()->pause();
break;
case Engine::Empty:
default:
playMedia();
break;
}
}
void
MainWindow::playMedia( bool show_welcome_dialog )
{
PlayDialog dialog( this, show_welcome_dialog );
switch( dialog.exec() ) {
case PlayDialog::FILE: {
const QString filter = engine()->fileFilter() + '|' + i18n("Supported Media Formats") + "\n*|" + i18n("All Files");
const KURL url = KFileDialog::getOpenURL( ":default", filter, this, i18n("Select A File To Play") );
open( url );
} break;
case PlayDialog::RECENT_FILE:
open( dialog.url() );
break;
case PlayDialog::CDDA:
open( "cdda:/1" );
break;
case PlayDialog::VCD:
open( "vcd://" ); // one / is not enough
break;
case PlayDialog::DVD:
open( "dvd:/" );
break;
}
}
class FullScreenToolBarHandler : QObject
{
KToolBar *m_toolbar;
int m_timer_id;
bool m_stay_hidden_for_a_bit;
QPoint m_home;
public:
FullScreenToolBarHandler( KMainWindow *parent )
: QObject( parent )
, m_toolbar( parent->toolBar() )
, m_timer_id( 0 )
, m_stay_hidden_for_a_bit( false )
{
DEBUG_BLOCK
parent->installEventFilter( this );
m_toolbar->installEventFilter( this );
}
bool eventFilter( QObject *o, QEvent *e )
{
if (o == parent() && e->type() == QEvent::MouseMove) {
killTimer( m_timer_id );
QMouseEvent const * const me = (QMouseEvent*)e;
if (m_stay_hidden_for_a_bit) {
// wait for a small pause before showing the toolbar again
// usage = user removes mouse from toolbar after using it
// toolbar disappears (usage is over) but usually we show
// toolbar immediately when mouse is moved.. so we need this hack
// HACK if user thrusts mouse to top, we assume they really want the toolbar
// back. Is hack as 80% of users have at top, but 20% at bottom, we don't cater
// for the 20% as lots more code, for now.
if (me->pos().y() < m_toolbar->height())
goto show_toolbar;
m_timer_id = startTimer( 100 );
}
else {
if (m_toolbar->isHidden()) {
if (m_home.isNull())
m_home = me->pos();
else if ((m_home - me->pos()).manhattanLength() > 6)
// then cursor has moved far enough to trigger show toolbar
show_toolbar:
m_toolbar->show(),
m_home = QPoint();
else
// cursor hasn't moved far enough yet
// don't reset timer below, return instead
return false;
}
// reset the hide timer
m_timer_id = startTimer( VideoWindow::CURSOR_HIDE_TIMEOUT );
}
}
if (o == parent() && e->type() == QEvent::Resize)
{
//we aren't managed by mainWindow when at FullScreen
videoWindow()->move( 0, 0 );
videoWindow()->resize( ((QWidget*)o)->size() );
videoWindow()->lower();
}
if (o == m_toolbar)
switch (e->type()) {
case QEvent::Enter:
m_stay_hidden_for_a_bit = false;
killTimer( m_timer_id );
break;
case QEvent::Leave:
m_toolbar->hide();
m_stay_hidden_for_a_bit = true;
killTimer( m_timer_id );
m_timer_id = startTimer( 100 );
break;
default: break;
}
return false;
}
void timerEvent( QTimerEvent* )
{
if (m_stay_hidden_for_a_bit)
;
else if (!m_toolbar->hasMouse())
m_toolbar->hide();
m_stay_hidden_for_a_bit = false;
}
};
void
MainWindow::fullScreenToggled( bool isFullScreen )
{
static FullScreenToolBarHandler *s_handler;
DEBUG_FUNC_INFO
if( isFullScreen )
toolBar()->setPalette( palette() ), // due to 2px spacing in QMainWindow :(
setPaletteBackgroundColor( Qt::black ); // due to 2px spacing
else
toolBar()->unsetPalette(),
unsetPalette();
toolBar()->setMovingEnabled( !isFullScreen );
toolBar()->setHidden( isFullScreen && engine()->state() == Engine::Playing );
reinterpret_cast<QWidget*>(menuBar())->setHidden( isFullScreen );
statusBar()->setHidden( isFullScreen );
setMouseTracking( isFullScreen ); /// @see mouseMoveEvent()
if (isFullScreen)
s_handler = new FullScreenToolBarHandler( this );
else
delete s_handler;
// prevent videoWindow() moving around when mouse moves
setCentralWidget( isFullScreen ? 0 : videoWindow() );
}
void
MainWindow::configure()
{
const QCString sender = this->sender()->name();
if( sender == "video_settings" )
Codeine::showVideoSettingsDialog( this );
else if( sender == "xine_settings" )
Codeine::showXineConfigurationDialog( this, *engine() );
}
void
MainWindow::streamInformation()
{
MessageBox::information( TheStream::information(), i18n("Media Information") );
}
void
MainWindow::setChannels( const QStringList &channels )
{
DEBUG_FUNC_INFO
//TODO -1 = auto
QStringList::ConstIterator it = channels.begin();
QPopupMenu *menu = (QPopupMenu*)child( (*it).latin1() );
menu->clear();
menu->insertItem( i18n("&Determine Automatically"), 1 );
menu->insertSeparator();
//the id is crucial, since the slot this menu is connected to requires
//that information to set the correct channel
//NOTE we subtract 2 in xineEngine because QMenuData doesn't allow negative id
int id = 2;
++it;
for( QStringList::ConstIterator const end = channels.end(); it != end; ++it, ++id )
menu->insertItem( *it, id );
menu->insertSeparator();
menu->insertItem( i18n("&Off"), 0 );
id = channels.first() == "subtitle_channels_menu" ? SubtitleChannelsMenuItemId : AudioChannelsMenuItemId;
MainWindow::menu( "settings" )->setItemEnabled( id, channels.count() > 1 );
}
void
MainWindow::aboutToShowMenu()
{
QPopupMenu *menu = (QPopupMenu*)sender();
QCString name( sender() ? sender()->name() : 0 );
// uncheck all items first
for( uint x = 0; x < menu->count(); ++x )
menu->setItemChecked( menu->idAt( x ), false );
int id;
if( name == "subtitle_channels_menu" )
id = TheStream::subtitleChannel() + 2;
else if( name == "audio_channels_menu" )
id = TheStream::audioChannel() + 2;
else
id = TheStream::aspectRatio();
menu->setItemChecked( id, true );
}
void
MainWindow::dragEnterEvent( QDragEnterEvent *e )
{
e->accept( KURLDrag::canDecode( e ) );
}
void
MainWindow::dropEvent( QDropEvent *e )
{
KURL::List list;
KURLDrag::decode( e, list );
if( !list.isEmpty() )
open( list.first() );
else
engineMessage( i18n("Sorry, no media was found in the drop") );
}
void
MainWindow::keyPressEvent( QKeyEvent *e )
{
#define seek( step ) { \
const int new_pos = m_positionSlider->value() step; \
engine()->seek( new_pos > 0 ? (uint)new_pos : 0 ); \
}
switch( e->key() )
{
case Qt::Key_Left: seek( -500 ); break;
case Qt::Key_Right: seek( +500 ); break;
case Key_Escape: KWin::clearState( winId(), NET::FullScreen );
default: ;
}
#undef seek
}
QPopupMenu*
MainWindow::menu( const char *name )
{
// KXMLGUI is "really good".
return static_cast<QPopupMenu*>(factory()->container( name, this ));
}
/// Convenience class for other classes that need access to the actionCollection
KActionCollection*
actionCollection()
{
return static_cast<MainWindow*>(kapp->mainWidget())->actionCollection();
}
/// Convenience class for other classes that need access to the actions
KAction*
action( const char *name )
{
#define QT_FATAL_ASSERT
MainWindow *mainWindow = 0;
KActionCollection *actionCollection = 0;
KAction *action = 0;
if( mainWindow = (MainWindow*)kapp->mainWidget() )
if( actionCollection = mainWindow->actionCollection() )
action = actionCollection->action( name );
Q_ASSERT( mainWindow );
Q_ASSERT( actionCollection );
Q_ASSERT( action );
return action;
}
} //namespace Codeine

@ -0,0 +1,75 @@
// (c) 2004 Max Howell (max.howell@methylblue.com)
// See COPYING file for licensing information
#ifndef CODEINEMAINWINDOW_H
#define CODEINEMAINWINDOW_H
#include "codeine.h"
#include <kmainwindow.h>
class KURL;
class QLabel;
class QPopupMenu;
class QSlider;
namespace Codeine
{
class MainWindow : public KMainWindow
{
Q_OBJECT
MainWindow();
~MainWindow();
friend int ::main( int, char** );
enum { SubtitleChannelsMenuItemId = 2000, AudioChannelsMenuItemId, AspectRatioMenuItemId };
public slots:
void play();
void playMedia( bool show_welcome_dialog = false );
void configure();
void streamInformation();
void captureFrame();
private slots:
void engineMessage( const QString& );
void engineStateChanged( Engine::State );
void init();
void showTime( int = -1 );
void setChannels( const QStringList& );
void aboutToShowMenu();
void fullScreenToggled( bool );
private:
void setupActions();
bool load( const KURL& );
bool open( const KURL& );
QPopupMenu *menu( const char *name );
virtual void timerEvent( QTimerEvent* );
virtual void dragEnterEvent( QDragEnterEvent* );
virtual void dropEvent( QDropEvent* );
virtual void keyPressEvent( QKeyEvent* );
virtual void saveProperties( KConfig* );
virtual void readProperties( KConfig* );
virtual bool queryExit();
QSlider *m_positionSlider;
QLabel *m_timeLabel;
QLabel *m_titleLabel;
QWidget *m_analyzer;
//undefined
MainWindow( const MainWindow& );
MainWindow &operator=( const MainWindow& );
};
}
#endif

@ -0,0 +1,114 @@
// (C) 2005 Max Howell (max.howell@methylblue.com)
// See COPYING file for licensing information
#include "config.h"
#include "listView.cpp"
#include <kapplication.h>
#include <kconfig.h>
#include <kguiitem.h>
#include <klistview.h>
#include <kpushbutton.h>
#include <kstdguiitem.h>
#include "playDialog.h"
#include "mxcl.library.h"
#include <qfile.h>
#include <qlabel.h>
#include <qlayout.h>
#include <qsignalmapper.h>
QString i18n( const char *text );
namespace Codeine {
PlayDialog::PlayDialog( QWidget *parent, bool be_welcome_dialog )
: QDialog( parent )
{
setCaption( kapp->makeStdCaption( i18n("Play Media") ) );
QSignalMapper *mapper = new QSignalMapper( this );
QWidget *o, *closeButton = new KPushButton( KStdGuiItem::close(), this );
QBoxLayout *hbox, *vbox = new QVBoxLayout( this, 15, 20 );
vbox->addWidget( new QLabel( i18n( "What media would you like to play?" ), this ) );
QGridLayout *grid = new QGridLayout( vbox, 1, 3, 20 );
//TODO use the kguiItems from the actions
mapper->setMapping( o = new KPushButton( KGuiItem( i18n("Play File..."), "fileopen" ), this ), FILE );
connect( o, SIGNAL(clicked()), mapper, SLOT(map()) );
grid->QLayout::add( o );
mapper->setMapping( o = new KPushButton( KGuiItem( i18n("Play VCD"), "cdaudio_unmount" ), this ), VCD );
connect( o, SIGNAL(clicked()), mapper, SLOT(map()) );
grid->QLayout::add( o );
mapper->setMapping( o = new KPushButton( KGuiItem( i18n("Play DVD"), "dvd_unmount" ), this ), DVD );
connect( o, SIGNAL(clicked()), mapper, SLOT(map()) );
grid->QLayout::add( o );
mapper->setMapping( closeButton, QDialog::Rejected );
connect( closeButton, SIGNAL(clicked()), mapper, SLOT(map()) );
createRecentFileWidget( vbox );
hbox = new QHBoxLayout( vbox );
hbox->addItem( new QSpacerItem( 10, 10, QSizePolicy::Expanding ) );
if( be_welcome_dialog ) {
QWidget *w = new KPushButton( KStdGuiItem::quit(), this );
hbox->addWidget( w );
connect( w, SIGNAL(clicked()), kapp, SLOT(quit()) );
}
hbox->addWidget( closeButton );
connect( mapper, SIGNAL(mapped( int )), SLOT(done( int )) );
}
void
PlayDialog::createRecentFileWidget( QBoxLayout *layout )
{
KListView *lv;
lv = new Codeine::ListView( this );
lv->setColumnText( 1, i18n("Recently Played Media") );
const QStringList list1 = Codeine::config( "General" )->readPathListEntry( "Recent Urls" );
KURL::List urls;
foreach( list1 )
urls += *it;
for( KURL::List::Iterator it = urls.begin(), end = urls.end(); it != end; ) {
if( urls.contains( *it ) > 1 )
//remove duplicates
it = urls.remove( it );
else if( (*it).protocol() == "file" && !QFile::exists( (*it).path() ) )
//remove stale entries
it = urls.remove( it );
else
++it;
}
for( KURL::List::ConstIterator it = urls.begin(), end = urls.end(); it != end; ++it ) {
const QString fileName = (*it).fileName();
new KListViewItem( lv, 0, (*it).url(), fileName.isEmpty() ? (*it).prettyURL() : fileName );
}
if( lv->childCount() ) {
layout->addWidget( lv, 1 );
connect( lv, SIGNAL(executed( QListViewItem* )), SLOT(done( QListViewItem* )) );
}
else
delete lv;
}
void
PlayDialog::done( QListViewItem *item )
{
m_url = item->text( 0 );
QDialog::done( RECENT_FILE );
}
}

@ -0,0 +1,36 @@
// (C) 2005 Max Howell (max.howell@methylblue.com)
// See COPYING file for licensing information
#ifndef CODEINEPLAYDIALOG_H
#define CODEINEPLAYDIALOG_H
#include <kurl.h>
#include <qdialog.h>
class KListView;
class QBoxLayout;
class QListViewItem;
namespace Codeine
{
class PlayDialog : public QDialog
{
Q_OBJECT
public:
PlayDialog( QWidget*, bool show_welcome_dialog = false );
const KURL &url() const { return m_url; }
enum DialogCode { FILE = QDialog::Accepted + 2, VCD, CDDA, DVD, RECENT_FILE };
private slots:
void done( QListViewItem* );
private:
void createRecentFileWidget( QBoxLayout* );
KURL m_url;
};
}
#endif

@ -0,0 +1,123 @@
// (C) 2005 Max Howell (max.howell@methylblue.com)
// See COPYING file for licensing information
//TODO error messages that vary depending on if the file is remote or not
#include "codeine.h"
#include "debug.h"
#include <kio/netaccess.h>
#include "playlistFile.h"
#include <qfile.h>
#include <qtextstream.h>
#include <mxcl.library.h>
PlaylistFile::PlaylistFile( const KURL &url )
: m_url( url )
, m_isRemoteFile( !url.isLocalFile() )
, m_isValid( false )
{
mxcl::WaitCursor allocateOnStack;
QString &path = m_path = url.path();
if( path.endsWith( ".pls", false ) )
m_type = PLS; else
if( path.endsWith( ".m3u", false ) )
m_type = M3U;
else {
m_type = Unknown;
m_error = i18n( "The file is not a playlist" );
return;
}
if( m_isRemoteFile ) {
path = QString();
if( !KIO::NetAccess::download( url, path, Codeine::mainWindow() ) ) {
m_error = i18n( "Codeine could not download the remote playlist: %1" ).arg( url.prettyURL() );
return;
}
}
QFile file( path );
if( file.open( IO_ReadOnly ) ) {
QTextStream stream( &file );
switch( m_type ) {
case M3U: parseM3uFile( stream ); break;
case PLS: parsePlsFile( stream ); break;
default: ;
}
if( m_contents.isEmpty() )
m_error = i18n( "<qt>The playlist, <i>'%1'</i>, could not be interpreted. Perhaps it is empty?" ).arg( path ),
m_isValid = false;
}
else
m_error = i18n( "Codeine could not open the file: %1" ).arg( path );
}
PlaylistFile::~PlaylistFile()
{
if( m_isRemoteFile )
KIO::NetAccess::removeTempFile( m_path );
}
void
PlaylistFile::parsePlsFile( QTextStream &stream )
{
DEBUG_BLOCK
for( QString line = stream.readLine(); !line.isNull(); )
{
if( line.startsWith( "File" ) ) {
const KURL url = line.section( '=', -1 );
const QString title = stream.readLine().section( '=', -1 );
debug() << url << endl << title << endl;
m_contents += url;
m_isValid = true;
return; //TODO continue for all urls
}
line = stream.readLine();
}
}
void
PlaylistFile::parseM3uFile( QTextStream &stream )
{
DEBUG_BLOCK
for( QString line; !stream.atEnd(); )
{
line = stream.readLine();
if( line.startsWith( "#EXTINF", false ) )
continue;
else if( !line.startsWith( "#" ) && !line.isEmpty() )
{
KURL url;
// KURL::isRelativeURL() expects absolute URLs to start with a protocol, so prepend it if missing
if( line.startsWith( "/" ) )
line.prepend( "file://" );
if( KURL::isRelativeURL( line ) )
url.setPath( m_url.directory() + line );
else
url = KURL::fromPathOrURL( line );
m_contents += url;
m_isValid = true;
return;
}
}
}

@ -0,0 +1,36 @@
// (C) 2005 Max Howell (max.howell@methylblue.com)
// See COPYING file for licensing information
#ifndef CODEINE_PLAYLIST_FILE_H
#define CODEINE_PLAYLIST_FILE_H
#include <kurl.h>
class PlaylistFile
{
public:
PlaylistFile( const KURL &url );
~PlaylistFile();
enum FileFormat { M3U, PLS, Unknown, NotPlaylistFile = Unknown };
bool isPlaylist() const { return m_type != Unknown; }
bool isValid() const { return m_isValid; }
KURL firstUrl() const { return m_contents.isEmpty() ? KURL() : m_contents.first(); }
QString error() const { return m_error; }
private:
/// both only return first url currently
void parsePlsFile( QTextStream& );
void parseM3uFile( QTextStream& );
KURL m_url;
bool m_isRemoteFile;
bool m_isValid;
QString m_error;
FileFormat m_type;
QString m_path;
KURL::List m_contents;
};
#endif

@ -0,0 +1,145 @@
// (c) 2004 Max Howell (max.howell@methylblue.com)
// See COPYING file for licensing information
#include "debug.h"
#include "slider.h"
#include <qapplication.h>
#include <qlabel.h>
#include <qsize.h>
#include <qtooltip.h>
#include <qpainter.h>
#include "xineEngine.h"
using Codeine::Slider;
Slider *Slider::s_instance = 0;
Slider::Slider( QWidget *parent, uint max )
: QSlider( Qt::Horizontal, parent )
, m_sliding( false )
, m_outside( false )
, m_prevValue( 0 )
{
s_instance = this;
setRange( 0, max );
setFocusPolicy( NoFocus );
setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding );
}
void
Slider::wheelEvent( QWheelEvent *e )
{
//if you use this class elsewhere, NOTE this is Codeine specific
e->ignore(); //pass to VideoWindow
}
void
Slider::mouseMoveEvent( QMouseEvent *e )
{
if( m_sliding )
{
//feels better, but using set value of 20 is bad of course
QRect rect = this->rect();
rect.addCoords( -20, -20, 20, 20 );
if( !rect.contains( e->pos() ) ) {
if( !m_outside )
QSlider::setValue( m_prevValue );
m_outside = true;
} else {
m_outside = false;
QSlider::setValue(
QRangeControl::valueFromPosition(
e->pos().x() - sliderRect().width()/2,
width() - sliderRect().width() ) );
emit sliderMoved( value() );
}
}
else
QSlider::mouseMoveEvent( e );
}
void
Slider::mousePressEvent( QMouseEvent *e )
{
m_sliding = true;
m_prevValue = QSlider::value();
if( !sliderRect().contains( e->pos() ) )
mouseMoveEvent( e );
}
void
Slider::mouseReleaseEvent( QMouseEvent* )
{
if( !m_outside && QSlider::value() != m_prevValue )
emit sliderReleased( value() );
m_sliding = false;
m_outside = false;
}
static inline QString timeAsString( const int s )
{
#define zeroPad( n ) n < 10 ? QString("0%1").arg( n ) : QString::number( n )
using Codeine::engine;
const int m = s / 60;
const int h = m / 60;
QString time;
time.prepend( zeroPad( s % 60 ) ); //seconds
time.prepend( ':' );
time.prepend( zeroPad( m % 60 ) ); //minutes
time.prepend( ':' );
time.prepend( QString::number( h ) ); //hours
return time;
}
void
Slider::setValue( int newValue )
{
static QLabel *w1 = 0;
static QLabel *w2 = 0;
if (!w1) {
w1 = new QLabel( this );
w1->setPalette( QToolTip::palette() );
w1->setFrameStyle( QFrame::Plain | QFrame::Box );
w2 = new QLabel( this );
w2->setPalette( QToolTip::palette() );
w2->setFrameStyle( QFrame::Plain | QFrame::Box );
}
//TODO stupidly inefficeint! :)
w1->setShown( mainWindow()->isFullScreen() );
w2->setShown( mainWindow()->isFullScreen() );
//don't adjust the slider while the user is dragging it!
if( !m_sliding || m_outside ) {
const int l = engine()->length() / 1000;
const int left = int(l * (newValue / 65535.0));
const int right = l - left;
QSlider::setValue( newValue );
w1->move( 0, height() - w1->height() - 1 );
w1->setText( timeAsString( left ) + ' ' );
w1->adjustSize();
w2->move( width() - w2->width(), height() - w1->height() - 1 );
w2->setText( timeAsString( right ) + ' ' );
w2->adjustSize();
}
else
m_prevValue = newValue;
}

@ -0,0 +1,52 @@
// (c) 2004 Max Howell (max.howell@methylblue.com)
// See COPYING file for licensing information
#ifndef CODEINESLIDER_H
#define CODEINESLIDER_H
#include <qslider.h>
namespace Codeine
{
class Slider : public QSlider
{
Q_OBJECT
public:
static Slider *instance() { return s_instance; }
public:
Slider( QWidget*, uint max = 0 );
virtual void setValue( int );
signals:
//we emit this when the user has specifically changed the slider
//so connect to it if valueChanged() is too generic
//Qt also emits valueChanged( int )
void sliderReleased( uint );
protected:
virtual void wheelEvent( QWheelEvent* );
virtual void mouseMoveEvent( QMouseEvent* );
virtual void mouseReleaseEvent( QMouseEvent* );
virtual void mousePressEvent( QMouseEvent* );
virtual void keyPressEvent( QKeyEvent *e ) { e->ignore(); } //so that MainWindow gets the keypress
virtual QSize sizeHint() const { return QSlider::sizeHint() + QSize( 0, 6 ); }
virtual QSize minimumSizeHint() const { return sizeHint(); }
bool m_sliding;
private:
static Slider *s_instance;
bool m_outside;
int m_prevValue;
Slider( const Slider& ); //undefined
Slider &operator=( const Slider& ); //undefined
};
}
#endif

@ -0,0 +1,195 @@
// Copyright 2004 Max Howell (max.howell@methylblue.com)
// See COPYING file for licensing information
#include "actions.h"
#include "adjustSizeButton.h"
#include "debug.h"
#include "mainWindow.h"
#include <kconfig.h>
#include <kglobal.h>
#include "mxcl.library.h"
#include <qapplication.h>
#include <qevent.h>
#include <qlabel.h>
#include <qpopupmenu.h>
#include <qslider.h>
#include "theStream.h"
#include "videoSettings.h" //FIXME unfortunate
#include "xineEngine.h"
//TODO do in Sconstruct
#define QT_FATAL_ASSERT
//TODO make the XineEngine into xine::Stream and then make singleton and add functions like Stream::hasVideo() etc.
//TODO make convenience function to get fullscreen state
namespace Codeine {
void
MainWindow::engineStateChanged( Engine::State state )
{
Q_ASSERT( state != Engine::Uninitialised );
KURL const &url = TheStream::url();
bool const isFullScreen = toggleAction("fullscreen")->isChecked();
QWidget *const toolbar = reinterpret_cast<QWidget*>(toolBar());
Debug::Block block( state == Engine::Empty
? "State: Empty" : state == Engine::Loaded
? "State: Loaded" : state == Engine::Playing
? "State: Playing" : state == Engine::Paused
? "State: Paused" : state == Engine::TrackEnded
? "State: TrackEnded" : "State: Unknown" );
/// update actions
{
using namespace Engine;
#define enableIf( name, criteria ) action( name )->setEnabled( state & criteria );
enableIf( "stop", (Playing | Paused) );
enableIf( "fullscreen", (Playing | Paused) );
enableIf( "reset_zoom", ~Empty && !isFullScreen );
enableIf( "information", ~Empty );
enableIf( "video_settings", (Playing | Paused) );
enableIf( "volume", (Playing | Paused) );
#undef enableIf
toggleAction( "play" )->setChecked( state == Playing );
//FIXME bad design to do this way
QSlider *volume = (QSlider*)toolBar()->child( "volume" );
if (volume)
volume->setValue( engine()->volume() );
}
/// update VideoSettingsDialog instance
VideoSettingsDialog::stateChanged( this, state );
/// update menus
{
using namespace Engine;
// the toolbar play button is always enabled, but the menu item
// is disabled if we are empty, this looks more sensible
QPopupMenu * const file_menu = menu( "file" );
QPopupMenu * const settings_menu = menu( "settings" );
const int play_id = file_menu->idAt( 2 );
file_menu->setItemEnabled( play_id, state != Empty );
// menus are clearer when handled differently to toolbars
// KDE has a shit special action for this, but it stupidly changes
// the toolbar icon too.
// TODO do this from the playAction since we do it in context menu too
const KGuiItem item = (state == Playing) ? KGuiItem( i18n("&Pause"), "player_pause" ) : KGuiItem( i18n("&Play"), "player_play" );
file_menu->changeItem( play_id, item.iconSet(), item.text() );
file_menu->setItemChecked( play_id, false );
settings_menu->setItemEnabled( AspectRatioMenuItemId, state & (Playing | Paused) && TheStream::hasVideo() );
// set correct aspect ratio
if( state == Loaded )
static_cast<QPopupMenu*>(child( "aspect_ratio_menu" ))->setItemChecked( TheStream::aspectRatio(), true );
}
/// update statusBar
{
using namespace Engine;
m_analyzer->setShown( state & (Playing | Paused) && TheStream::hasAudio() );
m_timeLabel->setShown( state & (Playing | Paused) );
}
/// update position slider
switch( state )
{
case Engine::Empty:
m_positionSlider->setEnabled( false );
break;
case Engine::Loaded:
case Engine::TrackEnded:
m_positionSlider->setValue( 0 );
// NO BREAK!
case Engine::Playing:
case Engine::Paused:
m_positionSlider->setEnabled( TheStream::canSeek() );
break;
}
/// update recent files list if necessary
if( state == Engine::Loaded ) {
// update recently played list
#ifndef NO_SKIP_PR0N
// ;-)
const QString url_string = url.url();
if( !(url_string.contains( "porn", false ) || url_string.contains( "pr0n", false )) )
#endif
if( url.protocol() != "dvd" && url.protocol() != "vcd" ) {
KConfig *config = Codeine::config( "General" );
const QString prettyUrl = url.prettyURL();
QStringList urls = config->readPathListEntry( "Recent Urls" );
urls.remove( prettyUrl );
config->writePathEntry( "Recent Urls", urls << prettyUrl );
}
if( TheStream::hasVideo() && !isFullScreen )
new AdjustSizeButton( reinterpret_cast<QWidget*>(videoWindow()) );
}
/// set titles
switch( state )
{
case Engine::Empty:
m_titleLabel->setText( i18n("No media loaded") );
break;
case Engine::Paused:
m_titleLabel->setText( i18n("Paused") );
break;
case Engine::Loaded:
case Engine::Playing:
case Engine::TrackEnded:
m_titleLabel->setText( TheStream::prettyTitle() );
break;
}
/// set toolbar states
QWidget *dvd_button = (QWidget*)toolBar()->child( "toolbutton_toggle_dvd_menu" );
if (dvd_button)
dvd_button->setShown( state != Engine::Empty && url.protocol() == "dvd" );
if( isFullScreen && !toolbar->hasMouse() ) {
switch( state ) {
case Engine::TrackEnded:
toolbar->show();
if( videoWindow()->isActiveWindow() ) {
//FIXME dual-screen this seems to still show
QContextMenuEvent e( QContextMenuEvent::Other, QPoint(), Qt::MetaButton );
QApplication::sendEvent( videoWindow(), &e );
}
break;
case Engine::Empty:
case Engine::Loaded:
case Engine::Paused:
toolBar()->show();
break;
case Engine::Playing:
toolBar()->hide();
break;
}
}
}
}

@ -0,0 +1,144 @@
// (C) 2005 Max Howell (max.howell@methylblue.com)
// See COPYING file for licensing information
#include <kurl.h>
#include "mxcl.library.h"
#include "theStream.h"
#include <xine.h>
#include "xineEngine.h"
namespace Codeine
{
#define e VideoWindow::s_instance
KConfig*
TheStream::profile()
{
//TODO a unique id for discs, and then even to also record chapters etc.
// if( url().protocol() == "dvd" )
// return Codeine::config( QString( "dvd:/" ) + prettyTitle() );
// else
return Codeine::config( url().prettyURL() );
}
const KURL&
TheStream::url()
{ return e->m_url; }
bool
TheStream::canSeek()
//FIXME!
{ return e->m_url.protocol() != "http"; }
bool
TheStream::hasAudio()
{ return xine_get_stream_info( e->m_stream, XINE_STREAM_INFO_HAS_AUDIO ); }
bool
TheStream::hasVideo()
{ return xine_get_stream_info( e->m_stream, XINE_STREAM_INFO_HAS_VIDEO ); }
QSize
TheStream::defaultVideoSize()
{
return !e->m_stream
? QSize()
: QSize(
xine_get_stream_info( e->m_stream, XINE_STREAM_INFO_VIDEO_WIDTH ),
xine_get_stream_info( e->m_stream, XINE_STREAM_INFO_VIDEO_HEIGHT ) );
}
int TheStream::aspectRatio()
{ return xine_get_param( e->m_stream, XINE_PARAM_VO_ASPECT_RATIO ); }
int TheStream::subtitleChannel()
{ return xine_get_param( e->m_stream, XINE_PARAM_SPU_CHANNEL ); }
int TheStream::audioChannel()
{ return xine_get_param( e->m_stream, XINE_PARAM_AUDIO_CHANNEL_LOGICAL ); }
QString
TheStream::prettyTitle()
{
const KURL &url = e->m_url;
const QString artist = QString::fromUtf8( xine_get_meta_info( e->m_stream, XINE_META_INFO_ARTIST ) );
const QString title = QString::fromUtf8( xine_get_meta_info( e->m_stream, XINE_META_INFO_TITLE ) );
if (hasVideo() && !title.isEmpty())
return title;
else if (!title.isEmpty() && !artist.isEmpty())
return artist + " - " + title;
else if (url.protocol() != "http" && !url.fileName().isEmpty()) {
const QString n = url.fileName();
return KURL::decode_string( n.left( n.findRev( '.' ) ).replace( '_', ' ' ) ); }
else
return url.prettyURL();
}
static inline QString
entryHelper( const QString &plate, const QString &s1, const QString &s2 )
{
return s2.isEmpty() ? s2 : plate.arg( s1 ).arg( s2 );
}
static inline QString
sectionHelper( const QString &sectionTitle, const QStringList &entries )
{
QString s;
foreach( entries )
if( !(*it).isEmpty() )
s += *it;
return s.isEmpty() ? s : "<h2>" + sectionTitle + "</h2>" + s;
}
QString
TheStream::information()
{
#define meta( x ) xine_get_meta_info( e->m_stream, x )
#define info( x, y ) x.arg( xine_get_stream_info( e->m_stream, y ) )
#define simple( x ) QString::number( xine_get_stream_info( e->m_stream, x ) )
const QString plate = "<p><b>%1</b>: %2</p>";
QString s;
s += sectionHelper( i18n("Metadata"),
QStringList()
<< entryHelper( plate, i18n("Title"), meta( XINE_META_INFO_TITLE ) )
<< entryHelper( plate, i18n("Comment"), meta( XINE_META_INFO_COMMENT ) )
<< entryHelper( plate, i18n("Artist"), meta( XINE_META_INFO_ARTIST ) )
<< entryHelper( plate, i18n("Genre"), meta( XINE_META_INFO_GENRE ) )
<< entryHelper( plate, i18n("Album"), meta( XINE_META_INFO_ALBUM ) )
<< entryHelper( plate, i18n("Year"), meta( XINE_META_INFO_YEAR ) ) );
s += sectionHelper( i18n("Audio Properties"),
QStringList()
<< entryHelper( plate, i18n("Bitrate"), info( i18n("%1 bps"), XINE_STREAM_INFO_AUDIO_BITRATE ) )
<< entryHelper( plate, i18n("Sample-rate"), info( i18n("%1 Hz"), XINE_STREAM_INFO_AUDIO_SAMPLERATE ) ) );
s += sectionHelper( i18n("Technical Information"),
QStringList()
<< entryHelper( plate, i18n("Video Codec"), meta( XINE_META_INFO_VIDEOCODEC ) )
<< entryHelper( plate, i18n("Audio Codec"), meta( XINE_META_INFO_AUDIOCODEC ) )
<< entryHelper( plate, i18n("System Layer"), meta( XINE_META_INFO_SYSTEMLAYER ) )
<< entryHelper( plate, i18n("Input Plugin"), meta( XINE_META_INFO_INPUT_PLUGIN ))
<< entryHelper( plate, i18n("CDINDEX_DISCID"), meta( XINE_META_INFO_CDINDEX_DISCID ) ) );
QStringList texts;
texts << "BITRATE" << "SEEKABLE" << "VIDEO_WIDTH" << "VIDEO_HEIGHT" << "VIDEO_RATIO" << "VIDEO_CHANNELS" << "VIDEO_STREAMS" << "VIDEO_BITRATE" << "VIDEO_FOURCC" << "VIDEO_HANDLED" << "FRAME_DURATION" << "AUDIO_CHANNELS" << "AUDIO_BITS" << "-AUDIO_SAMPLERATE" << "-AUDIO_BITRATE" << "AUDIO_FOURCC" << "AUDIO_HANDLED" << "HAS_CHAPTERS" << "HAS_VIDEO" << "HAS_AUDIO" << "-IGNORE_VIDEO" << "-IGNORE_AUDIO" << "-IGNORE_SPU" << "VIDEO_HAS_STILL" << "MAX_AUDIO_CHANNEL" << "MAX_SPU_CHANNEL" << "AUDIO_MODE" << "SKIPPED_FRAMES" << "DISCARDED_FRAMES";
s += "<h2>Other</h2>";
for( uint x = 0; x <= 28; ++x )
s += entryHelper( plate, texts[x], simple( x ) );
#undef meta
#undef info
#undef simple
return s;
}
#undef e
}

@ -0,0 +1,50 @@
// (C) 2005 Max Howell (max.howell@methylblue.com)
// See COPYING file for licensing information
#ifndef CODEINE_THESTREAM_H
#define CODEINE_THESTREAM_H
#include "config.h" // needed for inline functions
#include <kurl.h> // larger :( but no macros at least
#include <qsize.h> // small header
#include <qstring.h> // small header
/// for purely static classes
#define CODEINE_NO_EXPORT( T ) \
T(); \
~T(); \
T( const T& ); \
T &operator=( const T& ); \
bool operator==( const T& ); \
bool operator!=( const T& );
namespace Codeine
{
class TheStream
{
CODEINE_NO_EXPORT( TheStream )
public:
static const KURL &url();
static bool canSeek();
static bool hasAudio();
static bool hasVideo();
static QSize defaultVideoSize();
static int aspectRatio();
static int subtitleChannel();
static int audioChannel();
static QString prettyTitle();
static QString information();
static inline bool hasProfile()
{ return KGlobal::config()->hasGroup( url().prettyURL() ); }
static KConfig *profile();
};
}
#endif

@ -0,0 +1,135 @@
// (C) 2005 Max Howell (max.howell@methylblue.com)
// See COPYING file for licensing information
#include <kwin.h>
#include "mxcl.library.h"
#include <qlabel.h>
#include <qlayout.h>
#include <qslider.h>
#include "videoSettings.h"
#include <xine.h>
#include "xineEngine.h"
extern "C"
{
// #include <X11/Xlib.h> is just dangerous! Here, there is a macro for Below that conflicts
// with QSlider::Below. Stupid X11 people.
typedef unsigned long XID;
typedef XID Window;
extern int XSetTransientForHint( Display*, Window, Window );
}
//TODO update from engine when new video is played
//TODO show a warning that when paused the changes aren't updated to the display, show an unpause button too
class SnapSlider : public QSlider
{
int m_offset;
public:
SnapSlider( const int value, QWidget *parent, const char *name )
: QSlider( (65536/4)-1, (3*(65536/4))-1, 1000, value, Qt::Horizontal, parent, name )
, m_offset( 0 )
{
setTickmarks( QSlider::Below );
setTickInterval( 65536 / 4 );
setMinimumWidth( fontMetrics().width( name ) * 3 );
connect( this, SIGNAL(valueChanged( int )), Codeine::engine(), SLOT(setStreamParameter( int )) );
}
virtual void mousePressEvent( QMouseEvent *e )
{
m_offset = e->pos().x() - (sliderStart() + (sliderRect().width()/2));
QSlider::mousePressEvent( e );
}
virtual void mouseMoveEvent( QMouseEvent *e )
{
const int MIDDLE = width() / 2;
const int x = e->pos().x() - m_offset;
const int F = sliderRect().width() / 2;
if( x > MIDDLE - F && x < MIDDLE + F ) {
QMouseEvent e2( e->type(), QPoint( MIDDLE + m_offset, e->pos().y() ), e->button(), e->state() );
QSlider::mouseMoveEvent( &e2 );
QRangeControl::setValue( 65536 / 2 - 1 ); // to ensure we are absolutely exact
}
else
QSlider::mouseMoveEvent( e );
}
};
Codeine::VideoSettingsDialog::VideoSettingsDialog( QWidget *parent )
: KDialog( parent, "video_settings_dialog", false, WType_TopLevel | WDestructiveClose )
{
XSetTransientForHint( x11Display(), winId(), parent->winId() );
KWin::setType( winId(), NET::Utility );
KWin::setState( winId(), NET::SkipTaskbar );
QFrame *frame = new QFrame( this );
(new QVBoxLayout( this, 10 ))->addWidget( frame );
frame->setFrameStyle( QFrame::StyledPanel | QFrame::Sunken );
frame->setPaletteBackgroundColor( backgroundColor().dark( 102 ) );
QGridLayout *grid = new QGridLayout( frame, 4, 2, 15, 10 );
grid->setAutoAdd( true );
#define makeSlider( PARAM, name ) \
new QLabel( name, frame ); \
new SnapSlider( xine_get_param( *Codeine::engine(), PARAM ), frame, name );
makeSlider( XINE_PARAM_VO_BRIGHTNESS, "brightness" );
makeSlider( XINE_PARAM_VO_CONTRAST, "contrast" );
makeSlider( XINE_PARAM_VO_SATURATION, "saturation" );
makeSlider( XINE_PARAM_VO_HUE, "hue" );
#undef makeSlider
setCaption( i18n("Video Settings") );
setMaximumSize( sizeHint().width() * 5, sizeHint().height() );
KDialog::show();
}
void
Codeine::VideoSettingsDialog::stateChanged( QWidget *parent, Engine::State state ) //static
{
QWidget *me = (QWidget*)parent->child( "video_settings_dialog" );
if( !me )
return;
switch( state )
{
case Engine::Playing:
case Engine::Paused:
me->setEnabled( true );
break;
case Engine::Loaded:
#define update( param, name ) static_cast<QSlider*>(me->child( name ))->setValue( xine_get_param( *Codeine::engine(), param ) );
update( XINE_PARAM_VO_BRIGHTNESS, "brightness" );
update( XINE_PARAM_VO_CONTRAST, "contrast" );
update( XINE_PARAM_VO_SATURATION, "saturation" );
update( XINE_PARAM_VO_HUE, "hue" );
#undef update
default:
me->setEnabled( false );
break;
}
}
namespace Codeine
{
void showVideoSettingsDialog( QWidget *parent )
{
// ensure that the dialog is shown by deleting the old one
delete parent->child( "video_settings_dialog" );
new VideoSettingsDialog( parent );
}
}

@ -0,0 +1,26 @@
// (C) 2005 Max Howell (max.howell@methylblue.com)
// See COPYING file for licensing information
#ifndef CODEINE_VIDEO_SETTINGS_H
#define CODEINE_VIDEO_SETTINGS_H
#include "codeine.h"
#include <kdialog.h>
namespace Codeine
{
class VideoSettingsDialog : public KDialog
{
VideoSettingsDialog(); //disable
VideoSettingsDialog( const VideoSettingsDialog& ); //disable
VideoSettingsDialog &operator=( const VideoSettingsDialog& ); //disable
public:
VideoSettingsDialog( QWidget *parent );
static void stateChanged( QWidget *parent, Engine::State );
};
}
#endif

@ -0,0 +1,380 @@
// (C) 2005 Max Howell (max.howell@methylblue.com)
// See COPYING file for licensing information
#define CODEINE_DEBUG_PREFIX "VideoWindow"
#include "actions.h"
#include <cmath> //std::log10
#include <cstdlib>
#include "debug.h"
#include <kapplication.h> //::makeStandardCaption
#include <kconfig.h>
#include <kiconloader.h>
#include <kpopupmenu.h>
#include <kwin.h>
#include "mxcl.library.h"
#include <qcursor.h>
#include <qevent.h>
#include "slider.h"
#include "theStream.h"
#include <X11/Xlib.h>
#include <xine.h>
#include "xineEngine.h"
namespace Codeine
{
namespace X
{
// we get thread locks if we don't cache these values
// (I don't know which ones exactly)
Display *d;
int s, w;
}
void
VideoWindow::initVideo()
{
X::d = XOpenDisplay( std::getenv("DISPLAY") );
X::s = DefaultScreen( X::d );
X::w = winId();
XLockDisplay( X::d );
XSelectInput( X::d, X::w, ExposureMask );
{
using X::d; using X::s;
//these are Xlib macros
double w = DisplayWidth( d, s ) * 1000 / DisplayWidthMM( d, s );
double h = DisplayHeight( d, s ) * 1000 / DisplayHeightMM( d, s );
m_displayRatio = w / h;
}
connect( &m_timer, SIGNAL(timeout()), SLOT(hideCursor()) );
XUnlockDisplay( X::d );
}
void
VideoWindow::cleanUpVideo()
{
XCloseDisplay( X::d );
}
void*
VideoWindow::x11Visual() const
{
DEBUG_FUNC_INFO
x11_visual_t* visual = new x11_visual_t;
visual->display = X::d;
visual->screen = X::s;
visual->d = winId();//X::w;
visual->dest_size_cb = &VideoWindow::destSizeCallBack;
visual->frame_output_cb = &VideoWindow::frameOutputCallBack;
visual->user_data = (void*)this;
return visual;
}
void
VideoWindow::destSizeCallBack(
void* p, int /*video_width*/, int /*video_height*/,
double /*video_aspect*/, int* dest_width,
int* dest_height, double* dest_aspect )
{
if( !p )
return;
#define vw static_cast<VideoWindow*>(p)
*dest_width = vw->width();
*dest_height = vw->height();
*dest_aspect = vw->m_displayRatio;
}
void
VideoWindow::frameOutputCallBack(
void* p, int video_width, int video_height, double video_aspect,
int* dest_x, int* dest_y, int* dest_width, int* dest_height,
double* dest_aspect, int* win_x, int* win_y )
{
if( !p )
return;
*dest_x = 0;
*dest_y = 0 ;
*dest_width = vw->width();
*dest_height = vw->height();
*win_x = vw->x();
*win_y = vw->y();
*dest_aspect = vw->m_displayRatio;
// correct size with video aspect
// TODO what's this about?
if( video_aspect >= vw->m_displayRatio )
video_width = (int) ( (double) (video_width * video_aspect / vw->m_displayRatio + 0.5) );
else
video_height = (int) ( (double) (video_height * vw->m_displayRatio / video_aspect) + 0.5);
#undef vw
}
void
VideoWindow::contextMenuEvent( QContextMenuEvent *e )
{
e->accept();
KPopupMenu popup;
if( state() == Engine::Playing )
popup.insertItem( SmallIconSet("player_pause"), i18n("Pause"), 1 );
else
action( "play" )->plug( &popup );
popup.insertSeparator();
if( TheStream::url().protocol() == "dvd" )
action( "toggle_dvd_menu" )->plug( &popup ),
popup.insertSeparator();
if( !((KToggleAction*)actionCollection()->action( "fullscreen" ))->isChecked() )
action( "reset_zoom" )->plug( &popup );
action( "capture_frame" )->plug( &popup );
popup.insertSeparator();
action( "video_settings" )->plug( &popup );
popup.insertSeparator();
action( "fullscreen" )->plug( &popup );
//show zoom information?
if( e->state() & Qt::MetaButton ) { //only on track end, or for special users
popup.insertSeparator();
action( "file_quit" )->plug( &popup );
}
if( popup.exec( e->globalPos() ) == 1 && state() == Engine::Playing )
// we check we are still paused as the menu generates a modal event loop
// so anything might have happened in the meantime.
pause();
}
bool
VideoWindow::event( QEvent *e )
{
//TODO it would perhaps make things more responsive to
// deactivate mouse tracking and use the x11Event() function to transfer mouse move events?
// perhaps even better would be a x11 implementation
switch( e->type() )
{
case QEvent::DragEnter:
case QEvent::Drop:
//FIXME why don't we just ignore the event? It should propogate down
return QApplication::sendEvent( qApp->mainWidget(), e );
case QEvent::Resize:
if( !TheStream::url().isEmpty() ) {
const QSize defaultSize = TheStream::defaultVideoSize();
const bool notDefaultSize = width() != defaultSize.width() && height() != defaultSize.height();
Codeine::action( "reset_zoom" )->setEnabled( notDefaultSize );
//showOSD( i18n("Scale: %1%").arg( size()
}
break;
case QEvent::Leave:
m_timer.stop();
break;
// Xlib.h sucks fucking balls!!!!11!!1!
#undef FocusOut
case QEvent::FocusOut:
// if the user summons some dialog via a shortcut or whatever we need to ensure
// the mouse gets shown, because if it is modal, we won't get mouse events after
// it is shown! This works because we are always the focus widget.
// @see MainWindow::MainWindow where we setFocusProxy()
case QEvent::Enter:
case QEvent::MouseMove:
case QEvent::MouseButtonPress:
unsetCursor();
if( hasFocus() )
// see above comment
m_timer.start( CURSOR_HIDE_TIMEOUT, true );
break;
case QEvent::MouseButtonDblClick:
Codeine::action( "fullscreen" )->activate();
break;
default: ;
}
if( !m_xine )
return QWidget::event( e );
switch( e->type() )
{
case QEvent::Close:
stop();
return false;
case VideoWindow::ExposeEvent:
//see VideoWindow::x11Event()
return true;
// Xlib.h sucks fucking balls!!!!11!!1!
#undef KeyPress
case QEvent::KeyPress: {
if( m_url.protocol() != "dvd" )
// let MainWindow handle this
return QWidget::event( e );
//FIXME left and right keys don't work during DVDs
int keyCode = XINE_EVENT_INPUT_UP;
//#define XINE_EVENT_INPUT_UP 110
//#define XINE_EVENT_INPUT_DOWN 111
//#define XINE_EVENT_INPUT_LEFT 112
//#define XINE_EVENT_INPUT_RIGHT 113
//#define XINE_EVENT_INPUT_SELECT 114
switch( static_cast<QKeyEvent*>(e)->key() ) {
case Key_Return:
case Key_Enter: keyCode++;
case Key_Right: keyCode++;
case Key_Left: keyCode++;
case Key_Down: keyCode++;
case Key_Up:
{
//this whole shebang is cheeky as xine doesn't
//guarentee the codes will stay the same
xine_event_t xineEvent;
xineEvent.type = keyCode;
xineEvent.data = NULL;
xineEvent.data_length = 0;
xine_event_send( m_stream, &xineEvent );
return true;
}
default:
return false;
}
}
case QEvent::MouseButtonPress:
#define mouseEvent static_cast<QMouseEvent*>(e)
if( mouseEvent->button() != Qt::LeftButton )
return false;
mouseEvent->accept();
//FALL THROUGH
case QEvent::MouseMove:
{
x11_rectangle_t x11Rect;
xine_event_t xineEvent;
xine_input_data_t xineInput;
x11Rect.x = mouseEvent->x();
x11Rect.y = mouseEvent->y();
x11Rect.w = 0;
x11Rect.h = 0;
xine_gui_send_vo_data( m_stream, XINE_GUI_SEND_TRANSLATE_GUI_TO_VIDEO, (void*)&x11Rect );
xineEvent.type = e->type() == QEvent::MouseMove ? XINE_EVENT_INPUT_MOUSE_MOVE : XINE_EVENT_INPUT_MOUSE_BUTTON;
xineEvent.data = &xineInput;
xineEvent.data_length = sizeof( xine_input_data_t );
xineInput.button = 1; //HACK e->type() == QEvent::MouseMove ? 0 : 1;
xineInput.x = x11Rect.x;
xineInput.y = x11Rect.y;
xine_event_send( m_stream, &xineEvent );
return e->type() == QEvent::MouseMove ? false : true;
#undef mouseEvent
}
case QEvent::Wheel:
{
//TODO seek amount should depend on the length, basically seek at most say 30s, and at least 0.5s
//TODO this is replicated (somewhat) in MainWindow::keyPressEvent
int pos, time, length;
xine_get_pos_length( m_stream, &pos, &time, &length );
pos += int(std::log10( (double)length ) * static_cast<QWheelEvent*>(e)->delta());
seek( pos > 0 ? (uint)pos : 0 );
return true;
}
default: ;
}
return QWidget::event( e );
}
bool
VideoWindow::x11Event( XEvent *e )
{
if( m_stream && e->type == Expose && e->xexpose.count == 0 ) {
xine_gui_send_vo_data(
m_stream,
XINE_GUI_SEND_EXPOSE_EVENT,
e );
return true;
}
return false;
}
void
VideoWindow::hideCursor()
{
setCursor( Qt::BlankCursor );
}
QSize
VideoWindow::sizeHint() const //virtual
{
QSize s = TheStream::profile()->readSizeEntry( "Preferred Size" );
if( !s.isValid() )
s = TheStream::defaultVideoSize();
if( s.isValid() && !s.isNull() )
return s;
return minimumSizeHint();
}
QSize
VideoWindow::minimumSizeHint() const //virtual
{
const int x = fontMetrics().width( "x" ) * 4;
return QSize( x * 12, x * 4 ); //FIXME
}
void
VideoWindow::resetZoom()
{
TheStream::profile()->deleteEntry( "Preferred Size" );
topLevelWidget()->adjustSize();
}
} //namespace Codeine

@ -0,0 +1,114 @@
// (C) 2005 Max Howell (max.howell@methylblue.com)
// See COPYING file for licensing information
#include <klocale.h>
#include <ktoolbar.h>
#include <qevent.h>
#include <qlabel.h>
#include <qlayout.h>
#include <qslider.h>
#include "debug.h"
#include "volumeAction.h"
#include "volumeAction.moc"
#include "xineEngine.h"
class VolumeSlider : public QFrame
{
public:
VolumeSlider( QWidget *parent )
: QFrame( parent )
{
slider = new QSlider( Qt::Vertical, this, "volume" );
label = new QLabel( this );
QBoxLayout *lay = new QVBoxLayout( this );
lay->addWidget( slider, 0, Qt::AlignHCenter );
lay->addWidget( label, 0, Qt::AlignHCenter );
lay->setMargin( 4 );
slider->setRange( 0, 100 );
setFrameStyle( QFrame::Plain | QFrame::Box );
setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed );
hide();
}
QLabel *label;
QSlider *slider;
};
VolumeAction::VolumeAction( KToolBar *bar, KActionCollection *ac )
: KToggleAction( i18n("Volume"), "volume", Qt::Key_1, 0, 0, ac, "volume" )
, m_anchor( 0 )
{
m_widget = new VolumeSlider( bar->topLevelWidget() );
connect( this, SIGNAL(toggled( bool )), SLOT(toggled( bool )) );
connect( m_widget->slider, SIGNAL(sliderMoved( int )), SLOT(sliderMoved( int )) );
connect( m_widget->slider, SIGNAL(sliderMoved( int )), Codeine::engine(), SLOT(setStreamParameter( int )) );
connect( m_widget->slider, SIGNAL(sliderReleased()), SLOT(sliderReleased()) );
}
int
VolumeAction::plug( QWidget *bar, int index )
{
DEBUG_BLOCK
int const id = KAction::plug( bar, index );
m_anchor = (QWidget*)bar->child( "toolbutton_volume" ); //KAction creates it with this name
m_anchor->installEventFilter( this ); //so we can keep m_widget anchored
return id;
}
void
VolumeAction::toggled( bool const b )
{
DEBUG_BLOCK
m_widget->raise();
m_widget->setShown( b );
}
void
VolumeAction::sliderMoved( int v )
{
v = 100 - v; //Qt sliders are wrong way round when vertical
QString const t = QString::number( v ) + '%';
setToolTip( i18n( "Volume: %1" ).arg( t ) );
m_widget->label->setText( t );
}
bool
VolumeAction::eventFilter( QObject *o, QEvent *e )
{
switch (e->type()) {
case QEvent::Move:
case QEvent::Resize: {
QWidget const * const &a = m_anchor;
m_widget->move( a->mapTo( m_widget->parentWidget(), QPoint( 0, a->height() ) ) );
m_widget->resize( a->width(), m_widget->sizeHint().height() );
return false;
}
//TODO one click method, flawed currently in fullscreen mode by palette change in mainwindow.cpp
/* case QEvent::MouseButtonPress:
m_widget->show();
break;
case QEvent::MouseButtonRelease:
m_widget->hide();
break;*/
default:
return false;
}
}

@ -0,0 +1,29 @@
// (C) 2005 Max Howell (max.howell@methylblue.com)
// See COPYING file for licensing information
#ifndef CODEINE_VOLUME_ACTION_H
#define CODEINE_VOLUME_ACTION_H
#include <kactionclasses.h>
class VolumeAction : public KToggleAction
{
Q_OBJECT
QWidget *m_anchor;
class VolumeSlider *m_widget;
virtual bool eventFilter( QObject *o, QEvent *e );
virtual int plug( QWidget*, int );
private slots:
void toggled( bool );
void sliderMoved( int );
void sliderReleased() { setChecked( false ); toggled( false ); }
public:
VolumeAction( KToolBar *anchor, KActionCollection *ac );
};
#endif

@ -0,0 +1,321 @@
// (C) 2005 Max Howell (max.howell@methylblue.com)
// See COPYING file for licensing information
#include "debug.h"
#include <kapplication.h> // XineConfigDialog::ctor -> to get the iconloader
#include <kcombobox.h>
#include <kiconloader.h> // XineConfigDialog::ctor
#include <klineedit.h>
#include <kseparator.h>
#include <kstdguiitem.h>
#include <qcheckbox.h>
#include <qlabel.h>
#include <qlayout.h>
#include <qscrollview.h>
#include <qspinbox.h>
#include <qtabwidget.h>
#include <qtooltip.h>
#include <qvbox.h>
#include <xine.h>
#include "xineConfig.h"
QString i18n(const char *text);
KDialogBase *XineConfigDialog::s_instance = 0;
namespace Codeine
{
void
showXineConfigurationDialog( QWidget *parent, xine_t *xine )
{
XineConfigDialog d( xine, parent );
if( d.exec() == QDialog::Accepted )
d.saveSettings();
}
}
class TabWidget : public QTabWidget
{
public:
TabWidget( QWidget *parent ) : QTabWidget( parent ) {}
virtual QSize sizeHint() const
{
// Qt gives a stupid default sizeHint for this widget
return QSize(
reinterpret_cast<QWidget*>(tabBar())->sizeHint().width() + 5,
QTabWidget::sizeHint().height() );
}
};
///@class XineConfigDialog
XineConfigDialog::XineConfigDialog( xine_t *xine, QWidget *parent )
: KDialogBase( parent, "xine_config_dialog",
true, //modal
i18n("Configure xine"), User1 | Stretch | Ok | Cancel,
Ok, //default button
false, //draw separator
KStdGuiItem::reset() )
, m_xine( xine )
{
DEBUG_BLOCK
s_instance = this;
const int METRIC = fontMetrics().width( 'x' );
const int METRIC_3B2 = (3*METRIC)/2;
QVBox *box = new QVBox( this );
box->setSpacing( METRIC );
setMainWidget( box );
{
QHBox *hbox = new QHBox( box );
hbox->setSpacing( METRIC_3B2 );
hbox->setMargin( METRIC_3B2 );
QPixmap info = kapp->iconLoader()->loadIcon( "messagebox_info", KIcon::NoGroup, KIcon::SizeMedium, KIcon::DefaultState, 0, true );
QLabel *label = new QLabel( hbox );
label->setPixmap( info );
label->setSizePolicy( QSizePolicy::Maximum, QSizePolicy::Maximum );
label = new QLabel( i18n(
"xine's defaults are usually sensible and should not require modification. "
"However, full configurability is provided for your pleasure ;-)." ), hbox );
label->setAlignment( QLabel::WordBreak | QLabel::AlignVCenter );
}
//FIXME after many hours I have discovered that this
// widget somehow sets the minSize of this widget to 0,0
// whenever you resize the widget. WTF?
TabWidget *tabs = new TabWidget( box );
class XineConfigEntryIterator {
xine_t *m_xine;
xine_cfg_entry_t m_entry;
bool m_valid;
public:
XineConfigEntryIterator( xine_t *xine ) : m_xine( xine ) { m_valid = xine_config_get_first_entry( m_xine, &m_entry ); }
inline XineConfigEntryIterator &operator++() { m_valid = xine_config_get_next_entry( m_xine, &m_entry ); return *this; }
inline xine_cfg_entry_t *operator*() { return m_valid ? &m_entry : 0; }
};
QGridLayout *grid = 0;
QString currentPage;
QScrollView *view = 0;
parent = 0;
for( XineConfigEntryIterator it( m_xine ); *it; ++it )
{
const QString pageName = QString::fromUtf8( (*it)->key ).section( '.', 0, 0 );
if( (QStringList() << "ui" << "effects" << "subtitles").contains( pageName ) )
continue;
if( pageName != currentPage ) {
if( view )
//NOTE won't be executed for last tab
view->viewport()->setMinimumWidth( grid->sizeHint().width() ); // seems necessary
QString pageTitle = pageName;
pageTitle[0] = pageTitle[0].upper();
tabs->addTab( view = new QScrollView, pageTitle );
view->setResizePolicy( QScrollView::AutoOneFit );
view->setHScrollBarMode( QScrollView::AlwaysOff );
view->setFrameShape( QFrame::NoFrame );
view->addChild( parent = new QWidget( view->viewport() ) );
QBoxLayout *layout = new QVBoxLayout( parent, /*margin*/METRIC_3B2, /*spacing*/0 );
parent = new QFrame( parent );
static_cast<QFrame*>(parent)->setFrameStyle( QFrame::Panel | QFrame::Raised );
static_cast<QFrame*>(parent)->setLineWidth( 2 );
grid = new QGridLayout( parent, /*rows*/0, /*cols*/2, /*margin*/20, /*spacing*/int(METRIC*2.5) );
grid->setColStretch( 0, 3 );
grid->setColStretch( 1, 2 );
layout->addWidget( parent, 0 );
layout->addStretch( 1 );
currentPage = pageName;
}
m_entrys.append( new XineConfigEntry( parent, grid, *it ) );
}
//finishing touches
m_entrys.setAutoDelete( true );
enableButton( Ok, false );
enableButton( User1, false );
Q_ASSERT( !isUnsavedSettings() );
}
void
XineConfigDialog::slotHelp()
{
/// HACK called when a widget's input value changes
const bool b = isUnsavedSettings();
enableButton( Ok, b );
enableButton( User1, b );
}
void
XineConfigDialog::slotUser1()
{
for( QPtrListIterator<XineConfigEntry> it( m_entrys ); *it != 0; ++it )
(*it)->reset();
slotHelp();
}
bool
XineConfigDialog::isUnsavedSettings() const
{
for( QPtrListIterator<XineConfigEntry> it( m_entrys ); *it != 0; ++it )
if( (*it)->isChanged() )
return true;
return false;
}
#include <qdir.h>
void
XineConfigDialog::saveSettings()
{
for( XineConfigEntry *entry = m_entrys.first(); entry; entry = m_entrys.next() )
if( entry->isChanged() )
entry->save( m_xine );
xine_config_save( m_xine, QFile::encodeName( QDir::homeDirPath() + "/.xine/config" ) );
}
///@class XineConfigEntry
XineConfigEntry::XineConfigEntry( QWidget *parent, QGridLayout *grid, xine_cfg_entry_t *entry )
: m_widget( 0 )
, m_key( entry->key )
, m_string( entry->str_value )
, m_number( entry->num_value )
{
QWidget *&w = m_widget;
const char *signal = 0;
const int row = grid->numRows();
QString description_text = QString::fromUtf8( entry->description );
description_text[0] = description_text[0].upper();
switch( entry->type )
{
case XINE_CONFIG_TYPE_STRING: {
w = new KLineEdit( m_string, parent );
signal = SIGNAL(textChanged( const QString& ));
break;
}
case XINE_CONFIG_TYPE_ENUM: {
w = new KComboBox( parent );
for( int i = 0; entry->enum_values[i]; ++i )
((KComboBox*)w)->insertItem( QString::fromUtf8( entry->enum_values[i] ) );
((KComboBox*)w)->setCurrentItem( m_number );
signal = SIGNAL(activated( int ));
break;
}
case XINE_CONFIG_TYPE_RANGE:
case XINE_CONFIG_TYPE_NUM: {
w = new QSpinBox(
QMIN( m_number, entry->range_min ), // xine bug, sometimes the min and max ranges
QMAX( m_number, entry->range_max ), // are both 0 even though this is bullshit
1, parent );
((QSpinBox*)w)->setValue( m_number );
signal = SIGNAL(valueChanged( int ));
break;
}
case XINE_CONFIG_TYPE_BOOL: {
w = new QCheckBox( description_text, parent );
((QCheckBox*)w)->setChecked( m_number );
connect( w, SIGNAL(toggled( bool )), XineConfigDialog::instance(), SLOT(slotHelp()) );
QToolTip::add( w, "<qt>" + QString::fromUtf8( entry->help ) );
grid->addMultiCellWidget( w, row, row, 0, 1 );
return; //no need for a description label
}
default:
;
}
connect( w, signal, XineConfigDialog::instance(), SLOT(slotHelp()) );
QLabel *description = new QLabel( description_text + ':', parent );
description->setAlignment( QLabel::WordBreak | QLabel::AlignVCenter );
const QString tip = "<qt>" + QString::fromUtf8( entry->help );
QToolTip::add( w, tip );
QToolTip::add( description, tip );
// grid->addWidget( description, row, 0, Qt::AlignVCenter );
grid->addWidget( w, row, 1, Qt::AlignTop );
}
bool
XineConfigEntry::isChanged() const
{
#define _( x ) static_cast<x*>(m_widget)
switch( classType( m_widget->className() ) ) {
case LineEdit: return _(KLineEdit)->text().utf8() != m_string;
case ComboBox: return _(KComboBox)->currentItem() != m_number;
case SpinBox: return _(QSpinBox)->value() != m_number;
case CheckBox: return _(QCheckBox)->isChecked() != m_number;
}
return false;
}
void
XineConfigEntry::reset()
{
// this is because we only get called by the XineConfigDialog reset button
// and we don't want to cause a check for Ok/Reset button enabled state for
// every XineConfigEntry
m_widget->blockSignals( true );
switch( classType( m_widget->className() ) ) {
case LineEdit: _(KLineEdit)->setText( m_string ); break;
case ComboBox: _(KComboBox)->setCurrentItem( m_number ); break;
case SpinBox: _(QSpinBox)->setValue( m_number ); break;
case CheckBox: _(QCheckBox)->setChecked( (bool)m_number ); break;
}
m_widget->blockSignals( false );
}
void
XineConfigEntry::save( xine_t *xine )
{
xine_cfg_entry_t ent;
if( xine_config_lookup_entry( xine, key(), &ent ) )
{
switch( classType( m_widget->className() ) ) {
case LineEdit: m_string = _(KLineEdit)->text().utf8(); break;
case ComboBox: m_number = _(KComboBox)->currentItem(); break;
case SpinBox: m_number = _(QSpinBox)->value(); break;
case CheckBox: m_number = _(QCheckBox)->isChecked(); break;
}
ent.str_value = qstrdup( m_string );
ent.num_value = m_number;
debug() << "Saving setting: " << key() << endl;
xine_config_update_entry( xine, &ent );
}
else
Debug::warning() << "Couldn't save: " << key() << endl;
#undef _
}

@ -0,0 +1,69 @@
// (C) 2005 Max Howell (max.howell@methylblue.com)
// See COPYING file for licensing information
#ifndef XINECONFIG_H
#define XINECONFIG_H
#include <kdialogbase.h>
#include <qptrlist.h>
class KComboBox;
class KLineEdit;
class QCheckBox;
class QGridLayout;
class QSpinBox;
typedef struct xine_s xine_t;
typedef struct xine_cfg_entry_s xine_cfg_entry_t;
///stores a single config entry of the config file
class XineConfigEntry : public QObject
{
enum ClassType { LineEdit, ComboBox, SpinBox, CheckBox };
QWidget *m_widget;
QCString m_key;
QCString m_string;
int m_number;
static inline ClassType classType( const QCString &name )
{
return name == "KLineEdit" ? LineEdit
: name == "KComboBox" ? ComboBox
: name == "QSpinBox" ? SpinBox : CheckBox;
}
public:
XineConfigEntry( QWidget *parent, QGridLayout*, xine_cfg_entry_t* );
bool isChanged() const;
void save( xine_t* );
void reset();
inline const QCString &key() const { return m_key; }
};
class XineConfigDialog : public KDialogBase
{
static KDialogBase *s_instance;
QPtrList<XineConfigEntry> m_entrys;
xine_t *m_xine;
public:
XineConfigDialog( xine_t *xine, QWidget *parent );
bool isUnsavedSettings() const;
void saveSettings();
static KDialogBase *instance() { return s_instance; }
protected:
virtual void slotUser1();
virtual void slotHelp();
};
#endif

@ -0,0 +1,876 @@
// (C) 2005 Max Howell (max.howell@methylblue.com)
// See COPYING file for licensing information
#define CODEINE_DEBUG_PREFIX "engine"
#include "actions.h" //::seek() FIXME unfortunate
#include <cmath> //the fade out
#include "config.h"
#include "debug.h"
#include <limits>
#include <klocale.h>
#include "mxcl.library.h"
#include <qapplication.h> //::sendEvent()
#include <qdatetime.h> //record()
#include <qdir.h> //::exists()
#include "slider.h"
#include "theStream.h"
#include <xine.h>
#include "xineEngine.h"
#include "xineScope.h"
#define XINE_SAFE_MODE 1
extern "C" { void _debug( const char *string ) { debug() << string; } } //FIXME
namespace Codeine {
VideoWindow *VideoWindow::s_instance = 0;
VideoWindow::VideoWindow( QWidget *parent )
: QWidget( parent, "VideoWindow" )
, m_osd( 0 )
, m_stream( 0 )
, m_eventQueue( 0 )
, m_videoPort( 0 )
, m_audioPort( 0 )
, m_scope( 0 )
, m_xine( 0 )
, m_current_vpts( 0 )
{
DEBUG_BLOCK
s_instance = this;
setWFlags( Qt::WNoAutoErase );
setMouseTracking( true );
setAcceptDrops( true );
setUpdatesEnabled( false ); //to stop Qt drawing over us
setPaletteBackgroundColor( Qt::black );
setFocusPolicy( ClickFocus );
//TODO sucks
//TODO namespace this?
myList->next = myList; //init the buffer list
}
VideoWindow::~VideoWindow()
{
DEBUG_BLOCK
eject();
// fade out volume on exit
if( m_stream && xine_get_status( m_stream ) == XINE_STATUS_PLAY ) {
int cum = 0;
for( int v = 99; v >= 0; v-- ) {
xine_set_param( m_stream, XINE_PARAM_AUDIO_AMP_LEVEL, v );
int sleep = int(32000 * (-std::log10( double(v + 1) ) + 2));
::usleep( sleep );
cum += sleep;
}
debug() << "Total sleep: " << cum << "x10^-6 s\n";
xine_stop( m_stream );
::sleep( 1 );
}
//xine_set_param( m_stream, XINE_PARAM_IGNORE_VIDEO, 1 );
if( m_osd ) xine_osd_free( m_osd );
if( m_stream ) xine_close( m_stream );
if( m_eventQueue ) xine_event_dispose_queue( m_eventQueue );
if( m_stream ) xine_dispose( m_stream );
if( m_audioPort ) xine_close_audio_driver( m_xine, m_audioPort );
if( m_videoPort ) xine_close_video_driver( m_xine, m_videoPort );
if( m_scope ) xine_post_dispose( m_xine, m_scope );
if( m_xine ) xine_exit( m_xine );
cleanUpVideo();
}
bool
VideoWindow::init()
{
DEBUG_BLOCK
initVideo();
debug() << "xine_new()\n";
m_xine = xine_new();
if( !m_xine )
return false;
#ifdef XINE_SAFE_MODE
xine_engine_set_param( m_xine, XINE_ENGINE_PARAM_VERBOSITY, 99 );
#endif
debug() << "xine_config_load()\n";
xine_config_load( m_xine, QFile::encodeName( QDir::homeDirPath() + "/.xine/config" ) );
debug() << "xine_init()\n";
xine_init( m_xine );
debug() << "xine_open_video_driver()\n";
m_videoPort = xine_open_video_driver( m_xine, "auto", XINE_VISUAL_TYPE_X11, videoWindow()->x11Visual() );
debug() << "xine_open_audio_driver()\n";
m_audioPort = xine_open_audio_driver( m_xine, "auto", NULL );
debug() << "xine_stream_new()\n";
m_stream = xine_stream_new( m_xine, m_audioPort, m_videoPort );
if( !m_stream )
return false;
// we do these after creating the stream as they are non-fatal
// and the messagebox creates a modal event loop that allows
// events that require a stream to have been created..
if( !m_videoPort )
MessageBox::error( i18n("xine was unable to initialize any video-drivers.") );
if( !m_audioPort )
MessageBox::error( i18n("xine was unable to initialize any audio-drivers.") );
debug() << "xine_osd_new()\n";
m_osd = xine_osd_new( m_stream, 10, 10, 1000, 18 * 6 + 10 );
if( m_osd ) {
xine_osd_set_font( m_osd, "sans", 18 );
xine_osd_set_text_palette( m_osd, XINE_TEXTPALETTE_WHITE_BLACK_TRANSPARENT, XINE_OSD_TEXT1 );
}
#ifndef XINE_SAFE_MODE
debug() << "scope_plugin_new()\n";
m_scope = scope_plugin_new( m_xine, m_audioPort );
//FIXME this one seems to make seeking unstable for Codeine, perhaps
xine_set_param( m_stream, XINE_PARAM_METRONOM_PREBUFFER, 6000 ); //less buffering, faster seeking..
// causes an abort currently
//xine_trick_mode( m_stream, XINE_TRICK_MODE_SEEK_TO_TIME, 1 );
#endif
{
typedef QValueList<int> List;
List params( List()
<< XINE_PARAM_VO_HUE << XINE_PARAM_VO_SATURATION << XINE_PARAM_VO_CONTRAST << XINE_PARAM_VO_BRIGHTNESS
<< XINE_PARAM_SPU_CHANNEL << XINE_PARAM_AUDIO_CHANNEL_LOGICAL << XINE_PARAM_VO_ASPECT_RATIO );
for( List::ConstIterator it = params.constBegin(), end = params.constEnd(); it != end; ++it )
debug1( xine_get_param( m_stream, *it ) );
}
debug() << "xine_event_create_listener_thread()\n";
xine_event_create_listener_thread( m_eventQueue = xine_event_new_queue( m_stream ), &VideoWindow::xineEventListener, (void*)this );
//set the UI up to a default state
announceStateChange();
startTimer( 200 ); //prunes the scope
return true;
}
void
VideoWindow::eject()
{
//WARNING! don't xine_stop or that, buggers up dtor
if( m_url.isEmpty() )
return;
KConfig *profile = TheStream::profile(); // the config profile for this video file
#define writeParameter( param, default ) { \
const int value = xine_get_param( m_stream, param ); \
const QString key = QString::number( param ); \
if( value != default ) \
profile->writeEntry( key, value ); \
else \
profile->deleteEntry( key ); }
writeParameter( XINE_PARAM_VO_HUE, 32768 );
writeParameter( XINE_PARAM_VO_SATURATION, 32772 );
writeParameter( XINE_PARAM_VO_CONTRAST, 32772 );
writeParameter( XINE_PARAM_VO_BRIGHTNESS, 32800 )
writeParameter( XINE_PARAM_SPU_CHANNEL, -1 );
writeParameter( XINE_PARAM_AUDIO_CHANNEL_LOGICAL, -1 );
writeParameter( XINE_PARAM_VO_ASPECT_RATIO, 0 );
#undef writeParameter
if( xine_get_status( m_stream ) == XINE_STATUS_PLAY && //XINE_STATUS_PLAY = playing OR paused
length() - time() > 5000 ) // if we are really close to the end, don't remember the position
profile->writeEntry( "Position", position() );
else
profile->deleteEntry( "Position" );
const QSize s = videoWindow()->size();
const QSize defaultSize = TheStream::defaultVideoSize();
if( s.width() == defaultSize.width() || s.height() == defaultSize.height() )
profile->deleteEntry( "Preferred Size" );
else
profile->writeEntry( "Preferred Size", s );
profile->sync();
m_url = KURL();
}
bool
VideoWindow::load( const KURL &url )
{
mxcl::WaitCursor allocateOnStack;
eject(); //save profile for this video
m_url = url;
// only gets shown if there is an error generally, as no event processing
// occurs, so no paint event. This is fine IMO, TODO although if xine_open hangs
// due to something, it would be good to show the message...
emit statusMessage( i18n("Loading media: %1" ).arg( url.fileName() ) );
debug() << "xine_open()\n";
if( xine_open( m_stream, url.url().local8Bit() ) )
{
KConfig *profile = TheStream::profile();
#define setParameter( param, default ) xine_set_param( m_stream, param, profile->readNumEntry( QString::number( param ), default ) );
setParameter( XINE_PARAM_VO_HUE, 32768 );
setParameter( XINE_PARAM_VO_SATURATION, 32772 );
setParameter( XINE_PARAM_VO_CONTRAST, 32772 );
setParameter( XINE_PARAM_VO_BRIGHTNESS, 32800 )
setParameter( XINE_PARAM_SPU_CHANNEL, -1 );
setParameter( XINE_PARAM_AUDIO_CHANNEL_LOGICAL, -1 );
setParameter( XINE_PARAM_VO_ASPECT_RATIO, 0 );
setParameter( XINE_PARAM_AUDIO_AMP_LEVEL, 100 );
#undef setParameter
videoWindow()->setShown( xine_get_stream_info( m_stream, XINE_STREAM_INFO_HAS_VIDEO ) );
//TODO popup message for no audio
//TODO popup message for no video + no audio
#ifndef XINE_SAFE_MODE
// ensure old buffers are deleted
// FIXME leaves one erroneous buffer
timerEvent( 0 );
if( m_scope ) {
xine_post_out_t *source = xine_get_audio_source( m_stream );
xine_post_in_t *target = (xine_post_in_t*)xine_post_input( m_scope, const_cast<char*>("audio in") );
xine_post_wire( source, target );
}
#endif
announceStateChange();
return true;
}
showErrorMessage();
announceStateChange();
m_url = KURL();
return false;
}
bool
VideoWindow::play( uint offset )
{
mxcl::WaitCursor allocateOnStack;
const bool resume = offset > 0 && /*FIXME*/ m_url.protocol() != "dvd";
if( resume )
//HACK because we have to do xine_play() the audio "stutters"
// so we mute it and then unmute it to make it sound better
xine_set_param( m_stream, XINE_PARAM_AUDIO_AMP_MUTE, 1 );
debug() << "xine_play()\n";
if( xine_play( m_stream, offset, 0 ) )
{
if( resume ) {
//we have to set this or it stays at 0
Slider::instance()->setValue( offset );
// we come up paused if we are resuming playback from a previous session
pause();
// see above from HACK
xine_set_param( m_stream, XINE_PARAM_AUDIO_AMP_MUTE, 0 );
}
else
announceStateChange();
return true;
}
showErrorMessage();
return false;
}
void
VideoWindow::record()
{
xine_cfg_entry_t config;
if( xine_config_lookup_entry( m_xine, "misc.save_dir", &config ) )
{
//TODO which fricking KDE function tells me this? Who can tell, stupid KDE API
QDir d( QDir::home().filePath( "Desktop" ) );
config.str_value = qstrdup( d.exists() //FIXME tiny-mem-leak, *shrug*
? d.path().utf8()
: QDir::homeDirPath().utf8() );
xine_config_update_entry( m_xine, &config );
const QString fileName = m_url.filename();
QString
url = m_url.url();
url += "#save:";
url += m_url.host();
url += " [";
url += QDate::currentDate().toString();
url += ']';
url += fileName.mid( fileName.findRev( '.' ) + 1 ).lower();
xine_open( m_stream, url.local8Bit() );
xine_play( m_stream, 0, 0 );
emit statusMessage( i18n( "Recording to: %1" ).arg( url ) );
debug() << url << endl;
}
else
debug() << "unable to set misc.save_dir\n";
}
void
VideoWindow::stop()
{
xine_stop( m_stream );
announceStateChange();
}
void
VideoWindow::pause()
{
if( xine_get_status( m_stream ) == XINE_STATUS_STOP )
play();
else if( m_url.protocol() == "http" )
// we are playing and it's an HTTP stream
stop();
else if( xine_get_param( m_stream, XINE_PARAM_SPEED ) ) {
// do first because xine is slow to pause and is bad feedback otherwise
emit stateChanged( Engine::Paused );
xine_set_param( m_stream, XINE_PARAM_SPEED, XINE_SPEED_PAUSE );
xine_set_param( m_stream, XINE_PARAM_AUDIO_CLOSE_DEVICE, 1);
showOSD( i18n( "Playback paused" ) );
}
else {
xine_set_param( m_stream, XINE_PARAM_SPEED, XINE_SPEED_NORMAL );
announceStateChange();
showOSD( i18n( "Playback resumed" ) );
}
}
void
VideoWindow::showErrorMessage()
{
const QString name = m_url.fileName();
debug() << "xine_get_error()\n";
switch( xine_get_error( m_stream ) )
{
case XINE_ERROR_NO_INPUT_PLUGIN:
MessageBox::sorry( i18n("There is no input plugin that can read: %1.").arg( name ) );
break;
case XINE_ERROR_NO_DEMUX_PLUGIN:
MessageBox::sorry( i18n("There is no demux plugin available for %1.").arg( name ) );
break;
case XINE_ERROR_DEMUX_FAILED:
MessageBox::sorry( i18n("Demuxing failed for %1.").arg( name ) );
break;
case XINE_ERROR_INPUT_FAILED:
case XINE_ERROR_MALFORMED_MRL:
case XINE_ERROR_NONE:
MessageBox::sorry( i18n("Internal error while attempting to play %1.").arg( name ) );
break;
}
}
Engine::State
VideoWindow::state() const
{
//FIXME this is for the analyzer, but I don't like the analyzer being dodgy like this
if( !m_xine || !m_stream )
return Engine::Uninitialised;
switch( xine_get_status( m_stream ) )
{
case XINE_STATUS_PLAY: return xine_get_param( m_stream, XINE_PARAM_SPEED ) ? Engine::Playing : Engine::Paused;
case XINE_STATUS_IDLE: return Engine::Empty; //FIXME this route never used!
case XINE_STATUS_STOP:
default: return m_url.isEmpty() ? Engine::Empty : Engine::Loaded;
}
}
uint
VideoWindow::posTimeLength( PosTimeLength type ) const
{
int pos = 0, time = 0, length = 0;
xine_get_pos_length( m_stream, &pos, &time, &length );
switch( type ) {
case Pos: return pos;
case Time: return time;
case Length: return length;
}
return 0; //--warning
}
uint
VideoWindow::volume() const
{
//TODO I don't like the design
return xine_get_param( m_stream, XINE_PARAM_AUDIO_AMP_LEVEL );
}
void
VideoWindow::seek( uint pos )
{
bool wasPaused = false;
// If we seek to the end the track ended event is sent, but it is
// delayed as it happens in xine-event loop and before that we are
// already processing the next seek event (if user uses mouse wheel
// or keyboard to seek) and this causes the ui to think video is
// stopped but xine is actually playing the track. Tada!
// TODO set state based on events from xine only
if( pos > 65534 )
pos = 65534;
switch( state() ) {
case Engine::Uninitialised:
//NOTE should never happen
Debug::warning() << "Seek attempt thwarted! xine not initialised!\n";
return;
case Engine::Empty:
Debug::warning() << "Seek attempt thwarted! No media loaded!\n";
return;
case Engine::Loaded:
// then the state is changing and we should announce it
play( pos );
return;
case Engine::Paused:
// xine_play unpauses stream if stream was paused
// was broken at 1.0.1 still
wasPaused = true;
xine_set_param( m_stream, XINE_PARAM_AUDIO_AMP_MUTE, 1 );
break;
default:
;
}
if( !TheStream::canSeek() ) {
// for http streaming it is not a good idea to seek as xine freezes
// and/or just breaks, this is xine 1.0.1
Debug::warning() << "We won't try to seek as the media is not seekable!\n";
return;
}
//TODO depend on a version that CAN seek in flacs!
if( m_url.path().endsWith( ".flac", false ) ) {
emit statusMessage( i18n("xine cannot currently seek in flac media") );
return;
}
//better feedback
//NOTE doesn't work! I can't tell why..
Slider::instance()->QSlider::setValue( pos );
Slider::instance()->repaint( false );
const bool fullscreen = toggleAction("fullscreen")->isChecked();
if( fullscreen ) {
//TODO don't use OSD (sucks) show slider widget instead
QString osd = "[";
QChar separator = '|';
for( uint x = 0, y = int(pos / (65535.0/20.0)); x < 20; x++ ) {
if( x > y )
separator = '.';
osd += separator;
}
osd += ']';
xine_osd_clear( m_osd );
xine_osd_draw_text( m_osd, 0, 0, osd.utf8(), XINE_OSD_TEXT1 );
xine_osd_show( m_osd, 0 );
}
xine_play( m_stream, (int)pos, 0 );
if( fullscreen )
//after xine_play because the hide command uses stream position
xine_osd_hide( m_osd, xine_get_current_vpts( m_stream ) + 180000 ); //2 seconds
if( wasPaused )
xine_set_param( m_stream, XINE_PARAM_SPEED, XINE_SPEED_PAUSE ),
xine_set_param( m_stream, XINE_PARAM_AUDIO_AMP_MUTE, 0 );
}
void
VideoWindow::setStreamParameter( int value )
{
QCString sender = this->sender()->name();
int parameter;
if( sender == "hue" )
parameter = XINE_PARAM_VO_HUE;
else if( sender == "saturation" )
parameter = XINE_PARAM_VO_SATURATION;
else if( sender == "contrast" )
parameter = XINE_PARAM_VO_CONTRAST;
else if( sender == "brightness" )
parameter = XINE_PARAM_VO_BRIGHTNESS;
else if( sender == "subtitle_channels_menu" )
parameter = XINE_PARAM_SPU_CHANNEL,
value -= 2;
else if( sender == "audio_channels_menu" )
parameter = XINE_PARAM_AUDIO_CHANNEL_LOGICAL,
value -= 2;
else if( sender == "aspect_ratio_menu" )
parameter = XINE_PARAM_VO_ASPECT_RATIO;
else if( sender == "volume" )
parameter = XINE_PARAM_AUDIO_AMP_LEVEL;
else
return;
xine_set_param( m_stream, parameter, value );
}
const Engine::Scope&
VideoWindow::scope()
{
using Analyzer::SCOPE_SIZE;
static Engine::Scope scope( SCOPE_SIZE );
if( xine_get_status( m_stream ) != XINE_STATUS_PLAY )
return scope;
//prune the buffer list and update the m_current_vpts timestamp
timerEvent( 0 );
for( int channels = xine_get_stream_info( m_stream, XINE_STREAM_INFO_AUDIO_CHANNELS ), frame = 0; frame < SCOPE_SIZE; )
{
MyNode *best_node = 0;
for( MyNode *node = myList->next; node != myList; node = node->next )
if( node->vpts <= m_current_vpts && (!best_node || node->vpts > best_node->vpts) )
best_node = node;
if( !best_node || best_node->vpts_end < m_current_vpts )
break;
int64_t
diff = m_current_vpts;
diff -= best_node->vpts;
diff *= 1<<16;
diff /= myMetronom->pts_per_smpls;
const int16_t*
data16 = best_node->mem;
data16 += diff;
diff += diff % channels; //important correction to ensure we don't overflow the buffer
diff /= channels;
int
n = best_node->num_frames;
n -= diff;
n += frame; //clipping for # of frames we need
if( n > SCOPE_SIZE )
n = SCOPE_SIZE; //bounds limiting
for( int a, c; frame < n; ++frame, data16 += channels ) {
for( a = c = 0; c < channels; ++c )
a += data16[c];
a /= channels;
scope[frame] = a;
}
m_current_vpts = best_node->vpts_end;
m_current_vpts++; //FIXME needs to be done for some reason, or you get situations where it uses same buffer again and again
}
return scope;
}
void
VideoWindow::timerEvent( QTimerEvent* )
{
/// here we prune the buffer list regularly
#ifndef XINE_SAFE_MODE
MyNode * const first_node = myList->next;
MyNode const * const list_end = myList;
m_current_vpts = (xine_get_status( m_stream ) == XINE_STATUS_PLAY)
? xine_get_current_vpts( m_stream )
: std::numeric_limits<int64_t>::max();
for( MyNode *prev = first_node, *node = first_node->next; node != list_end; node = node->next )
{
// we never delete first_node
// this maintains thread-safety
if( node->vpts_end < m_current_vpts ) {
prev->next = node->next;
free( node->mem );
free( node );
node = prev;
}
prev = node;
}
#endif
}
void
VideoWindow::customEvent( QCustomEvent *e )
{
switch( e->type() - 2000 ) {
case XINE_EVENT_UI_PLAYBACK_FINISHED:
emit stateChanged( Engine::TrackEnded );
break;
case XINE_EVENT_FRAME_FORMAT_CHANGE:
//TODO not ideal really
debug() << "XINE_EVENT_FRAME_FORMAT_CHANGE\n";
break;
case XINE_EVENT_UI_CHANNELS_CHANGED:
{
char s[128]; //apparently sufficient
{
QStringList languages( "subtitle_channels_menu" );
int channels = xine_get_stream_info( m_stream, XINE_STREAM_INFO_MAX_SPU_CHANNEL );
for( int j = 0; j < channels; j++ )
languages += xine_get_spu_lang( m_stream, j, s ) ? s : i18n("Channel %1").arg( j+1 );
emit channelsChanged( languages );
}
{
QStringList languages( "audio_channels_menu" );
int channels = xine_get_stream_info( m_stream, XINE_STREAM_INFO_MAX_AUDIO_CHANNEL );
for( int j = 0; j < channels; j++ )
languages += xine_get_audio_lang( m_stream, j, s ) ? s : i18n("Channel %1").arg( j+1 );
emit channelsChanged( languages );
}
break;
}
case 1000:
#define message static_cast<QString*>(e->data())
emit statusMessage( *message );
delete message;
break;
case 1001:
MessageBox::sorry( (*message).arg( m_url.prettyURL() ) );
delete message;
break;
case 1002:
emit titleChanged( *message );
delete message;
break;
#undef message
default:
;
}
}
void
VideoWindow::xineEventListener( void *p, const xine_event_t* xineEvent )
{
if( !p )
return;
#define engine static_cast<VideoWindow*>(p)
switch( xineEvent->type ) {
case XINE_EVENT_UI_NUM_BUTTONS: debug() << "XINE_EVENT_UI_NUM_BUTTONS\n"; break;
case XINE_EVENT_MRL_REFERENCE: {
//FIXME this is not the right way, it will have bugs
debug() << "XINE_EVENT_MRL_REFERENCE\n";
engine->m_url = QString::fromUtf8( ((xine_mrl_reference_data_t*)xineEvent->data)->mrl );
QTimer::singleShot( 0, engine, SLOT(play()) );
break;
}
case XINE_EVENT_DROPPED_FRAMES: debug() << "XINE_EVENT_DROPPED_FRAMES\n"; break;
case XINE_EVENT_UI_PLAYBACK_FINISHED:
case XINE_EVENT_FRAME_FORMAT_CHANGE:
case XINE_EVENT_UI_CHANNELS_CHANGED:
{
QCustomEvent *ce;
ce = new QCustomEvent( 2000 + xineEvent->type );
ce->setData( const_cast<xine_event_t*>(xineEvent) );
QApplication::postEvent( engine, ce );
break;
}
case XINE_EVENT_UI_SET_TITLE:
QApplication::postEvent( engine, new QCustomEvent(
QEvent::Type(3002),
new QString( QString::fromUtf8( static_cast<xine_ui_data_t*>(xineEvent->data)->str ) ) ) );
break;
case XINE_EVENT_PROGRESS:
{
xine_progress_data_t* pd = (xine_progress_data_t*)xineEvent->data;
QString
msg = "%1 %2%";
msg = msg.arg( QString::fromUtf8( pd->description ) )
.arg( KGlobal::locale()->formatNumber( pd->percent, 0 ) );
QApplication::postEvent( engine, new QCustomEvent( QEvent::Type(3000), new QString( msg ) ) );
break;
}
case XINE_EVENT_UI_MESSAGE:
{
debug() << "message received from xine\n";
xine_ui_message_data_t *data = (xine_ui_message_data_t *)xineEvent->data;
QString message;
switch( data->type ) {
case XINE_MSG_NO_ERROR:
{
//series of \0 separated strings, terminated with a \0\0
char str[2000];
char *p = str;
for( char *msg = data->messages; !(*msg == '\0' && *(msg+1) == '\0'); ++msg, ++p )
*p = *msg == '\0' ? '\n' : *msg;
*p = '\0';
debug() << str << endl;
break;
}
case XINE_MSG_ENCRYPTED_SOURCE:
message = i18n("The source is encrypted and can not be decrypted."); goto param;
case XINE_MSG_UNKNOWN_HOST:
message = i18n("The host is unknown for the URL: <i>%1</i>"); goto param;
case XINE_MSG_UNKNOWN_DEVICE:
message = i18n("The device name you specified seems invalid."); goto param;
case XINE_MSG_NETWORK_UNREACHABLE:
message = i18n("The network appears unreachable."); goto param;
case XINE_MSG_AUDIO_OUT_UNAVAILABLE:
message = i18n("Audio output unavailable; the device is busy."); goto param;
case XINE_MSG_CONNECTION_REFUSED:
message = i18n("The connection was refused for the URL: <i>%1</i>"); goto param;
case XINE_MSG_FILE_NOT_FOUND:
message = i18n("xine could not find the URL: <i>%1</i>"); goto param;
case XINE_MSG_PERMISSION_ERROR:
message = i18n("Access was denied for the URL: <i>%1</i>"); goto param;
case XINE_MSG_READ_ERROR:
message = i18n("The source cannot be read for the URL: <i>%1</i>"); goto param;
case XINE_MSG_LIBRARY_LOAD_ERROR:
message = i18n("A problem occurred while loading a library or decoder."); goto param;
case XINE_MSG_GENERAL_WARNING:
case XINE_MSG_SECURITY:
default:
if(data->explanation)
{
message += "<b>";
message += QString::fromUtf8( (char*) data + data->explanation );
message += "</b>";
}
else break; //if no explanation then why bother!
//FALL THROUGH
param:
message.prepend( "<p>" );
message += "<p>";
if(data->parameters)
{
message += "xine says: <i>";
message += QString::fromUtf8( (char*) data + data->parameters);
message += "</i>";
}
else message += i18n("Sorry, no additional information is available.");
QApplication::postEvent( engine, new QCustomEvent(QEvent::Type(3001), new QString(message)) );
}
} //case
} //switch
#undef engine
}
void
VideoWindow::toggleDVDMenu()
{
xine_event_t e;
e.type = XINE_EVENT_INPUT_MENU1;
e.data = NULL;
e.data_length = 0;
xine_event_send( m_stream, &e );
}
void
VideoWindow::showOSD( const QString &message )
{
if( m_osd ) {
xine_osd_clear( m_osd );
xine_osd_draw_text( m_osd, 0, 0, message.utf8(), XINE_OSD_TEXT1 );
xine_osd_show( m_osd, 0 );
xine_osd_hide( m_osd, xine_get_current_vpts( m_stream ) + 180000 ); //2 seconds
}
}
QString
VideoWindow::fileFilter() const
{
char *supportedExtensions = xine_get_file_extensions( m_xine );
QString filter( "*." );
filter.append( supportedExtensions );
filter.remove( "txt" );
filter.remove( "png" );
filter.replace( ' ', " *." );
std::free( supportedExtensions );
return filter;
}
} //namespace Codeine

@ -0,0 +1,159 @@
// (C) 2005 Max Howell (max.howell@methylblue.com)
// See COPYING file for licensing information
#ifndef CODEINE_VIDEOWINDOW_H
#define CODEINE_VIDEOWINDOW_H
#include "codeine.h"
#include <qtimer.h>
#include <qwidget.h>
#include <kurl.h>
#include <vector>
typedef struct xine_s xine_t;
typedef struct xine_stream_s xine_stream_t;
typedef struct xine_video_port_s xine_video_port_t;
typedef struct xine_audio_port_s xine_audio_port_t;
typedef struct xine_event_queue_s xine_event_queue_t;
typedef struct xine_post_s xine_post_t;
typedef struct xine_osd_s xine_osd_t;
namespace Engine {
typedef std::vector<int16_t> Scope;
}
namespace Codeine
{
/** Functions declared here are defined in:
* xineEngine.cpp
* videoWindow.cpp
*/
class VideoWindow : public QWidget
{
Q_OBJECT
enum PosTimeLength { Pos, Time, Length };
static VideoWindow *s_instance;
VideoWindow( const VideoWindow& ); //disable
VideoWindow &operator=( const VideoWindow& ); //disable
friend class TheStream;
friend VideoWindow* const engine();
friend VideoWindow* const videoWindow();
public:
VideoWindow( QWidget *parent );
~VideoWindow();
bool init();
void exit();
bool load( const KURL &url );
bool play( uint = 0 );
uint position() const { return posTimeLength( Pos ); }
uint time() const { return posTimeLength( Time ); }
uint length() const { return posTimeLength( Length ); }
uint volume() const;
const Engine::Scope &scope();
Engine::State state() const;
operator xine_t*() const { return m_xine; }
operator xine_stream_t*() const { return m_stream; }
public slots:
void pause();
void record();
void seek( uint );
void stop();
///special slot, see implementation to facilitate understanding
void setStreamParameter( int );
signals:
void stateChanged( Engine::State );
void statusMessage( const QString& );
void titleChanged( const QString& );
void channelsChanged( const QStringList& );
private:
#ifdef HAVE_XINE_H
static void xineEventListener( void*, const xine_event_t* );
#endif
uint posTimeLength( PosTimeLength ) const;
void showErrorMessage();
virtual void customEvent( QCustomEvent* );
virtual void timerEvent( QTimerEvent* );
void eject();
void announceStateChange() { emit stateChanged( state() ); }
xine_osd_t *m_osd;
xine_stream_t *m_stream;
xine_event_queue_t *m_eventQueue;
xine_video_port_t *m_videoPort;
xine_audio_port_t *m_audioPort;
xine_post_t *m_scope;
xine_t *m_xine;
int64_t m_current_vpts;
KURL m_url;
public:
QString fileFilter() const;
public slots:
void toggleDVDMenu();
void showOSD( const QString& );
/// Stuff to do with video and the video window/widget
private:
static void destSizeCallBack( void*, int, int, double, int*, int*, double* );
static void frameOutputCallBack( void*, int, int, double, int*, int*, int*, int*, double*, int*, int* );
void initVideo();
void cleanUpVideo();
public:
static const uint CURSOR_HIDE_TIMEOUT = 2000;
virtual QSize sizeHint() const;
virtual QSize minimumSizeHint() const;
void *x11Visual() const;
void becomePreferredSize();
QImage captureFrame() const;
enum { ExposeEvent = 3000 };
public slots:
void resetZoom();
private slots:
void hideCursor();
private:
virtual void contextMenuEvent( QContextMenuEvent* );
virtual bool event( QEvent* );
virtual bool x11Event( XEvent* );
double m_displayRatio;
QTimer m_timer;
};
//global function for general use by Codeine
//videoWindow() is const for Xlib-thread-safety reasons
inline VideoWindow* const videoWindow() { return VideoWindow::s_instance; }
inline VideoWindow* const engine() { return VideoWindow::s_instance; }
}
#endif

@ -0,0 +1,148 @@
/* Author: Max Howell <max.howell@methylblue.com>, (C) 2004
Copyright: See COPYING file that comes with this distribution */
/* gcc doesn't like inline for me */
#define inline
/* need access to port_ticket */
#define XINE_ENGINE_INTERNAL
#include "xineScope.h"
#include <xine/post.h>
#include <xine/xine_internal.h>
static MyNode theList;
static metronom_t theMetronom;
static int myChannels = 0;
MyNode* const myList = &theList;
metronom_t* const myMetronom = &theMetronom;
/* defined in xineEngine.cpp */
extern void _debug( const char * );
/*************************
* post plugin functions *
*************************/
static int
scope_port_open( xine_audio_port_t *port_gen, xine_stream_t *stream, uint32_t bits, uint32_t rate, int mode )
{
_debug( "scope_port_open()\n" );
#define port ((post_audio_port_t*)port_gen)
_x_post_rewire( (post_plugin_t*)port->post );
_x_post_inc_usage( port );
port->stream = stream;
port->bits = bits;
port->rate = rate;
port->mode = mode;
myChannels = _x_ao_mode2channels( mode );
return port->original_port->open( port->original_port, stream, bits, rate, mode );
}
static void
scope_port_close( xine_audio_port_t *port_gen, xine_stream_t *stream )
{
_debug( "scope_port_close()\n" );
port->stream = NULL;
port->original_port->close( port->original_port, stream );
_x_post_dec_usage( port );
}
static void
scope_port_put_buffer( xine_audio_port_t *port_gen, audio_buffer_t *buf, xine_stream_t *stream )
{
MyNode *new_node;
const int num_samples = buf->num_frames * myChannels;
/* we are too simple to handle 8bit */
/* what does it mean when stream == NULL? */
if( port->bits == 8 ) {
port->original_port->put_buffer( port->original_port, buf, stream ); return; }
/* I keep my own metronom because xine wouldn't for some reason */
memcpy( myMetronom, stream->metronom, sizeof(metronom_t) );
new_node = malloc( sizeof(MyNode) );
new_node->vpts = myMetronom->got_audio_samples( myMetronom, buf->vpts, buf->num_frames );
new_node->num_frames = buf->num_frames;
new_node->mem = malloc( num_samples * 2 );
memcpy( new_node->mem, buf->mem, num_samples * 2 );
{
int64_t
K = myMetronom->pts_per_smpls; /*smpls = 1<<16 samples*/
K *= num_samples;
K /= (1<<16);
K += new_node->vpts;
new_node->vpts_end = K;
}
/* pass data to original port */
port->original_port->put_buffer( port->original_port, buf, stream );
/* finally we should append the current buffer to the list
* NOTE this is thread-safe due to the way we handle the list in the GUI thread */
new_node->next = myList->next;
myList->next = new_node;
#undef port
}
static void
scope_dispose( post_plugin_t *this )
{
free( this );
}
/************************
* plugin init function *
************************/
xine_post_t*
scope_plugin_new( xine_t *xine, xine_audio_port_t *audio_target )
{
if( audio_target == NULL )
return NULL;
post_plugin_t *post_plugin = xine_xmalloc( sizeof(post_plugin_t) );
{
post_plugin_t *this = post_plugin;
post_in_t *input;
post_out_t *output;
post_audio_port_t *port;
_x_post_init( this, 1, 0 );
port = _x_post_intercept_audio_port( this, audio_target, &input, &output );
port->new_port.open = scope_port_open;
port->new_port.close = scope_port_close;
port->new_port.put_buffer = scope_port_put_buffer;
this->xine_post.audio_input[0] = &port->new_port;
this->xine_post.type = PLUGIN_POST;
this->dispose = scope_dispose;
}
/* code is straight from xine_init_post()
can't use that function as it only dlopens the plugins
and our plugin is statically linked in */
post_plugin->running_ticket = xine->port_ticket;
post_plugin->xine = xine;
return &post_plugin->xine_post;
}

@ -0,0 +1,38 @@
/* Author: Max Howell <max.howell@methylblue.com>, (C) 2004
Copyright: See COPYING file that comes with this distribution
This has to be a c file or for some reason it won't link! (GCC 3.4.1)
*/
#ifndef XINESCOPE_H
#define XINESCOPE_H
/* need access to some stuff for scope time stamping */
#define METRONOM_INTERNAL
#include <sys/types.h>
#include <xine/metronom.h>
typedef struct my_node_s MyNode;
struct my_node_s
{
MyNode *next;
int16_t *mem;
int num_frames;
int64_t vpts;
int64_t vpts_end;
};
extern metronom_t* const myMetronom;
extern MyNode* const myList;
#ifdef __cplusplus
extern "C"
{
xine_post_t*
scope_plugin_new( xine_t*, xine_audio_port_t* );
}
#endif
#endif

@ -0,0 +1,43 @@
// (C) 2005 Max Howell (max.howell@methylblue.com)
// See COPYING file for licensing information
#ifndef CODEINE_H
#define CODEINE_H
// try to keep this file light. It gets included by
// practically every implementation and many headers
namespace Engine
{
enum State
{
Uninitialised = 0,
Empty = 1,
Loaded = 2,
Playing = 4,
Paused = 8,
TrackEnded = 16
};
}
class QWidget;
namespace Analyzer
{
static const int SCOPE_SIZE_EXP = 9;
static const int SCOPE_SIZE = 1 << SCOPE_SIZE_EXP;
}
namespace Codeine
{
QWidget *mainWindow(); //defined in mainWindow.cpp
}
/// used by mainWindow.h and xineEngine.h
int main( int, char** );
#define APP_VERSION "1.0.1"
#define APP_NAME "codeine"
#define PRETTY_NAME "Codeine"
#endif

@ -0,0 +1,263 @@
// Author: Max Howell <max.howell@methylblue.com>, (C) 2003-5
// Copyright: See COPYING file that comes with this distribution
//
#ifndef CODEINE_DEBUG_H
#define CODEINE_DEBUG_H
#include <kdebug.h>
#include <qcstring.h>
#include <qvariant.h>
#include <sys/time.h>
class QApplication; ///@see Debug::Indent
extern QApplication *qApp;
/**
* @namespace Debug
* @short kdebug with indentation functionality and convenience macros
* @author Max Howell <max.howell@methylblue.com>
*
* Usage:
*
* #define DEBUG_PREFIX "Blah"
* #include "debug.h"
*
* void function()
* {
* Debug::Block myBlock( __PRETTY_FUNCTION__ );
*
* debug() << "output1" << endl;
* debug() << "output2" << endl;
* }
*
* Will output:
*
* app: BEGIN: void function()
* app: [Blah] output1
* app: [Blah] output2
* app: END: void function(): Took 0.1s
*
* @see Block
* @see CrashHelper
* @see ListStream
*/
namespace Debug
{
inline QCString &indent()
{
static QCString indent;
return indent;
#if 0
static timeval *stamp = 0;
if( stamp == 0 ) {
stamp = new timeval;
return "[00:00] "; }
timeval now;
gettimeofday( &now, 0 );
now.tv_sec -= stamp->tv_sec;
QString time( "[%1:%2]" );
return time.arg( now.tv_sec / 60, 2 ).arg( now.tv_sec % 60, 2 ).latin1() + indent;
#endif
}
#ifdef NDEBUG
static inline kndbgstream debug() { return kndbgstream(); }
static inline kndbgstream warning() { return kndbgstream(); }
static inline kndbgstream error() { return kndbgstream(); }
static inline kndbgstream fatal() { return kndbgstream(); }
static inline void debug1( QVariant v ) {}
typedef kndbgstream Stream;
#else
#ifndef DEBUG_PREFIX
#define AMK_PREFIX ""
#else
#define AMK_PREFIX "[" DEBUG_PREFIX "] "
#endif
//from kdebug.h
enum DebugLevels {
KDEBUG_INFO = 0,
KDEBUG_WARN = 1,
KDEBUG_ERROR = 2,
KDEBUG_FATAL = 3
};
static inline kdbgstream debug() { return kdbgstream( indent(), 0, KDEBUG_INFO ) << AMK_PREFIX; }
static inline kdbgstream warning() { return kdbgstream( indent(), 0, KDEBUG_WARN ) << AMK_PREFIX << "[WARNING!] "; }
static inline kdbgstream error() { return kdbgstream( indent(), 0, KDEBUG_ERROR ) << AMK_PREFIX << "[ERROR!] "; }
static inline kdbgstream fatal() { return kdbgstream( indent(), 0, KDEBUG_FATAL ) << AMK_PREFIX; }
/// convenience function
static inline void debug1( QVariant v ) { kdbgstream( indent(), 0, KDEBUG_INFO ) << v << endl; }
typedef kdbgstream Stream;
#undef AMK_PREFIX
#endif
typedef kndbgstream DummyStream;
}
using Debug::debug;
using Debug::debug1;
/// Standard function announcer
#define DEBUG_FUNC_INFO kdDebug() << Debug::indent() << k_funcinfo << endl;
/// Announce a line
#define DEBUG_LINE_INFO kdDebug() << Debug::indent() << k_funcinfo << "Line: " << __LINE__ << endl;
/// Convenience macro for making a standard Debug::Block
#define DEBUG_BLOCK Debug::Block uniquelyNamedStackAllocatedStandardBlock( __PRETTY_FUNCTION__ );
#define DEBUG_INDENT Debug::indent() += " ";
#define DEBUG_UNINDENT { QCString &s = Debug::indent(); s.truncate( s.length() - 2 ); }
/// Use this to remind yourself to finish the implementation of a function
#define DEBUG_NOTIMPLEMENTED warning() << "NOT-IMPLEMENTED: " << __PRETTY_FUNCTION__ << endl;
/// Use this to alert other developers to stop using a function
#define DEBUG_DEPRECATED warning() << "DEPRECATED: " << __PRETTY_FUNCTION__ << endl;
namespace Debug
{
/**
* @class Debug::Block
* @short Use this to label sections of your code
*
* Usage:
*
* void function()
* {
* Debug::Block myBlock( "section" );
*
* debug() << "output1" << endl;
* debug() << "output2" << endl;
* }
*
* Will output:
*
* app: BEGIN: section
* app: [prefix] output1
* app: [prefix] output2
* app: END: section - Took 0.1s
*
*/
class Block
{
timeval m_start;
const char *m_label;
public:
Block( const char *label )
: m_label( label )
{
gettimeofday( &m_start, 0 );
kdDebug() << indent() << "BEGIN: " << label << "\n";
DEBUG_INDENT
}
~Block()
{
timeval end;
gettimeofday( &end, 0 );
end.tv_sec -= m_start.tv_sec;
if( end.tv_usec < m_start.tv_usec) {
// Manually carry a one from the seconds field.
end.tv_usec += 1000000;
end.tv_sec--;
}
end.tv_usec -= m_start.tv_usec;
double duration = double(end.tv_sec) + (double(end.tv_usec) / 1000000.0);
DEBUG_UNINDENT
kdDebug() << indent() << "END__: " << m_label
<< " - Took " << QString::number( duration, 'g', 3 ) << "s\n";
}
};
/**
* @name Debug::stamp()
* @short To facilitate crash/freeze bugs, by making it easy to mark code that has been processed
*
* Usage:
*
* {
* Debug::stamp();
* function1();
* Debug::stamp();
* function2();
* Debug::stamp();
* }
*
* Will output (assuming the crash occurs in function2()
*
* app: Stamp: 1
* app: Stamp: 2
*
*/
inline void stamp()
{
static int n = 0;
debug() << "| Stamp: " << ++n << endl;
}
}
namespace Debug
{
/**
* @class Debug::List
* @short You can pass anything to this and it will output it as a list
*
* debug() << (Debug::List() << anInt << aString << aQStringList << aDouble) << endl;
*/
typedef QValueList<QVariant> List;
}
#include <kmessagebox.h>
namespace Codeine
{
//FIXME this function is inlined, so this may cause linkage problems for some people..
extern class VideoWindow* const videoWindow();
namespace MessageBox
{
static inline void error( const QString &message )
{
KMessageBox::error( (QWidget*)videoWindow(), message );
}
static inline void sorry( const QString &message )
{
KMessageBox::error( (QWidget*)videoWindow(), message );
}
static inline void information( const QString &message, const QString &title )
{
KMessageBox::information( (QWidget*)videoWindow(), message, title );
}
}
}
#endif

@ -0,0 +1,19 @@
// (C) 2005 Max Howell (max.howell@methylblue.com)
// See COPYING file for licensing information
#include "mxcl.library.h"
#include <qapplication.h>
#include <kcursor.h>
namespace mxcl
{
WaitCursor::WaitCursor()
{
QApplication::setOverrideCursor( KCursor::waitCursor() );
}
WaitCursor::~WaitCursor()
{
QApplication::restoreOverrideCursor();
}
}

@ -0,0 +1,28 @@
// (C) 2005 Max Howell (max.howell@methylblue.com)
// See COPYING file for licensing information
#ifndef MXCL_LIBRARY_H
#define MXCL_LIBRARY_H
namespace mxcl
{
/// Allocate on stack, wait cursor will be shown during existance
struct WaitCursor
{
WaitCursor();
~WaitCursor();
};
}
/// almost always negates the need to #include <klocale.h> in implementations
#include <qstring.h>
QString i18n( const char *text );
/// very useful for QStringLists
#define foreach( x ) \
for( QStringList::ConstIterator it = x.constBegin(), end = x.constEnd(); it != end; ++it )
#endif

@ -0,0 +1,12 @@
# Copyright 2005 Max Howell <max.howell@methylblue.com>
Import( "*" )
myenv=env.Copy()
## Additional paths for compiling the source files
## Always add '../' (top-level directory) because moc makes code that needs it
KDEaddpaths( ['./', '../', '../../'], myenv )
KDEaddlibs( ['qt-mt', 'kdecore', 'kdeui', 'kparts', 'xine'], myenv )
KDEshlib( "libcodeine", Split( "part.cpp xineEngine.cpp videoWindow.cpp toolbar.cpp ../mxcl.library.cpp" ), myenv )

@ -0,0 +1,83 @@
// Author: Max Howell <max.howell@methylblue.com>, (C) 2005
// Copyright: See COPYING file that comes with this distribution
#include "codeine.h"
#include "debug.h"
#include <kaboutdata.h>
#include <kparts/genericfactory.h>
#include "part.h"
#include <qtimer.h>
#include "toolbar.h"
#include "videoWindow.h"
#include <kaction.h>
#include <qslider.h>
namespace Codeine
{
typedef KParts::GenericFactory<Codeine::Part> Factory;
}
K_EXPORT_COMPONENT_FACTORY( libcodeine, Codeine::Factory )
namespace Codeine
{
Part::Part( QWidget *parentWidget, const char *widgetName, QObject *parent, const char *name, const QStringList& )
: ReadOnlyPart( parent, name )
, m_statusBarExtension( new KParts::StatusBarExtension( this ) )
{
setInstance( Factory::instance() );
setWidget( new VideoWindow( parentWidget, widgetName ) );
if( !videoWindow()->init() )
//FIXME this will terminate the host, eg Konqueror
Debug::fatal() << "Couldn't init xine!\n";
KAction *play = new KToggleAction( i18n("Play"), "player_play", Qt::Key_Space, videoWindow(), SLOT(togglePlay()), actionCollection(), "play" );
KAction *mute = new KToggleAction( i18n("Mute"), "player_mute", Qt::Key_M, videoWindow(), SLOT(toggleMute()), actionCollection(), "mute" );
KToolBar *toolBar = new MouseOverToolBar( widget() );
play->plug( toolBar );
mute->plug( toolBar );
m_slider = new QSlider( Qt::Horizontal, toolBar, "slider" );
m_slider->setMaxValue( 65535 );
toolBar->setStretchableWidget( m_slider );
toolBar->addSeparator(); //FIXME ugly
QObject *o = (QObject*)statusBar();
connect( videoWindow(), SIGNAL(statusMessage( const QString& )), o, SLOT(message( const QString& )) );
connect( videoWindow(), SIGNAL(titleChanged( const QString& )), o, SLOT(message( const QString& )) ); //FIXME
}
bool
Part::openURL( const KURL &url )
{
//FIXME nasty, we'd rather not do this way
killTimers();
startTimer( 100 );
return videoWindow()->play( m_url = url );
}
bool
Part::closeURL()
{
m_url = KURL();
videoWindow()->eject();
return true;
}
KAboutData*
Part::createAboutData()
{
// generic factory expects this on the heap
return new KAboutData( APP_NAME, PRETTY_NAME, APP_VERSION );
}
void
Part::timerEvent( QTimerEvent* )
{
m_slider->setValue( videoWindow()->position() );
}
}

@ -0,0 +1,38 @@
// Author: Max Howell <max.howell@methylblue.com>, (C) 2005
// Copyright: See COPYING file that comes with this distribution
#ifndef CODEINE_PART_H
#define CODEINE_PART_H
#include <kparts/statusbarextension.h>
#include <kparts/part.h>
#include <kurl.h>
class KAboutData;
class QSlider;
namespace Codeine
{
class Part : public KParts::ReadOnlyPart
{
public:
Part( QWidget*, const char*, QObject*, const char*, const QStringList& );
virtual bool openFile() { return false; } //pure virtual in base class
virtual bool openURL( const KURL& );
virtual bool closeURL();
static KAboutData *createAboutData();
private:
KParts::StatusBarExtension *m_statusBarExtension;
QSlider *m_slider;
KStatusBar *statusBar() { return m_statusBarExtension->statusBar(); }
virtual void timerEvent( QTimerEvent* );
};
}
#endif

@ -0,0 +1,44 @@
// (C) 2005 Max Howell (max.howell@methylblue.com)
// See COPYING file for licensing information
#include <kpushbutton.h>
#include <qapplication.h>
#include <qevent.h>
#include "toolbar.h"
MouseOverToolBar::MouseOverToolBar( QWidget *parent )
: KToolBar( parent )
{
parent->installEventFilter( this );
move( 0, 0 ); //TODO necessary?
hide();
setPalette( QApplication::palette() ); //videoWindow palette has a black background
}
bool
MouseOverToolBar::eventFilter( QObject *o, QEvent *e )
{
Q_ASSERT( o == parent() );
switch( e->type() )
{
case QEvent::Resize:
resize( static_cast<QResizeEvent*>(e)->size().width(), sizeHint().height() );
break;
case QEvent::Enter:
show();
break;
case QEvent::Leave:
hide();
break;
default:
;
}
return false;
}

@ -0,0 +1,18 @@
// (C) 2005 Max Howell (max.howell@methylblue.com)
// See COPYING file for licensing information
#ifndef CODEINE_TOOLBAR_H
#define CODEINE_TOOLBAR_H
#include <ktoolbar.h>
class MouseOverToolBar : public KToolBar
{
virtual bool eventFilter( QObject*, QEvent* );
public:
MouseOverToolBar( QWidget *parent );
};
#endif

@ -0,0 +1,192 @@
// (C) 2005 Max Howell (max.howell@methylblue.com)
// See COPYING file for licensing information
#define CODEINE_DEBUG_PREFIX "videoWindow"
#include <cstdlib>
#include "debug.h"
#include <qapplication.h> //sendEvent()
#include <qcursor.h>
#include <qevent.h>
#include "videoWindow.h"
#include <X11/Xlib.h> //TODO this breaks compile for lots of people due to excessive macro content
#include <xine.h> //x11_visual_t
namespace Codeine {
VideoWindow *VideoWindow::s_instance = 0;
namespace X
{
Display *d;
int s, w;
}
VideoWindow::VideoWindow( QWidget *parent, const char *name )
: QWidget( parent, name )
, m_osd( 0 )
, m_stream( 0 )
, m_eventQueue( 0 )
, m_videoPort( 0 )
, m_audioPort( 0 )
, m_xine( 0 )
, m_displayRatio( 1 )
{
s_instance = this;
// with this Konqueror would crash on exit
// without this we may be unstable!
//XInitThreads();
show();
setWFlags( Qt::WNoAutoErase );
setMouseTracking( true );
setAcceptDrops( true );
setUpdatesEnabled( false ); //to stop Qt drawing over us
setPaletteBackgroundColor( Qt::black );
X::d = XOpenDisplay( std::getenv("DISPLAY") );
X::s = DefaultScreen( X::d );
X::w = winId();
XLockDisplay( X::d );
XSelectInput( X::d, X::w, ExposureMask );
{
using X::d; using X::s;
//these are Xlib macros
double w = DisplayWidth( d, s ) * 1000 / DisplayWidthMM( d, s );
double h = DisplayHeight( d, s ) * 1000 / DisplayHeightMM( d, s );
m_displayRatio = w / h;
}
XUnlockDisplay( X::d );
connect( &m_timer, SIGNAL(timeout()), SLOT(hideCursor()) );
}
VideoWindow::~VideoWindow()
{
DEBUG_BLOCK
if( m_osd ) xine_osd_free( m_osd );
if( m_stream ) xine_close( m_stream );
if( m_eventQueue ) xine_event_dispose_queue( m_eventQueue );
if( m_stream ) xine_dispose( m_stream );
if( m_videoPort ) xine_close_video_driver( m_xine, m_videoPort );
if( m_audioPort ) xine_close_audio_driver( m_xine, m_audioPort );
if( m_xine ) xine_exit( m_xine );
XCloseDisplay( X::d );
}
void*
VideoWindow::x11Visual() const
{
x11_visual_t* visual = new x11_visual_t;
visual->display = X::d;
visual->screen = X::s;
visual->d = X::w;
visual->dest_size_cb = &VideoWindow::destSizeCallBack;
visual->frame_output_cb = &VideoWindow::frameOutputCallBack;
visual->user_data = (void*)this;
return visual;
}
void
VideoWindow::destSizeCallBack(
void* p, int /*video_width*/, int /*video_height*/,
double /*video_aspect*/, int* dest_width,
int* dest_height, double* dest_aspect )
{
if( !p )
return;
#define vw static_cast<VideoWindow*>(p)
*dest_width = vw->width();
*dest_height = vw->height();
*dest_aspect = vw->m_displayRatio;
}
void
VideoWindow::frameOutputCallBack(
void* p, int video_width, int video_height, double video_aspect,
int* dest_x, int* dest_y, int* dest_width, int* dest_height,
double* dest_aspect, int* win_x, int* win_y )
{
if( !p )
return;
*dest_x = 0;
*dest_y = 0 ;
*dest_width = vw->width();
*dest_height = vw->height();
*win_x = vw->x();
*win_y = vw->y();
*dest_aspect = vw->m_displayRatio;
// correct size with video aspect
// TODO what's this about?
if( video_aspect >= vw->m_displayRatio )
video_width = int( double(video_width * video_aspect / vw->m_displayRatio + 0.5) );
else
video_height = int( double(video_height * vw->m_displayRatio / video_aspect) + 0.5 );
#undef vw
}
bool
VideoWindow::event( QEvent *e )
{
switch( e->type() )
{
case QEvent::MouseMove:
case QEvent::MouseButtonPress:
unsetCursor();
m_timer.start( CURSOR_HIDE_TIMEOUT, true );
break;
case QEvent::Close:
case QEvent::Hide:
xine_stop( m_stream );
break;
case QEvent::Leave:
m_timer.stop();
break;
default:
;
}
return QWidget::event( e );
}
bool
VideoWindow::x11Event( XEvent *e )
{
if( e->type == Expose && e->xexpose.count == 0 ) {
xine_gui_send_vo_data( m_stream, XINE_GUI_SEND_EXPOSE_EVENT, e );
return true;
}
return false;
}
void
VideoWindow::hideCursor()
{
setCursor( Qt::BlankCursor );
}
} //namespace Codeine

@ -0,0 +1,94 @@
// (C) 2005 Max Howell (max.howell@methylblue.com)
// See COPYING file for licensing information
#ifndef CODEINE_VIDEO_WINDOW_H
#define CODEINE_VIDEO_WINDOW_H
#include "../codeine.h"
#include <qtimer.h>
#include <qwidget.h>
#include <kurl.h>
typedef struct xine_s xine_t;
typedef struct xine_stream_s xine_stream_t;
typedef struct xine_video_port_s xine_video_port_t;
typedef struct xine_audio_port_s xine_audio_port_t;
typedef struct xine_event_queue_s xine_event_queue_t;
typedef struct xine_post_s xine_post_t;
typedef struct xine_osd_s xine_osd_t;
namespace Codeine
{
class VideoWindow : public QWidget
{
Q_OBJECT
static VideoWindow *s_instance;
static const uint CURSOR_HIDE_TIMEOUT = 2000;
friend VideoWindow* const videoWindow();
public:
VideoWindow( QWidget *parent, const char *name );
~VideoWindow();
bool init();
bool play( KURL );
void eject();
int position();
signals:
void statusMessage( const QString& );
void titleChanged( const QString& );
private:
/// @see xineEngine.cpp
#ifdef HAVE_XINE_H
static void xineEventListener( void*, const xine_event_t* );
#endif
void showErrorMessage(); //TODO don't use this, just show delayed message
virtual void customEvent( QCustomEvent* );
virtual bool x11Event( XEvent* );
virtual bool event( QEvent* );
xine_osd_t *m_osd;
xine_stream_t *m_stream;
xine_event_queue_t *m_eventQueue;
xine_video_port_t *m_videoPort;
xine_audio_port_t *m_audioPort;
xine_t *m_xine;
KURL m_url;
private:
void *x11Visual() const;
static void destSizeCallBack( void*, int, int, double, int*, int*, double* );
static void frameOutputCallBack( void*, int, int, double, int*, int*, int*, int*, double*, int*, int* );
double m_displayRatio;
QTimer m_timer;
public slots:
void togglePlay();
void toggleMute();
private slots:
void hideCursor();
private:
/// prevent compiler generated functions
VideoWindow( const VideoWindow& );
VideoWindow &operator=( const VideoWindow& );
bool operator==( const VideoWindow& );
};
inline VideoWindow* const videoWindow() { return VideoWindow::s_instance; }
}
#endif

@ -0,0 +1,345 @@
// (C) 2005 Max Howell (max.howell@methylblue.com)
// See COPYING file for licensing information
#define CODEINE_DEBUG_PREFIX "engine"
#include "debug.h"
#include <kglobalsettings.h>
#include <klocale.h>
#include "mxcl.library.h"
#include <qapplication.h> //::sendEvent()
#include <qdatetime.h> //::play()
#include <qdir.h> //QDir::homeDir()
#include <xine.h>
#include "videoWindow.h"
namespace Codeine {
bool
VideoWindow::init()
{
mxcl::WaitCursor allocateOnStack;
debug() << "xine_new()\n";
m_xine = xine_new();
if( !m_xine )
return false;
debug() << "xine_config_load()\n";
xine_config_load( m_xine, QFile::encodeName( QDir::homeDirPath() + "/.xine/config" ) );
debug() << "xine_init()\n";
xine_init( m_xine );
debug() << "xine_open_video_driver()\n";
m_videoPort = xine_open_video_driver( m_xine, "auto", XINE_VISUAL_TYPE_X11, x11Visual() );
debug() << "xine_open_audio_driver()\n";
m_audioPort = xine_open_audio_driver( m_xine, "auto", NULL );
debug() << "xine_stream_new()\n";
m_stream = xine_stream_new( m_xine, m_audioPort, m_videoPort );
if( !m_stream )
return false;
if( !m_audioPort )
MessageBox::error( i18n("xine was unable to initialize any audio-drivers.") );
if( !m_videoPort )
MessageBox::error( i18n("xine was unable to initialize any video-drivers.") );
debug() << "xine_osd_new()\n";
m_osd = xine_osd_new( m_stream, 10, 10, 1000, 18 * 6 + 10 );
if( m_osd ) {
xine_osd_set_font( m_osd, "sans", 18 );
xine_osd_set_text_palette( m_osd, XINE_TEXTPALETTE_WHITE_BLACK_TRANSPARENT, XINE_OSD_TEXT1 );
}
debug() << "xine_event_create_listener_thread()\n";
xine_event_create_listener_thread(
m_eventQueue = xine_event_new_queue( m_stream ),
&VideoWindow::xineEventListener, (void*)this );
{ /// set save directory
xine_cfg_entry_t config;
if( xine_config_lookup_entry( m_xine, "misc.save_dir", &config ) ) {
const QCString dir = KGlobalSettings::desktopPath().local8Bit();
config.str_value = qstrdup( dir );
xine_config_update_entry( m_xine, &config );
}
}
return true;
}
bool
VideoWindow::play( KURL url )
{
DEBUG_BLOCK
m_url = url;
mxcl::WaitCursor allocateOnStack;
//TODO make sensible
if( url.protocol() == "http" ) {
/// automatically save http streams to Desktop folder
const QString fileName = url.filename();
QString
u = url.url();
u += "#save:";
u += url.host();
u += " [";
u += QDate::currentDate().toString();
u += ']';
u += fileName.mid( fileName.findRev( '.' ) + 1 ).lower();
url = u;
}
debug() << "About to load..\n";
if( xine_open( m_stream, url.url().local8Bit() ) )
{
debug() << "About to play..\n";
if( xine_play( m_stream, 0, 0 ) )
return true;
}
showErrorMessage();
return false;
}
void
VideoWindow::togglePlay()
{
if( xine_get_param( m_stream, XINE_PARAM_SPEED ) ) {
xine_set_param( m_stream, XINE_PARAM_SPEED, XINE_SPEED_PAUSE );
xine_set_param( m_stream, XINE_PARAM_AUDIO_CLOSE_DEVICE, 1);
}
else
xine_set_param( m_stream, XINE_PARAM_SPEED, XINE_SPEED_NORMAL );
}
void
VideoWindow::toggleMute()
{
bool const muted = xine_get_param( m_stream, XINE_PARAM_AUDIO_MUTE );
xine_set_param( m_stream, XINE_PARAM_AUDIO_MUTE, !muted );
}
void
VideoWindow::eject()
{
m_url = KURL();
xine_stop( m_stream );
}
int
VideoWindow::position()
{
int pos = 0, time = 0, length = 0;
xine_get_pos_length( m_stream, &pos, &time, &length );
return pos;
}
void
VideoWindow::showErrorMessage()
{
const QString filename = m_url.fileName();
debug() << "xine_get_error()\n";
// NOTE these error messages are somewhat customised
// relative to the main application
// This is because when embedded in some other application
// the error messages have no context, so we must say that we are a video player!
switch( xine_get_error( m_stream ) )
{
case XINE_ERROR_NO_INPUT_PLUGIN:
MessageBox::sorry( i18n("The Codeine video player could not find an input plugin for '%1'.").arg( filename ) );
break;
case XINE_ERROR_NO_DEMUX_PLUGIN:
MessageBox::sorry( i18n("The Codeine video player could not find a demux plugin for '%1'.").arg( filename ) );
break;
case XINE_ERROR_DEMUX_FAILED:
MessageBox::sorry( i18n("The Codeine video player failed to demux '%1'; please check your xine installation.").arg( filename ) );
break;
case XINE_ERROR_INPUT_FAILED:
case XINE_ERROR_MALFORMED_MRL:
case XINE_ERROR_NONE:
MessageBox::sorry( i18n("The Codeine video player reports an internal error; please check your xine installation.") );
break;
}
}
void
VideoWindow::customEvent( QCustomEvent *e )
{
switch( e->type() - 2000 ) {
case XINE_EVENT_UI_PLAYBACK_FINISHED:
//FIXME emit stateChanged( Engine::TrackEnded );
break;
case 1000:
#define message static_cast<QString*>(e->data())
emit statusMessage( *message );
delete message;
break;
case 1001:
MessageBox::sorry( (*message).arg( "FIXME" ) ); //FIXME
delete message;
break;
case 1002:
emit titleChanged( *message );
delete message;
break;
#undef message
default:
;
}
}
void
VideoWindow::xineEventListener( void *p, const xine_event_t* xineEvent )
{
if( !p )
return;
#define engine static_cast<VideoWindow*>(p)
switch( xineEvent->type ) {
case XINE_EVENT_MRL_REFERENCE:
{
mxcl::WaitCursor allocateOnStack;
const char *mrl = ((xine_mrl_reference_data_t*)xineEvent->data)->mrl;
debug() << "XINE_EVENT_MRL_REFERENCE: " << mrl << endl;
if( xine_open( engine->m_stream, mrl ) )
xine_play( engine->m_stream, 0, 0 );
break;
}
case XINE_EVENT_UI_NUM_BUTTONS: debug() << "XINE_EVENT_UI_NUM_BUTTONS\n"; break;
case XINE_EVENT_DROPPED_FRAMES: debug() << "XINE_EVENT_DROPPED_FRAMES\n"; break;
case XINE_EVENT_UI_PLAYBACK_FINISHED:
case XINE_EVENT_FRAME_FORMAT_CHANGE:
case XINE_EVENT_UI_CHANNELS_CHANGED:
{
QCustomEvent *ce;
ce = new QCustomEvent( 2000 + xineEvent->type );
ce->setData( const_cast<xine_event_t*>(xineEvent) );
QApplication::postEvent( engine, ce );
break;
}
case XINE_EVENT_UI_SET_TITLE:
QApplication::postEvent( engine, new QCustomEvent(
QEvent::Type(3002),
new QString( QString::fromUtf8( static_cast<xine_ui_data_t*>(xineEvent->data)->str ) ) ) );
break;
case XINE_EVENT_PROGRESS:
{
xine_progress_data_t* pd = (xine_progress_data_t*)xineEvent->data;
QString
msg = "%1 %2%";
msg = msg.arg( QString::fromUtf8( pd->description ) )
.arg( KGlobal::locale()->formatNumber( pd->percent, 0 ) );
QApplication::postEvent( engine, new QCustomEvent( QEvent::Type(3000), new QString( msg ) ) );
break;
}
case XINE_EVENT_UI_MESSAGE:
{
debug() << "Message received from xine\n";
xine_ui_message_data_t *data = (xine_ui_message_data_t *)xineEvent->data;
QString message;
switch( data->type ) {
case XINE_MSG_NO_ERROR:
{
//series of \0 separated strings, terminated with a \0\0
char str[2000];
char *p = str;
for( char *msg = data->messages; !(*msg == '\0' && *(msg+1) == '\0'); ++msg, ++p )
*p = *msg == '\0' ? '\n' : *msg;
*p = '\0';
debug() << str << endl;
break;
}
case XINE_MSG_ENCRYPTED_SOURCE:
message = i18n("The source is encrypted and can not be decrypted."); goto param;
case XINE_MSG_UNKNOWN_HOST:
message = i18n("The host is unknown for the URL: <i>%1</i>"); goto param;
case XINE_MSG_UNKNOWN_DEVICE:
message = i18n("The device name you specified seems invalid."); goto param;
case XINE_MSG_NETWORK_UNREACHABLE:
message = i18n("The network appears unreachable."); goto param;
case XINE_MSG_AUDIO_OUT_UNAVAILABLE:
message = i18n("Audio output unavailable; the device is busy."); goto param;
case XINE_MSG_CONNECTION_REFUSED:
message = i18n("The connection was refused for the URL: <i>%1</i>"); goto param;
case XINE_MSG_FILE_NOT_FOUND:
message = i18n("xine could not find the URL: <i>%1</i>"); goto param;
case XINE_MSG_PERMISSION_ERROR:
message = i18n("Access was denied for the URL: <i>%1</i>"); goto param;
case XINE_MSG_READ_ERROR:
message = i18n("The source cannot be read for the URL: <i>%1</i>"); goto param;
case XINE_MSG_LIBRARY_LOAD_ERROR:
message = i18n("A problem occurred while loading a library or decoder."); goto param;
case XINE_MSG_GENERAL_WARNING:
case XINE_MSG_SECURITY:
default:
if(data->explanation)
{
message += "<b>";
message += QString::fromUtf8( (char*) data + data->explanation );
message += "</b>";
}
else break; //if no explanation then why bother!
//FALL THROUGH
param:
message.prepend( "<p>" );
message += "<p>";
if(data->parameters)
{
message += "xine says: <i>";
message += QString::fromUtf8( (char*) data + data->parameters);
message += "</i>";
}
else message += i18n("Sorry, no additional information is available.");
QApplication::postEvent( engine, new QCustomEvent(QEvent::Type(3001), new QString(message)) );
}
} //case
} //switch
#undef engine
}
} //namespace Codeine
Loading…
Cancel
Save