summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoss Burton <rburton@src.gnome.org>2003-03-30 20:28:32 +0000
committerRoss Burton <rburton@src.gnome.org>2003-03-30 20:28:32 +0000
commit36ff0dc5f60f5d69ba2b4f6179f5f129945e6f53 (patch)
treeda2734767d67cf35c2b608c93f96fc3c9258309e
Initial revision
-rw-r--r--AUTHORS1
-rw-r--r--COPYING340
-rw-r--r--ChangeLog0
-rw-r--r--INSTALL229
-rw-r--r--Makefile.am13
-rw-r--r--NEWS0
-rw-r--r--NOTES13
-rw-r--r--README0
-rwxr-xr-xautogen.sh358
-rw-r--r--configure.in62
-rw-r--r--data/grip-48.pngbin0 -> 4213 bytes
-rw-r--r--data/logo.xcfbin0 -> 293243 bytes
-rw-r--r--data/sound-juicer.glade1176
-rw-r--r--data/sound-juicer.gladep7
-rw-r--r--po/Makefile256
-rw-r--r--po/Makefile.in255
-rw-r--r--po/Makefile.in.in255
-rw-r--r--po/POTFILES1
-rw-r--r--po/POTFILES.in1
-rw-r--r--po/sound-juicer.pot94
-rw-r--r--src/bacon-cd-selection.c439
-rw-r--r--src/bacon-cd-selection.h55
-rw-r--r--src/cd-drive.c593
-rw-r--r--src/cd-drive.h52
-rw-r--r--src/sj-error.c32
-rw-r--r--src/sj-error.h14
-rw-r--r--src/sj-gstreamer-fake.c41
-rw-r--r--src/sj-gstreamer.c181
-rw-r--r--src/sj-gstreamer.h14
-rw-r--r--src/sj-musicbrainz-fake.c45
-rw-r--r--src/sj-musicbrainz.c122
-rw-r--r--src/sj-musicbrainz.h11
-rw-r--r--src/sj-structures.h24
-rw-r--r--tests/Makefile.am17
-rw-r--r--tests/data-test.c31
-rw-r--r--tests/glade-test.c490
-rw-r--r--tests/gst-test.c22
-rw-r--r--tests/mb-test.c25
38 files changed, 5269 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..7e081b3
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1 @@
+Ross Burton <ross@burtonini.com>
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..d60c31a
--- /dev/null
+++ b/COPYING
@@ -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.
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/ChangeLog
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..54caf7c
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,229 @@
+Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002 Free Software
+Foundation, Inc.
+
+ This file is free documentation; the Free Software Foundation gives
+unlimited permission to copy, distribute and modify it.
+
+Basic Installation
+==================
+
+ These are generic installation instructions.
+
+ The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation. It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions. Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, and a
+file `config.log' containing compiler output (useful mainly for
+debugging `configure').
+
+ It can also use an optional file (typically called `config.cache'
+and enabled with `--cache-file=config.cache' or simply `-C') that saves
+the results of its tests to speed up reconfiguring. (Caching is
+disabled by default to prevent problems with accidental use of stale
+cache files.)
+
+ If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release. If you are using the cache, and at
+some point `config.cache' contains results you don't want to keep, you
+may remove or edit it.
+
+ The file `configure.ac' (or `configure.in') is used to create
+`configure' by a program called `autoconf'. You only need
+`configure.ac' if you want to change it or regenerate `configure' using
+a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+ 1. `cd' to the directory containing the package's source code and type
+ `./configure' to configure the package for your system. If you're
+ using `csh' on an old version of System V, you might need to type
+ `sh ./configure' instead to prevent `csh' from trying to execute
+ `configure' itself.
+
+ Running `configure' takes awhile. While running, it prints some
+ messages telling which features it is checking for.
+
+ 2. Type `make' to compile the package.
+
+ 3. Optionally, type `make check' to run any self-tests that come with
+ the package.
+
+ 4. Type `make install' to install the programs and any data files and
+ documentation.
+
+ 5. You can remove the program binaries and object files from the
+ source code directory by typing `make clean'. To also remove the
+ files that `configure' created (so you can compile the package for
+ a different kind of computer), type `make distclean'. There is
+ also a `make maintainer-clean' target, but that is intended mainly
+ for the package's developers. If you use it, you may have to get
+ all sorts of other programs in order to regenerate files that came
+ with the distribution.
+
+Compilers and Options
+=====================
+
+ Some systems require unusual options for compilation or linking that
+the `configure' script does not know about. Run `./configure --help'
+for details on some of the pertinent environment variables.
+
+ You can give `configure' initial values for configuration parameters
+by setting variables in the command line or in the environment. Here
+is an example:
+
+ ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix
+
+ *Note Defining Variables::, for more details.
+
+Compiling For Multiple Architectures
+====================================
+
+ You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory. To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'. `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script. `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+ If you have to use a `make' that does not support the `VPATH'
+variable, you have to compile the package for one architecture at a
+time in the source code directory. After you have installed the
+package for one architecture, use `make distclean' before reconfiguring
+for another architecture.
+
+Installation Names
+==================
+
+ By default, `make install' will install the package's files in
+`/usr/local/bin', `/usr/local/man', etc. You can specify an
+installation prefix other than `/usr/local' by giving `configure' the
+option `--prefix=PATH'.
+
+ You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files. If you
+give `configure' the option `--exec-prefix=PATH', the package will use
+PATH as the prefix for installing programs and libraries.
+Documentation and other data files will still use the regular prefix.
+
+ In addition, if you use an unusual directory layout you can give
+options like `--bindir=PATH' to specify different values for particular
+kinds of files. Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+ If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+ Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System). The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+ For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+ There may be some features `configure' cannot figure out
+automatically, but needs to determine by the type of machine the package
+will run on. Usually, assuming the package is built to be run on the
+_same_ architectures, `configure' can figure that out, but if it prints
+a message saying it cannot guess the machine type, give it the
+`--build=TYPE' option. TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name which has the form:
+
+ CPU-COMPANY-SYSTEM
+
+where SYSTEM can have one of these forms:
+
+ OS KERNEL-OS
+
+ See the file `config.sub' for the possible values of each field. If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the machine type.
+
+ If you are _building_ compiler tools for cross-compiling, you should
+use the `--target=TYPE' option to select the type of system they will
+produce code for.
+
+ If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the
+"host" platform (i.e., that on which the generated programs will
+eventually be run) with `--host=TYPE'.
+
+Sharing Defaults
+================
+
+ If you want to set default values for `configure' scripts to share,
+you can create a site shell script called `config.site' that gives
+default values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists. Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Defining Variables
+==================
+
+ Variables not defined in a site shell script can be set in the
+environment passed to `configure'. However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost. In order to avoid this problem, you should set
+them in the `configure' command line, using `VAR=value'. For example:
+
+ ./configure CC=/usr/local2/bin/gcc
+
+will cause the specified gcc to be used as the C compiler (unless it is
+overridden in the site shell script).
+
+`configure' Invocation
+======================
+
+ `configure' recognizes the following options to control how it
+operates.
+
+`--help'
+`-h'
+ Print a summary of the options to `configure', and exit.
+
+`--version'
+`-V'
+ Print the version of Autoconf used to generate the `configure'
+ script, and exit.
+
+`--cache-file=FILE'
+ Enable the cache: use and save the results of the tests in FILE,
+ traditionally `config.cache'. FILE defaults to `/dev/null' to
+ disable caching.
+
+`--config-cache'
+`-C'
+ Alias for `--cache-file=config.cache'.
+
+`--quiet'
+`--silent'
+`-q'
+ Do not print messages saying which checks are being made. To
+ suppress all normal output, redirect it to `/dev/null' (any error
+ messages will still be shown).
+
+`--srcdir=DIR'
+ Look for the package's source code in directory DIR. Usually
+ `configure' can determine that directory automatically.
+
+`configure' also accepts some other, not widely useful, options. Run
+`configure --help' for more details.
+
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..5da004c
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,13 @@
+SUBDIRS = tests
+
+INTLTOOL_BUILT = \
+ intltool-extract \
+ intltool-merge \
+ intltool-update
+
+EXTRA_DIST = \
+ autogen.sh \
+ $(INTLTOOL_BUILT:=.in) \
+ NOTES
+
+CLEANFILES = $(INTLTOOL_BUILT)
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/NEWS
diff --git a/NOTES b/NOTES
new file mode 100644
index 0000000..4765e1a
--- /dev/null
+++ b/NOTES
@@ -0,0 +1,13 @@
+Either pass in an AlbumDetails* to sj_gstreamer_extract_track(), or
+put an AlbumDetails* into each TrackDetails. This means we can set the
+album name.
+
+Use GConf. Keys so far:
+/apps/sound-juicer/base_path (string, default "")
+/apps/sound-juicer/device (string, default "")
+
+GErrorify everywhere!
+
+Instead of using an int counter, lookup the track number too?
+
+Find out why track duration doesn't work on MB.
diff --git a/README b/README
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/README
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..75b9849
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,358 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+#name of package
+PKG_NAME=${PKG_NAME:Sound Juicer}
+srcdir=${srcdir:-.}
+
+# default version requirements ...
+REQUIRED_AUTOCONF_VERSION=${REQUIRED_AUTOCONF_VERSION:-2.53}
+REQUIRED_AUTOMAKE_VERSION=${REQUIRED_AUTOMAKE_VERSION:-1.7}
+REQUIRED_LIBTOOL_VERSION=${REQUIRED_LIBTOOL_VERSION:-1.4.3}
+REQUIRED_GETTEXT_VERSION=${REQUIRED_GETTEXT_VERSION:-0.10.40}
+REQUIRED_GLIB_GETTEXT_VERSION=${REQUIRED_GLIB_GETTEXT_VERSION:-2.2.0}
+REQUIRED_INTLTOOL_VERSION=${REQUIRED_INTLTOOL_VERSION:-0.25}
+REQUIRED_PKG_CONFIG_VERSION=${REQUIRED_PKG_CONFIG_VERSION:-0.14.0}
+REQUIRED_GTK_DOC_VERSION=${REQUIRED_GTK_DOC_VERSION:-1.0}
+
+# a list of required m4 macros. Package can set an initial value
+REQUIRED_M4MACROS=${REQUIRED_M4MACROS:-}
+FORBIDDEN_M4MACROS=${FORBIDDEN_M4MACROS:-}
+
+# if GNOME2_DIR set, modify ACLOCAL_FLAGS ...
+if [ -n "$GNOME2_DIR" ]; then
+ ACLOCAL_FLAGS="-I $GNOME2_DIR/share/aclocal $ACLOCAL_FLAGS"
+ LD_LIBRARY_PATH="$GNOME2_DIR/lib:$LD_LIBRARY_PATH"
+ PATH="$GNOME2_DIR/bin:$PATH"
+ export PATH
+ export LD_LIBRARY_PATH
+fi
+
+
+# some terminal codes ...
+boldface="`tput bold 2>/dev/null`"
+normal="`tput sgr0 2>/dev/null`"
+printbold() {
+ echo -n "$boldface"
+ echo "$@"
+ echo -n "$normal"
+}
+printerr() {
+ echo "$@" >&2
+}
+
+# Usage:
+# compare_versions MIN_VERSION ACTUAL_VERSION
+# returns true if ACTUAL_VERSION >= MIN_VERSION
+compare_versions() {
+ local min_version actual_version status save_IFS cur min
+ min_version=$1
+ actual_version=$2
+ status=0
+ IFS="${IFS= }"; save_IFS="$IFS"; IFS="."
+ set $actual_version
+ for min in $min_version; do
+ cur=`echo $1 | sed 's/[^0-9].*$//'`; shift # remove letter suffixes
+ if [ -z "$min" ]; then break; fi
+ if [ -z "$cur" ]; then status=1; break; fi
+ if [ $cur -gt $min ]; then break; fi
+ if [ $cur -lt $min ]; then status=1; break; fi
+ done
+ IFS="$save_IFS"
+ return $status
+}
+
+# Usage:
+# version_check PACKAGE VARIABLE CHECKPROGS MIN_VERSION SOURCE
+# checks to see if the package is available
+version_check() {
+ local package variable checkprogs min_version source status checkprog actual_version
+ package=$1
+ variable=$2
+ checkprogs=$3
+ min_version=$4
+ source=$5
+ status=1
+
+ checkprog=`eval echo "\\$$variable"`
+ if [ -n "$checkprog" ]; then
+ printbold "using $checkprog for $package"
+ return 0
+ fi
+
+ printbold "checking for $package >= $min_version..."
+ for checkprog in $checkprogs; do
+ echo -n " testing $checkprog... "
+ if $checkprog --version < /dev/null > /dev/null 2>&1; then
+ actual_version=`$checkprog --version | head -1 | \
+ sed 's/^.*[ ]\([0-9.]*[a-z]*\).*$/\1/'`
+ if compare_versions $min_version $actual_version; then
+ echo "found."
+ # set variable
+ eval "$variable=$checkprog"
+ status=0
+ break
+ else
+ echo "too old (found version $actual_version)"
+ fi
+ else
+ echo "not found."
+ fi
+ done
+ if [ "$status" != 0 ]; then
+ printerr "***Error***: You must have $package >= $min_version installed"
+ printerr " to build $PKG_NAME. Download the appropriate package for"
+ printerr " from your distribution or get the source tarball at"
+ printerr " $source"
+ printerr
+ fi
+ return $status
+}
+
+# Usage:
+# require_m4macro filename.m4
+# adds filename.m4 to the list of required macros
+require_m4macro() {
+ case "$REQUIRED_M4MACROS" in
+ $1\ * | *\ $1\ * | *\ $1) ;;
+ *) REQUIRED_M4MACROS="$REQUIRED_M4MACROS $1" ;;
+ esac
+}
+
+forbid_m4macro() {
+ case "$FORBIDDEN_M4MACROS" in
+ $1\ * | *\ $1\ * | *\ $1) ;;
+ *) FORBIDDEN_M4MACROS="$FORBIDDEN_M4MACROS $1" ;;
+ esac
+}
+
+# Usage:
+# check_m4macros
+# Checks that all the requested macro files are in the aclocal macro path
+# Uses REQUIRED_M4MACROS and ACLOCAL variables.
+check_m4macros() {
+ local macrodirs status macro dir macrofound
+
+ # construct list of macro directories
+ macrodirs="`$ACLOCAL --print-ac-dir`"
+ set - $ACLOCAL_FLAGS
+ while [ $# -gt 0 ]; do
+ if [ "$1" = "-I" ]; then
+ macrodirs="$macrodirs $2"
+ shift
+ fi
+ shift
+ done
+
+ status=0
+ if [ -n "$REQUIRED_M4MACROS" ]; then
+ printbold "Checking for required M4 macros..."
+ # check that each macro file is in one of the macro dirs
+ for macro in $REQUIRED_M4MACROS; do
+ macrofound=false
+ for dir in $macrodirs; do
+ if [ -f "$dir/$macro" ]; then
+ macrofound=true
+ break
+ fi
+ done
+ if $macrofound; then
+ :
+ else
+ printerr " $macro not found"
+ status=1
+ fi
+ done
+ fi
+ if [ -n "$FORBIDDEN_M4MACROS" ]; then
+ printbold "Checking for forbidden M4 macros..."
+ # check that each macro file is in one of the macro dirs
+ for macro in $FORBIDDEN_M4MACROS; do
+ macrofound=false
+ for dir in $macrodirs; do
+ if [ -f "$dir/$macro" ]; then
+ macrofound=true
+ break
+ fi
+ done
+ if $macrofound; then
+ printerr " $macro found (should be cleared from macros dir)"
+ status=1
+ fi
+ done
+ fi
+ if [ "$status" != 0 ]; then
+ printerr "***Error***: some autoconf macros required to build $PKG_NAME"
+ printerr " were not found in your aclocal path, or some forbidden"
+ printerr " macros were found. Perhaps you need to adjust your"
+ printerr " ACLOCAL_PATH?"
+ printerr
+ fi
+ return $status
+}
+
+# try to catch the case where the macros2/ directory hasn't been cleared out.
+forbid_m4macro gnome-cxx-check.m4
+
+want_libtool=false
+want_gettext=false
+want_glib_gettext=false
+want_intltool=false
+want_pkg_config=false
+want_gtk_doc=false
+
+configure_files="`find $srcdir -name configure.ac -print -or -name configure.in -print`"
+for configure_ac in $configure_files; do
+ if grep "^A[CM]_PROG_LIBTOOL" $configure_ac >/dev/null; then
+ want_libtool=true
+ fi
+ if grep "^AM_GNU_GETTEXT" $configure_ac >/dev/null; then
+ want_gettext=true
+ fi
+ if grep "^AM_GLIB_GNU_GETTEXT" $configure_ac >/dev/null; then
+ want_glib_gettext=true
+ fi
+ if grep "^AC_PROG_INTLTOOL" $configure_ac >/dev/null; then
+ want_intltool=true
+ fi
+ if grep "^PKG_CHECK_MODULES" $configure_ac >/dev/null; then
+ want_pkg_config=true
+ fi
+ if grep "^GTK_DOC_CHECK" $configure_ac >/dev/null; then
+ want_gtk_doc=true
+ fi
+done
+
+DIE=0
+
+#tell Mandrake autoconf wrapper we want autoconf 2.5x, not 2.13
+WANT_AUTOCONF_2_5=1
+export WANT_AUTOCONF_2_5
+version_check autoconf AUTOCONF 'autoconf2.50 autoconf autoconf-2.53' $REQUIRED_AUTOCONF_VERSION \
+ "http://ftp.gnu.org/pub/gnu/autoconf/autoconf-$REQUIRED_AUTOCONF_VERSION.tar.gz" || DIE=1
+AUTOHEADER=`echo $AUTOCONF | sed s/autoconf/autoheader/`
+
+case $REQUIRED_AUTOMAKE_VERSION in
+ 1.4*) automake_progs="automake-1.4" ;;
+ 1.5*) automake_progs="automake-1.7 automake-1.6 automake-1.5" ;;
+ 1.6*) automake_progs="automake-1.7 automake-1.6" ;;
+ 1.7*) automake_progs="automake-1.7" ;;
+esac
+version_check automake AUTOMAKE "$automake_progs" $REQUIRED_AUTOMAKE_VERSION \
+ "http://ftp.gnu.org/pub/gnu/automake/automake-$REQUIRED_AUTOMAKE_VERSION.tar.gz" || DIE=1
+ACLOCAL=`echo $AUTOMAKE | sed s/automake/aclocal/`
+
+if $want_libtool; then
+ version_check libtool LIBTOOLIZE libtoolize $REQUIRED_LIBTOOL_VERSION \
+ "http://ftp.gnu.org/pub/gnu/libtool/libtool-$REQUIRED_LIBTOOL_VERSION.tar.gz" || DIE=1
+ require_m4macro libtool.m4
+fi
+
+if $want_gettext; then
+ version_check gettext GETTEXTIZE gettextize $REQUIRED_GETTEXT_VERSION \
+ "http://ftp.gnu.org/pub/gnu/gettext/gettext-$REQUIRED_GETTEXT_VERSION.tar.gz" || DIE=1
+ require_m4macro gettext.m4
+fi
+
+if $want_glib_gettextize; then
+ version_check glib-gettext GLIB_GETTEXTIZE glib-gettextize $REQUIRED_GLIB_GETTEXT_VERSION \
+ "ftp://ftp.gtk.org/pub/gtk/v2.2/glib-$REQUIRED_GLIB_GETTEXT_VERSION.tar.gz" || DIE=1
+ require_m4macro glib-gettext.m4
+fi
+
+if $want_intltool; then
+ version_check intltool INTLTOOLIZE intltoolize $REQUIRED_INTLTOOL_VERSION \
+ "http://ftp.gnome.org/pub/GNOME/sources/intltool/" || DIE=1
+ require_m4macro intltool.m4
+fi
+
+if $want_pkg_config; then
+ version_check pkg-config PKG_CONFIG pkg-config $REQUIRED_PKG_CONFIG_VERSION \
+ "'http://www.freedesktop.org/software/pkgconfig/releases/pkgconfig-$REQUIRED_PKG_CONFIG_VERSION.tar.gz" || DIE=1
+ require_m4macro pkg.m4
+fi
+
+if $want_gtk_doc; then
+ version_check gtk-doc GTKDOCIZE gtkdocize $REQUIRED_GTK_DOC_VERSION \
+ "http://ftp.gnome.org/pub/GNOME/sources/gtk-doc/" || DIE=1
+ require_m4macro gtk-doc.m4
+fi
+
+check_m4macros || DIE=1
+
+if [ "$DIE" -eq 1 ]; then
+ exit 1
+fi
+
+if test -z "$*"; then
+ printerr "**Warning**: I am going to run \`configure' with no arguments."
+ printerr "If you wish to pass any to it, please specify them on the"
+ printerr \`$0\'" command line."
+ printerr
+fi
+
+topdir=`pwd`
+for configure_ac in $configure_files; do
+ dirname=`dirname $configure_ac`
+ basename=`basename $configure_ac`
+ if test -f $dirname/NO-AUTO-GEN; then
+ echo skipping $dirname -- flagged as no auto-gen
+ else
+ printbold "Processing $configure_ac"
+
+ aclocalinclude="$ACLOCAL_FLAGS"
+ printbold "Running $ACLOCAL..."
+ $ACLOCAL $aclocalinclude || exit 1
+
+ if grep "GNOME_AUTOGEN_OBSOLETE" aclocal.m4 >/dev/null; then
+ printerr "*** obsolete gnome macros were used in $configure_ac"
+ fi
+
+ if grep "^A[CM]_PROG_LIBTOOL" $basename >/dev/null; then
+ printbold "Running $LIBTOOLIZE..."
+ $LIBTOOLIZE --force || exit 1
+ fi
+ if grep "^AM_GLIB_GNU_GETTEXT" $basename >/dev/null; then
+ printbold "Running $GLIB_GETTEXTIZE... Ignore non-fatal messages."
+ echo "no" | $GLIB_GETTEXTIZE --force --copy || exit 1
+ elif grep "^AM_GNU_GETTEXT" $basename >/dev/null; then
+ if grep "^AM_GNU_GETTEXT_VERSION" $basename > /dev/null; then
+ printbold "Running autopoint..."
+ autopoint --force || exit 1
+ else
+ printbold "Running $GETTEXTIZE... Ignore non-fatal messages."
+ echo "no" | $GETTEXTIZE --force --copy || exit 1
+ fi
+ fi
+ if grep "^AC_PROG_INTLTOOL" $basename >/dev/null; then
+ printbold "Running $INTLTOOLIZE..."
+ $INTLTOOLIZE --force --automake || exit 1
+ fi
+ if grep "^GTK_DOC_CHECK" $basename >/dev/null; then
+ printbold "Running $GTKDOCIZE..."
+ $GTKDOCIZE || exit 1
+ fi
+ if grep "^A[CM]_CONFIG_HEADER" $basename >/dev/null; then
+ printbold "Running $AUTOHEADER..."
+ $AUTOHEADER || exit 1
+ fi
+
+ printbold "Running $AUTOMAKE..."
+ $AUTOMAKE --gnu --add-missing || exit 1
+
+ printbold "Running $AUTOCONF..."
+ $AUTOCONF || exit 1
+
+ cd $topdir
+ fi
+done
+
+conf_flags="--enable-maintainer-mode --enable-compile-warnings"
+
+if test x$NOCONFIGURE = x; then
+ printbold Running $srcdir/configure $conf_flags "$@" ...
+ $srcdir/configure $conf_flags "$@" \
+ && echo Now type \`make\' to compile $PKG_NAME || exit 1
+else
+ echo Skipping configure process.
+fi
diff --git a/configure.in b/configure.in
new file mode 100644
index 0000000..4adf55f
--- /dev/null
+++ b/configure.in
@@ -0,0 +1,62 @@
+dnl Initial blurb
+AC_PREREQ(2.52)
+AC_INIT(sound-juicer, 0.1, http://www.burtonini.com/)
+AC_CONFIG_SRCDIR(src/sj-gstreamer.c)
+AM_INIT_AUTOMAKE(AC_PACKAGE_NAME, AC_PACKAGE_VERSION)
+AM_CONFIG_HEADER(config.h)
+
+# Honor aclocal flags
+ACLOCAL="$ACLOCAL $ACLOCAL_FLAGS"
+
+dnl Do the gettext/i18n stuff
+GETTEXT_PACKAGE=sound-juicer
+AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE", [The name of the gettext package.])
+AC_SUBST(GETTEXT_PACKAGE)
+
+AM_MAINTAINER_MODE
+
+AC_ISC_POSIX
+AC_PROG_CC
+AM_PROG_CC_STDC
+AC_HEADER_STDC
+AC_PROG_INTLTOOL([0.20])
+
+PKG_CHECK_MODULES(GLIB, glib-2.0)
+AC_SUBST(GLIB_CFLAGS)
+AC_SUBST(GLIB_LIBS)
+
+PKG_CHECK_MODULES(LIBGLADE, libglade-2.0)
+AC_SUBST(LIBGLADE_CFLAGS)
+AC_SUBST(LIBGLADE_LIBS)
+
+PKG_CHECK_MODULES(GCONF, gconf-2.0)
+AC_SUBST(GCONF_CFLAGS)
+AC_SUBST(GCONF_LIBS)
+
+PKG_CHECK_MODULES(GNOME, libgnomeui-2.0)
+AC_SUBST(GNOME_CFLAGS)
+AC_SUBST(GNOME_LIBS)
+
+PKG_CHECK_MODULES(GSTREAMER, gstreamer-0.6)
+AC_SUBST(GSTREAMER_CFLAGS)
+AC_SUBST(GSTREAMER_LIBS)
+
+PKG_CHECK_MODULES(MUSICBRAINZ, libmusicbrainz >= 2.0.0)
+AC_SUBST(MUSICBRAINZ_CFLAGS)
+AC_SUBST(MUSICBRAINZ_LIBS)
+
+dnl Find how and where to put the GConf schemas
+AC_PATH_PROG(GCONFTOOL, gconftool-2, no)
+if test x"$GCONFTOOL" = xno; then
+ AC_MSG_ERROR([gconftool-2 executable not found in your path - should be installed with GConf])
+fi
+AM_GCONF_SOURCE_2
+
+ALL_LINGUAS=""
+AM_GLIB_GNU_GETTEXT
+
+AC_OUTPUT([
+Makefile
+po/Makefile.in
+tests/Makefile
+])
diff --git a/data/grip-48.png b/data/grip-48.png
new file mode 100644
index 0000000..a184924
--- /dev/null
+++ b/data/grip-48.png
Binary files differ
diff --git a/data/logo.xcf b/data/logo.xcf
new file mode 100644
index 0000000..bb43ab4
--- /dev/null
+++ b/data/logo.xcf
Binary files differ
diff --git a/data/sound-juicer.glade b/data/sound-juicer.glade
new file mode 100644
index 0000000..ce5f59f
--- /dev/null
+++ b/data/sound-juicer.glade
@@ -0,0 +1,1176 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+
+<widget class="GtkWindow" id="main_window">
+ <property name="width_request">350</property>
+ <property name="height_request">420</property>
+ <property name="title" translatable="yes">Sound Juicer</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <signal name="destroy" handler="on_quit_activate" last_modification_time="Fri, 28 Mar 2003 09:09:29 GMT"/>
+ <signal name="destroy_event" handler="on_destory_event" last_modification_time="Fri, 28 Mar 2003 09:10:18 GMT"/>
+
+ <child>
+ <widget class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkMenuBar" id="menubar1">
+ <property name="visible">True</property>
+
+ <child>
+ <widget class="GtkMenuItem" id="menuitem1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_File</property>
+ <property name="use_underline">True</property>
+
+ <child>
+ <widget class="GtkMenu" id="menuitem1_menu">
+
+ <child>
+ <widget class="GtkImageMenuItem" id="extract">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Extract</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_extract_activate" last_modification_time="Tue, 25 Mar 2003 07:45:51 GMT"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image21">
+ <property name="visible">True</property>
+ <property name="stock">gtk-cdrom</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="re-read">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Re-read</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_reread_activate" last_modification_time="Tue, 25 Mar 2003 07:45:51 GMT"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image22">
+ <property name="visible">True</property>
+ <property name="stock">gtk-refresh</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="separatormenuitem1">
+ <property name="visible">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="quit">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Quit</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_quit_activate" last_modification_time="Tue, 25 Mar 2003 18:52:39 GMT"/>
+ <accelerator key="Q" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image23">
+ <property name="visible">True</property>
+ <property name="stock">gtk-quit</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="menuitem2">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Edit</property>
+ <property name="use_underline">True</property>
+
+ <child>
+ <widget class="GtkMenu" id="menuitem2_menu">
+
+ <child>
+ <widget class="GtkImageMenuItem" id="edit_preferences">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Prefere_nces</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_edit_preferences_cb" last_modification_time="Tue, 25 Mar 2003 08:37:30 GMT"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image24">
+ <property name="visible">True</property>
+ <property name="stock">gtk-preferences</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="menuitem4">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Help</property>
+ <property name="use_underline">True</property>
+
+ <child>
+ <widget class="GtkMenu" id="menuitem4_menu">
+
+ <child>
+ <widget class="GtkImageMenuItem" id="about">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_About</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_about_activate" last_modification_time="Thu, 20 Mar 2003 18:36:52 GMT"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image25">
+ <property name="visible">True</property>
+ <property name="stock">gnome-stock-about</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox2">
+ <property name="border_width">8</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">8</property>
+
+ <child>
+ <widget class="GtkFrame" id="frame3">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="label_yalign">0.5</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+
+ <child>
+ <widget class="GtkTable" id="table1">
+ <property name="border_width">8</property>
+ <property name="visible">True</property>
+ <property name="n_rows">3</property>
+ <property name="n_columns">2</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">4</property>
+ <property name="column_spacing">8</property>
+
+ <child>
+ <widget class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Title:&lt;/b&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Artist:&lt;/b&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label3">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Duration:&lt;/b&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="duration_label">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">label</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">True</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="artist_label">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">label</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">True</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="title_label">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">label</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">True</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label9">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;CD Info&lt;/b&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="type">label_item</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkTreeView" id="track_listview">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="headers_visible">True</property>
+ <property name="rules_hint">True</property>
+ <property name="reorderable">False</property>
+ <property name="enable_search">True</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHButtonBox" id="hbuttonbox1">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <property name="spacing">8</property>
+
+ <child>
+ <widget class="GtkButton" id="reread_button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-refresh</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <signal name="clicked" handler="on_reread_activate" last_modification_time="Thu, 27 Mar 2003 09:28:44 GMT"/>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="extract_button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <signal name="clicked" handler="on_extract_activate" last_modification_time="Tue, 25 Mar 2003 19:54:03 GMT"/>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">2</property>
+
+ <child>
+ <widget class="GtkImage" id="image4">
+ <property name="visible">True</property>
+ <property name="stock">gtk-cdrom</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label6">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Extract</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkStatusbar" id="statusbar1">
+ <property name="visible">True</property>
+ <property name="has_resize_grip">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+<widget class="GtkDialog" id="prefs_dialog">
+ <property name="title" translatable="yes">Preferences</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="has_separator">True</property>
+
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area1">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+
+ <child>
+ <widget class="GtkButton" id="prefs_help">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-help</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="response_id">-11</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="prefs_close">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-close</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="response_id">-7</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox3">
+ <property name="border_width">4</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">8</property>
+
+ <child>
+ <widget class="GtkFrame" id="frame1">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="label_yalign">0.5</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox5">
+ <property name="border_width">8</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="Custom" id="cd_option">
+ <property name="visible">True</property>
+ <property name="creation_function">bacon_cd_selection_new</property>
+ <property name="int1">0</property>
+ <property name="int2">0</property>
+ <property name="last_modification_time">Sat, 22 Mar 2003 16:23:05 GMT</property>
+ <signal name="device_changed" handler="prefs_cdrom_changed_cb" last_modification_time="Tue, 25 Mar 2003 19:53:07 GMT"/>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label4">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;CD Drive&lt;/b&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="type">label_item</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkFrame" id="frame6">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="label_yalign">0.5</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox3">
+ <property name="border_width">8</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">8</property>
+
+ <child>
+ <widget class="GtkLabel" id="path_label">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">label</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">True</property>
+ <property name="selectable">True</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="prefs_browse">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <signal name="clicked" handler="prefs_browse_clicked" last_modification_time="Thu, 27 Mar 2003 08:48:05 GMT"/>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment3">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox4">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">2</property>
+
+ <child>
+ <widget class="GtkImage" id="image26">
+ <property name="visible">True</property>
+ <property name="stock">gtk-open</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label17">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Browse...</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label15">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Output Path&lt;/b&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="type">label_item</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkFrame" id="frame7">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="label_yalign">0.5</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+
+ <child>
+ <placeholder/>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label20">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Output Name&lt;/b&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="type">label_item</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+<widget class="GtkDialog" id="multiple_dialog">
+ <property name="title" translatable="yes">Multiple Albums Found</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="has_separator">False</property>
+
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox2">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area2">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+
+ <child>
+ <widget class="GtkButton" id="ok_button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="response_id">-5</property>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment2">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox2">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">2</property>
+
+ <child>
+ <widget class="GtkImage" id="image16">
+ <property name="visible">True</property>
+ <property name="stock">gtk-go-forward</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label11">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Continue</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox4">
+ <property name="border_width">8</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">8</property>
+
+ <child>
+ <widget class="GtkLabel" id="label10">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">This CD could be more than one album. Please select which album it is below and press &lt;i&gt;Continue&lt;/i&gt;.</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">True</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow2">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkTreeView" id="albums_listview">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="headers_visible">True</property>
+ <property name="rules_hint">False</property>
+ <property name="reorderable">False</property>
+ <property name="enable_search">True</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+<widget class="GtkDialog" id="progress_dialog">
+ <property name="title" translatable="yes">Progress</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="has_separator">True</property>
+
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox3">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area3">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+
+ <child>
+ <widget class="GtkButton" id="okbutton2">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="response_id">-6</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox7">
+ <property name="border_width">8</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">8</property>
+
+ <child>
+ <widget class="GtkLabel" id="progress_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Currently ripping track %d (%d remaining)</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkFrame" id="frame4">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="label_yalign">0.5</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox8">
+ <property name="border_width">8</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">8</property>
+
+ <child>
+ <widget class="GtkProgressBar" id="track_progress">
+ <property name="visible">True</property>
+ <property name="orientation">GTK_PROGRESS_LEFT_TO_RIGHT</property>
+ <property name="fraction">0</property>
+ <property name="pulse_step">0.1</property>
+ <property name="text" translatable="yes"></property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label13">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Track Progress&lt;/b&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="type">label_item</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkFrame" id="frame5">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="label_yalign">0.5</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox9">
+ <property name="border_width">8</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">8</property>
+
+ <child>
+ <widget class="GtkProgressBar" id="disc_progress">
+ <property name="visible">True</property>
+ <property name="orientation">GTK_PROGRESS_LEFT_TO_RIGHT</property>
+ <property name="fraction">0</property>
+ <property name="pulse_step">0.1</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label14">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Disc Progress&lt;/b&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="type">label_item</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+</glade-interface>
diff --git a/data/sound-juicer.gladep b/data/sound-juicer.gladep
new file mode 100644
index 0000000..7012234
--- /dev/null
+++ b/data/sound-juicer.gladep
@@ -0,0 +1,7 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-project SYSTEM "http://glade.gnome.org/glade-project-2.0.dtd">
+
+<glade-project>
+ <name></name>
+ <program_name></program_name>
+</glade-project>
diff --git a/po/Makefile b/po/Makefile
new file mode 100644
index 0000000..a5eaf1d
--- /dev/null
+++ b/po/Makefile
@@ -0,0 +1,256 @@
+# Makefile for program source directory in GNU NLS utilities package.
+# Copyright (C) 1995, 1996, 1997 by Ulrich Drepper <drepper@gnu.ai.mit.edu>
+#
+# This file file be copied and used freely without restrictions. It can
+# be used in projects which are not available under the GNU Public License
+# but which still want to provide support for the GNU gettext functionality.
+# Please note that the actual code is *not* freely available.
+#
+# - Modified by Owen Taylor <otaylor@redhat.com> to use GETTEXT_PACKAGE
+# instead of PACKAGE and to look for po2tbl in ./ not in intl/
+#
+# - Modified by jacob berkman <jacob@ximian.com> to install
+# Makefile.in.in and po2tbl.sed.in for use with glib-gettextize
+
+GETTEXT_PACKAGE = sound-juicer
+PACKAGE = sound-juicer
+VERSION = 0.1
+
+SHELL = /bin/sh
+
+
+srcdir = .
+top_srcdir = ..
+top_builddir = ..
+
+
+prefix = /usr/local
+exec_prefix = ${prefix}
+datadir = ${prefix}/share
+libdir = ${exec_prefix}/lib
+localedir = $(libdir)/locale
+gnulocaledir = $(datadir)/locale
+gettextsrcdir = $(datadir)/glib-2.0/gettext/po
+subdir = po
+
+INSTALL = /usr/bin/install -c
+INSTALL_DATA = ${INSTALL} -m 644
+MKINSTALLDIRS = $(top_srcdir)/./mkinstalldirs
+
+CC = gcc
+GENCAT = @GENCAT@
+GMSGFMT = /usr/bin/msgfmt
+MSGFMT = /usr/bin/msgfmt
+XGETTEXT = /usr/bin/xgettext
+INTLTOOL_UPDATE = $(top_builddir)/intltool-update
+INTLTOOL_EXTRACT = $(top_builddir)/intltool-extract
+MSGMERGE = INTLTOOL_EXTRACT=$(INTLTOOL_EXTRACT) $(INTLTOOL_UPDATE) --gettext-package $(GETTEXT_PACKAGE) --dist
+GENPOT = INTLTOOL_EXTRACT=$(INTLTOOL_EXTRACT) $(INTLTOOL_UPDATE) --gettext-package $(GETTEXT_PACKAGE) --pot
+
+DEFS = -DHAVE_CONFIG_H
+CFLAGS = -g -O2
+CPPFLAGS =
+
+INCLUDES = -I.. -I$(top_srcdir)/intl
+
+COMPILE = $(CC) -c $(DEFS) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) $(XCFLAGS)
+
+SOURCES =
+POFILES =
+GMOFILES =
+DISTFILES = ChangeLog Makefile.in.in POTFILES.in $(GETTEXT_PACKAGE).pot \
+$(POFILES) $(GMOFILES) $(SOURCES)
+
+POTFILES = \
+ ../data/sound-juicer.glade
+
+CATALOGS =
+CATOBJEXT = .gmo
+INSTOBJEXT = .mo
+
+.SUFFIXES:
+.SUFFIXES: .c .o .po .pox .gmo .mo .msg .cat
+
+.c.o:
+ $(COMPILE) $<
+
+.po.pox:
+ $(MAKE) $(GETTEXT_PACKAGE).pot
+ $(MSGMERGE) $< $(srcdir)/$(GETTEXT_PACKAGE).pot -o $*.pox
+
+.po.mo:
+ $(MSGFMT) -o $@ $<
+
+.po.gmo:
+ file=$(srcdir)/`echo $* | sed 's,.*/,,'`.gmo \
+ && rm -f $$file && $(GMSGFMT) -o $$file $<
+
+.po.cat:
+ sed -f ../intl/po2msg.sed < $< > $*.msg \
+ && rm -f $@ && $(GENCAT) $@ $*.msg
+
+
+all: all-yes
+
+all-yes: $(CATALOGS)
+all-no:
+
+$(srcdir)/$(GETTEXT_PACKAGE).pot: $(POTFILES)
+ $(GENPOT)
+
+install: install-exec install-data
+install-exec:
+install-data: install-data-yes
+install-data-no: all
+install-data-yes: all
+ if test -r "$(MKINSTALLDIRS)"; then \
+ $(MKINSTALLDIRS) $(DESTDIR)$(datadir); \
+ else \
+ $(SHELL) $(top_srcdir)/mkinstalldirs $(DESTDIR)$(datadir); \
+ fi
+ @catalogs='$(CATALOGS)'; \
+ for cat in $$catalogs; do \
+ cat=`basename $$cat`; \
+ case "$$cat" in \
+ *.gmo) destdir=$(gnulocaledir);; \
+ *) destdir=$(localedir);; \
+ esac; \
+ lang=`echo $$cat | sed 's/\$(CATOBJEXT)$$//'`; \
+ dir=$(DESTDIR)$$destdir/$$lang/LC_MESSAGES; \
+ if test -r "$(MKINSTALLDIRS)"; then \
+ $(MKINSTALLDIRS) $$dir; \
+ else \
+ $(SHELL) $(top_srcdir)/mkinstalldirs $$dir; \
+ fi; \
+ if test -r $$cat; then \
+ $(INSTALL_DATA) $$cat $$dir/$(GETTEXT_PACKAGE)$(INSTOBJEXT); \
+ echo "installing $$cat as $$dir/$(GETTEXT_PACKAGE)$(INSTOBJEXT)"; \
+ else \
+ $(INSTALL_DATA) $(srcdir)/$$cat $$dir/$(GETTEXT_PACKAGE)$(INSTOBJEXT); \
+ echo "installing $(srcdir)/$$cat as" \
+ "$$dir/$(GETTEXT_PACKAGE)$(INSTOBJEXT)"; \
+ fi; \
+ if test -r $$cat.m; then \
+ $(INSTALL_DATA) $$cat.m $$dir/$(GETTEXT_PACKAGE)$(INSTOBJEXT).m; \
+ echo "installing $$cat.m as $$dir/$(GETTEXT_PACKAGE)$(INSTOBJEXT).m"; \
+ else \
+ if test -r $(srcdir)/$$cat.m ; then \
+ $(INSTALL_DATA) $(srcdir)/$$cat.m \
+ $$dir/$(GETTEXT_PACKAGE)$(INSTOBJEXT).m; \
+ echo "installing $(srcdir)/$$cat as" \
+ "$$dir/$(GETTEXT_PACKAGE)$(INSTOBJEXT).m"; \
+ else \
+ true; \
+ fi; \
+ fi; \
+ done
+ if test "$(PACKAGE)" = "glib"; then \
+ if test -r "$(MKINSTALLDIRS)"; then \
+ $(MKINSTALLDIRS) $(DESTDIR)$(gettextsrcdir); \
+ else \
+ $(SHELL) $(top_srcdir)/mkinstalldirs $(DESTDIR)$(gettextsrcdir); \
+ fi; \
+ $(INSTALL_DATA) $(srcdir)/Makefile.in.in \
+ $(DESTDIR)$(gettextsrcdir)/Makefile.in.in; \
+ else \
+ : ; \
+ fi
+
+# Define this as empty until I found a useful application.
+installcheck:
+
+uninstall:
+ catalogs='$(CATALOGS)'; \
+ for cat in $$catalogs; do \
+ cat=`basename $$cat`; \
+ lang=`echo $$cat | sed 's/\$(CATOBJEXT)$$//'`; \
+ rm -f $(DESTDIR)$(localedir)/$$lang/LC_MESSAGES/$(GETTEXT_PACKAGE)$(INSTOBJEXT); \
+ rm -f $(DESTDIR)$(localedir)/$$lang/LC_MESSAGES/$(GETTEXT_PACKAGE)$(INSTOBJEXT).m; \
+ rm -f $(DESTDIR)$(gnulocaledir)/$$lang/LC_MESSAGES/$(GETTEXT_PACKAGE)$(INSTOBJEXT); \
+ rm -f $(DESTDIR)$(gnulocaledir)/$$lang/LC_MESSAGES/$(GETTEXT_PACKAGE)$(INSTOBJEXT).m; \
+ done
+ rm -f $(DESTDIR)$(gettextsrcdir)/po-Makefile.in.in
+
+check: all
+
+dvi info tags TAGS ID:
+
+mostlyclean:
+ rm -f core core.* *.pox $(GETTEXT_PACKAGE).po *.old.po cat-id-tbl.tmp
+ rm -fr *.o
+
+clean: mostlyclean
+
+distclean: clean
+ rm -f Makefile Makefile.in POTFILES *.mo *.msg *.cat *.cat.m
+
+maintainer-clean: distclean
+ @echo "This command is intended for maintainers to use;"
+ @echo "it deletes files that may require special tools to rebuild."
+ rm -f $(GMOFILES)
+
+distdir = ../$(GETTEXT_PACKAGE)-$(VERSION)/$(subdir)
+dist distdir: update-po $(DISTFILES)
+ dists="$(DISTFILES)"; \
+ for file in $$dists; do \
+ ln $(srcdir)/$$file $(distdir) 2> /dev/null \
+ || cp -p $(srcdir)/$$file $(distdir); \
+ done
+
+update-po: Makefile
+ $(MAKE) $(GETTEXT_PACKAGE).pot
+ cd $(srcdir); \
+ catalogs='$(CATALOGS)'; \
+ for cat in $$catalogs; do \
+ cat=`basename $$cat`; \
+ lang=`echo $$cat | sed 's/\$(CATOBJEXT)$$//'`; \
+ cp $$lang.po $$lang.old.po; \
+ echo "$$lang:"; \
+ if $(MSGMERGE) $$lang; then \
+ rm -f $$lang.old.po; \
+ else \
+ echo "msgmerge for $$cat failed!"; \
+ rm -f $$lang.po; \
+ mv $$lang.old.po $$lang.po; \
+ fi; \
+ done
+
+.po: Makefile
+ $(MAKE) $(PACKAGE).pot;
+ PATH=`pwd`/../src:$$PATH; \
+ echo; printf "$*: "; \
+ if $(MSGMERGE) $*; then \
+ rm -f $*.old.po; \
+ else \
+ echo "msgmerge for * failed!"; \
+ mv $*.old.po $*.po; \
+ fi; \
+ msgfmt --statistics $*.po; echo;
+
+
+# POTFILES is created from POTFILES.in by stripping comments, empty lines
+# and Intltool tags (enclosed in square brackets), and appending a full
+# relative path to them
+POTFILES: POTFILES.in
+ ( if test 'x$(srcdir)' != 'x.'; then \
+ posrcprefix='$(top_srcdir)/'; \
+ else \
+ posrcprefix="../"; \
+ fi; \
+ rm -f $@-t $@ \
+ && (sed -e '/^#/d' \
+ -e "s/^\[.*\] +//" \
+ -e '/^[ ]*$$/d' \
+ -e "s@.*@ $$posrcprefix& \\\\@" < $(srcdir)/$@.in \
+ | sed -e '$$s/\\$$//') > $@-t \
+ && chmod a-w $@-t \
+ && mv $@-t $@ )
+
+Makefile: Makefile.in.in ../config.status POTFILES
+ cd .. \
+ && CONFIG_FILES=$(subdir)/$@.in CONFIG_HEADERS= \
+ $(SHELL) ./config.status
+
+# Tell versions [3.59,3.63) of GNU make not to export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/po/Makefile.in b/po/Makefile.in
new file mode 100644
index 0000000..ba10c99
--- /dev/null
+++ b/po/Makefile.in
@@ -0,0 +1,255 @@
+# Makefile for program source directory in GNU NLS utilities package.
+# Copyright (C) 1995, 1996, 1997 by Ulrich Drepper <drepper@gnu.ai.mit.edu>
+#
+# This file file be copied and used freely without restrictions. It can
+# be used in projects which are not available under the GNU Public License
+# but which still want to provide support for the GNU gettext functionality.
+# Please note that the actual code is *not* freely available.
+#
+# - Modified by Owen Taylor <otaylor@redhat.com> to use GETTEXT_PACKAGE
+# instead of PACKAGE and to look for po2tbl in ./ not in intl/
+#
+# - Modified by jacob berkman <jacob@ximian.com> to install
+# Makefile.in.in and po2tbl.sed.in for use with glib-gettextize
+
+GETTEXT_PACKAGE = sound-juicer
+PACKAGE = sound-juicer
+VERSION = 0.1
+
+SHELL = /bin/sh
+
+
+srcdir = .
+top_srcdir = ..
+top_builddir = ..
+
+
+prefix = /usr/local
+exec_prefix = ${prefix}
+datadir = ${prefix}/share
+libdir = ${exec_prefix}/lib
+localedir = $(libdir)/locale
+gnulocaledir = $(datadir)/locale
+gettextsrcdir = $(datadir)/glib-2.0/gettext/po
+subdir = po
+
+INSTALL = /usr/bin/install -c
+INSTALL_DATA = ${INSTALL} -m 644
+MKINSTALLDIRS = $(top_srcdir)/./mkinstalldirs
+
+CC = gcc
+GENCAT = @GENCAT@
+GMSGFMT = /usr/bin/msgfmt
+MSGFMT = /usr/bin/msgfmt
+XGETTEXT = /usr/bin/xgettext
+INTLTOOL_UPDATE = $(top_builddir)/intltool-update
+INTLTOOL_EXTRACT = $(top_builddir)/intltool-extract
+MSGMERGE = INTLTOOL_EXTRACT=$(INTLTOOL_EXTRACT) $(INTLTOOL_UPDATE) --gettext-package $(GETTEXT_PACKAGE) --dist
+GENPOT = INTLTOOL_EXTRACT=$(INTLTOOL_EXTRACT) $(INTLTOOL_UPDATE) --gettext-package $(GETTEXT_PACKAGE) --pot
+
+DEFS = -DHAVE_CONFIG_H
+CFLAGS = -g -O2
+CPPFLAGS =
+
+INCLUDES = -I.. -I$(top_srcdir)/intl
+
+COMPILE = $(CC) -c $(DEFS) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) $(XCFLAGS)
+
+SOURCES =
+POFILES =
+GMOFILES =
+DISTFILES = ChangeLog Makefile.in.in POTFILES.in $(GETTEXT_PACKAGE).pot \
+$(POFILES) $(GMOFILES) $(SOURCES)
+
+POTFILES = \
+
+CATALOGS =
+CATOBJEXT = .gmo
+INSTOBJEXT = .mo
+
+.SUFFIXES:
+.SUFFIXES: .c .o .po .pox .gmo .mo .msg .cat
+
+.c.o:
+ $(COMPILE) $<
+
+.po.pox:
+ $(MAKE) $(GETTEXT_PACKAGE).pot
+ $(MSGMERGE) $< $(srcdir)/$(GETTEXT_PACKAGE).pot -o $*.pox
+
+.po.mo:
+ $(MSGFMT) -o $@ $<
+
+.po.gmo:
+ file=$(srcdir)/`echo $* | sed 's,.*/,,'`.gmo \
+ && rm -f $$file && $(GMSGFMT) -o $$file $<
+
+.po.cat:
+ sed -f ../intl/po2msg.sed < $< > $*.msg \
+ && rm -f $@ && $(GENCAT) $@ $*.msg
+
+
+all: all-yes
+
+all-yes: $(CATALOGS)
+all-no:
+
+$(srcdir)/$(GETTEXT_PACKAGE).pot: $(POTFILES)
+ $(GENPOT)
+
+install: install-exec install-data
+install-exec:
+install-data: install-data-yes
+install-data-no: all
+install-data-yes: all
+ if test -r "$(MKINSTALLDIRS)"; then \
+ $(MKINSTALLDIRS) $(DESTDIR)$(datadir); \
+ else \
+ $(SHELL) $(top_srcdir)/mkinstalldirs $(DESTDIR)$(datadir); \
+ fi
+ @catalogs='$(CATALOGS)'; \
+ for cat in $$catalogs; do \
+ cat=`basename $$cat`; \
+ case "$$cat" in \
+ *.gmo) destdir=$(gnulocaledir);; \
+ *) destdir=$(localedir);; \
+ esac; \
+ lang=`echo $$cat | sed 's/\$(CATOBJEXT)$$//'`; \
+ dir=$(DESTDIR)$$destdir/$$lang/LC_MESSAGES; \
+ if test -r "$(MKINSTALLDIRS)"; then \
+ $(MKINSTALLDIRS) $$dir; \
+ else \
+ $(SHELL) $(top_srcdir)/mkinstalldirs $$dir; \
+ fi; \
+ if test -r $$cat; then \
+ $(INSTALL_DATA) $$cat $$dir/$(GETTEXT_PACKAGE)$(INSTOBJEXT); \
+ echo "installing $$cat as $$dir/$(GETTEXT_PACKAGE)$(INSTOBJEXT)"; \
+ else \
+ $(INSTALL_DATA) $(srcdir)/$$cat $$dir/$(GETTEXT_PACKAGE)$(INSTOBJEXT); \
+ echo "installing $(srcdir)/$$cat as" \
+ "$$dir/$(GETTEXT_PACKAGE)$(INSTOBJEXT)"; \
+ fi; \
+ if test -r $$cat.m; then \
+ $(INSTALL_DATA) $$cat.m $$dir/$(GETTEXT_PACKAGE)$(INSTOBJEXT).m; \
+ echo "installing $$cat.m as $$dir/$(GETTEXT_PACKAGE)$(INSTOBJEXT).m"; \
+ else \
+ if test -r $(srcdir)/$$cat.m ; then \
+ $(INSTALL_DATA) $(srcdir)/$$cat.m \
+ $$dir/$(GETTEXT_PACKAGE)$(INSTOBJEXT).m; \
+ echo "installing $(srcdir)/$$cat as" \
+ "$$dir/$(GETTEXT_PACKAGE)$(INSTOBJEXT).m"; \
+ else \
+ true; \
+ fi; \
+ fi; \
+ done
+ if test "$(PACKAGE)" = "glib"; then \
+ if test -r "$(MKINSTALLDIRS)"; then \
+ $(MKINSTALLDIRS) $(DESTDIR)$(gettextsrcdir); \
+ else \
+ $(SHELL) $(top_srcdir)/mkinstalldirs $(DESTDIR)$(gettextsrcdir); \
+ fi; \
+ $(INSTALL_DATA) $(srcdir)/Makefile.in.in \
+ $(DESTDIR)$(gettextsrcdir)/Makefile.in.in; \
+ else \
+ : ; \
+ fi
+
+# Define this as empty until I found a useful application.
+installcheck:
+
+uninstall:
+ catalogs='$(CATALOGS)'; \
+ for cat in $$catalogs; do \
+ cat=`basename $$cat`; \
+ lang=`echo $$cat | sed 's/\$(CATOBJEXT)$$//'`; \
+ rm -f $(DESTDIR)$(localedir)/$$lang/LC_MESSAGES/$(GETTEXT_PACKAGE)$(INSTOBJEXT); \
+ rm -f $(DESTDIR)$(localedir)/$$lang/LC_MESSAGES/$(GETTEXT_PACKAGE)$(INSTOBJEXT).m; \
+ rm -f $(DESTDIR)$(gnulocaledir)/$$lang/LC_MESSAGES/$(GETTEXT_PACKAGE)$(INSTOBJEXT); \
+ rm -f $(DESTDIR)$(gnulocaledir)/$$lang/LC_MESSAGES/$(GETTEXT_PACKAGE)$(INSTOBJEXT).m; \
+ done
+ rm -f $(DESTDIR)$(gettextsrcdir)/po-Makefile.in.in
+
+check: all
+
+dvi info tags TAGS ID:
+
+mostlyclean:
+ rm -f core core.* *.pox $(GETTEXT_PACKAGE).po *.old.po cat-id-tbl.tmp
+ rm -fr *.o
+
+clean: mostlyclean
+
+distclean: clean
+ rm -f Makefile Makefile.in POTFILES *.mo *.msg *.cat *.cat.m
+
+maintainer-clean: distclean
+ @echo "This command is intended for maintainers to use;"
+ @echo "it deletes files that may require special tools to rebuild."
+ rm -f $(GMOFILES)
+
+distdir = ../$(GETTEXT_PACKAGE)-$(VERSION)/$(subdir)
+dist distdir: update-po $(DISTFILES)
+ dists="$(DISTFILES)"; \
+ for file in $$dists; do \
+ ln $(srcdir)/$$file $(distdir) 2> /dev/null \
+ || cp -p $(srcdir)/$$file $(distdir); \
+ done
+
+update-po: Makefile
+ $(MAKE) $(GETTEXT_PACKAGE).pot
+ cd $(srcdir); \
+ catalogs='$(CATALOGS)'; \
+ for cat in $$catalogs; do \
+ cat=`basename $$cat`; \
+ lang=`echo $$cat | sed 's/\$(CATOBJEXT)$$//'`; \
+ cp $$lang.po $$lang.old.po; \
+ echo "$$lang:"; \
+ if $(MSGMERGE) $$lang; then \
+ rm -f $$lang.old.po; \
+ else \
+ echo "msgmerge for $$cat failed!"; \
+ rm -f $$lang.po; \
+ mv $$lang.old.po $$lang.po; \
+ fi; \
+ done
+
+.po: Makefile
+ $(MAKE) $(PACKAGE).pot;
+ PATH=`pwd`/../src:$$PATH; \
+ echo; printf "$*: "; \
+ if $(MSGMERGE) $*; then \
+ rm -f $*.old.po; \
+ else \
+ echo "msgmerge for * failed!"; \
+ mv $*.old.po $*.po; \
+ fi; \
+ msgfmt --statistics $*.po; echo;
+
+
+# POTFILES is created from POTFILES.in by stripping comments, empty lines
+# and Intltool tags (enclosed in square brackets), and appending a full
+# relative path to them
+POTFILES: POTFILES.in
+ ( if test 'x$(srcdir)' != 'x.'; then \
+ posrcprefix='$(top_srcdir)/'; \
+ else \
+ posrcprefix="../"; \
+ fi; \
+ rm -f $@-t $@ \
+ && (sed -e '/^#/d' \
+ -e "s/^\[.*\] +//" \
+ -e '/^[ ]*$$/d' \
+ -e "s@.*@ $$posrcprefix& \\\\@" < $(srcdir)/$@.in \
+ | sed -e '$$s/\\$$//') > $@-t \
+ && chmod a-w $@-t \
+ && mv $@-t $@ )
+
+Makefile: Makefile.in.in ../config.status POTFILES
+ cd .. \
+ && CONFIG_FILES=$(subdir)/$@.in CONFIG_HEADERS= \
+ $(SHELL) ./config.status
+
+# Tell versions [3.59,3.63) of GNU make not to export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/po/Makefile.in.in b/po/Makefile.in.in
new file mode 100644
index 0000000..98a4ca9
--- /dev/null
+++ b/po/Makefile.in.in
@@ -0,0 +1,255 @@
+# Makefile for program source directory in GNU NLS utilities package.
+# Copyright (C) 1995, 1996, 1997 by Ulrich Drepper <drepper@gnu.ai.mit.edu>
+#
+# This file file be copied and used freely without restrictions. It can
+# be used in projects which are not available under the GNU Public License
+# but which still want to provide support for the GNU gettext functionality.
+# Please note that the actual code is *not* freely available.
+#
+# - Modified by Owen Taylor <otaylor@redhat.com> to use GETTEXT_PACKAGE
+# instead of PACKAGE and to look for po2tbl in ./ not in intl/
+#
+# - Modified by jacob berkman <jacob@ximian.com> to install
+# Makefile.in.in and po2tbl.sed.in for use with glib-gettextize
+
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+PACKAGE = @PACKAGE@
+VERSION = @VERSION@
+
+SHELL = /bin/sh
+@SET_MAKE@
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = ..
+VPATH = @srcdir@
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+datadir = @datadir@
+libdir = @libdir@
+localedir = $(libdir)/locale
+gnulocaledir = $(datadir)/locale
+gettextsrcdir = $(datadir)/glib-2.0/gettext/po
+subdir = po
+
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+MKINSTALLDIRS = $(top_srcdir)/@MKINSTALLDIRS@
+
+CC = @CC@
+GENCAT = @GENCAT@
+GMSGFMT = @GMSGFMT@
+MSGFMT = @MSGFMT@
+XGETTEXT = @XGETTEXT@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+MSGMERGE = INTLTOOL_EXTRACT=$(INTLTOOL_EXTRACT) $(INTLTOOL_UPDATE) --gettext-package $(GETTEXT_PACKAGE) --dist
+GENPOT = INTLTOOL_EXTRACT=$(INTLTOOL_EXTRACT) $(INTLTOOL_UPDATE) --gettext-package $(GETTEXT_PACKAGE) --pot
+
+DEFS = @DEFS@
+CFLAGS = @CFLAGS@
+CPPFLAGS = @CPPFLAGS@
+
+INCLUDES = -I.. -I$(top_srcdir)/intl
+
+COMPILE = $(CC) -c $(DEFS) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) $(XCFLAGS)
+
+SOURCES =
+POFILES = @POFILES@
+GMOFILES = @GMOFILES@
+DISTFILES = ChangeLog Makefile.in.in POTFILES.in $(GETTEXT_PACKAGE).pot \
+$(POFILES) $(GMOFILES) $(SOURCES)
+
+POTFILES = \
+
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+INSTOBJEXT = @INSTOBJEXT@
+
+.SUFFIXES:
+.SUFFIXES: .c .o .po .pox .gmo .mo .msg .cat
+
+.c.o:
+ $(COMPILE) $<
+
+.po.pox:
+ $(MAKE) $(GETTEXT_PACKAGE).pot
+ $(MSGMERGE) $< $(srcdir)/$(GETTEXT_PACKAGE).pot -o $*.pox
+
+.po.mo:
+ $(MSGFMT) -o $@ $<
+
+.po.gmo:
+ file=$(srcdir)/`echo $* | sed 's,.*/,,'`.gmo \
+ && rm -f $$file && $(GMSGFMT) -o $$file $<
+
+.po.cat:
+ sed -f ../intl/po2msg.sed < $< > $*.msg \
+ && rm -f $@ && $(GENCAT) $@ $*.msg
+
+
+all: all-@USE_NLS@
+
+all-yes: $(CATALOGS)
+all-no:
+
+$(srcdir)/$(GETTEXT_PACKAGE).pot: $(POTFILES)
+ $(GENPOT)
+
+install: install-exec install-data
+install-exec:
+install-data: install-data-@USE_NLS@
+install-data-no: all
+install-data-yes: all
+ if test -r "$(MKINSTALLDIRS)"; then \
+ $(MKINSTALLDIRS) $(DESTDIR)$(datadir); \
+ else \
+ $(SHELL) $(top_srcdir)/mkinstalldirs $(DESTDIR)$(datadir); \
+ fi
+ @catalogs='$(CATALOGS)'; \
+ for cat in $$catalogs; do \
+ cat=`basename $$cat`; \
+ case "$$cat" in \
+ *.gmo) destdir=$(gnulocaledir);; \
+ *) destdir=$(localedir);; \
+ esac; \
+ lang=`echo $$cat | sed 's/\$(CATOBJEXT)$$//'`; \
+ dir=$(DESTDIR)$$destdir/$$lang/LC_MESSAGES; \
+ if test -r "$(MKINSTALLDIRS)"; then \
+ $(MKINSTALLDIRS) $$dir; \
+ else \
+ $(SHELL) $(top_srcdir)/mkinstalldirs $$dir; \
+ fi; \
+ if test -r $$cat; then \
+ $(INSTALL_DATA) $$cat $$dir/$(GETTEXT_PACKAGE)$(INSTOBJEXT); \
+ echo "installing $$cat as $$dir/$(GETTEXT_PACKAGE)$(INSTOBJEXT)"; \
+ else \
+ $(INSTALL_DATA) $(srcdir)/$$cat $$dir/$(GETTEXT_PACKAGE)$(INSTOBJEXT); \
+ echo "installing $(srcdir)/$$cat as" \
+ "$$dir/$(GETTEXT_PACKAGE)$(INSTOBJEXT)"; \
+ fi; \
+ if test -r $$cat.m; then \
+ $(INSTALL_DATA) $$cat.m $$dir/$(GETTEXT_PACKAGE)$(INSTOBJEXT).m; \
+ echo "installing $$cat.m as $$dir/$(GETTEXT_PACKAGE)$(INSTOBJEXT).m"; \
+ else \
+ if test -r $(srcdir)/$$cat.m ; then \
+ $(INSTALL_DATA) $(srcdir)/$$cat.m \
+ $$dir/$(GETTEXT_PACKAGE)$(INSTOBJEXT).m; \
+ echo "installing $(srcdir)/$$cat as" \
+ "$$dir/$(GETTEXT_PACKAGE)$(INSTOBJEXT).m"; \
+ else \
+ true; \
+ fi; \
+ fi; \
+ done
+ if test "$(PACKAGE)" = "glib"; then \
+ if test -r "$(MKINSTALLDIRS)"; then \
+ $(MKINSTALLDIRS) $(DESTDIR)$(gettextsrcdir); \
+ else \
+ $(SHELL) $(top_srcdir)/mkinstalldirs $(DESTDIR)$(gettextsrcdir); \
+ fi; \
+ $(INSTALL_DATA) $(srcdir)/Makefile.in.in \
+ $(DESTDIR)$(gettextsrcdir)/Makefile.in.in; \
+ else \
+ : ; \
+ fi
+
+# Define this as empty until I found a useful application.
+installcheck:
+
+uninstall:
+ catalogs='$(CATALOGS)'; \
+ for cat in $$catalogs; do \
+ cat=`basename $$cat`; \
+ lang=`echo $$cat | sed 's/\$(CATOBJEXT)$$//'`; \
+ rm -f $(DESTDIR)$(localedir)/$$lang/LC_MESSAGES/$(GETTEXT_PACKAGE)$(INSTOBJEXT); \
+ rm -f $(DESTDIR)$(localedir)/$$lang/LC_MESSAGES/$(GETTEXT_PACKAGE)$(INSTOBJEXT).m; \
+ rm -f $(DESTDIR)$(gnulocaledir)/$$lang/LC_MESSAGES/$(GETTEXT_PACKAGE)$(INSTOBJEXT); \
+ rm -f $(DESTDIR)$(gnulocaledir)/$$lang/LC_MESSAGES/$(GETTEXT_PACKAGE)$(INSTOBJEXT).m; \
+ done
+ rm -f $(DESTDIR)$(gettextsrcdir)/po-Makefile.in.in
+
+check: all
+
+dvi info tags TAGS ID:
+
+mostlyclean:
+ rm -f core core.* *.pox $(GETTEXT_PACKAGE).po *.old.po cat-id-tbl.tmp
+ rm -fr *.o
+
+clean: mostlyclean
+
+distclean: clean
+ rm -f Makefile Makefile.in POTFILES *.mo *.msg *.cat *.cat.m
+
+maintainer-clean: distclean
+ @echo "This command is intended for maintainers to use;"
+ @echo "it deletes files that may require special tools to rebuild."
+ rm -f $(GMOFILES)
+
+distdir = ../$(GETTEXT_PACKAGE)-$(VERSION)/$(subdir)
+dist distdir: update-po $(DISTFILES)
+ dists="$(DISTFILES)"; \
+ for file in $$dists; do \
+ ln $(srcdir)/$$file $(distdir) 2> /dev/null \
+ || cp -p $(srcdir)/$$file $(distdir); \
+ done
+
+update-po: Makefile
+ $(MAKE) $(GETTEXT_PACKAGE).pot
+ cd $(srcdir); \
+ catalogs='$(CATALOGS)'; \
+ for cat in $$catalogs; do \
+ cat=`basename $$cat`; \
+ lang=`echo $$cat | sed 's/\$(CATOBJEXT)$$//'`; \
+ cp $$lang.po $$lang.old.po; \
+ echo "$$lang:"; \
+ if $(MSGMERGE) $$lang; then \
+ rm -f $$lang.old.po; \
+ else \
+ echo "msgmerge for $$cat failed!"; \
+ rm -f $$lang.po; \
+ mv $$lang.old.po $$lang.po; \
+ fi; \
+ done
+
+.po: Makefile
+ $(MAKE) $(PACKAGE).pot;
+ PATH=`pwd`/../src:$$PATH; \
+ echo; printf "$*: "; \
+ if $(MSGMERGE) $*; then \
+ rm -f $*.old.po; \
+ else \
+ echo "msgmerge for * failed!"; \
+ mv $*.old.po $*.po; \
+ fi; \
+ msgfmt --statistics $*.po; echo;
+
+
+# POTFILES is created from POTFILES.in by stripping comments, empty lines
+# and Intltool tags (enclosed in square brackets), and appending a full
+# relative path to them
+POTFILES: POTFILES.in
+ ( if test 'x$(srcdir)' != 'x.'; then \
+ posrcprefix='$(top_srcdir)/'; \
+ else \
+ posrcprefix="../"; \
+ fi; \
+ rm -f $@-t $@ \
+ && (sed -e '/^#/d' \
+ -e "s/^\[.*\] +//" \
+ -e '/^[ ]*$$/d' \
+ -e "s@.*@ $$posrcprefix& \\\\@" < $(srcdir)/$@.in \
+ | sed -e '$$s/\\$$//') > $@-t \
+ && chmod a-w $@-t \
+ && mv $@-t $@ )
+
+Makefile: Makefile.in.in ../config.status POTFILES
+ cd .. \
+ && CONFIG_FILES=$(subdir)/$@.in CONFIG_HEADERS= \
+ $(SHELL) ./config.status
+
+# Tell versions [3.59,3.63) of GNU make not to export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/po/POTFILES b/po/POTFILES
new file mode 100644
index 0000000..c8d3bf5
--- /dev/null
+++ b/po/POTFILES
@@ -0,0 +1 @@
+ ../data/sound-juicer.glade
diff --git a/po/POTFILES.in b/po/POTFILES.in
new file mode 100644
index 0000000..93df9ed
--- /dev/null
+++ b/po/POTFILES.in
@@ -0,0 +1 @@
+data/sound-juicer.glade
diff --git a/po/sound-juicer.pot b/po/sound-juicer.pot
new file mode 100644
index 0000000..3e7afc9
--- /dev/null
+++ b/po/sound-juicer.pot
@@ -0,0 +1,94 @@
+# 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"
+"POT-Creation-Date: 2003-03-25 13:06+0000\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"
+
+#: data/sound-juicer.glade.h:1
+msgid "<b>Artist:</b>"
+msgstr ""
+
+#: data/sound-juicer.glade.h:2
+msgid "<b>CD Drive</b>"
+msgstr ""
+
+#: data/sound-juicer.glade.h:3
+msgid "<b>CD Info</b>"
+msgstr ""
+
+#: data/sound-juicer.glade.h:4
+msgid "<b>Duration:</b>"
+msgstr ""
+
+#: data/sound-juicer.glade.h:5
+msgid "<b>Output Path</b>"
+msgstr ""
+
+#: data/sound-juicer.glade.h:6
+msgid "<b>Title:</b>"
+msgstr ""
+
+#: data/sound-juicer.glade.h:7
+msgid "Multiple Albums Found"
+msgstr ""
+
+#: data/sound-juicer.glade.h:8
+msgid "Prefere_nces"
+msgstr ""
+
+#: data/sound-juicer.glade.h:9
+msgid "Preferences"
+msgstr ""
+
+#: data/sound-juicer.glade.h:10
+msgid "Re-read"
+msgstr ""
+
+#: data/sound-juicer.glade.h:11
+msgid "Sound Juicer"
+msgstr ""
+
+#: data/sound-juicer.glade.h:12
+msgid ""
+"This CD could be more than one album. Please select which album it is below "
+"and press OK."
+msgstr ""
+
+#: data/sound-juicer.glade.h:13
+msgid "_About"
+msgstr ""
+
+#: data/sound-juicer.glade.h:14
+msgid "_Edit"
+msgstr ""
+
+#: data/sound-juicer.glade.h:15
+msgid "_Extract"
+msgstr ""
+
+#: data/sound-juicer.glade.h:16
+msgid "_File"
+msgstr ""
+
+#: data/sound-juicer.glade.h:17
+msgid "_Help"
+msgstr ""
+
+#: data/sound-juicer.glade.h:18
+msgid "_Quit"
+msgstr ""
+
+#: data/sound-juicer.glade.h:19
+msgid "label"
+msgstr ""
diff --git a/src/bacon-cd-selection.c b/src/bacon-cd-selection.c
new file mode 100644
index 0000000..57e1b8d
--- /dev/null
+++ b/src/bacon-cd-selection.c
@@ -0,0 +1,439 @@
+/*
+ * Copyright (C) 2002 Bastien Nocera <hadess@hadess.net>
+ *
+ * bacon-cd-selection.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 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.
+ *
+ * Authors: Bastien Nocera <hadess@hadess.net>
+ */
+
+#include <config.h>
+
+#include <string.h>
+#include <gnome.h>
+
+#include "bacon-cd-selection.h"
+#include "cd-drive.h"
+
+/* Signals */
+enum {
+ DEVICE_CHANGED,
+ LAST_SIGNAL
+};
+
+/* Arguments */
+enum {
+ PROP_0,
+ PROP_DEVICE,
+};
+
+struct BaconCdSelectionPrivate {
+ gboolean is_entry;
+ GtkWidget *widget;
+ GList *cdroms;
+};
+
+
+static void bacon_cd_selection_class_init (BaconCdSelectionClass *klass);
+static void bacon_cd_selection_instance_init (BaconCdSelection *bcs);
+
+static void bacon_cd_selection_set_property (GObject *object, guint property_id,
+ const GValue *value, GParamSpec *pspec);
+static void bacon_cd_selection_get_property (GObject *object, guint property_id,
+ GValue *value, GParamSpec *pspec);
+
+static void bacon_cd_selection_realize (GtkWidget *widget);
+static void bacon_cd_selection_unrealize (GtkWidget *widget);
+static void bacon_cd_selection_finalize (GObject *object);
+
+static GtkWidgetClass *parent_class = NULL;
+
+static int bcs_table_signals[LAST_SIGNAL] = { 0 };
+
+static CDDrive *
+get_drive (BaconCdSelection *bcs, int nr)
+{
+ GList *item;
+
+ item = g_list_nth (bcs->priv->cdroms, nr);
+ if (item == NULL)
+ return NULL;
+ else
+ return item->data;
+}
+
+
+GtkType
+bacon_cd_selection_get_type (void)
+{
+ static GtkType bacon_cd_selection_type = 0;
+
+ if (!bacon_cd_selection_type) {
+ static const GTypeInfo bacon_cd_selection_info = {
+ sizeof (BaconCdSelectionClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) bacon_cd_selection_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL /* class_data */,
+ sizeof (BaconCdSelection),
+ 0 /* n_preallocs */,
+ (GInstanceInitFunc) bacon_cd_selection_instance_init,
+ };
+
+ bacon_cd_selection_type = g_type_register_static
+ (GTK_TYPE_VBOX,
+ "BaconCdSelection", &bacon_cd_selection_info,
+ (GTypeFlags)0);
+ }
+
+ return bacon_cd_selection_type;
+}
+
+static void
+bacon_cd_selection_class_init (BaconCdSelectionClass *klass)
+{
+ GObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+
+ object_class = (GObjectClass *) klass;
+ widget_class = (GtkWidgetClass *) klass;
+
+ parent_class = gtk_type_class (gtk_vbox_get_type ());
+
+ /* GtkWidget */
+ widget_class->realize = bacon_cd_selection_realize;
+ widget_class->unrealize = bacon_cd_selection_unrealize;
+
+ /* GObject */
+ object_class->set_property = bacon_cd_selection_set_property;
+ object_class->get_property = bacon_cd_selection_get_property;
+ object_class->finalize = bacon_cd_selection_finalize;
+
+ /* Properties */
+ g_object_class_install_property (object_class, PROP_DEVICE,
+ g_param_spec_string ("device", NULL, NULL,
+ FALSE, G_PARAM_READWRITE));
+
+ /* Signals */
+ bcs_table_signals[DEVICE_CHANGED] =
+ g_signal_new ("device-changed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (BaconCdSelectionClass,
+ device_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE, 1, G_TYPE_STRING);
+}
+
+static void
+bacon_cd_selection_instance_init (BaconCdSelection *bcs)
+{
+ bcs->priv = g_new0 (BaconCdSelectionPrivate, 1);
+
+#ifdef __linux__
+ bcs->priv->is_entry = FALSE;
+#else
+ bcs->priv->is_entry = TRUE;
+#endif
+
+ bcs->priv->cdroms = NULL;
+}
+
+static void
+bacon_cd_selection_realize (GtkWidget *widget)
+{
+ if (GTK_WIDGET_CLASS (parent_class)->realize != NULL) {
+ (* GTK_WIDGET_CLASS (parent_class)->realize) (widget);
+ }
+}
+
+static void
+bacon_cd_selection_unrealize (GtkWidget *widget)
+{
+ if (GTK_WIDGET_CLASS (parent_class)->unrealize != NULL) {
+ (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
+ }
+}
+
+static void
+bacon_cd_selection_finalize (GObject *object)
+{
+ GList *l;
+
+ BaconCdSelection *bcs = (BaconCdSelection *) object;
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+
+ gtk_widget_destroy (bcs->priv->widget);
+
+ l = bcs->priv->cdroms;
+ while (l != NULL)
+ {
+ CDDrive *cdrom = l->data;
+
+ cd_drive_free (cdrom);
+ l = g_list_remove (l, cdrom);
+ g_free (cdrom);
+ }
+
+ bcs->priv = NULL;
+ bcs = NULL;
+}
+
+static void
+option_menu_device_changed (GtkOptionMenu *option_menu, gpointer user_data)
+{
+ BaconCdSelection *bcs = (BaconCdSelection *) user_data;
+ CDDrive *drive;
+ int i;
+
+ i = gtk_option_menu_get_history (GTK_OPTION_MENU (option_menu));
+ drive = get_drive (bcs, i);
+
+ g_signal_emit (G_OBJECT (bcs),
+ bcs_table_signals[DEVICE_CHANGED],
+ 0, drive->device);
+}
+
+static GtkWidget *
+cdrom_option_menu (BaconCdSelection *bcs)
+{
+ GList *l;
+ GtkWidget *option_menu, *menu, *item;
+ CDDrive *cdrom;
+
+ bcs->priv->cdroms = scan_for_cdroms (FALSE, FALSE);
+
+ menu = gtk_menu_new();
+ gtk_widget_show(menu);
+
+ option_menu = gtk_option_menu_new ();
+
+ for (l = bcs->priv->cdroms; l != NULL; l = l->next)
+ {
+ cdrom = l->data;
+
+ if (cdrom->display_name == NULL)
+ g_warning ("cdrom->display_name != NULL failed");
+ item = gtk_menu_item_new_with_label (cdrom->display_name
+ ? cdrom->display_name : _("Unnamed CDROM"));
+ gtk_widget_show (item);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+ }
+ gtk_option_menu_set_history (GTK_OPTION_MENU (option_menu), 0);
+
+ gtk_option_menu_set_menu (GTK_OPTION_MENU (option_menu), menu);
+
+ if (bcs->priv->cdroms == NULL)
+ gtk_widget_set_sensitive (option_menu, FALSE);
+
+ return option_menu;
+}
+
+static void
+on_combo_entry_changed (GnomeFileEntry *entry, gpointer user_data)
+{
+ BaconCdSelection *bcs = (BaconCdSelection *) user_data;
+ const char *str;
+ GtkWidget *widget;
+
+ widget = gnome_file_entry_gtk_entry (entry);
+ str = gtk_entry_get_text (GTK_ENTRY (widget));
+
+ g_signal_emit (G_OBJECT (bcs),
+ bcs_table_signals[DEVICE_CHANGED],
+ 0, str);
+}
+
+GtkWidget *
+bacon_cd_selection_new (void)
+{
+ GtkWidget *widget;
+ BaconCdSelection *bcs;
+
+ widget = GTK_WIDGET
+ (g_object_new (bacon_cd_selection_get_type (), NULL));
+ bcs = BACON_CD_SELECTION (widget);
+
+ if (bcs->priv->is_entry)
+ {
+ bcs->priv->widget = gnome_file_entry_new (NULL,
+ _("Select the drive"));
+ g_signal_connect (G_OBJECT (bcs->priv->widget), "changed",
+ G_CALLBACK (on_combo_entry_changed), bcs);
+
+ gtk_box_pack_start (GTK_BOX (widget),
+ bcs->priv->widget,
+ TRUE, /* expand */
+ TRUE, /* fill */
+ 0); /* padding */
+ } else {
+ bcs->priv->widget = cdrom_option_menu (bcs);
+
+ g_signal_connect (bcs->priv->widget, "changed",
+ (GCallback)option_menu_device_changed, bcs);
+
+ gtk_box_pack_start (GTK_BOX (widget),
+ bcs->priv->widget,
+ TRUE, /* expand */
+ TRUE, /* fill */
+ 0); /* padding */
+ }
+
+ gtk_widget_show_all (bcs->priv->widget);
+
+ return widget;
+}
+
+/* Properties */
+static void
+bacon_cd_selection_set_property (GObject *object, guint property_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ BaconCdSelection *bcs;
+
+ g_return_if_fail (BACON_IS_CD_SELECTION (object));
+
+ bcs = BACON_CD_SELECTION (object);
+
+ switch (property_id)
+ {
+ case PROP_DEVICE:
+ bacon_cd_selection_set_device (bcs, g_value_get_string (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+bacon_cd_selection_get_property (GObject *object, guint property_id,
+ GValue *value, GParamSpec *pspec)
+{
+ BaconCdSelection *bcs;
+
+ g_return_if_fail (BACON_IS_CD_SELECTION (object));
+
+ bcs = BACON_CD_SELECTION (object);
+
+ switch (property_id)
+ {
+ case PROP_DEVICE:
+ g_value_set_string (value, bacon_cd_selection_get_device (bcs));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+const char *
+bacon_cd_selection_get_default_device (BaconCdSelection *bcs)
+{
+ GList *l;
+ CDDrive *drive;
+
+ l = bcs->priv->cdroms;
+ if (bcs->priv->cdroms == NULL)
+ /* TODO: not sure about this. if this is NULL, there are no devices present, so return NULL? */
+ return "/dev/cdrom";
+
+ drive = l->data;
+
+ return drive->device;
+}
+
+void
+bacon_cd_selection_set_device (BaconCdSelection *bcs, const char *device)
+{
+ GtkWidget *entry;
+ GList *l;
+ CDDrive *drive;
+ gboolean found;
+ int i;
+
+ g_return_if_fail (bcs != NULL);
+ g_return_if_fail (BACON_IS_CD_SELECTION (bcs));
+
+ if (bcs->priv->is_entry == TRUE)
+ {
+ entry = gnome_file_entry_gtk_entry
+ (GNOME_FILE_ENTRY (bcs->priv->widget));
+ gtk_entry_set_text (GTK_ENTRY (entry), device);
+ } else {
+ i = -1;
+ found = FALSE;
+
+ for (l = bcs->priv->cdroms; l != NULL && found == FALSE;
+ l = l->next)
+ {
+ i++;
+
+ drive = l->data;
+
+ if (strcmp (drive->device, device) == 0)
+ found = TRUE;
+ }
+
+ if (found)
+ {
+ gtk_option_menu_set_history (GTK_OPTION_MENU
+ (bcs->priv->widget), i);
+ } else {
+ /* If the device doesn't exist, set it back to
+ * the default */
+ gtk_option_menu_set_history (GTK_OPTION_MENU
+ (bcs->priv->widget), 0);
+
+ drive = get_drive (bcs, 0);
+
+ if (drive == NULL)
+ return;
+
+ g_signal_emit (G_OBJECT (bcs),
+ bcs_table_signals [DEVICE_CHANGED],
+ 0, drive->device);
+ }
+
+ }
+}
+
+const char *
+bacon_cd_selection_get_device (BaconCdSelection *bcs)
+{
+ GtkWidget *entry;
+ CDDrive *drive;
+ int i;
+
+ g_return_val_if_fail (bcs != NULL, NULL);
+ g_return_val_if_fail (BACON_IS_CD_SELECTION (bcs), NULL);
+
+ if (bcs->priv->is_entry == TRUE)
+ {
+ entry = gnome_file_entry_gtk_entry
+ (GNOME_FILE_ENTRY (bcs->priv->widget));
+ return gtk_entry_get_text (GTK_ENTRY (entry));
+ } else {
+ i = gtk_option_menu_get_history (GTK_OPTION_MENU
+ (bcs->priv->widget));
+ drive = get_drive (bcs, i);
+
+ return drive ? drive->device : NULL;
+ }
+
+ return NULL;
+}
+
diff --git a/src/bacon-cd-selection.h b/src/bacon-cd-selection.h
new file mode 100644
index 0000000..ea07294
--- /dev/null
+++ b/src/bacon-cd-selection.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2002 Bastien Nocera <hadess@hadess.net>
+ *
+ * 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.
+ *
+ * Authors: Bastien Nocera <hadess@hadess.net>
+ */
+
+#ifndef HAVE_BACON_CD_SELECTION_H
+#define HAVE_BACON_CD_SELECTION_H
+
+#include <gtk/gtkwidget.h>
+
+G_BEGIN_DECLS
+
+#define BACON_CD_SELECTION(obj) (GTK_CHECK_CAST ((obj), bacon_cd_selection_get_type (), BaconCdSelection))
+#define BACON_CD_SELECTION_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), bacon_cd_selection_get_type (), BaconCdSelectionClass))
+#define BACON_IS_CD_SELECTION(obj) (GTK_CHECK_TYPE (obj, bacon_cd_selection_get_type ()))
+#define BACON_IS_CD_SELECTION_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), bacon_cd_selection_get_type ()))
+
+typedef struct BaconCdSelectionPrivate BaconCdSelectionPrivate;
+
+typedef struct {
+ GtkVBox widget;
+ BaconCdSelectionPrivate *priv;
+} BaconCdSelection;
+
+typedef struct {
+ GtkVBoxClass parent_class;
+ void (*device_changed) (GtkWidget *gtx, const char *title);
+} BaconCdSelectionClass;
+
+GtkType bacon_cd_selection_get_type (void);
+GtkWidget *bacon_cd_selection_new (void);
+
+void bacon_cd_selection_set_device (BaconCdSelection *tcs,
+ const char *device);
+const char *bacon_cd_selection_get_device (BaconCdSelection *tcs);
+const char *bacon_cd_selection_get_default_device (BaconCdSelection *tcs);
+
+G_END_DECLS
+
+#endif /* HAVE_BACON_CD_SELECTION_H */
diff --git a/src/cd-drive.c b/src/cd-drive.c
new file mode 100644
index 0000000..e5c45f1
--- /dev/null
+++ b/src/cd-drive.c
@@ -0,0 +1,593 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+
+ cd-drive.c: easy to use cd burner software
+
+ Copyright (C) 2002 Red Hat, Inc.
+
+ 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.
+
+ Authors: Alexander Larsson <alexl@redhat.com>
+ Bastien Nocera <hadess@hadess.net>
+*/
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <math.h>
+
+#ifdef __linux__
+#include <scsi/scsi.h>
+#include <scsi/sg.h>
+#endif /* __linux__ */
+
+#include <glib.h>
+#include <libgnome/gnome-i18n.h>
+
+#include "cd-drive.h"
+
+#ifdef __linux__
+
+#ifdef USE_STABLE_LIBGLIB
+static gboolean
+g_str_has_prefix (gchar *haystack, gchar *needle)
+{
+ if (haystack == NULL && needle == NULL) {
+ return TRUE;
+ }
+
+ if (haystack == NULL || needle == NULL) {
+ return FALSE;
+ }
+
+ if (strncmp (haystack, needle, strlen (needle)) == 0) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+#endif /* USE_STABLE_LIBGLIB */
+
+static char **
+read_lines (char *filename)
+{
+ char *contents;
+ gsize len;
+ char *p, *n;
+ GPtrArray *array;
+
+ if (g_file_get_contents (filename,
+ &contents,
+ &len, NULL)) {
+
+ array = g_ptr_array_new ();
+
+ p = contents;
+ while ((n = memchr (p, '\n', len - (p - contents))) != NULL) {
+ *n = 0;
+ g_ptr_array_add (array, g_strdup (p));
+ p = n + 1;
+ }
+ if ((gsize)(p - contents) < len) {
+ g_ptr_array_add (array, g_strndup (p, len - (p - contents)));
+ }
+
+ g_ptr_array_add (array, NULL);
+
+ g_free (contents);
+ return (char **)g_ptr_array_free (array, FALSE);
+ }
+ return NULL;
+}
+
+struct scsi_unit {
+ gboolean exist;
+ char *vendor;
+ char *model;
+ char *rev;
+ int bus;
+ int id;
+ int lun;
+ int type;
+};
+
+struct cdrom_unit {
+ char *device;
+ int speed;
+ gboolean can_write_cdr;
+ gboolean can_write_cdrw;
+ gboolean can_write_dvdr;
+ gboolean can_write_dvdram;
+ gboolean can_read_dvd;
+};
+
+static void
+parse_sg_line (char *device_str, char *devices, struct scsi_unit *scsi_unit)
+{
+ char vendor[9], model[17], rev[5];
+ int host_no, access_count, queue_depth, device_busy, online, channel;
+
+ scsi_unit->exist = FALSE;
+
+ if (strcmp (device_str, "<no active device>") == 0) {
+ scsi_unit->exist = FALSE;
+ return;
+ }
+ if (sscanf (device_str, "%8c\t%16c\t%4c", vendor, model, rev) != 3) {
+ g_warning ("Couldn't match line in /proc/scsi/sg/device_strs\n");
+ return;
+ }
+ vendor[8] = 0; model[16] = 0; rev[4] = 0;
+
+ scsi_unit->vendor = g_strdup (g_strstrip (vendor));
+ scsi_unit->model = g_strdup (g_strstrip (model));
+ scsi_unit->rev = g_strdup (g_strstrip (rev));
+
+ if (sscanf (devices, "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d",
+ &host_no,
+ &channel, &scsi_unit->id, &scsi_unit->lun, &scsi_unit->type,
+ &access_count, &queue_depth, &device_busy,
+ &online) != 9) {
+
+ g_warning ("Couldn't match line in /proc/scsi/sg/devices\n");
+ return;
+ }
+ scsi_unit->bus = host_no;
+ scsi_unit->exist = TRUE;
+}
+
+static int
+count_strings (char *p)
+{
+ int n_strings;
+
+ n_strings = 0;
+ while (*p != 0) {
+ n_strings++;
+ while (*p != '\t' && *p != 0) {
+ p++;
+ }
+ if (*p == '\t') {
+ p++;
+ }
+ }
+ return n_strings;
+}
+
+static int
+get_cd_scsi_id (const char *dev, int *bus, int *id, int *lun)
+{
+ int fd;
+ char *devfile;
+ struct {
+ long mux4;
+ long hostUniqueId;
+ } m_idlun;
+
+ devfile = g_strdup_printf ("/dev/%s", dev);
+ fd = open(devfile, O_RDONLY | O_NONBLOCK);
+ g_free (devfile);
+
+ if (fd < 0) {
+ g_warning ("Failed to open cd device %s\n", dev);
+ return 0;
+ }
+
+ if (ioctl (fd, SCSI_IOCTL_GET_BUS_NUMBER, bus) < 0) {
+ g_warning ("Failed to get scsi bus nr\n");
+ close (fd);
+ return 0;
+ }
+ if (ioctl (fd, SCSI_IOCTL_GET_IDLUN, &m_idlun) < 0) {
+ g_warning ("Failed to get scsi id and lun\n");
+ close(fd);
+ return 0;
+ }
+ *id = m_idlun.mux4 & 0xFF;
+ *lun = (m_idlun.mux4 >> 8) & 0xFF;
+
+ close(fd);
+ return 1;
+}
+
+static struct scsi_unit *
+lookup_scsi_unit (int bus, int id, int lun,
+ struct scsi_unit *scsi_units, int n_scsi_units)
+{
+ int i;
+
+ for (i = 0; i < n_scsi_units; i++) {
+ if (scsi_units[i].bus == bus &&
+ scsi_units[i].id == id &&
+ scsi_units[i].lun == lun) {
+ return &scsi_units[i];
+ }
+ }
+ return NULL;
+}
+
+static int
+get_device_max_speed (char *id)
+{
+ int max_speed, i;
+ const char *argv[20]; /* Shouldn't need more than 20 arguments */
+ char *dev_str, *stdout_data, *speed;
+
+ max_speed = 1;
+
+ i = 0;
+ argv[i++] = "cdrecord";
+ argv[i++] = "-prcap";
+ dev_str = g_strdup_printf ("dev=%s", id);
+ argv[i++] = dev_str;
+ argv[i++] = NULL;
+
+ if (g_spawn_sync (NULL,
+ (char **)argv,
+ NULL,
+ G_SPAWN_SEARCH_PATH | G_SPAWN_STDERR_TO_DEV_NULL,
+ NULL, NULL,
+ &stdout_data,
+ NULL,
+ NULL,
+ NULL)) {
+ speed = strstr (stdout_data, "Maximum write speed in kB/s:");
+ if (speed != NULL) {
+ speed += strlen ("Maximum write speed in kB/s:");
+ max_speed = (int)floor (atol (speed) / 176.0 + 0.5);
+ }
+ }
+
+ g_free (dev_str);
+ return max_speed;
+
+}
+
+
+static char *
+get_scsi_cd_name (int bus, int id, int lun, const char *dev,
+ struct scsi_unit *scsi_units, int n_scsi_units)
+{
+ struct scsi_unit *scsi_unit;
+
+ scsi_unit = lookup_scsi_unit (bus, id, lun, scsi_units, n_scsi_units);
+ if (scsi_unit == NULL) {
+ return g_strdup_printf (_("Unnamed SCSI CDROM (%s)"), dev);
+ }
+
+ return g_strdup_printf ("%s - %s",
+ scsi_unit->vendor,
+ scsi_unit->model);
+}
+
+static GList *
+add_linux_cd_recorder (GList *cdroms,
+ struct cdrom_unit *cdrom_s,
+ struct scsi_unit *scsi_units,
+ int n_scsi_units)
+{
+ int bus, id, lun;
+ CDDrive *cdrom;
+
+ if (!get_cd_scsi_id (cdrom_s->device, &bus, &id, &lun)) {
+ return cdroms;
+ }
+
+ cdrom = g_new0 (CDDrive, 1);
+ cdrom->device = g_strdup_printf ("/dev/%s", cdrom_s->device);
+ cdrom->cdrecord_id = g_strdup_printf ("%d,%d,%d",
+ bus, id, lun);
+ cdrom->display_name = get_scsi_cd_name (bus, id, lun, cdrom_s->device, scsi_units, n_scsi_units);
+ cdrom->max_speed_write = get_device_max_speed (cdrom->cdrecord_id);
+ cdrom->max_speed_read = cdrom_s->speed;
+ if (cdrom_s->can_write_dvdr
+ || cdrom_s->can_write_dvdram) {
+ cdrom->type = CDDRIVE_TYPE_DVD_RECORDER;
+ } else {
+ cdrom->type = CDDRIVE_TYPE_CD_RECORDER;
+ }
+
+ return g_list_append (cdroms, cdrom);
+}
+
+static char *
+cdrom_get_name (struct cdrom_unit *cdrom, struct scsi_unit *scsi_units, int n_scsi_units)
+{
+ char *filename, *line, *retval;
+ int bus, id, lun, i;
+
+ g_return_val_if_fail (cdrom != NULL, FALSE);
+
+ if ((cdrom->device[0] == 's' &&
+ cdrom->device[1] == 'r') ||
+ (cdrom->device[0] == 's' &&
+ cdrom->device[1] == 'c' &&
+ cdrom->device[2] == 'd')) {
+ get_cd_scsi_id (cdrom->device, &bus, &id, &lun);
+
+ retval = get_scsi_cd_name (bus, id, lun, cdrom->device, scsi_units, n_scsi_units);
+ } else {
+ filename = g_strdup_printf ("/proc/ide/%s/model",
+ cdrom->device);
+ if (!g_file_get_contents (filename, &line, NULL, NULL) ||
+ line == NULL) {
+ g_free (filename);
+ return NULL;
+ }
+ g_free (filename);
+
+ i = strlen (line);
+ if (line[i-1] != '\n') {
+ retval = g_strdup (line);
+ } else {
+ retval = g_strndup (line, i - 1);
+ }
+
+ g_free (line);
+ }
+
+ return retval;
+}
+
+static GList *
+add_linux_cd_drive (GList *cdroms, struct cdrom_unit *cdrom_s,
+ struct scsi_unit *scsi_units, int n_scsi_units)
+{
+ CDDrive *cdrom;
+
+ cdrom = g_new0 (CDDrive, 1);
+ cdrom->cdrecord_id = NULL;
+ cdrom->device = g_strdup_printf ("/dev/%s", cdrom_s->device);
+ cdrom->display_name = cdrom_get_name (cdrom_s, scsi_units, n_scsi_units);
+ cdrom->max_speed_write = 0; /* Can't write */
+ cdrom->max_speed_read = cdrom_s->speed;
+ if (cdrom_s->can_read_dvd) {
+ cdrom->type = CDDRIVE_TYPE_DVD_DRIVE;
+ } else {
+ cdrom->type = CDDRIVE_TYPE_CD_DRIVE;
+ }
+
+ return g_list_append (cdroms, cdrom);
+}
+
+static char *
+get_cd_device_file (const char *str)
+{
+ char *devname;
+
+ if (str[0] == 's' && str[1] == 'r') {
+ devname = g_strdup_printf ("/dev/scd%c", str[2]);
+ if (g_file_test (devname, G_FILE_TEST_EXISTS)) {
+ g_free (devname);
+ return g_strdup_printf ("scd%c", str[2]);
+ }
+ g_free (devname);
+ }
+ return g_strdup (str);
+}
+
+static GList *
+linux_scan (gboolean recorder_only)
+{
+ char **device_str, **devices;
+ char **cdrom_info;
+ struct scsi_unit *scsi_units;
+ struct cdrom_unit *cdroms;
+ char *p, *t;
+ int n_scsi_units, n_cdroms, i, j;
+ int fd;
+ GList *cdroms_list;
+
+ /* Open /dev/sg0 to force loading of the sg module if not loaded yet */
+ fd = open ("/dev/sg0", O_RDONLY);
+ if (fd != -1) {
+ close (fd);
+ }
+
+ devices = read_lines ("/proc/scsi/sg/devices");
+ if (devices != NULL) {
+ device_str = read_lines ("/proc/scsi/sg/device_strs");
+ if (device_str == NULL) {
+ g_warning ("Can't read /proc/scsi/sg/device_strs");
+ g_strfreev (devices);
+ return NULL;
+ }
+
+ /* Count the number of scsi units, DO NOT REMOVE */
+ for (n_scsi_units = 0;
+ device_str[n_scsi_units] != NULL && devices[n_scsi_units] != NULL;
+ n_scsi_units++) {
+ /* Nothing */
+ }
+
+ scsi_units = g_new0 (struct scsi_unit, n_scsi_units);
+ for (i = 0; i < n_scsi_units; i++) {
+ parse_sg_line (device_str[i], devices[i], &scsi_units[i]);
+ }
+
+ g_strfreev (device_str);
+ g_strfreev (devices);
+ } else {
+ scsi_units = NULL;
+ n_scsi_units = 0;
+ }
+
+ cdrom_info = read_lines ("/proc/sys/dev/cdrom/info");
+ if (cdrom_info == NULL || cdrom_info[0] == NULL || cdrom_info[1] == NULL) {
+ g_warning ("Couldn't read /proc/sys/dev/cdrom/info");
+ g_free (scsi_units);
+ return NULL;
+ }
+
+ if (!g_str_has_prefix (cdrom_info[2], "drive name:\t")) {
+ g_free (scsi_units);
+ return NULL;
+ }
+ p = cdrom_info[2] + strlen ("drive name:\t");
+ while (*p == '\t') {
+ p++;
+ }
+ n_cdroms = count_strings (p);
+ cdroms = g_new0 (struct cdrom_unit, n_cdroms);
+
+ for (j = 0; j < n_cdroms; j++) {
+ t = strchr (p, '\t');
+ if (t != NULL) {
+ *t = 0;
+ }
+ cdroms[j].device = get_cd_device_file (p);
+ if (t != NULL) {
+ p = t + 1;
+ }
+ }
+
+ for (i = 3; cdrom_info[i] != NULL; i++) {
+ if (g_str_has_prefix (cdrom_info[i], "Can write CD-R:")) {
+ p = cdrom_info[i] + strlen ("Can write CD-R:");
+ while (*p == '\t') {
+ p++;
+ }
+ for (j = 0; j < n_cdroms; j++) {
+ cdroms[j].can_write_cdr = *p++ == '1';
+
+ /* Skip tab */
+ p++;
+ }
+ }
+ if (g_str_has_prefix (cdrom_info[i], "Can write CD-RW:")) {
+ p = cdrom_info[i] + strlen ("Can write CD-RW:");
+ while (*p == '\t') {
+ p++;
+ }
+ for (j = 0; j < n_cdroms; j++) {
+ cdroms[j].can_write_cdrw = *p++ == '1';
+
+ /* Skip tab */
+ p++;
+ }
+ }
+ if (g_str_has_prefix (cdrom_info[i], "Can write DVD-R:")) {
+ p = cdrom_info[i] + strlen ("Can write DVD-R:");
+ while (*p == '\t') {
+ p++;
+ }
+ for (j = 0; j < n_cdroms; j++) {
+ cdroms[j].can_write_dvdr = *p++ == '1';
+
+ /* Skip tab */
+ p++;
+ }
+ }
+ if (g_str_has_prefix (cdrom_info[i], "Can write DVD-RAM:")) {
+ p = cdrom_info[i] + strlen ("Can write DVD-RAM:");
+ while (*p == '\t') {
+ p++;
+ }
+ for (j = 0; j < n_cdroms; j++) {
+ cdroms[j].can_write_dvdram = *p++ == '1';
+
+ /* Skip tab */
+ p++;
+ }
+ }
+ if (g_str_has_prefix (cdrom_info[i], "Can read DVD:")) {
+ p = cdrom_info[i] + strlen ("Can read DVD:");
+ while (*p == '\t') {
+ p++;
+ }
+ for (j = 0; j < n_cdroms; j++) {
+ cdroms[j].can_read_dvd = *p++ == '1';
+
+ /* Skip tab */
+ p++;
+ }
+ }
+ if (g_str_has_prefix (cdrom_info[i], "drive speed:")) {
+ p = cdrom_info[i] + strlen ("drive speed:");
+ while (*p == '\t') {
+ p++;
+ }
+ for (j = 0; j < n_cdroms; j++) {
+ cdroms[j].speed = atoi (p);
+
+ /* Skip tab */
+ p++;
+ }
+ }
+ }
+ g_strfreev (cdrom_info);
+
+ cdroms_list = NULL;
+ for (i = 0; i < n_cdroms; i++) {
+ if (cdroms[i].can_write_cdr ||
+ cdroms[i].can_write_cdrw ||
+ cdroms[i].can_write_dvdr ||
+ cdroms[i].can_write_dvdram) {
+ cdroms_list = add_linux_cd_recorder (cdroms_list,
+ &cdroms[i], scsi_units, n_scsi_units);
+ } else if (!recorder_only) {
+ cdroms_list = add_linux_cd_drive (cdroms_list,
+ &cdroms[i], scsi_units, n_scsi_units);
+ }
+ }
+
+ g_free (scsi_units);
+ g_free (cdroms);
+
+ return cdroms_list;
+}
+#endif /* __linux__ */
+
+GList *
+scan_for_cdroms (gboolean recorder_only, gboolean add_image)
+{
+ CDDrive *cdrom;
+ GList *cdroms = NULL;
+
+#ifdef __linux__
+ cdroms = linux_scan (recorder_only);
+#endif
+
+ if (add_image) {
+ /* File */
+ cdrom = g_new0 (CDDrive, 1);
+ cdrom->display_name = g_strdup (_("File image"));
+ cdrom->max_speed_read = 0;
+ cdrom->max_speed_write = 0;
+ cdrom->type = CDDRIVE_TYPE_FILE;
+
+ cdroms = g_list_append (cdroms, cdrom);
+ }
+
+ return cdroms;
+}
+
+void
+cd_drive_free (CDDrive *drive)
+{
+ g_return_if_fail (drive != NULL);
+
+ g_free (drive->display_name);
+ g_free (drive->cdrecord_id);
+ g_free (drive->device);
+}
+
diff --git a/src/cd-drive.h b/src/cd-drive.h
new file mode 100644
index 0000000..ef0e238
--- /dev/null
+++ b/src/cd-drive.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+
+ cd-drive.h: easy to use cd burner software
+
+ Copyright (C) 2002 Red Hat, Inc.
+
+ 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.
+
+ Authors: Alexander Larsson <alexl@redhat.com>
+ Bastien Nocera <hadess@hadess.net>
+*/
+
+#ifndef CD_DRIVE_H
+#define CD_DRIVE_H
+
+#include <glib.h>
+
+typedef enum {
+ CDDRIVE_TYPE_FILE,
+ CDDRIVE_TYPE_CD_RECORDER,
+ CDDRIVE_TYPE_DVD_RECORDER,
+ CDDRIVE_TYPE_CD_DRIVE,
+ CDDRIVE_TYPE_DVD_DRIVE,
+} CDDriveType;
+
+typedef struct {
+ CDDriveType type;
+ char *display_name;
+ int max_speed_write;
+ int max_speed_read;
+ char *cdrecord_id;
+ char *device;
+} CDDrive;
+
+/* Returns a list of CDDrive structs */
+GList *scan_for_cdroms (gboolean recorder_only, gboolean add_image);
+void cd_drive_free (CDDrive *drive);
+
+#endif
diff --git a/src/sj-error.c b/src/sj-error.c
new file mode 100644
index 0000000..409ef77
--- /dev/null
+++ b/src/sj-error.c
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2003 Ross Burton
+ *
+ * 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.
+ */
+
+#include <config.h>
+
+#include "sj-error.h"
+
+GQuark
+sj_error_quark (void)
+{
+ static GQuark q = 0;
+ if (q == 0) {
+ q = g_quark_from_static_string ("sj-error-quark");
+ }
+ return q;
+}
diff --git a/src/sj-error.h b/src/sj-error.h
new file mode 100644
index 0000000..eeb5815
--- /dev/null
+++ b/src/sj-error.h
@@ -0,0 +1,14 @@
+#ifndef SJ_ERROR_H
+#define SJ_ERROR_H
+
+#include <glib.h>
+
+#define SJ_ERROR sj_error_quark ()
+
+typedef enum {
+ SJ_ERROR_INTERNAL_ERROR
+} SjError;
+
+GQuark sj_error_quark (void) G_GNUC_CONST;
+
+#endif
diff --git a/src/sj-gstreamer-fake.c b/src/sj-gstreamer-fake.c
new file mode 100644
index 0000000..5f6f394
--- /dev/null
+++ b/src/sj-gstreamer-fake.c
@@ -0,0 +1,41 @@
+#include <glib/gerror.h>
+#include <glib/gmessages.h>
+#include <glib/gtimer.h>
+#include <glib/gmain.h>
+#include "sj-gstreamer.h"
+
+void sj_gstreamer_init (int argc, char **argv, GError **error)
+{
+ g_print ("gstreamer-fake: init()\n");
+}
+
+void sj_gstreamer_shutdown (void)
+{
+ g_print ("gstreamer-fake: shutdown()\n");
+}
+
+void sj_gstreamer_set_cdrom (const char* device)
+{
+ g_print ("gstreamer-fake: set_cdrom(\"%s\")\n", device == NULL ? "NULL" : device);
+}
+
+static int counter;
+
+static gboolean idle_callback(gpointer data)
+{
+ g_print(".");
+ g_usleep(100);
+ counter++;
+ if (counter > 100) {
+ /* fire finish */
+ } else if (counter % 10 == 0) {
+ /* fire progress event */
+ }
+ return TRUE;
+}
+
+void sj_gstreamer_extract_track (const TrackDetails *track, const char* path, GError **error)
+{
+ g_print ("gstreamer-fake: extract_track()\n");
+ g_idle_add (idle_callback, NULL);
+}
diff --git a/src/sj-gstreamer.c b/src/sj-gstreamer.c
new file mode 100644
index 0000000..a8c3a2e
--- /dev/null
+++ b/src/sj-gstreamer.c
@@ -0,0 +1,181 @@
+#include <glib/gerror.h>
+#include <glib/gtypes.h>
+#include <gst/gst.h>
+#include "sj-structures.h"
+#include "sj-gstreamer.h"
+#include "sj-error.h"
+
+static GstElement *pipeline;
+static GstElement *cdparanoia, *vorbisenc, *filesink;
+static GstFormat track_format;
+GstPad *source_pad;
+
+#define THREADED 0
+#define MAINLOOP 1
+
+typedef void (*progress_cb_t) (int seconds);
+typedef void (*completion_cb_t) (void);
+
+progress_cb_t progress_cb;
+completion_cb_t completion_cb;
+
+static void eos_cb (GstElement *gstelement, gpointer user_data)
+{
+ completion_cb();
+}
+
+void sj_gstreamer_init (int argc, char **argv, GError **error)
+{
+ gst_init (&argc, &argv);
+
+#if THREADED
+ pipeline = gst_thread_new ("pipeline");
+#else
+ pipeline = gst_pipeline_new ("pipeline");
+#endif
+
+ /* Read from CD */
+ cdparanoia = gst_element_factory_make ("cdparanoia", "cdparanoia");
+ if (cdparanoia == NULL) {
+ g_set_error (error,
+ SJ_ERROR, SJ_ERROR_INTERNAL_ERROR,
+ "Could not create cdparanoia element");
+ return;
+ }
+ g_object_set (G_OBJECT (cdparanoia), "paranoia_mode", 0, NULL);
+ /* Get the track format for seeking later */
+ track_format = gst_format_get_by_nick ("track");
+ g_assert (track_format != 0); /* TODO: GError */
+ /* Get the source pad for seeking */
+ source_pad = gst_element_get_pad (cdparanoia, "src");
+ g_assert (source_pad); /* TODO: GError */
+
+ /* Encode to Ogg Vorbis */
+ vorbisenc = gst_element_factory_make ("vorbisenc", "vorbisenc");
+ if (vorbisenc == NULL) {
+ g_set_error (error,
+ SJ_ERROR, SJ_ERROR_INTERNAL_ERROR,
+ "Could not create Vorbis encoding element");
+ return;
+ }
+
+ /* Write to disk */
+ filesink = gst_element_factory_make ("filesink", "filesink");
+ if (filesink == NULL) {
+ g_set_error (error,
+ SJ_ERROR, SJ_ERROR_INTERNAL_ERROR,
+ "Could not create file sink element");
+ return;
+ }
+
+ /* Add the elements to the pipeline */
+ gst_bin_add (GST_BIN (pipeline), cdparanoia);
+ gst_bin_add (GST_BIN (pipeline), vorbisenc);
+ gst_bin_add (GST_BIN (pipeline), filesink);
+
+ /* Link it all together */
+#if 0
+ gst_element_link_pads (cdparanoia, "src", vorbisenc, "sink");
+ gst_element_link_pads (vorbisenc, "src", filesink, "sink");
+#else
+ gst_element_link_many (cdparanoia, vorbisenc, filesink, NULL);
+#endif
+
+ g_signal_connect (cdparanoia, "eos", eos_cb, NULL);
+}
+
+void sj_gstreamer_shutdown (void)
+{
+ g_return_if_fail (pipeline != NULL);
+ gst_element_set_state (pipeline, GST_STATE_NULL);
+}
+
+void sj_gstreamer_set_cdrom (const char* device)
+{
+ g_assert (cdparanoia != NULL);
+ g_return_if_fail (device != NULL);
+
+ g_object_set (G_OBJECT (cdparanoia), "location", device, NULL);
+}
+
+static gint seconds;
+
+static gboolean tick_timeout_cb(gpointer user_data)
+{
+ gint64 nanos;
+ gint secs;
+ static GstFormat format = GST_FORMAT_TIME;
+
+ if (gst_element_get_state (pipeline) != GST_STATE_PLAYING) {
+ return FALSE;
+ }
+
+ if (!gst_pad_query (source_pad, GST_QUERY_POSITION, &format, &nanos)) {
+ g_print ("pad_query failed!\n");
+ return TRUE;
+ }
+
+ secs = nanos / GST_SECOND;
+ if (secs != seconds) {
+ seconds = secs;
+ progress_cb(seconds);
+ }
+ return TRUE;
+}
+
+void sj_gstreamer_set_callbacks (progress_cb_t progress, completion_cb_t completion)
+{
+ progress_cb = progress;
+ completion_cb = completion;
+}
+void sj_gstreamer_extract_track (const TrackDetails *track, const char* path, GError **error)
+{
+ GstEvent *event;
+ GstCaps *caps;
+ char *tracknumber;
+
+ g_return_if_fail (pipeline != NULL);
+ g_return_if_fail (path != NULL);
+ g_return_if_fail (track != NULL);
+
+ /* Set the output filename */
+ g_object_set (G_OBJECT (filesink), "location", path, NULL);
+
+ /* Set the Ogg metadata */
+ /* Requires gst 0.6.1 to work correctly */
+ tracknumber = g_strdup_printf("%d", track->number);
+ caps = GST_CAPS_NEW ("vorbisenc_metadata",
+ "application/x-gst-metadata",
+ "title", GST_PROPS_STRING (track->title),
+ "artist", GST_PROPS_STRING (track->artist),
+ "tracknumber", GST_PROPS_STRING (tracknumber),
+ "album", GST_PROPS_STRING (track->album->title),
+ "comment", GST_PROPS_STRING("Ripped with Sound Juicer")
+ );
+ g_object_set (G_OBJECT (vorbisenc), "metadata", caps, NULL);
+ g_free (tracknumber);
+
+ /* Let's get ready to rumble! */
+ gst_element_set_state (pipeline, GST_STATE_PAUSED);
+
+ /* Seek to the right track */
+ event = gst_event_new_segment_seek (track_format | GST_SEEK_METHOD_SET | GST_SEEK_FLAG_FLUSH,
+ track->number-1, track->number);
+ if (!gst_pad_send_event (source_pad, event)) {
+ g_set_error (error,
+ SJ_ERROR, SJ_ERROR_INTERNAL_ERROR,
+ "Could not seek to track");
+ return;
+ }
+
+ gst_element_set_state (pipeline, GST_STATE_PLAYING);
+#if MAINLOOP
+ g_idle_add ((GSourceFunc)gst_bin_iterate, pipeline);
+ g_timeout_add (200, (GSourceFunc)tick_timeout_cb, NULL);
+#else
+ while (gst_bin_iterate (GST_BIN (pipeline))) {
+ g_print(".");
+ }
+ gst_element_set_state (pipeline, GST_STATE_NULL);
+#endif
+}
diff --git a/src/sj-gstreamer.h b/src/sj-gstreamer.h
new file mode 100644
index 0000000..16014f3
--- /dev/null
+++ b/src/sj-gstreamer.h
@@ -0,0 +1,14 @@
+#ifndef SJ_GSTREAMER_H
+#define SJ_GSTREAMER_H
+
+#include <glib/gerror.h>
+#include "sj-structures.h"
+
+void sj_gstreamer_init (int argc, char **argv, GError **error);
+void sj_gstreamer_shutdown (void);
+
+void sj_gstreamer_set_cdrom (const char* device);
+
+void sj_gstreamer_extract_track (const TrackDetails *track, const char* path, GError **error);
+
+#endif
diff --git a/src/sj-musicbrainz-fake.c b/src/sj-musicbrainz-fake.c
new file mode 100644
index 0000000..fb5caa3
--- /dev/null
+++ b/src/sj-musicbrainz-fake.c
@@ -0,0 +1,45 @@
+#include <glib/glist.h>
+#include <glib/gerror.h>
+#include <glib/gstrfuncs.h>
+#include "sj-musicbrainz.h"
+#include "sj-structures.h"
+
+void sj_musicbrainz_init (void) {}
+
+void sj_musicbrainz_set_cdrom (const char* device) {}
+
+GList* sj_musicbrainz_list_albums (GError **error) {
+ GList *albums = NULL;
+ AlbumDetails *album;
+ TrackDetails *track;
+ int i;
+
+ album = g_new0 (AlbumDetails, 1);
+ album->title = "Some Title";
+ album->artist = "Some Artist";
+
+ for (i = 1; i < 10; i++) {
+ track = g_new0 (TrackDetails, 1);
+ track->number = i;
+ track->title = g_strdup_printf ("Track %d", i);
+ track->artist = album->artist;
+ track->duration = 60;
+ album->tracks = g_list_append (album->tracks, track);
+ }
+ albums = g_list_append (albums, album);
+
+ album = g_new0 (AlbumDetails, 1);
+ album->title = "Another Album";
+ album->artist = "Someone Else";
+
+ for (i = 1; i < 10; i++) {
+ track = g_new0 (TrackDetails, 1);
+ track->number = i;
+ track->title = g_strdup_printf ("Song %d", i);
+ track->artist = album->artist;
+ track->duration = 120;
+ album->tracks = g_list_append (album->tracks, track);
+ }
+ albums = g_list_append (albums, album);
+ return albums;
+}
diff --git a/src/sj-musicbrainz.c b/src/sj-musicbrainz.c
new file mode 100644
index 0000000..07dee05
--- /dev/null
+++ b/src/sj-musicbrainz.c
@@ -0,0 +1,122 @@
+#include <glib/gerror.h>
+#include <glib/glist.h>
+#include <glib/gstrfuncs.h>
+#include <glib/gmessages.h>
+#include <musicbrainz/queries.h>
+#include <musicbrainz/mb_c.h>
+#include <stdlib.h>
+#include "sj-musicbrainz.h"
+#include "sj-structures.h"
+
+static char* cdrom;
+
+void sj_musicbrainz_init (void) {
+ cdrom = g_strdup ("/dev/cdrom");
+}
+
+void sj_musicbrainz_set_cdrom(const char* device) {
+ g_return_if_fail (device != NULL);
+ g_free (cdrom);
+ cdrom = g_strdup (device);
+}
+
+static GList* get_offline_track_listing(musicbrainz_t mb, GError **error)
+{
+ GList* list = NULL;
+ AlbumDetails *album;
+ TrackDetails *track;
+ int num_tracks, i;
+
+ if (!mb_Query (mb, MBQ_GetCDTOC)) {
+ char error[255];
+ mb_GetQueryError (mb, error, 255);
+ g_print("Cannot read CD: %s\n", error);
+ return NULL; /* TODO: GError */
+ }
+ num_tracks = mb_GetResultInt (mb, MBE_TOCGetLastTrack);
+
+ album = g_new0 (AlbumDetails, 1);
+ album->artist = "Unknown Artist";
+ album->title = "Unknown Title";
+ for (i = 1; i <= num_tracks; i++) {
+ track = g_new0 (TrackDetails, 1);
+ track->album = album;
+ track->number = i;
+ track->title = g_strdup_printf ("Track %d", i);
+ track->artist = album->artist;
+ /* TODO: track duration */
+ album->tracks = g_list_append (album->tracks, track);
+ }
+ return g_list_append (list, album);
+}
+
+/* TODO: GErrorify */
+GList* sj_musicbrainz_list_albums(GError **error) {
+ GList *albums = NULL;
+ musicbrainz_t mb;
+ char data[256];
+ int num_albums, i, j;
+
+ mb = mb_New ();
+ if (!mb) {
+ g_print ("Cannot get MusicBrainz connection\n");
+ return NULL; /* TODO: GError */
+ }
+ mb_SetDevice (mb, cdrom);
+
+ if (!mb_Query (mb, MBQ_GetCDInfo)) {
+ char error[255];
+ mb_GetQueryError (mb, error, 255);
+ g_print("Cannot lookup CD: %s\n", error);
+ return get_offline_track_listing (mb, NULL);
+ }
+
+ num_albums = mb_GetResultInt(mb, MBE_GetNumAlbums);
+ if (num_albums < 1) {
+ g_print("This CD was not found.\n");
+ return NULL;
+ }
+
+ for (i = 1; i <= num_albums; i++) {
+ int num_tracks;
+ AlbumDetails *album;
+
+ mb_Select1(mb, MBS_SelectAlbum, i);
+ album = g_new0 (AlbumDetails, 1);
+
+ mb_GetResultData(mb, MBE_AlbumGetAlbumName, data, 256);
+ album->title = g_strdup (data);
+
+ mb_GetResultData1(mb, MBE_AlbumGetArtistName, data, 256, 1);
+ album->artist = g_strdup (data);
+
+ num_tracks = mb_GetResultInt(mb, MBE_AlbumGetNumTracks);
+
+ for (j = 1; j <= num_tracks; j++) {
+ TrackDetails *track;
+ track = g_new0 (TrackDetails, 1);
+
+ track->album = album;
+
+ track->number = j; /* TODO: replace with number lookup */
+
+ if (mb_GetResultData1(mb, MBE_AlbumGetTrackName, data, 256, j)) {
+ track->title = g_strdup (data);
+ }
+
+ if (mb_GetResultData1(mb, MBE_AlbumGetArtistName, data, 256, i)) {
+ track->artist = g_strdup (data);
+ }
+
+ if (mb_GetResultData1(mb, MBE_AlbumGetTrackDuration, data, 256, j)) {
+ track->duration = atoi (data) / 1000;
+ }
+
+ album->tracks = g_list_append (album->tracks, track);
+ }
+
+ albums = g_list_append (albums, album);
+ }
+ mb_Delete (mb);
+ return albums;
+}
diff --git a/src/sj-musicbrainz.h b/src/sj-musicbrainz.h
new file mode 100644
index 0000000..0164fe7
--- /dev/null
+++ b/src/sj-musicbrainz.h
@@ -0,0 +1,11 @@
+#ifndef SJ_MUSICBRAINZ_H
+#define SJ_MUSICBRAINZ_H
+
+#include <glib/glist.h>
+#include <glib/gerror.h>
+
+void sj_musicbrainz_init (void);
+void sj_musicbrainz_set_cdrom (const char* device);
+GList* sj_musicbrainz_list_albums (GError **error);
+
+#endif
diff --git a/src/sj-structures.h b/src/sj-structures.h
new file mode 100644
index 0000000..94ca558
--- /dev/null
+++ b/src/sj-structures.h
@@ -0,0 +1,24 @@
+#ifndef SJ_STRUCTURES_H
+#define SJ_STRUCTURES_H
+
+#include <glib/glist.h>
+
+typedef struct _AlbumDetails AlbumDetails;
+typedef struct _TrackDetails TrackDetails;
+
+
+struct _TrackDetails {
+ AlbumDetails *album;
+ int number; /* track number */
+ const char *title;
+ const char *artist;
+ int duration; /* seconds */
+};
+
+struct _AlbumDetails {
+ const char* title;
+ const char* artist;
+ GList* tracks;
+};
+
+#endif
diff --git a/tests/Makefile.am b/tests/Makefile.am
new file mode 100644
index 0000000..d875c4a
--- /dev/null
+++ b/tests/Makefile.am
@@ -0,0 +1,17 @@
+INCLUDES = -I../src/ @LIBGLADE_CFLAGS@ @GSTREAMER_CFLAGS@ @MUSICBRAINZ_CFLAGS@ @GNOME_CFLAGS@
+AM_CFLAGS = -g -Wall
+DEFS = -DGNOME_DISABLE_DEPRECATED=1
+
+noinst_PROGRAMS = data-test glade-test gst-test mb-test
+
+data_test_SOURCES = data-test.c
+data_test_LDADD = @GLIB_LIBS@
+
+gst_test_SOURCES = gst-test.c ../src/sj-gstreamer.c ../src/sj-error.c
+gst_test_LDADD = @GLIB_LIBS@ @GSTREAMER_LIBS@
+
+mb_test_SOURCES = mb-test.c ../src/sj-musicbrainz.c
+mb_test_LDADD = @GLIB_LIBS@ @MUSICBRAINZ_LIBS@
+
+glade_test_SOURCES = glade-test.c ../src/sj-error.c ../src/sj-gstreamer.c ../src/sj-musicbrainz.c ../src/bacon-cd-selection.c ../src/cd-drive.c
+glade_test_LDADD = @MUSICBRAINZ_LIBS@ @GSTREAMER_LIBS@ @LIBGLADE_LIBS@ @GNOME_LIBS@
diff --git a/tests/data-test.c b/tests/data-test.c
new file mode 100644
index 0000000..08677c9
--- /dev/null
+++ b/tests/data-test.c
@@ -0,0 +1,31 @@
+#include "sj-structures.h"
+#include <glib.h>
+
+int main(int argc, char **argv)
+{
+ AlbumDetails* album;
+ TrackDetails* track;
+ int i;
+
+ album = g_new0 (AlbumDetails, 1);
+ album->title = "Some Title";
+ album->artist = "Some Artist";
+
+ for (i = 1; i < 10; i++) {
+ track = g_new0 (TrackDetails, 1);
+ track->number = i;
+ track->title = g_strdup_printf ("Track %d", i);
+ track->artist = album->artist;
+ track->duration = 0;
+ album->tracks = g_list_append (album->tracks, track);
+ }
+
+ g_print("Album Title: %s; Artist: %s\n", album->title, album->artist);
+ /* This destroys the GList but I don't care */
+ while (album->tracks) {
+ TrackDetails *track = (TrackDetails*)album->tracks->data;
+ g_print("Track %d; Title: %s; Artist: %s\n", track->number, track->title, track->artist);
+ album->tracks = g_list_next (album->tracks);
+ }
+ return 0;
+}
diff --git a/tests/glade-test.c b/tests/glade-test.c
new file mode 100644
index 0000000..7d226f7
--- /dev/null
+++ b/tests/glade-test.c
@@ -0,0 +1,490 @@
+#include "config.h"
+#include <string.h>
+#include <sys/stat.h>
+#include <gtk/gtk.h>
+#include <glade/glade-xml.h>
+#include <gconf/gconf-client.h>
+#include <libgnomeui/gnome-about.h>
+#include <libgnomeui/gnome-file-entry.h>
+#include "bacon-cd-selection.h"
+#include "sj-musicbrainz.h"
+#include "sj-gstreamer.h"
+#include "sj-structures.h"
+
+GladeXML *glade;
+
+GConfClient *gconf_client;
+
+GtkWidget *main_window, *progress_dialog = NULL;
+GtkWidget *title_label, *artist_label, *basepath_label;
+GtkWidget *track_listview, *cd_option, *reread_button, *extract_button;
+GtkWidget *progress_label, *track_progress;
+GtkListStore *track_store;
+
+const char *base_path;
+const char *device;
+int duration; /* duration of current track for progress dialog */
+gboolean ripping = FALSE;
+
+#define GCONF_ROOT "/apps/sound-juicer"
+#define GCONF_DEVICE GCONF_ROOT "/device"
+#define GCONF_BASEPATH GCONF_ROOT "/base_path"
+
+enum {
+ COLUMN_EXTRACT,
+ COLUMN_NUMBER,
+ COLUMN_TITLE,
+ COLUMN_ARTIST,
+ COLUMN_DURATION,
+ COLUMN_DETAILS,
+ COLUMN_TOTAL
+};
+
+/**
+ * Clicked About in the menus
+ */
+void on_about_activate (GtkMenuItem *item, gpointer user_data)
+{
+ GtkWidget *dialog = NULL;
+ const char* authors[] = {"Ross Burton <ross@burtonini.com>", NULL};
+ dialog = gnome_about_new ("Sound Juicer",
+ VERSION,
+ "Copyright (C) 2003 Ross Burton",
+ "A CD ripper",
+ authors,
+ NULL, NULL, NULL);
+ gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (main_window));
+ gtk_dialog_run (GTK_DIALOG (dialog));
+}
+
+/**
+ * Clicked Quit
+ */
+void on_quit_activate (GtkMenuItem *item, gpointer user_data)
+{
+ sj_gstreamer_shutdown ();
+ gtk_main_quit ();
+}
+
+gboolean on_destory_event (GtkWidget *widget, GdkEvent *event, gpointer user_data)
+{
+ if (ripping) {
+ GtkWidget *dialog;
+ int response;
+
+ dialog = gtk_message_dialog_new (GTK_WINDOW (main_window), GTK_DIALOG_MODAL,
+ GTK_MESSAGE_QUESTION,
+ GTK_BUTTONS_NONE,
+ "You are currently ripping a CD. Do you want to quit now or contine ripping?");
+ gtk_dialog_add_button (GTK_DIALOG (dialog), "gtk-quit", GTK_RESPONSE_ACCEPT);
+ gtk_dialog_add_button (GTK_DIALOG (dialog), "Continue", GTK_RESPONSE_REJECT);
+ gtk_widget_show_all (dialog);
+ response = gtk_dialog_run (GTK_DIALOG (dialog));
+ return response != GTK_RESPONSE_ACCEPT;
+ }
+ return FALSE;
+}
+
+/**
+ * GtkTreeView cell renderer callback to render durations
+ */
+static void duration_cell_data_cb (GtkTreeViewColumn *tree_column,
+ GtkCellRenderer *cell,
+ GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ GValue v = {0};
+ int duration;
+
+ gtk_tree_model_get_value (tree_model, iter, COLUMN_DURATION, &v);
+ duration = g_value_get_int (&v);
+
+ g_value_unset (&v);
+ g_value_init(&v, G_TYPE_STRING);
+ g_value_set_string (&v, g_strdup_printf("%d:%02d", duration / 60, duration % 60));
+ g_object_set_property (G_OBJECT (cell), "text", &v);
+}
+
+/**
+ * Utility function to update the UI for a given Album
+ */
+static void update_ui_for_album (AlbumDetails *album)
+{
+ GList *l;
+ if (album == NULL) {
+ gtk_label_set_text (GTK_LABEL (title_label), "");
+ gtk_label_set_text (GTK_LABEL (artist_label), "");
+ gtk_list_store_clear (track_store);
+ gtk_widget_set_sensitive (extract_button, FALSE);
+ } else {
+ gtk_label_set_text (GTK_LABEL (title_label), album->title);
+ gtk_label_set_text (GTK_LABEL (artist_label), album->artist);
+ gtk_widget_set_sensitive (extract_button, TRUE);
+
+ gtk_list_store_clear (track_store);
+ for (l = album->tracks; l; l=g_list_next (l)) {
+ GtkTreeIter iter;
+ TrackDetails *track = (TrackDetails*)l->data;
+ gtk_list_store_append (track_store, &iter);
+ gtk_list_store_set (track_store, &iter,
+ COLUMN_NUMBER, track->number,
+ COLUMN_TITLE, track->title,
+ COLUMN_ARTIST, track->artist,
+ COLUMN_DURATION, track->duration,
+ COLUMN_DETAILS, track,
+ -1);
+ }
+ }
+}
+
+/**
+ * Called by the Multiple Album dialog when the user hits return in
+ * the list view
+ */
+static void album_row_activated (GtkTreeView *treeview,
+ GtkTreePath *arg1,
+ GtkTreeViewColumn *arg2,
+ gpointer user_data)
+{
+ GtkDialog *dialog = GTK_DIALOG (user_data);
+ gtk_dialog_response (dialog, GTK_RESPONSE_OK);
+}
+
+/**
+ * Utility function for when there are more than one albums available
+ */
+AlbumDetails* multiple_album_dialog(GList *albums)
+{
+ static GtkWidget *dialog = NULL, *albums_listview;
+ static GtkListStore *albums_store;
+ static GtkTreeSelection *selection;
+ AlbumDetails *album;
+ GtkTreeIter iter;
+ int response;
+
+ if (dialog == NULL) {
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *text_renderer = text_renderer = gtk_cell_renderer_text_new ();
+
+ dialog = glade_xml_get_widget (glade, "multiple_dialog");
+ g_assert (dialog != NULL);
+ gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (main_window));
+ albums_listview = glade_xml_get_widget (glade, "albums_listview");
+ /* TODO: A little hacky */
+ g_signal_connect (albums_listview, "row-activated", G_CALLBACK (album_row_activated), dialog);
+
+ albums_store = gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER);
+ column = gtk_tree_view_column_new_with_attributes ("Title",
+ text_renderer,
+ "text", 0,
+ NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (albums_listview), column);
+
+ column = gtk_tree_view_column_new_with_attributes ("Artist",
+ text_renderer,
+ "text", 1,
+ NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (albums_listview), column);
+ gtk_tree_view_set_model (GTK_TREE_VIEW (albums_listview), GTK_TREE_MODEL (albums_store));
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW (albums_listview));
+ gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
+ }
+
+ gtk_list_store_clear (albums_store);
+ for (; albums ; albums = g_list_next (albums)) {
+ GtkTreeIter iter;
+ AlbumDetails *album = (AlbumDetails*)(albums->data);
+ gtk_list_store_append (albums_store, &iter);
+ gtk_list_store_set (albums_store, &iter,
+ 0, album->title,
+ 1, album->artist,
+ 2, album,
+ -1);
+ }
+ /* TODO: focus is a little broken here */
+ gtk_widget_grab_focus (albums_listview);
+ gtk_widget_show_all (dialog);
+ response = gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_hide (dialog);
+
+ if (response == GTK_RESPONSE_DELETE_EVENT) {
+ return NULL;
+ }
+ gtk_tree_selection_get_selected (selection, NULL, &iter);
+ gtk_tree_model_get (GTK_TREE_MODEL (albums_store), &iter, 2, &album, -1);
+ return album;
+}
+
+/**
+ * The GConf key for the base path changed
+ */
+void basepath_changed_cb (GConfClient *client, guint cnxn_id, GConfEntry *entry, gpointer user_data)
+{
+ g_assert (strcmp (entry->key, GCONF_BASEPATH) == 0);
+ if (entry->value == NULL) {
+ base_path = g_strdup (g_get_home_dir ());
+ } else {
+ base_path = gconf_value_get_string (entry->value);
+ }
+ /* TODO: sanity check the path somewhat */
+ gtk_label_set_text (GTK_LABEL (basepath_label), base_path);
+}
+
+/**
+ * Utility function to reread a CD
+ */
+void reread_cd (void)
+{
+ GList *albums;
+ GError *error = NULL;
+
+ albums = sj_musicbrainz_list_albums(&error);
+ if (error) {
+ g_print ("Cannot list albums: %s\n", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ /* If there is more than one album... */
+ if (g_list_next (albums)) {
+ update_ui_for_album(multiple_album_dialog (albums));
+ } else {
+ update_ui_for_album (albums ? albums->data : NULL);
+ }
+}
+
+/**
+ * The GConf key for the device changed
+ */
+void device_changed_cb (GConfClient *client, guint cnxn_id, GConfEntry *entry, gpointer user_data)
+{
+ g_assert (strcmp (entry->key, GCONF_DEVICE) == 0);
+
+ if (entry->value == NULL) {
+ device = bacon_cd_selection_get_device (BACON_CD_SELECTION (cd_option));
+ if (device == NULL) {
+#if 0
+ GtkWidget *dialog;
+ dialog = gtk_message_dialog_new (NULL, 0,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_CLOSE,
+ "<b>No CD-ROMs found</b>\n\n"
+ "Sound Juicer could not find any CD-ROM drives to read.");
+ gtk_label_set_use_markup (GTK_LABEL (GTK_MESSAGE_DIALOG (dialog)->label), TRUE);
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ exit(1); /* TODO: fix */
+#endif
+ }
+ } else {
+ device = gconf_value_get_string (entry->value);
+ /* TODO: sanity check device */
+ }
+ sj_musicbrainz_set_cdrom (device);
+ sj_gstreamer_set_cdrom (device);
+
+ reread_cd();
+}
+
+/**
+ * Called for every selected track when ripping
+ */
+static gboolean rip_track_foreach_cb (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ /* Remember, FALSE to continue! */
+ TrackDetails *track;
+ gboolean extract;
+ char *file_path, *directory;
+ int number;
+
+ gtk_tree_model_get (model, iter, COLUMN_EXTRACT, &extract, -1);
+ if (!extract) return FALSE;
+
+ gtk_tree_model_get (model, iter,
+ COLUMN_NUMBER, &number,
+ COLUMN_DETAILS, &track,
+ COLUMN_DURATION, &duration,
+ -1);
+
+ gtk_label_set_text (GTK_LABEL (progress_label), g_strdup_printf ("Currently extracting '%s'", track->title));
+ file_path = g_strdup_printf("%s/%s/%s/%s.ogg", base_path, track->artist, track->album->title, track->title); /* TODO: CRAP */
+ /* TODO: create the folder */
+ directory = g_path_get_dirname (file_path);
+ if (!g_file_test (directory, G_FILE_TEST_IS_DIR)) {
+ int res;
+ res = mkdir (directory, 0750);
+ if (res == -1) {
+ /* TODO: handle errno, dialog etc */
+ g_warning ("mkdir() failed");
+ return TRUE;
+ }
+ }
+ g_free (directory);
+ ripping = TRUE;
+ sj_gstreamer_extract_track (track, file_path, NULL);
+ g_free (file_path);
+ /* Need to wait for completion */
+ return FALSE;
+}
+
+/**
+ * Clicked on Extract in the UI
+ */
+void on_extract_activate (GtkWidget *button, gpointer user_data)
+{
+ if (progress_dialog == NULL) {
+ progress_dialog = glade_xml_get_widget (glade, "progress_dialog");
+ track_progress = glade_xml_get_widget (glade, "track_progress");
+ progress_label = glade_xml_get_widget (glade, "progress_label");
+ g_assert (progress_dialog != NULL);
+ }
+ gtk_widget_show_all (progress_dialog);
+ gtk_tree_model_foreach (GTK_TREE_MODEL (track_store), rip_track_foreach_cb, NULL);
+}
+
+/**
+ * Clicked on Reread in the UI (button/menu)
+ */
+void on_reread_activate (GtkWidget *button, gpointer user_data)
+{
+ reread_cd ();
+}
+
+/**
+ * Changed the CD-ROM device in the prefs dialog
+ */
+void prefs_cdrom_changed_cb (GtkWidget *widget, const char* device)
+{
+ gconf_client_set_string (gconf_client, "/apps/sound-juicer/device", device, NULL);
+}
+
+/**
+ * Clicked on Browse in the Prefs dialog
+ */
+void prefs_browse_clicked (GtkButton *button, gpointer user_data)
+{
+ g_print ("%s: TODO\n", __FUNCTION__);
+}
+
+/**
+ * Clicked on Preferences in the UI
+ */
+void on_edit_preferences_cb (GtkMenuItem *item, gpointer user_data)
+{
+ static GtkWidget *dialog;
+ if (dialog == NULL) {
+ dialog = glade_xml_get_widget (glade, "prefs_dialog");
+ g_assert (dialog != NULL);
+ gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (main_window));
+ /* Maybe connect a GConf notify to the base_path key here to update the label... */
+ }
+ gtk_widget_show_all (dialog);
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_hide (dialog);
+}
+
+static void on_extract_toggled (GtkCellRendererToggle *cellrenderertoggle,
+ gchar *path,
+ gpointer user_data)
+{
+ gboolean extract;
+ GtkTreeIter iter;
+ gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (track_store), &iter, path);
+ gtk_tree_model_get (GTK_TREE_MODEL (track_store), &iter, COLUMN_EXTRACT, &extract, -1);
+ gtk_list_store_set (track_store, &iter, COLUMN_EXTRACT, !extract, -1);
+}
+
+static void on_progress_cb (int seconds)
+{
+ g_print ("Callback: %d\n", seconds);
+ gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (track_progress), (float)seconds/(float)duration);
+ return;
+}
+
+static void on_completion_cb (void)
+{
+ gtk_widget_hide (progress_dialog);
+ g_print ("Callback complete\n");
+ ripping = FALSE;
+ return;
+}
+
+int main (int argc, char **argv)
+{
+ gtk_init (&argc, &argv);
+ sj_musicbrainz_init ();
+ sj_gstreamer_init (argc, argv, NULL);
+ sj_gstreamer_set_callbacks (on_progress_cb, on_completion_cb);
+
+ gconf_client = gconf_client_get_default();
+ g_assert (gconf_client != NULL);
+ /* TODO: move paths and key names to defines */
+ gconf_client_add_dir (gconf_client, GCONF_ROOT, GCONF_CLIENT_PRELOAD_RECURSIVE, NULL);
+ gconf_client_notify_add (gconf_client, GCONF_DEVICE, device_changed_cb, NULL, NULL, NULL);
+ gconf_client_notify_add (gconf_client, GCONF_BASEPATH, basepath_changed_cb, NULL, NULL, NULL);
+
+ glade = glade_xml_new ("../data/sound-juicer.glade", NULL, NULL);
+ g_assert (glade != NULL);
+ glade_xml_signal_autoconnect (glade);
+
+ main_window = glade_xml_get_widget (glade, "main_window");
+ title_label = glade_xml_get_widget (glade, "title_label");
+ artist_label = glade_xml_get_widget (glade, "artist_label");
+ basepath_label = glade_xml_get_widget (glade, "path_label"); /* from prefs, urgh */
+ track_listview = glade_xml_get_widget (glade, "track_listview");
+ cd_option = glade_xml_get_widget (glade, "cd_option"); /* hmm */
+ extract_button = glade_xml_get_widget (glade, "extract_button");
+ reread_button = glade_xml_get_widget (glade, "reread_button");
+
+ track_store = gtk_list_store_new (COLUMN_TOTAL, G_TYPE_BOOLEAN, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_POINTER);
+ gtk_tree_view_set_model (GTK_TREE_VIEW (track_listview), GTK_TREE_MODEL (track_store));
+ {
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *text_renderer, *toggle_renderer;
+
+ toggle_renderer = gtk_cell_renderer_toggle_new ();
+ g_signal_connect (toggle_renderer, "toggled", G_CALLBACK (on_extract_toggled), NULL);
+ column = gtk_tree_view_column_new_with_attributes ("Extract?",
+ toggle_renderer,
+ "active", COLUMN_EXTRACT,
+ NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (track_listview), column);
+
+ text_renderer = gtk_cell_renderer_text_new (); /* TODO: is this cheating? */
+ column = gtk_tree_view_column_new_with_attributes ("Track",
+ text_renderer,
+ "text", COLUMN_NUMBER,
+ NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (track_listview), column);
+
+ column = gtk_tree_view_column_new_with_attributes ("Title",
+ text_renderer,
+ "text", COLUMN_TITLE,
+ NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (track_listview), column);
+
+ column = gtk_tree_view_column_new_with_attributes ("Artist",
+ text_renderer,
+ "text", COLUMN_ARTIST,
+ NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (track_listview), column);
+
+ column = gtk_tree_view_column_new_with_attributes ("Duration",
+ text_renderer,
+ NULL);
+ gtk_tree_view_column_set_cell_data_func (column, text_renderer, duration_cell_data_cb, NULL, NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (track_listview), column);
+
+ }
+
+ /* YUCK. As bad as Baldrick's trousers */
+ basepath_changed_cb (gconf_client, -1, gconf_client_get_entry (gconf_client, GCONF_BASEPATH, NULL, TRUE, NULL), NULL);
+ device_changed_cb (gconf_client, -1, gconf_client_get_entry (gconf_client, GCONF_DEVICE, NULL, TRUE, NULL), NULL);
+
+ gtk_widget_show_all(main_window);
+ gtk_main();
+ return 0;
+}
diff --git a/tests/gst-test.c b/tests/gst-test.c
new file mode 100644
index 0000000..a0447a8
--- /dev/null
+++ b/tests/gst-test.c
@@ -0,0 +1,22 @@
+#include <glib.h>
+#include "sj-gstreamer.h"
+#include "sj-structures.h"
+
+int main(int argc, char** argv)
+{
+ GError *error = NULL;
+
+ TrackDetails track;
+ track.artist = "Some Artist";
+ track.title = "Some Title";
+ track.number = 7;
+
+ sj_gstreamer_init (argc, argv, &error);
+ if (error) {
+ g_print ("Cannot init SJ_GST: %s\n", error->message);
+ return 1;
+ }
+ sj_gstreamer_set_cdrom ("/dev/cdroms/cdrom1");
+ sj_gstreamer_extract_track (&track, "test.ogg", NULL);
+ return 0;
+}
diff --git a/tests/mb-test.c b/tests/mb-test.c
new file mode 100644
index 0000000..29e7819
--- /dev/null
+++ b/tests/mb-test.c
@@ -0,0 +1,25 @@
+#include <glib.h>
+#include "sj-structures.h"
+#include "sj-musicbrainz.h"
+
+int main (int argc, char** argv)
+{
+ GList *albums;
+
+ sj_musicbrainz_init ();
+ sj_musicbrainz_set_cdrom ("/dev/cdroms/cdrom1");
+ albums = sj_musicbrainz_list_albums(NULL);
+
+ while (albums) {
+ AlbumDetails *album;
+ album = (AlbumDetails*)albums->data;
+ g_print ("'%s', by %s\n", album->title, album->artist);
+ while (album->tracks) {
+ TrackDetails *track = (TrackDetails*)album->tracks->data;
+ g_print (" Track %d; Title: %s; Artist: %s\n", track->number, track->title, track->artist);
+ album->tracks = g_list_next (album->tracks);
+ }
+ albums = g_list_next (albums);
+ }
+ return 0;
+}